summaryrefslogtreecommitdiffstats
path: root/kernel/sound/soc/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/sound/soc/codecs')
-rw-r--r--kernel/sound/soc/codecs/88pm860x-codec.c1437
-rw-r--r--kernel/sound/soc/codecs/88pm860x-codec.h96
-rw-r--r--kernel/sound/soc/codecs/Kconfig867
-rw-r--r--kernel/sound/soc/codecs/Makefile363
-rw-r--r--kernel/sound/soc/codecs/ab8500-codec.c2618
-rw-r--r--kernel/sound/soc/codecs/ab8500-codec.h592
-rw-r--r--kernel/sound/soc/codecs/ac97.c156
-rw-r--r--kernel/sound/soc/codecs/ad1836.c418
-rw-r--r--kernel/sound/soc/codecs/ad1836.h51
-rw-r--r--kernel/sound/soc/codecs/ad193x-i2c.c54
-rw-r--r--kernel/sound/soc/codecs/ad193x-spi.c48
-rw-r--r--kernel/sound/soc/codecs/ad193x.c392
-rw-r--r--kernel/sound/soc/codecs/ad193x.h92
-rw-r--r--kernel/sound/soc/codecs/ad1980.c347
-rw-r--r--kernel/sound/soc/codecs/ad73311.c89
-rw-r--r--kernel/sound/soc/codecs/ad73311.h88
-rw-r--r--kernel/sound/soc/codecs/adau1373.c1549
-rw-r--r--kernel/sound/soc/codecs/adau1373.h29
-rw-r--r--kernel/sound/soc/codecs/adau1701.c837
-rw-r--r--kernel/sound/soc/codecs/adau1701.h17
-rw-r--r--kernel/sound/soc/codecs/adau1761-i2c.c60
-rw-r--r--kernel/sound/soc/codecs/adau1761-spi.c77
-rw-r--r--kernel/sound/soc/codecs/adau1761.c808
-rw-r--r--kernel/sound/soc/codecs/adau1761.h23
-rw-r--r--kernel/sound/soc/codecs/adau1781-i2c.c58
-rw-r--r--kernel/sound/soc/codecs/adau1781-spi.c75
-rw-r--r--kernel/sound/soc/codecs/adau1781.c508
-rw-r--r--kernel/sound/soc/codecs/adau1781.h23
-rw-r--r--kernel/sound/soc/codecs/adau17x1.c915
-rw-r--r--kernel/sound/soc/codecs/adau17x1.h127
-rw-r--r--kernel/sound/soc/codecs/adau1977-i2c.c59
-rw-r--r--kernel/sound/soc/codecs/adau1977-spi.c76
-rw-r--r--kernel/sound/soc/codecs/adau1977.c1011
-rw-r--r--kernel/sound/soc/codecs/adau1977.h37
-rw-r--r--kernel/sound/soc/codecs/adav801.c53
-rw-r--r--kernel/sound/soc/codecs/adav803.c50
-rw-r--r--kernel/sound/soc/codecs/adav80x.c880
-rw-r--r--kernel/sound/soc/codecs/adav80x.h42
-rw-r--r--kernel/sound/soc/codecs/ads117x.c91
-rw-r--r--kernel/sound/soc/codecs/ak4104.c360
-rw-r--r--kernel/sound/soc/codecs/ak4535.c459
-rw-r--r--kernel/sound/soc/codecs/ak4535.h37
-rw-r--r--kernel/sound/soc/codecs/ak4554.c105
-rw-r--r--kernel/sound/soc/codecs/ak4641.c624
-rw-r--r--kernel/sound/soc/codecs/ak4641.h47
-rw-r--r--kernel/sound/soc/codecs/ak4642.c642
-rw-r--r--kernel/sound/soc/codecs/ak4671.c678
-rw-r--r--kernel/sound/soc/codecs/ak4671.h151
-rw-r--r--kernel/sound/soc/codecs/ak5386.c216
-rw-r--r--kernel/sound/soc/codecs/alc5623.c1101
-rw-r--r--kernel/sound/soc/codecs/alc5623.h161
-rw-r--r--kernel/sound/soc/codecs/alc5632.c1199
-rw-r--r--kernel/sound/soc/codecs/alc5632.h252
-rw-r--r--kernel/sound/soc/codecs/arizona.c2145
-rw-r--r--kernel/sound/soc/codecs/arizona.h264
-rw-r--r--kernel/sound/soc/codecs/bt-sco.c90
-rw-r--r--kernel/sound/soc/codecs/cq93vc.c164
-rw-r--r--kernel/sound/soc/codecs/cs35l32.c624
-rw-r--r--kernel/sound/soc/codecs/cs35l32.h93
-rw-r--r--kernel/sound/soc/codecs/cs4265.c674
-rw-r--r--kernel/sound/soc/codecs/cs4265.h64
-rw-r--r--kernel/sound/soc/codecs/cs4270.c766
-rw-r--r--kernel/sound/soc/codecs/cs4271-i2c.c62
-rw-r--r--kernel/sound/soc/codecs/cs4271-spi.c55
-rw-r--r--kernel/sound/soc/codecs/cs4271.c678
-rw-r--r--kernel/sound/soc/codecs/cs4271.h11
-rw-r--r--kernel/sound/soc/codecs/cs42l51-i2c.c60
-rw-r--r--kernel/sound/soc/codecs/cs42l51.c572
-rw-r--r--kernel/sound/soc/codecs/cs42l51.h168
-rw-r--r--kernel/sound/soc/codecs/cs42l52.c1302
-rw-r--r--kernel/sound/soc/codecs/cs42l52.h274
-rw-r--r--kernel/sound/soc/codecs/cs42l56.c1424
-rw-r--r--kernel/sound/soc/codecs/cs42l56.h177
-rw-r--r--kernel/sound/soc/codecs/cs42l73.c1509
-rw-r--r--kernel/sound/soc/codecs/cs42l73.h226
-rw-r--r--kernel/sound/soc/codecs/cs42xx8-i2c.c64
-rw-r--r--kernel/sound/soc/codecs/cs42xx8.c603
-rw-r--r--kernel/sound/soc/codecs/cs42xx8.h238
-rw-r--r--kernel/sound/soc/codecs/cx20442.c442
-rw-r--r--kernel/sound/soc/codecs/cx20442.h18
-rw-r--r--kernel/sound/soc/codecs/da7210.c1384
-rw-r--r--kernel/sound/soc/codecs/da7213.c1600
-rw-r--r--kernel/sound/soc/codecs/da7213.h523
-rw-r--r--kernel/sound/soc/codecs/da732x.c1589
-rw-r--r--kernel/sound/soc/codecs/da732x.h130
-rw-r--r--kernel/sound/soc/codecs/da732x_reg.h654
-rw-r--r--kernel/sound/soc/codecs/da9055.c1554
-rw-r--r--kernel/sound/soc/codecs/dmic.c86
-rw-r--r--kernel/sound/soc/codecs/es8328-i2c.c60
-rw-r--r--kernel/sound/soc/codecs/es8328-spi.c49
-rw-r--r--kernel/sound/soc/codecs/es8328.c756
-rw-r--r--kernel/sound/soc/codecs/es8328.h314
-rw-r--r--kernel/sound/soc/codecs/hdmi.c109
-rw-r--r--kernel/sound/soc/codecs/isabelle.c1166
-rw-r--r--kernel/sound/soc/codecs/isabelle.h143
-rw-r--r--kernel/sound/soc/codecs/jz4740.c375
-rw-r--r--kernel/sound/soc/codecs/l3.c91
-rw-r--r--kernel/sound/soc/codecs/lm4857.c211
-rw-r--r--kernel/sound/soc/codecs/lm49453.c1476
-rw-r--r--kernel/sound/soc/codecs/lm49453.h380
-rw-r--r--kernel/sound/soc/codecs/max9768.c255
-rw-r--r--kernel/sound/soc/codecs/max98088.c2026
-rw-r--r--kernel/sound/soc/codecs/max98088.h206
-rw-r--r--kernel/sound/soc/codecs/max98090.c2724
-rw-r--r--kernel/sound/soc/codecs/max98090.h1551
-rw-r--r--kernel/sound/soc/codecs/max98095.c2446
-rw-r--r--kernel/sound/soc/codecs/max98095.h321
-rw-r--r--kernel/sound/soc/codecs/max98357a.c145
-rw-r--r--kernel/sound/soc/codecs/max9850.c367
-rw-r--r--kernel/sound/soc/codecs/max9850.h38
-rw-r--r--kernel/sound/soc/codecs/max9877.c188
-rw-r--r--kernel/sound/soc/codecs/max9877.h37
-rw-r--r--kernel/sound/soc/codecs/max98925.c655
-rw-r--r--kernel/sound/soc/codecs/max98925.h832
-rw-r--r--kernel/sound/soc/codecs/mc13783.c813
-rw-r--r--kernel/sound/soc/codecs/mc13783.h28
-rw-r--r--kernel/sound/soc/codecs/ml26124.c648
-rw-r--r--kernel/sound/soc/codecs/ml26124.h184
-rw-r--r--kernel/sound/soc/codecs/pcm1681.c345
-rw-r--r--kernel/sound/soc/codecs/pcm1792a.c272
-rw-r--r--kernel/sound/soc/codecs/pcm1792a.h27
-rw-r--r--kernel/sound/soc/codecs/pcm3008.c172
-rw-r--r--kernel/sound/soc/codecs/pcm3008.h22
-rw-r--r--kernel/sound/soc/codecs/pcm512x-i2c.c80
-rw-r--r--kernel/sound/soc/codecs/pcm512x-spi.c73
-rw-r--r--kernel/sound/soc/codecs/pcm512x.c1609
-rw-r--r--kernel/sound/soc/codecs/pcm512x.h270
-rw-r--r--kernel/sound/soc/codecs/rl6231.c133
-rw-r--r--kernel/sound/soc/codecs/rl6231.h34
-rw-r--r--kernel/sound/soc/codecs/rt286.c1358
-rw-r--r--kernel/sound/soc/codecs/rt286.h205
-rw-r--r--kernel/sound/soc/codecs/rt5631.c1741
-rw-r--r--kernel/sound/soc/codecs/rt5631.h701
-rw-r--r--kernel/sound/soc/codecs/rt5640.c2258
-rw-r--r--kernel/sound/soc/codecs/rt5640.h2103
-rw-r--r--kernel/sound/soc/codecs/rt5645.c2895
-rw-r--r--kernel/sound/soc/codecs/rt5645.h2204
-rw-r--r--kernel/sound/soc/codecs/rt5651.c1820
-rw-r--r--kernel/sound/soc/codecs/rt5651.h2080
-rw-r--r--kernel/sound/soc/codecs/rt5670-dsp.h54
-rw-r--r--kernel/sound/soc/codecs/rt5670.c3059
-rw-r--r--kernel/sound/soc/codecs/rt5670.h2014
-rw-r--r--kernel/sound/soc/codecs/rt5677-spi.c130
-rw-r--r--kernel/sound/soc/codecs/rt5677-spi.h21
-rw-r--r--kernel/sound/soc/codecs/rt5677.c5159
-rw-r--r--kernel/sound/soc/codecs/rt5677.h1778
-rw-r--r--kernel/sound/soc/codecs/sgtl5000.c1578
-rw-r--r--kernel/sound/soc/codecs/sgtl5000.h400
-rw-r--r--kernel/sound/soc/codecs/si476x.c272
-rw-r--r--kernel/sound/soc/codecs/sigmadsp-i2c.c94
-rw-r--r--kernel/sound/soc/codecs/sigmadsp-regmap.c60
-rw-r--r--kernel/sound/soc/codecs/sigmadsp.c814
-rw-r--r--kernel/sound/soc/codecs/sigmadsp.h66
-rw-r--r--kernel/sound/soc/codecs/sirf-audio-codec.c581
-rw-r--r--kernel/sound/soc/codecs/sirf-audio-codec.h125
-rw-r--r--kernel/sound/soc/codecs/sn95031.c930
-rw-r--r--kernel/sound/soc/codecs/sn95031.h133
-rw-r--r--kernel/sound/soc/codecs/spdif_receiver.c91
-rw-r--r--kernel/sound/soc/codecs/spdif_transmitter.c92
-rw-r--r--kernel/sound/soc/codecs/ssm2518.c833
-rw-r--r--kernel/sound/soc/codecs/ssm2518.h20
-rw-r--r--kernel/sound/soc/codecs/ssm2602-i2c.c66
-rw-r--r--kernel/sound/soc/codecs/ssm2602-spi.c48
-rw-r--r--kernel/sound/soc/codecs/ssm2602.c651
-rw-r--r--kernel/sound/soc/codecs/ssm2602.h139
-rw-r--r--kernel/sound/soc/codecs/ssm4567.c471
-rw-r--r--kernel/sound/soc/codecs/sta32x.c1166
-rw-r--r--kernel/sound/soc/codecs/sta32x.h211
-rw-r--r--kernel/sound/soc/codecs/sta350.c1280
-rw-r--r--kernel/sound/soc/codecs/sta350.h238
-rw-r--r--kernel/sound/soc/codecs/sta529.c399
-rw-r--r--kernel/sound/soc/codecs/stac9766.c407
-rw-r--r--kernel/sound/soc/codecs/stac9766.h17
-rw-r--r--kernel/sound/soc/codecs/tas2552.c565
-rw-r--r--kernel/sound/soc/codecs/tas2552.h129
-rw-r--r--kernel/sound/soc/codecs/tas5086.c1009
-rw-r--r--kernel/sound/soc/codecs/tfa9879.c328
-rw-r--r--kernel/sound/soc/codecs/tfa9879.h202
-rw-r--r--kernel/sound/soc/codecs/tlv320aic23-i2c.c67
-rw-r--r--kernel/sound/soc/codecs/tlv320aic23-spi.c56
-rw-r--r--kernel/sound/soc/codecs/tlv320aic23.c617
-rw-r--r--kernel/sound/soc/codecs/tlv320aic23.h125
-rw-r--r--kernel/sound/soc/codecs/tlv320aic26.c382
-rw-r--r--kernel/sound/soc/codecs/tlv320aic26.h90
-rw-r--r--kernel/sound/soc/codecs/tlv320aic31xx.c1299
-rw-r--r--kernel/sound/soc/codecs/tlv320aic31xx.h259
-rw-r--r--kernel/sound/soc/codecs/tlv320aic32x4.c887
-rw-r--r--kernel/sound/soc/codecs/tlv320aic32x4.h149
-rw-r--r--kernel/sound/soc/codecs/tlv320aic3x.c1840
-rw-r--r--kernel/sound/soc/codecs/tlv320aic3x.h283
-rw-r--r--kernel/sound/soc/codecs/tlv320dac33.c1600
-rw-r--r--kernel/sound/soc/codecs/tlv320dac33.h264
-rw-r--r--kernel/sound/soc/codecs/tpa6130a2.c503
-rw-r--r--kernel/sound/soc/codecs/tpa6130a2.h62
-rw-r--r--kernel/sound/soc/codecs/ts3a227e.c349
-rw-r--r--kernel/sound/soc/codecs/ts3a227e.h17
-rw-r--r--kernel/sound/soc/codecs/twl4030.c2241
-rw-r--r--kernel/sound/soc/codecs/twl6040.c1189
-rw-r--r--kernel/sound/soc/codecs/twl6040.h44
-rw-r--r--kernel/sound/soc/codecs/uda134x.c617
-rw-r--r--kernel/sound/soc/codecs/uda134x.h34
-rw-r--r--kernel/sound/soc/codecs/uda1380.c847
-rw-r--r--kernel/sound/soc/codecs/uda1380.h79
-rw-r--r--kernel/sound/soc/codecs/wl1273.c523
-rw-r--r--kernel/sound/soc/codecs/wl1273.h30
-rw-r--r--kernel/sound/soc/codecs/wm0010.c1019
-rw-r--r--kernel/sound/soc/codecs/wm1250-ev1.c267
-rw-r--r--kernel/sound/soc/codecs/wm2000.c956
-rw-r--r--kernel/sound/soc/codecs/wm2000.h74
-rw-r--r--kernel/sound/soc/codecs/wm2200.c2510
-rw-r--r--kernel/sound/soc/codecs/wm2200.h3674
-rw-r--r--kernel/sound/soc/codecs/wm5100-tables.c1485
-rw-r--r--kernel/sound/soc/codecs/wm5100.c2735
-rw-r--r--kernel/sound/soc/codecs/wm5100.h5315
-rw-r--r--kernel/sound/soc/codecs/wm5102.c1977
-rw-r--r--kernel/sound/soc/codecs/wm5102.h23
-rw-r--r--kernel/sound/soc/codecs/wm5110.c1757
-rw-r--r--kernel/sound/soc/codecs/wm5110.h23
-rw-r--r--kernel/sound/soc/codecs/wm8350.c1632
-rw-r--r--kernel/sound/soc/codecs/wm8350.h29
-rw-r--r--kernel/sound/soc/codecs/wm8400.c1379
-rw-r--r--kernel/sound/soc/codecs/wm8400.h59
-rw-r--r--kernel/sound/soc/codecs/wm8510.c737
-rw-r--r--kernel/sound/soc/codecs/wm8510.h102
-rw-r--r--kernel/sound/soc/codecs/wm8523.c545
-rw-r--r--kernel/sound/soc/codecs/wm8523.h157
-rw-r--r--kernel/sound/soc/codecs/wm8580.c1016
-rw-r--r--kernel/sound/soc/codecs/wm8580.h35
-rw-r--r--kernel/sound/soc/codecs/wm8711.c525
-rw-r--r--kernel/sound/soc/codecs/wm8711.h39
-rw-r--r--kernel/sound/soc/codecs/wm8727.c88
-rw-r--r--kernel/sound/soc/codecs/wm8728.c366
-rw-r--r--kernel/sound/soc/codecs/wm8728.h21
-rw-r--r--kernel/sound/soc/codecs/wm8731.c835
-rw-r--r--kernel/sound/soc/codecs/wm8731.h39
-rw-r--r--kernel/sound/soc/codecs/wm8737.c753
-rw-r--r--kernel/sound/soc/codecs/wm8737.h322
-rw-r--r--kernel/sound/soc/codecs/wm8741.c643
-rw-r--r--kernel/sound/soc/codecs/wm8741.h211
-rw-r--r--kernel/sound/soc/codecs/wm8750.c873
-rw-r--r--kernel/sound/soc/codecs/wm8750.h60
-rw-r--r--kernel/sound/soc/codecs/wm8753.c1656
-rw-r--r--kernel/sound/soc/codecs/wm8753.h118
-rw-r--r--kernel/sound/soc/codecs/wm8770.c718
-rw-r--r--kernel/sound/soc/codecs/wm8770.h51
-rw-r--r--kernel/sound/soc/codecs/wm8776.c583
-rw-r--r--kernel/sound/soc/codecs/wm8776.h48
-rw-r--r--kernel/sound/soc/codecs/wm8782.c84
-rw-r--r--kernel/sound/soc/codecs/wm8804-i2c.c65
-rw-r--r--kernel/sound/soc/codecs/wm8804-spi.c57
-rw-r--r--kernel/sound/soc/codecs/wm8804.c731
-rw-r--r--kernel/sound/soc/codecs/wm8804.h73
-rw-r--r--kernel/sound/soc/codecs/wm8900.c1358
-rw-r--r--kernel/sound/soc/codecs/wm8900.h55
-rw-r--r--kernel/sound/soc/codecs/wm8903.c2210
-rw-r--r--kernel/sound/soc/codecs/wm8903.h1225
-rw-r--r--kernel/sound/soc/codecs/wm8904.c2308
-rw-r--r--kernel/sound/soc/codecs/wm8904.h1592
-rw-r--r--kernel/sound/soc/codecs/wm8940.c803
-rw-r--r--kernel/sound/soc/codecs/wm8940.h102
-rw-r--r--kernel/sound/soc/codecs/wm8955.c1024
-rw-r--r--kernel/sound/soc/codecs/wm8955.h486
-rw-r--r--kernel/sound/soc/codecs/wm8958-dsp2.c1031
-rw-r--r--kernel/sound/soc/codecs/wm8960.c1128
-rw-r--r--kernel/sound/soc/codecs/wm8960.h113
-rw-r--r--kernel/sound/soc/codecs/wm8961.c998
-rw-r--r--kernel/sound/soc/codecs/wm8961.h863
-rw-r--r--kernel/sound/soc/codecs/wm8962.c3897
-rw-r--r--kernel/sound/soc/codecs/wm8962.h3784
-rw-r--r--kernel/sound/soc/codecs/wm8971.c725
-rw-r--r--kernel/sound/soc/codecs/wm8971.h56
-rw-r--r--kernel/sound/soc/codecs/wm8974.c649
-rw-r--r--kernel/sound/soc/codecs/wm8974.h86
-rw-r--r--kernel/sound/soc/codecs/wm8978.c1087
-rw-r--r--kernel/sound/soc/codecs/wm8978.h85
-rw-r--r--kernel/sound/soc/codecs/wm8983.c1180
-rw-r--r--kernel/sound/soc/codecs/wm8983.h1029
-rw-r--r--kernel/sound/soc/codecs/wm8985.c1191
-rw-r--r--kernel/sound/soc/codecs/wm8985.h1045
-rw-r--r--kernel/sound/soc/codecs/wm8988.c966
-rw-r--r--kernel/sound/soc/codecs/wm8988.h57
-rw-r--r--kernel/sound/soc/codecs/wm8990.c1371
-rw-r--r--kernel/sound/soc/codecs/wm8990.h826
-rw-r--r--kernel/sound/soc/codecs/wm8991.c1378
-rw-r--r--kernel/sound/soc/codecs/wm8991.h819
-rw-r--r--kernel/sound/soc/codecs/wm8993.c1758
-rw-r--r--kernel/sound/soc/codecs/wm8993.h2138
-rw-r--r--kernel/sound/soc/codecs/wm8994.c4523
-rw-r--r--kernel/sound/soc/codecs/wm8994.h168
-rw-r--r--kernel/sound/soc/codecs/wm8995.c2346
-rw-r--r--kernel/sound/soc/codecs/wm8995.h4266
-rw-r--r--kernel/sound/soc/codecs/wm8996.c3111
-rw-r--r--kernel/sound/soc/codecs/wm8996.h3721
-rw-r--r--kernel/sound/soc/codecs/wm8997.c1181
-rw-r--r--kernel/sound/soc/codecs/wm8997.h23
-rw-r--r--kernel/sound/soc/codecs/wm9081.c1395
-rw-r--r--kernel/sound/soc/codecs/wm9081.h784
-rw-r--r--kernel/sound/soc/codecs/wm9090.c652
-rw-r--r--kernel/sound/soc/codecs/wm9090.h713
-rw-r--r--kernel/sound/soc/codecs/wm9705.c424
-rw-r--r--kernel/sound/soc/codecs/wm9705.h11
-rw-r--r--kernel/sound/soc/codecs/wm9712.c758
-rw-r--r--kernel/sound/soc/codecs/wm9712.h11
-rw-r--r--kernel/sound/soc/codecs/wm9713.c1318
-rw-r--r--kernel/sound/soc/codecs/wm9713.h50
-rw-r--r--kernel/sound/soc/codecs/wm_adsp.c1747
-rw-r--r--kernel/sound/soc/codecs/wm_adsp.h90
-rw-r--r--kernel/sound/soc/codecs/wm_hubs.c1311
-rw-r--r--kernel/sound/soc/codecs/wm_hubs.h74
-rw-r--r--kernel/sound/soc/codecs/wmfw.h133
310 files changed, 225209 insertions, 0 deletions
diff --git a/kernel/sound/soc/codecs/88pm860x-codec.c b/kernel/sound/soc/codecs/88pm860x-codec.c
new file mode 100644
index 000000000..a0f265327
--- /dev/null
+++ b/kernel/sound/soc/codecs/88pm860x-codec.c
@@ -0,0 +1,1437 @@
+/*
+ * 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>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/88pm860x.h>
+#include <linux/slab.h>
+#include <linux/delay.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 <sound/initval.h>
+#include <sound/jack.h>
+#include <trace/events/asoc.h>
+
+#include "88pm860x-codec.h"
+
+#define MAX_NAME_LEN 20
+#define REG_CACHE_SIZE 0x40
+#define REG_CACHE_BASE 0xb0
+
+/* Status Register 1 (0x01) */
+#define REG_STATUS_1 0x01
+#define MIC_STATUS (1 << 7)
+#define HOOK_STATUS (1 << 6)
+#define HEADSET_STATUS (1 << 5)
+
+/* Mic Detection Register (0x37) */
+#define REG_MIC_DET 0x37
+#define CONTINUOUS_POLLING (3 << 1)
+#define EN_MIC_DET (1 << 0)
+#define MICDET_MASK 0x07
+
+/* Headset Detection Register (0x38) */
+#define REG_HS_DET 0x38
+#define EN_HS_DET (1 << 0)
+
+/* Misc2 Register (0x42) */
+#define REG_MISC2 0x42
+#define AUDIO_PLL (1 << 5)
+#define AUDIO_SECTION_RESET (1 << 4)
+#define AUDIO_SECTION_ON (1 << 3)
+
+/* PCM Interface Register 2 (0xb1) */
+#define PCM_INF2_BCLK (1 << 6) /* Bit clock polarity */
+#define PCM_INF2_FS (1 << 5) /* Frame Sync polarity */
+#define PCM_INF2_MASTER (1 << 4) /* Master / Slave */
+#define PCM_INF2_18WL (1 << 3) /* 18 / 16 bits */
+#define PCM_GENERAL_I2S 0
+#define PCM_EXACT_I2S 1
+#define PCM_LEFT_I2S 2
+#define PCM_RIGHT_I2S 3
+#define PCM_SHORT_FS 4
+#define PCM_LONG_FS 5
+#define PCM_MODE_MASK 7
+
+/* I2S Interface Register 4 (0xbe) */
+#define I2S_EQU_BYP (1 << 6)
+
+/* DAC Offset Register (0xcb) */
+#define DAC_MUTE (1 << 7)
+#define MUTE_LEFT (1 << 6)
+#define MUTE_RIGHT (1 << 2)
+
+/* ADC Analog Register 1 (0xd0) */
+#define REG_ADC_ANA_1 0xd0
+#define MIC1BIAS_MASK 0x60
+
+/* Earpiece/Speaker Control Register 2 (0xda) */
+#define REG_EAR2 0xda
+#define RSYNC_CHANGE (1 << 2)
+
+/* Audio Supplies Register 2 (0xdc) */
+#define REG_SUPPLIES2 0xdc
+#define LDO15_READY (1 << 4)
+#define LDO15_EN (1 << 3)
+#define CPUMP_READY (1 << 2)
+#define CPUMP_EN (1 << 1)
+#define AUDIO_EN (1 << 0)
+#define SUPPLY_MASK (LDO15_EN | CPUMP_EN | AUDIO_EN)
+
+/* Audio Enable Register 1 (0xdd) */
+#define ADC_MOD_RIGHT (1 << 1)
+#define ADC_MOD_LEFT (1 << 0)
+
+/* Audio Enable Register 2 (0xde) */
+#define ADC_LEFT (1 << 5)
+#define ADC_RIGHT (1 << 4)
+
+/* DAC Enable Register 2 (0xe1) */
+#define DAC_LEFT (1 << 5)
+#define DAC_RIGHT (1 << 4)
+#define MODULATOR (1 << 3)
+
+/* Shorts Register (0xeb) */
+#define REG_SHORTS 0xeb
+#define CLR_SHORT_LO2 (1 << 7)
+#define SHORT_LO2 (1 << 6)
+#define CLR_SHORT_LO1 (1 << 5)
+#define SHORT_LO1 (1 << 4)
+#define CLR_SHORT_HS2 (1 << 3)
+#define SHORT_HS2 (1 << 2)
+#define CLR_SHORT_HS1 (1 << 1)
+#define SHORT_HS1 (1 << 0)
+
+/*
+ * This widget should be just after DAC & PGA in DAPM power-on sequence and
+ * before DAC & PGA in DAPM power-off sequence.
+ */
+#define PM860X_DAPM_OUTPUT(wname, wevent) \
+ SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, 0, 0, NULL, 0, wevent, \
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD)
+
+struct pm860x_det {
+ struct snd_soc_jack *hp_jack;
+ struct snd_soc_jack *mic_jack;
+ int hp_det;
+ int mic_det;
+ int hook_det;
+ int hs_shrt;
+ int lo_shrt;
+};
+
+struct pm860x_priv {
+ unsigned int sysclk;
+ unsigned int pcmclk;
+ unsigned int dir;
+ unsigned int filter;
+ struct snd_soc_codec *codec;
+ 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];
+};
+
+/* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */
+static const DECLARE_TLV_DB_SCALE(dpga_tlv, -9450, 150, 1);
+
+/* -9dB to 0db in 3dB steps */
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -900, 300, 0);
+
+/* {-23, -17, -13.5, -11, -9, -6, -3, 0}dB */
+static const unsigned int mic_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 0, TLV_DB_SCALE_ITEM(-2300, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(-1700, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(-1350, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(-1100, 0, 0),
+ 4, 7, TLV_DB_SCALE_ITEM(-900, 300, 0),
+};
+
+/* {0, 0, 0, -6, 0, 6, 12, 18}dB */
+static const unsigned int aux_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 2, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 3, 7, TLV_DB_SCALE_ITEM(-600, 600, 0),
+};
+
+/* {-16, -13, -10, -7, -5.2, -3,3, -2.2, 0}dB, mute instead of -16dB */
+static const unsigned int out_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 3, TLV_DB_SCALE_ITEM(-1600, 300, 1),
+ 4, 4, TLV_DB_SCALE_ITEM(-520, 0, 0),
+ 5, 5, TLV_DB_SCALE_ITEM(-330, 0, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(-220, 220, 0),
+};
+
+static const unsigned int st_tlv[] = {
+ TLV_DB_RANGE_HEAD(8),
+ 0, 1, TLV_DB_SCALE_ITEM(-12041, 602, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(-11087, 250, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(-10643, 158, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(-10351, 116, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(-10133, 92, 0),
+ 10, 13, TLV_DB_SCALE_ITEM(-9958, 70, 0),
+ 14, 17, TLV_DB_SCALE_ITEM(-9689, 53, 0),
+ 18, 271, TLV_DB_SCALE_ITEM(-9484, 37, 0),
+};
+
+/* Sidetone Gain = M * 2^(-5-N) */
+struct st_gain {
+ unsigned int db;
+ unsigned int m;
+ unsigned int n;
+};
+
+static struct st_gain st_table[] = {
+ {-12041, 1, 15}, {-11439, 1, 14}, {-11087, 3, 15}, {-10837, 1, 13},
+ {-10643, 5, 15}, {-10485, 3, 14}, {-10351, 7, 15}, {-10235, 1, 12},
+ {-10133, 9, 15}, {-10041, 5, 14}, { -9958, 11, 15}, { -9883, 3, 13},
+ { -9813, 13, 15}, { -9749, 7, 14}, { -9689, 15, 15}, { -9633, 1, 11},
+ { -9580, 17, 15}, { -9531, 9, 14}, { -9484, 19, 15}, { -9439, 5, 13},
+ { -9397, 21, 15}, { -9356, 11, 14}, { -9318, 23, 15}, { -9281, 3, 12},
+ { -9245, 25, 15}, { -9211, 13, 14}, { -9178, 27, 15}, { -9147, 7, 13},
+ { -9116, 29, 15}, { -9087, 15, 14}, { -9058, 31, 15}, { -9031, 1, 10},
+ { -8978, 17, 14}, { -8929, 9, 13}, { -8882, 19, 14}, { -8837, 5, 12},
+ { -8795, 21, 14}, { -8754, 11, 13}, { -8716, 23, 14}, { -8679, 3, 11},
+ { -8643, 25, 14}, { -8609, 13, 13}, { -8576, 27, 14}, { -8545, 7, 12},
+ { -8514, 29, 14}, { -8485, 15, 13}, { -8456, 31, 14}, { -8429, 1, 9},
+ { -8376, 17, 13}, { -8327, 9, 12}, { -8280, 19, 13}, { -8235, 5, 11},
+ { -8193, 21, 13}, { -8152, 11, 12}, { -8114, 23, 13}, { -8077, 3, 10},
+ { -8041, 25, 13}, { -8007, 13, 12}, { -7974, 27, 13}, { -7943, 7, 11},
+ { -7912, 29, 13}, { -7883, 15, 12}, { -7854, 31, 13}, { -7827, 1, 8},
+ { -7774, 17, 12}, { -7724, 9, 11}, { -7678, 19, 12}, { -7633, 5, 10},
+ { -7591, 21, 12}, { -7550, 11, 11}, { -7512, 23, 12}, { -7475, 3, 9},
+ { -7439, 25, 12}, { -7405, 13, 11}, { -7372, 27, 12}, { -7341, 7, 10},
+ { -7310, 29, 12}, { -7281, 15, 11}, { -7252, 31, 12}, { -7225, 1, 7},
+ { -7172, 17, 11}, { -7122, 9, 10}, { -7075, 19, 11}, { -7031, 5, 9},
+ { -6989, 21, 11}, { -6948, 11, 10}, { -6910, 23, 11}, { -6873, 3, 8},
+ { -6837, 25, 11}, { -6803, 13, 10}, { -6770, 27, 11}, { -6739, 7, 9},
+ { -6708, 29, 11}, { -6679, 15, 10}, { -6650, 31, 11}, { -6623, 1, 6},
+ { -6570, 17, 10}, { -6520, 9, 9}, { -6473, 19, 10}, { -6429, 5, 8},
+ { -6386, 21, 10}, { -6346, 11, 9}, { -6307, 23, 10}, { -6270, 3, 7},
+ { -6235, 25, 10}, { -6201, 13, 9}, { -6168, 27, 10}, { -6137, 7, 8},
+ { -6106, 29, 10}, { -6077, 15, 9}, { -6048, 31, 10}, { -6021, 1, 5},
+ { -5968, 17, 9}, { -5918, 9, 8}, { -5871, 19, 9}, { -5827, 5, 7},
+ { -5784, 21, 9}, { -5744, 11, 8}, { -5705, 23, 9}, { -5668, 3, 6},
+ { -5633, 25, 9}, { -5599, 13, 8}, { -5566, 27, 9}, { -5535, 7, 7},
+ { -5504, 29, 9}, { -5475, 15, 8}, { -5446, 31, 9}, { -5419, 1, 4},
+ { -5366, 17, 8}, { -5316, 9, 7}, { -5269, 19, 8}, { -5225, 5, 6},
+ { -5182, 21, 8}, { -5142, 11, 7}, { -5103, 23, 8}, { -5066, 3, 5},
+ { -5031, 25, 8}, { -4997, 13, 7}, { -4964, 27, 8}, { -4932, 7, 6},
+ { -4902, 29, 8}, { -4873, 15, 7}, { -4844, 31, 8}, { -4816, 1, 3},
+ { -4764, 17, 7}, { -4714, 9, 6}, { -4667, 19, 7}, { -4623, 5, 5},
+ { -4580, 21, 7}, { -4540, 11, 6}, { -4501, 23, 7}, { -4464, 3, 4},
+ { -4429, 25, 7}, { -4395, 13, 6}, { -4362, 27, 7}, { -4330, 7, 5},
+ { -4300, 29, 7}, { -4270, 15, 6}, { -4242, 31, 7}, { -4214, 1, 2},
+ { -4162, 17, 6}, { -4112, 9, 5}, { -4065, 19, 6}, { -4021, 5, 4},
+ { -3978, 21, 6}, { -3938, 11, 5}, { -3899, 23, 6}, { -3862, 3, 3},
+ { -3827, 25, 6}, { -3793, 13, 5}, { -3760, 27, 6}, { -3728, 7, 4},
+ { -3698, 29, 6}, { -3668, 15, 5}, { -3640, 31, 6}, { -3612, 1, 1},
+ { -3560, 17, 5}, { -3510, 9, 4}, { -3463, 19, 5}, { -3419, 5, 3},
+ { -3376, 21, 5}, { -3336, 11, 4}, { -3297, 23, 5}, { -3260, 3, 2},
+ { -3225, 25, 5}, { -3191, 13, 4}, { -3158, 27, 5}, { -3126, 7, 3},
+ { -3096, 29, 5}, { -3066, 15, 4}, { -3038, 31, 5}, { -3010, 1, 0},
+ { -2958, 17, 4}, { -2908, 9, 3}, { -2861, 19, 4}, { -2816, 5, 2},
+ { -2774, 21, 4}, { -2734, 11, 3}, { -2695, 23, 4}, { -2658, 3, 1},
+ { -2623, 25, 4}, { -2589, 13, 3}, { -2556, 27, 4}, { -2524, 7, 2},
+ { -2494, 29, 4}, { -2464, 15, 3}, { -2436, 31, 4}, { -2408, 2, 0},
+ { -2356, 17, 3}, { -2306, 9, 2}, { -2259, 19, 3}, { -2214, 5, 1},
+ { -2172, 21, 3}, { -2132, 11, 2}, { -2093, 23, 3}, { -2056, 3, 0},
+ { -2021, 25, 3}, { -1987, 13, 2}, { -1954, 27, 3}, { -1922, 7, 1},
+ { -1892, 29, 3}, { -1862, 15, 2}, { -1834, 31, 3}, { -1806, 4, 0},
+ { -1754, 17, 2}, { -1704, 9, 1}, { -1657, 19, 2}, { -1612, 5, 0},
+ { -1570, 21, 2}, { -1530, 11, 1}, { -1491, 23, 2}, { -1454, 6, 0},
+ { -1419, 25, 2}, { -1384, 13, 1}, { -1352, 27, 2}, { -1320, 7, 0},
+ { -1290, 29, 2}, { -1260, 15, 1}, { -1232, 31, 2}, { -1204, 8, 0},
+ { -1151, 17, 1}, { -1102, 9, 0}, { -1055, 19, 1}, { -1010, 10, 0},
+ { -968, 21, 1}, { -928, 11, 0}, { -889, 23, 1}, { -852, 12, 0},
+ { -816, 25, 1}, { -782, 13, 0}, { -750, 27, 1}, { -718, 14, 0},
+ { -688, 29, 1}, { -658, 15, 0}, { -630, 31, 1}, { -602, 16, 0},
+ { -549, 17, 0}, { -500, 18, 0}, { -453, 19, 0}, { -408, 20, 0},
+ { -366, 21, 0}, { -325, 22, 0}, { -287, 23, 0}, { -250, 24, 0},
+ { -214, 25, 0}, { -180, 26, 0}, { -148, 27, 0}, { -116, 28, 0},
+ { -86, 29, 0}, { -56, 30, 0}, { -28, 31, 0}, { 0, 0, 0},
+};
+
+static int snd_soc_get_volsw_2r_st(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(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;
+
+ for (i = 0; i < ARRAY_SIZE(st_table); i++) {
+ if ((st_table[i].m == val[0]) && (st_table[i].n == val[1]))
+ ucontrol->value.integer.value[0] = i;
+ if ((st_table[i].m == val2[0]) && (st_table[i].n == val2[1]))
+ ucontrol->value.integer.value[1] = i;
+ }
+ return 0;
+}
+
+static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ int err;
+ unsigned int val, val2;
+
+ val = ucontrol->value.integer.value[0];
+ val2 = ucontrol->value.integer.value[1];
+
+ if (val >= ARRAY_SIZE(st_table) || val2 >= ARRAY_SIZE(st_table))
+ return -EINVAL;
+
+ err = snd_soc_update_bits(codec, reg, 0x3f, st_table[val].m);
+ if (err < 0)
+ return err;
+ err = snd_soc_update_bits(codec, 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);
+ if (err < 0)
+ return err;
+ err = snd_soc_update_bits(codec, PM860X_SIDETONE_SHIFT, 0x0f,
+ st_table[val2].n);
+ return err;
+}
+
+static int snd_soc_get_volsw_2r_out(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(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;
+ ucontrol->value.integer.value[0] = (max - val) & mask;
+ ucontrol->value.integer.value[1] = (max - val2) & mask;
+
+ return 0;
+}
+
+static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ int err;
+ unsigned int val, val2, val_mask;
+
+ val_mask = mask << shift;
+ val = ((max - ucontrol->value.integer.value[0]) & mask);
+ val2 = ((max - ucontrol->value.integer.value[1]) & mask);
+
+ val = val << shift;
+ val2 = val2 << shift;
+
+ err = snd_soc_update_bits(codec, reg, val_mask, val);
+ if (err < 0)
+ return err;
+
+ err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+ return err;
+}
+
+/* DAPM Widget Events */
+/*
+ * A lot registers are belong to RSYNC domain. It requires enabling RSYNC bit
+ * after updating these registers. Otherwise, these updated registers won't
+ * be effective.
+ */
+static int pm860x_rsync_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ /*
+ * In order to avoid current on the load, mute power-on and power-off
+ * should be transients.
+ * Unmute by DAC_MUTE. It should be unmuted when DAPM sequence is
+ * finished.
+ */
+ snd_soc_update_bits(codec, PM860X_DAC_OFFSET, DAC_MUTE, 0);
+ snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ RSYNC_CHANGE, RSYNC_CHANGE);
+ return 0;
+}
+
+static int pm860x_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ unsigned int dac = 0;
+ int data;
+
+ if (!strcmp(w->name, "Left DAC"))
+ dac = DAC_LEFT;
+ if (!strcmp(w->name, "Right DAC"))
+ dac = DAC_RIGHT;
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (dac) {
+ /* Auto mute in power-on sequence. */
+ dac |= MODULATOR;
+ snd_soc_update_bits(codec, PM860X_DAC_OFFSET,
+ DAC_MUTE, DAC_MUTE);
+ snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ RSYNC_CHANGE, RSYNC_CHANGE);
+ /* update dac */
+ snd_soc_update_bits(codec, 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,
+ DAC_MUTE, DAC_MUTE);
+ snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ RSYNC_CHANGE, RSYNC_CHANGE);
+ /* update dac */
+ data = snd_soc_read(codec, PM860X_DAC_EN_2);
+ data &= ~dac;
+ if (!(data & (DAC_LEFT | DAC_RIGHT)))
+ data &= ~MODULATOR;
+ snd_soc_write(codec, PM860X_DAC_EN_2, data);
+ }
+ break;
+ }
+ return 0;
+}
+
+static const char *pm860x_opamp_texts[] = {"-50%", "-25%", "0%", "75%"};
+
+static const char *pm860x_pa_texts[] = {"-33%", "0%", "33%", "66%"};
+
+static SOC_ENUM_SINGLE_DECL(pm860x_hs1_opamp_enum,
+ PM860X_HS1_CTRL, 5, pm860x_opamp_texts);
+
+static SOC_ENUM_SINGLE_DECL(pm860x_hs2_opamp_enum,
+ PM860X_HS2_CTRL, 5, pm860x_opamp_texts);
+
+static SOC_ENUM_SINGLE_DECL(pm860x_hs1_pa_enum,
+ PM860X_HS1_CTRL, 3, pm860x_pa_texts);
+
+static SOC_ENUM_SINGLE_DECL(pm860x_hs2_pa_enum,
+ PM860X_HS2_CTRL, 3, pm860x_pa_texts);
+
+static SOC_ENUM_SINGLE_DECL(pm860x_lo1_opamp_enum,
+ PM860X_LO1_CTRL, 5, pm860x_opamp_texts);
+
+static SOC_ENUM_SINGLE_DECL(pm860x_lo2_opamp_enum,
+ PM860X_LO2_CTRL, 5, pm860x_opamp_texts);
+
+static SOC_ENUM_SINGLE_DECL(pm860x_lo1_pa_enum,
+ PM860X_LO1_CTRL, 3, pm860x_pa_texts);
+
+static SOC_ENUM_SINGLE_DECL(pm860x_lo2_pa_enum,
+ PM860X_LO2_CTRL, 3, pm860x_pa_texts);
+
+static SOC_ENUM_SINGLE_DECL(pm860x_spk_pa_enum,
+ PM860X_EAR_CTRL_1, 5, pm860x_pa_texts);
+
+static SOC_ENUM_SINGLE_DECL(pm860x_ear_pa_enum,
+ PM860X_EAR_CTRL_2, 0, pm860x_pa_texts);
+
+static SOC_ENUM_SINGLE_DECL(pm860x_spk_ear_opamp_enum,
+ PM860X_EAR_CTRL_1, 3, pm860x_opamp_texts);
+
+static const struct snd_kcontrol_new pm860x_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("ADC Capture Volume", PM860X_ADC_ANA_2,
+ PM860X_ADC_ANA_3, 6, 3, 0, adc_tlv),
+ SOC_DOUBLE_TLV("AUX Capture Volume", PM860X_ADC_ANA_3, 0, 3, 7, 0,
+ aux_tlv),
+ SOC_SINGLE_TLV("MIC1 Capture Volume", PM860X_ADC_ANA_2, 0, 7, 0,
+ mic_tlv),
+ SOC_SINGLE_TLV("MIC3 Capture Volume", PM860X_ADC_ANA_2, 3, 7, 0,
+ mic_tlv),
+ SOC_DOUBLE_R_EXT_TLV("Sidetone Volume", PM860X_SIDETONE_L_GAIN,
+ PM860X_SIDETONE_R_GAIN, 0, ARRAY_SIZE(st_table)-1,
+ 0, snd_soc_get_volsw_2r_st,
+ snd_soc_put_volsw_2r_st, st_tlv),
+ SOC_SINGLE_TLV("Speaker Playback Volume", PM860X_EAR_CTRL_1,
+ 0, 7, 0, out_tlv),
+ SOC_DOUBLE_R_TLV("Line Playback Volume", PM860X_LO1_CTRL,
+ PM860X_LO2_CTRL, 0, 7, 0, out_tlv),
+ SOC_DOUBLE_R_TLV("Headset Playback Volume", PM860X_HS1_CTRL,
+ PM860X_HS2_CTRL, 0, 7, 0, out_tlv),
+ SOC_DOUBLE_R_EXT_TLV("Hifi Left Playback Volume",
+ PM860X_HIFIL_GAIN_LEFT,
+ PM860X_HIFIL_GAIN_RIGHT, 0, 63, 0,
+ snd_soc_get_volsw_2r_out,
+ snd_soc_put_volsw_2r_out, dpga_tlv),
+ SOC_DOUBLE_R_EXT_TLV("Hifi Right Playback Volume",
+ PM860X_HIFIR_GAIN_LEFT,
+ PM860X_HIFIR_GAIN_RIGHT, 0, 63, 0,
+ snd_soc_get_volsw_2r_out,
+ snd_soc_put_volsw_2r_out, dpga_tlv),
+ SOC_DOUBLE_R_EXT_TLV("Lofi Playback Volume", PM860X_LOFI_GAIN_LEFT,
+ PM860X_LOFI_GAIN_RIGHT, 0, 63, 0,
+ snd_soc_get_volsw_2r_out,
+ snd_soc_put_volsw_2r_out, dpga_tlv),
+ SOC_ENUM("Headset1 Operational Amplifier Current",
+ pm860x_hs1_opamp_enum),
+ SOC_ENUM("Headset2 Operational Amplifier Current",
+ pm860x_hs2_opamp_enum),
+ SOC_ENUM("Headset1 Amplifier Current", pm860x_hs1_pa_enum),
+ SOC_ENUM("Headset2 Amplifier Current", pm860x_hs2_pa_enum),
+ SOC_ENUM("Lineout1 Operational Amplifier Current",
+ pm860x_lo1_opamp_enum),
+ SOC_ENUM("Lineout2 Operational Amplifier Current",
+ pm860x_lo2_opamp_enum),
+ SOC_ENUM("Lineout1 Amplifier Current", pm860x_lo1_pa_enum),
+ SOC_ENUM("Lineout2 Amplifier Current", pm860x_lo2_pa_enum),
+ SOC_ENUM("Speaker Operational Amplifier Current",
+ pm860x_spk_ear_opamp_enum),
+ SOC_ENUM("Speaker Amplifier Current", pm860x_spk_pa_enum),
+ SOC_ENUM("Earpiece Amplifier Current", pm860x_ear_pa_enum),
+};
+
+/*
+ * DAPM Controls
+ */
+
+/* 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);
+
+/* AUX2 Switch */
+static const struct snd_kcontrol_new aux2_switch_controls =
+ SOC_DAPM_SINGLE("Switch", PM860X_ANA_TO_ANA, 5, 1, 0);
+
+/* Left Ex. PA Switch */
+static const struct snd_kcontrol_new lepa_switch_controls =
+ SOC_DAPM_SINGLE("Switch", PM860X_DAC_EN_2, 2, 1, 0);
+
+/* Right Ex. PA Switch */
+static const struct snd_kcontrol_new repa_switch_controls =
+ SOC_DAPM_SINGLE("Switch", PM860X_DAC_EN_2, 1, 1, 0);
+
+/* 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",
+};
+
+static SOC_ENUM_SINGLE_DECL(i2s_din_enum,
+ PM860X_I2S_IFACE_3, 1, i2s_din_text);
+
+static const struct snd_kcontrol_new i2s_din_mux =
+ SOC_DAPM_ENUM("I2S DIN Mux", i2s_din_enum);
+
+/* I2S Mic Mux / Mux8 */
+static const char *i2s_mic_text[] = {
+ "Ex PA", "ADC",
+};
+
+static SOC_ENUM_SINGLE_DECL(i2s_mic_enum,
+ PM860X_I2S_IFACE_3, 4, i2s_mic_text);
+
+static const struct snd_kcontrol_new i2s_mic_mux =
+ SOC_DAPM_ENUM("I2S Mic Mux", i2s_mic_enum);
+
+/* ADCL Mux / Mux2 */
+static const char *adcl_text[] = {
+ "ADCR", "ADCL",
+};
+
+static SOC_ENUM_SINGLE_DECL(adcl_enum,
+ PM860X_PCM_IFACE_3, 4, adcl_text);
+
+static const struct snd_kcontrol_new adcl_mux =
+ SOC_DAPM_ENUM("ADC Left Mux", adcl_enum);
+
+/* ADCR Mux / Mux3 */
+static const char *adcr_text[] = {
+ "ADCL", "ADCR",
+};
+
+static SOC_ENUM_SINGLE_DECL(adcr_enum,
+ PM860X_PCM_IFACE_3, 2, adcr_text);
+
+static const struct snd_kcontrol_new adcr_mux =
+ SOC_DAPM_ENUM("ADC Right Mux", adcr_enum);
+
+/* ADCR EC Mux / Mux6 */
+static const char *adcr_ec_text[] = {
+ "ADCR", "EC",
+};
+
+static SOC_ENUM_SINGLE_DECL(adcr_ec_enum,
+ PM860X_ADC_EN_2, 3, adcr_ec_text);
+
+static const struct snd_kcontrol_new adcr_ec_mux =
+ SOC_DAPM_ENUM("ADCR EC Mux", adcr_ec_enum);
+
+/* EC Mux / Mux4 */
+static const char *ec_text[] = {
+ "Left", "Right", "Left + Right",
+};
+
+static SOC_ENUM_SINGLE_DECL(ec_enum,
+ PM860X_EC_PATH, 1, ec_text);
+
+static const struct snd_kcontrol_new ec_mux =
+ SOC_DAPM_ENUM("EC Mux", ec_enum);
+
+static const char *dac_text[] = {
+ "No input", "Right", "Left", "No input",
+};
+
+/* DAC Headset 1 Mux / Mux10 */
+static SOC_ENUM_SINGLE_DECL(dac_hs1_enum,
+ PM860X_ANA_INPUT_SEL_1, 0, dac_text);
+
+static const struct snd_kcontrol_new dac_hs1_mux =
+ SOC_DAPM_ENUM("DAC HS1 Mux", dac_hs1_enum);
+
+/* DAC Headset 2 Mux / Mux11 */
+static SOC_ENUM_SINGLE_DECL(dac_hs2_enum,
+ PM860X_ANA_INPUT_SEL_1, 2, dac_text);
+
+static const struct snd_kcontrol_new dac_hs2_mux =
+ SOC_DAPM_ENUM("DAC HS2 Mux", dac_hs2_enum);
+
+/* DAC Lineout 1 Mux / Mux12 */
+static SOC_ENUM_SINGLE_DECL(dac_lo1_enum,
+ PM860X_ANA_INPUT_SEL_1, 4, dac_text);
+
+static const struct snd_kcontrol_new dac_lo1_mux =
+ SOC_DAPM_ENUM("DAC LO1 Mux", dac_lo1_enum);
+
+/* DAC Lineout 2 Mux / Mux13 */
+static SOC_ENUM_SINGLE_DECL(dac_lo2_enum,
+ PM860X_ANA_INPUT_SEL_1, 6, dac_text);
+
+static const struct snd_kcontrol_new dac_lo2_mux =
+ SOC_DAPM_ENUM("DAC LO2 Mux", dac_lo2_enum);
+
+/* DAC Spearker Earphone Mux / Mux14 */
+static SOC_ENUM_SINGLE_DECL(dac_spk_ear_enum,
+ PM860X_ANA_INPUT_SEL_2, 0, dac_text);
+
+static const struct snd_kcontrol_new dac_spk_ear_mux =
+ SOC_DAPM_ENUM("DAC SP Mux", dac_spk_ear_enum);
+
+/* Headset 1 Mux / Mux15 */
+static const char *in_text[] = {
+ "Digital", "Analog",
+};
+
+static SOC_ENUM_SINGLE_DECL(hs1_enum,
+ PM860X_ANA_TO_ANA, 0, in_text);
+
+static const struct snd_kcontrol_new hs1_mux =
+ SOC_DAPM_ENUM("Headset1 Mux", hs1_enum);
+
+/* Headset 2 Mux / Mux16 */
+static SOC_ENUM_SINGLE_DECL(hs2_enum,
+ PM860X_ANA_TO_ANA, 1, in_text);
+
+static const struct snd_kcontrol_new hs2_mux =
+ SOC_DAPM_ENUM("Headset2 Mux", hs2_enum);
+
+/* Lineout 1 Mux / Mux17 */
+static SOC_ENUM_SINGLE_DECL(lo1_enum,
+ PM860X_ANA_TO_ANA, 2, in_text);
+
+static const struct snd_kcontrol_new lo1_mux =
+ SOC_DAPM_ENUM("Lineout1 Mux", lo1_enum);
+
+/* Lineout 2 Mux / Mux18 */
+static SOC_ENUM_SINGLE_DECL(lo2_enum,
+ PM860X_ANA_TO_ANA, 3, in_text);
+
+static const struct snd_kcontrol_new lo2_mux =
+ SOC_DAPM_ENUM("Lineout2 Mux", lo2_enum);
+
+/* Speaker Earpiece Demux */
+static const char *spk_text[] = {
+ "Earpiece", "Speaker",
+};
+
+static SOC_ENUM_SINGLE_DECL(spk_enum,
+ PM860X_ANA_TO_ANA, 6, spk_text);
+
+static const struct snd_kcontrol_new spk_demux =
+ SOC_DAPM_ENUM("Speaker Earpiece Demux", spk_enum);
+
+/* MIC Mux / Mux1 */
+static const char *mic_text[] = {
+ "Mic 1", "Mic 2",
+};
+
+static SOC_ENUM_SINGLE_DECL(mic_enum,
+ PM860X_ADC_ANA_4, 4, mic_text);
+
+static const struct snd_kcontrol_new mic_mux =
+ SOC_DAPM_ENUM("MIC Mux", mic_enum);
+
+static const struct snd_soc_dapm_widget pm860x_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("PCM SDI", "PCM Playback", 0,
+ PM860X_ADC_EN_2, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PCM SDO", "PCM Capture", 0,
+ PM860X_PCM_IFACE_3, 1, 1),
+
+
+ SND_SOC_DAPM_AIF_IN("I2S DIN", "I2S Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S DIN1", "I2S Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("I2S DOUT", "I2S Capture", 0,
+ PM860X_I2S_IFACE_3, 5, 1),
+ SND_SOC_DAPM_SUPPLY("I2S CLK", PM860X_DAC_EN_2, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("I2S Mic Mux", SND_SOC_NOPM, 0, 0, &i2s_mic_mux),
+ SND_SOC_DAPM_MUX("ADC Left Mux", SND_SOC_NOPM, 0, 0, &adcl_mux),
+ SND_SOC_DAPM_MUX("ADC Right Mux", SND_SOC_NOPM, 0, 0, &adcr_mux),
+ SND_SOC_DAPM_MUX("EC Mux", SND_SOC_NOPM, 0, 0, &ec_mux),
+ SND_SOC_DAPM_MUX("ADCR EC Mux", SND_SOC_NOPM, 0, 0, &adcr_ec_mux),
+ SND_SOC_DAPM_SWITCH("Left EPA", SND_SOC_NOPM, 0, 0,
+ &lepa_switch_controls),
+ SND_SOC_DAPM_SWITCH("Right EPA", SND_SOC_NOPM, 0, 0,
+ &repa_switch_controls),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "Left ADC MOD", PM860X_ADC_EN_1,
+ 0, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "Right ADC MOD", PM860X_ADC_EN_1,
+ 1, 1, 1, 0),
+ SND_SOC_DAPM_ADC("Left ADC", NULL, PM860X_ADC_EN_2, 5, 0),
+ SND_SOC_DAPM_ADC("Right ADC", NULL, PM860X_ADC_EN_2, 4, 0),
+
+ SND_SOC_DAPM_SWITCH("AUX1 Switch", SND_SOC_NOPM, 0, 0,
+ &aux1_switch_controls),
+ SND_SOC_DAPM_SWITCH("AUX2 Switch", SND_SOC_NOPM, 0, 0,
+ &aux2_switch_controls),
+
+ SND_SOC_DAPM_MUX("MIC Mux", SND_SOC_NOPM, 0, 0, &mic_mux),
+ SND_SOC_DAPM_MICBIAS("Mic1 Bias", PM860X_ADC_ANA_1, 2, 0),
+ SND_SOC_DAPM_MICBIAS("Mic3 Bias", PM860X_ADC_ANA_1, 7, 0),
+ SND_SOC_DAPM_PGA("MIC1 Volume", PM860X_ADC_EN_1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC3 Volume", PM860X_ADC_EN_1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX1 Volume", PM860X_ADC_EN_1, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX2 Volume", PM860X_ADC_EN_1, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Sidetone PGA", PM860X_ADC_EN_2, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Lofi PGA", PM860X_ADC_EN_2, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("AUX1"),
+ SND_SOC_DAPM_INPUT("AUX2"),
+ SND_SOC_DAPM_INPUT("MIC1P"),
+ SND_SOC_DAPM_INPUT("MIC1N"),
+ SND_SOC_DAPM_INPUT("MIC2P"),
+ SND_SOC_DAPM_INPUT("MIC2N"),
+ SND_SOC_DAPM_INPUT("MIC3P"),
+ SND_SOC_DAPM_INPUT("MIC3N"),
+
+ SND_SOC_DAPM_DAC_E("Left DAC", NULL, SND_SOC_NOPM, 0, 0,
+ pm860x_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("Right DAC", NULL, SND_SOC_NOPM, 0, 0,
+ pm860x_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX("I2S DIN Mux", SND_SOC_NOPM, 0, 0, &i2s_din_mux),
+ SND_SOC_DAPM_MUX("DAC HS1 Mux", SND_SOC_NOPM, 0, 0, &dac_hs1_mux),
+ SND_SOC_DAPM_MUX("DAC HS2 Mux", SND_SOC_NOPM, 0, 0, &dac_hs2_mux),
+ SND_SOC_DAPM_MUX("DAC LO1 Mux", SND_SOC_NOPM, 0, 0, &dac_lo1_mux),
+ SND_SOC_DAPM_MUX("DAC LO2 Mux", SND_SOC_NOPM, 0, 0, &dac_lo2_mux),
+ SND_SOC_DAPM_MUX("DAC SP Mux", SND_SOC_NOPM, 0, 0, &dac_spk_ear_mux),
+ SND_SOC_DAPM_MUX("Headset1 Mux", SND_SOC_NOPM, 0, 0, &hs1_mux),
+ SND_SOC_DAPM_MUX("Headset2 Mux", SND_SOC_NOPM, 0, 0, &hs2_mux),
+ SND_SOC_DAPM_MUX("Lineout1 Mux", SND_SOC_NOPM, 0, 0, &lo1_mux),
+ SND_SOC_DAPM_MUX("Lineout2 Mux", SND_SOC_NOPM, 0, 0, &lo2_mux),
+ SND_SOC_DAPM_MUX("Speaker Earpiece Demux", SND_SOC_NOPM, 0, 0,
+ &spk_demux),
+
+
+ SND_SOC_DAPM_PGA("Headset1 PGA", PM860X_DAC_EN_1, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Headset2 PGA", PM860X_DAC_EN_1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("HS1"),
+ SND_SOC_DAPM_OUTPUT("HS2"),
+ SND_SOC_DAPM_PGA("Lineout1 PGA", PM860X_DAC_EN_1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Lineout2 PGA", PM860X_DAC_EN_1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("LINEOUT1"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT2"),
+ SND_SOC_DAPM_PGA("Earpiece PGA", PM860X_DAC_EN_1, 4, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("EARP"),
+ SND_SOC_DAPM_OUTPUT("EARN"),
+ SND_SOC_DAPM_PGA("Speaker PGA", PM860X_DAC_EN_1, 5, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("LSP"),
+ SND_SOC_DAPM_OUTPUT("LSN"),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "VCODEC", PM860X_AUDIO_SUPPLIES_2,
+ 0, SUPPLY_MASK, SUPPLY_MASK, 0),
+
+ PM860X_DAPM_OUTPUT("RSYNC", pm860x_rsync_event),
+};
+
+static const struct snd_soc_dapm_route pm860x_dapm_routes[] = {
+ /* supply */
+ {"Left DAC", NULL, "VCODEC"},
+ {"Right DAC", NULL, "VCODEC"},
+ {"Left ADC", NULL, "VCODEC"},
+ {"Right ADC", NULL, "VCODEC"},
+ {"Left ADC", NULL, "Left ADC MOD"},
+ {"Right ADC", NULL, "Right ADC MOD"},
+
+ /* I2S Clock */
+ {"I2S DIN", NULL, "I2S CLK"},
+ {"I2S DIN1", NULL, "I2S CLK"},
+ {"I2S DOUT", NULL, "I2S CLK"},
+
+ /* PCM/AIF1 Inputs */
+ {"PCM SDO", NULL, "ADC Left Mux"},
+ {"PCM SDO", NULL, "ADCR EC Mux"},
+
+ /* PCM/AFI2 Outputs */
+ {"Lofi PGA", NULL, "PCM SDI"},
+ {"Lofi PGA", NULL, "Sidetone PGA"},
+ {"Left DAC", NULL, "Lofi PGA"},
+ {"Right DAC", NULL, "Lofi PGA"},
+
+ /* I2S/AIF2 Inputs */
+ {"MIC Mux", "Mic 1", "MIC1P"},
+ {"MIC Mux", "Mic 1", "MIC1N"},
+ {"MIC Mux", "Mic 2", "MIC2P"},
+ {"MIC Mux", "Mic 2", "MIC2N"},
+ {"MIC1 Volume", NULL, "MIC Mux"},
+ {"MIC3 Volume", NULL, "MIC3P"},
+ {"MIC3 Volume", NULL, "MIC3N"},
+ {"Left ADC", NULL, "MIC1 Volume"},
+ {"Right ADC", NULL, "MIC3 Volume"},
+ {"ADC Left Mux", "ADCR", "Right ADC"},
+ {"ADC Left Mux", "ADCL", "Left ADC"},
+ {"ADC Right Mux", "ADCL", "Left ADC"},
+ {"ADC Right Mux", "ADCR", "Right ADC"},
+ {"Left EPA", "Switch", "Left DAC"},
+ {"Right EPA", "Switch", "Right DAC"},
+ {"EC Mux", "Left", "Left DAC"},
+ {"EC Mux", "Right", "Right DAC"},
+ {"EC Mux", "Left + Right", "Left DAC"},
+ {"EC Mux", "Left + Right", "Right DAC"},
+ {"ADCR EC Mux", "ADCR", "ADC Right Mux"},
+ {"ADCR EC Mux", "EC", "EC Mux"},
+ {"I2S Mic Mux", "Ex PA", "Left EPA"},
+ {"I2S Mic Mux", "Ex PA", "Right EPA"},
+ {"I2S Mic Mux", "ADC", "ADC Left Mux"},
+ {"I2S Mic Mux", "ADC", "ADCR EC Mux"},
+ {"I2S DOUT", NULL, "I2S Mic Mux"},
+
+ /* I2S/AIF2 Outputs */
+ {"I2S DIN Mux", "DIN", "I2S DIN"},
+ {"I2S DIN Mux", "DIN1", "I2S DIN1"},
+ {"Left DAC", NULL, "I2S DIN Mux"},
+ {"Right DAC", NULL, "I2S DIN Mux"},
+ {"DAC HS1 Mux", "Left", "Left DAC"},
+ {"DAC HS1 Mux", "Right", "Right DAC"},
+ {"DAC HS2 Mux", "Left", "Left DAC"},
+ {"DAC HS2 Mux", "Right", "Right DAC"},
+ {"DAC LO1 Mux", "Left", "Left DAC"},
+ {"DAC LO1 Mux", "Right", "Right DAC"},
+ {"DAC LO2 Mux", "Left", "Left DAC"},
+ {"DAC LO2 Mux", "Right", "Right DAC"},
+ {"Headset1 Mux", "Digital", "DAC HS1 Mux"},
+ {"Headset2 Mux", "Digital", "DAC HS2 Mux"},
+ {"Lineout1 Mux", "Digital", "DAC LO1 Mux"},
+ {"Lineout2 Mux", "Digital", "DAC LO2 Mux"},
+ {"Headset1 PGA", NULL, "Headset1 Mux"},
+ {"Headset2 PGA", NULL, "Headset2 Mux"},
+ {"Lineout1 PGA", NULL, "Lineout1 Mux"},
+ {"Lineout2 PGA", NULL, "Lineout2 Mux"},
+ {"DAC SP Mux", "Left", "Left DAC"},
+ {"DAC SP Mux", "Right", "Right DAC"},
+ {"Speaker Earpiece Demux", "Speaker", "DAC SP Mux"},
+ {"Speaker PGA", NULL, "Speaker Earpiece Demux"},
+ {"Earpiece PGA", NULL, "Speaker Earpiece Demux"},
+
+ {"RSYNC", NULL, "Headset1 PGA"},
+ {"RSYNC", NULL, "Headset2 PGA"},
+ {"RSYNC", NULL, "Lineout1 PGA"},
+ {"RSYNC", NULL, "Lineout2 PGA"},
+ {"RSYNC", NULL, "Speaker PGA"},
+ {"RSYNC", NULL, "Speaker PGA"},
+ {"RSYNC", NULL, "Earpiece PGA"},
+ {"RSYNC", NULL, "Earpiece PGA"},
+
+ {"HS1", NULL, "RSYNC"},
+ {"HS2", NULL, "RSYNC"},
+ {"LINEOUT1", NULL, "RSYNC"},
+ {"LINEOUT2", NULL, "RSYNC"},
+ {"LSP", NULL, "RSYNC"},
+ {"LSN", NULL, "RSYNC"},
+ {"EARP", NULL, "RSYNC"},
+ {"EARN", NULL, "RSYNC"},
+};
+
+/*
+ * Use MUTE_LEFT & MUTE_RIGHT to implement digital mute.
+ * These bits can also be used to mute.
+ */
+static int pm860x_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ 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,
+ RSYNC_CHANGE, RSYNC_CHANGE);
+ return 0;
+}
+
+static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned char inf = 0, mask = 0;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ inf &= ~PCM_INF2_18WL;
+ break;
+ case 18:
+ inf |= PCM_INF2_18WL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mask |= PCM_INF2_18WL;
+ snd_soc_update_bits(codec, PM860X_PCM_IFACE_2, mask, inf);
+
+ /* sample rate */
+ switch (params_rate(params)) {
+ case 8000:
+ inf = 0;
+ break;
+ case 16000:
+ inf = 3;
+ break;
+ case 32000:
+ inf = 6;
+ break;
+ case 48000:
+ inf = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, PM860X_PCM_RATE, 0x0f, inf);
+
+ return 0;
+}
+
+static int pm860x_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ 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:
+ if (pm860x->dir == PM860X_CLK_DIR_OUT) {
+ inf |= PCM_INF2_MASTER;
+ ret = 0;
+ }
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ if (pm860x->dir == PM860X_CLK_DIR_IN) {
+ inf &= ~PCM_INF2_MASTER;
+ ret = 0;
+ }
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ inf |= PCM_EXACT_I2S;
+ ret = 0;
+ break;
+ }
+ mask |= PCM_MODE_MASK;
+ if (ret)
+ return ret;
+ snd_soc_update_bits(codec, 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);
+
+ if (dir == PM860X_CLK_DIR_OUT)
+ pm860x->dir = PM860X_CLK_DIR_OUT;
+ else {
+ pm860x->dir = PM860X_CLK_DIR_IN;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned char inf;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ inf = 0;
+ break;
+ case 18:
+ inf = PCM_INF2_18WL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, PM860X_I2S_IFACE_2, PCM_INF2_18WL, inf);
+
+ /* sample rate */
+ switch (params_rate(params)) {
+ case 8000:
+ inf = 0;
+ break;
+ case 11025:
+ inf = 1;
+ break;
+ case 16000:
+ inf = 3;
+ break;
+ case 22050:
+ inf = 4;
+ break;
+ case 32000:
+ inf = 6;
+ break;
+ case 44100:
+ inf = 7;
+ break;
+ case 48000:
+ inf = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, PM860X_I2S_IFACE_4, 0xf, inf);
+
+ return 0;
+}
+
+static int pm860x_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ 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:
+ if (pm860x->dir == PM860X_CLK_DIR_OUT)
+ inf |= PCM_INF2_MASTER;
+ else
+ return -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ if (pm860x->dir == PM860X_CLK_DIR_IN)
+ inf &= ~PCM_INF2_MASTER;
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ inf |= PCM_EXACT_I2S;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mask |= PCM_MODE_MASK;
+ snd_soc_update_bits(codec, PM860X_I2S_IFACE_2, mask, inf);
+ return 0;
+}
+
+static int pm860x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ int data;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ /* Enable Audio PLL & Audio section */
+ data = AUDIO_PLL | AUDIO_SECTION_ON;
+ pm860x_reg_write(pm860x->i2c, REG_MISC2, data);
+ udelay(300);
+ data = AUDIO_PLL | AUDIO_SECTION_RESET
+ | AUDIO_SECTION_ON;
+ pm860x_reg_write(pm860x->i2c, REG_MISC2, data);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ data = AUDIO_PLL | AUDIO_SECTION_RESET | AUDIO_SECTION_ON;
+ pm860x_set_bits(pm860x->i2c, REG_MISC2, data, 0);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pm860x_pcm_dai_ops = {
+ .digital_mute = pm860x_digital_mute,
+ .hw_params = pm860x_pcm_hw_params,
+ .set_fmt = pm860x_pcm_set_dai_fmt,
+ .set_sysclk = pm860x_set_dai_sysclk,
+};
+
+static const struct snd_soc_dai_ops pm860x_i2s_dai_ops = {
+ .digital_mute = pm860x_digital_mute,
+ .hw_params = pm860x_i2s_hw_params,
+ .set_fmt = pm860x_i2s_set_dai_fmt,
+ .set_sysclk = pm860x_set_dai_sysclk,
+};
+
+#define PM860X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
+
+static struct snd_soc_dai_driver pm860x_dai[] = {
+ {
+ /* DAI PCM */
+ .name = "88pm860x-pcm",
+ .id = 1,
+ .playback = {
+ .stream_name = "PCM Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PM860X_RATES,
+ .formats = SNDRV_PCM_FORMAT_S16_LE | \
+ SNDRV_PCM_FORMAT_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,
+ },
+ .ops = &pm860x_pcm_dai_ops,
+ }, {
+ /* DAI I2S */
+ .name = "88pm860x-i2s",
+ .id = 2,
+ .playback = {
+ .stream_name = "I2S Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FORMAT_S16_LE | \
+ SNDRV_PCM_FORMAT_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,
+ },
+ .ops = &pm860x_i2s_dai_ops,
+ },
+};
+
+static irqreturn_t pm860x_codec_handler(int irq, void *data)
+{
+ struct pm860x_priv *pm860x = data;
+ int status, shrt, report = 0, mic_report = 0;
+ int mask;
+
+ status = pm860x_reg_read(pm860x->i2c, REG_STATUS_1);
+ shrt = pm860x_reg_read(pm860x->i2c, REG_SHORTS);
+ mask = pm860x->det.hs_shrt | pm860x->det.hook_det | pm860x->det.lo_shrt
+ | pm860x->det.hp_det;
+
+#ifndef CONFIG_SND_SOC_88PM860X_MODULE
+ if (status & (HEADSET_STATUS | MIC_STATUS | SHORT_HS1 | SHORT_HS2 |
+ SHORT_LO1 | SHORT_LO2))
+ trace_snd_soc_jack_irq(dev_name(pm860x->codec->dev));
+#endif
+
+ if ((pm860x->det.hp_det & SND_JACK_HEADPHONE)
+ && (status & HEADSET_STATUS))
+ report |= SND_JACK_HEADPHONE;
+
+ if ((pm860x->det.mic_det & SND_JACK_MICROPHONE)
+ && (status & MIC_STATUS))
+ mic_report |= SND_JACK_MICROPHONE;
+
+ if (pm860x->det.hs_shrt && (shrt & (SHORT_HS1 | SHORT_HS2)))
+ report |= pm860x->det.hs_shrt;
+
+ if (pm860x->det.hook_det && (status & HOOK_STATUS))
+ report |= pm860x->det.hook_det;
+
+ if (pm860x->det.lo_shrt && (shrt & (SHORT_LO1 | SHORT_LO2)))
+ report |= pm860x->det.lo_shrt;
+
+ if (report)
+ snd_soc_jack_report(pm860x->det.hp_jack, report, mask);
+ if (mic_report)
+ snd_soc_jack_report(pm860x->det.mic_jack, SND_JACK_MICROPHONE,
+ SND_JACK_MICROPHONE);
+
+ dev_dbg(pm860x->codec->dev, "headphone report:0x%x, mask:%x\n",
+ report, mask);
+ dev_dbg(pm860x->codec->dev, "microphone report:0x%x\n", mic_report);
+ return IRQ_HANDLED;
+}
+
+int pm860x_hs_jack_detect(struct snd_soc_codec *codec,
+ 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);
+ int data;
+
+ pm860x->det.hp_jack = jack;
+ pm860x->det.hp_det = det;
+ pm860x->det.hook_det = hook;
+ pm860x->det.hs_shrt = hs_shrt;
+ pm860x->det.lo_shrt = lo_shrt;
+
+ if (det & SND_JACK_HEADPHONE)
+ pm860x_set_bits(pm860x->i2c, REG_HS_DET,
+ EN_HS_DET, EN_HS_DET);
+ /* headset short detect */
+ if (hs_shrt) {
+ data = CLR_SHORT_HS2 | CLR_SHORT_HS1;
+ pm860x_set_bits(pm860x->i2c, REG_SHORTS, data, data);
+ }
+ /* Lineout short detect */
+ if (lo_shrt) {
+ data = CLR_SHORT_LO2 | CLR_SHORT_LO1;
+ pm860x_set_bits(pm860x->i2c, REG_SHORTS, data, data);
+ }
+
+ /* sync status */
+ pm860x_codec_handler(0, pm860x);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm860x_hs_jack_detect);
+
+int pm860x_mic_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack, int det)
+{
+ struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+
+ pm860x->det.mic_jack = jack;
+ pm860x->det.mic_det = det;
+
+ if (det & SND_JACK_MICROPHONE)
+ pm860x_set_bits(pm860x->i2c, REG_MIC_DET,
+ MICDET_MASK, MICDET_MASK);
+
+ /* sync status */
+ pm860x_codec_handler(0, pm860x);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm860x_mic_jack_detect);
+
+static int pm860x_probe(struct snd_soc_codec *codec)
+{
+ struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
+
+ pm860x->codec = codec;
+
+ for (i = 0; i < 4; i++) {
+ ret = request_threaded_irq(pm860x->irq[i], NULL,
+ pm860x_codec_handler, IRQF_ONESHOT,
+ pm860x->name[i], pm860x);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to request IRQ!\n");
+ goto out;
+ }
+ }
+
+ return 0;
+
+out:
+ while (--i >= 0)
+ free_irq(pm860x->irq[i], pm860x);
+ return ret;
+}
+
+static int pm860x_remove(struct snd_soc_codec *codec)
+{
+ struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ 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 int pm860x_codec_probe(struct platform_device *pdev)
+{
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct pm860x_priv *pm860x;
+ struct resource *res;
+ int i, ret;
+
+ pm860x = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_priv),
+ GFP_KERNEL);
+ if (pm860x == NULL)
+ return -ENOMEM;
+
+ pm860x->chip = chip;
+ pm860x->i2c = (chip->id == CHIP_PM8607) ? chip->client
+ : chip->companion;
+ pm860x->regmap = (chip->id == CHIP_PM8607) ? chip->regmap
+ : chip->regmap_companion;
+ platform_set_drvdata(pdev, pm860x);
+
+ for (i = 0; i < 4; i++) {
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get IRQ resources\n");
+ return -EINVAL;
+ }
+ pm860x->irq[i] = res->start + chip->irq_base;
+ strncpy(pm860x->name[i], res->name, MAX_NAME_LEN);
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pm860x,
+ pm860x_dai, ARRAY_SIZE(pm860x_dai));
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register codec\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);
+
+MODULE_DESCRIPTION("ASoC 88PM860x driver");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:88pm860x-codec");
+
diff --git a/kernel/sound/soc/codecs/88pm860x-codec.h b/kernel/sound/soc/codecs/88pm860x-codec.h
new file mode 100644
index 000000000..f7282f4f4
--- /dev/null
+++ b/kernel/sound/soc/codecs/88pm860x-codec.h
@@ -0,0 +1,96 @@
+/*
+ * 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
+#define __88PM860X_H
+
+#define PM860X_PCM_IFACE_1 0xb0
+#define PM860X_PCM_IFACE_2 0xb1
+#define PM860X_PCM_IFACE_3 0xb2
+#define PM860X_PCM_RATE 0xb3
+#define PM860X_EC_PATH 0xb4
+#define PM860X_SIDETONE_L_GAIN 0xb5
+#define PM860X_SIDETONE_R_GAIN 0xb6
+#define PM860X_SIDETONE_SHIFT 0xb7
+#define PM860X_ADC_OFFSET_1 0xb8
+#define PM860X_ADC_OFFSET_2 0xb9
+#define PM860X_DMIC_DELAY 0xba
+
+#define PM860X_I2S_IFACE_1 0xbb
+#define PM860X_I2S_IFACE_2 0xbc
+#define PM860X_I2S_IFACE_3 0xbd
+#define PM860X_I2S_IFACE_4 0xbe
+#define PM860X_EQUALIZER_N0_1 0xbf
+#define PM860X_EQUALIZER_N0_2 0xc0
+#define PM860X_EQUALIZER_N1_1 0xc1
+#define PM860X_EQUALIZER_N1_2 0xc2
+#define PM860X_EQUALIZER_D1_1 0xc3
+#define PM860X_EQUALIZER_D1_2 0xc4
+#define PM860X_LOFI_GAIN_LEFT 0xc5
+#define PM860X_LOFI_GAIN_RIGHT 0xc6
+#define PM860X_HIFIL_GAIN_LEFT 0xc7
+#define PM860X_HIFIL_GAIN_RIGHT 0xc8
+#define PM860X_HIFIR_GAIN_LEFT 0xc9
+#define PM860X_HIFIR_GAIN_RIGHT 0xca
+#define PM860X_DAC_OFFSET 0xcb
+#define PM860X_OFFSET_LEFT_1 0xcc
+#define PM860X_OFFSET_LEFT_2 0xcd
+#define PM860X_OFFSET_RIGHT_1 0xce
+#define PM860X_OFFSET_RIGHT_2 0xcf
+#define PM860X_ADC_ANA_1 0xd0
+#define PM860X_ADC_ANA_2 0xd1
+#define PM860X_ADC_ANA_3 0xd2
+#define PM860X_ADC_ANA_4 0xd3
+#define PM860X_ANA_TO_ANA 0xd4
+#define PM860X_HS1_CTRL 0xd5
+#define PM860X_HS2_CTRL 0xd6
+#define PM860X_LO1_CTRL 0xd7
+#define PM860X_LO2_CTRL 0xd8
+#define PM860X_EAR_CTRL_1 0xd9
+#define PM860X_EAR_CTRL_2 0xda
+#define PM860X_AUDIO_SUPPLIES_1 0xdb
+#define PM860X_AUDIO_SUPPLIES_2 0xdc
+#define PM860X_ADC_EN_1 0xdd
+#define PM860X_ADC_EN_2 0xde
+#define PM860X_DAC_EN_1 0xdf
+#define PM860X_DAC_EN_2 0xe1
+#define PM860X_AUDIO_CAL_1 0xe2
+#define PM860X_AUDIO_CAL_2 0xe3
+#define PM860X_AUDIO_CAL_3 0xe4
+#define PM860X_AUDIO_CAL_4 0xe5
+#define PM860X_AUDIO_CAL_5 0xe6
+#define PM860X_ANA_INPUT_SEL_1 0xe7
+#define PM860X_ANA_INPUT_SEL_2 0xe8
+
+#define PM860X_PCM_IFACE_4 0xe9
+#define PM860X_I2S_IFACE_5 0xea
+
+#define PM860X_SHORTS 0x3b
+#define PM860X_PLL_ADJ_1 0x3c
+#define PM860X_PLL_ADJ_2 0x3d
+
+/* bits definition */
+#define PM860X_CLK_DIR_IN 0
+#define PM860X_CLK_DIR_OUT 1
+
+#define PM860X_DET_HEADSET (1 << 0)
+#define PM860X_DET_MIC (1 << 1)
+#define PM860X_DET_HOOK (1 << 2)
+#define PM860X_SHORT_HEADSET (1 << 3)
+#define PM860X_SHORT_LINEOUT (1 << 4)
+#define PM860X_DET_MASK 0x1F
+
+extern int pm860x_hs_jack_detect(struct snd_soc_codec *, struct snd_soc_jack *,
+ int, int, int, int);
+extern int pm860x_mic_jack_detect(struct snd_soc_codec *, struct snd_soc_jack *,
+ int);
+
+#endif /* __88PM860X_H */
diff --git a/kernel/sound/soc/codecs/Kconfig b/kernel/sound/soc/codecs/Kconfig
new file mode 100644
index 000000000..061c46587
--- /dev/null
+++ b/kernel/sound/soc/codecs/Kconfig
@@ -0,0 +1,867 @@
+# Helper to resolve issues with configs that have SPI enabled but I2C
+# modular, meaning we can't build the codec driver in with I2C support.
+# We use an ordered list of conditional defaults to pick the appropriate
+# setting - SPI can't be modular so that case doesn't need to be covered.
+config SND_SOC_I2C_AND_SPI
+ tristate
+ default m if I2C=m
+ default y if I2C=y
+ default y if SPI_MASTER=y
+
+menu "CODEC drivers"
+
+config SND_SOC_ALL_CODECS
+ tristate "Build all ASoC CODEC drivers"
+ depends on COMPILE_TEST
+ 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 if GPIOLIB
+ select SND_SOC_MAX98925 if I2C
+ 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_I2C if I2C
+ select SND_SOC_WM8804_SPI if SPI_MASTER
+ 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.
+
+ Support for the bus types used to access the codecs to be built must
+ be selected separately.
+
+ If unsure select "N".
+
+config SND_SOC_88PM860X
+ tristate
+
+config SND_SOC_ARIZONA
+ tristate
+ default y if SND_SOC_WM5102=y
+ default y if SND_SOC_WM5110=y
+ default y if SND_SOC_WM8997=y
+ default m if SND_SOC_WM5102=m
+ default m if SND_SOC_WM5110=m
+ default m if SND_SOC_WM8997=m
+
+config SND_SOC_WM_HUBS
+ tristate
+ default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y
+ default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m
+
+config SND_SOC_WM_ADSP
+ tristate
+ default y if SND_SOC_WM5102=y
+ default y if SND_SOC_WM5110=y
+ default y if SND_SOC_WM2200=y
+ default m if SND_SOC_WM5102=m
+ default m if SND_SOC_WM5110=m
+ default m if SND_SOC_WM2200=m
+
+config SND_SOC_AB8500_CODEC
+ tristate
+
+config SND_SOC_AC97_CODEC
+ tristate
+ select SND_AC97_CODEC
+
+config SND_SOC_AD1836
+ tristate
+
+config SND_SOC_AD193X
+ tristate
+
+config SND_SOC_AD193X_SPI
+ tristate
+ select SND_SOC_AD193X
+
+config SND_SOC_AD193X_I2C
+ tristate
+ select SND_SOC_AD193X
+
+config SND_SOC_AD1980
+ select REGMAP_AC97
+ tristate
+
+config SND_SOC_AD73311
+ tristate
+
+config SND_SOC_ADAU1373
+ tristate
+
+config SND_SOC_ADAU1701
+ tristate "Analog Devices ADAU1701 CODEC"
+ depends on I2C
+ select SND_SOC_SIGMADSP_I2C
+
+config SND_SOC_ADAU17X1
+ tristate
+ select SND_SOC_SIGMADSP_REGMAP
+
+config SND_SOC_ADAU1761
+ tristate
+ select SND_SOC_ADAU17X1
+
+config SND_SOC_ADAU1761_I2C
+ tristate
+ select SND_SOC_ADAU1761
+ select REGMAP_I2C
+
+config SND_SOC_ADAU1761_SPI
+ tristate
+ select SND_SOC_ADAU1761
+ select REGMAP_SPI
+
+config SND_SOC_ADAU1781
+ select SND_SOC_ADAU17X1
+ tristate
+
+config SND_SOC_ADAU1781_I2C
+ tristate
+ select SND_SOC_ADAU1781
+ select REGMAP_I2C
+
+config SND_SOC_ADAU1781_SPI
+ tristate
+ select SND_SOC_ADAU1781
+ select REGMAP_SPI
+
+config SND_SOC_ADAU1977
+ tristate
+
+config SND_SOC_ADAU1977_SPI
+ tristate
+ select SND_SOC_ADAU1977
+ select REGMAP_SPI
+
+config SND_SOC_ADAU1977_I2C
+ tristate
+ select SND_SOC_ADAU1977
+ select REGMAP_I2C
+
+config SND_SOC_ADAV80X
+ tristate
+
+config SND_SOC_ADAV801
+ tristate
+ select SND_SOC_ADAV80X
+
+config SND_SOC_ADAV803
+ tristate
+ select SND_SOC_ADAV80X
+
+config SND_SOC_ADS117X
+ tristate
+
+config SND_SOC_AK4104
+ tristate "AKM AK4104 CODEC"
+ depends on SPI_MASTER
+
+config SND_SOC_AK4535
+ tristate
+
+config SND_SOC_AK4554
+ tristate "AKM AK4554 CODEC"
+
+config SND_SOC_AK4641
+ tristate
+
+config SND_SOC_AK4642
+ tristate "AKM AK4642 CODEC"
+ depends on I2C
+
+config SND_SOC_AK4671
+ tristate
+
+config SND_SOC_AK5386
+ tristate "AKM AK5638 CODEC"
+
+config SND_SOC_ALC5623
+ tristate "Realtek ALC5623 CODEC"
+ depends on I2C
+
+config SND_SOC_ALC5632
+ tristate
+
+config SND_SOC_CQ0093VC
+ tristate
+
+config SND_SOC_CS35L32
+ tristate "Cirrus Logic CS35L32 CODEC"
+ depends on I2C
+
+config SND_SOC_CS42L51
+ tristate
+
+config SND_SOC_CS42L51_I2C
+ tristate "Cirrus Logic CS42L51 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_CS42L51
+
+config SND_SOC_CS42L52
+ tristate "Cirrus Logic CS42L52 CODEC"
+ depends on I2C && INPUT
+
+config SND_SOC_CS42L56
+ tristate "Cirrus Logic CS42L56 CODEC"
+ depends on I2C && INPUT
+
+config SND_SOC_CS42L73
+ tristate "Cirrus Logic CS42L73 CODEC"
+ depends on I2C
+
+config SND_SOC_CS4265
+ tristate "Cirrus Logic CS4265 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+
+# Cirrus Logic CS4270 Codec
+config SND_SOC_CS4270
+ tristate "Cirrus Logic CS4270 CODEC"
+ depends on I2C
+
+# Cirrus Logic CS4270 Codec VD = 3.3V Errata
+# Select if you are affected by the errata where the part will not function
+# if MCLK divide-by-1.5 is selected and VD is set to 3.3V. The driver will
+# not select any sample rates that require MCLK to be divided by 1.5.
+config SND_SOC_CS4270_VD33_ERRATA
+ bool
+ depends on SND_SOC_CS4270
+
+config SND_SOC_CS4271
+ tristate
+
+config SND_SOC_CS4271_I2C
+ tristate "Cirrus Logic CS4271 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_CS4271
+ select REGMAP_I2C
+
+config SND_SOC_CS4271_SPI
+ tristate "Cirrus Logic CS4271 CODEC (SPI)"
+ depends on SPI_MASTER
+ select SND_SOC_CS4271
+ select REGMAP_SPI
+
+config SND_SOC_CS42XX8
+ tristate
+
+config SND_SOC_CS42XX8_I2C
+ tristate "Cirrus Logic CS42448/CS42888 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_CS42XX8
+ select REGMAP_I2C
+
+config SND_SOC_CX20442
+ tristate
+ depends on TTY
+
+config SND_SOC_JZ4740_CODEC
+ select REGMAP_MMIO
+ tristate
+
+config SND_SOC_L3
+ tristate
+
+config SND_SOC_DA7210
+ tristate
+
+config SND_SOC_DA7213
+ tristate
+
+config SND_SOC_DA732X
+ tristate
+
+config SND_SOC_DA9055
+ tristate
+
+config SND_SOC_BT_SCO
+ tristate
+
+config SND_SOC_DMIC
+ tristate
+
+config SND_SOC_HDMI_CODEC
+ tristate "HDMI stub CODEC"
+
+config SND_SOC_ES8328
+ tristate "Everest Semi ES8328 CODEC"
+
+config SND_SOC_ES8328_I2C
+ tristate
+ select SND_SOC_ES8328
+
+config SND_SOC_ES8328_SPI
+ tristate
+ select SND_SOC_ES8328
+
+config SND_SOC_ISABELLE
+ tristate
+
+config SND_SOC_LM49453
+ tristate
+
+config SND_SOC_MAX98088
+ tristate
+
+config SND_SOC_MAX98090
+ tristate
+
+config SND_SOC_MAX98095
+ tristate
+
+config SND_SOC_MAX98357A
+ tristate
+
+config SND_SOC_MAX98925
+ tristate
+
+config SND_SOC_MAX9850
+ tristate
+
+config SND_SOC_PCM1681
+ tristate "Texas Instruments PCM1681 CODEC"
+ depends on I2C
+
+config SND_SOC_PCM1792A
+ tristate "Texas Instruments PCM1792A CODEC"
+ depends on SPI_MASTER
+
+config SND_SOC_PCM3008
+ tristate
+
+config SND_SOC_PCM512x
+ tristate
+
+config SND_SOC_PCM512x_I2C
+ tristate "Texas Instruments PCM512x CODECs - I2C"
+ depends on I2C
+ select SND_SOC_PCM512x
+ select REGMAP_I2C
+
+config SND_SOC_PCM512x_SPI
+ tristate "Texas Instruments PCM512x CODECs - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_PCM512x
+ select REGMAP_SPI
+
+config SND_SOC_RL6231
+ tristate
+ 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_RT5670=y
+ default y if SND_SOC_RT5677=y
+ 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_RT5670=m
+ default m if SND_SOC_RT5677=m
+
+config SND_SOC_RT286
+ tristate
+ depends on I2C
+
+config SND_SOC_RT5631
+ tristate "Realtek ALC5631/RT5631 CODEC"
+ depends on I2C
+
+config SND_SOC_RT5640
+ tristate
+
+config SND_SOC_RT5645
+ tristate
+
+config SND_SOC_RT5651
+ tristate
+
+config SND_SOC_RT5670
+ tristate
+
+config SND_SOC_RT5677
+ tristate
+ select REGMAP_I2C
+ select REGMAP_IRQ
+
+config SND_SOC_RT5677_SPI
+ tristate
+ default SND_SOC_RT5677 && SPI
+
+#Freescale sgtl5000 codec
+config SND_SOC_SGTL5000
+ tristate "Freescale SGTL5000 CODEC"
+ depends on I2C
+
+config SND_SOC_SI476X
+ tristate
+
+config SND_SOC_SIGMADSP
+ tristate
+ select CRC32
+
+config SND_SOC_SIGMADSP_I2C
+ tristate
+ select SND_SOC_SIGMADSP
+
+config SND_SOC_SIGMADSP_REGMAP
+ tristate
+ select SND_SOC_SIGMADSP
+
+config SND_SOC_SIRF_AUDIO_CODEC
+ tristate "SiRF SoC internal audio codec"
+ select REGMAP_MMIO
+
+config SND_SOC_SN95031
+ tristate
+
+config SND_SOC_SPDIF
+ tristate "S/PDIF CODEC"
+
+config SND_SOC_SSM2518
+ tristate
+
+config SND_SOC_SSM2602
+ tristate
+
+config SND_SOC_SSM2602_SPI
+ tristate "Analog Devices SSM2602 CODEC - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_SSM2602
+ select REGMAP_SPI
+
+config SND_SOC_SSM2602_I2C
+ tristate "Analog Devices SSM2602 CODEC - I2C"
+ depends on I2C
+ select SND_SOC_SSM2602
+ select REGMAP_I2C
+
+config SND_SOC_SSM4567
+ tristate "Analog Devices ssm4567 amplifier driver support"
+ depends on I2C
+
+config SND_SOC_STA32X
+ tristate "STA326, STA328 and STA329 speaker amplifier"
+ depends on I2C
+ select REGMAP_I2C
+
+config SND_SOC_STA350
+ tristate "STA350 speaker amplifier"
+ depends on I2C
+
+config SND_SOC_STA529
+ tristate
+
+config SND_SOC_STAC9766
+ tristate
+
+config SND_SOC_TAS2552
+ tristate "Texas Instruments TAS2552 Mono Audio amplifier"
+ depends on I2C
+
+config SND_SOC_TAS5086
+ tristate "Texas Instruments TAS5086 speaker amplifier"
+ depends on I2C
+
+config SND_SOC_TFA9879
+ tristate "NXP Semiconductors TFA9879 amplifier"
+ depends on I2C
+
+config SND_SOC_TLV320AIC23
+ tristate
+
+config SND_SOC_TLV320AIC23_I2C
+ tristate "Texas Instruments TLV320AIC23 audio CODEC - I2C"
+ depends on I2C
+ select SND_SOC_TLV320AIC23
+
+config SND_SOC_TLV320AIC23_SPI
+ tristate "Texas Instruments TLV320AIC23 audio CODEC - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_TLV320AIC23
+
+config SND_SOC_TLV320AIC26
+ tristate
+ depends on SPI
+
+config SND_SOC_TLV320AIC31XX
+ tristate "Texas Instruments TLV320AIC31xx CODECs"
+ depends on I2C
+ select REGMAP_I2C
+
+config SND_SOC_TLV320AIC32X4
+ tristate
+
+config SND_SOC_TLV320AIC3X
+ tristate "Texas Instruments TLV320AIC3x CODECs"
+ depends on I2C
+
+config SND_SOC_TLV320DAC33
+ tristate
+
+config SND_SOC_TS3A227E
+ tristate "TI Headset/Mic detect and keypress chip"
+ depends on I2C
+
+config SND_SOC_TWL4030
+ select MFD_TWL4030_AUDIO
+ tristate
+
+config SND_SOC_TWL6040
+ tristate
+
+config SND_SOC_UDA134X
+ tristate
+
+config SND_SOC_UDA1380
+ tristate
+
+config SND_SOC_WL1273
+ tristate
+
+config SND_SOC_WM0010
+ tristate
+
+config SND_SOC_WM1250_EV1
+ tristate
+
+config SND_SOC_WM2000
+ tristate
+
+config SND_SOC_WM2200
+ tristate
+
+config SND_SOC_WM5100
+ tristate
+
+config SND_SOC_WM5102
+ tristate
+
+config SND_SOC_WM5110
+ tristate
+
+config SND_SOC_WM8350
+ tristate
+
+config SND_SOC_WM8400
+ tristate
+
+config SND_SOC_WM8510
+ tristate "Wolfson Microelectronics WM8510 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
+
+config SND_SOC_WM8523
+ tristate "Wolfson Microelectronics WM8523 DAC"
+ depends on I2C
+
+config SND_SOC_WM8580
+ tristate "Wolfson Microelectronics WM8523 CODEC"
+ depends on I2C
+
+config SND_SOC_WM8711
+ tristate "Wolfson Microelectronics WM8711 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
+
+config SND_SOC_WM8727
+ tristate
+
+config SND_SOC_WM8728
+ tristate "Wolfson Microelectronics WM8728 DAC"
+ depends on SND_SOC_I2C_AND_SPI
+
+config SND_SOC_WM8731
+ tristate "Wolfson Microelectronics WM8731 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
+
+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"
+ depends on SND_SOC_I2C_AND_SPI
+
+config SND_SOC_WM8750
+ tristate "Wolfson Microelectronics WM8750 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
+
+config SND_SOC_WM8753
+ tristate "Wolfson Microelectronics WM8753 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
+
+config SND_SOC_WM8770
+ tristate "Wolfson Microelectronics WM8770 CODEC"
+ depends on SPI_MASTER
+
+config SND_SOC_WM8776
+ tristate "Wolfson Microelectronics WM8776 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
+
+config SND_SOC_WM8782
+ tristate
+
+config SND_SOC_WM8804
+ tristate
+
+config SND_SOC_WM8804_I2C
+ tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver I2C"
+ depends on I2C
+ select SND_SOC_WM8804
+ select REGMAP_I2C
+
+config SND_SOC_WM8804_SPI
+ tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver SPI"
+ depends on SPI_MASTER
+ select SND_SOC_WM8804
+ select REGMAP_SPI
+
+config SND_SOC_WM8900
+ tristate
+
+config SND_SOC_WM8903
+ tristate "Wolfson Microelectronics WM8903 CODEC"
+ depends on I2C
+
+config SND_SOC_WM8904
+ tristate
+
+config SND_SOC_WM8940
+ tristate
+
+config SND_SOC_WM8955
+ tristate
+
+config SND_SOC_WM8960
+ tristate
+
+config SND_SOC_WM8961
+ tristate
+
+config SND_SOC_WM8962
+ tristate "Wolfson Microelectronics WM8962 CODEC"
+ depends on I2C && INPUT
+
+config SND_SOC_WM8971
+ tristate
+
+config SND_SOC_WM8974
+ tristate
+
+config SND_SOC_WM8978
+ tristate "Wolfson Microelectronics WM8978 codec"
+ depends on I2C
+
+config SND_SOC_WM8983
+ tristate
+
+config SND_SOC_WM8985
+ tristate
+
+config SND_SOC_WM8988
+ tristate
+
+config SND_SOC_WM8990
+ tristate
+
+config SND_SOC_WM8991
+ tristate
+
+config SND_SOC_WM8993
+ tristate
+
+config SND_SOC_WM8994
+ tristate
+
+config SND_SOC_WM8995
+ tristate
+
+config SND_SOC_WM8996
+ tristate
+
+config SND_SOC_WM8997
+ tristate
+
+config SND_SOC_WM9081
+ tristate
+
+config SND_SOC_WM9090
+ tristate
+
+config SND_SOC_WM9705
+ tristate
+
+config SND_SOC_WM9712
+ tristate
+
+config SND_SOC_WM9713
+ tristate
+
+# Amp
+config SND_SOC_LM4857
+ tristate
+
+config SND_SOC_MAX9768
+ tristate
+
+config SND_SOC_MAX9877
+ tristate
+
+config SND_SOC_MC13783
+ tristate
+
+config SND_SOC_ML26124
+ tristate
+
+config SND_SOC_TPA6130A2
+ tristate "Texas Instruments TPA6130A2 headphone amplifier"
+ depends on I2C
+
+endmenu
diff --git a/kernel/sound/soc/codecs/Makefile b/kernel/sound/soc/codecs/Makefile
new file mode 100644
index 000000000..abe2d7edf
--- /dev/null
+++ b/kernel/sound/soc/codecs/Makefile
@@ -0,0 +1,363 @@
+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-max98925-objs := max98925.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-wm8804-i2c-objs := wm8804-i2c.o
+snd-soc-wm8804-spi-objs := wm8804-spi.o
+snd-soc-wm8900-objs := wm8900.o
+snd-soc-wm8903-objs := wm8903.o
+snd-soc-wm8904-objs := wm8904.o
+snd-soc-wm8996-objs := wm8996.o
+snd-soc-wm8940-objs := wm8940.o
+snd-soc-wm8955-objs := wm8955.o
+snd-soc-wm8960-objs := wm8960.o
+snd-soc-wm8961-objs := wm8961.o
+snd-soc-wm8962-objs := wm8962.o
+snd-soc-wm8971-objs := wm8971.o
+snd-soc-wm8974-objs := wm8974.o
+snd-soc-wm8978-objs := wm8978.o
+snd-soc-wm8983-objs := wm8983.o
+snd-soc-wm8985-objs := wm8985.o
+snd-soc-wm8988-objs := wm8988.o
+snd-soc-wm8990-objs := wm8990.o
+snd-soc-wm8991-objs := wm8991.o
+snd-soc-wm8993-objs := wm8993.o
+snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o
+snd-soc-wm8995-objs := wm8995.o
+snd-soc-wm8997-objs := wm8997.o
+snd-soc-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
+
+# Amp
+snd-soc-max9877-objs := max9877.o
+snd-soc-tpa6130a2-objs := tpa6130a2.o
+snd-soc-tas2552-objs := tas2552.o
+
+obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
+obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
+obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
+obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
+obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
+obj-$(CONFIG_SND_SOC_AD193X_SPI) += snd-soc-ad193x-spi.o
+obj-$(CONFIG_SND_SOC_AD193X_I2C) += snd-soc-ad193x-i2c.o
+obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
+obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
+obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o
+obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o
+obj-$(CONFIG_SND_SOC_ADAU17X1) += snd-soc-adau17x1.o
+obj-$(CONFIG_SND_SOC_ADAU1761) += snd-soc-adau1761.o
+obj-$(CONFIG_SND_SOC_ADAU1761_I2C) += snd-soc-adau1761-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU1761_SPI) += snd-soc-adau1761-spi.o
+obj-$(CONFIG_SND_SOC_ADAU1781) += snd-soc-adau1781.o
+obj-$(CONFIG_SND_SOC_ADAU1781_I2C) += snd-soc-adau1781-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU1781_SPI) += snd-soc-adau1781-spi.o
+obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o
+obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o
+obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o
+obj-$(CONFIG_SND_SOC_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_AK4535) += snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.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_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_CQ0093VC) += snd-soc-cq93vc.o
+obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.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_CS4265) += snd-soc-cs4265.o
+obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o
+obj-$(CONFIG_SND_SOC_CS4271_I2C) += snd-soc-cs4271-i2c.o
+obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o
+obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
+obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
+obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
+obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
+obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.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_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_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_LM4857) += snd-soc-lm4857.o
+obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.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_MAX98925) += snd-soc-max98925.o
+obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.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_PCM1681) += snd-soc-pcm1681.o
+obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
+obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.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_RL6231) += snd-soc-rl6231.o
+obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.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_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_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_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.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_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_TAS2552) += snd-soc-tas2552.o
+obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
+obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.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_TLV320AIC3X) += snd-soc-tlv320aic3x.o
+obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.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_UDA1380) += snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
+obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o
+obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
+obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
+obj-$(CONFIG_SND_SOC_WM2200) += snd-soc-wm2200.o
+obj-$(CONFIG_SND_SOC_WM5100) += snd-soc-wm5100.o
+obj-$(CONFIG_SND_SOC_WM5102) += snd-soc-wm5102.o
+obj-$(CONFIG_SND_SOC_WM5110) += snd-soc-wm5110.o
+obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
+obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
+obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
+obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o
+obj-$(CONFIG_SND_SOC_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_WM8737) += snd-soc-wm8737.o
+obj-$(CONFIG_SND_SOC_WM8741) += snd-soc-wm8741.o
+obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
+obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
+obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o
+obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o
+obj-$(CONFIG_SND_SOC_WM8782) += snd-soc-wm8782.o
+obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o
+obj-$(CONFIG_SND_SOC_WM8804_I2C) += snd-soc-wm8804-i2c.o
+obj-$(CONFIG_SND_SOC_WM8804_SPI) += snd-soc-wm8804-spi.o
+obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
+obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
+obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o
+obj-$(CONFIG_SND_SOC_WM8996) += snd-soc-wm8996.o
+obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o
+obj-$(CONFIG_SND_SOC_WM8955) += snd-soc-wm8955.o
+obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
+obj-$(CONFIG_SND_SOC_WM8961) += snd-soc-wm8961.o
+obj-$(CONFIG_SND_SOC_WM8962) += snd-soc-wm8962.o
+obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
+obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o
+obj-$(CONFIG_SND_SOC_WM8978) += snd-soc-wm8978.o
+obj-$(CONFIG_SND_SOC_WM8983) += snd-soc-wm8983.o
+obj-$(CONFIG_SND_SOC_WM8985) += snd-soc-wm8985.o
+obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
+obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
+obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o
+obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o
+obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o
+obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o
+obj-$(CONFIG_SND_SOC_WM8997) += snd-soc-wm8997.o
+obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o
+obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o
+obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
+obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
+obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
+obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
+obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
+
+# Amp
+obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
+obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
diff --git a/kernel/sound/soc/codecs/ab8500-codec.c b/kernel/sound/soc/codecs/ab8500-codec.c
new file mode 100644
index 000000000..88ca9cb0c
--- /dev/null
+++ b/kernel/sound/soc/codecs/ab8500-codec.c
@@ -0,0 +1,2618 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ * Kristoffer Karlsson <kristoffer.karlsson@stericsson.com>,
+ * Roger Nilsson <roger.xr.nilsson@stericsson.com>,
+ * for ST-Ericsson.
+ *
+ * Based on the early work done by:
+ * Mikko J. Lehto <mikko.lehto@symbio.com>,
+ * 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>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/mfd/abx500/ab8500-codec.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "ab8500-codec.h"
+
+/* Macrocell value definitions */
+#define CLK_32K_OUT2_DISABLE 0x01
+#define INACTIVE_RESET_AUDIO 0x02
+#define ENABLE_AUDIO_CLK_TO_AUDIO_BLK 0x10
+#define ENABLE_VINTCORE12_SUPPLY 0x04
+#define GPIO27_DIR_OUTPUT 0x04
+#define GPIO29_DIR_OUTPUT 0x10
+#define GPIO31_DIR_OUTPUT 0x40
+
+/* Macrocell register definitions */
+#define AB8500_GPIO_DIR4_REG 0x13 /* Bank AB8500_MISC */
+
+/* Nr of FIR/IIR-coeff banks in ANC-block */
+#define AB8500_NR_OF_ANC_COEFF_BANKS 2
+
+/* Minimum duration to keep ANC IIR Init bit high or
+low before proceeding with the configuration sequence */
+#define AB8500_ANC_SM_DELAY 2000
+
+#define AB8500_FILTER_CONTROL(xname, xcount, xmin, xmax) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = filter_control_info, \
+ .get = filter_control_get, .put = filter_control_put, \
+ .private_value = (unsigned long)&(struct filter_control) \
+ {.count = xcount, .min = xmin, .max = xmax} }
+
+struct filter_control {
+ long min, max;
+ unsigned int count;
+ long value[128];
+};
+
+/* Sidetone states */
+static const char * const enum_sid_state[] = {
+ "Unconfigured",
+ "Apply FIR",
+ "FIR is configured",
+};
+enum sid_state {
+ SID_UNCONFIGURED = 0,
+ SID_APPLY_FIR = 1,
+ SID_FIR_CONFIGURED = 2,
+};
+
+static const char * const enum_anc_state[] = {
+ "Unconfigured",
+ "Apply FIR and IIR",
+ "FIR and IIR are configured",
+ "Apply FIR",
+ "FIR is configured",
+ "Apply IIR",
+ "IIR is configured"
+};
+enum anc_state {
+ ANC_UNCONFIGURED = 0,
+ ANC_APPLY_FIR_IIR = 1,
+ ANC_FIR_IIR_CONFIGURED = 2,
+ ANC_APPLY_FIR = 3,
+ ANC_FIR_CONFIGURED = 4,
+ ANC_APPLY_IIR = 5,
+ ANC_IIR_CONFIGURED = 6
+};
+
+/* Analog microphones */
+enum amic_idx {
+ AMIC_IDX_1A,
+ AMIC_IDX_1B,
+ AMIC_IDX_2
+};
+
+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;
+ struct mutex ctrl_lock;
+
+ /* Sidetone */
+ long *sid_fir_values;
+ enum sid_state sid_status;
+
+ /* ANC */
+ long *anc_fir_values;
+ long *anc_iir_values;
+ enum anc_state anc_status;
+};
+
+static inline const char *amic_micbias_str(enum amic_micbias micbias)
+{
+ switch (micbias) {
+ case AMIC_MICBIAS_VAMIC1:
+ return "VAMIC1";
+ case AMIC_MICBIAS_VAMIC2:
+ return "VAMIC2";
+ default:
+ return "Unknown";
+ }
+}
+
+static inline const char *amic_type_str(enum amic_type type)
+{
+ switch (type) {
+ case AMIC_TYPE_DIFFERENTIAL:
+ return "DIFFERENTIAL";
+ case AMIC_TYPE_SINGLE_ENDED:
+ return "SINGLE ENDED";
+ default:
+ return "Unknown";
+ }
+}
+
+/*
+ * Read'n'write functions
+ */
+
+/* Read a register from the audio-bank of AB8500 */
+static int ab8500_codec_read_reg(void *context, unsigned int reg,
+ unsigned int *value)
+{
+ struct device *dev = context;
+ int status;
+
+ u8 value8;
+ status = abx500_get_register_interruptible(dev, AB8500_AUDIO,
+ reg, &value8);
+ *value = (unsigned int)value8;
+
+ return status;
+}
+
+/* Write to a register in the audio-bank of AB8500 */
+static int ab8500_codec_write_reg(void *context, unsigned int reg,
+ unsigned int value)
+{
+ struct device *dev = context;
+
+ return abx500_set_register_interruptible(dev, AB8500_AUDIO,
+ reg, value);
+}
+
+static const struct regmap_config ab8500_codec_regmap = {
+ .reg_read = ab8500_codec_read_reg,
+ .reg_write = ab8500_codec_write_reg,
+};
+
+/*
+ * Controls - DAPM
+ */
+
+/* Earpiece */
+
+/* Earpiece source selector */
+static const char * const enum_ear_lineout_source[] = {"Headset Left",
+ "Speaker Left"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ear_lineout_source, AB8500_DMICFILTCONF,
+ AB8500_DMICFILTCONF_DA3TOEAR, enum_ear_lineout_source);
+static const struct snd_kcontrol_new dapm_ear_lineout_source =
+ SOC_DAPM_ENUM("Earpiece or LineOut Mono Source",
+ dapm_enum_ear_lineout_source);
+
+/* LineOut */
+
+/* LineOut source selector */
+static const char * const enum_lineout_source[] = {"Mono Path", "Stereo Path"};
+static SOC_ENUM_DOUBLE_DECL(dapm_enum_lineout_source, AB8500_ANACONF5,
+ AB8500_ANACONF5_HSLDACTOLOL,
+ AB8500_ANACONF5_HSRDACTOLOR, enum_lineout_source);
+static const struct snd_kcontrol_new dapm_lineout_source[] = {
+ SOC_DAPM_ENUM("LineOut Source", dapm_enum_lineout_source),
+};
+
+/* Handsfree */
+
+/* Speaker Left - ANC selector */
+static const char * const enum_HFx_sel[] = {"Audio Path", "ANC"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_HFl_sel, AB8500_DIGMULTCONF2,
+ AB8500_DIGMULTCONF2_HFLSEL, enum_HFx_sel);
+static const struct snd_kcontrol_new dapm_HFl_select[] = {
+ SOC_DAPM_ENUM("Speaker Left Source", dapm_enum_HFl_sel),
+};
+
+/* Speaker Right - ANC selector */
+static SOC_ENUM_SINGLE_DECL(dapm_enum_HFr_sel, AB8500_DIGMULTCONF2,
+ AB8500_DIGMULTCONF2_HFRSEL, enum_HFx_sel);
+static const struct snd_kcontrol_new dapm_HFr_select[] = {
+ SOC_DAPM_ENUM("Speaker Right Source", dapm_enum_HFr_sel),
+};
+
+/* Mic 1 */
+
+/* Mic 1 - Mic 1a or 1b selector */
+static const char * const enum_mic1ab_sel[] = {"Mic 1b", "Mic 1a"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_mic1ab_sel, AB8500_ANACONF3,
+ AB8500_ANACONF3_MIC1SEL, enum_mic1ab_sel);
+static const struct snd_kcontrol_new dapm_mic1ab_mux[] = {
+ SOC_DAPM_ENUM("Mic 1a or 1b Select", dapm_enum_mic1ab_sel),
+};
+
+/* Mic 1 - AD3 - Mic 1 or DMic 3 selector */
+static const char * const enum_ad3_sel[] = {"Mic 1", "DMic 3"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad3_sel, AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_AD3SEL, enum_ad3_sel);
+static const struct snd_kcontrol_new dapm_ad3_select[] = {
+ SOC_DAPM_ENUM("AD3 Source Select", dapm_enum_ad3_sel),
+};
+
+/* Mic 1 - AD6 - Mic 1 or DMic 6 selector */
+static const char * const enum_ad6_sel[] = {"Mic 1", "DMic 6"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad6_sel, AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_AD6SEL, enum_ad6_sel);
+static const struct snd_kcontrol_new dapm_ad6_select[] = {
+ SOC_DAPM_ENUM("AD6 Source Select", dapm_enum_ad6_sel),
+};
+
+/* Mic 2 */
+
+/* Mic 2 - AD5 - Mic 2 or DMic 5 selector */
+static const char * const enum_ad5_sel[] = {"Mic 2", "DMic 5"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad5_sel, AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_AD5SEL, enum_ad5_sel);
+static const struct snd_kcontrol_new dapm_ad5_select[] = {
+ SOC_DAPM_ENUM("AD5 Source Select", dapm_enum_ad5_sel),
+};
+
+/* LineIn */
+
+/* LineIn left - AD1 - LineIn Left or DMic 1 selector */
+static const char * const enum_ad1_sel[] = {"LineIn Left", "DMic 1"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad1_sel, AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_AD1SEL, enum_ad1_sel);
+static const struct snd_kcontrol_new dapm_ad1_select[] = {
+ SOC_DAPM_ENUM("AD1 Source Select", dapm_enum_ad1_sel),
+};
+
+/* LineIn right - Mic 2 or LineIn Right selector */
+static const char * const enum_mic2lr_sel[] = {"Mic 2", "LineIn Right"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_mic2lr_sel, AB8500_ANACONF3,
+ AB8500_ANACONF3_LINRSEL, enum_mic2lr_sel);
+static const struct snd_kcontrol_new dapm_mic2lr_select[] = {
+ SOC_DAPM_ENUM("Mic 2 or LINR Select", dapm_enum_mic2lr_sel),
+};
+
+/* LineIn right - AD2 - LineIn Right or DMic2 selector */
+static const char * const enum_ad2_sel[] = {"LineIn Right", "DMic 2"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad2_sel, AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_AD2SEL, enum_ad2_sel);
+static const struct snd_kcontrol_new dapm_ad2_select[] = {
+ SOC_DAPM_ENUM("AD2 Source Select", dapm_enum_ad2_sel),
+};
+
+
+/* ANC */
+
+static const char * const enum_anc_in_sel[] = {"Mic 1 / DMic 6",
+ "Mic 2 / DMic 5"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_anc_in_sel, AB8500_DMICFILTCONF,
+ AB8500_DMICFILTCONF_ANCINSEL, enum_anc_in_sel);
+static const struct snd_kcontrol_new dapm_anc_in_select[] = {
+ SOC_DAPM_ENUM("ANC Source", dapm_enum_anc_in_sel),
+};
+
+/* ANC - Enable/Disable */
+static const struct snd_kcontrol_new dapm_anc_enable[] = {
+ SOC_DAPM_SINGLE("Switch", AB8500_ANCCONF1,
+ AB8500_ANCCONF1_ENANC, 0, 0),
+};
+
+/* ANC to Earpiece - Mute */
+static const struct snd_kcontrol_new dapm_anc_ear_mute[] = {
+ SOC_DAPM_SINGLE("Switch", AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_ANCSEL, 1, 0),
+};
+
+
+
+/* Sidetone left */
+
+/* Sidetone left - Input selector */
+static const char * const enum_stfir1_in_sel[] = {
+ "LineIn Left", "LineIn Right", "Mic 1", "Headset Left"
+};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_stfir1_in_sel, AB8500_DIGMULTCONF2,
+ AB8500_DIGMULTCONF2_FIRSID1SEL, enum_stfir1_in_sel);
+static const struct snd_kcontrol_new dapm_stfir1_in_select[] = {
+ SOC_DAPM_ENUM("Sidetone Left Source", dapm_enum_stfir1_in_sel),
+};
+
+/* Sidetone right path */
+
+/* Sidetone right - Input selector */
+static const char * const enum_stfir2_in_sel[] = {
+ "LineIn Right", "Mic 1", "DMic 4", "Headset Right"
+};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_stfir2_in_sel, AB8500_DIGMULTCONF2,
+ AB8500_DIGMULTCONF2_FIRSID2SEL, enum_stfir2_in_sel);
+static const struct snd_kcontrol_new dapm_stfir2_in_select[] = {
+ SOC_DAPM_ENUM("Sidetone Right Source", dapm_enum_stfir2_in_sel),
+};
+
+/* Vibra */
+
+static const char * const enum_pwm2vibx[] = {"Audio Path", "PWM Generator"};
+
+static SOC_ENUM_SINGLE_DECL(dapm_enum_pwm2vib1, AB8500_PWMGENCONF1,
+ AB8500_PWMGENCONF1_PWMTOVIB1, enum_pwm2vibx);
+
+static const struct snd_kcontrol_new dapm_pwm2vib1[] = {
+ SOC_DAPM_ENUM("Vibra 1 Controller", dapm_enum_pwm2vib1),
+};
+
+static SOC_ENUM_SINGLE_DECL(dapm_enum_pwm2vib2, AB8500_PWMGENCONF1,
+ AB8500_PWMGENCONF1_PWMTOVIB2, enum_pwm2vibx);
+
+static const struct snd_kcontrol_new dapm_pwm2vib2[] = {
+ SOC_DAPM_ENUM("Vibra 2 Controller", dapm_enum_pwm2vib2),
+};
+
+/*
+ * DAPM-widgets
+ */
+
+static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {
+
+ /* Clocks */
+ SND_SOC_DAPM_CLOCK_SUPPLY("audioclk"),
+
+ /* Regulators */
+ SND_SOC_DAPM_REGULATOR_SUPPLY("V-AUD", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC1", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC2", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("V-DMIC", 0, 0),
+
+ /* Power */
+ SND_SOC_DAPM_SUPPLY("Audio Power",
+ AB8500_POWERUP, AB8500_POWERUP_POWERUP, 0,
+ NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("Audio Analog Power",
+ AB8500_POWERUP, AB8500_POWERUP_ENANA, 0,
+ NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Main supply node */
+ SND_SOC_DAPM_SUPPLY("Main Supply", SND_SOC_NOPM, 0, 0,
+ NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* DA/AD */
+
+ SND_SOC_DAPM_INPUT("ADC Input"),
+ SND_SOC_DAPM_ADC("ADC", "ab8500_0c", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("DAC Output"),
+
+ SND_SOC_DAPM_AIF_IN("DA_IN1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DA_IN2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DA_IN3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DA_IN4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DA_IN5", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DA_IN6", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AD_OUT1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AD_OUT2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AD_OUT3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AD_OUT4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AD_OUT57", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AD_OUT68", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ /* Headset path */
+
+ SND_SOC_DAPM_SUPPLY("Charge Pump", AB8500_ANACONF5,
+ AB8500_ANACONF5_ENCPHS, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DA1 Enable", "ab8500_0p",
+ AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA1, 0),
+ SND_SOC_DAPM_DAC("DA2 Enable", "ab8500_0p",
+ AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA2, 0),
+
+ SND_SOC_DAPM_PGA("HSL Digital Volume", SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("HSR Digital Volume", SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_DAC("HSL DAC", "ab8500_0p",
+ AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHSL, 0),
+ SND_SOC_DAPM_DAC("HSR DAC", "ab8500_0p",
+ AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHSR, 0),
+ SND_SOC_DAPM_MIXER("HSL DAC Mute", AB8500_MUTECONF,
+ AB8500_MUTECONF_MUTDACHSL, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HSR DAC Mute", AB8500_MUTECONF,
+ AB8500_MUTECONF_MUTDACHSR, 1,
+ NULL, 0),
+ SND_SOC_DAPM_DAC("HSL DAC Driver", "ab8500_0p",
+ AB8500_ANACONF3, AB8500_ANACONF3_ENDRVHSL, 0),
+ SND_SOC_DAPM_DAC("HSR DAC Driver", "ab8500_0p",
+ AB8500_ANACONF3, AB8500_ANACONF3_ENDRVHSR, 0),
+
+ SND_SOC_DAPM_MIXER("HSL Mute",
+ AB8500_MUTECONF, AB8500_MUTECONF_MUTHSL, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HSR Mute",
+ AB8500_MUTECONF, AB8500_MUTECONF_MUTHSR, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HSL Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENHSL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HSR Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENHSR, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("HSL Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("HSR Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("Headset Left"),
+ SND_SOC_DAPM_OUTPUT("Headset Right"),
+
+ /* LineOut path */
+
+ SND_SOC_DAPM_MUX("LineOut Source",
+ SND_SOC_NOPM, 0, 0, dapm_lineout_source),
+
+ SND_SOC_DAPM_MIXER("LOL Disable HFL",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENHFL, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LOR Disable HFR",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENHFR, 1,
+ NULL, 0),
+
+ SND_SOC_DAPM_MIXER("LOL Enable",
+ AB8500_ANACONF5, AB8500_ANACONF5_ENLOL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LOR Enable",
+ AB8500_ANACONF5, AB8500_ANACONF5_ENLOR, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LineOut Left"),
+ SND_SOC_DAPM_OUTPUT("LineOut Right"),
+
+ /* Earpiece path */
+
+ SND_SOC_DAPM_MUX("Earpiece or LineOut Mono Source",
+ SND_SOC_NOPM, 0, 0, &dapm_ear_lineout_source),
+ SND_SOC_DAPM_MIXER("EAR DAC",
+ AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACEAR, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("EAR Mute",
+ AB8500_MUTECONF, AB8500_MUTECONF_MUTEAR, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("EAR Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENEAR, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("Earpiece"),
+
+ /* Handsfree path */
+
+ SND_SOC_DAPM_MIXER("DA3 Channel Volume",
+ AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA3, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DA4 Channel Volume",
+ AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA4, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MUX("Speaker Left Source",
+ SND_SOC_NOPM, 0, 0, dapm_HFl_select),
+ SND_SOC_DAPM_MUX("Speaker Right Source",
+ SND_SOC_NOPM, 0, 0, dapm_HFr_select),
+ SND_SOC_DAPM_MIXER("HFL DAC", AB8500_DAPATHCONF,
+ AB8500_DAPATHCONF_ENDACHFL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HFR DAC",
+ AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHFR, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DA4 or ANC path to HfR",
+ AB8500_DIGMULTCONF2, AB8500_DIGMULTCONF2_DATOHFREN, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DA3 or ANC path to HfL",
+ AB8500_DIGMULTCONF2, AB8500_DIGMULTCONF2_DATOHFLEN, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HFL Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENHFL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("HFR Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENHFR, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("Speaker Left"),
+ SND_SOC_DAPM_OUTPUT("Speaker Right"),
+
+ /* Vibrator path */
+
+ SND_SOC_DAPM_INPUT("PWMGEN1"),
+ SND_SOC_DAPM_INPUT("PWMGEN2"),
+
+ SND_SOC_DAPM_MIXER("DA5 Channel Volume",
+ AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA5, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DA6 Channel Volume",
+ AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA6, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("VIB1 DAC",
+ AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACVIB1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("VIB2 DAC",
+ AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACVIB2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MUX("Vibra 1 Controller",
+ SND_SOC_NOPM, 0, 0, dapm_pwm2vib1),
+ SND_SOC_DAPM_MUX("Vibra 2 Controller",
+ SND_SOC_NOPM, 0, 0, dapm_pwm2vib2),
+ SND_SOC_DAPM_MIXER("VIB1 Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENVIB1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("VIB2 Enable",
+ AB8500_ANACONF4, AB8500_ANACONF4_ENVIB2, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("Vibra 1"),
+ SND_SOC_DAPM_OUTPUT("Vibra 2"),
+
+ /* Mic 1 */
+
+ SND_SOC_DAPM_INPUT("Mic 1"),
+
+ SND_SOC_DAPM_MUX("Mic 1a or 1b Select",
+ SND_SOC_NOPM, 0, 0, dapm_mic1ab_mux),
+ SND_SOC_DAPM_MIXER("MIC1 Mute",
+ AB8500_ANACONF2, AB8500_ANACONF2_MUTMIC1, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("MIC1A V-AMICx Enable",
+ AB8500_ANACONF2, AB8500_ANACONF2_ENMIC1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("MIC1B V-AMICx Enable",
+ AB8500_ANACONF2, AB8500_ANACONF2_ENMIC1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("MIC1 ADC",
+ AB8500_ANACONF3, AB8500_ANACONF3_ENADCMIC, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MUX("AD3 Source Select",
+ SND_SOC_NOPM, 0, 0, dapm_ad3_select),
+ SND_SOC_DAPM_MIXER("AD3 Channel Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD3 Enable",
+ AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34, 0,
+ NULL, 0),
+
+ /* Mic 2 */
+
+ SND_SOC_DAPM_INPUT("Mic 2"),
+
+ SND_SOC_DAPM_MIXER("MIC2 Mute",
+ AB8500_ANACONF2, AB8500_ANACONF2_MUTMIC2, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("MIC2 V-AMICx Enable", AB8500_ANACONF2,
+ AB8500_ANACONF2_ENMIC2, 0,
+ NULL, 0),
+
+ /* LineIn */
+
+ SND_SOC_DAPM_INPUT("LineIn Left"),
+ SND_SOC_DAPM_INPUT("LineIn Right"),
+
+ SND_SOC_DAPM_MIXER("LINL Mute",
+ AB8500_ANACONF2, AB8500_ANACONF2_MUTLINL, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LINR Mute",
+ AB8500_ANACONF2, AB8500_ANACONF2_MUTLINR, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LINL Enable", AB8500_ANACONF2,
+ AB8500_ANACONF2_ENLINL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LINR Enable", AB8500_ANACONF2,
+ AB8500_ANACONF2_ENLINR, 0,
+ NULL, 0),
+
+ /* LineIn Bypass path */
+ SND_SOC_DAPM_MIXER("LINL to HSL Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LINR to HSR Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+
+ /* LineIn, Mic 2 */
+ SND_SOC_DAPM_MUX("Mic 2 or LINR Select",
+ SND_SOC_NOPM, 0, 0, dapm_mic2lr_select),
+ SND_SOC_DAPM_MIXER("LINL ADC", AB8500_ANACONF3,
+ AB8500_ANACONF3_ENADCLINL, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("LINR ADC", AB8500_ANACONF3,
+ AB8500_ANACONF3_ENADCLINR, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MUX("AD1 Source Select",
+ SND_SOC_NOPM, 0, 0, dapm_ad1_select),
+ SND_SOC_DAPM_MUX("AD2 Source Select",
+ SND_SOC_NOPM, 0, 0, dapm_ad2_select),
+ SND_SOC_DAPM_MIXER("AD1 Channel Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD2 Channel Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_MIXER("AD12 Enable",
+ AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD12, 0,
+ NULL, 0),
+
+ /* HD Capture path */
+
+ SND_SOC_DAPM_MUX("AD5 Source Select",
+ SND_SOC_NOPM, 0, 0, dapm_ad5_select),
+ SND_SOC_DAPM_MUX("AD6 Source Select",
+ SND_SOC_NOPM, 0, 0, dapm_ad6_select),
+ SND_SOC_DAPM_MIXER("AD5 Channel Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD6 Channel Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD57 Enable",
+ AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD68 Enable",
+ AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0,
+ NULL, 0),
+
+ /* Digital Microphone path */
+
+ SND_SOC_DAPM_INPUT("DMic 1"),
+ SND_SOC_DAPM_INPUT("DMic 2"),
+ SND_SOC_DAPM_INPUT("DMic 3"),
+ SND_SOC_DAPM_INPUT("DMic 4"),
+ SND_SOC_DAPM_INPUT("DMic 5"),
+ SND_SOC_DAPM_INPUT("DMic 6"),
+
+ SND_SOC_DAPM_MIXER("DMIC1",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC2",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC3",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC3, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC4",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC4, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC5",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC5, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC6",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC6, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD4 Channel Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD4 Enable",
+ AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34,
+ 0, NULL, 0),
+
+ /* Acoustical Noise Cancellation path */
+
+ SND_SOC_DAPM_INPUT("ANC Configure Input"),
+ SND_SOC_DAPM_OUTPUT("ANC Configure Output"),
+
+ SND_SOC_DAPM_MUX("ANC Source",
+ SND_SOC_NOPM, 0, 0,
+ dapm_anc_in_select),
+ SND_SOC_DAPM_SWITCH("ANC",
+ SND_SOC_NOPM, 0, 0,
+ dapm_anc_enable),
+ SND_SOC_DAPM_SWITCH("ANC to Earpiece",
+ SND_SOC_NOPM, 0, 0,
+ dapm_anc_ear_mute),
+
+ /* Sidetone Filter path */
+
+ SND_SOC_DAPM_MUX("Sidetone Left Source",
+ SND_SOC_NOPM, 0, 0,
+ dapm_stfir1_in_select),
+ SND_SOC_DAPM_MUX("Sidetone Right Source",
+ SND_SOC_NOPM, 0, 0,
+ dapm_stfir2_in_select),
+ SND_SOC_DAPM_MIXER("STFIR1 Control",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("STFIR2 Control",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("STFIR1 Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("STFIR2 Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+};
+
+/*
+ * DAPM-routes
+ */
+static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
+ /* Power AB8500 audio-block when AD/DA is active */
+ {"Main Supply", NULL, "V-AUD"},
+ {"Main Supply", NULL, "audioclk"},
+ {"Main Supply", NULL, "Audio Power"},
+ {"Main Supply", NULL, "Audio Analog Power"},
+
+ {"DAC", NULL, "ab8500_0p"},
+ {"DAC", NULL, "Main Supply"},
+ {"ADC", NULL, "ab8500_0c"},
+ {"ADC", NULL, "Main Supply"},
+
+ /* ANC Configure */
+ {"ANC Configure Input", NULL, "Main Supply"},
+ {"ANC Configure Output", NULL, "ANC Configure Input"},
+
+ /* AD/DA */
+ {"ADC", NULL, "ADC Input"},
+ {"DAC Output", NULL, "DAC"},
+
+ /* Powerup charge pump if DA1/2 is in use */
+
+ {"DA_IN1", NULL, "ab8500_0p"},
+ {"DA_IN1", NULL, "Charge Pump"},
+ {"DA_IN2", NULL, "ab8500_0p"},
+ {"DA_IN2", NULL, "Charge Pump"},
+
+ /* Headset path */
+
+ {"DA1 Enable", NULL, "DA_IN1"},
+ {"DA2 Enable", NULL, "DA_IN2"},
+
+ {"HSL Digital Volume", NULL, "DA1 Enable"},
+ {"HSR Digital Volume", NULL, "DA2 Enable"},
+
+ {"HSL DAC", NULL, "HSL Digital Volume"},
+ {"HSR DAC", NULL, "HSR Digital Volume"},
+
+ {"HSL DAC Mute", NULL, "HSL DAC"},
+ {"HSR DAC Mute", NULL, "HSR DAC"},
+
+ {"HSL DAC Driver", NULL, "HSL DAC Mute"},
+ {"HSR DAC Driver", NULL, "HSR DAC Mute"},
+
+ {"HSL Mute", NULL, "HSL DAC Driver"},
+ {"HSR Mute", NULL, "HSR DAC Driver"},
+
+ {"HSL Enable", NULL, "HSL Mute"},
+ {"HSR Enable", NULL, "HSR Mute"},
+
+ {"HSL Volume", NULL, "HSL Enable"},
+ {"HSR Volume", NULL, "HSR Enable"},
+
+ {"Headset Left", NULL, "HSL Volume"},
+ {"Headset Right", NULL, "HSR Volume"},
+
+ /* HF or LineOut path */
+
+ {"DA_IN3", NULL, "ab8500_0p"},
+ {"DA3 Channel Volume", NULL, "DA_IN3"},
+ {"DA_IN4", NULL, "ab8500_0p"},
+ {"DA4 Channel Volume", NULL, "DA_IN4"},
+
+ {"Speaker Left Source", "Audio Path", "DA3 Channel Volume"},
+ {"Speaker Right Source", "Audio Path", "DA4 Channel Volume"},
+
+ {"DA3 or ANC path to HfL", NULL, "Speaker Left Source"},
+ {"DA4 or ANC path to HfR", NULL, "Speaker Right Source"},
+
+ /* HF path */
+
+ {"HFL DAC", NULL, "DA3 or ANC path to HfL"},
+ {"HFR DAC", NULL, "DA4 or ANC path to HfR"},
+
+ {"HFL Enable", NULL, "HFL DAC"},
+ {"HFR Enable", NULL, "HFR DAC"},
+
+ {"Speaker Left", NULL, "HFL Enable"},
+ {"Speaker Right", NULL, "HFR Enable"},
+
+ /* Earpiece path */
+
+ {"Earpiece or LineOut Mono Source", "Headset Left",
+ "HSL Digital Volume"},
+ {"Earpiece or LineOut Mono Source", "Speaker Left",
+ "DA3 or ANC path to HfL"},
+
+ {"EAR DAC", NULL, "Earpiece or LineOut Mono Source"},
+
+ {"EAR Mute", NULL, "EAR DAC"},
+
+ {"EAR Enable", NULL, "EAR Mute"},
+
+ {"Earpiece", NULL, "EAR Enable"},
+
+ /* LineOut path stereo */
+
+ {"LineOut Source", "Stereo Path", "HSL DAC Driver"},
+ {"LineOut Source", "Stereo Path", "HSR DAC Driver"},
+
+ /* LineOut path mono */
+
+ {"LineOut Source", "Mono Path", "EAR DAC"},
+
+ /* LineOut path */
+
+ {"LOL Disable HFL", NULL, "LineOut Source"},
+ {"LOR Disable HFR", NULL, "LineOut Source"},
+
+ {"LOL Enable", NULL, "LOL Disable HFL"},
+ {"LOR Enable", NULL, "LOR Disable HFR"},
+
+ {"LineOut Left", NULL, "LOL Enable"},
+ {"LineOut Right", NULL, "LOR Enable"},
+
+ /* Vibrator path */
+
+ {"DA_IN5", NULL, "ab8500_0p"},
+ {"DA5 Channel Volume", NULL, "DA_IN5"},
+ {"DA_IN6", NULL, "ab8500_0p"},
+ {"DA6 Channel Volume", NULL, "DA_IN6"},
+
+ {"VIB1 DAC", NULL, "DA5 Channel Volume"},
+ {"VIB2 DAC", NULL, "DA6 Channel Volume"},
+
+ {"Vibra 1 Controller", "Audio Path", "VIB1 DAC"},
+ {"Vibra 2 Controller", "Audio Path", "VIB2 DAC"},
+ {"Vibra 1 Controller", "PWM Generator", "PWMGEN1"},
+ {"Vibra 2 Controller", "PWM Generator", "PWMGEN2"},
+
+ {"VIB1 Enable", NULL, "Vibra 1 Controller"},
+ {"VIB2 Enable", NULL, "Vibra 2 Controller"},
+
+ {"Vibra 1", NULL, "VIB1 Enable"},
+ {"Vibra 2", NULL, "VIB2 Enable"},
+
+
+ /* Mic 2 */
+
+ {"MIC2 V-AMICx Enable", NULL, "Mic 2"},
+
+ /* LineIn */
+ {"LINL Mute", NULL, "LineIn Left"},
+ {"LINR Mute", NULL, "LineIn Right"},
+
+ {"LINL Enable", NULL, "LINL Mute"},
+ {"LINR Enable", NULL, "LINR Mute"},
+
+ /* LineIn, Mic 2 */
+ {"Mic 2 or LINR Select", "LineIn Right", "LINR Enable"},
+ {"Mic 2 or LINR Select", "Mic 2", "MIC2 V-AMICx Enable"},
+
+ {"LINL ADC", NULL, "LINL Enable"},
+ {"LINR ADC", NULL, "Mic 2 or LINR Select"},
+
+ {"AD1 Source Select", "LineIn Left", "LINL ADC"},
+ {"AD2 Source Select", "LineIn Right", "LINR ADC"},
+
+ {"AD1 Channel Volume", NULL, "AD1 Source Select"},
+ {"AD2 Channel Volume", NULL, "AD2 Source Select"},
+
+ {"AD12 Enable", NULL, "AD1 Channel Volume"},
+ {"AD12 Enable", NULL, "AD2 Channel Volume"},
+
+ {"AD_OUT1", NULL, "ab8500_0c"},
+ {"AD_OUT1", NULL, "AD12 Enable"},
+ {"AD_OUT2", NULL, "ab8500_0c"},
+ {"AD_OUT2", NULL, "AD12 Enable"},
+
+ /* Mic 1 */
+
+ {"MIC1 Mute", NULL, "Mic 1"},
+
+ {"MIC1A V-AMICx Enable", NULL, "MIC1 Mute"},
+ {"MIC1B V-AMICx Enable", NULL, "MIC1 Mute"},
+
+ {"Mic 1a or 1b Select", "Mic 1a", "MIC1A V-AMICx Enable"},
+ {"Mic 1a or 1b Select", "Mic 1b", "MIC1B V-AMICx Enable"},
+
+ {"MIC1 ADC", NULL, "Mic 1a or 1b Select"},
+
+ {"AD3 Source Select", "Mic 1", "MIC1 ADC"},
+
+ {"AD3 Channel Volume", NULL, "AD3 Source Select"},
+
+ {"AD3 Enable", NULL, "AD3 Channel Volume"},
+
+ {"AD_OUT3", NULL, "ab8500_0c"},
+ {"AD_OUT3", NULL, "AD3 Enable"},
+
+ /* HD Capture path */
+
+ {"AD5 Source Select", "Mic 2", "LINR ADC"},
+ {"AD6 Source Select", "Mic 1", "MIC1 ADC"},
+
+ {"AD5 Channel Volume", NULL, "AD5 Source Select"},
+ {"AD6 Channel Volume", NULL, "AD6 Source Select"},
+
+ {"AD57 Enable", NULL, "AD5 Channel Volume"},
+ {"AD68 Enable", NULL, "AD6 Channel Volume"},
+
+ {"AD_OUT57", NULL, "ab8500_0c"},
+ {"AD_OUT57", NULL, "AD57 Enable"},
+ {"AD_OUT68", NULL, "ab8500_0c"},
+ {"AD_OUT68", NULL, "AD68 Enable"},
+
+ /* Digital Microphone path */
+
+ {"DMic 1", NULL, "V-DMIC"},
+ {"DMic 2", NULL, "V-DMIC"},
+ {"DMic 3", NULL, "V-DMIC"},
+ {"DMic 4", NULL, "V-DMIC"},
+ {"DMic 5", NULL, "V-DMIC"},
+ {"DMic 6", NULL, "V-DMIC"},
+
+ {"AD1 Source Select", NULL, "DMic 1"},
+ {"AD2 Source Select", NULL, "DMic 2"},
+ {"AD3 Source Select", NULL, "DMic 3"},
+ {"AD5 Source Select", NULL, "DMic 5"},
+ {"AD6 Source Select", NULL, "DMic 6"},
+
+ {"AD4 Channel Volume", NULL, "DMic 4"},
+ {"AD4 Enable", NULL, "AD4 Channel Volume"},
+
+ {"AD_OUT4", NULL, "ab8500_0c"},
+ {"AD_OUT4", NULL, "AD4 Enable"},
+
+ /* LineIn Bypass path */
+
+ {"LINL to HSL Volume", NULL, "LINL Enable"},
+ {"LINR to HSR Volume", NULL, "LINR Enable"},
+
+ {"HSL DAC Driver", NULL, "LINL to HSL Volume"},
+ {"HSR DAC Driver", NULL, "LINR to HSR Volume"},
+
+ /* ANC path (Acoustic Noise Cancellation) */
+
+ {"ANC Source", "Mic 2 / DMic 5", "AD5 Channel Volume"},
+ {"ANC Source", "Mic 1 / DMic 6", "AD6 Channel Volume"},
+
+ {"ANC", "Switch", "ANC Source"},
+
+ {"Speaker Left Source", "ANC", "ANC"},
+ {"Speaker Right Source", "ANC", "ANC"},
+ {"ANC to Earpiece", "Switch", "ANC"},
+
+ {"HSL Digital Volume", NULL, "ANC to Earpiece"},
+
+ /* Sidetone Filter path */
+
+ {"Sidetone Left Source", "LineIn Left", "AD12 Enable"},
+ {"Sidetone Left Source", "LineIn Right", "AD12 Enable"},
+ {"Sidetone Left Source", "Mic 1", "AD3 Enable"},
+ {"Sidetone Left Source", "Headset Left", "DA_IN1"},
+ {"Sidetone Right Source", "LineIn Right", "AD12 Enable"},
+ {"Sidetone Right Source", "Mic 1", "AD3 Enable"},
+ {"Sidetone Right Source", "DMic 4", "AD4 Enable"},
+ {"Sidetone Right Source", "Headset Right", "DA_IN2"},
+
+ {"STFIR1 Control", NULL, "Sidetone Left Source"},
+ {"STFIR2 Control", NULL, "Sidetone Right Source"},
+
+ {"STFIR1 Volume", NULL, "STFIR1 Control"},
+ {"STFIR2 Volume", NULL, "STFIR2 Control"},
+
+ {"DA1 Enable", NULL, "STFIR1 Volume"},
+ {"DA2 Enable", NULL, "STFIR2 Volume"},
+};
+
+static const struct snd_soc_dapm_route ab8500_dapm_routes_mic1a_vamicx[] = {
+ {"MIC1A V-AMICx Enable", NULL, "V-AMIC1"},
+ {"MIC1A V-AMICx Enable", NULL, "V-AMIC2"},
+};
+
+static const struct snd_soc_dapm_route ab8500_dapm_routes_mic1b_vamicx[] = {
+ {"MIC1B V-AMICx Enable", NULL, "V-AMIC1"},
+ {"MIC1B V-AMICx Enable", NULL, "V-AMIC2"},
+};
+
+static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = {
+ {"MIC2 V-AMICx Enable", NULL, "V-AMIC1"},
+ {"MIC2 V-AMICx Enable", NULL, "V-AMIC2"},
+};
+
+/* ANC FIR-coefficients configuration sequence */
+static void anc_fir(struct snd_soc_codec *codec,
+ unsigned int bnk, unsigned int par, unsigned int val)
+{
+ if (par == 0 && bnk == 0)
+ snd_soc_update_bits(codec, 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);
+
+ if (par == AB8500_ANC_FIR_COEFFS - 1 && bnk == 1)
+ snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ BIT(AB8500_ANCCONF1_ANCFIRUPDATE), 0);
+}
+
+/* ANC IIR-coefficients configuration sequence */
+static void anc_iir(struct snd_soc_codec *codec, unsigned int bnk,
+ unsigned int par, unsigned int val)
+{
+ if (par == 0) {
+ if (bnk == 0) {
+ snd_soc_update_bits(codec, 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,
+ BIT(AB8500_ANCCONF1_ANCIIRINIT), 0);
+ usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY);
+ } else {
+ snd_soc_update_bits(codec, 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_write(codec, AB8500_ANCCONF7, val >> 8 & 0xff);
+ snd_soc_write(codec, AB8500_ANCCONF8, val & 0xff);
+
+ if (par == AB8500_ANC_IIR_COEFFS - 1 && bnk == 1)
+ snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ BIT(AB8500_ANCCONF1_ANCIIRUPDATE), 0);
+}
+
+/* ANC IIR-/FIR-coefficients configuration sequence */
+static void anc_configure(struct snd_soc_codec *codec,
+ bool apply_fir, bool apply_iir)
+{
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ unsigned int bnk, par, val;
+
+ dev_dbg(codec->dev, "%s: Enter.\n", __func__);
+
+ if (apply_fir)
+ snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ BIT(AB8500_ANCCONF1_ENANC), 0);
+
+ snd_soc_update_bits(codec, 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,
+ drvdata->anc_fir_values[par]);
+ anc_fir(codec, 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,
+ drvdata->anc_iir_values[par]);
+ anc_iir(codec, bnk, par, val);
+ }
+
+ dev_dbg(codec->dev, "%s: Exit.\n", __func__);
+}
+
+/*
+ * Control-events
+ */
+
+static int sid_status_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+
+ mutex_lock(&drvdata->ctrl_lock);
+ ucontrol->value.integer.value[0] = drvdata->sid_status;
+ mutex_unlock(&drvdata->ctrl_lock);
+
+ return 0;
+}
+
+/* Write sidetone FIR-coefficients configuration sequence */
+static int sid_status_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ unsigned int param, sidconf, val;
+ int status = 1;
+
+ dev_dbg(codec->dev, "%s: Enter\n", __func__);
+
+ if (ucontrol->value.integer.value[0] != SID_APPLY_FIR) {
+ dev_err(codec->dev,
+ "%s: ERROR: This control supports '%s' only!\n",
+ __func__, enum_sid_state[SID_APPLY_FIR]);
+ return -EIO;
+ }
+
+ mutex_lock(&drvdata->ctrl_lock);
+
+ sidconf = snd_soc_read(codec, 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",
+ __func__);
+ status = -EPERM;
+ } else {
+ status = -EBUSY;
+ }
+ goto out;
+ }
+
+ snd_soc_write(codec, 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);
+ }
+
+ snd_soc_update_bits(codec, AB8500_SIDFIRADR,
+ BIT(AB8500_SIDFIRADR_FIRSIDSET),
+ BIT(AB8500_SIDFIRADR_FIRSIDSET));
+ snd_soc_update_bits(codec, AB8500_SIDFIRADR,
+ BIT(AB8500_SIDFIRADR_FIRSIDSET), 0);
+
+ drvdata->sid_status = SID_FIR_CONFIGURED;
+
+out:
+ mutex_unlock(&drvdata->ctrl_lock);
+
+ dev_dbg(codec->dev, "%s: Exit\n", __func__);
+
+ return status;
+}
+
+static int anc_status_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+
+ mutex_lock(&drvdata->ctrl_lock);
+ ucontrol->value.integer.value[0] = drvdata->anc_status;
+ mutex_unlock(&drvdata->ctrl_lock);
+
+ return 0;
+}
+
+static int anc_status_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ struct device *dev = codec->dev;
+ bool apply_fir, apply_iir;
+ unsigned int req;
+ int status;
+
+ dev_dbg(dev, "%s: Enter.\n", __func__);
+
+ mutex_lock(&drvdata->ctrl_lock);
+
+ req = ucontrol->value.integer.value[0];
+ if (req >= ARRAY_SIZE(enum_anc_state)) {
+ status = -EINVAL;
+ goto cleanup;
+ }
+ if (req != ANC_APPLY_FIR_IIR && req != ANC_APPLY_FIR &&
+ req != ANC_APPLY_IIR) {
+ dev_err(dev, "%s: ERROR: Unsupported status to set '%s'!\n",
+ __func__, enum_anc_state[req]);
+ status = -EINVAL;
+ goto cleanup;
+ }
+ apply_fir = req == ANC_APPLY_FIR || req == ANC_APPLY_FIR_IIR;
+ apply_iir = req == ANC_APPLY_IIR || req == ANC_APPLY_FIR_IIR;
+
+ status = snd_soc_dapm_force_enable_pin(&codec->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);
+
+ anc_configure(codec, apply_fir, apply_iir);
+
+ if (apply_fir) {
+ if (drvdata->anc_status == ANC_IIR_CONFIGURED)
+ drvdata->anc_status = ANC_FIR_IIR_CONFIGURED;
+ else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED)
+ drvdata->anc_status = ANC_FIR_CONFIGURED;
+ }
+ if (apply_iir) {
+ if (drvdata->anc_status == ANC_FIR_CONFIGURED)
+ drvdata->anc_status = ANC_FIR_IIR_CONFIGURED;
+ else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED)
+ drvdata->anc_status = ANC_IIR_CONFIGURED;
+ }
+
+ status = snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input");
+ snd_soc_dapm_sync(&codec->dapm);
+
+cleanup:
+ mutex_unlock(&drvdata->ctrl_lock);
+
+ if (status < 0)
+ dev_err(dev, "%s: Unable to configure ANC! (status = %d)\n",
+ __func__, status);
+
+ dev_dbg(dev, "%s: Exit.\n", __func__);
+
+ return (status < 0) ? status : 1;
+}
+
+static int filter_control_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct filter_control *fc =
+ (struct filter_control *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = fc->count;
+ uinfo->value.integer.min = fc->min;
+ uinfo->value.integer.max = fc->max;
+
+ return 0;
+}
+
+static int filter_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
+ struct filter_control *fc =
+ (struct filter_control *)kcontrol->private_value;
+ unsigned int i;
+
+ mutex_lock(&drvdata->ctrl_lock);
+ for (i = 0; i < fc->count; i++)
+ ucontrol->value.integer.value[i] = fc->value[i];
+ mutex_unlock(&drvdata->ctrl_lock);
+
+ return 0;
+}
+
+static int filter_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
+ struct filter_control *fc =
+ (struct filter_control *)kcontrol->private_value;
+ unsigned int i;
+
+ mutex_lock(&drvdata->ctrl_lock);
+ for (i = 0; i < fc->count; i++)
+ fc->value[i] = ucontrol->value.integer.value[i];
+ mutex_unlock(&drvdata->ctrl_lock);
+
+ return 0;
+}
+
+/*
+ * Controls - Non-DAPM ASoC
+ */
+
+static DECLARE_TLV_DB_SCALE(adx_dig_gain_tlv, -3200, 100, 1);
+/* -32dB = Mute */
+
+static DECLARE_TLV_DB_SCALE(dax_dig_gain_tlv, -6300, 100, 1);
+/* -63dB = Mute */
+
+static DECLARE_TLV_DB_SCALE(hs_ear_dig_gain_tlv, -100, 100, 1);
+/* -1dB = Mute */
+
+static const unsigned int hs_gain_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 3, TLV_DB_SCALE_ITEM(-3200, 400, 0),
+ 4, 15, TLV_DB_SCALE_ITEM(-1800, 200, 0),
+};
+
+static DECLARE_TLV_DB_SCALE(mic_gain_tlv, 0, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(lin_gain_tlv, -1000, 200, 0);
+
+static DECLARE_TLV_DB_SCALE(lin2hs_gain_tlv, -3800, 200, 1);
+/* -38dB = Mute */
+
+static const char * const enum_hsfadspeed[] = {"2ms", "0.5ms", "10.6ms",
+ "5ms"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_hsfadspeed,
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_HSFADSPEED, enum_hsfadspeed);
+
+static const char * const enum_envdetthre[] = {
+ "250mV", "300mV", "350mV", "400mV",
+ "450mV", "500mV", "550mV", "600mV",
+ "650mV", "700mV", "750mV", "800mV",
+ "850mV", "900mV", "950mV", "1.00V" };
+static SOC_ENUM_SINGLE_DECL(soc_enum_envdeththre,
+ AB8500_ENVCPCONF, AB8500_ENVCPCONF_ENVDETHTHRE, enum_envdetthre);
+static SOC_ENUM_SINGLE_DECL(soc_enum_envdetlthre,
+ AB8500_ENVCPCONF, AB8500_ENVCPCONF_ENVDETLTHRE, enum_envdetthre);
+static const char * const enum_envdettime[] = {
+ "26.6us", "53.2us", "106us", "213us",
+ "426us", "851us", "1.70ms", "3.40ms",
+ "6.81ms", "13.6ms", "27.2ms", "54.5ms",
+ "109ms", "218ms", "436ms", "872ms" };
+static SOC_ENUM_SINGLE_DECL(soc_enum_envdettime,
+ AB8500_SIGENVCONF, AB8500_SIGENVCONF_ENVDETTIME, enum_envdettime);
+
+static const char * const enum_sinc31[] = {"Sinc 3", "Sinc 1"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_hsesinc, AB8500_HSLEARDIGGAIN,
+ AB8500_HSLEARDIGGAIN_HSSINC1, enum_sinc31);
+
+static const char * const enum_fadespeed[] = {"1ms", "4ms", "8ms", "16ms"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_fadespeed, AB8500_HSRDIGGAIN,
+ AB8500_HSRDIGGAIN_FADESPEED, enum_fadespeed);
+
+/* Earpiece */
+
+static const char * const enum_lowpow[] = {"Normal", "Low Power"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_eardaclowpow, AB8500_ANACONF1,
+ AB8500_ANACONF1_EARDACLOWPOW, enum_lowpow);
+static SOC_ENUM_SINGLE_DECL(soc_enum_eardrvlowpow, AB8500_ANACONF1,
+ AB8500_ANACONF1_EARDRVLOWPOW, enum_lowpow);
+
+static const char * const enum_av_mode[] = {"Audio", "Voice"};
+static SOC_ENUM_DOUBLE_DECL(soc_enum_ad12voice, AB8500_ADFILTCONF,
+ AB8500_ADFILTCONF_AD1VOICE, AB8500_ADFILTCONF_AD2VOICE, enum_av_mode);
+static SOC_ENUM_DOUBLE_DECL(soc_enum_ad34voice, AB8500_ADFILTCONF,
+ AB8500_ADFILTCONF_AD3VOICE, AB8500_ADFILTCONF_AD4VOICE, enum_av_mode);
+
+/* DA */
+
+static SOC_ENUM_SINGLE_DECL(soc_enum_da12voice,
+ AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_DA12VOICE,
+ enum_av_mode);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da34voice,
+ AB8500_DASLOTCONF3, AB8500_DASLOTCONF3_DA34VOICE,
+ enum_av_mode);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da56voice,
+ AB8500_DASLOTCONF5, AB8500_DASLOTCONF5_DA56VOICE,
+ enum_av_mode);
+
+static const char * const enum_da2hslr[] = {"Sidetone", "Audio Path"};
+static SOC_ENUM_DOUBLE_DECL(soc_enum_da2hslr, AB8500_DIGMULTCONF1,
+ AB8500_DIGMULTCONF1_DATOHSLEN,
+ AB8500_DIGMULTCONF1_DATOHSREN, enum_da2hslr);
+
+static const char * const enum_sinc53[] = {"Sinc 5", "Sinc 3"};
+static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic12sinc, AB8500_DMICFILTCONF,
+ AB8500_DMICFILTCONF_DMIC1SINC3,
+ AB8500_DMICFILTCONF_DMIC2SINC3, enum_sinc53);
+static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic34sinc, AB8500_DMICFILTCONF,
+ AB8500_DMICFILTCONF_DMIC3SINC3,
+ AB8500_DMICFILTCONF_DMIC4SINC3, enum_sinc53);
+static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic56sinc, AB8500_DMICFILTCONF,
+ AB8500_DMICFILTCONF_DMIC5SINC3,
+ AB8500_DMICFILTCONF_DMIC6SINC3, enum_sinc53);
+
+/* Digital interface - DA from slot mapping */
+static const char * const enum_da_from_slot_map[] = {"SLOT0",
+ "SLOT1",
+ "SLOT2",
+ "SLOT3",
+ "SLOT4",
+ "SLOT5",
+ "SLOT6",
+ "SLOT7",
+ "SLOT8",
+ "SLOT9",
+ "SLOT10",
+ "SLOT11",
+ "SLOT12",
+ "SLOT13",
+ "SLOT14",
+ "SLOT15",
+ "SLOT16",
+ "SLOT17",
+ "SLOT18",
+ "SLOT19",
+ "SLOT20",
+ "SLOT21",
+ "SLOT22",
+ "SLOT23",
+ "SLOT24",
+ "SLOT25",
+ "SLOT26",
+ "SLOT27",
+ "SLOT28",
+ "SLOT29",
+ "SLOT30",
+ "SLOT31"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_da1slotmap,
+ AB8500_DASLOTCONF1, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da2slotmap,
+ AB8500_DASLOTCONF2, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da3slotmap,
+ AB8500_DASLOTCONF3, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da4slotmap,
+ AB8500_DASLOTCONF4, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da5slotmap,
+ AB8500_DASLOTCONF5, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da6slotmap,
+ AB8500_DASLOTCONF6, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da7slotmap,
+ AB8500_DASLOTCONF7, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da8slotmap,
+ AB8500_DASLOTCONF8, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+ enum_da_from_slot_map);
+
+/* Digital interface - AD to slot mapping */
+static const char * const enum_ad_to_slot_map[] = {"AD_OUT1",
+ "AD_OUT2",
+ "AD_OUT3",
+ "AD_OUT4",
+ "AD_OUT5",
+ "AD_OUT6",
+ "AD_OUT7",
+ "AD_OUT8",
+ "zeroes",
+ "zeroes",
+ "zeroes",
+ "zeroes",
+ "tristate",
+ "tristate",
+ "tristate",
+ "tristate"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot0map,
+ AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot1map,
+ AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot2map,
+ AB8500_ADSLOTSEL2, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot3map,
+ AB8500_ADSLOTSEL2, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot4map,
+ AB8500_ADSLOTSEL3, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot5map,
+ AB8500_ADSLOTSEL3, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot6map,
+ AB8500_ADSLOTSEL4, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot7map,
+ AB8500_ADSLOTSEL4, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot8map,
+ AB8500_ADSLOTSEL5, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot9map,
+ AB8500_ADSLOTSEL5, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot10map,
+ AB8500_ADSLOTSEL6, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot11map,
+ AB8500_ADSLOTSEL6, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot12map,
+ AB8500_ADSLOTSEL7, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot13map,
+ AB8500_ADSLOTSEL7, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot14map,
+ AB8500_ADSLOTSEL8, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot15map,
+ AB8500_ADSLOTSEL8, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot16map,
+ AB8500_ADSLOTSEL9, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot17map,
+ AB8500_ADSLOTSEL9, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot18map,
+ AB8500_ADSLOTSEL10, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot19map,
+ AB8500_ADSLOTSEL10, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot20map,
+ AB8500_ADSLOTSEL11, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot21map,
+ AB8500_ADSLOTSEL11, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot22map,
+ AB8500_ADSLOTSEL12, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot23map,
+ AB8500_ADSLOTSEL12, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot24map,
+ AB8500_ADSLOTSEL13, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot25map,
+ AB8500_ADSLOTSEL13, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot26map,
+ AB8500_ADSLOTSEL14, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot27map,
+ AB8500_ADSLOTSEL14, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot28map,
+ AB8500_ADSLOTSEL15, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot29map,
+ AB8500_ADSLOTSEL15, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot30map,
+ AB8500_ADSLOTSEL16, AB8500_ADSLOTSELX_EVEN_SHIFT,
+ enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot31map,
+ AB8500_ADSLOTSEL16, AB8500_ADSLOTSELX_ODD_SHIFT,
+ enum_ad_to_slot_map);
+
+/* Digital interface - Burst mode */
+static const char * const enum_mask[] = {"Unmasked", "Masked"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomask,
+ AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFOMASK,
+ enum_mask);
+static const char * const enum_bitclk0[] = {"19_2_MHz", "38_4_MHz"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_bfifo19m2,
+ AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFO19M2,
+ enum_bitclk0);
+static const char * const enum_slavemaster[] = {"Slave", "Master"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast,
+ AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOMAST_SHIFT,
+ enum_slavemaster);
+
+/* Sidetone */
+static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state);
+
+/* ANC */
+static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_ancstate, enum_anc_state);
+
+static struct snd_kcontrol_new ab8500_ctrls[] = {
+ /* Charge pump */
+ SOC_ENUM("Charge Pump High Threshold For Low Voltage",
+ soc_enum_envdeththre),
+ SOC_ENUM("Charge Pump Low Threshold For Low Voltage",
+ soc_enum_envdetlthre),
+ SOC_SINGLE("Charge Pump Envelope Detection Switch",
+ AB8500_SIGENVCONF, AB8500_SIGENVCONF_ENVDETCPEN,
+ 1, 0),
+ SOC_ENUM("Charge Pump Envelope Detection Decay Time",
+ soc_enum_envdettime),
+
+ /* Headset */
+ SOC_ENUM("Headset Mode", soc_enum_da12voice),
+ SOC_SINGLE("Headset High Pass Switch",
+ AB8500_ANACONF1, AB8500_ANACONF1_HSHPEN,
+ 1, 0),
+ SOC_SINGLE("Headset Low Power Switch",
+ AB8500_ANACONF1, AB8500_ANACONF1_HSLOWPOW,
+ 1, 0),
+ SOC_SINGLE("Headset DAC Low Power Switch",
+ AB8500_ANACONF1, AB8500_ANACONF1_DACLOWPOW1,
+ 1, 0),
+ SOC_SINGLE("Headset DAC Drv Low Power Switch",
+ AB8500_ANACONF1, AB8500_ANACONF1_DACLOWPOW0,
+ 1, 0),
+ SOC_ENUM("Headset Fade Speed", soc_enum_hsfadspeed),
+ SOC_ENUM("Headset Source", soc_enum_da2hslr),
+ SOC_ENUM("Headset Filter", soc_enum_hsesinc),
+ SOC_DOUBLE_R_TLV("Headset Master Volume",
+ AB8500_DADIGGAIN1, AB8500_DADIGGAIN2,
+ 0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv),
+ SOC_DOUBLE_R_TLV("Headset Digital Volume",
+ AB8500_HSLEARDIGGAIN, AB8500_HSRDIGGAIN,
+ 0, AB8500_HSLEARDIGGAIN_HSLDGAIN_MAX, 1, hs_ear_dig_gain_tlv),
+ SOC_DOUBLE_TLV("Headset Volume",
+ AB8500_ANAGAIN3,
+ AB8500_ANAGAIN3_HSLGAIN, AB8500_ANAGAIN3_HSRGAIN,
+ AB8500_ANAGAIN3_HSXGAIN_MAX, 1, hs_gain_tlv),
+
+ /* Earpiece */
+ SOC_ENUM("Earpiece DAC Mode",
+ soc_enum_eardaclowpow),
+ SOC_ENUM("Earpiece DAC Drv Mode",
+ soc_enum_eardrvlowpow),
+
+ /* HandsFree */
+ SOC_ENUM("HF Mode", soc_enum_da34voice),
+ SOC_SINGLE("HF and Headset Swap Switch",
+ AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_SWAPDA12_34,
+ 1, 0),
+ SOC_DOUBLE("HF Low EMI Mode Switch",
+ AB8500_CLASSDCONF1,
+ AB8500_CLASSDCONF1_HFLSWAPEN, AB8500_CLASSDCONF1_HFRSWAPEN,
+ 1, 0),
+ SOC_DOUBLE("HF FIR Bypass Switch",
+ AB8500_CLASSDCONF2,
+ AB8500_CLASSDCONF2_FIRBYP0, AB8500_CLASSDCONF2_FIRBYP1,
+ 1, 0),
+ SOC_DOUBLE("HF High Volume Switch",
+ AB8500_CLASSDCONF2,
+ AB8500_CLASSDCONF2_HIGHVOLEN0, AB8500_CLASSDCONF2_HIGHVOLEN1,
+ 1, 0),
+ SOC_SINGLE("HF L and R Bridge Switch",
+ AB8500_CLASSDCONF1, AB8500_CLASSDCONF1_PARLHF,
+ 1, 0),
+ SOC_DOUBLE_R_TLV("HF Master Volume",
+ AB8500_DADIGGAIN3, AB8500_DADIGGAIN4,
+ 0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv),
+
+ /* Vibra */
+ SOC_DOUBLE("Vibra High Volume Switch",
+ AB8500_CLASSDCONF2,
+ AB8500_CLASSDCONF2_HIGHVOLEN2, AB8500_CLASSDCONF2_HIGHVOLEN3,
+ 1, 0),
+ SOC_DOUBLE("Vibra Low EMI Mode Switch",
+ AB8500_CLASSDCONF1,
+ AB8500_CLASSDCONF1_VIB1SWAPEN, AB8500_CLASSDCONF1_VIB2SWAPEN,
+ 1, 0),
+ SOC_DOUBLE("Vibra FIR Bypass Switch",
+ AB8500_CLASSDCONF2,
+ AB8500_CLASSDCONF2_FIRBYP2, AB8500_CLASSDCONF2_FIRBYP3,
+ 1, 0),
+ SOC_ENUM("Vibra Mode", soc_enum_da56voice),
+ SOC_DOUBLE_R("Vibra PWM Duty Cycle N",
+ AB8500_PWMGENCONF3, AB8500_PWMGENCONF5,
+ AB8500_PWMGENCONFX_PWMVIBXDUTCYC,
+ AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX, 0),
+ SOC_DOUBLE_R("Vibra PWM Duty Cycle P",
+ AB8500_PWMGENCONF2, AB8500_PWMGENCONF4,
+ AB8500_PWMGENCONFX_PWMVIBXDUTCYC,
+ AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX, 0),
+ SOC_SINGLE("Vibra 1 and 2 Bridge Switch",
+ AB8500_CLASSDCONF1, AB8500_CLASSDCONF1_PARLVIB,
+ 1, 0),
+ SOC_DOUBLE_R_TLV("Vibra Master Volume",
+ AB8500_DADIGGAIN5, AB8500_DADIGGAIN6,
+ 0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv),
+
+ /* HandsFree, Vibra */
+ SOC_SINGLE("ClassD High Pass Volume",
+ AB8500_CLASSDCONF3, AB8500_CLASSDCONF3_DITHHPGAIN,
+ AB8500_CLASSDCONF3_DITHHPGAIN_MAX, 0),
+ SOC_SINGLE("ClassD White Volume",
+ AB8500_CLASSDCONF3, AB8500_CLASSDCONF3_DITHWGAIN,
+ AB8500_CLASSDCONF3_DITHWGAIN_MAX, 0),
+
+ /* Mic 1, Mic 2, LineIn */
+ SOC_DOUBLE_R_TLV("Mic Master Volume",
+ AB8500_ADDIGGAIN3, AB8500_ADDIGGAIN4,
+ 0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv),
+
+ /* Mic 1 */
+ SOC_SINGLE_TLV("Mic 1",
+ AB8500_ANAGAIN1,
+ AB8500_ANAGAINX_MICXGAIN,
+ AB8500_ANAGAINX_MICXGAIN_MAX, 0, mic_gain_tlv),
+ SOC_SINGLE("Mic 1 Low Power Switch",
+ AB8500_ANAGAIN1, AB8500_ANAGAINX_LOWPOWMICX,
+ 1, 0),
+
+ /* Mic 2 */
+ SOC_DOUBLE("Mic High Pass Switch",
+ AB8500_ADFILTCONF,
+ AB8500_ADFILTCONF_AD3NH, AB8500_ADFILTCONF_AD4NH,
+ 1, 1),
+ SOC_ENUM("Mic Mode", soc_enum_ad34voice),
+ SOC_ENUM("Mic Filter", soc_enum_dmic34sinc),
+ SOC_SINGLE_TLV("Mic 2",
+ AB8500_ANAGAIN2,
+ AB8500_ANAGAINX_MICXGAIN,
+ AB8500_ANAGAINX_MICXGAIN_MAX, 0, mic_gain_tlv),
+ SOC_SINGLE("Mic 2 Low Power Switch",
+ AB8500_ANAGAIN2, AB8500_ANAGAINX_LOWPOWMICX,
+ 1, 0),
+
+ /* LineIn */
+ SOC_DOUBLE("LineIn High Pass Switch",
+ AB8500_ADFILTCONF,
+ AB8500_ADFILTCONF_AD1NH, AB8500_ADFILTCONF_AD2NH,
+ 1, 1),
+ SOC_ENUM("LineIn Filter", soc_enum_dmic12sinc),
+ SOC_ENUM("LineIn Mode", soc_enum_ad12voice),
+ SOC_DOUBLE_R_TLV("LineIn Master Volume",
+ AB8500_ADDIGGAIN1, AB8500_ADDIGGAIN2,
+ 0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv),
+ SOC_DOUBLE_TLV("LineIn",
+ AB8500_ANAGAIN4,
+ AB8500_ANAGAIN4_LINLGAIN, AB8500_ANAGAIN4_LINRGAIN,
+ AB8500_ANAGAIN4_LINXGAIN_MAX, 0, lin_gain_tlv),
+ SOC_DOUBLE_R_TLV("LineIn to Headset Volume",
+ AB8500_DIGLINHSLGAIN, AB8500_DIGLINHSRGAIN,
+ AB8500_DIGLINHSXGAIN_LINTOHSXGAIN,
+ AB8500_DIGLINHSXGAIN_LINTOHSXGAIN_MAX,
+ 1, lin2hs_gain_tlv),
+
+ /* DMic */
+ SOC_ENUM("DMic Filter", soc_enum_dmic56sinc),
+ SOC_DOUBLE_R_TLV("DMic Master Volume",
+ AB8500_ADDIGGAIN5, AB8500_ADDIGGAIN6,
+ 0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv),
+
+ /* Digital gains */
+ SOC_ENUM("Digital Gain Fade Speed", soc_enum_fadespeed),
+
+ /* Analog loopback */
+ SOC_DOUBLE_R_TLV("Analog Loopback Volume",
+ AB8500_ADDIGLOOPGAIN1, AB8500_ADDIGLOOPGAIN2,
+ 0, AB8500_ADDIGLOOPGAINX_ADXLBGAIN_MAX, 1, dax_dig_gain_tlv),
+
+ /* Digital interface - DA from slot mapping */
+ SOC_ENUM("Digital Interface DA 1 From Slot Map", soc_enum_da1slotmap),
+ SOC_ENUM("Digital Interface DA 2 From Slot Map", soc_enum_da2slotmap),
+ SOC_ENUM("Digital Interface DA 3 From Slot Map", soc_enum_da3slotmap),
+ SOC_ENUM("Digital Interface DA 4 From Slot Map", soc_enum_da4slotmap),
+ SOC_ENUM("Digital Interface DA 5 From Slot Map", soc_enum_da5slotmap),
+ SOC_ENUM("Digital Interface DA 6 From Slot Map", soc_enum_da6slotmap),
+ SOC_ENUM("Digital Interface DA 7 From Slot Map", soc_enum_da7slotmap),
+ SOC_ENUM("Digital Interface DA 8 From Slot Map", soc_enum_da8slotmap),
+
+ /* Digital interface - AD to slot mapping */
+ SOC_ENUM("Digital Interface AD To Slot 0 Map", soc_enum_adslot0map),
+ SOC_ENUM("Digital Interface AD To Slot 1 Map", soc_enum_adslot1map),
+ SOC_ENUM("Digital Interface AD To Slot 2 Map", soc_enum_adslot2map),
+ SOC_ENUM("Digital Interface AD To Slot 3 Map", soc_enum_adslot3map),
+ SOC_ENUM("Digital Interface AD To Slot 4 Map", soc_enum_adslot4map),
+ SOC_ENUM("Digital Interface AD To Slot 5 Map", soc_enum_adslot5map),
+ SOC_ENUM("Digital Interface AD To Slot 6 Map", soc_enum_adslot6map),
+ SOC_ENUM("Digital Interface AD To Slot 7 Map", soc_enum_adslot7map),
+ SOC_ENUM("Digital Interface AD To Slot 8 Map", soc_enum_adslot8map),
+ SOC_ENUM("Digital Interface AD To Slot 9 Map", soc_enum_adslot9map),
+ SOC_ENUM("Digital Interface AD To Slot 10 Map", soc_enum_adslot10map),
+ SOC_ENUM("Digital Interface AD To Slot 11 Map", soc_enum_adslot11map),
+ SOC_ENUM("Digital Interface AD To Slot 12 Map", soc_enum_adslot12map),
+ SOC_ENUM("Digital Interface AD To Slot 13 Map", soc_enum_adslot13map),
+ SOC_ENUM("Digital Interface AD To Slot 14 Map", soc_enum_adslot14map),
+ SOC_ENUM("Digital Interface AD To Slot 15 Map", soc_enum_adslot15map),
+ SOC_ENUM("Digital Interface AD To Slot 16 Map", soc_enum_adslot16map),
+ SOC_ENUM("Digital Interface AD To Slot 17 Map", soc_enum_adslot17map),
+ SOC_ENUM("Digital Interface AD To Slot 18 Map", soc_enum_adslot18map),
+ SOC_ENUM("Digital Interface AD To Slot 19 Map", soc_enum_adslot19map),
+ SOC_ENUM("Digital Interface AD To Slot 20 Map", soc_enum_adslot20map),
+ SOC_ENUM("Digital Interface AD To Slot 21 Map", soc_enum_adslot21map),
+ SOC_ENUM("Digital Interface AD To Slot 22 Map", soc_enum_adslot22map),
+ SOC_ENUM("Digital Interface AD To Slot 23 Map", soc_enum_adslot23map),
+ SOC_ENUM("Digital Interface AD To Slot 24 Map", soc_enum_adslot24map),
+ SOC_ENUM("Digital Interface AD To Slot 25 Map", soc_enum_adslot25map),
+ SOC_ENUM("Digital Interface AD To Slot 26 Map", soc_enum_adslot26map),
+ SOC_ENUM("Digital Interface AD To Slot 27 Map", soc_enum_adslot27map),
+ SOC_ENUM("Digital Interface AD To Slot 28 Map", soc_enum_adslot28map),
+ SOC_ENUM("Digital Interface AD To Slot 29 Map", soc_enum_adslot29map),
+ SOC_ENUM("Digital Interface AD To Slot 30 Map", soc_enum_adslot30map),
+ SOC_ENUM("Digital Interface AD To Slot 31 Map", soc_enum_adslot31map),
+
+ /* Digital interface - Loopback */
+ SOC_SINGLE("Digital Interface AD 1 Loopback Switch",
+ AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_DAI7TOADO1,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 2 Loopback Switch",
+ AB8500_DASLOTCONF2, AB8500_DASLOTCONF2_DAI8TOADO2,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 3 Loopback Switch",
+ AB8500_DASLOTCONF3, AB8500_DASLOTCONF3_DAI7TOADO3,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 4 Loopback Switch",
+ AB8500_DASLOTCONF4, AB8500_DASLOTCONF4_DAI8TOADO4,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 5 Loopback Switch",
+ AB8500_DASLOTCONF5, AB8500_DASLOTCONF5_DAI7TOADO5,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 6 Loopback Switch",
+ AB8500_DASLOTCONF6, AB8500_DASLOTCONF6_DAI8TOADO6,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 7 Loopback Switch",
+ AB8500_DASLOTCONF7, AB8500_DASLOTCONF7_DAI8TOADO7,
+ 1, 0),
+ SOC_SINGLE("Digital Interface AD 8 Loopback Switch",
+ AB8500_DASLOTCONF8, AB8500_DASLOTCONF8_DAI7TOADO8,
+ 1, 0),
+
+ /* Digital interface - Burst FIFO */
+ SOC_SINGLE("Digital Interface 0 FIFO Enable Switch",
+ AB8500_DIGIFCONF3, AB8500_DIGIFCONF3_IF0BFIFOEN,
+ 1, 0),
+ SOC_ENUM("Burst FIFO Mask", soc_enum_bfifomask),
+ SOC_ENUM("Burst FIFO Bit-clock Frequency", soc_enum_bfifo19m2),
+ SOC_SINGLE("Burst FIFO Threshold",
+ AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFOINT_SHIFT,
+ AB8500_FIFOCONF1_BFIFOINT_MAX, 0),
+ SOC_SINGLE("Burst FIFO Length",
+ AB8500_FIFOCONF2, AB8500_FIFOCONF2_BFIFOTX_SHIFT,
+ AB8500_FIFOCONF2_BFIFOTX_MAX, 0),
+ SOC_SINGLE("Burst FIFO EOS Extra Slots",
+ AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOEXSL_SHIFT,
+ AB8500_FIFOCONF3_BFIFOEXSL_MAX, 0),
+ SOC_SINGLE("Burst FIFO FS Extra Bit-clocks",
+ AB8500_FIFOCONF3, AB8500_FIFOCONF3_PREBITCLK0_SHIFT,
+ AB8500_FIFOCONF3_PREBITCLK0_MAX, 0),
+ SOC_ENUM("Burst FIFO Interface Mode", soc_enum_bfifomast),
+
+ SOC_SINGLE("Burst FIFO Interface Switch",
+ AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFORUN_SHIFT,
+ 1, 0),
+ SOC_SINGLE("Burst FIFO Switch Frame Number",
+ AB8500_FIFOCONF4, AB8500_FIFOCONF4_BFIFOFRAMSW_SHIFT,
+ AB8500_FIFOCONF4_BFIFOFRAMSW_MAX, 0),
+ SOC_SINGLE("Burst FIFO Wake Up Delay",
+ AB8500_FIFOCONF5, AB8500_FIFOCONF5_BFIFOWAKEUP_SHIFT,
+ AB8500_FIFOCONF5_BFIFOWAKEUP_MAX, 0),
+ SOC_SINGLE("Burst FIFO Samples In FIFO",
+ AB8500_FIFOCONF6, AB8500_FIFOCONF6_BFIFOSAMPLE_SHIFT,
+ AB8500_FIFOCONF6_BFIFOSAMPLE_MAX, 0),
+
+ /* ANC */
+ SOC_ENUM_EXT("ANC Status", soc_enum_ancstate,
+ anc_status_control_get, anc_status_control_put),
+ SOC_SINGLE_XR_SX("ANC Warp Delay Shift",
+ AB8500_ANCCONF2, 1, AB8500_ANCCONF2_SHIFT,
+ AB8500_ANCCONF2_MIN, AB8500_ANCCONF2_MAX, 0),
+ SOC_SINGLE_XR_SX("ANC FIR Output Shift",
+ AB8500_ANCCONF3, 1, AB8500_ANCCONF3_SHIFT,
+ AB8500_ANCCONF3_MIN, AB8500_ANCCONF3_MAX, 0),
+ SOC_SINGLE_XR_SX("ANC IIR Output Shift",
+ AB8500_ANCCONF4, 1, AB8500_ANCCONF4_SHIFT,
+ AB8500_ANCCONF4_MIN, AB8500_ANCCONF4_MAX, 0),
+ SOC_SINGLE_XR_SX("ANC Warp Delay",
+ AB8500_ANCCONF9, 2, AB8500_ANC_WARP_DELAY_SHIFT,
+ AB8500_ANC_WARP_DELAY_MIN, AB8500_ANC_WARP_DELAY_MAX, 0),
+
+ /* Sidetone */
+ SOC_ENUM_EXT("Sidetone Status", soc_enum_sidstate,
+ sid_status_control_get, sid_status_control_put),
+ SOC_SINGLE_STROBE("Sidetone Reset",
+ AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0),
+};
+
+static struct snd_kcontrol_new ab8500_filter_controls[] = {
+ AB8500_FILTER_CONTROL("ANC FIR Coefficients", AB8500_ANC_FIR_COEFFS,
+ AB8500_ANC_FIR_COEFF_MIN, AB8500_ANC_FIR_COEFF_MAX),
+ AB8500_FILTER_CONTROL("ANC IIR Coefficients", AB8500_ANC_IIR_COEFFS,
+ AB8500_ANC_IIR_COEFF_MIN, AB8500_ANC_IIR_COEFF_MAX),
+ AB8500_FILTER_CONTROL("Sidetone FIR Coefficients",
+ AB8500_SID_FIR_COEFFS, AB8500_SID_FIR_COEFF_MIN,
+ AB8500_SID_FIR_COEFF_MAX)
+};
+enum ab8500_filter {
+ AB8500_FILTER_ANC_FIR = 0,
+ AB8500_FILTER_ANC_IIR = 1,
+ AB8500_FILTER_SID_FIR = 2,
+};
+
+/*
+ * Extended interface for codec-driver
+ */
+
+static int ab8500_audio_init_audioblock(struct snd_soc_codec *codec)
+{
+ int status;
+
+ dev_dbg(codec->dev, "%s: Enter.\n", __func__);
+
+ /* Reset audio-registers and disable 32kHz-clock output 2 */
+ status = ab8500_sysctrl_write(AB8500_STW4500CTRL3,
+ AB8500_STW4500CTRL3_CLK32KOUT2DIS |
+ AB8500_STW4500CTRL3_RESETAUDN,
+ AB8500_STW4500CTRL3_RESETAUDN);
+ if (status < 0)
+ return status;
+
+ return 0;
+}
+
+static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
+ struct amic_settings *amics)
+{
+ u8 value8;
+ unsigned int value;
+ int status;
+ const struct snd_soc_dapm_route *route;
+
+ dev_dbg(codec->dev, "%s: Enter.\n", __func__);
+
+ /* Set DMic-clocks to outputs */
+ status = abx500_get_register_interruptible(codec->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,
+ AB8500_MISC,
+ AB8500_GPIO_DIR4_REG,
+ value);
+ if (status < 0)
+ return status;
+
+ /* Attach regulators to AMic DAPM-paths */
+ dev_dbg(codec->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__,
+ 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__,
+ 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);
+ if (status < 0) {
+ dev_err(codec->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__,
+ amic_type_str(amics->mic1_type));
+ snd_soc_update_bits(codec, 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__,
+ amic_type_str(amics->mic2_type));
+ snd_soc_update_bits(codec, AB8500_ANAGAIN2, AB8500_ANAGAINX_ENSEMICX,
+ amics->mic2_type == AMIC_TYPE_DIFFERENTIAL ?
+ 0 : AB8500_ANAGAINX_ENSEMICX);
+
+ return 0;
+}
+
+static int ab8500_audio_set_ear_cmv(struct snd_soc_codec *codec,
+ enum ear_cm_voltage ear_cmv)
+{
+ char *cmv_str;
+
+ switch (ear_cmv) {
+ case EAR_CMV_0_95V:
+ cmv_str = "0.95V";
+ break;
+ case EAR_CMV_1_10V:
+ cmv_str = "1.10V";
+ break;
+ case EAR_CMV_1_27V:
+ cmv_str = "1.27V";
+ break;
+ case EAR_CMV_1_58V:
+ cmv_str = "1.58V";
+ break;
+ default:
+ dev_err(codec->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__,
+ cmv_str);
+ snd_soc_update_bits(codec, AB8500_ANACONF1, AB8500_ANACONF1_EARSELCM,
+ ear_cmv);
+
+ return 0;
+}
+
+static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai,
+ unsigned int delay)
+{
+ unsigned int mask, val;
+ struct snd_soc_codec *codec = dai->codec;
+
+ mask = BIT(AB8500_DIGIFCONF2_IF0DEL);
+ val = 0;
+
+ switch (delay) {
+ case 0:
+ break;
+ case 1:
+ val |= BIT(AB8500_DIGIFCONF2_IF0DEL);
+ break;
+ default:
+ dev_err(dai->codec->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",
+ __func__, delay);
+ snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+
+ return 0;
+}
+
+/* Gates clocking according format mask */
+static int ab8500_codec_set_dai_clock_gate(struct snd_soc_codec *codec,
+ unsigned int fmt)
+{
+ unsigned int mask;
+ unsigned int val;
+
+ mask = BIT(AB8500_DIGIFCONF1_ENMASTGEN) |
+ BIT(AB8500_DIGIFCONF1_ENFSBITCLK0);
+
+ val = BIT(AB8500_DIGIFCONF1_ENMASTGEN);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
+ case SND_SOC_DAIFMT_CONT: /* continuous clock */
+ dev_dbg(codec->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",
+ __func__);
+ break;
+ default:
+ dev_err(codec->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);
+
+ return 0;
+}
+
+static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ unsigned int mask;
+ unsigned int val;
+ struct snd_soc_codec *codec = dai->codec;
+ int status;
+
+ dev_dbg(codec->dev, "%s: Enter (fmt = 0x%x)\n", __func__, fmt);
+
+ mask = BIT(AB8500_DIGIFCONF3_IF1DATOIF0AD) |
+ BIT(AB8500_DIGIFCONF3_IF1CLKTOIF0CLK) |
+ BIT(AB8500_DIGIFCONF3_IF0BFIFOEN) |
+ BIT(AB8500_DIGIFCONF3_IF0MASTER);
+ val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_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__);
+ 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__);
+ 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",
+ __func__);
+ default:
+ dev_err(dai->codec->dev,
+ "%s: ERROR: Unsupporter master mask 0x%x\n",
+ __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ break;
+ }
+
+ snd_soc_update_bits(codec, AB8500_DIGIFCONF3, mask, val);
+
+ /* Set clock gating */
+ status = ab8500_codec_set_dai_clock_gate(codec, fmt);
+ if (status) {
+ dev_err(dai->codec->dev,
+ "%s: ERROR: Failed to set clock gate (%d).\n",
+ __func__, status);
+ return status;
+ }
+
+ /* Setting data transfer format */
+
+ mask = BIT(AB8500_DIGIFCONF2_IF0FORMAT0) |
+ BIT(AB8500_DIGIFCONF2_IF0FORMAT1) |
+ BIT(AB8500_DIGIFCONF2_FSYNC0P) |
+ BIT(AB8500_DIGIFCONF2_BITCLK0P);
+ val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S: /* I2S mode */
+ dev_dbg(dai->codec->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,
+ "%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,
+ "%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,
+ "%s: ERROR: Unsupported format (0x%x)!\n",
+ __func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
+ dev_dbg(dai->codec->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,
+ "%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,
+ "%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,
+ "%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,
+ "%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);
+
+ return 0;
+}
+
+static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int val, mask, slot, slots_active;
+
+ mask = BIT(AB8500_DIGIFCONF2_IF0WL0) |
+ BIT(AB8500_DIGIFCONF2_IF0WL1);
+ val = 0;
+
+ switch (slot_width) {
+ case 16:
+ break;
+ case 20:
+ val |= BIT(AB8500_DIGIFCONF2_IF0WL0);
+ break;
+ case 24:
+ val |= BIT(AB8500_DIGIFCONF2_IF0WL1);
+ break;
+ case 32:
+ val |= BIT(AB8500_DIGIFCONF2_IF0WL1) |
+ BIT(AB8500_DIGIFCONF2_IF0WL0);
+ break;
+ default:
+ dev_err(dai->codec->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",
+ __func__, slot_width);
+ snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+
+ /* Setup TDM clocking according to slot count */
+ dev_dbg(dai->codec->dev, "%s: Slots, total: %d\n", __func__, slots);
+ mask = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) |
+ BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
+ switch (slots) {
+ case 2:
+ val = AB8500_MASK_NONE;
+ break;
+ case 4:
+ val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0);
+ break;
+ case 8:
+ val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
+ break;
+ case 16:
+ val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) |
+ BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
+ break;
+ default:
+ dev_err(dai->codec->dev,
+ "%s: ERROR: Unsupported number of slots (%d)!\n",
+ __func__, slots);
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val);
+
+ /* Setup TDM DA according to active tx slots */
+
+ if (tx_mask & ~0xff)
+ return -EINVAL;
+
+ mask = AB8500_DASLOTCONFX_SLTODAX_MASK;
+ tx_mask = tx_mask << AB8500_DA_DATA0_OFFSET;
+ slots_active = hweight32(tx_mask);
+
+ dev_dbg(dai->codec->dev, "%s: Slots, active, TX: %d\n", __func__,
+ slots_active);
+
+ switch (slots_active) {
+ case 0:
+ 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);
+ 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);
+ slot = fls(tx_mask);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot);
+ break;
+ case 8:
+ dev_dbg(dai->codec->dev,
+ "%s: In 8-channel mode DA-from-slot mapping is set manually.",
+ __func__);
+ break;
+ default:
+ dev_err(dai->codec->dev,
+ "%s: Unsupported number of active TX-slots (%d)!\n",
+ __func__, slots_active);
+ return -EINVAL;
+ }
+
+ /* Setup TDM AD according to active RX-slots */
+
+ if (rx_mask & ~0xff)
+ return -EINVAL;
+
+ rx_mask = rx_mask << AB8500_AD_DATA0_OFFSET;
+ slots_active = hweight32(rx_mask);
+
+ dev_dbg(dai->codec->dev, "%s: Slots, active, RX: %d\n", __func__,
+ slots_active);
+
+ switch (slots_active) {
+ case 0:
+ break;
+ case 1:
+ slot = ffs(rx_mask);
+ snd_soc_update_bits(codec, 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,
+ 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,
+ 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,
+ "%s: In 8-channel mode AD-to-slot mapping is set manually.",
+ __func__);
+ break;
+ default:
+ dev_err(dai->codec->dev,
+ "%s: Unsupported number of active RX-slots (%d)!\n",
+ __func__, slots_active);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ab8500_codec_ops = {
+ .set_fmt = ab8500_codec_set_dai_fmt,
+ .set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
+};
+
+static struct snd_soc_dai_driver ab8500_codec_dai[] = {
+ {
+ .name = "ab8500-codec-dai.0",
+ .id = 0,
+ .playback = {
+ .stream_name = "ab8500_0p",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = AB8500_SUPPORTED_RATE,
+ .formats = AB8500_SUPPORTED_FMT,
+ },
+ .ops = &ab8500_codec_ops,
+ .symmetric_rates = 1
+ },
+ {
+ .name = "ab8500-codec-dai.1",
+ .id = 1,
+ .capture = {
+ .stream_name = "ab8500_0c",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = AB8500_SUPPORTED_RATE,
+ .formats = AB8500_SUPPORTED_FMT,
+ },
+ .ops = &ab8500_codec_ops,
+ .symmetric_rates = 1
+ }
+};
+
+static void ab8500_codec_of_probe(struct device *dev, struct device_node *np,
+ struct ab8500_codec_platform_data *codec)
+{
+ u32 value;
+
+ if (of_get_property(np, "stericsson,amic1-type-single-ended", NULL))
+ 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))
+ 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))
+ 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))
+ 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))
+ codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC1;
+ else
+ codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC2;
+
+ if (!of_property_read_u32(np, "stericsson,earpeice-cmv", &value)) {
+ switch (value) {
+ case 950 :
+ codec->ear_cmv = EAR_CMV_0_95V;
+ break;
+ case 1100 :
+ codec->ear_cmv = EAR_CMV_1_10V;
+ break;
+ case 1270 :
+ codec->ear_cmv = EAR_CMV_1_27V;
+ break;
+ case 1580 :
+ codec->ear_cmv = EAR_CMV_1_58V;
+ break;
+ default :
+ codec->ear_cmv = EAR_CMV_UNKNOWN;
+ dev_err(dev, "Unsuitable earpiece voltage found in DT\n");
+ }
+ } else {
+ dev_warn(dev, "No earpiece voltage found in DT - using default\n");
+ codec->ear_cmv = EAR_CMV_0_95V;
+ }
+}
+
+static int ab8500_codec_probe(struct snd_soc_codec *codec)
+{
+ struct device *dev = codec->dev;
+ struct device_node *np = dev->of_node;
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev);
+ struct ab8500_platform_data *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);
+
+ } 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);
+ 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);
+ if (status < 0) {
+ pr_err("%s: Failed to set earpiece CM-voltage (%d)!\n",
+ __func__, status);
+ return status;
+ }
+
+ status = ab8500_audio_init_audioblock(codec);
+ if (status < 0) {
+ dev_err(dev, "%s: failed to init audio-block (%d)!\n",
+ __func__, status);
+ return status;
+ }
+
+ /* Override HW-defaults */
+ snd_soc_write(codec, AB8500_ANACONF5,
+ BIT(AB8500_ANACONF5_HSAUTOEN));
+ snd_soc_write(codec, AB8500_SHORTCIRCONF,
+ BIT(AB8500_SHORTCIRCONF_HSZCDDIS));
+
+ /* Add filter controls */
+ status = snd_soc_add_codec_controls(codec, ab8500_filter_controls,
+ ARRAY_SIZE(ab8500_filter_controls));
+ if (status < 0) {
+ dev_err(dev,
+ "%s: failed to add ab8500 filter controls (%d).\n",
+ __func__, status);
+ return status;
+ }
+ fc = (struct filter_control *)
+ &ab8500_filter_controls[AB8500_FILTER_ANC_FIR].private_value;
+ drvdata->anc_fir_values = (long *)fc->value;
+ fc = (struct filter_control *)
+ &ab8500_filter_controls[AB8500_FILTER_ANC_IIR].private_value;
+ drvdata->anc_iir_values = (long *)fc->value;
+ fc = (struct filter_control *)
+ &ab8500_filter_controls[AB8500_FILTER_SID_FIR].private_value;
+ drvdata->sid_fir_values = (long *)fc->value;
+
+ (void)snd_soc_dapm_disable_pin(&codec->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 int ab8500_codec_driver_probe(struct platform_device *pdev)
+{
+ int status;
+ struct ab8500_codec_drvdata *drvdata;
+
+ dev_dbg(&pdev->dev, "%s: Enter.\n", __func__);
+
+ /* Create driver private-data struct */
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(struct ab8500_codec_drvdata),
+ GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+ drvdata->sid_status = SID_UNCONFIGURED;
+ drvdata->anc_status = ANC_UNCONFIGURED;
+ dev_set_drvdata(&pdev->dev, drvdata);
+
+ drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev,
+ &ab8500_codec_regmap);
+ if (IS_ERR(drvdata->regmap)) {
+ status = PTR_ERR(drvdata->regmap);
+ dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+ __func__, status);
+ return status;
+ }
+
+ dev_dbg(&pdev->dev, "%s: Register codec.\n", __func__);
+ status = snd_soc_register_codec(&pdev->dev, &ab8500_codec_driver,
+ ab8500_codec_dai,
+ ARRAY_SIZE(ab8500_codec_dai));
+ if (status < 0)
+ dev_err(&pdev->dev,
+ "%s: Error: Failed to register codec (%d).\n",
+ __func__, status);
+
+ return status;
+}
+
+static 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_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/ab8500-codec.h b/kernel/sound/soc/codecs/ab8500-codec.h
new file mode 100644
index 000000000..e2e54425d
--- /dev/null
+++ b/kernel/sound/soc/codecs/ab8500-codec.h
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ * Kristoffer Karlsson <kristoffer.karlsson@stericsson.com>,
+ * Roger Nilsson <roger.xr.nilsson@stericsson.com>,
+ * for ST-Ericsson.
+ *
+ * Based on the early work done by:
+ * 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
+#define AB8500_CODEC_REGISTERS_H
+
+#define AB8500_SUPPORTED_RATE (SNDRV_PCM_RATE_48000)
+#define AB8500_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE)
+
+/* AB8500 interface slot offset definitions */
+
+#define AB8500_AD_DATA0_OFFSET 0
+#define AB8500_DA_DATA0_OFFSET 8
+#define AB8500_AD_DATA1_OFFSET 16
+#define AB8500_DA_DATA1_OFFSET 24
+
+/* AB8500 audio bank (0x0d) register definitions */
+
+#define AB8500_POWERUP 0x00
+#define AB8500_AUDSWRESET 0x01
+#define AB8500_ADPATHENA 0x02
+#define AB8500_DAPATHENA 0x03
+#define AB8500_ANACONF1 0x04
+#define AB8500_ANACONF2 0x05
+#define AB8500_DIGMICCONF 0x06
+#define AB8500_ANACONF3 0x07
+#define AB8500_ANACONF4 0x08
+#define AB8500_DAPATHCONF 0x09
+#define AB8500_MUTECONF 0x0A
+#define AB8500_SHORTCIRCONF 0x0B
+#define AB8500_ANACONF5 0x0C
+#define AB8500_ENVCPCONF 0x0D
+#define AB8500_SIGENVCONF 0x0E
+#define AB8500_PWMGENCONF1 0x0F
+#define AB8500_PWMGENCONF2 0x10
+#define AB8500_PWMGENCONF3 0x11
+#define AB8500_PWMGENCONF4 0x12
+#define AB8500_PWMGENCONF5 0x13
+#define AB8500_ANAGAIN1 0x14
+#define AB8500_ANAGAIN2 0x15
+#define AB8500_ANAGAIN3 0x16
+#define AB8500_ANAGAIN4 0x17
+#define AB8500_DIGLINHSLGAIN 0x18
+#define AB8500_DIGLINHSRGAIN 0x19
+#define AB8500_ADFILTCONF 0x1A
+#define AB8500_DIGIFCONF1 0x1B
+#define AB8500_DIGIFCONF2 0x1C
+#define AB8500_DIGIFCONF3 0x1D
+#define AB8500_DIGIFCONF4 0x1E
+#define AB8500_ADSLOTSEL1 0x1F
+#define AB8500_ADSLOTSEL2 0x20
+#define AB8500_ADSLOTSEL3 0x21
+#define AB8500_ADSLOTSEL4 0x22
+#define AB8500_ADSLOTSEL5 0x23
+#define AB8500_ADSLOTSEL6 0x24
+#define AB8500_ADSLOTSEL7 0x25
+#define AB8500_ADSLOTSEL8 0x26
+#define AB8500_ADSLOTSEL9 0x27
+#define AB8500_ADSLOTSEL10 0x28
+#define AB8500_ADSLOTSEL11 0x29
+#define AB8500_ADSLOTSEL12 0x2A
+#define AB8500_ADSLOTSEL13 0x2B
+#define AB8500_ADSLOTSEL14 0x2C
+#define AB8500_ADSLOTSEL15 0x2D
+#define AB8500_ADSLOTSEL16 0x2E
+#define AB8500_ADSLOTSEL(slot) (AB8500_ADSLOTSEL1 + (slot >> 1))
+#define AB8500_ADSLOTHIZCTRL1 0x2F
+#define AB8500_ADSLOTHIZCTRL2 0x30
+#define AB8500_ADSLOTHIZCTRL3 0x31
+#define AB8500_ADSLOTHIZCTRL4 0x32
+#define AB8500_DASLOTCONF1 0x33
+#define AB8500_DASLOTCONF2 0x34
+#define AB8500_DASLOTCONF3 0x35
+#define AB8500_DASLOTCONF4 0x36
+#define AB8500_DASLOTCONF5 0x37
+#define AB8500_DASLOTCONF6 0x38
+#define AB8500_DASLOTCONF7 0x39
+#define AB8500_DASLOTCONF8 0x3A
+#define AB8500_CLASSDCONF1 0x3B
+#define AB8500_CLASSDCONF2 0x3C
+#define AB8500_CLASSDCONF3 0x3D
+#define AB8500_DMICFILTCONF 0x3E
+#define AB8500_DIGMULTCONF1 0x3F
+#define AB8500_DIGMULTCONF2 0x40
+#define AB8500_ADDIGGAIN1 0x41
+#define AB8500_ADDIGGAIN2 0x42
+#define AB8500_ADDIGGAIN3 0x43
+#define AB8500_ADDIGGAIN4 0x44
+#define AB8500_ADDIGGAIN5 0x45
+#define AB8500_ADDIGGAIN6 0x46
+#define AB8500_DADIGGAIN1 0x47
+#define AB8500_DADIGGAIN2 0x48
+#define AB8500_DADIGGAIN3 0x49
+#define AB8500_DADIGGAIN4 0x4A
+#define AB8500_DADIGGAIN5 0x4B
+#define AB8500_DADIGGAIN6 0x4C
+#define AB8500_ADDIGLOOPGAIN1 0x4D
+#define AB8500_ADDIGLOOPGAIN2 0x4E
+#define AB8500_HSLEARDIGGAIN 0x4F
+#define AB8500_HSRDIGGAIN 0x50
+#define AB8500_SIDFIRGAIN1 0x51
+#define AB8500_SIDFIRGAIN2 0x52
+#define AB8500_ANCCONF1 0x53
+#define AB8500_ANCCONF2 0x54
+#define AB8500_ANCCONF3 0x55
+#define AB8500_ANCCONF4 0x56
+#define AB8500_ANCCONF5 0x57
+#define AB8500_ANCCONF6 0x58
+#define AB8500_ANCCONF7 0x59
+#define AB8500_ANCCONF8 0x5A
+#define AB8500_ANCCONF9 0x5B
+#define AB8500_ANCCONF10 0x5C
+#define AB8500_ANCCONF11 0x5D
+#define AB8500_ANCCONF12 0x5E
+#define AB8500_ANCCONF13 0x5F
+#define AB8500_ANCCONF14 0x60
+#define AB8500_SIDFIRADR 0x61
+#define AB8500_SIDFIRCOEF1 0x62
+#define AB8500_SIDFIRCOEF2 0x63
+#define AB8500_SIDFIRCONF 0x64
+#define AB8500_AUDINTMASK1 0x65
+#define AB8500_AUDINTSOURCE1 0x66
+#define AB8500_AUDINTMASK2 0x67
+#define AB8500_AUDINTSOURCE2 0x68
+#define AB8500_FIFOCONF1 0x69
+#define AB8500_FIFOCONF2 0x6A
+#define AB8500_FIFOCONF3 0x6B
+#define AB8500_FIFOCONF4 0x6C
+#define AB8500_FIFOCONF5 0x6D
+#define AB8500_FIFOCONF6 0x6E
+#define AB8500_AUDREV 0x6F
+
+#define AB8500_FIRST_REG AB8500_POWERUP
+#define AB8500_LAST_REG AB8500_AUDREV
+#define AB8500_CACHEREGNUM (AB8500_LAST_REG + 1)
+
+#define AB8500_MASK_ALL 0xFF
+#define AB8500_MASK_SLOT(slot) ((slot & 1) ? 0xF0 : 0x0F)
+#define AB8500_MASK_NONE 0x00
+
+/* AB8500_POWERUP */
+#define AB8500_POWERUP_POWERUP 7
+#define AB8500_POWERUP_ENANA 3
+
+/* AB8500_AUDSWRESET */
+#define AB8500_AUDSWRESET_SWRESET 7
+
+/* AB8500_ADPATHENA */
+#define AB8500_ADPATHENA_ENAD12 7
+#define AB8500_ADPATHENA_ENAD34 5
+#define AB8500_ADPATHENA_ENAD5768 3
+
+/* AB8500_DAPATHENA */
+#define AB8500_DAPATHENA_ENDA1 7
+#define AB8500_DAPATHENA_ENDA2 6
+#define AB8500_DAPATHENA_ENDA3 5
+#define AB8500_DAPATHENA_ENDA4 4
+#define AB8500_DAPATHENA_ENDA5 3
+#define AB8500_DAPATHENA_ENDA6 2
+
+/* AB8500_ANACONF1 */
+#define AB8500_ANACONF1_HSLOWPOW 7
+#define AB8500_ANACONF1_DACLOWPOW1 6
+#define AB8500_ANACONF1_DACLOWPOW0 5
+#define AB8500_ANACONF1_EARDACLOWPOW 4
+#define AB8500_ANACONF1_EARSELCM 2
+#define AB8500_ANACONF1_HSHPEN 1
+#define AB8500_ANACONF1_EARDRVLOWPOW 0
+
+/* AB8500_ANACONF2 */
+#define AB8500_ANACONF2_ENMIC1 7
+#define AB8500_ANACONF2_ENMIC2 6
+#define AB8500_ANACONF2_ENLINL 5
+#define AB8500_ANACONF2_ENLINR 4
+#define AB8500_ANACONF2_MUTMIC1 3
+#define AB8500_ANACONF2_MUTMIC2 2
+#define AB8500_ANACONF2_MUTLINL 1
+#define AB8500_ANACONF2_MUTLINR 0
+
+/* AB8500_DIGMICCONF */
+#define AB8500_DIGMICCONF_ENDMIC1 7
+#define AB8500_DIGMICCONF_ENDMIC2 6
+#define AB8500_DIGMICCONF_ENDMIC3 5
+#define AB8500_DIGMICCONF_ENDMIC4 4
+#define AB8500_DIGMICCONF_ENDMIC5 3
+#define AB8500_DIGMICCONF_ENDMIC6 2
+#define AB8500_DIGMICCONF_HSFADSPEED 0
+
+/* AB8500_ANACONF3 */
+#define AB8500_ANACONF3_MIC1SEL 7
+#define AB8500_ANACONF3_LINRSEL 6
+#define AB8500_ANACONF3_ENDRVHSL 5
+#define AB8500_ANACONF3_ENDRVHSR 4
+#define AB8500_ANACONF3_ENADCMIC 2
+#define AB8500_ANACONF3_ENADCLINL 1
+#define AB8500_ANACONF3_ENADCLINR 0
+
+/* AB8500_ANACONF4 */
+#define AB8500_ANACONF4_DISPDVSS 7
+#define AB8500_ANACONF4_ENEAR 6
+#define AB8500_ANACONF4_ENHSL 5
+#define AB8500_ANACONF4_ENHSR 4
+#define AB8500_ANACONF4_ENHFL 3
+#define AB8500_ANACONF4_ENHFR 2
+#define AB8500_ANACONF4_ENVIB1 1
+#define AB8500_ANACONF4_ENVIB2 0
+
+/* AB8500_DAPATHCONF */
+#define AB8500_DAPATHCONF_ENDACEAR 6
+#define AB8500_DAPATHCONF_ENDACHSL 5
+#define AB8500_DAPATHCONF_ENDACHSR 4
+#define AB8500_DAPATHCONF_ENDACHFL 3
+#define AB8500_DAPATHCONF_ENDACHFR 2
+#define AB8500_DAPATHCONF_ENDACVIB1 1
+#define AB8500_DAPATHCONF_ENDACVIB2 0
+
+/* AB8500_MUTECONF */
+#define AB8500_MUTECONF_MUTEAR 6
+#define AB8500_MUTECONF_MUTHSL 5
+#define AB8500_MUTECONF_MUTHSR 4
+#define AB8500_MUTECONF_MUTDACEAR 2
+#define AB8500_MUTECONF_MUTDACHSL 1
+#define AB8500_MUTECONF_MUTDACHSR 0
+
+/* AB8500_SHORTCIRCONF */
+#define AB8500_SHORTCIRCONF_ENSHORTPWD 7
+#define AB8500_SHORTCIRCONF_EARSHORTDIS 6
+#define AB8500_SHORTCIRCONF_HSSHORTDIS 5
+#define AB8500_SHORTCIRCONF_HSPULLDEN 4
+#define AB8500_SHORTCIRCONF_HSOSCEN 2
+#define AB8500_SHORTCIRCONF_HSFADDIS 1
+#define AB8500_SHORTCIRCONF_HSZCDDIS 0
+/* Zero cross should be disabled */
+
+/* AB8500_ANACONF5 */
+#define AB8500_ANACONF5_ENCPHS 7
+#define AB8500_ANACONF5_HSLDACTOLOL 5
+#define AB8500_ANACONF5_HSRDACTOLOR 4
+#define AB8500_ANACONF5_ENLOL 3
+#define AB8500_ANACONF5_ENLOR 2
+#define AB8500_ANACONF5_HSAUTOEN 0
+
+/* AB8500_ENVCPCONF */
+#define AB8500_ENVCPCONF_ENVDETHTHRE 4
+#define AB8500_ENVCPCONF_ENVDETLTHRE 0
+#define AB8500_ENVCPCONF_ENVDETHTHRE_MAX 0x0F
+#define AB8500_ENVCPCONF_ENVDETLTHRE_MAX 0x0F
+
+/* AB8500_SIGENVCONF */
+#define AB8500_SIGENVCONF_CPLVEN 5
+#define AB8500_SIGENVCONF_ENVDETCPEN 4
+#define AB8500_SIGENVCONF_ENVDETTIME 0
+#define AB8500_SIGENVCONF_ENVDETTIME_MAX 0x0F
+
+/* AB8500_PWMGENCONF1 */
+#define AB8500_PWMGENCONF1_PWMTOVIB1 7
+#define AB8500_PWMGENCONF1_PWMTOVIB2 6
+#define AB8500_PWMGENCONF1_PWM1CTRL 5
+#define AB8500_PWMGENCONF1_PWM2CTRL 4
+#define AB8500_PWMGENCONF1_PWM1NCTRL 3
+#define AB8500_PWMGENCONF1_PWM1PCTRL 2
+#define AB8500_PWMGENCONF1_PWM2NCTRL 1
+#define AB8500_PWMGENCONF1_PWM2PCTRL 0
+
+/* AB8500_PWMGENCONF2 */
+/* AB8500_PWMGENCONF3 */
+/* AB8500_PWMGENCONF4 */
+/* AB8500_PWMGENCONF5 */
+#define AB8500_PWMGENCONFX_PWMVIBXPOL 7
+#define AB8500_PWMGENCONFX_PWMVIBXDUTCYC 0
+#define AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX 0x64
+
+/* AB8500_ANAGAIN1 */
+/* AB8500_ANAGAIN2 */
+#define AB8500_ANAGAINX_ENSEMICX 7
+#define AB8500_ANAGAINX_LOWPOWMICX 6
+#define AB8500_ANAGAINX_MICXGAIN 0
+#define AB8500_ANAGAINX_MICXGAIN_MAX 0x1F
+
+/* AB8500_ANAGAIN3 */
+#define AB8500_ANAGAIN3_HSLGAIN 4
+#define AB8500_ANAGAIN3_HSRGAIN 0
+#define AB8500_ANAGAIN3_HSXGAIN_MAX 0x0F
+
+/* AB8500_ANAGAIN4 */
+#define AB8500_ANAGAIN4_LINLGAIN 4
+#define AB8500_ANAGAIN4_LINRGAIN 0
+#define AB8500_ANAGAIN4_LINXGAIN_MAX 0x0F
+
+/* AB8500_DIGLINHSLGAIN */
+/* AB8500_DIGLINHSRGAIN */
+#define AB8500_DIGLINHSXGAIN_LINTOHSXGAIN 0
+#define AB8500_DIGLINHSXGAIN_LINTOHSXGAIN_MAX 0x13
+
+/* AB8500_ADFILTCONF */
+#define AB8500_ADFILTCONF_AD1NH 7
+#define AB8500_ADFILTCONF_AD2NH 6
+#define AB8500_ADFILTCONF_AD3NH 5
+#define AB8500_ADFILTCONF_AD4NH 4
+#define AB8500_ADFILTCONF_AD1VOICE 3
+#define AB8500_ADFILTCONF_AD2VOICE 2
+#define AB8500_ADFILTCONF_AD3VOICE 1
+#define AB8500_ADFILTCONF_AD4VOICE 0
+
+/* AB8500_DIGIFCONF1 */
+#define AB8500_DIGIFCONF1_ENMASTGEN 7
+#define AB8500_DIGIFCONF1_IF1BITCLKOS1 6
+#define AB8500_DIGIFCONF1_IF1BITCLKOS0 5
+#define AB8500_DIGIFCONF1_ENFSBITCLK1 4
+#define AB8500_DIGIFCONF1_IF0BITCLKOS1 2
+#define AB8500_DIGIFCONF1_IF0BITCLKOS0 1
+#define AB8500_DIGIFCONF1_ENFSBITCLK0 0
+
+/* AB8500_DIGIFCONF2 */
+#define AB8500_DIGIFCONF2_FSYNC0P 6
+#define AB8500_DIGIFCONF2_BITCLK0P 5
+#define AB8500_DIGIFCONF2_IF0DEL 4
+#define AB8500_DIGIFCONF2_IF0FORMAT1 3
+#define AB8500_DIGIFCONF2_IF0FORMAT0 2
+#define AB8500_DIGIFCONF2_IF0WL1 1
+#define AB8500_DIGIFCONF2_IF0WL0 0
+
+/* AB8500_DIGIFCONF3 */
+#define AB8500_DIGIFCONF3_IF0DATOIF1AD 7
+#define AB8500_DIGIFCONF3_IF0CLKTOIF1CLK 6
+#define AB8500_DIGIFCONF3_IF1MASTER 5
+#define AB8500_DIGIFCONF3_IF1DATOIF0AD 3
+#define AB8500_DIGIFCONF3_IF1CLKTOIF0CLK 2
+#define AB8500_DIGIFCONF3_IF0MASTER 1
+#define AB8500_DIGIFCONF3_IF0BFIFOEN 0
+
+/* AB8500_DIGIFCONF4 */
+#define AB8500_DIGIFCONF4_FSYNC1P 6
+#define AB8500_DIGIFCONF4_BITCLK1P 5
+#define AB8500_DIGIFCONF4_IF1DEL 4
+#define AB8500_DIGIFCONF4_IF1FORMAT1 3
+#define AB8500_DIGIFCONF4_IF1FORMAT0 2
+#define AB8500_DIGIFCONF4_IF1WL1 1
+#define AB8500_DIGIFCONF4_IF1WL0 0
+
+/* AB8500_ADSLOTSELX */
+#define AB8500_AD_OUT1 0x0
+#define AB8500_AD_OUT2 0x1
+#define AB8500_AD_OUT3 0x2
+#define AB8500_AD_OUT4 0x3
+#define AB8500_AD_OUT5 0x4
+#define AB8500_AD_OUT6 0x5
+#define AB8500_AD_OUT7 0x6
+#define AB8500_AD_OUT8 0x7
+#define AB8500_ZEROES 0x8
+#define AB8500_TRISTATE 0xF
+#define AB8500_ADSLOTSELX_EVEN_SHIFT 0
+#define AB8500_ADSLOTSELX_ODD_SHIFT 4
+#define AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(out, slot) \
+ ((out) << (((slot) & 1) ? \
+ AB8500_ADSLOTSELX_ODD_SHIFT : AB8500_ADSLOTSELX_EVEN_SHIFT))
+
+/* AB8500_ADSLOTHIZCTRL1 */
+/* AB8500_ADSLOTHIZCTRL2 */
+/* AB8500_ADSLOTHIZCTRL3 */
+/* AB8500_ADSLOTHIZCTRL4 */
+/* AB8500_DASLOTCONF1 */
+#define AB8500_DASLOTCONF1_DA12VOICE 7
+#define AB8500_DASLOTCONF1_SWAPDA12_34 6
+#define AB8500_DASLOTCONF1_DAI7TOADO1 5
+
+/* AB8500_DASLOTCONF2 */
+#define AB8500_DASLOTCONF2_DAI8TOADO2 5
+
+/* AB8500_DASLOTCONF3 */
+#define AB8500_DASLOTCONF3_DA34VOICE 7
+#define AB8500_DASLOTCONF3_DAI7TOADO3 5
+
+/* AB8500_DASLOTCONF4 */
+#define AB8500_DASLOTCONF4_DAI8TOADO4 5
+
+/* AB8500_DASLOTCONF5 */
+#define AB8500_DASLOTCONF5_DA56VOICE 7
+#define AB8500_DASLOTCONF5_DAI7TOADO5 5
+
+/* AB8500_DASLOTCONF6 */
+#define AB8500_DASLOTCONF6_DAI8TOADO6 5
+
+/* AB8500_DASLOTCONF7 */
+#define AB8500_DASLOTCONF7_DAI8TOADO7 5
+
+/* AB8500_DASLOTCONF8 */
+#define AB8500_DASLOTCONF8_DAI7TOADO8 5
+
+#define AB8500_DASLOTCONFX_SLTODAX_SHIFT 0
+#define AB8500_DASLOTCONFX_SLTODAX_MASK 0x1F
+
+/* AB8500_CLASSDCONF1 */
+#define AB8500_CLASSDCONF1_PARLHF 7
+#define AB8500_CLASSDCONF1_PARLVIB 6
+#define AB8500_CLASSDCONF1_VIB1SWAPEN 3
+#define AB8500_CLASSDCONF1_VIB2SWAPEN 2
+#define AB8500_CLASSDCONF1_HFLSWAPEN 1
+#define AB8500_CLASSDCONF1_HFRSWAPEN 0
+
+/* AB8500_CLASSDCONF2 */
+#define AB8500_CLASSDCONF2_FIRBYP3 7
+#define AB8500_CLASSDCONF2_FIRBYP2 6
+#define AB8500_CLASSDCONF2_FIRBYP1 5
+#define AB8500_CLASSDCONF2_FIRBYP0 4
+#define AB8500_CLASSDCONF2_HIGHVOLEN3 3
+#define AB8500_CLASSDCONF2_HIGHVOLEN2 2
+#define AB8500_CLASSDCONF2_HIGHVOLEN1 1
+#define AB8500_CLASSDCONF2_HIGHVOLEN0 0
+
+/* AB8500_CLASSDCONF3 */
+#define AB8500_CLASSDCONF3_DITHHPGAIN 4
+#define AB8500_CLASSDCONF3_DITHHPGAIN_MAX 0x0A
+#define AB8500_CLASSDCONF3_DITHWGAIN 0
+#define AB8500_CLASSDCONF3_DITHWGAIN_MAX 0x0A
+
+/* AB8500_DMICFILTCONF */
+#define AB8500_DMICFILTCONF_ANCINSEL 7
+#define AB8500_DMICFILTCONF_DA3TOEAR 6
+#define AB8500_DMICFILTCONF_DMIC1SINC3 5
+#define AB8500_DMICFILTCONF_DMIC2SINC3 4
+#define AB8500_DMICFILTCONF_DMIC3SINC3 3
+#define AB8500_DMICFILTCONF_DMIC4SINC3 2
+#define AB8500_DMICFILTCONF_DMIC5SINC3 1
+#define AB8500_DMICFILTCONF_DMIC6SINC3 0
+
+/* AB8500_DIGMULTCONF1 */
+#define AB8500_DIGMULTCONF1_DATOHSLEN 7
+#define AB8500_DIGMULTCONF1_DATOHSREN 6
+#define AB8500_DIGMULTCONF1_AD1SEL 5
+#define AB8500_DIGMULTCONF1_AD2SEL 4
+#define AB8500_DIGMULTCONF1_AD3SEL 3
+#define AB8500_DIGMULTCONF1_AD5SEL 2
+#define AB8500_DIGMULTCONF1_AD6SEL 1
+#define AB8500_DIGMULTCONF1_ANCSEL 0
+
+/* AB8500_DIGMULTCONF2 */
+#define AB8500_DIGMULTCONF2_DATOHFREN 7
+#define AB8500_DIGMULTCONF2_DATOHFLEN 6
+#define AB8500_DIGMULTCONF2_HFRSEL 5
+#define AB8500_DIGMULTCONF2_HFLSEL 4
+#define AB8500_DIGMULTCONF2_FIRSID1SEL 2
+#define AB8500_DIGMULTCONF2_FIRSID2SEL 0
+
+/* AB8500_ADDIGGAIN1 */
+/* AB8500_ADDIGGAIN2 */
+/* AB8500_ADDIGGAIN3 */
+/* AB8500_ADDIGGAIN4 */
+/* AB8500_ADDIGGAIN5 */
+/* AB8500_ADDIGGAIN6 */
+#define AB8500_ADDIGGAINX_FADEDISADX 6
+#define AB8500_ADDIGGAINX_ADXGAIN_MAX 0x3F
+
+/* AB8500_DADIGGAIN1 */
+/* AB8500_DADIGGAIN2 */
+/* AB8500_DADIGGAIN3 */
+/* AB8500_DADIGGAIN4 */
+/* AB8500_DADIGGAIN5 */
+/* AB8500_DADIGGAIN6 */
+#define AB8500_DADIGGAINX_FADEDISDAX 6
+#define AB8500_DADIGGAINX_DAXGAIN_MAX 0x3F
+
+/* AB8500_ADDIGLOOPGAIN1 */
+/* AB8500_ADDIGLOOPGAIN2 */
+#define AB8500_ADDIGLOOPGAINX_FADEDISADXL 6
+#define AB8500_ADDIGLOOPGAINX_ADXLBGAIN_MAX 0x3F
+
+/* AB8500_HSLEARDIGGAIN */
+#define AB8500_HSLEARDIGGAIN_HSSINC1 7
+#define AB8500_HSLEARDIGGAIN_FADEDISHSL 4
+#define AB8500_HSLEARDIGGAIN_HSLDGAIN_MAX 0x09
+
+/* AB8500_HSRDIGGAIN */
+#define AB8500_HSRDIGGAIN_FADESPEED 6
+#define AB8500_HSRDIGGAIN_FADEDISHSR 4
+#define AB8500_HSRDIGGAIN_HSRDGAIN_MAX 0x09
+
+/* AB8500_SIDFIRGAIN1 */
+/* AB8500_SIDFIRGAIN2 */
+#define AB8500_SIDFIRGAINX_FIRSIDXGAIN_MAX 0x1F
+
+/* AB8500_ANCCONF1 */
+#define AB8500_ANCCONF1_ANCIIRUPDATE 3
+#define AB8500_ANCCONF1_ENANC 2
+#define AB8500_ANCCONF1_ANCIIRINIT 1
+#define AB8500_ANCCONF1_ANCFIRUPDATE 0
+
+/* AB8500_ANCCONF2 */
+#define AB8500_ANCCONF2_SHIFT 5
+#define AB8500_ANCCONF2_MIN -0x10
+#define AB8500_ANCCONF2_MAX 0xF
+
+/* AB8500_ANCCONF3 */
+#define AB8500_ANCCONF3_SHIFT 5
+#define AB8500_ANCCONF3_MIN -0x10
+#define AB8500_ANCCONF3_MAX 0xF
+
+/* AB8500_ANCCONF4 */
+#define AB8500_ANCCONF4_SHIFT 5
+#define AB8500_ANCCONF4_MIN -0x10
+#define AB8500_ANCCONF4_MAX 0xF
+
+/* AB8500_ANC_FIR_COEFFS */
+#define AB8500_ANC_FIR_COEFF_MIN -0x8000
+#define AB8500_ANC_FIR_COEFF_MAX 0x7FFF
+#define AB8500_ANC_FIR_COEFFS 15
+
+/* AB8500_ANC_IIR_COEFFS */
+#define AB8500_ANC_IIR_COEFF_MIN -0x800000
+#define AB8500_ANC_IIR_COEFF_MAX 0x7FFFFF
+#define AB8500_ANC_IIR_COEFFS 24
+/* AB8500_ANC_WARP_DELAY */
+#define AB8500_ANC_WARP_DELAY_SHIFT 16
+#define AB8500_ANC_WARP_DELAY_MIN 0x0000
+#define AB8500_ANC_WARP_DELAY_MAX 0xFFFF
+
+/* AB8500_ANCCONF11 */
+/* AB8500_ANCCONF12 */
+/* AB8500_ANCCONF13 */
+/* AB8500_ANCCONF14 */
+
+/* AB8500_SIDFIRADR */
+#define AB8500_SIDFIRADR_FIRSIDSET 7
+#define AB8500_SIDFIRADR_ADDRESS_SHIFT 0
+#define AB8500_SIDFIRADR_ADDRESS_MAX 0x7F
+
+/* AB8500_SIDFIRCOEF1 */
+/* AB8500_SIDFIRCOEF2 */
+#define AB8500_SID_FIR_COEFF_MIN 0
+#define AB8500_SID_FIR_COEFF_MAX 0xFFFF
+#define AB8500_SID_FIR_COEFFS 128
+
+/* AB8500_SIDFIRCONF */
+#define AB8500_SIDFIRCONF_ENFIRSIDS 2
+#define AB8500_SIDFIRCONF_FIRSIDSTOIF1 1
+#define AB8500_SIDFIRCONF_FIRSIDBUSY 0
+
+/* AB8500_AUDINTMASK1 */
+/* AB8500_AUDINTSOURCE1 */
+/* AB8500_AUDINTMASK2 */
+/* AB8500_AUDINTSOURCE2 */
+
+/* AB8500_FIFOCONF1 */
+#define AB8500_FIFOCONF1_BFIFOMASK 0x80
+#define AB8500_FIFOCONF1_BFIFO19M2 0x40
+#define AB8500_FIFOCONF1_BFIFOINT_SHIFT 0
+#define AB8500_FIFOCONF1_BFIFOINT_MAX 0x3F
+
+/* AB8500_FIFOCONF2 */
+#define AB8500_FIFOCONF2_BFIFOTX_SHIFT 0
+#define AB8500_FIFOCONF2_BFIFOTX_MAX 0xFF
+
+/* AB8500_FIFOCONF3 */
+#define AB8500_FIFOCONF3_BFIFOEXSL_SHIFT 5
+#define AB8500_FIFOCONF3_BFIFOEXSL_MAX 0x5
+#define AB8500_FIFOCONF3_PREBITCLK0_SHIFT 2
+#define AB8500_FIFOCONF3_PREBITCLK0_MAX 0x7
+#define AB8500_FIFOCONF3_BFIFOMAST_SHIFT 1
+#define AB8500_FIFOCONF3_BFIFORUN_SHIFT 0
+
+/* AB8500_FIFOCONF4 */
+#define AB8500_FIFOCONF4_BFIFOFRAMSW_SHIFT 0
+#define AB8500_FIFOCONF4_BFIFOFRAMSW_MAX 0xFF
+
+/* AB8500_FIFOCONF5 */
+#define AB8500_FIFOCONF5_BFIFOWAKEUP_SHIFT 0
+#define AB8500_FIFOCONF5_BFIFOWAKEUP_MAX 0xFF
+
+/* AB8500_FIFOCONF6 */
+#define AB8500_FIFOCONF6_BFIFOSAMPLE_SHIFT 0
+#define AB8500_FIFOCONF6_BFIFOSAMPLE_MAX 0xFF
+
+/* AB8500_AUDREV */
+
+#endif
diff --git a/kernel/sound/soc/codecs/ac97.c b/kernel/sound/soc/codecs/ac97.c
new file mode 100644
index 000000000..d0ac723ee
--- /dev/null
+++ b/kernel/sound/soc/codecs/ac97.c
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget ac97_widgets[] = {
+ SND_SOC_DAPM_INPUT("RX"),
+ SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route ac97_routes[] = {
+ { "AC97 Capture", NULL, "RX" },
+ { "TX", NULL, "AC97 Playback" },
+};
+
+static int ac97_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ 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,
+};
+
+static struct snd_soc_dai_driver ac97_dai = {
+ .name = "ac97-hifi",
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = STD_AC97_RATES,
+ .formats = SND_SOC_STD_AC97_FMTS,},
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = STD_AC97_RATES,
+ .formats = SND_SOC_STD_AC97_FMTS,},
+ .ops = &ac97_dai_ops,
+};
+
+static int ac97_soc_probe(struct snd_soc_codec *codec)
+{
+ struct snd_ac97 *ac97;
+ struct snd_ac97_bus *ac97_bus;
+ struct snd_ac97_template ac97_template;
+ int ret;
+
+ /* add codec as bus device for standard ac97 */
+ ret = snd_ac97_bus(codec->component.card->snd_card, 0, soc_ac97_ops,
+ NULL, &ac97_bus);
+ if (ret < 0)
+ return ret;
+
+ memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
+ ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
+ if (ret < 0)
+ return ret;
+
+ snd_soc_codec_set_drvdata(codec, ac97);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ac97_soc_suspend(struct snd_soc_codec *codec)
+{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_ac97_suspend(ac97);
+
+ return 0;
+}
+
+static int ac97_soc_resume(struct snd_soc_codec *codec)
+{
+
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_ac97_resume(ac97);
+
+ return 0;
+}
+#else
+#define ac97_soc_suspend NULL
+#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 int ac97_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_ac97, &ac97_dai, 1);
+}
+
+static int ac97_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver ac97_codec_driver = {
+ .driver = {
+ .name = "ac97-codec",
+ },
+
+ .probe = ac97_probe,
+ .remove = ac97_remove,
+};
+
+module_platform_driver(ac97_codec_driver);
+
+MODULE_DESCRIPTION("Soc Generic AC97 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ac97-codec");
diff --git a/kernel/sound/soc/codecs/ad1836.c b/kernel/sound/soc/codecs/ad1836.c
new file mode 100644
index 000000000..685998dd0
--- /dev/null
+++ b/kernel/sound/soc/codecs/ad1836.c
@@ -0,0 +1,418 @@
+ /*
+ * 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>
+#include <linux/slab.h>
+#include <linux/module.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/spi/spi.h>
+#include <linux/regmap.h>
+
+#include "ad1836.h"
+
+enum ad1836_type {
+ AD1835,
+ AD1836,
+ AD1838,
+};
+
+/* codec private data */
+struct ad1836_priv {
+ enum ad1836_type type;
+ struct regmap *regmap;
+};
+
+/*
+ * AD1836 volume/mute/de-emphasis etc. controls
+ */
+static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"};
+
+static SOC_ENUM_SINGLE_DECL(ad1836_deemp_enum,
+ AD1836_DAC_CTRL1, 8, ad1836_deemp);
+
+#define AD1836_DAC_VOLUME(x) \
+ SOC_DOUBLE_R("DAC" #x " Playback Volume", AD1836_DAC_L_VOL(x), \
+ AD1836_DAC_R_VOL(x), 0, 0x3FF, 0)
+
+#define AD1836_DAC_SWITCH(x) \
+ SOC_DOUBLE("DAC" #x " Playback Switch", AD1836_DAC_CTRL2, \
+ AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1)
+
+#define AD1836_ADC_SWITCH(x) \
+ SOC_DOUBLE("ADC" #x " Capture Switch", AD1836_ADC_CTRL2, \
+ AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1)
+
+static const struct snd_kcontrol_new ad183x_dac_controls[] = {
+ AD1836_DAC_VOLUME(1),
+ AD1836_DAC_SWITCH(1),
+ AD1836_DAC_VOLUME(2),
+ AD1836_DAC_SWITCH(2),
+ AD1836_DAC_VOLUME(3),
+ AD1836_DAC_SWITCH(3),
+ AD1836_DAC_VOLUME(4),
+ AD1836_DAC_SWITCH(4),
+};
+
+static const struct snd_soc_dapm_widget ad183x_dac_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("DAC1OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC2OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC3OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC4OUT"),
+};
+
+static const struct snd_soc_dapm_route ad183x_dac_routes[] = {
+ { "DAC1OUT", NULL, "DAC" },
+ { "DAC2OUT", NULL, "DAC" },
+ { "DAC3OUT", NULL, "DAC" },
+ { "DAC4OUT", NULL, "DAC" },
+};
+
+static const struct snd_kcontrol_new ad183x_adc_controls[] = {
+ AD1836_ADC_SWITCH(1),
+ AD1836_ADC_SWITCH(2),
+ AD1836_ADC_SWITCH(3),
+};
+
+static const struct snd_soc_dapm_widget ad183x_adc_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("ADC1IN"),
+ SND_SOC_DAPM_INPUT("ADC2IN"),
+};
+
+static const struct snd_soc_dapm_route ad183x_adc_routes[] = {
+ { "ADC", NULL, "ADC1IN" },
+ { "ADC", NULL, "ADC2IN" },
+};
+
+static const struct snd_kcontrol_new ad183x_controls[] = {
+ /* ADC high-pass filter */
+ SOC_SINGLE("ADC High Pass Filter Switch", AD1836_ADC_CTRL1,
+ AD1836_ADC_HIGHPASS_FILTER, 1, 0),
+
+ /* DAC de-emphasis */
+ SOC_ENUM("Playback Deemphasis", ad1836_deemp_enum),
+};
+
+static const struct snd_soc_dapm_widget ad183x_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", AD1836_DAC_CTRL1,
+ AD1836_DAC_POWERDOWN, 1),
+ SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1836_ADC_CTRL1,
+ AD1836_ADC_POWERDOWN, 1, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route ad183x_dapm_routes[] = {
+ { "DAC", NULL, "ADC_PWR" },
+ { "ADC", NULL, "ADC_PWR" },
+};
+
+static const DECLARE_TLV_DB_SCALE(ad1836_in_tlv, 0, 300, 0);
+
+static const struct snd_kcontrol_new ad1836_controls[] = {
+ SOC_DOUBLE_TLV("ADC2 Capture Volume", AD1836_ADC_CTRL1, 3, 0, 4, 0,
+ ad1836_in_tlv),
+};
+
+/*
+ * DAI ops entries
+ */
+
+static int ad1836_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ /* at present, we support adc aux mode to interface with
+ * blackfin sport tdm mode
+ */
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ /* ALCLK,ABCLK are both output, AD1836 can only be master */
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ad1836_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(dai->codec);
+ int word_len = 0;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ word_len = AD1836_WORD_LEN_16;
+ break;
+ case 20:
+ word_len = AD1836_WORD_LEN_20;
+ break;
+ case 24:
+ case 32:
+ word_len = AD1836_WORD_LEN_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(ad1836->regmap, AD1836_DAC_CTRL1,
+ AD1836_DAC_WORD_LEN_MASK,
+ word_len << AD1836_DAC_WORD_LEN_OFFSET);
+
+ regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
+ AD1836_ADC_WORD_LEN_MASK,
+ word_len << AD1836_ADC_WORD_OFFSET);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ad1836_dai_ops = {
+ .hw_params = ad1836_hw_params,
+ .set_fmt = ad1836_set_dai_fmt,
+};
+
+#define AD183X_DAI(_name, num_dacs, num_adcs) \
+{ \
+ .name = _name "-hifi", \
+ .playback = { \
+ .stream_name = "Playback", \
+ .channels_min = 2, \
+ .channels_max = (num_dacs) * 2, \
+ .rates = SNDRV_PCM_RATE_48000, \
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "Capture", \
+ .channels_min = 2, \
+ .channels_max = (num_adcs) * 2, \
+ .rates = SNDRV_PCM_RATE_48000, \
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \
+ }, \
+ .ops = &ad1836_dai_ops, \
+}
+
+static struct snd_soc_dai_driver ad183x_dais[] = {
+ [AD1835] = AD183X_DAI("ad1835", 4, 1),
+ [AD1836] = AD183X_DAI("ad1836", 3, 2),
+ [AD1838] = AD183X_DAI("ad1838", 3, 1),
+};
+
+#ifdef CONFIG_PM
+static int ad1836_suspend(struct snd_soc_codec *codec)
+{
+ struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
+ /* 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)
+{
+ struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
+ /* restore clock control mode */
+ return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
+ AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX);
+}
+#else
+#define ad1836_suspend NULL
+#define ad1836_resume NULL
+#endif
+
+static int ad1836_probe(struct snd_soc_codec *codec)
+{
+ struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int num_dacs, num_adcs;
+ int ret = 0;
+ int i;
+
+ num_dacs = ad183x_dais[ad1836->type].playback.channels_max / 2;
+ num_adcs = ad183x_dais[ad1836->type].capture.channels_max / 2;
+
+ /* default setting for ad1836 */
+ /* de-emphasis: 48kHz, power-on dac */
+ regmap_write(ad1836->regmap, AD1836_DAC_CTRL1, 0x300);
+ /* unmute dac channels */
+ regmap_write(ad1836->regmap, AD1836_DAC_CTRL2, 0x0);
+ /* high-pass filter enable, power-on adc */
+ regmap_write(ad1836->regmap, AD1836_ADC_CTRL1, 0x100);
+ /* unmute adc channles, adc aux mode */
+ regmap_write(ad1836->regmap, AD1836_ADC_CTRL2, 0x180);
+ /* volume */
+ for (i = 1; i <= num_dacs; ++i) {
+ regmap_write(ad1836->regmap, AD1836_DAC_L_VOL(i), 0x3FF);
+ regmap_write(ad1836->regmap, AD1836_DAC_R_VOL(i), 0x3FF);
+ }
+
+ if (ad1836->type == AD1836) {
+ /* left/right diff:PGA/MUX */
+ regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x3A);
+ ret = snd_soc_add_codec_controls(codec, ad1836_controls,
+ ARRAY_SIZE(ad1836_controls));
+ if (ret)
+ return ret;
+ } else {
+ regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x00);
+ }
+
+ ret = snd_soc_add_codec_controls(codec, ad183x_dac_controls, num_dacs * 2);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_add_codec_controls(codec, ad183x_adc_controls, num_adcs);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ad183x_dac_dapm_widgets, num_dacs);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ad183x_adc_dapm_widgets, num_adcs);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, ad183x_dac_routes, num_dacs);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, ad183x_adc_routes, num_adcs);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/* power down chip */
+static int ad1836_remove(struct snd_soc_codec *codec)
+{
+ struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
+ /* reset clock control mode */
+ return 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 reg_default ad1836_reg_defaults[] = {
+ { AD1836_DAC_CTRL1, 0x0000 },
+ { AD1836_DAC_CTRL2, 0x0000 },
+ { AD1836_DAC_L_VOL(0), 0x0000 },
+ { AD1836_DAC_R_VOL(0), 0x0000 },
+ { AD1836_DAC_L_VOL(1), 0x0000 },
+ { AD1836_DAC_R_VOL(1), 0x0000 },
+ { AD1836_DAC_L_VOL(2), 0x0000 },
+ { AD1836_DAC_R_VOL(2), 0x0000 },
+ { AD1836_DAC_L_VOL(3), 0x0000 },
+ { AD1836_DAC_R_VOL(3), 0x0000 },
+ { AD1836_ADC_CTRL1, 0x0000 },
+ { AD1836_ADC_CTRL2, 0x0000 },
+ { AD1836_ADC_CTRL3, 0x0000 },
+};
+
+static const struct regmap_config ad1836_regmap_config = {
+ .val_bits = 12,
+ .reg_bits = 4,
+ .read_flag_mask = 0x08,
+
+ .max_register = AD1836_ADC_CTRL3,
+ .reg_defaults = ad1836_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ad1836_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int ad1836_spi_probe(struct spi_device *spi)
+{
+ struct ad1836_priv *ad1836;
+ int ret;
+
+ ad1836 = devm_kzalloc(&spi->dev, sizeof(struct ad1836_priv),
+ GFP_KERNEL);
+ if (ad1836 == NULL)
+ return -ENOMEM;
+
+ ad1836->regmap = devm_regmap_init_spi(spi, &ad1836_regmap_config);
+ if (IS_ERR(ad1836->regmap))
+ return PTR_ERR(ad1836->regmap);
+
+ ad1836->type = spi_get_device_id(spi)->driver_data;
+
+ spi_set_drvdata(spi, ad1836);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_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 },
+ { "ad1837", AD1835 },
+ { "ad1838", AD1838 },
+ { "ad1839", AD1838 },
+ { },
+};
+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,
+};
+
+module_spi_driver(ad1836_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ad1836 driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ad1836.h b/kernel/sound/soc/codecs/ad1836.h
new file mode 100644
index 000000000..dd7be0dbb
--- /dev/null
+++ b/kernel/sound/soc/codecs/ad1836.h
@@ -0,0 +1,51 @@
+/*
+ * 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__
+#define __AD1836_H__
+
+#define AD1836_DAC_CTRL1 0
+#define AD1836_DAC_POWERDOWN 2
+#define AD1836_DAC_SERFMT_MASK 0xE0
+#define AD1836_DAC_SERFMT_PCK256 (0x4 << 5)
+#define AD1836_DAC_SERFMT_PCK128 (0x5 << 5)
+#define AD1836_DAC_WORD_LEN_MASK 0x18
+#define AD1836_DAC_WORD_LEN_OFFSET 3
+
+#define AD1836_DAC_CTRL2 1
+
+/* These macros are one-based. So AD183X_MUTE_LEFT(1) will return the mute bit
+ * for the first ADC/DAC */
+#define AD1836_MUTE_LEFT(x) (((x) * 2) - 2)
+#define AD1836_MUTE_RIGHT(x) (((x) * 2) - 1)
+
+#define AD1836_DAC_L_VOL(x) ((x) * 2)
+#define AD1836_DAC_R_VOL(x) (1 + ((x) * 2))
+
+#define AD1836_ADC_CTRL1 12
+#define AD1836_ADC_POWERDOWN 7
+#define AD1836_ADC_HIGHPASS_FILTER 8
+
+#define AD1836_ADC_CTRL2 13
+#define AD1836_ADC_WORD_LEN_MASK 0x30
+#define AD1836_ADC_WORD_OFFSET 4
+#define AD1836_ADC_SERFMT_MASK (7 << 6)
+#define AD1836_ADC_SERFMT_PCK256 (0x4 << 6)
+#define AD1836_ADC_SERFMT_PCK128 (0x5 << 6)
+#define AD1836_ADC_AUX (0x6 << 6)
+
+#define AD1836_ADC_CTRL3 14
+
+#define AD1836_NUM_REGS 16
+
+#define AD1836_WORD_LEN_24 0x0
+#define AD1836_WORD_LEN_20 0x1
+#define AD1836_WORD_LEN_16 0x2
+
+#endif
diff --git a/kernel/sound/soc/codecs/ad193x-i2c.c b/kernel/sound/soc/codecs/ad193x-i2c.c
new file mode 100644
index 000000000..df3a1a415
--- /dev/null
+++ b/kernel/sound/soc/codecs/ad193x-i2c.c
@@ -0,0 +1,54 @@
+/*
+ * AD1936/AD1937 audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "ad193x.h"
+
+static const struct i2c_device_id ad193x_id[] = {
+ { "ad1936", 0 },
+ { "ad1937", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ad193x_id);
+
+static int ad193x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regmap_config config;
+
+ config = ad193x_regmap_config;
+ 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;
+}
+
+static struct i2c_driver ad193x_i2c_driver = {
+ .driver = {
+ .name = "ad193x",
+ },
+ .probe = ad193x_i2c_probe,
+ .remove = ad193x_i2c_remove,
+ .id_table = ad193x_id,
+};
+module_i2c_driver(ad193x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AD1936/AD1937 audio CODEC driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ad193x-spi.c b/kernel/sound/soc/codecs/ad193x-spi.c
new file mode 100644
index 000000000..390cef9b9
--- /dev/null
+++ b/kernel/sound/soc/codecs/ad193x-spi.c
@@ -0,0 +1,48 @@
+/*
+ * AD1938/AD1939 audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "ad193x.h"
+
+static int ad193x_spi_probe(struct spi_device *spi)
+{
+ struct regmap_config config;
+
+ config = ad193x_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 16;
+ config.read_flag_mask = 0x09;
+ config.write_flag_mask = 0x08;
+
+ return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+}
+
+static int ad193x_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver ad193x_spi_driver = {
+ .driver = {
+ .name = "ad193x",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad193x_spi_probe,
+ .remove = ad193x_spi_remove,
+};
+module_spi_driver(ad193x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC AD1938/AD1939 audio CODEC driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ad193x.c b/kernel/sound/soc/codecs/ad193x.c
new file mode 100644
index 000000000..17c953595
--- /dev/null
+++ b/kernel/sound/soc/codecs/ad193x.c
@@ -0,0 +1,392 @@
+/*
+ * 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>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/slab.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 "ad193x.h"
+
+/* codec private data */
+struct ad193x_priv {
+ struct regmap *regmap;
+ int sysclk;
+};
+
+/*
+ * AD193X volume/mute/de-emphasis etc. controls
+ */
+static const char * const ad193x_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"};
+
+static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1,
+ ad193x_deemp);
+
+static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0);
+
+static const struct snd_kcontrol_new ad193x_snd_controls[] = {
+ /* DAC volume control */
+ SOC_DOUBLE_R_TLV("DAC1 Volume", AD193X_DAC_L1_VOL,
+ AD193X_DAC_R1_VOL, 0, 0xFF, 1, adau193x_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Volume", AD193X_DAC_L2_VOL,
+ AD193X_DAC_R2_VOL, 0, 0xFF, 1, adau193x_tlv),
+ SOC_DOUBLE_R_TLV("DAC3 Volume", AD193X_DAC_L3_VOL,
+ AD193X_DAC_R3_VOL, 0, 0xFF, 1, adau193x_tlv),
+ SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL,
+ AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv),
+
+ /* 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),
+ SOC_DOUBLE("DAC2 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL2_MUTE,
+ AD193X_DACR2_MUTE, 1, 1),
+ SOC_DOUBLE("DAC3 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL3_MUTE,
+ AD193X_DACR3_MUTE, 1, 1),
+ SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE,
+ AD193X_DACR4_MUTE, 1, 1),
+
+ /* 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"),
+ SND_SOC_DAPM_INPUT("ADC1IN"),
+ SND_SOC_DAPM_INPUT("ADC2IN"),
+};
+
+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" },
+ { "ADC", NULL, "ADC1IN" },
+ { "ADC", NULL, "ADC2IN" },
+ { "SYSCLK", NULL, "PLL_PWR" },
+};
+
+/*
+ * DAI ops entries
+ */
+
+static int ad193x_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(dai->codec);
+
+ if (mute)
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
+ AD193X_DAC_MASTER_MUTE,
+ AD193X_DAC_MASTER_MUTE);
+ else
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
+ AD193X_DAC_MASTER_MUTE, 0);
+
+ return 0;
+}
+
+static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int channels;
+
+ switch (slots) {
+ case 2:
+ channels = AD193X_2_CHANNELS;
+ break;
+ case 4:
+ channels = AD193X_4_CHANNELS;
+ break;
+ case 8:
+ channels = AD193X_8_CHANNELS;
+ break;
+ case 16:
+ channels = AD193X_16_CHANNELS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
+ AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT);
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
+ AD193X_ADC_CHAN_MASK, channels << AD193X_ADC_CHAN_SHFT);
+
+ return 0;
+}
+
+static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec_dai->codec);
+ unsigned int adc_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)
+ */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ adc_serfmt |= AD193X_ADC_SERFMT_TDM;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ adc_serfmt |= AD193X_ADC_SERFMT_AUX;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
+ break;
+ case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */
+ adc_fmt |= AD193X_ADC_LEFT_HIGH;
+ dac_fmt |= AD193X_DAC_LEFT_HIGH;
+ break;
+ case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */
+ adc_fmt |= AD193X_ADC_BCLK_INV;
+ dac_fmt |= AD193X_DAC_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */
+ adc_fmt |= AD193X_ADC_LEFT_HIGH;
+ adc_fmt |= AD193X_ADC_BCLK_INV;
+ dac_fmt |= AD193X_DAC_LEFT_HIGH;
+ dac_fmt |= AD193X_DAC_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
+ 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 */
+ adc_fmt |= AD193X_ADC_LCR_MASTER;
+ dac_fmt |= AD193X_DAC_LCR_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
+ adc_fmt |= AD193X_ADC_BCLK_MASTER;
+ dac_fmt |= AD193X_DAC_BCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+ 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);
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
+ AD193X_DAC_FMT_MASK, dac_fmt);
+
+ return 0;
+}
+
+static int ad193x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+ switch (freq) {
+ case 12288000:
+ case 18432000:
+ case 24576000:
+ case 36864000:
+ ad193x->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int ad193x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int word_len = 0, master_rate = 0;
+ struct snd_soc_codec *codec = dai->codec;
+ struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ word_len = 3;
+ break;
+ case 20:
+ word_len = 1;
+ break;
+ case 24:
+ case 32:
+ word_len = 0;
+ break;
+ }
+
+ switch (ad193x->sysclk) {
+ case 12288000:
+ master_rate = AD193X_PLL_INPUT_256;
+ break;
+ case 18432000:
+ master_rate = AD193X_PLL_INPUT_384;
+ break;
+ case 24576000:
+ master_rate = AD193X_PLL_INPUT_512;
+ break;
+ case 36864000:
+ master_rate = AD193X_PLL_INPUT_768;
+ break;
+ }
+
+ regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL0,
+ AD193X_PLL_INPUT_MASK, master_rate);
+
+ regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
+ AD193X_DAC_WORD_LEN_MASK,
+ word_len << AD193X_DAC_WORD_LEN_SHFT);
+
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
+ AD193X_ADC_WORD_LEN_MASK, word_len);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ad193x_dai_ops = {
+ .hw_params = ad193x_hw_params,
+ .digital_mute = ad193x_mute,
+ .set_tdm_slot = ad193x_set_tdm_slot,
+ .set_sysclk = ad193x_set_dai_sysclk,
+ .set_fmt = ad193x_set_dai_fmt,
+};
+
+/* codec DAI instance */
+static struct snd_soc_dai_driver ad193x_dai = {
+ .name = "ad193x-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &ad193x_dai_ops,
+};
+
+static int ad193x_codec_probe(struct snd_soc_codec *codec)
+{
+ struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+
+ /* default setting for ad193x */
+
+ /* 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);
+
+ 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 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)
+{
+ struct ad193x_priv *ad193x;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ad193x = devm_kzalloc(dev, sizeof(*ad193x), GFP_KERNEL);
+ if (ad193x == NULL)
+ return -ENOMEM;
+
+ ad193x->regmap = regmap;
+
+ dev_set_drvdata(dev, ad193x);
+
+ return snd_soc_register_codec(dev, &soc_codec_dev_ad193x,
+ &ad193x_dai, 1);
+}
+EXPORT_SYMBOL_GPL(ad193x_probe);
+
+MODULE_DESCRIPTION("ASoC ad193x driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ad193x.h b/kernel/sound/soc/codecs/ad193x.h
new file mode 100644
index 000000000..ab9a998f1
--- /dev/null
+++ b/kernel/sound/soc/codecs/ad193x.h
@@ -0,0 +1,92 @@
+/*
+ * AD193X Audio Codec driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __AD193X_H__
+#define __AD193X_H__
+
+#include <linux/regmap.h>
+
+struct device;
+
+extern const struct regmap_config ad193x_regmap_config;
+int ad193x_probe(struct device *dev, struct regmap *regmap);
+
+#define AD193X_PLL_CLK_CTRL0 0x00
+#define AD193X_PLL_POWERDOWN 0x01
+#define AD193X_PLL_INPUT_MASK 0x6
+#define AD193X_PLL_INPUT_256 (0 << 1)
+#define AD193X_PLL_INPUT_384 (1 << 1)
+#define AD193X_PLL_INPUT_512 (2 << 1)
+#define AD193X_PLL_INPUT_768 (3 << 1)
+#define AD193X_PLL_CLK_CTRL1 0x01
+#define AD193X_DAC_CTRL0 0x02
+#define AD193X_DAC_POWERDOWN 0x01
+#define AD193X_DAC_SERFMT_MASK 0xC0
+#define AD193X_DAC_SERFMT_STEREO (0 << 6)
+#define AD193X_DAC_SERFMT_TDM (1 << 6)
+#define AD193X_DAC_CTRL1 0x03
+#define AD193X_DAC_CHAN_SHFT 1
+#define AD193X_DAC_CHAN_MASK (3 << AD193X_DAC_CHAN_SHFT)
+#define AD193X_DAC_LCR_MASTER (1 << 4)
+#define AD193X_DAC_BCLK_MASTER (1 << 5)
+#define AD193X_DAC_LEFT_HIGH (1 << 3)
+#define AD193X_DAC_BCLK_INV (1 << 7)
+#define AD193X_DAC_FMT_MASK (AD193X_DAC_LCR_MASTER | \
+ AD193X_DAC_BCLK_MASTER | AD193X_DAC_LEFT_HIGH | AD193X_DAC_BCLK_INV)
+#define AD193X_DAC_CTRL2 0x04
+#define AD193X_DAC_WORD_LEN_SHFT 3
+#define AD193X_DAC_WORD_LEN_MASK 0x18
+#define AD193X_DAC_MASTER_MUTE 1
+#define AD193X_DAC_CHNL_MUTE 0x05
+#define AD193X_DACL1_MUTE 0
+#define AD193X_DACR1_MUTE 1
+#define AD193X_DACL2_MUTE 2
+#define AD193X_DACR2_MUTE 3
+#define AD193X_DACL3_MUTE 4
+#define AD193X_DACR3_MUTE 5
+#define AD193X_DACL4_MUTE 6
+#define AD193X_DACR4_MUTE 7
+#define AD193X_DAC_L1_VOL 0x06
+#define AD193X_DAC_R1_VOL 0x07
+#define AD193X_DAC_L2_VOL 0x08
+#define AD193X_DAC_R2_VOL 0x09
+#define AD193X_DAC_L3_VOL 0x0a
+#define AD193X_DAC_R3_VOL 0x0b
+#define AD193X_DAC_L4_VOL 0x0c
+#define AD193X_DAC_R4_VOL 0x0d
+#define AD193X_ADC_CTRL0 0x0e
+#define AD193X_ADC_POWERDOWN 0x01
+#define AD193X_ADC_HIGHPASS_FILTER 1
+#define AD193X_ADCL1_MUTE 2
+#define AD193X_ADCR1_MUTE 3
+#define AD193X_ADCL2_MUTE 4
+#define AD193X_ADCR2_MUTE 5
+#define AD193X_ADC_CTRL1 0x0f
+#define AD193X_ADC_SERFMT_MASK 0x60
+#define AD193X_ADC_SERFMT_STEREO (0 << 5)
+#define AD193X_ADC_SERFMT_TDM (1 << 5)
+#define AD193X_ADC_SERFMT_AUX (2 << 5)
+#define AD193X_ADC_WORD_LEN_MASK 0x3
+#define AD193X_ADC_CTRL2 0x10
+#define AD193X_ADC_CHAN_SHFT 4
+#define AD193X_ADC_CHAN_MASK (3 << AD193X_ADC_CHAN_SHFT)
+#define AD193X_ADC_LCR_MASTER (1 << 3)
+#define AD193X_ADC_BCLK_MASTER (1 << 6)
+#define AD193X_ADC_LEFT_HIGH (1 << 2)
+#define AD193X_ADC_BCLK_INV (1 << 1)
+#define AD193X_ADC_FMT_MASK (AD193X_ADC_LCR_MASTER | \
+ AD193X_ADC_BCLK_MASTER | AD193X_ADC_LEFT_HIGH | AD193X_ADC_BCLK_INV)
+
+#define AD193X_2_CHANNELS 0
+#define AD193X_4_CHANNELS 1
+#define AD193X_8_CHANNELS 2
+#define AD193X_16_CHANNELS 3
+
+#define AD193X_NUM_REGS 17
+
+#endif
diff --git a/kernel/sound/soc/codecs/ad1980.c b/kernel/sound/soc/codecs/ad1980.c
new file mode 100644
index 000000000..3cc69a626
--- /dev/null
+++ b/kernel/sound/soc/codecs/ad1980.c
@@ -0,0 +1,347 @@
+/*
+ * ad1980.c -- ALSA Soc AD1980 codec support
+ *
+ * Copyright: Analog Device 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.
+ */
+
+/*
+ * WARNING:
+ *
+ * Because Analog Devices Inc. discontinued the ad1980 sound chip since
+ * Sep. 2009, this ad1980 driver is not maintained, tested and supported
+ * by ADI now.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+static const struct reg_default ad1980_reg_defaults[] = {
+ { 0x02, 0x8000 },
+ { 0x04, 0x8000 },
+ { 0x06, 0x8000 },
+ { 0x0c, 0x8008 },
+ { 0x0e, 0x8008 },
+ { 0x10, 0x8808 },
+ { 0x12, 0x8808 },
+ { 0x16, 0x8808 },
+ { 0x18, 0x8808 },
+ { 0x1a, 0x0000 },
+ { 0x1c, 0x8000 },
+ { 0x20, 0x0000 },
+ { 0x28, 0x03c7 },
+ { 0x2c, 0xbb80 },
+ { 0x2e, 0xbb80 },
+ { 0x30, 0xbb80 },
+ { 0x32, 0xbb80 },
+ { 0x36, 0x8080 },
+ { 0x38, 0x8080 },
+ { 0x3a, 0x2000 },
+ { 0x60, 0x0000 },
+ { 0x62, 0x0000 },
+ { 0x72, 0x0000 },
+ { 0x74, 0x1001 },
+ { 0x76, 0x0000 },
+};
+
+static bool ad1980_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AC97_RESET ... AC97_MASTER_MONO:
+ case AC97_PHONE ... AC97_CD:
+ case AC97_AUX ... AC97_GENERAL_PURPOSE:
+ case AC97_POWERDOWN ... AC97_PCM_LR_ADC_RATE:
+ case AC97_SPDIF:
+ case AC97_CODEC_CLASS_REV:
+ case AC97_PCI_SVID:
+ case AC97_AD_CODEC_CFG:
+ case AC97_AD_JACK_SPDIF:
+ case AC97_AD_SERIAL_CFG:
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool ad1980_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ return false;
+ default:
+ return ad1980_readable_reg(dev, reg);
+ }
+}
+
+static const struct regmap_config ad1980_regmap_config = {
+ .reg_bits = 16,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .max_register = 0x7e,
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = regmap_ac97_default_volatile,
+ .readable_reg = ad1980_readable_reg,
+ .writeable_reg = ad1980_writeable_reg,
+
+ .reg_defaults = ad1980_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ad1980_reg_defaults),
+};
+
+static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line",
+ "Stereo Mix", "Mono Mix", "Phone"};
+
+static SOC_ENUM_DOUBLE_DECL(ad1980_cap_src,
+ AC97_REC_SEL, 8, 0, ad1980_rec_sel);
+
+static const struct snd_kcontrol_new ad1980_snd_ac97_controls[] = {
+SOC_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1),
+SOC_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1),
+
+SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
+SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
+
+SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1),
+SOC_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1),
+
+SOC_DOUBLE("PCM Capture Volume", AC97_REC_GAIN, 8, 0, 31, 0),
+SOC_SINGLE("PCM Capture Switch", AC97_REC_GAIN, 15, 1, 1),
+
+SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1),
+SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
+
+SOC_SINGLE("Phone Capture Volume", AC97_PHONE, 0, 31, 1),
+SOC_SINGLE("Phone Capture Switch", AC97_PHONE, 15, 1, 1),
+
+SOC_SINGLE("Mic Volume", AC97_MIC, 0, 31, 1),
+SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1),
+
+SOC_SINGLE("Stereo Mic Switch", AC97_AD_MISC, 6, 1, 0),
+SOC_DOUBLE("Line HP Swap Switch", AC97_AD_MISC, 10, 5, 1, 0),
+
+SOC_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
+SOC_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),
+
+SOC_DOUBLE("Center/LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 0, 31, 1),
+SOC_DOUBLE("Center/LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 7, 1, 1),
+
+SOC_ENUM("Capture Source", ad1980_cap_src),
+
+SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget ad1980_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2"),
+SND_SOC_DAPM_INPUT("CD_L"),
+SND_SOC_DAPM_INPUT("CD_R"),
+SND_SOC_DAPM_INPUT("AUX_L"),
+SND_SOC_DAPM_INPUT("AUX_R"),
+SND_SOC_DAPM_INPUT("LINE_IN_L"),
+SND_SOC_DAPM_INPUT("LINE_IN_R"),
+
+SND_SOC_DAPM_OUTPUT("LFE_OUT"),
+SND_SOC_DAPM_OUTPUT("CENTER_OUT"),
+SND_SOC_DAPM_OUTPUT("LINE_OUT_L"),
+SND_SOC_DAPM_OUTPUT("LINE_OUT_R"),
+SND_SOC_DAPM_OUTPUT("MONO_OUT"),
+SND_SOC_DAPM_OUTPUT("HP_OUT_L"),
+SND_SOC_DAPM_OUTPUT("HP_OUT_R"),
+};
+
+static const struct snd_soc_dapm_route ad1980_dapm_routes[] = {
+ { "Capture", NULL, "MIC1" },
+ { "Capture", NULL, "MIC2" },
+ { "Capture", NULL, "CD_L" },
+ { "Capture", NULL, "CD_R" },
+ { "Capture", NULL, "AUX_L" },
+ { "Capture", NULL, "AUX_R" },
+ { "Capture", NULL, "LINE_IN_L" },
+ { "Capture", NULL, "LINE_IN_R" },
+
+ { "LFE_OUT", NULL, "Playback" },
+ { "CENTER_OUT", NULL, "Playback" },
+ { "LINE_OUT_L", NULL, "Playback" },
+ { "LINE_OUT_R", NULL, "Playback" },
+ { "MONO_OUT", NULL, "Playback" },
+ { "HP_OUT_L", NULL, "Playback" },
+ { "HP_OUT_R", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver ad1980_dai = {
+ .name = "ad1980-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 6,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SND_SOC_STD_AC97_FMTS, },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SND_SOC_STD_AC97_FMTS, },
+};
+
+static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
+{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ unsigned int retry_cnt = 0;
+
+ 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;
+ }
+
+ 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);
+
+ 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");
+
+ return -EIO;
+}
+
+static int ad1980_soc_probe(struct snd_soc_codec *codec)
+{
+ struct snd_ac97 *ac97;
+ struct regmap *regmap;
+ int ret;
+ u16 vendor_id2;
+ u16 ext_status;
+
+ ac97 = snd_soc_new_ac97_codec(codec);
+ if (IS_ERR(ac97)) {
+ ret = PTR_ERR(ac97);
+ dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
+ return ret;
+ }
+
+ regmap = regmap_init_ac97(ac97, &ad1980_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ goto err_free_ac97;
+ }
+
+ snd_soc_codec_init_regmap(codec, regmap);
+ snd_soc_codec_set_drvdata(codec, ac97);
+
+ ret = ad1980_reset(codec, 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");
+ }
+ }
+
+ /* 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);
+
+ /*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);
+
+ return 0;
+
+reset_err:
+ snd_soc_codec_exit_regmap(codec);
+err_free_ac97:
+ snd_soc_free_ac97_codec(ac97);
+ return ret;
+}
+
+static int ad1980_soc_remove(struct snd_soc_codec *codec)
+{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_codec_exit_regmap(codec);
+ snd_soc_free_ac97_codec(ac97);
+ return 0;
+}
+
+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 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;
+}
+
+static struct platform_driver ad1980_codec_driver = {
+ .driver = {
+ .name = "ad1980",
+ },
+
+ .probe = ad1980_probe,
+ .remove = ad1980_remove,
+};
+
+module_platform_driver(ad1980_codec_driver);
+
+MODULE_DESCRIPTION("ASoC ad1980 driver (Obsolete)");
+MODULE_AUTHOR("Roy Huang, Cliff Cai");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ad73311.c b/kernel/sound/soc/codecs/ad73311.c
new file mode 100644
index 000000000..a9400aef6
--- /dev/null
+++ b/kernel/sound/soc/codecs/ad73311.c
@@ -0,0 +1,89 @@
+/*
+ * ad73311.c -- ALSA Soc AD73311 codec support
+ *
+ * Copyright: Analog Device 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>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "ad73311.h"
+
+static const struct snd_soc_dapm_widget ad73311_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("VINP"),
+SND_SOC_DAPM_INPUT("VINN"),
+SND_SOC_DAPM_OUTPUT("VOUTN"),
+SND_SOC_DAPM_OUTPUT("VOUTP"),
+};
+
+static const struct snd_soc_dapm_route ad73311_dapm_routes[] = {
+ { "Capture", NULL, "VINP" },
+ { "Capture", NULL, "VINN" },
+
+ { "VOUTN", NULL, "Playback" },
+ { "VOUTP", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver ad73311_dai = {
+ .name = "ad73311-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+};
+
+static 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 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;
+}
+
+static struct platform_driver ad73311_codec_driver = {
+ .driver = {
+ .name = "ad73311",
+ },
+
+ .probe = ad73311_probe,
+ .remove = ad73311_remove,
+};
+
+module_platform_driver(ad73311_codec_driver);
+
+MODULE_DESCRIPTION("ASoC ad73311 driver");
+MODULE_AUTHOR("Cliff Cai ");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ad73311.h b/kernel/sound/soc/codecs/ad73311.h
new file mode 100644
index 000000000..4b353eefc
--- /dev/null
+++ b/kernel/sound/soc/codecs/ad73311.h
@@ -0,0 +1,88 @@
+/*
+ * File: sound/soc/codec/ad73311.h
+ * Based on:
+ * Author: Cliff Cai <cliff.cai@analog.com>
+ *
+ * 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__
+#define __AD73311_H__
+
+#define AD_CONTROL 0x8000
+#define AD_DATA 0x0000
+#define AD_READ 0x4000
+#define AD_WRITE 0x0000
+
+/* Control register A */
+#define CTRL_REG_A (0 << 8)
+
+#define REGA_MODE_PRO 0x00
+#define REGA_MODE_DATA 0x01
+#define REGA_MODE_MIXED 0x03
+#define REGA_DLB 0x04
+#define REGA_SLB 0x08
+#define REGA_DEVC(x) ((x & 0x7) << 4)
+#define REGA_RESET 0x80
+
+/* Control register B */
+#define CTRL_REG_B (1 << 8)
+
+#define REGB_DIRATE(x) (x & 0x3)
+#define REGB_SCDIV(x) ((x & 0x3) << 2)
+#define REGB_MCDIV(x) ((x & 0x7) << 4)
+#define REGB_CEE (1 << 7)
+
+/* Control register C */
+#define CTRL_REG_C (2 << 8)
+
+#define REGC_PUDEV (1 << 0)
+#define REGC_PUADC (1 << 3)
+#define REGC_PUDAC (1 << 4)
+#define REGC_PUREF (1 << 5)
+#define REGC_REFUSE (1 << 6)
+
+/* Control register D */
+#define CTRL_REG_D (3 << 8)
+
+#define REGD_IGS(x) (x & 0x7)
+#define REGD_RMOD (1 << 3)
+#define REGD_OGS(x) ((x & 0x7) << 4)
+#define REGD_MUTE (1 << 7)
+
+/* Control register E */
+#define CTRL_REG_E (4 << 8)
+
+#define REGE_DA(x) (x & 0x1f)
+#define REGE_IBYP (1 << 5)
+
+/* Control register F */
+#define CTRL_REG_F (5 << 8)
+
+#define REGF_SEEN (1 << 5)
+#define REGF_INV (1 << 6)
+#define REGF_ALB (1 << 7)
+
+#endif
diff --git a/kernel/sound/soc/codecs/adau1373.c b/kernel/sound/soc/codecs/adau1373.c
new file mode 100644
index 000000000..783dcb570
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1373.c
@@ -0,0 +1,1549 @@
+/*
+ * 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/delay.h>
+#include <linux/pm.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"
+
+struct adau1373_dai {
+ unsigned int clk_src;
+ unsigned int sysclk;
+ bool enable_src;
+ bool master;
+};
+
+struct adau1373 {
+ struct regmap *regmap;
+ struct adau1373_dai dais[3];
+};
+
+#define ADAU1373_INPUT_MODE 0x00
+#define ADAU1373_AINL_CTRL(x) (0x01 + (x) * 2)
+#define ADAU1373_AINR_CTRL(x) (0x02 + (x) * 2)
+#define ADAU1373_LLINE_OUT(x) (0x9 + (x) * 2)
+#define ADAU1373_RLINE_OUT(x) (0xa + (x) * 2)
+#define ADAU1373_LSPK_OUT 0x0d
+#define ADAU1373_RSPK_OUT 0x0e
+#define ADAU1373_LHP_OUT 0x0f
+#define ADAU1373_RHP_OUT 0x10
+#define ADAU1373_ADC_GAIN 0x11
+#define ADAU1373_LADC_MIXER 0x12
+#define ADAU1373_RADC_MIXER 0x13
+#define ADAU1373_LLINE1_MIX 0x14
+#define ADAU1373_RLINE1_MIX 0x15
+#define ADAU1373_LLINE2_MIX 0x16
+#define ADAU1373_RLINE2_MIX 0x17
+#define ADAU1373_LSPK_MIX 0x18
+#define ADAU1373_RSPK_MIX 0x19
+#define ADAU1373_LHP_MIX 0x1a
+#define ADAU1373_RHP_MIX 0x1b
+#define ADAU1373_EP_MIX 0x1c
+#define ADAU1373_HP_CTRL 0x1d
+#define ADAU1373_HP_CTRL2 0x1e
+#define ADAU1373_LS_CTRL 0x1f
+#define ADAU1373_EP_CTRL 0x21
+#define ADAU1373_MICBIAS_CTRL1 0x22
+#define ADAU1373_MICBIAS_CTRL2 0x23
+#define ADAU1373_OUTPUT_CTRL 0x24
+#define ADAU1373_PWDN_CTRL1 0x25
+#define ADAU1373_PWDN_CTRL2 0x26
+#define ADAU1373_PWDN_CTRL3 0x27
+#define ADAU1373_DPLL_CTRL(x) (0x28 + (x) * 7)
+#define ADAU1373_PLL_CTRL1(x) (0x29 + (x) * 7)
+#define ADAU1373_PLL_CTRL2(x) (0x2a + (x) * 7)
+#define ADAU1373_PLL_CTRL3(x) (0x2b + (x) * 7)
+#define ADAU1373_PLL_CTRL4(x) (0x2c + (x) * 7)
+#define ADAU1373_PLL_CTRL5(x) (0x2d + (x) * 7)
+#define ADAU1373_PLL_CTRL6(x) (0x2e + (x) * 7)
+#define ADAU1373_HEADDECT 0x36
+#define ADAU1373_ADC_DAC_STATUS 0x37
+#define ADAU1373_ADC_CTRL 0x3c
+#define ADAU1373_DAI(x) (0x44 + (x))
+#define ADAU1373_CLK_SRC_DIV(x) (0x40 + (x) * 2)
+#define ADAU1373_BCLKDIV(x) (0x47 + (x))
+#define ADAU1373_SRC_RATIOA(x) (0x4a + (x) * 2)
+#define ADAU1373_SRC_RATIOB(x) (0x4b + (x) * 2)
+#define ADAU1373_DEEMP_CTRL 0x50
+#define ADAU1373_SRC_DAI_CTRL(x) (0x51 + (x))
+#define ADAU1373_DIN_MIX_CTRL(x) (0x56 + (x))
+#define ADAU1373_DOUT_MIX_CTRL(x) (0x5b + (x))
+#define ADAU1373_DAI_PBL_VOL(x) (0x62 + (x) * 2)
+#define ADAU1373_DAI_PBR_VOL(x) (0x63 + (x) * 2)
+#define ADAU1373_DAI_RECL_VOL(x) (0x68 + (x) * 2)
+#define ADAU1373_DAI_RECR_VOL(x) (0x69 + (x) * 2)
+#define ADAU1373_DAC1_PBL_VOL 0x6e
+#define ADAU1373_DAC1_PBR_VOL 0x6f
+#define ADAU1373_DAC2_PBL_VOL 0x70
+#define ADAU1373_DAC2_PBR_VOL 0x71
+#define ADAU1373_ADC_RECL_VOL 0x72
+#define ADAU1373_ADC_RECR_VOL 0x73
+#define ADAU1373_DMIC_RECL_VOL 0x74
+#define ADAU1373_DMIC_RECR_VOL 0x75
+#define ADAU1373_VOL_GAIN1 0x76
+#define ADAU1373_VOL_GAIN2 0x77
+#define ADAU1373_VOL_GAIN3 0x78
+#define ADAU1373_HPF_CTRL 0x7d
+#define ADAU1373_BASS1 0x7e
+#define ADAU1373_BASS2 0x7f
+#define ADAU1373_DRC(x) (0x80 + (x) * 0x10)
+#define ADAU1373_3D_CTRL1 0xc0
+#define ADAU1373_3D_CTRL2 0xc1
+#define ADAU1373_FDSP_SEL1 0xdc
+#define ADAU1373_FDSP_SEL2 0xdd
+#define ADAU1373_FDSP_SEL3 0xde
+#define ADAU1373_FDSP_SEL4 0xdf
+#define ADAU1373_DIGMICCTRL 0xe2
+#define ADAU1373_DIGEN 0xeb
+#define ADAU1373_SOFT_RESET 0xff
+
+
+#define ADAU1373_PLL_CTRL6_DPLL_BYPASS BIT(1)
+#define ADAU1373_PLL_CTRL6_PLL_EN BIT(0)
+
+#define ADAU1373_DAI_INVERT_BCLK BIT(7)
+#define ADAU1373_DAI_MASTER BIT(6)
+#define ADAU1373_DAI_INVERT_LRCLK BIT(4)
+#define ADAU1373_DAI_WLEN_16 0x0
+#define ADAU1373_DAI_WLEN_20 0x4
+#define ADAU1373_DAI_WLEN_24 0x8
+#define ADAU1373_DAI_WLEN_32 0xc
+#define ADAU1373_DAI_WLEN_MASK 0xc
+#define ADAU1373_DAI_FORMAT_RIGHT_J 0x0
+#define ADAU1373_DAI_FORMAT_LEFT_J 0x1
+#define ADAU1373_DAI_FORMAT_I2S 0x2
+#define ADAU1373_DAI_FORMAT_DSP 0x3
+
+#define ADAU1373_BCLKDIV_SOURCE BIT(5)
+#define ADAU1373_BCLKDIV_SR_MASK (0x07 << 2)
+#define ADAU1373_BCLKDIV_BCLK_MASK 0x03
+#define ADAU1373_BCLKDIV_32 0x03
+#define ADAU1373_BCLKDIV_64 0x02
+#define ADAU1373_BCLKDIV_128 0x01
+#define ADAU1373_BCLKDIV_256 0x00
+
+#define ADAU1373_ADC_CTRL_PEAK_DETECT BIT(0)
+#define ADAU1373_ADC_CTRL_RESET BIT(1)
+#define ADAU1373_ADC_CTRL_RESET_FORCE BIT(2)
+
+#define ADAU1373_OUTPUT_CTRL_LDIFF BIT(3)
+#define ADAU1373_OUTPUT_CTRL_LNFBEN BIT(2)
+
+#define ADAU1373_PWDN_CTRL3_PWR_EN BIT(0)
+
+#define ADAU1373_EP_CTRL_MICBIAS1_OFFSET 4
+#define ADAU1373_EP_CTRL_MICBIAS2_OFFSET 2
+
+static const struct reg_default adau1373_reg_defaults[] = {
+ { ADAU1373_INPUT_MODE, 0x00 },
+ { ADAU1373_AINL_CTRL(0), 0x00 },
+ { ADAU1373_AINR_CTRL(0), 0x00 },
+ { ADAU1373_AINL_CTRL(1), 0x00 },
+ { ADAU1373_AINR_CTRL(1), 0x00 },
+ { ADAU1373_AINL_CTRL(2), 0x00 },
+ { ADAU1373_AINR_CTRL(2), 0x00 },
+ { ADAU1373_AINL_CTRL(3), 0x00 },
+ { ADAU1373_AINR_CTRL(3), 0x00 },
+ { ADAU1373_LLINE_OUT(0), 0x00 },
+ { ADAU1373_RLINE_OUT(0), 0x00 },
+ { ADAU1373_LLINE_OUT(1), 0x00 },
+ { ADAU1373_RLINE_OUT(1), 0x00 },
+ { ADAU1373_LSPK_OUT, 0x00 },
+ { ADAU1373_RSPK_OUT, 0x00 },
+ { ADAU1373_LHP_OUT, 0x00 },
+ { ADAU1373_RHP_OUT, 0x00 },
+ { ADAU1373_ADC_GAIN, 0x00 },
+ { ADAU1373_LADC_MIXER, 0x00 },
+ { ADAU1373_RADC_MIXER, 0x00 },
+ { ADAU1373_LLINE1_MIX, 0x00 },
+ { ADAU1373_RLINE1_MIX, 0x00 },
+ { ADAU1373_LLINE2_MIX, 0x00 },
+ { ADAU1373_RLINE2_MIX, 0x00 },
+ { ADAU1373_LSPK_MIX, 0x00 },
+ { ADAU1373_RSPK_MIX, 0x00 },
+ { ADAU1373_LHP_MIX, 0x00 },
+ { ADAU1373_RHP_MIX, 0x00 },
+ { ADAU1373_EP_MIX, 0x00 },
+ { ADAU1373_HP_CTRL, 0x00 },
+ { ADAU1373_HP_CTRL2, 0x00 },
+ { ADAU1373_LS_CTRL, 0x00 },
+ { ADAU1373_EP_CTRL, 0x00 },
+ { ADAU1373_MICBIAS_CTRL1, 0x00 },
+ { ADAU1373_MICBIAS_CTRL2, 0x00 },
+ { ADAU1373_OUTPUT_CTRL, 0x00 },
+ { ADAU1373_PWDN_CTRL1, 0x00 },
+ { ADAU1373_PWDN_CTRL2, 0x00 },
+ { ADAU1373_PWDN_CTRL3, 0x00 },
+ { ADAU1373_DPLL_CTRL(0), 0x00 },
+ { ADAU1373_PLL_CTRL1(0), 0x00 },
+ { ADAU1373_PLL_CTRL2(0), 0x00 },
+ { ADAU1373_PLL_CTRL3(0), 0x00 },
+ { ADAU1373_PLL_CTRL4(0), 0x00 },
+ { ADAU1373_PLL_CTRL5(0), 0x00 },
+ { ADAU1373_PLL_CTRL6(0), 0x02 },
+ { ADAU1373_DPLL_CTRL(1), 0x00 },
+ { ADAU1373_PLL_CTRL1(1), 0x00 },
+ { ADAU1373_PLL_CTRL2(1), 0x00 },
+ { ADAU1373_PLL_CTRL3(1), 0x00 },
+ { ADAU1373_PLL_CTRL4(1), 0x00 },
+ { ADAU1373_PLL_CTRL5(1), 0x00 },
+ { ADAU1373_PLL_CTRL6(1), 0x02 },
+ { ADAU1373_HEADDECT, 0x00 },
+ { ADAU1373_ADC_CTRL, 0x00 },
+ { ADAU1373_CLK_SRC_DIV(0), 0x00 },
+ { ADAU1373_CLK_SRC_DIV(1), 0x00 },
+ { ADAU1373_DAI(0), 0x0a },
+ { ADAU1373_DAI(1), 0x0a },
+ { ADAU1373_DAI(2), 0x0a },
+ { ADAU1373_BCLKDIV(0), 0x00 },
+ { ADAU1373_BCLKDIV(1), 0x00 },
+ { ADAU1373_BCLKDIV(2), 0x00 },
+ { ADAU1373_SRC_RATIOA(0), 0x00 },
+ { ADAU1373_SRC_RATIOB(0), 0x00 },
+ { ADAU1373_SRC_RATIOA(1), 0x00 },
+ { ADAU1373_SRC_RATIOB(1), 0x00 },
+ { ADAU1373_SRC_RATIOA(2), 0x00 },
+ { ADAU1373_SRC_RATIOB(2), 0x00 },
+ { ADAU1373_DEEMP_CTRL, 0x00 },
+ { ADAU1373_SRC_DAI_CTRL(0), 0x08 },
+ { ADAU1373_SRC_DAI_CTRL(1), 0x08 },
+ { ADAU1373_SRC_DAI_CTRL(2), 0x08 },
+ { ADAU1373_DIN_MIX_CTRL(0), 0x00 },
+ { ADAU1373_DIN_MIX_CTRL(1), 0x00 },
+ { ADAU1373_DIN_MIX_CTRL(2), 0x00 },
+ { ADAU1373_DIN_MIX_CTRL(3), 0x00 },
+ { ADAU1373_DIN_MIX_CTRL(4), 0x00 },
+ { ADAU1373_DOUT_MIX_CTRL(0), 0x00 },
+ { ADAU1373_DOUT_MIX_CTRL(1), 0x00 },
+ { ADAU1373_DOUT_MIX_CTRL(2), 0x00 },
+ { ADAU1373_DOUT_MIX_CTRL(3), 0x00 },
+ { ADAU1373_DOUT_MIX_CTRL(4), 0x00 },
+ { ADAU1373_DAI_PBL_VOL(0), 0x00 },
+ { ADAU1373_DAI_PBR_VOL(0), 0x00 },
+ { ADAU1373_DAI_PBL_VOL(1), 0x00 },
+ { ADAU1373_DAI_PBR_VOL(1), 0x00 },
+ { ADAU1373_DAI_PBL_VOL(2), 0x00 },
+ { ADAU1373_DAI_PBR_VOL(2), 0x00 },
+ { ADAU1373_DAI_RECL_VOL(0), 0x00 },
+ { ADAU1373_DAI_RECR_VOL(0), 0x00 },
+ { ADAU1373_DAI_RECL_VOL(1), 0x00 },
+ { ADAU1373_DAI_RECR_VOL(1), 0x00 },
+ { ADAU1373_DAI_RECL_VOL(2), 0x00 },
+ { ADAU1373_DAI_RECR_VOL(2), 0x00 },
+ { ADAU1373_DAC1_PBL_VOL, 0x00 },
+ { ADAU1373_DAC1_PBR_VOL, 0x00 },
+ { ADAU1373_DAC2_PBL_VOL, 0x00 },
+ { ADAU1373_DAC2_PBR_VOL, 0x00 },
+ { ADAU1373_ADC_RECL_VOL, 0x00 },
+ { ADAU1373_ADC_RECR_VOL, 0x00 },
+ { ADAU1373_DMIC_RECL_VOL, 0x00 },
+ { ADAU1373_DMIC_RECR_VOL, 0x00 },
+ { ADAU1373_VOL_GAIN1, 0x00 },
+ { ADAU1373_VOL_GAIN2, 0x00 },
+ { ADAU1373_VOL_GAIN3, 0x00 },
+ { ADAU1373_HPF_CTRL, 0x00 },
+ { ADAU1373_BASS1, 0x00 },
+ { ADAU1373_BASS2, 0x00 },
+ { ADAU1373_DRC(0) + 0x0, 0x78 },
+ { ADAU1373_DRC(0) + 0x1, 0x18 },
+ { ADAU1373_DRC(0) + 0x2, 0x00 },
+ { ADAU1373_DRC(0) + 0x3, 0x00 },
+ { ADAU1373_DRC(0) + 0x4, 0x00 },
+ { ADAU1373_DRC(0) + 0x5, 0xc0 },
+ { ADAU1373_DRC(0) + 0x6, 0x00 },
+ { ADAU1373_DRC(0) + 0x7, 0x00 },
+ { ADAU1373_DRC(0) + 0x8, 0x00 },
+ { ADAU1373_DRC(0) + 0x9, 0xc0 },
+ { ADAU1373_DRC(0) + 0xa, 0x88 },
+ { ADAU1373_DRC(0) + 0xb, 0x7a },
+ { ADAU1373_DRC(0) + 0xc, 0xdf },
+ { ADAU1373_DRC(0) + 0xd, 0x20 },
+ { ADAU1373_DRC(0) + 0xe, 0x00 },
+ { ADAU1373_DRC(0) + 0xf, 0x00 },
+ { ADAU1373_DRC(1) + 0x0, 0x78 },
+ { ADAU1373_DRC(1) + 0x1, 0x18 },
+ { ADAU1373_DRC(1) + 0x2, 0x00 },
+ { ADAU1373_DRC(1) + 0x3, 0x00 },
+ { ADAU1373_DRC(1) + 0x4, 0x00 },
+ { ADAU1373_DRC(1) + 0x5, 0xc0 },
+ { ADAU1373_DRC(1) + 0x6, 0x00 },
+ { ADAU1373_DRC(1) + 0x7, 0x00 },
+ { ADAU1373_DRC(1) + 0x8, 0x00 },
+ { ADAU1373_DRC(1) + 0x9, 0xc0 },
+ { ADAU1373_DRC(1) + 0xa, 0x88 },
+ { ADAU1373_DRC(1) + 0xb, 0x7a },
+ { ADAU1373_DRC(1) + 0xc, 0xdf },
+ { ADAU1373_DRC(1) + 0xd, 0x20 },
+ { ADAU1373_DRC(1) + 0xe, 0x00 },
+ { ADAU1373_DRC(1) + 0xf, 0x00 },
+ { ADAU1373_DRC(2) + 0x0, 0x78 },
+ { ADAU1373_DRC(2) + 0x1, 0x18 },
+ { ADAU1373_DRC(2) + 0x2, 0x00 },
+ { ADAU1373_DRC(2) + 0x3, 0x00 },
+ { ADAU1373_DRC(2) + 0x4, 0x00 },
+ { ADAU1373_DRC(2) + 0x5, 0xc0 },
+ { ADAU1373_DRC(2) + 0x6, 0x00 },
+ { ADAU1373_DRC(2) + 0x7, 0x00 },
+ { ADAU1373_DRC(2) + 0x8, 0x00 },
+ { ADAU1373_DRC(2) + 0x9, 0xc0 },
+ { ADAU1373_DRC(2) + 0xa, 0x88 },
+ { ADAU1373_DRC(2) + 0xb, 0x7a },
+ { ADAU1373_DRC(2) + 0xc, 0xdf },
+ { ADAU1373_DRC(2) + 0xd, 0x20 },
+ { ADAU1373_DRC(2) + 0xe, 0x00 },
+ { ADAU1373_DRC(2) + 0xf, 0x00 },
+ { ADAU1373_3D_CTRL1, 0x00 },
+ { ADAU1373_3D_CTRL2, 0x00 },
+ { ADAU1373_FDSP_SEL1, 0x00 },
+ { ADAU1373_FDSP_SEL2, 0x00 },
+ { ADAU1373_FDSP_SEL2, 0x00 },
+ { ADAU1373_FDSP_SEL4, 0x00 },
+ { ADAU1373_DIGMICCTRL, 0x00 },
+ { ADAU1373_DIGEN, 0x00 },
+};
+
+static const unsigned int adau1373_out_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
+ 16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
+ 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0),
+};
+
+static const DECLARE_TLV_DB_MINMAX(adau1373_digital_tlv, -9563, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_in_pga_tlv, -1300, 100, 1);
+static const DECLARE_TLV_DB_SCALE(adau1373_ep_tlv, -600, 600, 1);
+
+static const DECLARE_TLV_DB_SCALE(adau1373_input_boost_tlv, 0, 2000, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_gain_boost_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_speaker_boost_tlv, 1200, 600, 0);
+
+static const char *adau1373_fdsp_sel_text[] = {
+ "None",
+ "Channel 1",
+ "Channel 2",
+ "Channel 3",
+ "Channel 4",
+ "Channel 5",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1373_drc1_channel_enum,
+ ADAU1373_FDSP_SEL1, 4, adau1373_fdsp_sel_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_drc2_channel_enum,
+ ADAU1373_FDSP_SEL1, 0, adau1373_fdsp_sel_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_drc3_channel_enum,
+ ADAU1373_FDSP_SEL2, 0, adau1373_fdsp_sel_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_hpf_channel_enum,
+ ADAU1373_FDSP_SEL3, 0, adau1373_fdsp_sel_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_bass_channel_enum,
+ ADAU1373_FDSP_SEL4, 4, adau1373_fdsp_sel_text);
+
+static const char *adau1373_hpf_cutoff_text[] = {
+ "3.7Hz", "50Hz", "100Hz", "150Hz", "200Hz", "250Hz", "300Hz", "350Hz",
+ "400Hz", "450Hz", "500Hz", "550Hz", "600Hz", "650Hz", "700Hz", "750Hz",
+ "800Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1373_hpf_cutoff_enum,
+ ADAU1373_HPF_CTRL, 3, adau1373_hpf_cutoff_text);
+
+static const char *adau1373_bass_lpf_cutoff_text[] = {
+ "801Hz", "1001Hz",
+};
+
+static const char *adau1373_bass_clip_level_text[] = {
+ "0.125", "0.250", "0.370", "0.500", "0.625", "0.750", "0.875",
+};
+
+static const unsigned int adau1373_bass_clip_level_values[] = {
+ 1, 2, 3, 4, 5, 6, 7,
+};
+
+static const char *adau1373_bass_hpf_cutoff_text[] = {
+ "158Hz", "232Hz", "347Hz", "520Hz",
+};
+
+static const unsigned int adau1373_bass_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 2, TLV_DB_SCALE_ITEM(-600, 600, 1),
+ 3, 4, TLV_DB_SCALE_ITEM(950, 250, 0),
+ 5, 7, TLV_DB_SCALE_ITEM(1400, 150, 0),
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1373_bass_lpf_cutoff_enum,
+ ADAU1373_BASS1, 5, adau1373_bass_lpf_cutoff_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1373_bass_clip_level_enum,
+ ADAU1373_BASS1, 2, 7, adau1373_bass_clip_level_text,
+ adau1373_bass_clip_level_values);
+
+static SOC_ENUM_SINGLE_DECL(adau1373_bass_hpf_cutoff_enum,
+ ADAU1373_BASS1, 0, adau1373_bass_hpf_cutoff_text);
+
+static const char *adau1373_3d_level_text[] = {
+ "0%", "6.67%", "13.33%", "20%", "26.67%", "33.33%",
+ "40%", "46.67%", "53.33%", "60%", "66.67%", "73.33%",
+ "80%", "86.67", "99.33%", "100%"
+};
+
+static const char *adau1373_3d_cutoff_text[] = {
+ "No 3D", "0.03125 fs", "0.04583 fs", "0.075 fs", "0.11458 fs",
+ "0.16875 fs", "0.27083 fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum,
+ ADAU1373_3D_CTRL1, 4, adau1373_3d_level_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum,
+ ADAU1373_3D_CTRL1, 0, adau1373_3d_cutoff_text);
+
+static const unsigned int adau1373_3d_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 7, TLV_DB_LINEAR_ITEM(-1800, -120),
+};
+
+static const char *adau1373_lr_mux_text[] = {
+ "Mute",
+ "Right Channel (L+R)",
+ "Left Channel (L+R)",
+ "Stereo",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1373_lineout1_lr_mux_enum,
+ ADAU1373_OUTPUT_CTRL, 4, adau1373_lr_mux_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_lineout2_lr_mux_enum,
+ ADAU1373_OUTPUT_CTRL, 6, adau1373_lr_mux_text);
+static SOC_ENUM_SINGLE_DECL(adau1373_speaker_lr_mux_enum,
+ ADAU1373_LS_CTRL, 4, adau1373_lr_mux_text);
+
+static const struct snd_kcontrol_new adau1373_controls[] = {
+ SOC_DOUBLE_R_TLV("AIF1 Capture Volume", ADAU1373_DAI_RECL_VOL(0),
+ ADAU1373_DAI_RECR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv),
+ SOC_DOUBLE_R_TLV("AIF2 Capture Volume", ADAU1373_DAI_RECL_VOL(1),
+ ADAU1373_DAI_RECR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv),
+ SOC_DOUBLE_R_TLV("AIF3 Capture Volume", ADAU1373_DAI_RECL_VOL(2),
+ ADAU1373_DAI_RECR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv),
+
+ SOC_DOUBLE_R_TLV("ADC Capture Volume", ADAU1373_ADC_RECL_VOL,
+ ADAU1373_ADC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+ SOC_DOUBLE_R_TLV("DMIC Capture Volume", ADAU1373_DMIC_RECL_VOL,
+ ADAU1373_DMIC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+
+ SOC_DOUBLE_R_TLV("AIF1 Playback Volume", ADAU1373_DAI_PBL_VOL(0),
+ ADAU1373_DAI_PBR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv),
+ SOC_DOUBLE_R_TLV("AIF2 Playback Volume", ADAU1373_DAI_PBL_VOL(1),
+ ADAU1373_DAI_PBR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv),
+ SOC_DOUBLE_R_TLV("AIF3 Playback Volume", ADAU1373_DAI_PBL_VOL(2),
+ ADAU1373_DAI_PBR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv),
+
+ SOC_DOUBLE_R_TLV("DAC1 Playback Volume", ADAU1373_DAC1_PBL_VOL,
+ ADAU1373_DAC1_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Playback Volume", ADAU1373_DAC2_PBL_VOL,
+ ADAU1373_DAC2_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+
+ SOC_DOUBLE_R_TLV("Lineout1 Playback Volume", ADAU1373_LLINE_OUT(0),
+ ADAU1373_RLINE_OUT(0), 0, 0x1f, 0, adau1373_out_tlv),
+ SOC_DOUBLE_R_TLV("Speaker Playback Volume", ADAU1373_LSPK_OUT,
+ ADAU1373_RSPK_OUT, 0, 0x1f, 0, adau1373_out_tlv),
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1373_LHP_OUT,
+ ADAU1373_RHP_OUT, 0, 0x1f, 0, adau1373_out_tlv),
+
+ SOC_DOUBLE_R_TLV("Input 1 Capture Volume", ADAU1373_AINL_CTRL(0),
+ ADAU1373_AINR_CTRL(0), 0, 0x1f, 0, adau1373_in_pga_tlv),
+ SOC_DOUBLE_R_TLV("Input 2 Capture Volume", ADAU1373_AINL_CTRL(1),
+ ADAU1373_AINR_CTRL(1), 0, 0x1f, 0, adau1373_in_pga_tlv),
+ SOC_DOUBLE_R_TLV("Input 3 Capture Volume", ADAU1373_AINL_CTRL(2),
+ ADAU1373_AINR_CTRL(2), 0, 0x1f, 0, adau1373_in_pga_tlv),
+ SOC_DOUBLE_R_TLV("Input 4 Capture Volume", ADAU1373_AINL_CTRL(3),
+ ADAU1373_AINR_CTRL(3), 0, 0x1f, 0, adau1373_in_pga_tlv),
+
+ SOC_SINGLE_TLV("Earpiece Playback Volume", ADAU1373_EP_CTRL, 0, 3, 0,
+ adau1373_ep_tlv),
+
+ SOC_DOUBLE_TLV("AIF3 Boost Playback Volume", ADAU1373_VOL_GAIN1, 4, 5,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("AIF2 Boost Playback Volume", ADAU1373_VOL_GAIN1, 2, 3,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("AIF1 Boost Playback Volume", ADAU1373_VOL_GAIN1, 0, 1,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("AIF3 Boost Capture Volume", ADAU1373_VOL_GAIN2, 4, 5,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("AIF2 Boost Capture Volume", ADAU1373_VOL_GAIN2, 2, 3,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("AIF1 Boost Capture Volume", ADAU1373_VOL_GAIN2, 0, 1,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("DMIC Boost Capture Volume", ADAU1373_VOL_GAIN3, 6, 7,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("ADC Boost Capture Volume", ADAU1373_VOL_GAIN3, 4, 5,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("DAC2 Boost Playback Volume", ADAU1373_VOL_GAIN3, 2, 3,
+ 1, 0, adau1373_gain_boost_tlv),
+ SOC_DOUBLE_TLV("DAC1 Boost Playback Volume", ADAU1373_VOL_GAIN3, 0, 1,
+ 1, 0, adau1373_gain_boost_tlv),
+
+ SOC_DOUBLE_TLV("Input 1 Boost Capture Volume", ADAU1373_ADC_GAIN, 0, 4,
+ 1, 0, adau1373_input_boost_tlv),
+ SOC_DOUBLE_TLV("Input 2 Boost Capture Volume", ADAU1373_ADC_GAIN, 1, 5,
+ 1, 0, adau1373_input_boost_tlv),
+ SOC_DOUBLE_TLV("Input 3 Boost Capture Volume", ADAU1373_ADC_GAIN, 2, 6,
+ 1, 0, adau1373_input_boost_tlv),
+ SOC_DOUBLE_TLV("Input 4 Boost Capture Volume", ADAU1373_ADC_GAIN, 3, 7,
+ 1, 0, adau1373_input_boost_tlv),
+
+ SOC_DOUBLE_TLV("Speaker Boost Playback Volume", ADAU1373_LS_CTRL, 2, 3,
+ 1, 0, adau1373_speaker_boost_tlv),
+
+ SOC_ENUM("Lineout1 LR Mux", adau1373_lineout1_lr_mux_enum),
+ SOC_ENUM("Speaker LR Mux", adau1373_speaker_lr_mux_enum),
+
+ SOC_ENUM("HPF Cutoff", adau1373_hpf_cutoff_enum),
+ SOC_DOUBLE("HPF Switch", ADAU1373_HPF_CTRL, 1, 0, 1, 0),
+ SOC_ENUM("HPF Channel", adau1373_hpf_channel_enum),
+
+ SOC_ENUM("Bass HPF Cutoff", adau1373_bass_hpf_cutoff_enum),
+ SOC_ENUM("Bass Clip Level Threshold", adau1373_bass_clip_level_enum),
+ SOC_ENUM("Bass LPF Cutoff", adau1373_bass_lpf_cutoff_enum),
+ SOC_DOUBLE("Bass Playback Switch", ADAU1373_BASS2, 0, 1, 1, 0),
+ SOC_SINGLE_TLV("Bass Playback Volume", ADAU1373_BASS2, 2, 7, 0,
+ adau1373_bass_tlv),
+ SOC_ENUM("Bass Channel", adau1373_bass_channel_enum),
+
+ SOC_ENUM("3D Freq", adau1373_3d_cutoff_enum),
+ SOC_ENUM("3D Level", adau1373_3d_level_enum),
+ SOC_SINGLE("3D Playback Switch", ADAU1373_3D_CTRL2, 0, 1, 0),
+ SOC_SINGLE_TLV("3D Playback Volume", ADAU1373_3D_CTRL2, 2, 7, 0,
+ adau1373_3d_tlv),
+ SOC_ENUM("3D Channel", adau1373_bass_channel_enum),
+
+ SOC_SINGLE("Zero Cross Switch", ADAU1373_PWDN_CTRL3, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_lineout2_controls[] = {
+ SOC_DOUBLE_R_TLV("Lineout2 Playback Volume", ADAU1373_LLINE_OUT(1),
+ ADAU1373_RLINE_OUT(1), 0, 0x1f, 0, adau1373_out_tlv),
+ SOC_ENUM("Lineout2 LR Mux", adau1373_lineout2_lr_mux_enum),
+};
+
+static const struct snd_kcontrol_new adau1373_drc_controls[] = {
+ SOC_ENUM("DRC1 Channel", adau1373_drc1_channel_enum),
+ SOC_ENUM("DRC2 Channel", adau1373_drc2_channel_enum),
+ SOC_ENUM("DRC3 Channel", adau1373_drc3_channel_enum),
+};
+
+static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ unsigned int pll_id = w->name[3] - '1';
+ unsigned int val;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ val = ADAU1373_PLL_CTRL6_PLL_EN;
+ else
+ val = 0;
+
+ regmap_update_bits(adau1373->regmap, ADAU1373_PLL_CTRL6(pll_id),
+ ADAU1373_PLL_CTRL6_PLL_EN, val);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ mdelay(5);
+
+ return 0;
+}
+
+static const char *adau1373_decimator_text[] = {
+ "ADC",
+ "DMIC1",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(adau1373_decimator_enum,
+ adau1373_decimator_text);
+
+static const struct snd_kcontrol_new adau1373_decimator_mux =
+ SOC_DAPM_ENUM("Decimator Mux", adau1373_decimator_enum);
+
+static const struct snd_kcontrol_new adau1373_left_adc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_LADC_MIXER, 4, 1, 0),
+ SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_LADC_MIXER, 3, 1, 0),
+ SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_LADC_MIXER, 2, 1, 0),
+ SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_LADC_MIXER, 1, 1, 0),
+ SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_LADC_MIXER, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_right_adc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_RADC_MIXER, 4, 1, 0),
+ SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_RADC_MIXER, 3, 1, 0),
+ SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_RADC_MIXER, 2, 1, 0),
+ SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_RADC_MIXER, 1, 1, 0),
+ SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_RADC_MIXER, 0, 1, 0),
+};
+
+#define DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+ SOC_DAPM_SINGLE("Left DAC2 Switch", _reg, 7, 1, 0), \
+ SOC_DAPM_SINGLE("Right DAC2 Switch", _reg, 6, 1, 0), \
+ SOC_DAPM_SINGLE("Left DAC1 Switch", _reg, 5, 1, 0), \
+ SOC_DAPM_SINGLE("Right DAC1 Switch", _reg, 4, 1, 0), \
+ SOC_DAPM_SINGLE("Input 4 Bypass Switch", _reg, 3, 1, 0), \
+ SOC_DAPM_SINGLE("Input 3 Bypass Switch", _reg, 2, 1, 0), \
+ SOC_DAPM_SINGLE("Input 2 Bypass Switch", _reg, 1, 1, 0), \
+ SOC_DAPM_SINGLE("Input 1 Bypass Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line1_mixer_controls,
+ ADAU1373_LLINE1_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line1_mixer_controls,
+ ADAU1373_RLINE1_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line2_mixer_controls,
+ ADAU1373_LLINE2_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line2_mixer_controls,
+ ADAU1373_RLINE2_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_spk_mixer_controls,
+ ADAU1373_LSPK_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_spk_mixer_controls,
+ ADAU1373_RSPK_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_ep_mixer_controls,
+ ADAU1373_EP_MIX);
+
+static const struct snd_kcontrol_new adau1373_left_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", ADAU1373_LHP_MIX, 5, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", ADAU1373_LHP_MIX, 4, 1, 0),
+ SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_LHP_MIX, 3, 1, 0),
+ SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_LHP_MIX, 2, 1, 0),
+ SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_LHP_MIX, 1, 1, 0),
+ SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_LHP_MIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_right_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Right DAC1 Switch", ADAU1373_RHP_MIX, 5, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", ADAU1373_RHP_MIX, 4, 1, 0),
+ SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_RHP_MIX, 3, 1, 0),
+ SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_RHP_MIX, 2, 1, 0),
+ SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_RHP_MIX, 1, 1, 0),
+ SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_RHP_MIX, 0, 1, 0),
+};
+
+#define DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+ SOC_DAPM_SINGLE("DMIC2 Swapped Switch", _reg, 6, 1, 0), \
+ SOC_DAPM_SINGLE("DMIC2 Switch", _reg, 5, 1, 0), \
+ SOC_DAPM_SINGLE("ADC/DMIC1 Swapped Switch", _reg, 4, 1, 0), \
+ SOC_DAPM_SINGLE("ADC/DMIC1 Switch", _reg, 3, 1, 0), \
+ SOC_DAPM_SINGLE("AIF3 Switch", _reg, 2, 1, 0), \
+ SOC_DAPM_SINGLE("AIF2 Switch", _reg, 1, 1, 0), \
+ SOC_DAPM_SINGLE("AIF1 Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel1_mixer_controls,
+ ADAU1373_DIN_MIX_CTRL(0));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel2_mixer_controls,
+ ADAU1373_DIN_MIX_CTRL(1));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel3_mixer_controls,
+ ADAU1373_DIN_MIX_CTRL(2));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel4_mixer_controls,
+ ADAU1373_DIN_MIX_CTRL(3));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel5_mixer_controls,
+ ADAU1373_DIN_MIX_CTRL(4));
+
+#define DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+ SOC_DAPM_SINGLE("DSP Channel5 Switch", _reg, 4, 1, 0), \
+ SOC_DAPM_SINGLE("DSP Channel4 Switch", _reg, 3, 1, 0), \
+ SOC_DAPM_SINGLE("DSP Channel3 Switch", _reg, 2, 1, 0), \
+ SOC_DAPM_SINGLE("DSP Channel2 Switch", _reg, 1, 1, 0), \
+ SOC_DAPM_SINGLE("DSP Channel1 Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif1_mixer_controls,
+ ADAU1373_DOUT_MIX_CTRL(0));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif2_mixer_controls,
+ ADAU1373_DOUT_MIX_CTRL(1));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif3_mixer_controls,
+ ADAU1373_DOUT_MIX_CTRL(2));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac1_mixer_controls,
+ ADAU1373_DOUT_MIX_CTRL(3));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac2_mixer_controls,
+ ADAU1373_DOUT_MIX_CTRL(4));
+
+static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
+ /* Datasheet claims Left ADC is bit 6 and Right ADC is bit 7, but that
+ * doesn't seem to be the case. */
+ SND_SOC_DAPM_ADC("Left ADC", NULL, ADAU1373_PWDN_CTRL1, 7, 0),
+ SND_SOC_DAPM_ADC("Right ADC", NULL, ADAU1373_PWDN_CTRL1, 6, 0),
+
+ SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1373_DIGMICCTRL, 0, 0),
+ SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1373_DIGMICCTRL, 2, 0),
+
+ SND_SOC_DAPM_MUX("Decimator Mux", SND_SOC_NOPM, 0, 0,
+ &adau1373_decimator_mux),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", ADAU1373_PWDN_CTRL1, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", ADAU1373_PWDN_CTRL1, 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("IN4PGA", ADAU1373_PWDN_CTRL1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IN3PGA", ADAU1373_PWDN_CTRL1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IN2PGA", ADAU1373_PWDN_CTRL1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IN1PGA", ADAU1373_PWDN_CTRL1, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("Left DAC2", NULL, ADAU1373_PWDN_CTRL2, 7, 0),
+ SND_SOC_DAPM_DAC("Right DAC2", NULL, ADAU1373_PWDN_CTRL2, 6, 0),
+ SND_SOC_DAPM_DAC("Left DAC1", NULL, ADAU1373_PWDN_CTRL2, 5, 0),
+ SND_SOC_DAPM_DAC("Right DAC1", NULL, ADAU1373_PWDN_CTRL2, 4, 0),
+
+ SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_left_adc_mixer_controls),
+ SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_right_adc_mixer_controls),
+
+ SOC_MIXER_ARRAY("Left Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 3, 0,
+ adau1373_left_line2_mixer_controls),
+ SOC_MIXER_ARRAY("Right Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 2, 0,
+ adau1373_right_line2_mixer_controls),
+ SOC_MIXER_ARRAY("Left Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 1, 0,
+ adau1373_left_line1_mixer_controls),
+ SOC_MIXER_ARRAY("Right Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 0, 0,
+ adau1373_right_line1_mixer_controls),
+
+ SOC_MIXER_ARRAY("Earpiece Mixer", ADAU1373_PWDN_CTRL3, 4, 0,
+ adau1373_ep_mixer_controls),
+ SOC_MIXER_ARRAY("Left Speaker Mixer", ADAU1373_PWDN_CTRL3, 3, 0,
+ adau1373_left_spk_mixer_controls),
+ SOC_MIXER_ARRAY("Right Speaker Mixer", ADAU1373_PWDN_CTRL3, 2, 0,
+ adau1373_right_spk_mixer_controls),
+ SOC_MIXER_ARRAY("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_left_hp_mixer_controls),
+ SOC_MIXER_ARRAY("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_right_hp_mixer_controls),
+ SND_SOC_DAPM_SUPPLY("Headphone Enable", ADAU1373_PWDN_CTRL3, 1, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("AIF1 CLK", ADAU1373_SRC_DAI_CTRL(0), 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF2 CLK", ADAU1373_SRC_DAI_CTRL(1), 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF3 CLK", ADAU1373_SRC_DAI_CTRL(2), 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF1 IN SRC", ADAU1373_SRC_DAI_CTRL(0), 2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF1 OUT SRC", ADAU1373_SRC_DAI_CTRL(0), 1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF2 IN SRC", ADAU1373_SRC_DAI_CTRL(1), 2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF2 OUT SRC", ADAU1373_SRC_DAI_CTRL(1), 1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF3 IN SRC", ADAU1373_SRC_DAI_CTRL(2), 2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF3 OUT SRC", ADAU1373_SRC_DAI_CTRL(2), 1, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_AIF_IN("AIF1 IN", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1 OUT", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2 IN", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2 OUT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF3 IN", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF3 OUT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ SOC_MIXER_ARRAY("DSP Channel1 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dsp_channel1_mixer_controls),
+ SOC_MIXER_ARRAY("DSP Channel2 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dsp_channel2_mixer_controls),
+ SOC_MIXER_ARRAY("DSP Channel3 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dsp_channel3_mixer_controls),
+ SOC_MIXER_ARRAY("DSP Channel4 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dsp_channel4_mixer_controls),
+ SOC_MIXER_ARRAY("DSP Channel5 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dsp_channel5_mixer_controls),
+
+ SOC_MIXER_ARRAY("AIF1 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_aif1_mixer_controls),
+ SOC_MIXER_ARRAY("AIF2 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_aif2_mixer_controls),
+ SOC_MIXER_ARRAY("AIF3 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_aif3_mixer_controls),
+ SOC_MIXER_ARRAY("DAC1 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dac1_mixer_controls),
+ SOC_MIXER_ARRAY("DAC2 Mixer", SND_SOC_NOPM, 0, 0,
+ adau1373_dac2_mixer_controls),
+
+ SND_SOC_DAPM_SUPPLY("DSP", ADAU1373_DIGEN, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Recording Engine B", ADAU1373_DIGEN, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Recording Engine A", ADAU1373_DIGEN, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Playback Engine B", ADAU1373_DIGEN, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Playback Engine A", ADAU1373_DIGEN, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PLL1", SND_SOC_NOPM, 0, 0, adau1373_pll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("PLL2", SND_SOC_NOPM, 0, 0, adau1373_pll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("SYSCLK1", ADAU1373_CLK_SRC_DIV(0), 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SYSCLK2", ADAU1373_CLK_SRC_DIV(1), 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("AIN1L"),
+ SND_SOC_DAPM_INPUT("AIN1R"),
+ SND_SOC_DAPM_INPUT("AIN2L"),
+ SND_SOC_DAPM_INPUT("AIN2R"),
+ SND_SOC_DAPM_INPUT("AIN3L"),
+ SND_SOC_DAPM_INPUT("AIN3R"),
+ SND_SOC_DAPM_INPUT("AIN4L"),
+ SND_SOC_DAPM_INPUT("AIN4R"),
+
+ SND_SOC_DAPM_INPUT("DMIC1DAT"),
+ SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+ SND_SOC_DAPM_OUTPUT("LOUT1L"),
+ SND_SOC_DAPM_OUTPUT("LOUT1R"),
+ SND_SOC_DAPM_OUTPUT("LOUT2L"),
+ SND_SOC_DAPM_OUTPUT("LOUT2R"),
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("EP"),
+};
+
+static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ unsigned int dai;
+ const char *clk;
+
+ dai = sink->name[3] - '1';
+
+ if (!adau1373->dais[dai].master)
+ return 0;
+
+ if (adau1373->dais[dai].clk_src == ADAU1373_CLK_SRC_PLL1)
+ clk = "SYSCLK1";
+ else
+ clk = "SYSCLK2";
+
+ return strcmp(source->name, clk) == 0;
+}
+
+static int adau1373_check_src(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ unsigned int dai;
+
+ dai = sink->name[3] - '1';
+
+ return adau1373->dais[dai].enable_src;
+}
+
+#define DSP_CHANNEL_MIXER_ROUTES(_sink) \
+ { _sink, "DMIC2 Swapped Switch", "DMIC2" }, \
+ { _sink, "DMIC2 Switch", "DMIC2" }, \
+ { _sink, "ADC/DMIC1 Swapped Switch", "Decimator Mux" }, \
+ { _sink, "ADC/DMIC1 Switch", "Decimator Mux" }, \
+ { _sink, "AIF1 Switch", "AIF1 IN" }, \
+ { _sink, "AIF2 Switch", "AIF2 IN" }, \
+ { _sink, "AIF3 Switch", "AIF3 IN" }
+
+#define DSP_OUTPUT_MIXER_ROUTES(_sink) \
+ { _sink, "DSP Channel1 Switch", "DSP Channel1 Mixer" }, \
+ { _sink, "DSP Channel2 Switch", "DSP Channel2 Mixer" }, \
+ { _sink, "DSP Channel3 Switch", "DSP Channel3 Mixer" }, \
+ { _sink, "DSP Channel4 Switch", "DSP Channel4 Mixer" }, \
+ { _sink, "DSP Channel5 Switch", "DSP Channel5 Mixer" }
+
+#define LEFT_OUTPUT_MIXER_ROUTES(_sink) \
+ { _sink, "Right DAC2 Switch", "Right DAC2" }, \
+ { _sink, "Left DAC2 Switch", "Left DAC2" }, \
+ { _sink, "Right DAC1 Switch", "Right DAC1" }, \
+ { _sink, "Left DAC1 Switch", "Left DAC1" }, \
+ { _sink, "Input 1 Bypass Switch", "IN1PGA" }, \
+ { _sink, "Input 2 Bypass Switch", "IN2PGA" }, \
+ { _sink, "Input 3 Bypass Switch", "IN3PGA" }, \
+ { _sink, "Input 4 Bypass Switch", "IN4PGA" }
+
+#define RIGHT_OUTPUT_MIXER_ROUTES(_sink) \
+ { _sink, "Right DAC2 Switch", "Right DAC2" }, \
+ { _sink, "Left DAC2 Switch", "Left DAC2" }, \
+ { _sink, "Right DAC1 Switch", "Right DAC1" }, \
+ { _sink, "Left DAC1 Switch", "Left DAC1" }, \
+ { _sink, "Input 1 Bypass Switch", "IN1PGA" }, \
+ { _sink, "Input 2 Bypass Switch", "IN2PGA" }, \
+ { _sink, "Input 3 Bypass Switch", "IN3PGA" }, \
+ { _sink, "Input 4 Bypass Switch", "IN4PGA" }
+
+static const struct snd_soc_dapm_route adau1373_dapm_routes[] = {
+ { "Left ADC Mixer", "DAC1 Switch", "Left DAC1" },
+ { "Left ADC Mixer", "Input 1 Switch", "IN1PGA" },
+ { "Left ADC Mixer", "Input 2 Switch", "IN2PGA" },
+ { "Left ADC Mixer", "Input 3 Switch", "IN3PGA" },
+ { "Left ADC Mixer", "Input 4 Switch", "IN4PGA" },
+
+ { "Right ADC Mixer", "DAC1 Switch", "Right DAC1" },
+ { "Right ADC Mixer", "Input 1 Switch", "IN1PGA" },
+ { "Right ADC Mixer", "Input 2 Switch", "IN2PGA" },
+ { "Right ADC Mixer", "Input 3 Switch", "IN3PGA" },
+ { "Right ADC Mixer", "Input 4 Switch", "IN4PGA" },
+
+ { "Left ADC", NULL, "Left ADC Mixer" },
+ { "Right ADC", NULL, "Right ADC Mixer" },
+
+ { "Decimator Mux", "ADC", "Left ADC" },
+ { "Decimator Mux", "ADC", "Right ADC" },
+ { "Decimator Mux", "DMIC1", "DMIC1" },
+
+ DSP_CHANNEL_MIXER_ROUTES("DSP Channel1 Mixer"),
+ DSP_CHANNEL_MIXER_ROUTES("DSP Channel2 Mixer"),
+ DSP_CHANNEL_MIXER_ROUTES("DSP Channel3 Mixer"),
+ DSP_CHANNEL_MIXER_ROUTES("DSP Channel4 Mixer"),
+ DSP_CHANNEL_MIXER_ROUTES("DSP Channel5 Mixer"),
+
+ DSP_OUTPUT_MIXER_ROUTES("AIF1 Mixer"),
+ DSP_OUTPUT_MIXER_ROUTES("AIF2 Mixer"),
+ DSP_OUTPUT_MIXER_ROUTES("AIF3 Mixer"),
+ DSP_OUTPUT_MIXER_ROUTES("DAC1 Mixer"),
+ DSP_OUTPUT_MIXER_ROUTES("DAC2 Mixer"),
+
+ { "AIF1 OUT", NULL, "AIF1 Mixer" },
+ { "AIF2 OUT", NULL, "AIF2 Mixer" },
+ { "AIF3 OUT", NULL, "AIF3 Mixer" },
+ { "Left DAC1", NULL, "DAC1 Mixer" },
+ { "Right DAC1", NULL, "DAC1 Mixer" },
+ { "Left DAC2", NULL, "DAC2 Mixer" },
+ { "Right DAC2", NULL, "DAC2 Mixer" },
+
+ LEFT_OUTPUT_MIXER_ROUTES("Left Lineout1 Mixer"),
+ RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout1 Mixer"),
+ LEFT_OUTPUT_MIXER_ROUTES("Left Lineout2 Mixer"),
+ RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout2 Mixer"),
+ LEFT_OUTPUT_MIXER_ROUTES("Left Speaker Mixer"),
+ RIGHT_OUTPUT_MIXER_ROUTES("Right Speaker Mixer"),
+
+ { "Left Headphone Mixer", "Left DAC2 Switch", "Left DAC2" },
+ { "Left Headphone Mixer", "Left DAC1 Switch", "Left DAC1" },
+ { "Left Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+ { "Left Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+ { "Left Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+ { "Left Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+ { "Right Headphone Mixer", "Right DAC2 Switch", "Right DAC2" },
+ { "Right Headphone Mixer", "Right DAC1 Switch", "Right DAC1" },
+ { "Right Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+ { "Right Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+ { "Right Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+ { "Right Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+
+ { "Left Headphone Mixer", NULL, "Headphone Enable" },
+ { "Right Headphone Mixer", NULL, "Headphone Enable" },
+
+ { "Earpiece Mixer", "Right DAC2 Switch", "Right DAC2" },
+ { "Earpiece Mixer", "Left DAC2 Switch", "Left DAC2" },
+ { "Earpiece Mixer", "Right DAC1 Switch", "Right DAC1" },
+ { "Earpiece Mixer", "Left DAC1 Switch", "Left DAC1" },
+ { "Earpiece Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+ { "Earpiece Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+ { "Earpiece Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+ { "Earpiece Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+
+ { "LOUT1L", NULL, "Left Lineout1 Mixer" },
+ { "LOUT1R", NULL, "Right Lineout1 Mixer" },
+ { "LOUT2L", NULL, "Left Lineout2 Mixer" },
+ { "LOUT2R", NULL, "Right Lineout2 Mixer" },
+ { "SPKL", NULL, "Left Speaker Mixer" },
+ { "SPKR", NULL, "Right Speaker Mixer" },
+ { "HPL", NULL, "Left Headphone Mixer" },
+ { "HPR", NULL, "Right Headphone Mixer" },
+ { "EP", NULL, "Earpiece Mixer" },
+
+ { "IN1PGA", NULL, "AIN1L" },
+ { "IN2PGA", NULL, "AIN2L" },
+ { "IN3PGA", NULL, "AIN3L" },
+ { "IN4PGA", NULL, "AIN4L" },
+ { "IN1PGA", NULL, "AIN1R" },
+ { "IN2PGA", NULL, "AIN2R" },
+ { "IN3PGA", NULL, "AIN3R" },
+ { "IN4PGA", NULL, "AIN4R" },
+
+ { "SYSCLK1", NULL, "PLL1" },
+ { "SYSCLK2", NULL, "PLL2" },
+
+ { "Left DAC1", NULL, "SYSCLK1" },
+ { "Right DAC1", NULL, "SYSCLK1" },
+ { "Left DAC2", NULL, "SYSCLK1" },
+ { "Right DAC2", NULL, "SYSCLK1" },
+ { "Left ADC", NULL, "SYSCLK1" },
+ { "Right ADC", NULL, "SYSCLK1" },
+
+ { "DSP", NULL, "SYSCLK1" },
+
+ { "AIF1 Mixer", NULL, "DSP" },
+ { "AIF2 Mixer", NULL, "DSP" },
+ { "AIF3 Mixer", NULL, "DSP" },
+ { "DAC1 Mixer", NULL, "DSP" },
+ { "DAC2 Mixer", NULL, "DSP" },
+ { "DAC1 Mixer", NULL, "Playback Engine A" },
+ { "DAC2 Mixer", NULL, "Playback Engine B" },
+ { "Left ADC Mixer", NULL, "Recording Engine A" },
+ { "Right ADC Mixer", NULL, "Recording Engine A" },
+
+ { "AIF1 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+ { "AIF2 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+ { "AIF3 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+ { "AIF1 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+ { "AIF2 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+ { "AIF3 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+
+ { "AIF1 IN", NULL, "AIF1 CLK" },
+ { "AIF1 OUT", NULL, "AIF1 CLK" },
+ { "AIF2 IN", NULL, "AIF2 CLK" },
+ { "AIF2 OUT", NULL, "AIF2 CLK" },
+ { "AIF3 IN", NULL, "AIF3 CLK" },
+ { "AIF3 OUT", NULL, "AIF3 CLK" },
+ { "AIF1 IN", NULL, "AIF1 IN SRC", adau1373_check_src },
+ { "AIF1 OUT", NULL, "AIF1 OUT SRC", adau1373_check_src },
+ { "AIF2 IN", NULL, "AIF2 IN SRC", adau1373_check_src },
+ { "AIF2 OUT", NULL, "AIF2 OUT SRC", adau1373_check_src },
+ { "AIF3 IN", NULL, "AIF3 IN SRC", adau1373_check_src },
+ { "AIF3 OUT", NULL, "AIF3 OUT SRC", adau1373_check_src },
+
+ { "DMIC1", NULL, "DMIC1DAT" },
+ { "DMIC1", NULL, "SYSCLK1" },
+ { "DMIC1", NULL, "Recording Engine A" },
+ { "DMIC2", NULL, "DMIC2DAT" },
+ { "DMIC2", NULL, "SYSCLK1" },
+ { "DMIC2", NULL, "Recording Engine B" },
+};
+
+static int adau1373_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+ unsigned int div;
+ unsigned int freq;
+ unsigned int ctrl;
+
+ freq = adau1373_dai->sysclk;
+
+ if (freq % params_rate(params) != 0)
+ return -EINVAL;
+
+ switch (freq / params_rate(params)) {
+ case 1024: /* sysclk / 256 */
+ div = 0;
+ break;
+ case 1536: /* 2/3 sysclk / 256 */
+ div = 1;
+ break;
+ case 2048: /* 1/2 sysclk / 256 */
+ div = 2;
+ break;
+ case 3072: /* 1/3 sysclk / 256 */
+ div = 3;
+ break;
+ case 4096: /* 1/4 sysclk / 256 */
+ div = 4;
+ break;
+ case 6144: /* 1/6 sysclk / 256 */
+ div = 5;
+ break;
+ case 5632: /* 2/11 sysclk / 256 */
+ div = 6;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adau1373_dai->enable_src = (div != 0);
+
+ regmap_update_bits(adau1373->regmap, ADAU1373_BCLKDIV(dai->id),
+ ADAU1373_BCLKDIV_SR_MASK | ADAU1373_BCLKDIV_BCLK_MASK,
+ (div << 2) | ADAU1373_BCLKDIV_64);
+
+ switch (params_width(params)) {
+ case 16:
+ ctrl = ADAU1373_DAI_WLEN_16;
+ break;
+ case 20:
+ ctrl = ADAU1373_DAI_WLEN_20;
+ break;
+ case 24:
+ ctrl = ADAU1373_DAI_WLEN_24;
+ break;
+ case 32:
+ ctrl = ADAU1373_DAI_WLEN_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(adau1373->regmap, ADAU1373_DAI(dai->id),
+ ADAU1373_DAI_WLEN_MASK, ctrl);
+}
+
+static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ ctrl = ADAU1373_DAI_MASTER;
+ adau1373_dai->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ ctrl = 0;
+ adau1373_dai->master = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl |= ADAU1373_DAI_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl |= ADAU1373_DAI_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl |= ADAU1373_DAI_FORMAT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl |= ADAU1373_DAI_FORMAT_DSP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl |= ADAU1373_DAI_INVERT_BCLK;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ ctrl |= ADAU1373_DAI_INVERT_LRCLK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ctrl |= ADAU1373_DAI_INVERT_LRCLK | ADAU1373_DAI_INVERT_BCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau1373->regmap, ADAU1373_DAI(dai->id),
+ ~ADAU1373_DAI_WLEN_MASK, ctrl);
+
+ return 0;
+}
+
+static int adau1373_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+
+ switch (clk_id) {
+ case ADAU1373_CLK_SRC_PLL1:
+ case ADAU1373_CLK_SRC_PLL2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adau1373_dai->sysclk = freq;
+ adau1373_dai->clk_src = clk_id;
+
+ regmap_update_bits(adau1373->regmap, ADAU1373_BCLKDIV(dai->id),
+ ADAU1373_BCLKDIV_SOURCE, clk_id << 5);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops adau1373_dai_ops = {
+ .hw_params = adau1373_hw_params,
+ .set_sysclk = adau1373_set_dai_sysclk,
+ .set_fmt = adau1373_set_dai_fmt,
+};
+
+#define ADAU1373_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver adau1373_dai_driver[] = {
+ {
+ .id = 0,
+ .name = "adau1373-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ADAU1373_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ADAU1373_FORMATS,
+ },
+ .ops = &adau1373_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .id = 1,
+ .name = "adau1373-aif2",
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ADAU1373_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ADAU1373_FORMATS,
+ },
+ .ops = &adau1373_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .id = 2,
+ .name = "adau1373-aif3",
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ADAU1373_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ADAU1373_FORMATS,
+ },
+ .ops = &adau1373_dai_ops,
+ .symmetric_rates = 1,
+ },
+};
+
+static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ unsigned int dpll_div = 0;
+ unsigned int x, r, n, m, i, j, mode;
+
+ switch (pll_id) {
+ case ADAU1373_PLL1:
+ case ADAU1373_PLL2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (source) {
+ case ADAU1373_PLL_SRC_BCLK1:
+ case ADAU1373_PLL_SRC_BCLK2:
+ case ADAU1373_PLL_SRC_BCLK3:
+ case ADAU1373_PLL_SRC_LRCLK1:
+ case ADAU1373_PLL_SRC_LRCLK2:
+ case ADAU1373_PLL_SRC_LRCLK3:
+ case ADAU1373_PLL_SRC_MCLK1:
+ case ADAU1373_PLL_SRC_MCLK2:
+ case ADAU1373_PLL_SRC_GPIO1:
+ case ADAU1373_PLL_SRC_GPIO2:
+ case ADAU1373_PLL_SRC_GPIO3:
+ case ADAU1373_PLL_SRC_GPIO4:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (freq_in < 7813 || freq_in > 27000000)
+ return -EINVAL;
+
+ if (freq_out < 45158000 || freq_out > 49152000)
+ return -EINVAL;
+
+ /* APLL input needs to be >= 8Mhz, so in case freq_in is less we use the
+ * DPLL to get it there. DPLL_out = (DPLL_in / div) * 1024 */
+ while (freq_in < 8000000) {
+ freq_in *= 2;
+ dpll_div++;
+ }
+
+ 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)
+ return -EINVAL;
+
+ if (dpll_div) {
+ dpll_div = 11 - dpll_div;
+ regmap_update_bits(adau1373->regmap, ADAU1373_PLL_CTRL6(pll_id),
+ ADAU1373_PLL_CTRL6_DPLL_BYPASS, 0);
+ } else {
+ regmap_update_bits(adau1373->regmap, ADAU1373_PLL_CTRL6(pll_id),
+ ADAU1373_PLL_CTRL6_DPLL_BYPASS,
+ ADAU1373_PLL_CTRL6_DPLL_BYPASS);
+ }
+
+ regmap_write(adau1373->regmap, ADAU1373_DPLL_CTRL(pll_id),
+ (source << 4) | dpll_div);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), (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);
+
+ /* Set sysclk to pll_rate / 4 */
+ regmap_update_bits(adau1373->regmap, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09);
+
+ return 0;
+}
+
+static void adau1373_load_drc_settings(struct adau1373 *adau1373,
+ unsigned int nr, uint8_t *drc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ADAU1373_DRC_SIZE; ++i)
+ regmap_write(adau1373->regmap, ADAU1373_DRC(nr) + i, drc[i]);
+}
+
+static bool adau1373_valid_micbias(enum adau1373_micbias_voltage micbias)
+{
+ switch (micbias) {
+ case ADAU1373_MICBIAS_2_9V:
+ case ADAU1373_MICBIAS_2_2V:
+ case ADAU1373_MICBIAS_2_6V:
+ case ADAU1373_MICBIAS_1_8V:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static int adau1373_probe(struct snd_soc_codec *codec)
+{
+ struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct adau1373_platform_data *pdata = codec->dev->platform_data;
+ bool lineout_differential = false;
+ unsigned int val;
+ int i;
+
+ if (pdata) {
+ if (pdata->num_drc > ARRAY_SIZE(pdata->drc_setting))
+ return -EINVAL;
+
+ if (!adau1373_valid_micbias(pdata->micbias1) ||
+ !adau1373_valid_micbias(pdata->micbias2))
+ return -EINVAL;
+
+ for (i = 0; i < pdata->num_drc; ++i) {
+ adau1373_load_drc_settings(adau1373, i,
+ pdata->drc_setting[i]);
+ }
+
+ snd_soc_add_codec_controls(codec, adau1373_drc_controls,
+ pdata->num_drc);
+
+ val = 0;
+ for (i = 0; i < 4; ++i) {
+ if (pdata->input_differential[i])
+ val |= BIT(i);
+ }
+ regmap_write(adau1373->regmap, ADAU1373_INPUT_MODE, val);
+
+ val = 0;
+ if (pdata->lineout_differential)
+ val |= ADAU1373_OUTPUT_CTRL_LDIFF;
+ if (pdata->lineout_ground_sense)
+ val |= ADAU1373_OUTPUT_CTRL_LNFBEN;
+ regmap_write(adau1373->regmap, ADAU1373_OUTPUT_CTRL, val);
+
+ lineout_differential = pdata->lineout_differential;
+
+ regmap_write(adau1373->regmap, ADAU1373_EP_CTRL,
+ (pdata->micbias1 << ADAU1373_EP_CTRL_MICBIAS1_OFFSET) |
+ (pdata->micbias2 << ADAU1373_EP_CTRL_MICBIAS2_OFFSET));
+ }
+
+ if (!lineout_differential) {
+ snd_soc_add_codec_controls(codec, adau1373_lineout2_controls,
+ ARRAY_SIZE(adau1373_lineout2_controls));
+ }
+
+ regmap_write(adau1373->regmap, ADAU1373_ADC_CTRL,
+ ADAU1373_ADC_CTRL_RESET_FORCE | ADAU1373_ADC_CTRL_PEAK_DETECT);
+
+ return 0;
+}
+
+static int adau1373_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(adau1373->regmap, ADAU1373_PWDN_CTRL3,
+ ADAU1373_PWDN_CTRL3_PWR_EN, ADAU1373_PWDN_CTRL3_PWR_EN);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(adau1373->regmap, ADAU1373_PWDN_CTRL3,
+ ADAU1373_PWDN_CTRL3_PWR_EN, 0);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int adau1373_resume(struct snd_soc_codec *codec)
+{
+ struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_sync(adau1373->regmap);
+
+ return 0;
+}
+
+static bool adau1373_register_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1373_SOFT_RESET:
+ case ADAU1373_ADC_DAC_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config adau1373_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 8,
+
+ .volatile_reg = adau1373_register_volatile,
+ .max_register = ADAU1373_SOFT_RESET,
+
+ .cache_type = REGCACHE_RBTREE,
+ .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,
+
+ .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),
+};
+
+static int adau1373_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adau1373 *adau1373;
+ int ret;
+
+ adau1373 = devm_kzalloc(&client->dev, sizeof(*adau1373), GFP_KERNEL);
+ if (!adau1373)
+ return -ENOMEM;
+
+ adau1373->regmap = devm_regmap_init_i2c(client,
+ &adau1373_regmap_config);
+ if (IS_ERR(adau1373->regmap))
+ return PTR_ERR(adau1373->regmap);
+
+ regmap_write(adau1373->regmap, ADAU1373_SOFT_RESET, 0x00);
+
+ dev_set_drvdata(&client->dev, adau1373);
+
+ ret = snd_soc_register_codec(&client->dev, &adau1373_codec_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 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id);
+
+static struct i2c_driver adau1373_i2c_driver = {
+ .driver = {
+ .name = "adau1373",
+ .owner = THIS_MODULE,
+ },
+ .probe = adau1373_i2c_probe,
+ .remove = adau1373_i2c_remove,
+ .id_table = adau1373_i2c_id,
+};
+
+module_i2c_driver(adau1373_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1373 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adau1373.h b/kernel/sound/soc/codecs/adau1373.h
new file mode 100644
index 000000000..c6ab55307
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1373.h
@@ -0,0 +1,29 @@
+#ifndef __ADAU1373_H__
+#define __ADAU1373_H__
+
+enum adau1373_pll_src {
+ ADAU1373_PLL_SRC_MCLK1 = 0,
+ ADAU1373_PLL_SRC_BCLK1 = 1,
+ ADAU1373_PLL_SRC_BCLK2 = 2,
+ ADAU1373_PLL_SRC_BCLK3 = 3,
+ ADAU1373_PLL_SRC_LRCLK1 = 4,
+ ADAU1373_PLL_SRC_LRCLK2 = 5,
+ ADAU1373_PLL_SRC_LRCLK3 = 6,
+ ADAU1373_PLL_SRC_GPIO1 = 7,
+ ADAU1373_PLL_SRC_GPIO2 = 8,
+ ADAU1373_PLL_SRC_GPIO3 = 9,
+ ADAU1373_PLL_SRC_GPIO4 = 10,
+ ADAU1373_PLL_SRC_MCLK2 = 11,
+};
+
+enum adau1373_pll {
+ ADAU1373_PLL1 = 0,
+ ADAU1373_PLL2 = 1,
+};
+
+enum adau1373_clk_src {
+ ADAU1373_CLK_SRC_PLL1 = 0,
+ ADAU1373_CLK_SRC_PLL2 = 1,
+};
+
+#endif
diff --git a/kernel/sound/soc/codecs/adau1701.c b/kernel/sound/soc/codecs/adau1701.c
new file mode 100644
index 000000000..d4e219b6b
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1701.c
@@ -0,0 +1,837 @@
+/*
+ * 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>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.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 "sigmadsp.h"
+#include "adau1701.h"
+
+#define ADAU1701_SAFELOAD_DATA(i) (0x0810 + (i))
+#define ADAU1701_SAFELOAD_ADDR(i) (0x0815 + (i))
+
+#define ADAU1701_DSPCTRL 0x081c
+#define ADAU1701_SEROCTL 0x081e
+#define ADAU1701_SERICTL 0x081f
+
+#define ADAU1701_AUXNPOW 0x0822
+#define ADAU1701_PINCONF_0 0x0820
+#define ADAU1701_PINCONF_1 0x0821
+#define ADAU1701_AUXNPOW 0x0822
+
+#define ADAU1701_OSCIPOW 0x0826
+#define ADAU1701_DACSET 0x0827
+
+#define ADAU1701_MAX_REGISTER 0x0828
+
+#define ADAU1701_DSPCTRL_CR (1 << 2)
+#define ADAU1701_DSPCTRL_DAM (1 << 3)
+#define ADAU1701_DSPCTRL_ADM (1 << 4)
+#define ADAU1701_DSPCTRL_IST (1 << 5)
+#define ADAU1701_DSPCTRL_SR_48 0x00
+#define ADAU1701_DSPCTRL_SR_96 0x01
+#define ADAU1701_DSPCTRL_SR_192 0x02
+#define ADAU1701_DSPCTRL_SR_MASK 0x03
+
+#define ADAU1701_SEROCTL_INV_LRCLK 0x2000
+#define ADAU1701_SEROCTL_INV_BCLK 0x1000
+#define ADAU1701_SEROCTL_MASTER 0x0800
+
+#define ADAU1701_SEROCTL_OBF16 0x0000
+#define ADAU1701_SEROCTL_OBF8 0x0200
+#define ADAU1701_SEROCTL_OBF4 0x0400
+#define ADAU1701_SEROCTL_OBF2 0x0600
+#define ADAU1701_SEROCTL_OBF_MASK 0x0600
+
+#define ADAU1701_SEROCTL_OLF1024 0x0000
+#define ADAU1701_SEROCTL_OLF512 0x0080
+#define ADAU1701_SEROCTL_OLF256 0x0100
+#define ADAU1701_SEROCTL_OLF_MASK 0x0180
+
+#define ADAU1701_SEROCTL_MSB_DEALY1 0x0000
+#define ADAU1701_SEROCTL_MSB_DEALY0 0x0004
+#define ADAU1701_SEROCTL_MSB_DEALY8 0x0008
+#define ADAU1701_SEROCTL_MSB_DEALY12 0x000c
+#define ADAU1701_SEROCTL_MSB_DEALY16 0x0010
+#define ADAU1701_SEROCTL_MSB_DEALY_MASK 0x001c
+
+#define ADAU1701_SEROCTL_WORD_LEN_24 0x0000
+#define ADAU1701_SEROCTL_WORD_LEN_20 0x0001
+#define ADAU1701_SEROCTL_WORD_LEN_16 0x0002
+#define ADAU1701_SEROCTL_WORD_LEN_MASK 0x0003
+
+#define ADAU1701_AUXNPOW_VBPD 0x40
+#define ADAU1701_AUXNPOW_VRPD 0x20
+
+#define ADAU1701_SERICTL_I2S 0
+#define ADAU1701_SERICTL_LEFTJ 1
+#define ADAU1701_SERICTL_TDM 2
+#define ADAU1701_SERICTL_RIGHTJ_24 3
+#define ADAU1701_SERICTL_RIGHTJ_20 4
+#define ADAU1701_SERICTL_RIGHTJ_18 5
+#define ADAU1701_SERICTL_RIGHTJ_16 6
+#define ADAU1701_SERICTL_MODE_MASK 7
+#define ADAU1701_SERICTL_INV_BCLK BIT(3)
+#define ADAU1701_SERICTL_INV_LRCLK BIT(4)
+
+#define ADAU1701_OSCIPOW_OPD 0x04
+#define ADAU1701_DACSET_DACINIT 1
+
+#define ADAU1707_CLKDIV_UNSET (-1U)
+
+#define ADAU1701_FIRMWARE "adau1701.bin"
+
+struct adau1701 {
+ int gpio_nreset;
+ int gpio_pll_mode[2];
+ unsigned int dai_fmt;
+ unsigned int pll_clkdiv;
+ unsigned int sysclk;
+ struct regmap *regmap;
+ struct i2c_client *client;
+ u8 pin_config[12];
+
+ struct sigmadsp *sigmadsp;
+};
+
+static const struct snd_kcontrol_new adau1701_controls[] = {
+ SOC_SINGLE("Master Capture Switch", ADAU1701_DSPCTRL, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget adau1701_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC0", "Playback", ADAU1701_AUXNPOW, 3, 1),
+ SND_SOC_DAPM_DAC("DAC1", "Playback", ADAU1701_AUXNPOW, 2, 1),
+ SND_SOC_DAPM_DAC("DAC2", "Playback", ADAU1701_AUXNPOW, 1, 1),
+ SND_SOC_DAPM_DAC("DAC3", "Playback", ADAU1701_AUXNPOW, 0, 1),
+ SND_SOC_DAPM_ADC("ADC", "Capture", ADAU1701_AUXNPOW, 7, 1),
+
+ SND_SOC_DAPM_OUTPUT("OUT0"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_INPUT("IN0"),
+ SND_SOC_DAPM_INPUT("IN1"),
+};
+
+static const struct snd_soc_dapm_route adau1701_dapm_routes[] = {
+ { "OUT0", NULL, "DAC0" },
+ { "OUT1", NULL, "DAC1" },
+ { "OUT2", NULL, "DAC2" },
+ { "OUT3", NULL, "DAC3" },
+
+ { "ADC", NULL, "IN0" },
+ { "ADC", NULL, "IN1" },
+};
+
+static unsigned int adau1701_register_size(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1701_PINCONF_0:
+ case ADAU1701_PINCONF_1:
+ return 3;
+ case ADAU1701_DSPCTRL:
+ case ADAU1701_SEROCTL:
+ case ADAU1701_AUXNPOW:
+ case ADAU1701_OSCIPOW:
+ case ADAU1701_DACSET:
+ return 2;
+ case ADAU1701_SERICTL:
+ return 1;
+ }
+
+ dev_err(dev, "Unsupported register address: %d\n", reg);
+ return 0;
+}
+
+static bool adau1701_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1701_DACSET:
+ case ADAU1701_DSPCTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int adau1701_reg_write(void *context, unsigned int reg,
+ unsigned int value)
+{
+ struct i2c_client *client = context;
+ unsigned int i;
+ unsigned int size;
+ uint8_t buf[5];
+ int ret;
+
+ size = adau1701_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ for (i = size + 1; i >= 2; --i) {
+ buf[i] = value;
+ value >>= 8;
+ }
+
+ ret = i2c_master_send(client, buf, size + 2);
+ if (ret == size + 2)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int adau1701_reg_read(void *context, unsigned int reg,
+ unsigned int *value)
+{
+ int ret;
+ unsigned int i;
+ unsigned int size;
+ uint8_t send_buf[2], recv_buf[3];
+ struct i2c_client *client = context;
+ struct i2c_msg msgs[2];
+
+ size = adau1701_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
+
+ send_buf[0] = reg >> 8;
+ send_buf[1] = reg & 0xff;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = size;
+ msgs[1].buf = recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+ else if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *value = 0;
+
+ for (i = 0; i < size; i++) {
+ *value <<= 8;
+ *value |= recv_buf[i];
+ }
+
+ return 0;
+}
+
+static int adau1701_safeload(struct sigmadsp *sigmadsp, unsigned int addr,
+ const uint8_t bytes[], size_t len)
+{
+ struct i2c_client *client = to_i2c_client(sigmadsp->dev);
+ struct adau1701 *adau1701 = i2c_get_clientdata(client);
+ unsigned int val;
+ unsigned int i;
+ uint8_t buf[10];
+ int ret;
+
+ ret = regmap_read(adau1701->regmap, ADAU1701_DSPCTRL, &val);
+ if (ret)
+ return ret;
+
+ if (val & ADAU1701_DSPCTRL_IST)
+ msleep(50);
+
+ for (i = 0; i < len / 4; i++) {
+ put_unaligned_le16(ADAU1701_SAFELOAD_DATA(i), buf);
+ buf[2] = 0x00;
+ memcpy(buf + 3, bytes + i * 4, 4);
+ ret = i2c_master_send(client, buf, 7);
+ if (ret < 0)
+ return ret;
+ else if (ret != 7)
+ return -EIO;
+
+ put_unaligned_le16(ADAU1701_SAFELOAD_ADDR(i), buf);
+ put_unaligned_le16(addr + i, buf + 2);
+ ret = i2c_master_send(client, buf, 4);
+ if (ret < 0)
+ return ret;
+ else if (ret != 4)
+ return -EIO;
+ }
+
+ return regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
+ ADAU1701_DSPCTRL_IST, ADAU1701_DSPCTRL_IST);
+}
+
+static const struct sigmadsp_ops adau1701_sigmadsp_ops = {
+ .safeload = adau1701_safeload,
+};
+
+static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv,
+ unsigned int rate)
+{
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ 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])) {
+ switch (clkdiv) {
+ case 64:
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
+ break;
+ case 256:
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
+ break;
+ case 384:
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
+ break;
+ 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);
+ break;
+ }
+ }
+
+ adau1701->pll_clkdiv = clkdiv;
+
+ if (gpio_is_valid(adau1701->gpio_nreset)) {
+ gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
+ /* minimum reset time is 20ns */
+ udelay(1);
+ gpio_set_value_cansleep(adau1701->gpio_nreset, 1);
+ /* power-up time may be as long as 85ms */
+ mdelay(85);
+ }
+
+ /*
+ * Postpone the firmware download to a point in time when we
+ * know the correct PLL setup
+ */
+ if (clkdiv != ADAU1707_CLKDIV_UNSET) {
+ ret = sigmadsp_setup(adau1701->sigmadsp, rate);
+ if (ret) {
+ dev_warn(codec->dev, "Failed to load firmware\n");
+ return ret;
+ }
+ }
+
+ regmap_write(adau1701->regmap, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
+ regmap_write(adau1701->regmap, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
+
+ regcache_mark_dirty(adau1701->regmap);
+ regcache_sync(adau1701->regmap);
+
+ return 0;
+}
+
+static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
+ struct snd_pcm_hw_params *params)
+{
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK;
+ unsigned int val;
+
+ switch (params_width(params)) {
+ case 16:
+ val = ADAU1701_SEROCTL_WORD_LEN_16;
+ break;
+ case 20:
+ val = ADAU1701_SEROCTL_WORD_LEN_20;
+ break;
+ case 24:
+ val = ADAU1701_SEROCTL_WORD_LEN_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (adau1701->dai_fmt == SND_SOC_DAIFMT_RIGHT_J) {
+ switch (params_width(params)) {
+ case 16:
+ val |= ADAU1701_SEROCTL_MSB_DEALY16;
+ break;
+ case 20:
+ val |= ADAU1701_SEROCTL_MSB_DEALY12;
+ break;
+ case 24:
+ val |= ADAU1701_SEROCTL_MSB_DEALY8;
+ break;
+ }
+ mask |= ADAU1701_SEROCTL_MSB_DEALY_MASK;
+ }
+
+ regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL, mask, val);
+
+ return 0;
+}
+
+static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
+ struct snd_pcm_hw_params *params)
+{
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
+ return 0;
+
+ switch (params_width(params)) {
+ case 16:
+ val = ADAU1701_SERICTL_RIGHTJ_16;
+ break;
+ case 20:
+ val = ADAU1701_SERICTL_RIGHTJ_20;
+ break;
+ case 24:
+ val = ADAU1701_SERICTL_RIGHTJ_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau1701->regmap, ADAU1701_SERICTL,
+ ADAU1701_SERICTL_MODE_MASK, val);
+
+ return 0;
+}
+
+static int adau1701_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ unsigned int clkdiv = adau1701->sysclk / params_rate(params);
+ unsigned int val;
+ int ret;
+
+ /*
+ * If the mclk/lrclk ratio changes, the chip needs updated PLL
+ * mode GPIO settings, and a full reset cycle, including a new
+ * firmware upload.
+ */
+ if (clkdiv != adau1701->pll_clkdiv) {
+ ret = adau1701_reset(codec, clkdiv, params_rate(params));
+ if (ret < 0)
+ return ret;
+ }
+
+ switch (params_rate(params)) {
+ case 192000:
+ val = ADAU1701_DSPCTRL_SR_192;
+ break;
+ case 96000:
+ val = ADAU1701_DSPCTRL_SR_96;
+ break;
+ case 48000:
+ val = ADAU1701_DSPCTRL_SR_48;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
+ ADAU1701_DSPCTRL_SR_MASK, val);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return adau1701_set_playback_pcm_format(codec, params);
+ else
+ return adau1701_set_capture_pcm_format(codec, 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);
+ unsigned int serictl = 0x00, seroctl = 0x00;
+ bool invert_lrclk;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* 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:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ invert_lrclk = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert_lrclk = true;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert_lrclk = false;
+ serictl |= ADAU1701_SERICTL_INV_BCLK;
+ seroctl |= ADAU1701_SEROCTL_INV_BCLK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ invert_lrclk = true;
+ serictl |= ADAU1701_SERICTL_INV_BCLK;
+ seroctl |= ADAU1701_SEROCTL_INV_BCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ serictl |= ADAU1701_SERICTL_LEFTJ;
+ seroctl |= ADAU1701_SEROCTL_MSB_DEALY0;
+ invert_lrclk = !invert_lrclk;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ serictl |= ADAU1701_SERICTL_RIGHTJ_24;
+ seroctl |= ADAU1701_SEROCTL_MSB_DEALY8;
+ invert_lrclk = !invert_lrclk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (invert_lrclk) {
+ seroctl |= ADAU1701_SEROCTL_INV_LRCLK;
+ serictl |= ADAU1701_SERICTL_INV_LRCLK;
+ }
+
+ adau1701->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ regmap_write(adau1701->regmap, ADAU1701_SERICTL, serictl);
+ regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL,
+ ~ADAU1701_SEROCTL_WORD_LEN_MASK, seroctl);
+
+ return 0;
+}
+
+static int adau1701_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* Enable VREF and VREF buffer */
+ regmap_update_bits(adau1701->regmap,
+ ADAU1701_AUXNPOW, mask, 0x00);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Disable VREF and VREF buffer */
+ regmap_update_bits(adau1701->regmap,
+ ADAU1701_AUXNPOW, mask, mask);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int mask = ADAU1701_DSPCTRL_DAM;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ if (mute)
+ val = 0;
+ else
+ val = mask;
+
+ regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, mask, val);
+
+ return 0;
+}
+
+static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ unsigned int val;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case ADAU1701_CLK_SRC_OSC:
+ val = 0x0;
+ break;
+ case ADAU1701_CLK_SRC_MCLK:
+ val = ADAU1701_OSCIPOW_OPD;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau1701->regmap, ADAU1701_OSCIPOW,
+ ADAU1701_OSCIPOW_OPD, val);
+ adau1701->sysclk = freq;
+
+ return 0;
+}
+
+static int adau1701_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(dai->codec);
+
+ return sigmadsp_restrict_params(adau1701->sigmadsp, substream);
+}
+
+#define ADAU1701_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000)
+
+#define ADAU1701_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops adau1701_dai_ops = {
+ .set_fmt = adau1701_set_dai_fmt,
+ .hw_params = adau1701_hw_params,
+ .digital_mute = adau1701_digital_mute,
+ .startup = adau1701_startup,
+};
+
+static struct snd_soc_dai_driver adau1701_dai = {
+ .name = "adau1701",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = ADAU1701_RATES,
+ .formats = ADAU1701_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = ADAU1701_RATES,
+ .formats = ADAU1701_FORMATS,
+ },
+ .ops = &adau1701_dai_ops,
+ .symmetric_rates = 1,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id adau1701_dt_ids[] = {
+ { .compatible = "adi,adau1701", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adau1701_dt_ids);
+#endif
+
+static int adau1701_probe(struct snd_soc_codec *codec)
+{
+ int i, ret;
+ unsigned int val;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+
+ ret = sigmadsp_attach(adau1701->sigmadsp, &codec->component);
+ if (ret)
+ return ret;
+
+ /*
+ * Let the pll_clkdiv variable default to something that won't happen
+ * at runtime. That way, we can postpone the firmware download from
+ * adau1701_reset() to a point in time when we know the correct PLL
+ * mode parameters.
+ */
+ adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
+
+ /* initalize with pre-configured pll mode settings */
+ ret = adau1701_reset(codec, adau1701->pll_clkdiv, 0);
+ if (ret < 0)
+ return ret;
+
+ /* set up pin config */
+ val = 0;
+ for (i = 0; i < 6; i++)
+ val |= adau1701->pin_config[i] << (i * 4);
+
+ regmap_write(adau1701->regmap, ADAU1701_PINCONF_0, val);
+
+ val = 0;
+ for (i = 0; i < 6; i++)
+ val |= adau1701->pin_config[i + 6] << (i * 4);
+
+ regmap_write(adau1701->regmap, ADAU1701_PINCONF_1, val);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver adau1701_codec_drv = {
+ .probe = adau1701_probe,
+ .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,
+};
+
+static const struct regmap_config adau1701_regmap = {
+ .reg_bits = 16,
+ .val_bits = 32,
+ .max_register = ADAU1701_MAX_REGISTER,
+ .cache_type = REGCACHE_RBTREE,
+ .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)
+{
+ struct adau1701 *adau1701;
+ struct device *dev = &client->dev;
+ int gpio_nreset = -EINVAL;
+ int gpio_pll_mode[2] = { -EINVAL, -EINVAL };
+ int ret;
+
+ adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
+ if (!adau1701)
+ return -ENOMEM;
+
+ 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];
+
+ 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];
+
+ of_property_read_u32(dev->of_node, "adi,pll-clkdiv",
+ &adau1701->pll_clkdiv);
+
+ of_property_read_u8_array(dev->of_node, "adi,pin-config",
+ adau1701->pin_config,
+ ARRAY_SIZE(adau1701->pin_config));
+ }
+
+ 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;
+ }
+
+ 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;
+
+ ret = devm_gpio_request_one(dev, gpio_pll_mode[1],
+ GPIOF_OUT_INIT_LOW,
+ "ADAU1701 PLL mode 1");
+ if (ret < 0)
+ return ret;
+ }
+
+ 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);
+
+ ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
+ &adau1701_dai, 1);
+ return ret;
+}
+
+static int adau1701_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id adau1701_i2c_id[] = {
+ { "adau1401", 0 },
+ { "adau1401a", 0 },
+ { "adau1701", 0 },
+ { "adau1702", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
+
+static struct i2c_driver adau1701_i2c_driver = {
+ .driver = {
+ .name = "adau1701",
+ .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,
+};
+
+module_i2c_driver(adau1701_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1701 SigmaDSP driver");
+MODULE_AUTHOR("Cliff Cai <cliff.cai@analog.com>");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adau1701.h b/kernel/sound/soc/codecs/adau1701.h
new file mode 100644
index 000000000..8d0949a2a
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1701.h
@@ -0,0 +1,17 @@
+/*
+ * header file for ADAU1701 SigmaDSP processor
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _ADAU1701_H
+#define _ADAU1701_H
+
+enum adau1701_clk_src {
+ ADAU1701_CLK_SRC_OSC,
+ ADAU1701_CLK_SRC_MCLK,
+};
+
+#endif
diff --git a/kernel/sound/soc/codecs/adau1761-i2c.c b/kernel/sound/soc/codecs/adau1761-i2c.c
new file mode 100644
index 000000000..862796dec
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1761-i2c.c
@@ -0,0 +1,60 @@
+/*
+ * Driver for ADAU1761/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>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "adau1761.h"
+
+static int adau1761_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regmap_config config;
+
+ config = adau1761_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 16;
+
+ return adau1761_probe(&client->dev,
+ devm_regmap_init_i2c(client, &config),
+ id->driver_data, NULL);
+}
+
+static int adau1761_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id adau1761_i2c_ids[] = {
+ { "adau1361", ADAU1361 },
+ { "adau1461", ADAU1761 },
+ { "adau1761", ADAU1761 },
+ { "adau1961", ADAU1361 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids);
+
+static struct i2c_driver adau1761_i2c_driver = {
+ .driver = {
+ .name = "adau1761",
+ .owner = THIS_MODULE,
+ },
+ .probe = adau1761_i2c_probe,
+ .remove = adau1761_i2c_remove,
+ .id_table = adau1761_i2c_ids,
+};
+module_i2c_driver(adau1761_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC I2C driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adau1761-spi.c b/kernel/sound/soc/codecs/adau1761-spi.c
new file mode 100644
index 000000000..cce2f11f1
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1761-spi.c
@@ -0,0 +1,77 @@
+/*
+ * Driver for ADAU1761/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>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "adau1761.h"
+
+static void adau1761_spi_switch_mode(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ /*
+ * To get the device into SPI mode CLATCH has to be pulled low three
+ * times. Do this by issuing three dummy reads.
+ */
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+}
+
+static int adau1761_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct regmap_config config;
+
+ if (!id)
+ return -EINVAL;
+
+ config = adau1761_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 24;
+ config.read_flag_mask = 0x1;
+
+ return adau1761_probe(&spi->dev,
+ devm_regmap_init_spi(spi, &config),
+ id->driver_data, adau1761_spi_switch_mode);
+}
+
+static int adau1761_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static const struct spi_device_id adau1761_spi_id[] = {
+ { "adau1361", ADAU1361 },
+ { "adau1461", ADAU1761 },
+ { "adau1761", ADAU1761 },
+ { "adau1961", ADAU1361 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adau1761_spi_id);
+
+static struct spi_driver adau1761_spi_driver = {
+ .driver = {
+ .name = "adau1761",
+ .owner = THIS_MODULE,
+ },
+ .probe = adau1761_spi_probe,
+ .remove = adau1761_spi_remove,
+ .id_table = adau1761_spi_id,
+};
+module_spi_driver(adau1761_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC SPI driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adau1761.c b/kernel/sound/soc/codecs/adau1761.c
new file mode 100644
index 000000000..a1baeee16
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1761.c
@@ -0,0 +1,808 @@
+/*
+ * Driver for ADAU1761/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>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/platform_data/adau17x1.h>
+
+#include "adau17x1.h"
+#include "adau1761.h"
+
+#define ADAU1761_DIGMIC_JACKDETECT 0x4008
+#define ADAU1761_REC_MIXER_LEFT0 0x400a
+#define ADAU1761_REC_MIXER_LEFT1 0x400b
+#define ADAU1761_REC_MIXER_RIGHT0 0x400c
+#define ADAU1761_REC_MIXER_RIGHT1 0x400d
+#define ADAU1761_LEFT_DIFF_INPUT_VOL 0x400e
+#define ADAU1761_RIGHT_DIFF_INPUT_VOL 0x400f
+#define ADAU1761_PLAY_LR_MIXER_LEFT 0x4020
+#define ADAU1761_PLAY_MIXER_LEFT0 0x401c
+#define ADAU1761_PLAY_MIXER_LEFT1 0x401d
+#define ADAU1761_PLAY_MIXER_RIGHT0 0x401e
+#define ADAU1761_PLAY_MIXER_RIGHT1 0x401f
+#define ADAU1761_PLAY_LR_MIXER_RIGHT 0x4021
+#define ADAU1761_PLAY_MIXER_MONO 0x4022
+#define ADAU1761_PLAY_HP_LEFT_VOL 0x4023
+#define ADAU1761_PLAY_HP_RIGHT_VOL 0x4024
+#define ADAU1761_PLAY_LINE_LEFT_VOL 0x4025
+#define ADAU1761_PLAY_LINE_RIGHT_VOL 0x4026
+#define ADAU1761_PLAY_MONO_OUTPUT_VOL 0x4027
+#define ADAU1761_POP_CLICK_SUPPRESS 0x4028
+#define ADAU1761_JACK_DETECT_PIN 0x4031
+#define ADAU1761_DEJITTER 0x4036
+#define ADAU1761_CLK_ENABLE0 0x40f9
+#define ADAU1761_CLK_ENABLE1 0x40fa
+
+#define ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW BIT(0)
+#define ADAU1761_DIGMIC_JACKDETECT_DIGMIC BIT(5)
+
+#define ADAU1761_DIFF_INPUT_VOL_LDEN BIT(0)
+
+#define ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP BIT(0)
+#define ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE BIT(1)
+
+#define ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP BIT(0)
+
+#define ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP BIT(0)
+
+#define ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP BIT(0)
+
+
+#define ADAU1761_FIRMWARE "adau1761.bin"
+
+static const struct reg_default adau1761_reg_defaults[] = {
+ { ADAU1761_DEJITTER, 0x03 },
+ { ADAU1761_DIGMIC_JACKDETECT, 0x00 },
+ { ADAU1761_REC_MIXER_LEFT0, 0x00 },
+ { ADAU1761_REC_MIXER_LEFT1, 0x00 },
+ { ADAU1761_REC_MIXER_RIGHT0, 0x00 },
+ { ADAU1761_REC_MIXER_RIGHT1, 0x00 },
+ { ADAU1761_LEFT_DIFF_INPUT_VOL, 0x00 },
+ { ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 },
+ { ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 },
+ { ADAU1761_PLAY_MIXER_LEFT0, 0x00 },
+ { ADAU1761_PLAY_MIXER_LEFT1, 0x00 },
+ { ADAU1761_PLAY_MIXER_RIGHT0, 0x00 },
+ { ADAU1761_PLAY_MIXER_RIGHT1, 0x00 },
+ { ADAU1761_PLAY_LR_MIXER_RIGHT, 0x00 },
+ { ADAU1761_PLAY_MIXER_MONO, 0x00 },
+ { ADAU1761_PLAY_HP_LEFT_VOL, 0x00 },
+ { ADAU1761_PLAY_HP_RIGHT_VOL, 0x00 },
+ { ADAU1761_PLAY_LINE_LEFT_VOL, 0x00 },
+ { ADAU1761_PLAY_LINE_RIGHT_VOL, 0x00 },
+ { ADAU1761_PLAY_MONO_OUTPUT_VOL, 0x00 },
+ { ADAU1761_POP_CLICK_SUPPRESS, 0x00 },
+ { ADAU1761_JACK_DETECT_PIN, 0x00 },
+ { ADAU1761_CLK_ENABLE0, 0x00 },
+ { ADAU1761_CLK_ENABLE1, 0x00 },
+ { ADAU17X1_CLOCK_CONTROL, 0x00 },
+ { ADAU17X1_PLL_CONTROL, 0x00 },
+ { ADAU17X1_REC_POWER_MGMT, 0x00 },
+ { ADAU17X1_MICBIAS, 0x00 },
+ { ADAU17X1_SERIAL_PORT0, 0x00 },
+ { ADAU17X1_SERIAL_PORT1, 0x00 },
+ { ADAU17X1_CONVERTER0, 0x00 },
+ { ADAU17X1_CONVERTER1, 0x00 },
+ { ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 },
+ { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 },
+ { ADAU17X1_ADC_CONTROL, 0x00 },
+ { ADAU17X1_PLAY_POWER_MGMT, 0x00 },
+ { ADAU17X1_DAC_CONTROL0, 0x00 },
+ { ADAU17X1_DAC_CONTROL1, 0x00 },
+ { ADAU17X1_DAC_CONTROL2, 0x00 },
+ { ADAU17X1_SERIAL_PORT_PAD, 0xaa },
+ { ADAU17X1_CONTROL_PORT_PAD0, 0xaa },
+ { ADAU17X1_CONTROL_PORT_PAD1, 0x00 },
+ { ADAU17X1_DSP_SAMPLING_RATE, 0x01 },
+ { ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 },
+ { ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 },
+ { ADAU17X1_DSP_ENABLE, 0x00 },
+ { ADAU17X1_DSP_RUN, 0x00 },
+ { ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 },
+};
+
+static const DECLARE_TLV_DB_SCALE(adau1761_sing_in_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_diff_in_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_out_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_sidetone_tlv, -1800, 300, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_boost_tlv, -600, 600, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_pga_boost_tlv, -2000, 2000, 1);
+
+static const unsigned int adau1761_bias_select_values[] = {
+ 0, 2, 3,
+};
+
+static const char * const adau1761_bias_select_text[] = {
+ "Normal operation", "Enhanced performance", "Power saving",
+};
+
+static const char * const adau1761_bias_select_extreme_text[] = {
+ "Normal operation", "Extreme power saving", "Enhanced performance",
+ "Power saving",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1761_adc_bias_enum,
+ ADAU17X1_REC_POWER_MGMT, 3, adau1761_bias_select_extreme_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_hp_bias_enum,
+ ADAU17X1_PLAY_POWER_MGMT, 6, adau1761_bias_select_extreme_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_dac_bias_enum,
+ ADAU17X1_PLAY_POWER_MGMT, 4, adau1761_bias_select_extreme_text);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_playback_bias_enum,
+ ADAU17X1_PLAY_POWER_MGMT, 2, 0x3, adau1761_bias_select_text,
+ adau1761_bias_select_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_capture_bias_enum,
+ ADAU17X1_REC_POWER_MGMT, 1, 0x3, adau1761_bias_select_text,
+ adau1761_bias_select_values);
+
+static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = {
+ SOC_SINGLE("Speaker Auto-mute Switch", ADAU1761_DIGMIC_JACKDETECT,
+ 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1761_differential_mode_controls[] = {
+ SOC_DOUBLE_R_TLV("Capture Volume", ADAU1761_LEFT_DIFF_INPUT_VOL,
+ ADAU1761_RIGHT_DIFF_INPUT_VOL, 2, 0x3f, 0,
+ adau1761_diff_in_tlv),
+ SOC_DOUBLE_R("Capture Switch", ADAU1761_LEFT_DIFF_INPUT_VOL,
+ ADAU1761_RIGHT_DIFF_INPUT_VOL, 1, 1, 0),
+
+ SOC_DOUBLE_R_TLV("PGA Boost Capture Volume", ADAU1761_REC_MIXER_LEFT1,
+ ADAU1761_REC_MIXER_RIGHT1, 3, 2, 0, adau1761_pga_boost_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_single_mode_controls[] = {
+ SOC_SINGLE_TLV("Input 1 Capture Volume", ADAU1761_REC_MIXER_LEFT0,
+ 4, 7, 0, adau1761_sing_in_tlv),
+ SOC_SINGLE_TLV("Input 2 Capture Volume", ADAU1761_REC_MIXER_LEFT0,
+ 1, 7, 0, adau1761_sing_in_tlv),
+ SOC_SINGLE_TLV("Input 3 Capture Volume", ADAU1761_REC_MIXER_RIGHT0,
+ 4, 7, 0, adau1761_sing_in_tlv),
+ SOC_SINGLE_TLV("Input 4 Capture Volume", ADAU1761_REC_MIXER_RIGHT0,
+ 1, 7, 0, adau1761_sing_in_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_controls[] = {
+ SOC_DOUBLE_R_TLV("Aux Capture Volume", ADAU1761_REC_MIXER_LEFT1,
+ ADAU1761_REC_MIXER_RIGHT1, 0, 7, 0, adau1761_sing_in_tlv),
+
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1761_PLAY_HP_LEFT_VOL,
+ ADAU1761_PLAY_HP_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv),
+ SOC_DOUBLE_R("Headphone Playback Switch", ADAU1761_PLAY_HP_LEFT_VOL,
+ ADAU1761_PLAY_HP_RIGHT_VOL, 1, 1, 0),
+ SOC_DOUBLE_R_TLV("Lineout Playback Volume", ADAU1761_PLAY_LINE_LEFT_VOL,
+ ADAU1761_PLAY_LINE_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv),
+ SOC_DOUBLE_R("Lineout Playback Switch", ADAU1761_PLAY_LINE_LEFT_VOL,
+ ADAU1761_PLAY_LINE_RIGHT_VOL, 1, 1, 0),
+
+ SOC_ENUM("ADC Bias", adau1761_adc_bias_enum),
+ SOC_ENUM("DAC Bias", adau1761_dac_bias_enum),
+ SOC_ENUM("Capture Bias", adau1761_capture_bias_enum),
+ SOC_ENUM("Playback Bias", adau1761_playback_bias_enum),
+ SOC_ENUM("Headphone Bias", adau1761_hp_bias_enum),
+};
+
+static const struct snd_kcontrol_new adau1761_mono_controls[] = {
+ SOC_SINGLE_TLV("Mono Playback Volume", ADAU1761_PLAY_MONO_OUTPUT_VOL,
+ 2, 0x3f, 0, adau1761_out_tlv),
+ SOC_SINGLE("Mono Playback Switch", ADAU1761_PLAY_MONO_OUTPUT_VOL,
+ 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1761_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch",
+ ADAU1761_PLAY_MIXER_LEFT0, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch",
+ ADAU1761_PLAY_MIXER_LEFT0, 6, 1, 0),
+ SOC_DAPM_SINGLE_TLV("Aux Bypass Volume",
+ ADAU1761_PLAY_MIXER_LEFT0, 1, 8, 0, adau1761_sidetone_tlv),
+ SOC_DAPM_SINGLE_TLV("Right Bypass Volume",
+ ADAU1761_PLAY_MIXER_LEFT1, 4, 8, 0, adau1761_sidetone_tlv),
+ SOC_DAPM_SINGLE_TLV("Left Bypass Volume",
+ ADAU1761_PLAY_MIXER_LEFT1, 0, 8, 0, adau1761_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch",
+ ADAU1761_PLAY_MIXER_RIGHT0, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch",
+ ADAU1761_PLAY_MIXER_RIGHT0, 6, 1, 0),
+ SOC_DAPM_SINGLE_TLV("Aux Bypass Volume",
+ ADAU1761_PLAY_MIXER_RIGHT0, 1, 8, 0, adau1761_sidetone_tlv),
+ SOC_DAPM_SINGLE_TLV("Right Bypass Volume",
+ ADAU1761_PLAY_MIXER_RIGHT1, 4, 8, 0, adau1761_sidetone_tlv),
+ SOC_DAPM_SINGLE_TLV("Left Bypass Volume",
+ ADAU1761_PLAY_MIXER_RIGHT1, 0, 8, 0, adau1761_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_left_lr_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Left Volume",
+ ADAU1761_PLAY_LR_MIXER_LEFT, 1, 2, 0, adau1761_boost_tlv),
+ SOC_DAPM_SINGLE_TLV("Right Volume",
+ ADAU1761_PLAY_LR_MIXER_LEFT, 3, 2, 0, adau1761_boost_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_right_lr_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Left Volume",
+ ADAU1761_PLAY_LR_MIXER_RIGHT, 1, 2, 0, adau1761_boost_tlv),
+ SOC_DAPM_SINGLE_TLV("Right Volume",
+ ADAU1761_PLAY_LR_MIXER_RIGHT, 3, 2, 0, adau1761_boost_tlv),
+};
+
+static const char * const adau1761_input_mux_text[] = {
+ "ADC", "DMIC",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1761_input_mux_enum,
+ ADAU17X1_ADC_CONTROL, 2, adau1761_input_mux_text);
+
+static const struct snd_kcontrol_new adau1761_input_mux_control =
+ SOC_DAPM_ENUM("Input Select", adau1761_input_mux_enum);
+
+static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+ /* After any power changes have been made the dejitter circuit
+ * has to be reinitialized. */
+ regmap_write(adau->regmap, ADAU1761_DEJITTER, 0);
+ if (!adau->master)
+ regmap_write(adau->regmap, ADAU1761_DEJITTER, 3);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget adau1x61_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Left Input Mixer", ADAU1761_REC_MIXER_LEFT0, 0, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("Right Input Mixer", ADAU1761_REC_MIXER_RIGHT0, 0, 0,
+ NULL, 0),
+
+ SOC_MIXER_ARRAY("Left Playback Mixer", ADAU1761_PLAY_MIXER_LEFT0,
+ 0, 0, adau1761_left_mixer_controls),
+ SOC_MIXER_ARRAY("Right Playback Mixer", ADAU1761_PLAY_MIXER_RIGHT0,
+ 0, 0, adau1761_right_mixer_controls),
+ SOC_MIXER_ARRAY("Left LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_LEFT,
+ 0, 0, adau1761_left_lr_mixer_controls),
+ SOC_MIXER_ARRAY("Right LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_RIGHT,
+ 0, 0, adau1761_right_lr_mixer_controls),
+
+ SND_SOC_DAPM_SUPPLY("Headphone", ADAU1761_PLAY_HP_LEFT_VOL,
+ 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("SYSCLK", 2, SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_POST("Dejitter fixup", adau1761_dejitter_fixup),
+
+ SND_SOC_DAPM_INPUT("LAUX"),
+ SND_SOC_DAPM_INPUT("RAUX"),
+ SND_SOC_DAPM_INPUT("LINP"),
+ SND_SOC_DAPM_INPUT("LINN"),
+ SND_SOC_DAPM_INPUT("RINP"),
+ SND_SOC_DAPM_INPUT("RINN"),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("LHP"),
+ SND_SOC_DAPM_OUTPUT("RHP"),
+};
+
+static const struct snd_soc_dapm_widget adau1761_mono_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Mono Playback Mixer", ADAU1761_PLAY_MIXER_MONO,
+ 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("MONOOUT"),
+};
+
+static const struct snd_soc_dapm_widget adau1761_capless_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY_S("Headphone VGND", 1, ADAU1761_PLAY_MIXER_MONO,
+ 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route adau1x61_dapm_routes[] = {
+ { "Left Input Mixer", NULL, "LINP" },
+ { "Left Input Mixer", NULL, "LINN" },
+ { "Left Input Mixer", NULL, "LAUX" },
+
+ { "Right Input Mixer", NULL, "RINP" },
+ { "Right Input Mixer", NULL, "RINN" },
+ { "Right Input Mixer", NULL, "RAUX" },
+
+ { "Left Playback Mixer", NULL, "Left Playback Enable"},
+ { "Right Playback Mixer", NULL, "Right Playback Enable"},
+ { "Left LR Playback Mixer", NULL, "Left Playback Enable"},
+ { "Right LR Playback Mixer", NULL, "Right Playback Enable"},
+
+ { "Left Playback Mixer", "Left DAC Switch", "Left DAC" },
+ { "Left Playback Mixer", "Right DAC Switch", "Right DAC" },
+
+ { "Right Playback Mixer", "Left DAC Switch", "Left DAC" },
+ { "Right Playback Mixer", "Right DAC Switch", "Right DAC" },
+
+ { "Left LR Playback Mixer", "Left Volume", "Left Playback Mixer" },
+ { "Left LR Playback Mixer", "Right Volume", "Right Playback Mixer" },
+
+ { "Right LR Playback Mixer", "Left Volume", "Left Playback Mixer" },
+ { "Right LR Playback Mixer", "Right Volume", "Right Playback Mixer" },
+
+ { "LHP", NULL, "Left Playback Mixer" },
+ { "RHP", NULL, "Right Playback Mixer" },
+
+ { "LHP", NULL, "Headphone" },
+ { "RHP", NULL, "Headphone" },
+
+ { "LOUT", NULL, "Left LR Playback Mixer" },
+ { "ROUT", NULL, "Right LR Playback Mixer" },
+
+ { "Left Playback Mixer", "Aux Bypass Volume", "LAUX" },
+ { "Left Playback Mixer", "Left Bypass Volume", "Left Input Mixer" },
+ { "Left Playback Mixer", "Right Bypass Volume", "Right Input Mixer" },
+ { "Right Playback Mixer", "Aux Bypass Volume", "RAUX" },
+ { "Right Playback Mixer", "Left Bypass Volume", "Left Input Mixer" },
+ { "Right Playback Mixer", "Right Bypass Volume", "Right Input Mixer" },
+};
+
+static const struct snd_soc_dapm_route adau1761_mono_dapm_routes[] = {
+ { "Mono Playback Mixer", NULL, "Left Playback Mixer" },
+ { "Mono Playback Mixer", NULL, "Right Playback Mixer" },
+
+ { "MONOOUT", NULL, "Mono Playback Mixer" },
+};
+
+static const struct snd_soc_dapm_route adau1761_capless_dapm_routes[] = {
+ { "Headphone", NULL, "Headphone VGND" },
+};
+
+static const struct snd_soc_dapm_widget adau1761_dmic_widgets[] = {
+ SND_SOC_DAPM_MUX("Left Decimator Mux", SND_SOC_NOPM, 0, 0,
+ &adau1761_input_mux_control),
+ SND_SOC_DAPM_MUX("Right Decimator Mux", SND_SOC_NOPM, 0, 0,
+ &adau1761_input_mux_control),
+
+ SND_SOC_DAPM_INPUT("DMIC"),
+};
+
+static const struct snd_soc_dapm_route adau1761_dmic_routes[] = {
+ { "Left Decimator Mux", "ADC", "Left Input Mixer" },
+ { "Left Decimator Mux", "DMIC", "DMIC" },
+ { "Right Decimator Mux", "ADC", "Right Input Mixer" },
+ { "Right Decimator Mux", "DMIC", "DMIC" },
+
+ { "Left Decimator", NULL, "Left Decimator Mux" },
+ { "Right Decimator", NULL, "Right Decimator Mux" },
+};
+
+static const struct snd_soc_dapm_route adau1761_no_dmic_routes[] = {
+ { "Left Decimator", NULL, "Left Input Mixer" },
+ { "Right Decimator", NULL, "Right Input Mixer" },
+};
+
+static const struct snd_soc_dapm_widget adau1761_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Serial Port Clock", ADAU1761_CLK_ENABLE0,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Serial Input Routing Clock", ADAU1761_CLK_ENABLE0,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Serial Output Routing Clock", ADAU1761_CLK_ENABLE0,
+ 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Decimator Resync Clock", ADAU1761_CLK_ENABLE0,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Interpolator Resync Clock", ADAU1761_CLK_ENABLE0,
+ 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Slew Clock", ADAU1761_CLK_ENABLE0, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ALC Clock", ADAU1761_CLK_ENABLE0, 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("Digital Clock 0", 1, ADAU1761_CLK_ENABLE1,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Digital Clock 1", 1, ADAU1761_CLK_ENABLE1,
+ 1, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
+ { "Left Decimator", NULL, "Digital Clock 0", },
+ { "Right Decimator", NULL, "Digital Clock 0", },
+ { "Left DAC", NULL, "Digital Clock 0", },
+ { "Right DAC", NULL, "Digital Clock 0", },
+
+ { "AIFCLK", NULL, "Digital Clock 1" },
+
+ { "Playback", NULL, "Serial Port Clock" },
+ { "Capture", NULL, "Serial Port Clock" },
+ { "Playback", NULL, "Serial Input Routing Clock" },
+ { "Capture", NULL, "Serial Output Routing Clock" },
+
+ { "Left Decimator", NULL, "Decimator Resync Clock" },
+ { "Right Decimator", NULL, "Decimator Resync Clock" },
+ { "Left DAC", NULL, "Interpolator Resync Clock" },
+ { "Right DAC", NULL, "Interpolator Resync Clock" },
+
+ { "DSP", NULL, "Digital Clock 0" },
+
+ { "Slew Clock", NULL, "Digital Clock 0" },
+ { "Right Playback Mixer", NULL, "Slew Clock" },
+ { "Left Playback Mixer", NULL, "Slew Clock" },
+
+ { "Left Input Mixer", NULL, "ALC Clock" },
+ { "Right Input Mixer", NULL, "ALC Clock" },
+
+ { "Digital Clock 0", NULL, "SYSCLK" },
+ { "Digital Clock 1", NULL, "SYSCLK" },
+};
+
+static int adau1761_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
+ break;
+
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static enum adau1761_output_mode adau1761_get_lineout_mode(
+ struct snd_soc_codec *codec)
+{
+ struct adau1761_platform_data *pdata = codec->dev->platform_data;
+
+ if (pdata)
+ return pdata->lineout_mode;
+
+ return ADAU1761_OUTPUT_MODE_LINE;
+}
+
+static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
+{
+ struct adau1761_platform_data *pdata = codec->dev->platform_data;
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ enum adau1761_digmic_jackdet_pin_mode mode;
+ unsigned int val = 0;
+ int ret;
+
+ if (pdata)
+ mode = pdata->digmic_jackdetect_pin_mode;
+ else
+ mode = ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE;
+
+ switch (mode) {
+ case ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT:
+ switch (pdata->jackdetect_debounce_time) {
+ case ADAU1761_JACKDETECT_DEBOUNCE_5MS:
+ case ADAU1761_JACKDETECT_DEBOUNCE_10MS:
+ case ADAU1761_JACKDETECT_DEBOUNCE_20MS:
+ case ADAU1761_JACKDETECT_DEBOUNCE_40MS:
+ val |= pdata->jackdetect_debounce_time << 6;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (pdata->jackdetect_active_low)
+ val |= ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW;
+
+ ret = snd_soc_add_codec_controls(codec,
+ 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,
+ 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,
+ ARRAY_SIZE(adau1761_dmic_widgets));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(&codec->dapm,
+ adau1761_dmic_routes,
+ ARRAY_SIZE(adau1761_dmic_routes));
+ if (ret)
+ return ret;
+
+ val |= ADAU1761_DIGMIC_JACKDETECT_DIGMIC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(adau->regmap, ADAU1761_DIGMIC_JACKDETECT, val);
+
+ return 0;
+}
+
+static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
+{
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct adau1761_platform_data *pdata = codec->dev->platform_data;
+ enum adau1761_output_mode mode;
+ int ret;
+
+ if (pdata)
+ mode = pdata->headphone_mode;
+ else
+ mode = ADAU1761_OUTPUT_MODE_HEADPHONE;
+
+ switch (mode) {
+ case ADAU1761_OUTPUT_MODE_LINE:
+ break;
+ case ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS:
+ regmap_update_bits(adau->regmap, ADAU1761_PLAY_MONO_OUTPUT_VOL,
+ ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP |
+ ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE,
+ ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP |
+ ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE);
+ /* fallthrough */
+ case ADAU1761_OUTPUT_MODE_HEADPHONE:
+ regmap_update_bits(adau->regmap, ADAU1761_PLAY_HP_RIGHT_VOL,
+ ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP,
+ ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (mode == ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS) {
+ ret = snd_soc_dapm_new_controls(&codec->dapm,
+ adau1761_capless_dapm_widgets,
+ ARRAY_SIZE(adau1761_capless_dapm_widgets));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_add_routes(&codec->dapm,
+ adau1761_capless_dapm_routes,
+ ARRAY_SIZE(adau1761_capless_dapm_routes));
+ } else {
+ ret = snd_soc_add_codec_controls(codec, adau1761_mono_controls,
+ ARRAY_SIZE(adau1761_mono_controls));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_new_controls(&codec->dapm,
+ adau1761_mono_dapm_widgets,
+ ARRAY_SIZE(adau1761_mono_dapm_widgets));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_add_routes(&codec->dapm,
+ adau1761_mono_dapm_routes,
+ ARRAY_SIZE(adau1761_mono_dapm_routes));
+ }
+
+ return ret;
+}
+
+static bool adau1761_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1761_DIGMIC_JACKDETECT:
+ case ADAU1761_REC_MIXER_LEFT0:
+ case ADAU1761_REC_MIXER_LEFT1:
+ case ADAU1761_REC_MIXER_RIGHT0:
+ case ADAU1761_REC_MIXER_RIGHT1:
+ case ADAU1761_LEFT_DIFF_INPUT_VOL:
+ case ADAU1761_RIGHT_DIFF_INPUT_VOL:
+ case ADAU1761_PLAY_LR_MIXER_LEFT:
+ case ADAU1761_PLAY_MIXER_LEFT0:
+ case ADAU1761_PLAY_MIXER_LEFT1:
+ case ADAU1761_PLAY_MIXER_RIGHT0:
+ case ADAU1761_PLAY_MIXER_RIGHT1:
+ case ADAU1761_PLAY_LR_MIXER_RIGHT:
+ case ADAU1761_PLAY_MIXER_MONO:
+ case ADAU1761_PLAY_HP_LEFT_VOL:
+ case ADAU1761_PLAY_HP_RIGHT_VOL:
+ case ADAU1761_PLAY_LINE_LEFT_VOL:
+ case ADAU1761_PLAY_LINE_RIGHT_VOL:
+ case ADAU1761_PLAY_MONO_OUTPUT_VOL:
+ case ADAU1761_POP_CLICK_SUPPRESS:
+ case ADAU1761_JACK_DETECT_PIN:
+ case ADAU1761_DEJITTER:
+ case ADAU1761_CLK_ENABLE0:
+ case ADAU1761_CLK_ENABLE1:
+ return true;
+ default:
+ break;
+ }
+
+ return adau17x1_readable_register(dev, reg);
+}
+
+static int adau1761_codec_probe(struct snd_soc_codec *codec)
+{
+ struct adau1761_platform_data *pdata = codec->dev->platform_data;
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = adau17x1_add_widgets(codec);
+ if (ret < 0)
+ return ret;
+
+ if (pdata && pdata->input_differential) {
+ regmap_update_bits(adau->regmap, ADAU1761_LEFT_DIFF_INPUT_VOL,
+ ADAU1761_DIFF_INPUT_VOL_LDEN,
+ ADAU1761_DIFF_INPUT_VOL_LDEN);
+ regmap_update_bits(adau->regmap, ADAU1761_RIGHT_DIFF_INPUT_VOL,
+ ADAU1761_DIFF_INPUT_VOL_LDEN,
+ ADAU1761_DIFF_INPUT_VOL_LDEN);
+ ret = snd_soc_add_codec_controls(codec,
+ adau1761_differential_mode_controls,
+ ARRAY_SIZE(adau1761_differential_mode_controls));
+ if (ret)
+ return ret;
+ } else {
+ ret = snd_soc_add_codec_controls(codec,
+ adau1761_single_mode_controls,
+ ARRAY_SIZE(adau1761_single_mode_controls));
+ if (ret)
+ return ret;
+ }
+
+ switch (adau1761_get_lineout_mode(codec)) {
+ case ADAU1761_OUTPUT_MODE_LINE:
+ break;
+ case ADAU1761_OUTPUT_MODE_HEADPHONE:
+ regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_LEFT_VOL,
+ ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP,
+ ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP);
+ regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_RIGHT_VOL,
+ ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP,
+ ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = adau1761_setup_headphone_mode(codec);
+ if (ret)
+ return ret;
+
+ ret = adau1761_setup_digmic_jackdetect(codec);
+ if (ret)
+ return ret;
+
+ if (adau->type == ADAU1761) {
+ ret = snd_soc_dapm_new_controls(&codec->dapm,
+ adau1761_dapm_widgets,
+ ARRAY_SIZE(adau1761_dapm_widgets));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(&codec->dapm,
+ adau1761_dapm_routes,
+ ARRAY_SIZE(adau1761_dapm_routes));
+ if (ret)
+ return ret;
+ }
+
+ ret = adau17x1_add_routes(codec);
+ 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),
+};
+
+#define ADAU1761_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver adau1361_dai_driver = {
+ .name = "adau-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ADAU1761_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ADAU1761_FORMATS,
+ },
+ .ops = &adau17x1_dai_ops,
+};
+
+static struct snd_soc_dai_driver adau1761_dai_driver = {
+ .name = "adau-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ADAU1761_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ADAU1761_FORMATS,
+ },
+ .ops = &adau17x1_dai_ops,
+};
+
+int adau1761_probe(struct device *dev, struct regmap *regmap,
+ enum adau17x1_type type, void (*switch_mode)(struct device *dev))
+{
+ struct snd_soc_dai_driver *dai_drv;
+ const char *firmware_name;
+ int ret;
+
+ if (type == ADAU1361) {
+ dai_drv = &adau1361_dai_driver;
+ firmware_name = NULL;
+ } else {
+ dai_drv = &adau1761_dai_driver;
+ firmware_name = ADAU1761_FIRMWARE;
+ }
+
+ ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name);
+ if (ret)
+ return ret;
+
+ return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);
+}
+EXPORT_SYMBOL_GPL(adau1761_probe);
+
+const struct regmap_config adau1761_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 16,
+ .max_register = 0x40fa,
+ .reg_defaults = adau1761_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adau1761_reg_defaults),
+ .readable_reg = adau1761_readable_register,
+ .volatile_reg = adau17x1_volatile_register,
+ .precious_reg = adau17x1_precious_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(adau1761_regmap_config);
+
+MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adau1761.h b/kernel/sound/soc/codecs/adau1761.h
new file mode 100644
index 000000000..a9e0d2883
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1761.h
@@ -0,0 +1,23 @@
+/*
+ * 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__
+#define __SOUND_SOC_CODECS_ADAU1761_H__
+
+#include <linux/regmap.h>
+#include "adau17x1.h"
+
+struct device;
+
+int adau1761_probe(struct device *dev, struct regmap *regmap,
+ enum adau17x1_type type, void (*switch_mode)(struct device *dev));
+
+extern const struct regmap_config adau1761_regmap_config;
+
+#endif
diff --git a/kernel/sound/soc/codecs/adau1781-i2c.c b/kernel/sound/soc/codecs/adau1781-i2c.c
new file mode 100644
index 000000000..2ce4362cc
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1781-i2c.c
@@ -0,0 +1,58 @@
+/*
+ * 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>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "adau1781.h"
+
+static int adau1781_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regmap_config config;
+
+ config = adau1781_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 16;
+
+ return adau1781_probe(&client->dev,
+ devm_regmap_init_i2c(client, &config),
+ id->driver_data, NULL);
+}
+
+static int adau1781_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id adau1781_i2c_ids[] = {
+ { "adau1381", ADAU1381 },
+ { "adau1781", ADAU1781 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids);
+
+static struct i2c_driver adau1781_i2c_driver = {
+ .driver = {
+ .name = "adau1781",
+ .owner = THIS_MODULE,
+ },
+ .probe = adau1781_i2c_probe,
+ .remove = adau1781_i2c_remove,
+ .id_table = adau1781_i2c_ids,
+};
+module_i2c_driver(adau1781_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 CODEC I2C driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adau1781-spi.c b/kernel/sound/soc/codecs/adau1781-spi.c
new file mode 100644
index 000000000..194686716
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1781-spi.c
@@ -0,0 +1,75 @@
+/*
+ * 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>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "adau1781.h"
+
+static void adau1781_spi_switch_mode(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ /*
+ * To get the device into SPI mode CLATCH has to be pulled low three
+ * times. Do this by issuing three dummy reads.
+ */
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+}
+
+static int adau1781_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct regmap_config config;
+
+ if (!id)
+ return -EINVAL;
+
+ config = adau1781_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 24;
+ config.read_flag_mask = 0x1;
+
+ return adau1781_probe(&spi->dev,
+ devm_regmap_init_spi(spi, &config),
+ id->driver_data, adau1781_spi_switch_mode);
+}
+
+static int adau1781_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static const struct spi_device_id adau1781_spi_id[] = {
+ { "adau1381", ADAU1381 },
+ { "adau1781", ADAU1781 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adau1781_spi_id);
+
+static struct spi_driver adau1781_spi_driver = {
+ .driver = {
+ .name = "adau1781",
+ .owner = THIS_MODULE,
+ },
+ .probe = adau1781_spi_probe,
+ .remove = adau1781_spi_remove,
+ .id_table = adau1781_spi_id,
+};
+module_spi_driver(adau1781_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 CODEC SPI driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adau1781.c b/kernel/sound/soc/codecs/adau1781.c
new file mode 100644
index 000000000..35581f43f
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1781.c
@@ -0,0 +1,508 @@
+/*
+ * Driver for ADAU1781/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>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/platform_data/adau17x1.h>
+
+#include "adau17x1.h"
+#include "adau1781.h"
+
+#define ADAU1781_DMIC_BEEP_CTRL 0x4008
+#define ADAU1781_LEFT_PGA 0x400e
+#define ADAU1781_RIGHT_PGA 0x400f
+#define ADAU1781_LEFT_PLAYBACK_MIXER 0x401c
+#define ADAU1781_RIGHT_PLAYBACK_MIXER 0x401e
+#define ADAU1781_MONO_PLAYBACK_MIXER 0x401f
+#define ADAU1781_LEFT_LINEOUT 0x4025
+#define ADAU1781_RIGHT_LINEOUT 0x4026
+#define ADAU1781_SPEAKER 0x4027
+#define ADAU1781_BEEP_ZC 0x4028
+#define ADAU1781_DEJITTER 0x4032
+#define ADAU1781_DIG_PWDN0 0x4080
+#define ADAU1781_DIG_PWDN1 0x4081
+
+#define ADAU1781_INPUT_DIFFERNTIAL BIT(3)
+
+#define ADAU1381_FIRMWARE "adau1381.bin"
+#define ADAU1781_FIRMWARE "adau1781.bin"
+
+static const struct reg_default adau1781_reg_defaults[] = {
+ { ADAU1781_DMIC_BEEP_CTRL, 0x00 },
+ { ADAU1781_LEFT_PGA, 0xc7 },
+ { ADAU1781_RIGHT_PGA, 0xc7 },
+ { ADAU1781_LEFT_PLAYBACK_MIXER, 0x00 },
+ { ADAU1781_RIGHT_PLAYBACK_MIXER, 0x00 },
+ { ADAU1781_MONO_PLAYBACK_MIXER, 0x00 },
+ { ADAU1781_LEFT_LINEOUT, 0x00 },
+ { ADAU1781_RIGHT_LINEOUT, 0x00 },
+ { ADAU1781_SPEAKER, 0x00 },
+ { ADAU1781_BEEP_ZC, 0x19 },
+ { ADAU1781_DEJITTER, 0x60 },
+ { ADAU1781_DIG_PWDN1, 0x0c },
+ { ADAU1781_DIG_PWDN1, 0x00 },
+ { ADAU17X1_CLOCK_CONTROL, 0x00 },
+ { ADAU17X1_PLL_CONTROL, 0x00 },
+ { ADAU17X1_REC_POWER_MGMT, 0x00 },
+ { ADAU17X1_MICBIAS, 0x04 },
+ { ADAU17X1_SERIAL_PORT0, 0x00 },
+ { ADAU17X1_SERIAL_PORT1, 0x00 },
+ { ADAU17X1_CONVERTER0, 0x00 },
+ { ADAU17X1_CONVERTER1, 0x00 },
+ { ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 },
+ { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 },
+ { ADAU17X1_ADC_CONTROL, 0x00 },
+ { ADAU17X1_PLAY_POWER_MGMT, 0x00 },
+ { ADAU17X1_DAC_CONTROL0, 0x00 },
+ { ADAU17X1_DAC_CONTROL1, 0x00 },
+ { ADAU17X1_DAC_CONTROL2, 0x00 },
+ { ADAU17X1_SERIAL_PORT_PAD, 0x00 },
+ { ADAU17X1_CONTROL_PORT_PAD0, 0x00 },
+ { ADAU17X1_CONTROL_PORT_PAD1, 0x00 },
+ { ADAU17X1_DSP_SAMPLING_RATE, 0x01 },
+ { ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 },
+ { ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 },
+ { ADAU17X1_DSP_ENABLE, 0x00 },
+ { ADAU17X1_DSP_RUN, 0x00 },
+ { ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 },
+};
+
+static const DECLARE_TLV_DB_SCALE(adau1781_speaker_tlv, 0, 200, 0);
+
+static const DECLARE_TLV_DB_RANGE(adau1781_pga_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
+ 4, 4, TLV_DB_SCALE_ITEM(1700, 0, 0),
+ 5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(adau1781_beep_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
+ 4, 4, TLV_DB_SCALE_ITEM(-2300, 0, 0),
+ 5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(adau1781_sidetone_tlv, -1800, 300, 1);
+
+static const char * const adau1781_speaker_bias_select_text[] = {
+ "Normal operation", "Power saving", "Enhanced performance",
+};
+
+static const char * const adau1781_bias_select_text[] = {
+ "Normal operation", "Extreme power saving", "Power saving",
+ "Enhanced performance",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1781_adc_bias_enum,
+ ADAU17X1_REC_POWER_MGMT, 3, adau1781_bias_select_text);
+static SOC_ENUM_SINGLE_DECL(adau1781_speaker_bias_enum,
+ ADAU17X1_PLAY_POWER_MGMT, 6, adau1781_speaker_bias_select_text);
+static SOC_ENUM_SINGLE_DECL(adau1781_dac_bias_enum,
+ ADAU17X1_PLAY_POWER_MGMT, 4, adau1781_bias_select_text);
+static SOC_ENUM_SINGLE_DECL(adau1781_playback_bias_enum,
+ ADAU17X1_PLAY_POWER_MGMT, 2, adau1781_bias_select_text);
+static SOC_ENUM_SINGLE_DECL(adau1781_capture_bias_enum,
+ ADAU17X1_REC_POWER_MGMT, 1, adau1781_bias_select_text);
+
+static const struct snd_kcontrol_new adau1781_controls[] = {
+ SOC_SINGLE_TLV("Beep Capture Volume", ADAU1781_DMIC_BEEP_CTRL, 0, 7, 0,
+ adau1781_beep_tlv),
+ SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAU1781_LEFT_PGA,
+ ADAU1781_RIGHT_PGA, 5, 7, 0, adau1781_pga_tlv),
+ SOC_DOUBLE_R("PGA Capture Switch", ADAU1781_LEFT_PGA,
+ ADAU1781_RIGHT_PGA, 1, 1, 0),
+
+ SOC_DOUBLE_R("Lineout Playback Switch", ADAU1781_LEFT_LINEOUT,
+ ADAU1781_RIGHT_LINEOUT, 1, 1, 0),
+ SOC_SINGLE("Beep ZC Switch", ADAU1781_BEEP_ZC, 0, 1, 0),
+
+ SOC_SINGLE("Mono Playback Switch", ADAU1781_MONO_PLAYBACK_MIXER,
+ 0, 1, 0),
+ SOC_SINGLE_TLV("Mono Playback Volume", ADAU1781_SPEAKER, 6, 3, 0,
+ adau1781_speaker_tlv),
+
+ SOC_ENUM("ADC Bias", adau1781_adc_bias_enum),
+ SOC_ENUM("DAC Bias", adau1781_dac_bias_enum),
+ SOC_ENUM("Capture Bias", adau1781_capture_bias_enum),
+ SOC_ENUM("Playback Bias", adau1781_playback_bias_enum),
+ SOC_ENUM("Speaker Bias", adau1781_speaker_bias_enum),
+};
+
+static const struct snd_kcontrol_new adau1781_beep_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Beep Capture Switch", ADAU1781_DMIC_BEEP_CTRL,
+ 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1781_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ ADAU1781_LEFT_PLAYBACK_MIXER, 5, 1, 0),
+ SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
+ ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1781_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ ADAU1781_RIGHT_PLAYBACK_MIXER, 6, 1, 0),
+ SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
+ ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Left Switch",
+ ADAU1781_MONO_PLAYBACK_MIXER, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("Right Switch",
+ ADAU1781_MONO_PLAYBACK_MIXER, 6, 1, 0),
+ SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
+ ADAU1781_MONO_PLAYBACK_MIXER, 2, 8, 0, adau1781_sidetone_tlv),
+};
+
+static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+ /* After any power changes have been made the dejitter circuit
+ * has to be reinitialized. */
+ regmap_write(adau->regmap, ADAU1781_DEJITTER, 0);
+ if (!adau->master)
+ regmap_write(adau->regmap, ADAU1781_DEJITTER, 5);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget adau1781_dapm_widgets[] = {
+ SND_SOC_DAPM_PGA("Left PGA", ADAU1781_LEFT_PGA, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right PGA", ADAU1781_RIGHT_PGA, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("Speaker", ADAU1781_SPEAKER, 0, 0, NULL, 0),
+
+ SOC_MIXER_NAMED_CTL_ARRAY("Beep Mixer", ADAU17X1_MICBIAS, 4, 0,
+ adau1781_beep_mixer_controls),
+
+ SOC_MIXER_ARRAY("Left Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ adau1781_left_mixer_controls),
+ SOC_MIXER_ARRAY("Right Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ adau1781_right_mixer_controls),
+ SOC_MIXER_ARRAY("Mono Mixer", SND_SOC_NOPM, 0, 0,
+ adau1781_mono_mixer_controls),
+
+ SND_SOC_DAPM_SUPPLY("Serial Input Routing", ADAU1781_DIG_PWDN0,
+ 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Serial Output Routing", ADAU1781_DIG_PWDN0,
+ 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Clock Domain Transfer", ADAU1781_DIG_PWDN0,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Serial Ports", ADAU1781_DIG_PWDN0, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Engine", ADAU1781_DIG_PWDN0, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Engine", ADAU1781_DIG_PWDN1, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Digital Mic", ADAU1781_DIG_PWDN1, 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Sound Engine", ADAU1781_DIG_PWDN0, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, ADAU1781_DIG_PWDN0, 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Zero Crossing Detector", ADAU1781_DIG_PWDN1, 2, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_POST("Dejitter fixup", adau1781_dejitter_fixup),
+
+ SND_SOC_DAPM_INPUT("BEEP"),
+
+ SND_SOC_DAPM_OUTPUT("AOUTL"),
+ SND_SOC_DAPM_OUTPUT("AOUTR"),
+ SND_SOC_DAPM_OUTPUT("SP"),
+ SND_SOC_DAPM_INPUT("LMIC"),
+ SND_SOC_DAPM_INPUT("RMIC"),
+};
+
+static const struct snd_soc_dapm_route adau1781_dapm_routes[] = {
+ { "Left Lineout Mixer", NULL, "Left Playback Enable" },
+ { "Right Lineout Mixer", NULL, "Right Playback Enable" },
+
+ { "Left Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
+ { "Left Lineout Mixer", "Switch", "Left DAC" },
+
+ { "Right Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
+ { "Right Lineout Mixer", "Switch", "Right DAC" },
+
+ { "Mono Mixer", "Beep Playback Volume", "Beep Mixer" },
+ { "Mono Mixer", "Right Switch", "Right DAC" },
+ { "Mono Mixer", "Left Switch", "Left DAC" },
+ { "Speaker", NULL, "Mono Mixer" },
+
+ { "Mono Mixer", NULL, "SYSCLK" },
+ { "Left Lineout Mixer", NULL, "SYSCLK" },
+ { "Left Lineout Mixer", NULL, "SYSCLK" },
+
+ { "Beep Mixer", "Beep Capture Switch", "BEEP" },
+ { "Beep Mixer", NULL, "Zero Crossing Detector" },
+
+ { "Left DAC", NULL, "DAC Engine" },
+ { "Right DAC", NULL, "DAC Engine" },
+
+ { "Sound Engine", NULL, "SYSCLK" },
+ { "DSP", NULL, "Sound Engine" },
+
+ { "Left Decimator", NULL, "ADC Engine" },
+ { "Right Decimator", NULL, "ADC Engine" },
+
+ { "AIFCLK", NULL, "SYSCLK" },
+
+ { "Playback", NULL, "Serial Input Routing" },
+ { "Playback", NULL, "Serial Ports" },
+ { "Playback", NULL, "Clock Domain Transfer" },
+ { "Capture", NULL, "Serial Output Routing" },
+ { "Capture", NULL, "Serial Ports" },
+ { "Capture", NULL, "Clock Domain Transfer" },
+
+ { "AOUTL", NULL, "Left Lineout Mixer" },
+ { "AOUTR", NULL, "Right Lineout Mixer" },
+ { "SP", NULL, "Speaker" },
+};
+
+static const struct snd_soc_dapm_route adau1781_adc_dapm_routes[] = {
+ { "Left PGA", NULL, "LMIC" },
+ { "Right PGA", NULL, "RMIC" },
+
+ { "Left Decimator", NULL, "Left PGA" },
+ { "Right Decimator", NULL, "Right PGA" },
+};
+
+static const char * const adau1781_dmic_select_text[] = {
+ "DMIC1", "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(adau1781_dmic_select_enum,
+ adau1781_dmic_select_text);
+
+static const struct snd_kcontrol_new adau1781_dmic_mux =
+ SOC_DAPM_ENUM("DMIC Select", adau1781_dmic_select_enum);
+
+static const struct snd_soc_dapm_widget adau1781_dmic_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("DMIC Select", SND_SOC_NOPM, 0, 0, &adau1781_dmic_mux),
+
+ SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1781_DMIC_BEEP_CTRL, 4, 0),
+ SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1781_DMIC_BEEP_CTRL, 5, 0),
+};
+
+static const struct snd_soc_dapm_route adau1781_dmic_dapm_routes[] = {
+ { "DMIC1", NULL, "LMIC" },
+ { "DMIC2", NULL, "RMIC" },
+
+ { "DMIC1", NULL, "Digital Mic" },
+ { "DMIC2", NULL, "Digital Mic" },
+
+ { "DMIC Select", "DMIC1", "DMIC1" },
+ { "DMIC Select", "DMIC2", "DMIC2" },
+
+ { "Left Decimator", NULL, "DMIC Select" },
+ { "Right Decimator", NULL, "DMIC Select" },
+};
+
+static int adau1781_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+
+ /* Precharge */
+ regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0x8, 0x8);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0xc, 0x0);
+ regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static bool adau1781_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1781_DMIC_BEEP_CTRL:
+ case ADAU1781_LEFT_PGA:
+ case ADAU1781_RIGHT_PGA:
+ case ADAU1781_LEFT_PLAYBACK_MIXER:
+ case ADAU1781_RIGHT_PLAYBACK_MIXER:
+ case ADAU1781_MONO_PLAYBACK_MIXER:
+ case ADAU1781_LEFT_LINEOUT:
+ case ADAU1781_RIGHT_LINEOUT:
+ case ADAU1781_SPEAKER:
+ case ADAU1781_BEEP_ZC:
+ case ADAU1781_DEJITTER:
+ case ADAU1781_DIG_PWDN0:
+ case ADAU1781_DIG_PWDN1:
+ return true;
+ default:
+ break;
+ }
+
+ return adau17x1_readable_register(dev, reg);
+}
+
+static int adau1781_set_input_mode(struct adau *adau, unsigned int reg,
+ bool differential)
+{
+ unsigned int val;
+
+ if (differential)
+ val = ADAU1781_INPUT_DIFFERNTIAL;
+ else
+ val = 0;
+
+ return regmap_update_bits(adau->regmap, reg,
+ ADAU1781_INPUT_DIFFERNTIAL, val);
+}
+
+static int adau1781_codec_probe(struct snd_soc_codec *codec)
+{
+ struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev);
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = adau17x1_add_widgets(codec);
+ if (ret)
+ return ret;
+
+ if (pdata) {
+ ret = adau1781_set_input_mode(adau, ADAU1781_LEFT_PGA,
+ pdata->left_input_differential);
+ if (ret)
+ return ret;
+ ret = adau1781_set_input_mode(adau, ADAU1781_RIGHT_PGA,
+ pdata->right_input_differential);
+ if (ret)
+ return ret;
+ }
+
+ if (pdata && pdata->use_dmic) {
+ ret = snd_soc_dapm_new_controls(&codec->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,
+ ARRAY_SIZE(adau1781_dmic_dapm_routes));
+ if (ret)
+ return ret;
+ } else {
+ ret = snd_soc_dapm_add_routes(&codec->dapm,
+ adau1781_adc_dapm_routes,
+ ARRAY_SIZE(adau1781_adc_dapm_routes));
+ if (ret)
+ return ret;
+ }
+
+ ret = adau17x1_add_routes(codec);
+ 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),
+};
+
+#define ADAU1781_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver adau1781_dai_driver = {
+ .name = "adau-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ADAU1781_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ADAU1781_FORMATS,
+ },
+ .ops = &adau17x1_dai_ops,
+};
+
+const struct regmap_config adau1781_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 16,
+ .max_register = 0x40f8,
+ .reg_defaults = adau1781_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adau1781_reg_defaults),
+ .readable_reg = adau1781_readable_register,
+ .volatile_reg = adau17x1_volatile_register,
+ .precious_reg = adau17x1_precious_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(adau1781_regmap_config);
+
+int adau1781_probe(struct device *dev, struct regmap *regmap,
+ enum adau17x1_type type, void (*switch_mode)(struct device *dev))
+{
+ const char *firmware_name;
+ int ret;
+
+ switch (type) {
+ case ADAU1381:
+ firmware_name = ADAU1381_FIRMWARE;
+ break;
+ case ADAU1781:
+ firmware_name = ADAU1781_FIRMWARE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name);
+ if (ret)
+ return ret;
+
+ return snd_soc_register_codec(dev, &adau1781_codec_driver,
+ &adau1781_dai_driver, 1);
+}
+EXPORT_SYMBOL_GPL(adau1781_probe);
+
+MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adau1781.h b/kernel/sound/soc/codecs/adau1781.h
new file mode 100644
index 000000000..2b96e0a9f
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1781.h
@@ -0,0 +1,23 @@
+/*
+ * 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__
+#define __SOUND_SOC_CODECS_ADAU1781_H__
+
+#include <linux/regmap.h>
+#include "adau17x1.h"
+
+struct device;
+
+int adau1781_probe(struct device *dev, struct regmap *regmap,
+ enum adau17x1_type type, void (*switch_mode)(struct device *dev));
+
+extern const struct regmap_config adau1781_regmap_config;
+
+#endif
diff --git a/kernel/sound/soc/codecs/adau17x1.c b/kernel/sound/soc/codecs/adau17x1.c
new file mode 100644
index 000000000..fa2e690e5
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau17x1.c
@@ -0,0 +1,915 @@
+/*
+ * 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/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#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 "sigmadsp.h"
+#include "adau17x1.h"
+
+static const char * const adau17x1_capture_mixer_boost_text[] = {
+ "Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau17x1_capture_boost_enum,
+ ADAU17X1_REC_POWER_MGMT, 5, adau17x1_capture_mixer_boost_text);
+
+static const char * const adau17x1_mic_bias_mode_text[] = {
+ "Normal operation", "High performance",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau17x1_mic_bias_mode_enum,
+ ADAU17X1_MICBIAS, 3, adau17x1_mic_bias_mode_text);
+
+static const DECLARE_TLV_DB_MINMAX(adau17x1_digital_tlv, -9563, 0);
+
+static const struct snd_kcontrol_new adau17x1_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Capture Volume",
+ ADAU17X1_LEFT_INPUT_DIGITAL_VOL,
+ ADAU17X1_RIGHT_INPUT_DIGITAL_VOL,
+ 0, 0xff, 1, adau17x1_digital_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume", ADAU17X1_DAC_CONTROL1,
+ ADAU17X1_DAC_CONTROL2, 0, 0xff, 1, adau17x1_digital_tlv),
+
+ SOC_SINGLE("ADC High Pass Filter Switch", ADAU17X1_ADC_CONTROL,
+ 5, 1, 0),
+ SOC_SINGLE("Playback De-emphasis Switch", ADAU17X1_DAC_CONTROL0,
+ 2, 1, 0),
+
+ SOC_ENUM("Capture Boost", adau17x1_capture_boost_enum),
+
+ SOC_ENUM("Mic Bias Mode", adau17x1_mic_bias_mode_enum),
+};
+
+static int adau17x1_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;
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ adau->pll_regs[5] = 1;
+ } else {
+ adau->pll_regs[5] = 0;
+ /* Bypass the PLL when disabled, otherwise registers will become
+ * inaccessible. */
+ regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL, 0);
+ }
+
+ /* The PLL register is 6 bytes long and can only be written at once. */
+ ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
+ adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ mdelay(5);
+ regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+ ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL,
+ ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL);
+ }
+
+ return 0;
+}
+
+static const char * const adau17x1_mono_stereo_text[] = {
+ "Stereo",
+ "Mono Left Channel (L+R)",
+ "Mono Right Channel (L+R)",
+ "Mono (L+R)",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau17x1_dac_mode_enum,
+ ADAU17X1_DAC_CONTROL0, 6, adau17x1_mono_stereo_text);
+
+static const struct snd_kcontrol_new adau17x1_dac_mode_mux =
+ SOC_DAPM_ENUM("DAC Mono-Stereo-Mode", adau17x1_dac_mode_enum);
+
+static const struct snd_soc_dapm_widget adau17x1_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY_S("PLL", 3, SND_SOC_NOPM, 0, 0, adau17x1_pll_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("AIFCLK", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS", ADAU17X1_MICBIAS, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Left Playback Enable", ADAU17X1_PLAY_POWER_MGMT,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Right Playback Enable", ADAU17X1_PLAY_POWER_MGMT,
+ 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Left DAC Mode Mux", SND_SOC_NOPM, 0, 0,
+ &adau17x1_dac_mode_mux),
+ SND_SOC_DAPM_MUX("Right DAC Mode Mux", SND_SOC_NOPM, 0, 0,
+ &adau17x1_dac_mode_mux),
+
+ SND_SOC_DAPM_ADC("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0),
+ SND_SOC_DAPM_ADC("Right Decimator", NULL, ADAU17X1_ADC_CONTROL, 1, 0),
+ SND_SOC_DAPM_DAC("Left DAC", NULL, ADAU17X1_DAC_CONTROL0, 0, 0),
+ SND_SOC_DAPM_DAC("Right DAC", NULL, ADAU17X1_DAC_CONTROL0, 1, 0),
+};
+
+static const struct snd_soc_dapm_route adau17x1_dapm_routes[] = {
+ { "Left Decimator", NULL, "SYSCLK" },
+ { "Right Decimator", NULL, "SYSCLK" },
+ { "Left DAC", NULL, "SYSCLK" },
+ { "Right DAC", NULL, "SYSCLK" },
+ { "Capture", NULL, "SYSCLK" },
+ { "Playback", NULL, "SYSCLK" },
+
+ { "Left DAC", NULL, "Left DAC Mode Mux" },
+ { "Right DAC", NULL, "Right DAC Mode Mux" },
+
+ { "Capture", NULL, "AIFCLK" },
+ { "Playback", NULL, "AIFCLK" },
+};
+
+static const struct snd_soc_dapm_route adau17x1_dapm_pll_route = {
+ "SYSCLK", NULL, "PLL",
+};
+
+/*
+ * The MUX register for the Capture and Playback MUXs selects either DSP as
+ * source/destination or one of the TDM slots. The TDM slot is selected via
+ * snd_soc_dai_set_tdm_slot(), so we only expose whether to go to the DSP or
+ * directly to the DAI interface with this control.
+ */
+static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_update update;
+ unsigned int stream = e->shift_l;
+ unsigned int val, change;
+ int reg;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ switch (ucontrol->value.enumerated.item[0]) {
+ case 0:
+ val = 0;
+ adau->dsp_bypass[stream] = false;
+ break;
+ default:
+ val = (adau->tdm_slot[stream] * 2) + 1;
+ adau->dsp_bypass[stream] = true;
+ break;
+ }
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = ADAU17X1_SERIAL_INPUT_ROUTE;
+ else
+ reg = ADAU17X1_SERIAL_OUTPUT_ROUTE;
+
+ change = snd_soc_test_bits(codec, 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,
+ ucontrol->value.enumerated.item[0], e, &update);
+ }
+
+ return change;
+}
+
+static int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int stream = e->shift_l;
+ unsigned int reg, val;
+ int ret;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = ADAU17X1_SERIAL_INPUT_ROUTE;
+ else
+ reg = ADAU17X1_SERIAL_OUTPUT_ROUTE;
+
+ ret = regmap_read(adau->regmap, reg, &val);
+ if (ret)
+ return ret;
+
+ if (val != 0)
+ val = 1;
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+#define DECLARE_ADAU17X1_DSP_MUX_CTRL(_name, _label, _stream, _text) \
+ const struct snd_kcontrol_new _name = \
+ SOC_DAPM_ENUM_EXT(_label, (const struct soc_enum)\
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, _stream, \
+ ARRAY_SIZE(_text), _text), \
+ adau17x1_dsp_mux_enum_get, adau17x1_dsp_mux_enum_put)
+
+static const char * const adau17x1_dac_mux_text[] = {
+ "DSP",
+ "AIFIN",
+};
+
+static const char * const adau17x1_capture_mux_text[] = {
+ "DSP",
+ "Decimator",
+};
+
+static DECLARE_ADAU17X1_DSP_MUX_CTRL(adau17x1_dac_mux, "DAC Playback Mux",
+ SNDRV_PCM_STREAM_PLAYBACK, adau17x1_dac_mux_text);
+
+static DECLARE_ADAU17X1_DSP_MUX_CTRL(adau17x1_capture_mux, "Capture Mux",
+ SNDRV_PCM_STREAM_CAPTURE, adau17x1_capture_mux_text);
+
+static const struct snd_soc_dapm_widget adau17x1_dsp_dapm_widgets[] = {
+ SND_SOC_DAPM_PGA("DSP", ADAU17X1_DSP_RUN, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SIGGEN("DSP Siggen"),
+
+ SND_SOC_DAPM_MUX("DAC Playback Mux", SND_SOC_NOPM, 0, 0,
+ &adau17x1_dac_mux),
+ SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0,
+ &adau17x1_capture_mux),
+};
+
+static const struct snd_soc_dapm_route adau17x1_dsp_dapm_routes[] = {
+ { "DAC Playback Mux", "DSP", "DSP" },
+ { "DAC Playback Mux", "AIFIN", "Playback" },
+
+ { "Left DAC Mode Mux", "Stereo", "DAC Playback Mux" },
+ { "Left DAC Mode Mux", "Mono (L+R)", "DAC Playback Mux" },
+ { "Left DAC Mode Mux", "Mono Left Channel (L+R)", "DAC Playback Mux" },
+ { "Right DAC Mode Mux", "Stereo", "DAC Playback Mux" },
+ { "Right DAC Mode Mux", "Mono (L+R)", "DAC Playback Mux" },
+ { "Right DAC Mode Mux", "Mono Right Channel (L+R)", "DAC Playback Mux" },
+
+ { "Capture Mux", "DSP", "DSP" },
+ { "Capture Mux", "Decimator", "Left Decimator" },
+ { "Capture Mux", "Decimator", "Right Decimator" },
+
+ { "Capture", NULL, "Capture Mux" },
+
+ { "DSP", NULL, "DSP Siggen" },
+
+ { "DSP", NULL, "Left Decimator" },
+ { "DSP", NULL, "Right Decimator" },
+};
+
+static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = {
+ { "Left DAC Mode Mux", "Stereo", "Playback" },
+ { "Left DAC Mode Mux", "Mono (L+R)", "Playback" },
+ { "Left DAC Mode Mux", "Mono Left Channel (L+R)", "Playback" },
+ { "Right DAC Mode Mux", "Stereo", "Playback" },
+ { "Right DAC Mode Mux", "Mono (L+R)", "Playback" },
+ { "Right DAC Mode Mux", "Mono Right Channel (L+R)", "Playback" },
+ { "Capture", NULL, "Left Decimator" },
+ { "Capture", NULL, "Right Decimator" },
+};
+
+bool adau17x1_has_dsp(struct adau *adau)
+{
+ switch (adau->type) {
+ case ADAU1761:
+ case ADAU1381:
+ case ADAU1781:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
+
+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);
+ unsigned int val, div, dsp_div;
+ unsigned int freq;
+ int ret;
+
+ if (adau->clk_src == ADAU17X1_CLK_SRC_PLL)
+ freq = adau->pll_freq;
+ else
+ freq = adau->sysclk;
+
+ if (freq % params_rate(params) != 0)
+ return -EINVAL;
+
+ switch (freq / params_rate(params)) {
+ case 1024: /* fs */
+ div = 0;
+ dsp_div = 1;
+ break;
+ case 6144: /* fs / 6 */
+ div = 1;
+ dsp_div = 6;
+ break;
+ case 4096: /* fs / 4 */
+ div = 2;
+ dsp_div = 5;
+ break;
+ case 3072: /* fs / 3 */
+ div = 3;
+ dsp_div = 4;
+ break;
+ case 2048: /* fs / 2 */
+ div = 4;
+ dsp_div = 3;
+ break;
+ case 1536: /* fs / 1.5 */
+ div = 5;
+ dsp_div = 2;
+ break;
+ case 512: /* fs / 0.5 */
+ div = 6;
+ dsp_div = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
+ ADAU17X1_CONVERTER0_CONVSR_MASK, div);
+ if (adau17x1_has_dsp(adau)) {
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_SAMPLING_RATE, div);
+ regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div);
+ }
+
+ if (adau->sigmadsp) {
+ ret = adau17x1_setup_firmware(adau, params_rate(params));
+ if (ret < 0)
+ return ret;
+ }
+
+ if (adau->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
+ return 0;
+
+ switch (params_width(params)) {
+ case 16:
+ val = ADAU17X1_SERIAL_PORT1_DELAY16;
+ break;
+ case 24:
+ val = ADAU17X1_SERIAL_PORT1_DELAY8;
+ break;
+ case 32:
+ val = ADAU17X1_SERIAL_PORT1_DELAY0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
+ ADAU17X1_SERIAL_PORT1_DELAY_MASK, val);
+}
+
+static int adau17x1_set_dai_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);
+ unsigned int ctrl0, ctrl1;
+ int lrclk_pol;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl0 = ADAU17X1_SERIAL_PORT0_MASTER;
+ adau->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ ctrl0 = 0;
+ adau->master = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ lrclk_pol = 0;
+ ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ lrclk_pol = 1;
+ ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ lrclk_pol = 1;
+ ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE;
+ ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ lrclk_pol = 1;
+ ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE;
+ ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk_pol = !lrclk_pol;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL;
+ lrclk_pol = !lrclk_pol;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (lrclk_pol)
+ ctrl0 |= ADAU17X1_SERIAL_PORT0_LRCLK_POL;
+
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT0, ctrl0);
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT1, ctrl1);
+
+ adau->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ return 0;
+}
+
+static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int ser_ctrl0, ser_ctrl1;
+ unsigned int conv_ctrl0, conv_ctrl1;
+
+ /* I2S mode */
+ if (slots == 0) {
+ slots = 2;
+ rx_mask = 3;
+ tx_mask = 3;
+ slot_width = 32;
+ }
+
+ switch (slots) {
+ case 2:
+ ser_ctrl0 = ADAU17X1_SERIAL_PORT0_STEREO;
+ break;
+ case 4:
+ ser_ctrl0 = ADAU17X1_SERIAL_PORT0_TDM4;
+ break;
+ case 8:
+ if (adau->type == ADAU1361)
+ return -EINVAL;
+
+ ser_ctrl0 = ADAU17X1_SERIAL_PORT0_TDM8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width * slots) {
+ case 32:
+ if (adau->type == ADAU1761)
+ return -EINVAL;
+
+ ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32;
+ break;
+ case 64:
+ ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK64;
+ break;
+ case 48:
+ ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK48;
+ break;
+ case 128:
+ ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK128;
+ break;
+ case 256:
+ if (adau->type == ADAU1361)
+ return -EINVAL;
+
+ ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK256;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (rx_mask) {
+ case 0x03:
+ conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(1);
+ adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 0;
+ break;
+ case 0x0c:
+ conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(2);
+ adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 1;
+ break;
+ case 0x30:
+ conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(3);
+ adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 2;
+ break;
+ case 0xc0:
+ conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(4);
+ adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (tx_mask) {
+ case 0x03:
+ conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(1);
+ adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 0;
+ break;
+ case 0x0c:
+ conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(2);
+ adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 1;
+ break;
+ case 0x30:
+ conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(3);
+ adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 2;
+ break;
+ case 0xc0:
+ conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(4);
+ adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
+ ADAU17X1_CONVERTER0_DAC_PAIR_MASK, conv_ctrl0);
+ regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER1,
+ ADAU17X1_CONVERTER1_ADC_PAIR_MASK, conv_ctrl1);
+ regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT0,
+ ADAU17X1_SERIAL_PORT0_TDM_MASK, ser_ctrl0);
+ regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
+ ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1);
+
+ if (!adau17x1_has_dsp(adau))
+ return 0;
+
+ if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) {
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE,
+ (adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] * 2) + 1);
+ }
+
+ if (adau->dsp_bypass[SNDRV_PCM_STREAM_CAPTURE]) {
+ regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE,
+ (adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] * 2) + 1);
+ }
+
+ return 0;
+}
+
+static int adau17x1_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+
+ if (adau->sigmadsp)
+ return sigmadsp_restrict_params(adau->sigmadsp, substream);
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops adau17x1_dai_ops = {
+ .hw_params = adau17x1_hw_params,
+ .set_sysclk = adau17x1_set_dai_sysclk,
+ .set_fmt = adau17x1_set_dai_fmt,
+ .set_pll = adau17x1_set_dai_pll,
+ .set_tdm_slot = adau17x1_set_dai_tdm_slot,
+ .startup = adau17x1_startup,
+};
+EXPORT_SYMBOL_GPL(adau17x1_dai_ops);
+
+int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
+ enum adau17x1_micbias_voltage micbias)
+{
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+ switch (micbias) {
+ case ADAU17X1_MICBIAS_0_90_AVDD:
+ case ADAU17X1_MICBIAS_0_65_AVDD:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_write(adau->regmap, ADAU17X1_MICBIAS, micbias << 2);
+}
+EXPORT_SYMBOL_GPL(adau17x1_set_micbias_voltage);
+
+bool adau17x1_precious_register(struct device *dev, unsigned int reg)
+{
+ /* SigmaDSP parameter memory */
+ if (reg < 0x400)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(adau17x1_precious_register);
+
+bool adau17x1_readable_register(struct device *dev, unsigned int reg)
+{
+ /* SigmaDSP parameter memory */
+ if (reg < 0x400)
+ return true;
+
+ switch (reg) {
+ case ADAU17X1_CLOCK_CONTROL:
+ case ADAU17X1_PLL_CONTROL:
+ case ADAU17X1_REC_POWER_MGMT:
+ case ADAU17X1_MICBIAS:
+ case ADAU17X1_SERIAL_PORT0:
+ case ADAU17X1_SERIAL_PORT1:
+ case ADAU17X1_CONVERTER0:
+ case ADAU17X1_CONVERTER1:
+ case ADAU17X1_LEFT_INPUT_DIGITAL_VOL:
+ case ADAU17X1_RIGHT_INPUT_DIGITAL_VOL:
+ case ADAU17X1_ADC_CONTROL:
+ case ADAU17X1_PLAY_POWER_MGMT:
+ case ADAU17X1_DAC_CONTROL0:
+ case ADAU17X1_DAC_CONTROL1:
+ case ADAU17X1_DAC_CONTROL2:
+ case ADAU17X1_SERIAL_PORT_PAD:
+ case ADAU17X1_CONTROL_PORT_PAD0:
+ case ADAU17X1_CONTROL_PORT_PAD1:
+ case ADAU17X1_DSP_SAMPLING_RATE:
+ case ADAU17X1_SERIAL_INPUT_ROUTE:
+ case ADAU17X1_SERIAL_OUTPUT_ROUTE:
+ case ADAU17X1_DSP_ENABLE:
+ case ADAU17X1_DSP_RUN:
+ case ADAU17X1_SERIAL_SAMPLING_RATE:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+EXPORT_SYMBOL_GPL(adau17x1_readable_register);
+
+bool adau17x1_volatile_register(struct device *dev, unsigned int reg)
+{
+ /* SigmaDSP parameter and program memory */
+ if (reg < 0x4000)
+ return true;
+
+ switch (reg) {
+ /* The PLL register is 6 bytes long */
+ case ADAU17X1_PLL_CONTROL:
+ case ADAU17X1_PLL_CONTROL + 1:
+ case ADAU17X1_PLL_CONTROL + 2:
+ case ADAU17X1_PLL_CONTROL + 3:
+ case ADAU17X1_PLL_CONTROL + 4:
+ case ADAU17X1_PLL_CONTROL + 5:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(adau17x1_volatile_register);
+
+int adau17x1_setup_firmware(struct adau *adau, unsigned int rate)
+{
+ int ret;
+ int dspsr;
+
+ ret = regmap_read(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, &dspsr);
+ if (ret)
+ return ret;
+
+ regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 1);
+ regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, 0xf);
+
+ ret = sigmadsp_setup(adau->sigmadsp, rate);
+ if (ret) {
+ regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 0);
+ return ret;
+ }
+ regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dspsr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_setup_firmware);
+
+int adau17x1_add_widgets(struct snd_soc_codec *codec)
+{
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = snd_soc_add_codec_controls(codec, adau17x1_controls,
+ ARRAY_SIZE(adau17x1_controls));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_new_controls(&codec->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,
+ ARRAY_SIZE(adau17x1_dsp_dapm_widgets));
+ if (ret)
+ return ret;
+
+ if (!adau->sigmadsp)
+ return 0;
+
+ ret = sigmadsp_attach(adau->sigmadsp, &codec->component);
+ if (ret) {
+ dev_err(codec->dev, "Failed to attach firmware: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_add_widgets);
+
+int adau17x1_add_routes(struct snd_soc_codec *codec)
+{
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = snd_soc_dapm_add_routes(&codec->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,
+ ARRAY_SIZE(adau17x1_dsp_dapm_routes));
+ } else {
+ ret = snd_soc_dapm_add_routes(&codec->dapm,
+ adau17x1_no_dsp_dapm_routes,
+ ARRAY_SIZE(adau17x1_no_dsp_dapm_routes));
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(adau17x1_add_routes);
+
+int adau17x1_resume(struct snd_soc_codec *codec)
+{
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+ if (adau->switch_mode)
+ adau->switch_mode(codec->dev);
+
+ regcache_sync(adau->regmap);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_resume);
+
+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;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ adau = devm_kzalloc(dev, sizeof(*adau), GFP_KERNEL);
+ if (!adau)
+ return -ENOMEM;
+
+ adau->regmap = regmap;
+ adau->switch_mode = switch_mode;
+ adau->type = type;
+
+ dev_set_drvdata(dev, adau);
+
+ if (firmware_name) {
+ adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, NULL,
+ firmware_name);
+ if (IS_ERR(adau->sigmadsp)) {
+ dev_warn(dev, "Could not find firmware file: %ld\n",
+ PTR_ERR(adau->sigmadsp));
+ adau->sigmadsp = NULL;
+ }
+ }
+
+ if (switch_mode)
+ switch_mode(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_probe);
+
+MODULE_DESCRIPTION("ASoC ADAU1X61/ADAU1X81 common code");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adau17x1.h b/kernel/sound/soc/codecs/adau17x1.h
new file mode 100644
index 000000000..e13583e6f
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau17x1.h
@@ -0,0 +1,127 @@
+#ifndef __ADAU17X1_H__
+#define __ADAU17X1_H__
+
+#include <linux/regmap.h>
+#include <linux/platform_data/adau17x1.h>
+
+#include "sigmadsp.h"
+
+enum adau17x1_type {
+ ADAU1361,
+ ADAU1761,
+ ADAU1381,
+ ADAU1781,
+};
+
+enum adau17x1_pll {
+ ADAU17X1_PLL,
+};
+
+enum adau17x1_pll_src {
+ ADAU17X1_PLL_SRC_MCLK,
+};
+
+enum adau17x1_clk_src {
+ ADAU17X1_CLK_SRC_MCLK,
+ ADAU17X1_CLK_SRC_PLL,
+};
+
+struct adau {
+ unsigned int sysclk;
+ unsigned int pll_freq;
+
+ enum adau17x1_clk_src clk_src;
+ enum adau17x1_type type;
+ void (*switch_mode)(struct device *dev);
+
+ unsigned int dai_fmt;
+
+ uint8_t pll_regs[6];
+
+ bool master;
+
+ unsigned int tdm_slot[2];
+ bool dsp_bypass[2];
+
+ struct regmap *regmap;
+ struct sigmadsp *sigmadsp;
+};
+
+int adau17x1_add_widgets(struct snd_soc_codec *codec);
+int adau17x1_add_routes(struct snd_soc_codec *codec);
+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,
+ 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);
+
+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
+#define ADAU17X1_MICBIAS 0x4010
+#define ADAU17X1_SERIAL_PORT0 0x4015
+#define ADAU17X1_SERIAL_PORT1 0x4016
+#define ADAU17X1_CONVERTER0 0x4017
+#define ADAU17X1_CONVERTER1 0x4018
+#define ADAU17X1_LEFT_INPUT_DIGITAL_VOL 0x401a
+#define ADAU17X1_RIGHT_INPUT_DIGITAL_VOL 0x401b
+#define ADAU17X1_ADC_CONTROL 0x4019
+#define ADAU17X1_PLAY_POWER_MGMT 0x4029
+#define ADAU17X1_DAC_CONTROL0 0x402a
+#define ADAU17X1_DAC_CONTROL1 0x402b
+#define ADAU17X1_DAC_CONTROL2 0x402c
+#define ADAU17X1_SERIAL_PORT_PAD 0x402d
+#define ADAU17X1_CONTROL_PORT_PAD0 0x402f
+#define ADAU17X1_CONTROL_PORT_PAD1 0x4030
+#define ADAU17X1_DSP_SAMPLING_RATE 0x40eb
+#define ADAU17X1_SERIAL_INPUT_ROUTE 0x40f2
+#define ADAU17X1_SERIAL_OUTPUT_ROUTE 0x40f3
+#define ADAU17X1_DSP_ENABLE 0x40f5
+#define ADAU17X1_DSP_RUN 0x40f6
+#define ADAU17X1_SERIAL_SAMPLING_RATE 0x40f8
+
+#define ADAU17X1_SERIAL_PORT0_BCLK_POL BIT(4)
+#define ADAU17X1_SERIAL_PORT0_LRCLK_POL BIT(3)
+#define ADAU17X1_SERIAL_PORT0_MASTER BIT(0)
+
+#define ADAU17X1_SERIAL_PORT1_DELAY1 0x00
+#define ADAU17X1_SERIAL_PORT1_DELAY0 0x01
+#define ADAU17X1_SERIAL_PORT1_DELAY8 0x02
+#define ADAU17X1_SERIAL_PORT1_DELAY16 0x03
+#define ADAU17X1_SERIAL_PORT1_DELAY_MASK 0x03
+
+#define ADAU17X1_CLOCK_CONTROL_INFREQ_MASK 0x6
+#define ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL BIT(3)
+#define ADAU17X1_CLOCK_CONTROL_SYSCLK_EN BIT(0)
+
+#define ADAU17X1_SERIAL_PORT1_BCLK32 (0x0 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK48 (0x1 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK64 (0x2 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK128 (0x3 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK256 (0x4 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK_MASK (0x7 << 5)
+
+#define ADAU17X1_SERIAL_PORT0_STEREO (0x0 << 1)
+#define ADAU17X1_SERIAL_PORT0_TDM4 (0x1 << 1)
+#define ADAU17X1_SERIAL_PORT0_TDM8 (0x2 << 1)
+#define ADAU17X1_SERIAL_PORT0_TDM_MASK (0x3 << 1)
+#define ADAU17X1_SERIAL_PORT0_PULSE_MODE BIT(5)
+
+#define ADAU17X1_CONVERTER0_DAC_PAIR(x) (((x) - 1) << 5)
+#define ADAU17X1_CONVERTER0_DAC_PAIR_MASK (0x3 << 5)
+#define ADAU17X1_CONVERTER1_ADC_PAIR(x) ((x) - 1)
+#define ADAU17X1_CONVERTER1_ADC_PAIR_MASK 0x3
+
+#define ADAU17X1_CONVERTER0_CONVSR_MASK 0x7
+
+
+#endif
diff --git a/kernel/sound/soc/codecs/adau1977-i2c.c b/kernel/sound/soc/codecs/adau1977-i2c.c
new file mode 100644
index 000000000..9700e8c83
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1977-i2c.c
@@ -0,0 +1,59 @@
+/*
+ * 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>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "adau1977.h"
+
+static int adau1977_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regmap_config config;
+
+ config = adau1977_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 8;
+
+ return adau1977_probe(&client->dev,
+ devm_regmap_init_i2c(client, &config),
+ id->driver_data, NULL);
+}
+
+static int adau1977_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id adau1977_i2c_ids[] = {
+ { "adau1977", ADAU1977 },
+ { "adau1978", ADAU1978 },
+ { "adau1979", ADAU1978 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids);
+
+static struct i2c_driver adau1977_i2c_driver = {
+ .driver = {
+ .name = "adau1977",
+ .owner = THIS_MODULE,
+ },
+ .probe = adau1977_i2c_probe,
+ .remove = adau1977_i2c_remove,
+ .id_table = adau1977_i2c_ids,
+};
+module_i2c_driver(adau1977_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adau1977-spi.c b/kernel/sound/soc/codecs/adau1977-spi.c
new file mode 100644
index 000000000..b05cf5da3
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1977-spi.c
@@ -0,0 +1,76 @@
+/*
+ * 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/spi/spi.h>
+#include <sound/soc.h>
+
+#include "adau1977.h"
+
+static void adau1977_spi_switch_mode(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ /*
+ * To get the device into SPI mode CLATCH has to be pulled low three
+ * times. Do this by issuing three dummy reads.
+ */
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+ spi_w8r8(spi, 0x00);
+}
+
+static int adau1977_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct regmap_config config;
+
+ if (!id)
+ return -EINVAL;
+
+ config = adau1977_regmap_config;
+ config.val_bits = 8;
+ config.reg_bits = 16;
+ config.read_flag_mask = 0x1;
+
+ return adau1977_probe(&spi->dev,
+ devm_regmap_init_spi(spi, &config),
+ id->driver_data, adau1977_spi_switch_mode);
+}
+
+static 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 },
+ { "adau1979", ADAU1978 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adau1977_spi_ids);
+
+static struct spi_driver adau1977_spi_driver = {
+ .driver = {
+ .name = "adau1977",
+ .owner = THIS_MODULE,
+ },
+ .probe = adau1977_spi_probe,
+ .remove = adau1977_spi_remove,
+ .id_table = adau1977_spi_ids,
+};
+module_spi_driver(adau1977_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adau1977.c b/kernel/sound/soc/codecs/adau1977.c
new file mode 100644
index 000000000..7ad8e156e
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1977.c
@@ -0,0 +1,1011 @@
+/*
+ * 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>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#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>
+
+#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 "adau1977.h"
+
+#define ADAU1977_REG_POWER 0x00
+#define ADAU1977_REG_PLL 0x01
+#define ADAU1977_REG_BOOST 0x02
+#define ADAU1977_REG_MICBIAS 0x03
+#define ADAU1977_REG_BLOCK_POWER_SAI 0x04
+#define ADAU1977_REG_SAI_CTRL0 0x05
+#define ADAU1977_REG_SAI_CTRL1 0x06
+#define ADAU1977_REG_CMAP12 0x07
+#define ADAU1977_REG_CMAP34 0x08
+#define ADAU1977_REG_SAI_OVERTEMP 0x09
+#define ADAU1977_REG_POST_ADC_GAIN(x) (0x0a + (x))
+#define ADAU1977_REG_MISC_CONTROL 0x0e
+#define ADAU1977_REG_DIAG_CONTROL 0x10
+#define ADAU1977_REG_STATUS(x) (0x11 + (x))
+#define ADAU1977_REG_DIAG_IRQ1 0x15
+#define ADAU1977_REG_DIAG_IRQ2 0x16
+#define ADAU1977_REG_ADJUST1 0x17
+#define ADAU1977_REG_ADJUST2 0x18
+#define ADAU1977_REG_ADC_CLIP 0x19
+#define ADAU1977_REG_DC_HPF_CAL 0x1a
+
+#define ADAU1977_POWER_RESET BIT(7)
+#define ADAU1977_POWER_PWUP BIT(0)
+
+#define ADAU1977_PLL_CLK_S BIT(4)
+#define ADAU1977_PLL_MCS_MASK 0x7
+
+#define ADAU1977_MICBIAS_MB_VOLTS_MASK 0xf0
+#define ADAU1977_MICBIAS_MB_VOLTS_OFFSET 4
+
+#define ADAU1977_BLOCK_POWER_SAI_LR_POL BIT(7)
+#define ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE BIT(6)
+#define ADAU1977_BLOCK_POWER_SAI_LDO_EN BIT(5)
+
+#define ADAU1977_SAI_CTRL0_FMT_MASK (0x3 << 6)
+#define ADAU1977_SAI_CTRL0_FMT_I2S (0x0 << 6)
+#define ADAU1977_SAI_CTRL0_FMT_LJ (0x1 << 6)
+#define ADAU1977_SAI_CTRL0_FMT_RJ_24BIT (0x2 << 6)
+#define ADAU1977_SAI_CTRL0_FMT_RJ_16BIT (0x3 << 6)
+
+#define ADAU1977_SAI_CTRL0_SAI_MASK (0x7 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_I2S (0x0 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_TDM_2 (0x1 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_TDM_4 (0x2 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_TDM_8 (0x3 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_TDM_16 (0x4 << 3)
+
+#define ADAU1977_SAI_CTRL0_FS_MASK (0x7)
+#define ADAU1977_SAI_CTRL0_FS_8000_12000 (0x0)
+#define ADAU1977_SAI_CTRL0_FS_16000_24000 (0x1)
+#define ADAU1977_SAI_CTRL0_FS_32000_48000 (0x2)
+#define ADAU1977_SAI_CTRL0_FS_64000_96000 (0x3)
+#define ADAU1977_SAI_CTRL0_FS_128000_192000 (0x4)
+
+#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_MASK (0x3 << 5)
+#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_32 (0x0 << 5)
+#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_24 (0x1 << 5)
+#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_16 (0x2 << 5)
+#define ADAU1977_SAI_CTRL1_DATA_WIDTH_MASK (0x1 << 4)
+#define ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT (0x1 << 4)
+#define ADAU1977_SAI_CTRL1_DATA_WIDTH_24BIT (0x0 << 4)
+#define ADAU1977_SAI_CTRL1_LRCLK_PULSE BIT(3)
+#define ADAU1977_SAI_CTRL1_MSB BIT(2)
+#define ADAU1977_SAI_CTRL1_BCLKRATE_16 (0x1 << 1)
+#define ADAU1977_SAI_CTRL1_BCLKRATE_32 (0x0 << 1)
+#define ADAU1977_SAI_CTRL1_BCLKRATE_MASK (0x1 << 1)
+#define ADAU1977_SAI_CTRL1_MASTER BIT(0)
+
+#define ADAU1977_SAI_OVERTEMP_DRV_C(x) BIT(4 + (x))
+#define ADAU1977_SAI_OVERTEMP_DRV_HIZ BIT(3)
+
+#define ADAU1977_MISC_CONTROL_SUM_MODE_MASK (0x3 << 6)
+#define ADAU1977_MISC_CONTROL_SUM_MODE_1CH (0x2 << 6)
+#define ADAU1977_MISC_CONTROL_SUM_MODE_2CH (0x1 << 6)
+#define ADAU1977_MISC_CONTROL_SUM_MODE_4CH (0x0 << 6)
+#define ADAU1977_MISC_CONTROL_MMUTE BIT(4)
+#define ADAU1977_MISC_CONTROL_DC_CAL BIT(0)
+
+#define ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET 4
+#define ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET 0
+
+struct adau1977 {
+ struct regmap *regmap;
+ bool right_j;
+ unsigned int sysclk;
+ enum adau1977_sysclk_src sysclk_src;
+ struct gpio_desc *reset_gpio;
+ enum adau1977_type type;
+
+ struct regulator *avdd_reg;
+ struct regulator *dvdd_reg;
+
+ struct snd_pcm_hw_constraint_list constraints;
+
+ struct device *dev;
+ void (*switch_mode)(struct device *dev);
+
+ unsigned int max_master_fs;
+ unsigned int slot_width;
+ bool enabled;
+ bool master;
+};
+
+static const struct reg_default adau1977_reg_defaults[] = {
+ { 0x00, 0x00 },
+ { 0x01, 0x41 },
+ { 0x02, 0x4a },
+ { 0x03, 0x7d },
+ { 0x04, 0x3d },
+ { 0x05, 0x02 },
+ { 0x06, 0x00 },
+ { 0x07, 0x10 },
+ { 0x08, 0x32 },
+ { 0x09, 0xf0 },
+ { 0x0a, 0xa0 },
+ { 0x0b, 0xa0 },
+ { 0x0c, 0xa0 },
+ { 0x0d, 0xa0 },
+ { 0x0e, 0x02 },
+ { 0x10, 0x0f },
+ { 0x15, 0x20 },
+ { 0x16, 0x00 },
+ { 0x17, 0x00 },
+ { 0x18, 0x00 },
+ { 0x1a, 0x00 },
+};
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(adau1977_adc_gain, -3562, 6000);
+
+static const struct snd_soc_dapm_widget adau1977_micbias_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("MICBIAS", ADAU1977_REG_MICBIAS,
+ 3, 0, NULL, 0)
+};
+
+static const struct snd_soc_dapm_widget adau1977_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Vref", ADAU1977_REG_BLOCK_POWER_SAI,
+ 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("ADC1", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 0, 0),
+ SND_SOC_DAPM_ADC("ADC2", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 1, 0),
+ SND_SOC_DAPM_ADC("ADC3", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 2, 0),
+ SND_SOC_DAPM_ADC("ADC4", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 3, 0),
+
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("AIN3"),
+ SND_SOC_DAPM_INPUT("AIN4"),
+
+ SND_SOC_DAPM_OUTPUT("VREF"),
+};
+
+static const struct snd_soc_dapm_route adau1977_dapm_routes[] = {
+ { "ADC1", NULL, "AIN1" },
+ { "ADC2", NULL, "AIN2" },
+ { "ADC3", NULL, "AIN3" },
+ { "ADC4", NULL, "AIN4" },
+
+ { "ADC1", NULL, "Vref" },
+ { "ADC2", NULL, "Vref" },
+ { "ADC3", NULL, "Vref" },
+ { "ADC4", NULL, "Vref" },
+
+ { "VREF", NULL, "Vref" },
+};
+
+#define ADAU1977_VOLUME(x) \
+ SOC_SINGLE_TLV("ADC" #x " Capture Volume", \
+ ADAU1977_REG_POST_ADC_GAIN((x) - 1), \
+ 0, 255, 1, adau1977_adc_gain)
+
+#define ADAU1977_HPF_SWITCH(x) \
+ SOC_SINGLE("ADC" #x " Highpass-Filter Capture Switch", \
+ ADAU1977_REG_DC_HPF_CAL, (x) - 1, 1, 0)
+
+#define ADAU1977_DC_SUB_SWITCH(x) \
+ SOC_SINGLE("ADC" #x " DC Substraction Capture Switch", \
+ ADAU1977_REG_DC_HPF_CAL, (x) + 3, 1, 0)
+
+static const struct snd_kcontrol_new adau1977_snd_controls[] = {
+ ADAU1977_VOLUME(1),
+ ADAU1977_VOLUME(2),
+ ADAU1977_VOLUME(3),
+ ADAU1977_VOLUME(4),
+
+ ADAU1977_HPF_SWITCH(1),
+ ADAU1977_HPF_SWITCH(2),
+ ADAU1977_HPF_SWITCH(3),
+ ADAU1977_HPF_SWITCH(4),
+
+ ADAU1977_DC_SUB_SWITCH(1),
+ ADAU1977_DC_SUB_SWITCH(2),
+ ADAU1977_DC_SUB_SWITCH(3),
+ ADAU1977_DC_SUB_SWITCH(4),
+};
+
+static int adau1977_reset(struct adau1977 *adau1977)
+{
+ int ret;
+
+ /*
+ * The reset bit is obviously volatile, but we need to be able to cache
+ * the other bits in the register, so we can't just mark the whole
+ * register as volatile. Since this is the only place where we'll ever
+ * touch the reset bit just bypass the cache for this operation.
+ */
+ regcache_cache_bypass(adau1977->regmap, true);
+ ret = regmap_write(adau1977->regmap, ADAU1977_REG_POWER,
+ ADAU1977_POWER_RESET);
+ regcache_cache_bypass(adau1977->regmap, false);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/*
+ * Returns the appropriate setting for ths FS field in the CTRL0 register
+ * depending on the rate.
+ */
+static int adau1977_lookup_fs(unsigned int rate)
+{
+ if (rate >= 8000 && rate <= 12000)
+ return ADAU1977_SAI_CTRL0_FS_8000_12000;
+ else if (rate >= 16000 && rate <= 24000)
+ return ADAU1977_SAI_CTRL0_FS_16000_24000;
+ else if (rate >= 32000 && rate <= 48000)
+ return ADAU1977_SAI_CTRL0_FS_32000_48000;
+ else if (rate >= 64000 && rate <= 96000)
+ return ADAU1977_SAI_CTRL0_FS_64000_96000;
+ else if (rate >= 128000 && rate <= 192000)
+ return ADAU1977_SAI_CTRL0_FS_128000_192000;
+ else
+ return -EINVAL;
+}
+
+static int adau1977_lookup_mcs(struct adau1977 *adau1977, unsigned int rate,
+ unsigned int fs)
+{
+ unsigned int mcs;
+
+ /*
+ * rate = sysclk / (512 * mcs_lut[mcs]) * 2**fs
+ * => mcs_lut[mcs] = sysclk / (512 * rate) * 2**fs
+ * => mcs_lut[mcs] = sysclk / ((512 / 2**fs) * rate)
+ */
+
+ rate *= 512 >> fs;
+
+ if (adau1977->sysclk % rate != 0)
+ return -EINVAL;
+
+ mcs = adau1977->sysclk / rate;
+
+ /* The factors configured by MCS are 1, 2, 3, 4, 6 */
+ if (mcs < 1 || mcs > 6 || mcs == 5)
+ return -EINVAL;
+
+ mcs = mcs - 1;
+ if (mcs == 5)
+ mcs = 4;
+
+ return mcs;
+}
+
+static int adau1977_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ unsigned int rate = params_rate(params);
+ unsigned int slot_width;
+ unsigned int ctrl0, ctrl0_mask;
+ unsigned int ctrl1;
+ int mcs, fs;
+ int ret;
+
+ fs = adau1977_lookup_fs(rate);
+ if (fs < 0)
+ return fs;
+
+ if (adau1977->sysclk_src == ADAU1977_SYSCLK_SRC_MCLK) {
+ mcs = adau1977_lookup_mcs(adau1977, rate, fs);
+ if (mcs < 0)
+ return mcs;
+ } else {
+ mcs = 0;
+ }
+
+ ctrl0_mask = ADAU1977_SAI_CTRL0_FS_MASK;
+ ctrl0 = fs;
+
+ if (adau1977->right_j) {
+ switch (params_width(params)) {
+ case 16:
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_16BIT;
+ break;
+ case 24:
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_24BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ctrl0_mask |= ADAU1977_SAI_CTRL0_FMT_MASK;
+ }
+
+ if (adau1977->master) {
+ switch (params_width(params)) {
+ case 16:
+ ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT;
+ slot_width = 16;
+ break;
+ case 24:
+ case 32:
+ ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_24BIT;
+ slot_width = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* In TDM mode there is a fixed slot width */
+ if (adau1977->slot_width)
+ slot_width = adau1977->slot_width;
+
+ if (slot_width == 16)
+ ctrl1 |= ADAU1977_SAI_CTRL1_BCLKRATE_16;
+ else
+ ctrl1 |= ADAU1977_SAI_CTRL1_BCLKRATE_32;
+
+ ret = regmap_update_bits(adau1977->regmap,
+ ADAU1977_REG_SAI_CTRL1,
+ ADAU1977_SAI_CTRL1_DATA_WIDTH_MASK |
+ ADAU1977_SAI_CTRL1_BCLKRATE_MASK,
+ ctrl1);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0,
+ ctrl0_mask, ctrl0);
+ if (ret < 0)
+ return ret;
+
+ return regmap_update_bits(adau1977->regmap, ADAU1977_REG_PLL,
+ ADAU1977_PLL_MCS_MASK, mcs);
+}
+
+static int adau1977_power_disable(struct adau1977 *adau1977)
+{
+ int ret = 0;
+
+ if (!adau1977->enabled)
+ return 0;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_POWER,
+ ADAU1977_POWER_PWUP, 0);
+ if (ret)
+ return ret;
+
+ regcache_mark_dirty(adau1977->regmap);
+
+ if (adau1977->reset_gpio)
+ gpiod_set_value_cansleep(adau1977->reset_gpio, 0);
+
+ regcache_cache_only(adau1977->regmap, true);
+
+ regulator_disable(adau1977->avdd_reg);
+ if (adau1977->dvdd_reg)
+ regulator_disable(adau1977->dvdd_reg);
+
+ adau1977->enabled = false;
+
+ return 0;
+}
+
+static int adau1977_power_enable(struct adau1977 *adau1977)
+{
+ unsigned int val;
+ int ret = 0;
+
+ if (adau1977->enabled)
+ return 0;
+
+ ret = regulator_enable(adau1977->avdd_reg);
+ if (ret)
+ return ret;
+
+ if (adau1977->dvdd_reg) {
+ ret = regulator_enable(adau1977->dvdd_reg);
+ if (ret)
+ goto err_disable_avdd;
+ }
+
+ if (adau1977->reset_gpio)
+ gpiod_set_value_cansleep(adau1977->reset_gpio, 1);
+
+ regcache_cache_only(adau1977->regmap, false);
+
+ if (adau1977->switch_mode)
+ adau1977->switch_mode(adau1977->dev);
+
+ ret = adau1977_reset(adau1977);
+ if (ret)
+ goto err_disable_dvdd;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_POWER,
+ ADAU1977_POWER_PWUP, ADAU1977_POWER_PWUP);
+ if (ret)
+ goto err_disable_dvdd;
+
+ ret = regcache_sync(adau1977->regmap);
+ if (ret)
+ goto err_disable_dvdd;
+
+ /*
+ * The PLL register is not affected by the software reset. It is
+ * possible that the value of the register was changed to the
+ * default value while we were in cache only mode. In this case
+ * regcache_sync will skip over it and we have to manually sync
+ * it.
+ */
+ ret = regmap_read(adau1977->regmap, ADAU1977_REG_PLL, &val);
+ if (ret)
+ goto err_disable_dvdd;
+
+ if (val == 0x41) {
+ regcache_cache_bypass(adau1977->regmap, true);
+ ret = regmap_write(adau1977->regmap, ADAU1977_REG_PLL,
+ 0x41);
+ if (ret)
+ goto err_disable_dvdd;
+ regcache_cache_bypass(adau1977->regmap, false);
+ }
+
+ adau1977->enabled = true;
+
+ return ret;
+
+err_disable_dvdd:
+ if (adau1977->dvdd_reg)
+ regulator_disable(adau1977->dvdd_reg);
+err_disable_avdd:
+ regulator_disable(adau1977->avdd_reg);
+ return ret;
+}
+
+static int adau1977_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ ret = adau1977_power_enable(adau1977);
+ break;
+ case SND_SOC_BIAS_OFF:
+ ret = adau1977_power_disable(adau1977);
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+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);
+ unsigned int ctrl0, ctrl1, drv;
+ unsigned int slot[4];
+ unsigned int i;
+ int ret;
+
+ if (slots == 0) {
+ /* 0 = No fixed slot width */
+ adau1977->slot_width = 0;
+ adau1977->max_master_fs = 192000;
+ return regmap_update_bits(adau1977->regmap,
+ ADAU1977_REG_SAI_CTRL0, ADAU1977_SAI_CTRL0_SAI_MASK,
+ ADAU1977_SAI_CTRL0_SAI_I2S);
+ }
+
+ if (rx_mask == 0 || tx_mask != 0)
+ return -EINVAL;
+
+ drv = 0;
+ for (i = 0; i < 4; i++) {
+ slot[i] = __ffs(rx_mask);
+ drv |= ADAU1977_SAI_OVERTEMP_DRV_C(i);
+ rx_mask &= ~(1 << slot[i]);
+ if (slot[i] >= slots)
+ return -EINVAL;
+ if (rx_mask == 0)
+ break;
+ }
+
+ if (rx_mask != 0)
+ return -EINVAL;
+
+ switch (width) {
+ case 16:
+ ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_16;
+ break;
+ case 24:
+ /* We can only generate 16 bit or 32 bit wide slots */
+ if (adau1977->master)
+ return -EINVAL;
+ ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_24;
+ break;
+ case 32:
+ ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slots) {
+ case 2:
+ ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_2;
+ break;
+ case 4:
+ ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_4;
+ break;
+ case 8:
+ ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_8;
+ break;
+ case 16:
+ ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_OVERTEMP,
+ ADAU1977_SAI_OVERTEMP_DRV_C(0) |
+ ADAU1977_SAI_OVERTEMP_DRV_C(1) |
+ ADAU1977_SAI_OVERTEMP_DRV_C(2) |
+ ADAU1977_SAI_OVERTEMP_DRV_C(3), drv);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(adau1977->regmap, ADAU1977_REG_CMAP12,
+ (slot[1] << ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET) |
+ (slot[0] << ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(adau1977->regmap, ADAU1977_REG_CMAP34,
+ (slot[3] << ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET) |
+ (slot[2] << ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0,
+ ADAU1977_SAI_CTRL0_SAI_MASK, ctrl0);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL1,
+ ADAU1977_SAI_CTRL1_SLOT_WIDTH_MASK, ctrl1);
+ if (ret)
+ return ret;
+
+ adau1977->slot_width = width;
+
+ /* In master mode the maximum bitclock is 24.576 MHz */
+ adau1977->max_master_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);
+ unsigned int val;
+
+ if (mute)
+ val = ADAU1977_MISC_CONTROL_MMUTE;
+ else
+ val = 0;
+
+ return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MISC_CONTROL,
+ ADAU1977_MISC_CONTROL_MMUTE, val);
+}
+
+static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ 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;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl1 |= ADAU1977_SAI_CTRL1_MASTER;
+ adau1977->master = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ invert_lrclk = false;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ block_power |= ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE;
+ invert_lrclk = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert_lrclk = true;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ block_power |= ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE;
+ invert_lrclk = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adau1977->right_j = false;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_LJ;
+ invert_lrclk = !invert_lrclk;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_24BIT;
+ adau1977->right_j = true;
+ invert_lrclk = !invert_lrclk;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1 |= ADAU1977_SAI_CTRL1_LRCLK_PULSE;
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_I2S;
+ invert_lrclk = false;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1 |= ADAU1977_SAI_CTRL1_LRCLK_PULSE;
+ ctrl0 |= ADAU1977_SAI_CTRL0_FMT_LJ;
+ invert_lrclk = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (invert_lrclk)
+ block_power |= ADAU1977_BLOCK_POWER_SAI_LR_POL;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI,
+ ADAU1977_BLOCK_POWER_SAI_LR_POL |
+ ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE, block_power);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0,
+ ADAU1977_SAI_CTRL0_FMT_MASK,
+ ctrl0);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL1,
+ ADAU1977_SAI_CTRL1_MASTER | ADAU1977_SAI_CTRL1_LRCLK_PULSE,
+ ctrl1);
+}
+
+static int adau1977_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ u64 formats = 0;
+
+ if (adau1977->slot_width == 16)
+ formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE;
+ else if (adau1977->right_j || adau1977->slot_width == 24)
+ formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE;
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &adau1977->constraints);
+
+ if (adau1977->master)
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 8000, adau1977->max_master_fs);
+
+ if (formats != 0)
+ snd_pcm_hw_constraint_mask64(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FORMAT, formats);
+
+ return 0;
+}
+
+static int adau1977_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int val;
+
+ if (tristate)
+ val = ADAU1977_SAI_OVERTEMP_DRV_HIZ;
+ else
+ val = 0;
+
+ return regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_OVERTEMP,
+ ADAU1977_SAI_OVERTEMP_DRV_HIZ, val);
+}
+
+static const struct snd_soc_dai_ops adau1977_dai_ops = {
+ .startup = adau1977_startup,
+ .hw_params = adau1977_hw_params,
+ .mute_stream = adau1977_mute,
+ .set_fmt = adau1977_set_dai_fmt,
+ .set_tdm_slot = adau1977_set_tdm_slot,
+ .set_tristate = adau1977_set_tristate,
+};
+
+static struct snd_soc_dai_driver adau1977_dai = {
+ .name = "adau1977-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 24,
+ },
+ .ops = &adau1977_dai_ops,
+};
+
+static const unsigned int adau1977_rates[] = {
+ 8000, 16000, 32000, 64000, 128000,
+ 11025, 22050, 44100, 88200, 172400,
+ 12000, 24000, 48000, 96000, 192000,
+};
+
+#define ADAU1977_RATE_CONSTRAINT_MASK_32000 0x001f
+#define ADAU1977_RATE_CONSTRAINT_MASK_44100 0x03e0
+#define ADAU1977_RATE_CONSTRAINT_MASK_48000 0x7c00
+/* All rates >= 32000 */
+#define ADAU1977_RATE_CONSTRAINT_MASK_LRCLK 0x739c
+
+static bool adau1977_check_sysclk(unsigned int mclk, unsigned int base_freq)
+{
+ unsigned int mcs;
+
+ if (mclk % (base_freq * 128) != 0)
+ return false;
+
+ mcs = mclk / (128 * base_freq);
+ if (mcs < 1 || mcs > 6 || mcs == 5)
+ return false;
+
+ return true;
+}
+
+static int adau1977_set_sysclk(struct snd_soc_codec *codec,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ unsigned int mask = 0;
+ unsigned int clk_src;
+ unsigned int ret;
+
+ if (dir != SND_SOC_CLOCK_IN)
+ return -EINVAL;
+
+ if (clk_id != ADAU1977_SYSCLK)
+ return -EINVAL;
+
+ switch (source) {
+ case ADAU1977_SYSCLK_SRC_MCLK:
+ clk_src = 0;
+ break;
+ case ADAU1977_SYSCLK_SRC_LRCLK:
+ clk_src = ADAU1977_PLL_CLK_S;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (freq != 0 && source == ADAU1977_SYSCLK_SRC_MCLK) {
+ if (freq < 4000000 || freq > 36864000)
+ return -EINVAL;
+
+ if (adau1977_check_sysclk(freq, 32000))
+ mask |= ADAU1977_RATE_CONSTRAINT_MASK_32000;
+ if (adau1977_check_sysclk(freq, 44100))
+ mask |= ADAU1977_RATE_CONSTRAINT_MASK_44100;
+ if (adau1977_check_sysclk(freq, 48000))
+ mask |= ADAU1977_RATE_CONSTRAINT_MASK_48000;
+
+ if (mask == 0)
+ return -EINVAL;
+ } else if (source == ADAU1977_SYSCLK_SRC_LRCLK) {
+ mask = ADAU1977_RATE_CONSTRAINT_MASK_LRCLK;
+ }
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_PLL,
+ ADAU1977_PLL_CLK_S, clk_src);
+ if (ret)
+ return ret;
+
+ adau1977->constraints.mask = mask;
+ adau1977->sysclk_src = source;
+ adau1977->sysclk = freq;
+
+ return 0;
+}
+
+static int adau1977_codec_probe(struct snd_soc_codec *codec)
+{
+ struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (adau1977->type) {
+ case ADAU1977:
+ ret = snd_soc_dapm_new_controls(&codec->dapm,
+ adau1977_micbias_dapm_widgets,
+ ARRAY_SIZE(adau1977_micbias_dapm_widgets));
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ 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 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 {
+ micbias = ADAU1977_MICBIAS_8V5;
+ }
+
+ return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MICBIAS,
+ ADAU1977_MICBIAS_MB_VOLTS_MASK,
+ micbias << ADAU1977_MICBIAS_MB_VOLTS_OFFSET);
+}
+
+int adau1977_probe(struct device *dev, struct regmap *regmap,
+ enum adau1977_type type, void (*switch_mode)(struct device *dev))
+{
+ unsigned int power_off_mask;
+ struct adau1977 *adau1977;
+ int ret;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ adau1977 = devm_kzalloc(dev, sizeof(*adau1977), GFP_KERNEL);
+ if (adau1977 == NULL)
+ return -ENOMEM;
+
+ adau1977->dev = dev;
+ adau1977->type = type;
+ adau1977->regmap = regmap;
+ adau1977->switch_mode = switch_mode;
+ adau1977->max_master_fs = 192000;
+
+ adau1977->constraints.list = adau1977_rates;
+ adau1977->constraints.count = ARRAY_SIZE(adau1977_rates);
+
+ adau1977->avdd_reg = devm_regulator_get(dev, "AVDD");
+ if (IS_ERR(adau1977->avdd_reg))
+ return PTR_ERR(adau1977->avdd_reg);
+
+ adau1977->dvdd_reg = devm_regulator_get_optional(dev, "DVDD");
+ if (IS_ERR(adau1977->dvdd_reg)) {
+ if (PTR_ERR(adau1977->dvdd_reg) != -ENODEV)
+ return PTR_ERR(adau1977->dvdd_reg);
+ adau1977->dvdd_reg = NULL;
+ }
+
+ adau1977->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(adau1977->reset_gpio))
+ return PTR_ERR(adau1977->reset_gpio);
+
+ dev_set_drvdata(dev, adau1977);
+
+ if (adau1977->reset_gpio)
+ ndelay(100);
+
+ ret = adau1977_power_enable(adau1977);
+ if (ret)
+ return ret;
+
+ if (type == ADAU1977) {
+ ret = adau1977_setup_micbias(adau1977);
+ if (ret)
+ goto err_poweroff;
+ }
+
+ if (adau1977->dvdd_reg)
+ power_off_mask = ~0;
+ else
+ power_off_mask = (unsigned int)~ADAU1977_BLOCK_POWER_SAI_LDO_EN;
+
+ ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI,
+ power_off_mask, 0x00);
+ if (ret)
+ goto err_poweroff;
+
+ ret = adau1977_power_disable(adau1977);
+ if (ret)
+ return ret;
+
+ return snd_soc_register_codec(dev, &adau1977_codec_driver,
+ &adau1977_dai, 1);
+
+err_poweroff:
+ adau1977_power_disable(adau1977);
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(adau1977_probe);
+
+static bool adau1977_register_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1977_REG_STATUS(0):
+ case ADAU1977_REG_STATUS(1):
+ case ADAU1977_REG_STATUS(2):
+ case ADAU1977_REG_STATUS(3):
+ case ADAU1977_REG_ADC_CLIP:
+ return true;
+ }
+
+ return false;
+}
+
+const struct regmap_config adau1977_regmap_config = {
+ .max_register = ADAU1977_REG_DC_HPF_CAL,
+ .volatile_reg = adau1977_register_volatile,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = adau1977_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adau1977_reg_defaults),
+};
+EXPORT_SYMBOL_GPL(adau1977_regmap_config);
+
+MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adau1977.h b/kernel/sound/soc/codecs/adau1977.h
new file mode 100644
index 000000000..95e714345
--- /dev/null
+++ b/kernel/sound/soc/codecs/adau1977.h
@@ -0,0 +1,37 @@
+/*
+ * 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__
+#define __SOUND_SOC_CODECS_ADAU1977_H__
+
+#include <linux/regmap.h>
+
+struct device;
+
+enum adau1977_type {
+ ADAU1977,
+ ADAU1978,
+ ADAU1979,
+};
+
+int adau1977_probe(struct device *dev, struct regmap *regmap,
+ enum adau1977_type type, void (*switch_mode)(struct device *dev));
+
+extern const struct regmap_config adau1977_regmap_config;
+
+enum adau1977_clk_id {
+ ADAU1977_SYSCLK,
+};
+
+enum adau1977_sysclk_src {
+ ADAU1977_SYSCLK_SRC_MCLK,
+ ADAU1977_SYSCLK_SRC_LRCLK,
+};
+
+#endif
diff --git a/kernel/sound/soc/codecs/adav801.c b/kernel/sound/soc/codecs/adav801.c
new file mode 100644
index 000000000..790fce33a
--- /dev/null
+++ b/kernel/sound/soc/codecs/adav801.c
@@ -0,0 +1,53 @@
+/*
+ * ADAV801 audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "adav80x.h"
+
+static const struct spi_device_id adav80x_spi_id[] = {
+ { "adav801", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adav80x_spi_id);
+
+static int adav80x_spi_probe(struct spi_device *spi)
+{
+ struct regmap_config config;
+
+ config = adav80x_regmap_config;
+ config.read_flag_mask = 0x01;
+
+ return adav80x_bus_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+}
+
+static 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);
+
+MODULE_DESCRIPTION("ASoC ADAV801 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_AUTHOR("Yi Li <yi.li@analog.com>>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adav803.c b/kernel/sound/soc/codecs/adav803.c
new file mode 100644
index 000000000..66d9fce34
--- /dev/null
+++ b/kernel/sound/soc/codecs/adav803.c
@@ -0,0 +1,50 @@
+/*
+ * ADAV803 audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "adav80x.h"
+
+static const struct i2c_device_id adav803_id[] = {
+ { "adav803", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adav803_id);
+
+static int adav803_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ 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);
+
+MODULE_DESCRIPTION("ASoC ADAV803 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_AUTHOR("Yi Li <yi.li@analog.com>>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adav80x.c b/kernel/sound/soc/codecs/adav80x.c
new file mode 100644
index 000000000..4373ada95
--- /dev/null
+++ b/kernel/sound/soc/codecs/adav80x.c
@@ -0,0 +1,880 @@
+/*
+ * 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>
+#include <linux/kernel.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 "adav80x.h"
+
+#define ADAV80X_PLAYBACK_CTRL 0x04
+#define ADAV80X_AUX_IN_CTRL 0x05
+#define ADAV80X_REC_CTRL 0x06
+#define ADAV80X_AUX_OUT_CTRL 0x07
+#define ADAV80X_DPATH_CTRL1 0x62
+#define ADAV80X_DPATH_CTRL2 0x63
+#define ADAV80X_DAC_CTRL1 0x64
+#define ADAV80X_DAC_CTRL2 0x65
+#define ADAV80X_DAC_CTRL3 0x66
+#define ADAV80X_DAC_L_VOL 0x68
+#define ADAV80X_DAC_R_VOL 0x69
+#define ADAV80X_PGA_L_VOL 0x6c
+#define ADAV80X_PGA_R_VOL 0x6d
+#define ADAV80X_ADC_CTRL1 0x6e
+#define ADAV80X_ADC_CTRL2 0x6f
+#define ADAV80X_ADC_L_VOL 0x70
+#define ADAV80X_ADC_R_VOL 0x71
+#define ADAV80X_PLL_CTRL1 0x74
+#define ADAV80X_PLL_CTRL2 0x75
+#define ADAV80X_ICLK_CTRL1 0x76
+#define ADAV80X_ICLK_CTRL2 0x77
+#define ADAV80X_PLL_CLK_SRC 0x78
+#define ADAV80X_PLL_OUTE 0x7a
+
+#define ADAV80X_PLL_CLK_SRC_PLL_XIN(pll) 0x00
+#define ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll) (0x40 << (pll))
+#define ADAV80X_PLL_CLK_SRC_PLL_MASK(pll) (0x40 << (pll))
+
+#define ADAV80X_ICLK_CTRL1_DAC_SRC(src) ((src) << 5)
+#define ADAV80X_ICLK_CTRL1_ADC_SRC(src) ((src) << 2)
+#define ADAV80X_ICLK_CTRL1_ICLK2_SRC(src) (src)
+#define ADAV80X_ICLK_CTRL2_ICLK1_SRC(src) ((src) << 3)
+
+#define ADAV80X_PLL_CTRL1_PLLDIV 0x10
+#define ADAV80X_PLL_CTRL1_PLLPD(pll) (0x04 << (pll))
+#define ADAV80X_PLL_CTRL1_XTLPD 0x02
+
+#define ADAV80X_PLL_CTRL2_FIELD(pll, x) ((x) << ((pll) * 4))
+
+#define ADAV80X_PLL_CTRL2_FS_48(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x00)
+#define ADAV80X_PLL_CTRL2_FS_32(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x08)
+#define ADAV80X_PLL_CTRL2_FS_44(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0c)
+
+#define ADAV80X_PLL_CTRL2_SEL(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x02)
+#define ADAV80X_PLL_CTRL2_DOUB(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x01)
+#define ADAV80X_PLL_CTRL2_PLL_MASK(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0f)
+
+#define ADAV80X_ADC_CTRL1_MODULATOR_MASK 0x80
+#define ADAV80X_ADC_CTRL1_MODULATOR_128FS 0x00
+#define ADAV80X_ADC_CTRL1_MODULATOR_64FS 0x80
+
+#define ADAV80X_DAC_CTRL1_PD 0x80
+
+#define ADAV80X_DAC_CTRL2_DIV1 0x00
+#define ADAV80X_DAC_CTRL2_DIV1_5 0x10
+#define ADAV80X_DAC_CTRL2_DIV2 0x20
+#define ADAV80X_DAC_CTRL2_DIV3 0x30
+#define ADAV80X_DAC_CTRL2_DIV_MASK 0x30
+
+#define ADAV80X_DAC_CTRL2_INTERPOL_256FS 0x00
+#define ADAV80X_DAC_CTRL2_INTERPOL_128FS 0x40
+#define ADAV80X_DAC_CTRL2_INTERPOL_64FS 0x80
+#define ADAV80X_DAC_CTRL2_INTERPOL_MASK 0xc0
+
+#define ADAV80X_DAC_CTRL2_DEEMPH_NONE 0x00
+#define ADAV80X_DAC_CTRL2_DEEMPH_44 0x01
+#define ADAV80X_DAC_CTRL2_DEEMPH_32 0x02
+#define ADAV80X_DAC_CTRL2_DEEMPH_48 0x03
+#define ADAV80X_DAC_CTRL2_DEEMPH_MASK 0x01
+
+#define ADAV80X_CAPTURE_MODE_MASTER 0x20
+#define ADAV80X_CAPTURE_WORD_LEN24 0x00
+#define ADAV80X_CAPTURE_WORD_LEN20 0x04
+#define ADAV80X_CAPTRUE_WORD_LEN18 0x08
+#define ADAV80X_CAPTURE_WORD_LEN16 0x0c
+#define ADAV80X_CAPTURE_WORD_LEN_MASK 0x0c
+
+#define ADAV80X_CAPTURE_MODE_LEFT_J 0x00
+#define ADAV80X_CAPTURE_MODE_I2S 0x01
+#define ADAV80X_CAPTURE_MODE_RIGHT_J 0x03
+#define ADAV80X_CAPTURE_MODE_MASK 0x03
+
+#define ADAV80X_PLAYBACK_MODE_MASTER 0x10
+#define ADAV80X_PLAYBACK_MODE_LEFT_J 0x00
+#define ADAV80X_PLAYBACK_MODE_I2S 0x01
+#define ADAV80X_PLAYBACK_MODE_RIGHT_J_24 0x04
+#define ADAV80X_PLAYBACK_MODE_RIGHT_J_20 0x05
+#define ADAV80X_PLAYBACK_MODE_RIGHT_J_18 0x06
+#define ADAV80X_PLAYBACK_MODE_RIGHT_J_16 0x07
+#define ADAV80X_PLAYBACK_MODE_MASK 0x07
+
+#define ADAV80X_PLL_OUTE_SYSCLKPD(x) BIT(2 - (x))
+
+static struct reg_default adav80x_reg_defaults[] = {
+ { ADAV80X_PLAYBACK_CTRL, 0x01 },
+ { ADAV80X_AUX_IN_CTRL, 0x01 },
+ { ADAV80X_REC_CTRL, 0x02 },
+ { ADAV80X_AUX_OUT_CTRL, 0x01 },
+ { ADAV80X_DPATH_CTRL1, 0xc0 },
+ { ADAV80X_DPATH_CTRL2, 0x11 },
+ { ADAV80X_DAC_CTRL1, 0x00 },
+ { ADAV80X_DAC_CTRL2, 0x00 },
+ { ADAV80X_DAC_CTRL3, 0x00 },
+ { ADAV80X_DAC_L_VOL, 0xff },
+ { ADAV80X_DAC_R_VOL, 0xff },
+ { ADAV80X_PGA_L_VOL, 0x00 },
+ { ADAV80X_PGA_R_VOL, 0x00 },
+ { ADAV80X_ADC_CTRL1, 0x00 },
+ { ADAV80X_ADC_CTRL2, 0x00 },
+ { ADAV80X_ADC_L_VOL, 0xff },
+ { ADAV80X_ADC_R_VOL, 0xff },
+ { ADAV80X_PLL_CTRL1, 0x00 },
+ { ADAV80X_PLL_CTRL2, 0x00 },
+ { ADAV80X_ICLK_CTRL1, 0x00 },
+ { ADAV80X_ICLK_CTRL2, 0x00 },
+ { ADAV80X_PLL_CLK_SRC, 0x00 },
+ { ADAV80X_PLL_OUTE, 0x00 },
+};
+
+struct adav80x {
+ struct regmap *regmap;
+
+ enum adav80x_clk_src clk_src;
+ unsigned int sysclk;
+ enum adav80x_pll_src pll_src;
+
+ unsigned int dai_fmt[2];
+ unsigned int rate;
+ bool deemph;
+ bool sysclk_pd[3];
+};
+
+static const char *adav80x_mux_text[] = {
+ "ADC",
+ "Playback",
+ "Aux Playback",
+};
+
+static const unsigned int adav80x_mux_values[] = {
+ 0, 2, 3,
+};
+
+#define ADAV80X_MUX_ENUM_DECL(name, reg, shift) \
+ SOC_VALUE_ENUM_DOUBLE_DECL(name, reg, shift, 7, \
+ ARRAY_SIZE(adav80x_mux_text), adav80x_mux_text, \
+ adav80x_mux_values)
+
+static ADAV80X_MUX_ENUM_DECL(adav80x_aux_capture_enum, ADAV80X_DPATH_CTRL1, 0);
+static ADAV80X_MUX_ENUM_DECL(adav80x_capture_enum, ADAV80X_DPATH_CTRL1, 3);
+static ADAV80X_MUX_ENUM_DECL(adav80x_dac_enum, ADAV80X_DPATH_CTRL2, 3);
+
+static const struct snd_kcontrol_new adav80x_aux_capture_mux_ctrl =
+ SOC_DAPM_ENUM("Route", adav80x_aux_capture_enum);
+static const struct snd_kcontrol_new adav80x_capture_mux_ctrl =
+ SOC_DAPM_ENUM("Route", adav80x_capture_enum);
+static const struct snd_kcontrol_new adav80x_dac_mux_ctrl =
+ SOC_DAPM_ENUM("Route", adav80x_dac_enum);
+
+#define ADAV80X_MUX(name, ctrl) \
+ SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", NULL, ADAV80X_DAC_CTRL1, 7, 1),
+ SND_SOC_DAPM_ADC("ADC", NULL, ADAV80X_ADC_CTRL1, 5, 1),
+
+ SND_SOC_DAPM_PGA("Right PGA", ADAV80X_ADC_CTRL1, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Left PGA", ADAV80X_ADC_CTRL1, 1, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT("AIFOUT", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFIN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("AIFAUXOUT", "Aux Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFAUXIN", "Aux Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ ADAV80X_MUX("Aux Capture Select", &adav80x_aux_capture_mux_ctrl),
+ ADAV80X_MUX("Capture Select", &adav80x_capture_mux_ctrl),
+ ADAV80X_MUX("DAC Select", &adav80x_dac_mux_ctrl),
+
+ SND_SOC_DAPM_INPUT("VINR"),
+ SND_SOC_DAPM_INPUT("VINL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+ SND_SOC_DAPM_OUTPUT("VOUTL"),
+
+ SND_SOC_DAPM_SUPPLY("SYSCLK", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", ADAV80X_PLL_CTRL1, 2, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2", ADAV80X_PLL_CTRL1, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("OSC", ADAV80X_PLL_CTRL1, 1, 1, NULL, 0),
+};
+
+static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ const char *clk;
+
+ switch (adav80x->clk_src) {
+ case ADAV80X_CLK_PLL1:
+ clk = "PLL1";
+ break;
+ case ADAV80X_CLK_PLL2:
+ clk = "PLL2";
+ break;
+ case ADAV80X_CLK_XTAL:
+ clk = "OSC";
+ break;
+ default:
+ return 0;
+ }
+
+ return strcmp(source->name, clk) == 0;
+}
+
+static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+
+ return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL;
+}
+
+
+static const struct snd_soc_dapm_route adav80x_dapm_routes[] = {
+ { "DAC Select", "ADC", "ADC" },
+ { "DAC Select", "Playback", "AIFIN" },
+ { "DAC Select", "Aux Playback", "AIFAUXIN" },
+ { "DAC", NULL, "DAC Select" },
+
+ { "Capture Select", "ADC", "ADC" },
+ { "Capture Select", "Playback", "AIFIN" },
+ { "Capture Select", "Aux Playback", "AIFAUXIN" },
+ { "AIFOUT", NULL, "Capture Select" },
+
+ { "Aux Capture Select", "ADC", "ADC" },
+ { "Aux Capture Select", "Playback", "AIFIN" },
+ { "Aux Capture Select", "Aux Playback", "AIFAUXIN" },
+ { "AIFAUXOUT", NULL, "Aux Capture Select" },
+
+ { "VOUTR", NULL, "DAC" },
+ { "VOUTL", NULL, "DAC" },
+
+ { "Left PGA", NULL, "VINL" },
+ { "Right PGA", NULL, "VINR" },
+ { "ADC", NULL, "Left PGA" },
+ { "ADC", NULL, "Right PGA" },
+
+ { "SYSCLK", NULL, "PLL1", adav80x_dapm_sysclk_check },
+ { "SYSCLK", NULL, "PLL2", adav80x_dapm_sysclk_check },
+ { "SYSCLK", NULL, "OSC", adav80x_dapm_sysclk_check },
+ { "PLL1", NULL, "OSC", adav80x_dapm_pll_check },
+ { "PLL2", NULL, "OSC", adav80x_dapm_pll_check },
+
+ { "ADC", NULL, "SYSCLK" },
+ { "DAC", NULL, "SYSCLK" },
+ { "AIFOUT", NULL, "SYSCLK" },
+ { "AIFAUXOUT", NULL, "SYSCLK" },
+ { "AIFIN", NULL, "SYSCLK" },
+ { "AIFAUXIN", NULL, "SYSCLK" },
+};
+
+static int adav80x_set_deemph(struct snd_soc_codec *codec)
+{
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ if (adav80x->deemph) {
+ switch (adav80x->rate) {
+ case 32000:
+ val = ADAV80X_DAC_CTRL2_DEEMPH_32;
+ break;
+ case 44100:
+ val = ADAV80X_DAC_CTRL2_DEEMPH_44;
+ break;
+ case 48000:
+ case 64000:
+ case 88200:
+ case 96000:
+ val = ADAV80X_DAC_CTRL2_DEEMPH_48;
+ break;
+ default:
+ val = ADAV80X_DAC_CTRL2_DEEMPH_NONE;
+ break;
+ }
+ } else {
+ val = ADAV80X_DAC_CTRL2_DEEMPH_NONE;
+ }
+
+ return regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL2,
+ ADAV80X_DAC_CTRL2_DEEMPH_MASK, val);
+}
+
+static int adav80x_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int deemph = ucontrol->value.integer.value[0];
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ adav80x->deemph = deemph;
+
+ return adav80x_set_deemph(codec);
+}
+
+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);
+
+ ucontrol->value.integer.value[0] = adav80x->deemph;
+ return 0;
+};
+
+static const DECLARE_TLV_DB_SCALE(adav80x_inpga_tlv, 0, 50, 0);
+static const DECLARE_TLV_DB_MINMAX(adav80x_digital_tlv, -9563, 0);
+
+static const struct snd_kcontrol_new adav80x_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume", ADAV80X_DAC_L_VOL,
+ ADAV80X_DAC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv),
+ SOC_DOUBLE_R_TLV("Master Capture Volume", ADAV80X_ADC_L_VOL,
+ ADAV80X_ADC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv),
+
+ SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAV80X_PGA_L_VOL,
+ ADAV80X_PGA_R_VOL, 0, 0x30, 0, adav80x_inpga_tlv),
+
+ SOC_DOUBLE("Master Playback Switch", ADAV80X_DAC_CTRL1, 0, 1, 1, 0),
+ SOC_DOUBLE("Master Capture Switch", ADAV80X_ADC_CTRL1, 2, 3, 1, 1),
+
+ SOC_SINGLE("ADC High Pass Filter Switch", ADAV80X_ADC_CTRL1, 6, 1, 0),
+
+ SOC_SINGLE_BOOL_EXT("Playback De-emphasis Switch", 0,
+ adav80x_get_deemph, adav80x_put_deemph),
+};
+
+static unsigned int adav80x_port_ctrl_regs[2][2] = {
+ { ADAV80X_REC_CTRL, ADAV80X_PLAYBACK_CTRL, },
+ { ADAV80X_AUX_OUT_CTRL, ADAV80X_AUX_IN_CTRL },
+};
+
+static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int capture = 0x00;
+ unsigned int playback = 0x00;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ capture |= ADAV80X_CAPTURE_MODE_MASTER;
+ playback |= ADAV80X_PLAYBACK_MODE_MASTER;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ capture |= ADAV80X_CAPTURE_MODE_I2S;
+ playback |= ADAV80X_PLAYBACK_MODE_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ capture |= ADAV80X_CAPTURE_MODE_LEFT_J;
+ playback |= ADAV80X_PLAYBACK_MODE_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ capture |= ADAV80X_CAPTURE_MODE_RIGHT_J;
+ playback |= ADAV80X_PLAYBACK_MODE_RIGHT_J_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][0],
+ ADAV80X_CAPTURE_MODE_MASK | ADAV80X_CAPTURE_MODE_MASTER,
+ capture);
+ regmap_write(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][1],
+ playback);
+
+ adav80x->dai_fmt[dai->id] = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ return 0;
+}
+
+static int adav80x_set_adc_clock(struct snd_soc_codec *codec,
+ unsigned int sample_rate)
+{
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ if (sample_rate <= 48000)
+ val = ADAV80X_ADC_CTRL1_MODULATOR_128FS;
+ else
+ val = ADAV80X_ADC_CTRL1_MODULATOR_64FS;
+
+ regmap_update_bits(adav80x->regmap, ADAV80X_ADC_CTRL1,
+ ADAV80X_ADC_CTRL1_MODULATOR_MASK, val);
+
+ return 0;
+}
+
+static int adav80x_set_dac_clock(struct snd_soc_codec *codec,
+ unsigned int sample_rate)
+{
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ if (sample_rate <= 48000)
+ val = ADAV80X_DAC_CTRL2_DIV1 | ADAV80X_DAC_CTRL2_INTERPOL_256FS;
+ else
+ val = ADAV80X_DAC_CTRL2_DIV2 | ADAV80X_DAC_CTRL2_INTERPOL_128FS;
+
+ regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL2,
+ ADAV80X_DAC_CTRL2_DIV_MASK | ADAV80X_DAC_CTRL2_INTERPOL_MASK,
+ val);
+
+ return 0;
+}
+
+static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec,
+ struct snd_soc_dai *dai, struct snd_pcm_hw_params *params)
+{
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ switch (params_width(params)) {
+ case 16:
+ val = ADAV80X_CAPTURE_WORD_LEN16;
+ break;
+ case 18:
+ val = ADAV80X_CAPTRUE_WORD_LEN18;
+ break;
+ case 20:
+ val = ADAV80X_CAPTURE_WORD_LEN20;
+ break;
+ case 24:
+ val = ADAV80X_CAPTURE_WORD_LEN24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][0],
+ ADAV80X_CAPTURE_WORD_LEN_MASK, val);
+
+ return 0;
+}
+
+static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec,
+ struct snd_soc_dai *dai, struct snd_pcm_hw_params *params)
+{
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ if (adav80x->dai_fmt[dai->id] != SND_SOC_DAIFMT_RIGHT_J)
+ return 0;
+
+ switch (params_width(params)) {
+ case 16:
+ val = ADAV80X_PLAYBACK_MODE_RIGHT_J_16;
+ break;
+ case 18:
+ val = ADAV80X_PLAYBACK_MODE_RIGHT_J_18;
+ break;
+ case 20:
+ val = ADAV80X_PLAYBACK_MODE_RIGHT_J_20;
+ break;
+ case 24:
+ val = ADAV80X_PLAYBACK_MODE_RIGHT_J_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][1],
+ ADAV80X_PLAYBACK_MODE_MASK, val);
+
+ return 0;
+}
+
+static int adav80x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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);
+ } else {
+ adav80x_set_capture_pcm_format(codec, dai, params);
+ adav80x_set_adc_clock(codec, rate);
+ }
+ adav80x->rate = rate;
+ adav80x_set_deemph(codec);
+
+ return 0;
+}
+
+static int adav80x_set_sysclk(struct snd_soc_codec *codec,
+ 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;
+
+ if (dir == SND_SOC_CLOCK_IN) {
+ switch (clk_id) {
+ case ADAV80X_CLK_XIN:
+ case ADAV80X_CLK_XTAL:
+ case ADAV80X_CLK_MCLKI:
+ case ADAV80X_CLK_PLL1:
+ case ADAV80X_CLK_PLL2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adav80x->sysclk = freq;
+
+ if (adav80x->clk_src != clk_id) {
+ unsigned int iclk_ctrl1, iclk_ctrl2;
+
+ adav80x->clk_src = clk_id;
+ if (clk_id == ADAV80X_CLK_XTAL)
+ clk_id = ADAV80X_CLK_XIN;
+
+ iclk_ctrl1 = ADAV80X_ICLK_CTRL1_DAC_SRC(clk_id) |
+ ADAV80X_ICLK_CTRL1_ADC_SRC(clk_id) |
+ ADAV80X_ICLK_CTRL1_ICLK2_SRC(clk_id);
+ iclk_ctrl2 = ADAV80X_ICLK_CTRL2_ICLK1_SRC(clk_id);
+
+ regmap_write(adav80x->regmap, ADAV80X_ICLK_CTRL1,
+ iclk_ctrl1);
+ regmap_write(adav80x->regmap, ADAV80X_ICLK_CTRL2,
+ iclk_ctrl2);
+
+ snd_soc_dapm_sync(dapm);
+ }
+ } else {
+ unsigned int mask;
+
+ switch (clk_id) {
+ case ADAV80X_CLK_SYSCLK1:
+ case ADAV80X_CLK_SYSCLK2:
+ case ADAV80X_CLK_SYSCLK3:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ clk_id -= ADAV80X_CLK_SYSCLK1;
+ mask = ADAV80X_PLL_OUTE_SYSCLKPD(clk_id);
+
+ if (freq == 0) {
+ regmap_update_bits(adav80x->regmap, ADAV80X_PLL_OUTE,
+ mask, mask);
+ adav80x->sysclk_pd[clk_id] = true;
+ } else {
+ regmap_update_bits(adav80x->regmap, ADAV80X_PLL_OUTE,
+ mask, 0);
+ adav80x->sysclk_pd[clk_id] = false;
+ }
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (adav80x->sysclk_pd[0])
+ snd_soc_dapm_disable_pin_unlocked(dapm, "PLL1");
+ else
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL1");
+
+ if (adav80x->sysclk_pd[1] || adav80x->sysclk_pd[2])
+ snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2");
+ else
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2");
+
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+ }
+
+ return 0;
+}
+
+static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int pll_ctrl1 = 0;
+ unsigned int pll_ctrl2 = 0;
+ unsigned int pll_src;
+
+ switch (source) {
+ case ADAV80X_PLL_SRC_XTAL:
+ case ADAV80X_PLL_SRC_XIN:
+ case ADAV80X_PLL_SRC_MCLKI:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!freq_out)
+ return 0;
+
+ switch (freq_in) {
+ case 27000000:
+ break;
+ case 54000000:
+ if (source == ADAV80X_PLL_SRC_XIN) {
+ pll_ctrl1 |= ADAV80X_PLL_CTRL1_PLLDIV;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ if (freq_out > 12288000) {
+ pll_ctrl2 |= ADAV80X_PLL_CTRL2_DOUB(pll_id);
+ freq_out /= 2;
+ }
+
+ /* freq_out = sample_rate * 256 */
+ switch (freq_out) {
+ case 8192000:
+ pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_32(pll_id);
+ break;
+ case 11289600:
+ pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_44(pll_id);
+ break;
+ case 12288000:
+ pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_48(pll_id);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CTRL1,
+ ADAV80X_PLL_CTRL1_PLLDIV, pll_ctrl1);
+ regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CTRL2,
+ ADAV80X_PLL_CTRL2_PLL_MASK(pll_id), pll_ctrl2);
+
+ if (source != adav80x->pll_src) {
+ if (source == ADAV80X_PLL_SRC_MCLKI)
+ pll_src = ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll_id);
+ else
+ pll_src = ADAV80X_PLL_CLK_SRC_PLL_XIN(pll_id);
+
+ regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CLK_SRC,
+ ADAV80X_PLL_CLK_SRC_PLL_MASK(pll_id), pll_src);
+
+ adav80x->pll_src = source;
+
+ snd_soc_dapm_sync(&codec->dapm);
+ }
+
+ return 0;
+}
+
+static int adav80x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ unsigned int mask = ADAV80X_DAC_CTRL1_PD;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL1, mask,
+ 0x00);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL1, mask,
+ mask);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+/* Enforce the same sample rate on all audio interfaces */
+static int adav80x_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+
+ if (!snd_soc_codec_is_active(codec) || !adav80x->rate)
+ return 0;
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, adav80x->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);
+
+ if (!snd_soc_codec_is_active(codec))
+ adav80x->rate = 0;
+}
+
+static const struct snd_soc_dai_ops adav80x_dai_ops = {
+ .set_fmt = adav80x_set_dai_fmt,
+ .hw_params = adav80x_hw_params,
+ .startup = adav80x_dai_startup,
+ .shutdown = adav80x_dai_shutdown,
+};
+
+#define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+#define ADAV80X_CAPTURE_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+
+#define ADAV80X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver adav80x_dais[] = {
+ {
+ .name = "adav80x-hifi",
+ .id = 0,
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ADAV80X_PLAYBACK_RATES,
+ .formats = ADAV80X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ADAV80X_CAPTURE_RATES,
+ .formats = ADAV80X_FORMATS,
+ },
+ .ops = &adav80x_dai_ops,
+ },
+ {
+ .name = "adav80x-aux",
+ .id = 1,
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ADAV80X_PLAYBACK_RATES,
+ .formats = ADAV80X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Aux Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ADAV80X_CAPTURE_RATES,
+ .formats = ADAV80X_FORMATS,
+ },
+ .ops = &adav80x_dai_ops,
+ },
+};
+
+static int adav80x_probe(struct snd_soc_codec *codec)
+{
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+
+ /* Force PLLs on for SYSCLK output */
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1");
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2");
+
+ /* Power down S/PDIF receiver, since it is currently not supported */
+ regmap_write(adav80x->regmap, ADAV80X_PLL_OUTE, 0x20);
+ /* Disable DAC zero flag */
+ regmap_write(adav80x->regmap, ADAV80X_DAC_CTRL3, 0x6);
+
+ return 0;
+}
+
+static int adav80x_resume(struct snd_soc_codec *codec)
+{
+ struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+
+ 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),
+};
+
+int adav80x_bus_probe(struct device *dev, struct regmap *regmap)
+{
+ struct adav80x *adav80x;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ adav80x = devm_kzalloc(dev, sizeof(*adav80x), GFP_KERNEL);
+ if (!adav80x)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, adav80x);
+ adav80x->regmap = regmap;
+
+ return snd_soc_register_codec(dev, &adav80x_codec_driver,
+ adav80x_dais, ARRAY_SIZE(adav80x_dais));
+}
+EXPORT_SYMBOL_GPL(adav80x_bus_probe);
+
+const struct regmap_config adav80x_regmap_config = {
+ .val_bits = 8,
+ .pad_bits = 1,
+ .reg_bits = 7,
+ .read_flag_mask = 0x01,
+
+ .max_register = ADAV80X_PLL_OUTE,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = adav80x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults),
+};
+EXPORT_SYMBOL_GPL(adav80x_regmap_config);
+
+MODULE_DESCRIPTION("ASoC ADAV80x driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_AUTHOR("Yi Li <yi.li@analog.com>>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/adav80x.h b/kernel/sound/soc/codecs/adav80x.h
new file mode 100644
index 000000000..8a1d7c09d
--- /dev/null
+++ b/kernel/sound/soc/codecs/adav80x.h
@@ -0,0 +1,42 @@
+/*
+ * header file for ADAV80X parts
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _ADAV80X_H
+#define _ADAV80X_H
+
+#include <linux/regmap.h>
+
+struct device;
+
+extern const struct regmap_config adav80x_regmap_config;
+int adav80x_bus_probe(struct device *dev, struct regmap *regmap);
+
+enum adav80x_pll_src {
+ ADAV80X_PLL_SRC_XIN,
+ ADAV80X_PLL_SRC_XTAL,
+ ADAV80X_PLL_SRC_MCLKI,
+};
+
+enum adav80x_pll {
+ ADAV80X_PLL1 = 0,
+ ADAV80X_PLL2 = 1,
+};
+
+enum adav80x_clk_src {
+ ADAV80X_CLK_XIN = 0,
+ ADAV80X_CLK_MCLKI = 1,
+ ADAV80X_CLK_PLL1 = 2,
+ ADAV80X_CLK_PLL2 = 3,
+ ADAV80X_CLK_XTAL = 6,
+
+ ADAV80X_CLK_SYSCLK1 = 6,
+ ADAV80X_CLK_SYSCLK2 = 7,
+ ADAV80X_CLK_SYSCLK3 = 8,
+};
+
+#endif
diff --git a/kernel/sound/soc/codecs/ads117x.c b/kernel/sound/soc/codecs/ads117x.c
new file mode 100644
index 000000000..1222282e9
--- /dev/null
+++ b/kernel/sound/soc/codecs/ads117x.c
@@ -0,0 +1,91 @@
+/*
+ * 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>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
+#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+static const struct snd_soc_dapm_widget ads117x_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("Input1"),
+SND_SOC_DAPM_INPUT("Input2"),
+SND_SOC_DAPM_INPUT("Input3"),
+SND_SOC_DAPM_INPUT("Input4"),
+SND_SOC_DAPM_INPUT("Input5"),
+SND_SOC_DAPM_INPUT("Input6"),
+SND_SOC_DAPM_INPUT("Input7"),
+SND_SOC_DAPM_INPUT("Input8"),
+};
+
+static const struct snd_soc_dapm_route ads117x_dapm_routes[] = {
+ { "Capture", NULL, "Input1" },
+ { "Capture", NULL, "Input2" },
+ { "Capture", NULL, "Input3" },
+ { "Capture", NULL, "Input4" },
+ { "Capture", NULL, "Input5" },
+ { "Capture", NULL, "Input6" },
+ { "Capture", NULL, "Input7" },
+ { "Capture", NULL, "Input8" },
+};
+
+static struct snd_soc_dai_driver ads117x_dai = {
+/* ADC */
+ .name = "ads117x-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = ADS117X_RATES,
+ .formats = ADS117X_FORMATS,},
+};
+
+static 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 int ads117x_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_ads117x, &ads117x_dai, 1);
+}
+
+static int ads117x_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver ads117x_codec_driver = {
+ .driver = {
+ .name = "ads117x-codec",
+ },
+
+ .probe = ads117x_probe,
+ .remove = ads117x_remove,
+};
+
+module_platform_driver(ads117x_codec_driver);
+
+MODULE_DESCRIPTION("ASoC ads117x driver");
+MODULE_AUTHOR("Graeme Gregory");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ak4104.c b/kernel/sound/soc/codecs/ak4104.c
new file mode 100644
index 000000000..1fd7f72b2
--- /dev/null
+++ b/kernel/sound/soc/codecs/ak4104.c
@@ -0,0 +1,360 @@
+/*
+ * 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/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+/* AK4104 registers addresses */
+#define AK4104_REG_CONTROL1 0x00
+#define AK4104_REG_RESERVED 0x01
+#define AK4104_REG_CONTROL2 0x02
+#define AK4104_REG_TX 0x03
+#define AK4104_REG_CHN_STATUS(x) ((x) + 0x04)
+#define AK4104_NUM_REGS 10
+
+#define AK4104_REG_MASK 0x1f
+#define AK4104_READ 0xc0
+#define AK4104_WRITE 0xe0
+#define AK4104_RESERVED_VAL 0x5b
+
+/* Bit masks for AK4104 registers */
+#define AK4104_CONTROL1_RSTN (1 << 0)
+#define AK4104_CONTROL1_PW (1 << 1)
+#define AK4104_CONTROL1_DIF0 (1 << 2)
+#define AK4104_CONTROL1_DIF1 (1 << 3)
+
+#define AK4104_CONTROL2_SEL0 (1 << 0)
+#define AK4104_CONTROL2_SEL1 (1 << 1)
+#define AK4104_CONTROL2_MODE (1 << 2)
+
+#define AK4104_TX_TXE (1 << 0)
+#define AK4104_TX_V (1 << 1)
+
+struct ak4104_private {
+ struct regmap *regmap;
+ struct regulator *regulator;
+};
+
+static const struct snd_soc_dapm_widget ak4104_dapm_widgets[] = {
+SND_SOC_DAPM_PGA("TXE", AK4104_REG_TX, AK4104_TX_TXE, 0, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route ak4104_dapm_routes[] = {
+ { "TXE", NULL, "Playback" },
+ { "TX", NULL, "TXE" },
+};
+
+static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ int val = 0;
+ int ret;
+
+ /* set DAI format */
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val |= AK4104_CONTROL1_DIF0;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val |= AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1;
+ break;
+ default:
+ dev_err(codec->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)
+ return -EINVAL;
+
+ ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
+ AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1,
+ val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ak4104_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ int ret, val = 0;
+
+ /* set the IEC958 bits: consumer mode, no copyright bit */
+ val |= IEC958_AES0_CON_NOT_COPYRIGHT;
+ regmap_write(ak4104->regmap, AK4104_REG_CHN_STATUS(0), val);
+
+ val = 0;
+
+ switch (params_rate(params)) {
+ case 22050:
+ val |= IEC958_AES3_CON_FS_22050;
+ break;
+ case 24000:
+ val |= IEC958_AES3_CON_FS_24000;
+ break;
+ case 32000:
+ val |= IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ val |= IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ val |= IEC958_AES3_CON_FS_48000;
+ break;
+ case 88200:
+ val |= IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ val |= IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ val |= IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ val |= IEC958_AES3_CON_FS_192000;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported sampling rate\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_write(ak4104->regmap, AK4104_REG_CHN_STATUS(3), val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ak4101_dai_ops = {
+ .hw_params = ak4104_hw_params,
+ .set_fmt = ak4104_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver ak4104_dai = {
+ .name = "ak4104-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE
+ },
+ .ops = &ak4101_dai_ops,
+};
+
+static int ak4104_probe(struct snd_soc_codec *codec)
+{
+ struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = regulator_enable(ak4104->regulator);
+ if (ret < 0) {
+ dev_err(codec->dev, "Unable to enable regulator: %d\n", ret);
+ return ret;
+ }
+
+ /* set power-up and non-reset bits */
+ ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
+ AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN,
+ AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN);
+ if (ret < 0)
+ goto exit_disable_regulator;
+
+ /* enable transmitter */
+ ret = regmap_update_bits(ak4104->regmap, AK4104_REG_TX,
+ AK4104_TX_TXE, AK4104_TX_TXE);
+ if (ret < 0)
+ goto exit_disable_regulator;
+
+ return 0;
+
+exit_disable_regulator:
+ regulator_disable(ak4104->regulator);
+ return ret;
+}
+
+static int ak4104_remove(struct snd_soc_codec *codec)
+{
+ struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+
+ 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)
+{
+ struct ak4104_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ regulator_disable(priv->regulator);
+
+ return 0;
+}
+
+static int ak4104_soc_resume(struct snd_soc_codec *codec)
+{
+ struct ak4104_private *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = regulator_enable(priv->regulator);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+#else
+#define ak4104_soc_suspend NULL
+#define ak4104_soc_resume NULL
+#endif /* CONFIG_PM */
+
+static 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 regmap_config ak4104_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK4104_NUM_REGS - 1,
+ .read_flag_mask = AK4104_READ,
+ .write_flag_mask = AK4104_WRITE,
+
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int ak4104_spi_probe(struct spi_device *spi)
+{
+ struct device_node *np = spi->dev.of_node;
+ struct ak4104_private *ak4104;
+ unsigned int val;
+ int ret;
+
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_0;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ ak4104 = devm_kzalloc(&spi->dev, sizeof(struct ak4104_private),
+ GFP_KERNEL);
+ if (ak4104 == NULL)
+ return -ENOMEM;
+
+ ak4104->regulator = devm_regulator_get(&spi->dev, "vdd");
+ if (IS_ERR(ak4104->regulator)) {
+ ret = PTR_ERR(ak4104->regulator);
+ dev_err(&spi->dev, "Unable to get Vdd regulator: %d\n", ret);
+ return ret;
+ }
+
+ ak4104->regmap = devm_regmap_init_spi(spi, &ak4104_regmap);
+ if (IS_ERR(ak4104->regmap)) {
+ ret = PTR_ERR(ak4104->regmap);
+ return ret;
+ }
+
+ 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;
+ }
+ }
+
+ /* read the 'reserved' register - according to the datasheet, it
+ * should contain 0x5b. Not a good way to verify the presence of
+ * the device, but there is no hardware ID register. */
+ ret = regmap_read(ak4104->regmap, AK4104_REG_RESERVED, &val);
+ if (ret != 0)
+ return ret;
+ if (val != AK4104_RESERVED_VAL)
+ return -ENODEV;
+
+ spi_set_drvdata(spi, ak4104);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_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", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ak4104_of_match);
+
+static const struct spi_device_id ak4104_id_table[] = {
+ { "ak4104", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ak4104_id_table);
+
+static struct spi_driver ak4104_spi_driver = {
+ .driver = {
+ .name = "ak4104",
+ .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);
+
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_DESCRIPTION("Asahi Kasei AK4104 ALSA SoC driver");
+MODULE_LICENSE("GPL");
+
diff --git a/kernel/sound/soc/codecs/ak4535.c b/kernel/sound/soc/codecs/ak4535.c
new file mode 100644
index 000000000..9130d916f
--- /dev/null
+++ b/kernel/sound/soc/codecs/ak4535.c
@@ -0,0 +1,459 @@
+/*
+ * ak4535.c -- AK4535 ALSA Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * 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>
+#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/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#include "ak4535.h"
+
+/* codec private data */
+struct ak4535_priv {
+ struct regmap *regmap;
+ unsigned int sysclk;
+};
+
+/*
+ * ak4535 register cache
+ */
+static const struct reg_default ak4535_reg_defaults[] = {
+ { 0, 0x00 },
+ { 1, 0x80 },
+ { 2, 0x00 },
+ { 3, 0x03 },
+ { 4, 0x02 },
+ { 5, 0x00 },
+ { 6, 0x11 },
+ { 7, 0x01 },
+ { 8, 0x00 },
+ { 9, 0x40 },
+ { 10, 0x36 },
+ { 11, 0x10 },
+ { 12, 0x00 },
+ { 13, 0x00 },
+ { 14, 0x57 },
+};
+
+static bool ak4535_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AK4535_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char *ak4535_mono_gain[] = {"+6dB", "-17dB"};
+static const char *ak4535_mono_out[] = {"(L + R)/2", "Hi-Z"};
+static const char *ak4535_hp_out[] = {"Stereo", "Mono"};
+static const char *ak4535_deemp[] = {"44.1kHz", "Off", "48kHz", "32kHz"};
+static const char *ak4535_mic_select[] = {"Internal", "External"};
+
+static const struct soc_enum ak4535_enum[] = {
+ SOC_ENUM_SINGLE(AK4535_SIG1, 7, 2, ak4535_mono_gain),
+ SOC_ENUM_SINGLE(AK4535_SIG1, 6, 2, ak4535_mono_out),
+ SOC_ENUM_SINGLE(AK4535_MODE2, 2, 2, ak4535_hp_out),
+ SOC_ENUM_SINGLE(AK4535_DAC, 0, 4, ak4535_deemp),
+ SOC_ENUM_SINGLE(AK4535_MIC, 1, 2, ak4535_mic_select),
+};
+
+static const struct snd_kcontrol_new ak4535_snd_controls[] = {
+ SOC_SINGLE("ALC2 Switch", AK4535_SIG1, 1, 1, 0),
+ SOC_ENUM("Mono 1 Output", ak4535_enum[1]),
+ SOC_ENUM("Mono 1 Gain", ak4535_enum[0]),
+ SOC_ENUM("Headphone Output", ak4535_enum[2]),
+ SOC_ENUM("Playback Deemphasis", ak4535_enum[3]),
+ SOC_SINGLE("Bass Volume", AK4535_DAC, 2, 3, 0),
+ SOC_SINGLE("Mic Boost (+20dB) Switch", AK4535_MIC, 0, 1, 0),
+ SOC_ENUM("Mic Select", ak4535_enum[4]),
+ SOC_SINGLE("ALC Operation Time", AK4535_TIMER, 0, 3, 0),
+ SOC_SINGLE("ALC Recovery Time", AK4535_TIMER, 2, 3, 0),
+ SOC_SINGLE("ALC ZC Time", AK4535_TIMER, 4, 3, 0),
+ SOC_SINGLE("ALC 1 Switch", AK4535_ALC1, 5, 1, 0),
+ SOC_SINGLE("ALC 2 Switch", AK4535_ALC1, 6, 1, 0),
+ SOC_SINGLE("ALC Volume", AK4535_ALC2, 0, 127, 0),
+ SOC_SINGLE("Capture Volume", AK4535_PGA, 0, 127, 0),
+ SOC_SINGLE("Left Playback Volume", AK4535_LATT, 0, 127, 1),
+ SOC_SINGLE("Right Playback Volume", AK4535_RATT, 0, 127, 1),
+ SOC_SINGLE("AUX Bypass Volume", AK4535_VOL, 0, 15, 0),
+ SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0),
+};
+
+/* Mono 1 Mixer */
+static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0),
+ SOC_DAPM_SINGLE("Mono Playback Switch", AK4535_SIG1, 5, 1, 0),
+};
+
+/* Stereo Mixer */
+static const struct snd_kcontrol_new ak4535_stereo_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG2, 4, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", AK4535_SIG2, 7, 1, 0),
+ SOC_DAPM_SINGLE("Aux Bypass Switch", AK4535_SIG2, 5, 1, 0),
+};
+
+/* Input Mixer */
+static const struct snd_kcontrol_new ak4535_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Capture Switch", AK4535_MIC, 2, 1, 0),
+ SOC_DAPM_SINGLE("Aux Capture Switch", AK4535_MIC, 5, 1, 0),
+};
+
+/* Input mux */
+static const struct snd_kcontrol_new ak4535_input_mux_control =
+ SOC_DAPM_ENUM("Input Select", ak4535_enum[4]);
+
+/* HP L switch */
+static const struct snd_kcontrol_new ak4535_hpl_control =
+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 1, 1, 1);
+
+/* HP R switch */
+static const struct snd_kcontrol_new ak4535_hpr_control =
+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 0, 1, 1);
+
+/* mono 2 switch */
+static const struct snd_kcontrol_new ak4535_mono2_control =
+ SOC_DAPM_SINGLE("Switch", AK4535_SIG1, 0, 1, 0);
+
+/* Line out switch */
+static const struct snd_kcontrol_new ak4535_line_control =
+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 6, 1, 0);
+
+/* ak4535 dapm widgets */
+static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4535_stereo_mixer_controls[0],
+ ARRAY_SIZE(ak4535_stereo_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4535_mono1_mixer_controls[0],
+ ARRAY_SIZE(ak4535_mono1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4535_input_mixer_controls[0],
+ ARRAY_SIZE(ak4535_input_mixer_controls)),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &ak4535_input_mux_control),
+ SND_SOC_DAPM_DAC("DAC", "Playback", AK4535_PM2, 0, 0),
+ SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
+ &ak4535_mono2_control),
+ /* speaker powersave bit */
+ SND_SOC_DAPM_PGA("Speaker Enable", AK4535_MODE2, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
+ &ak4535_line_control),
+ SND_SOC_DAPM_SWITCH("Left HP Enable", SND_SOC_NOPM, 0, 0,
+ &ak4535_hpl_control),
+ SND_SOC_DAPM_SWITCH("Right HP Enable", SND_SOC_NOPM, 0, 0,
+ &ak4535_hpr_control),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPP"),
+ SND_SOC_DAPM_OUTPUT("SPN"),
+ SND_SOC_DAPM_OUTPUT("MOUT1"),
+ SND_SOC_DAPM_OUTPUT("MOUT2"),
+ SND_SOC_DAPM_OUTPUT("MICOUT"),
+ SND_SOC_DAPM_ADC("ADC", "Capture", AK4535_PM1, 0, 0),
+ SND_SOC_DAPM_PGA("Spk Amp", AK4535_PM2, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP R Amp", AK4535_PM2, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP L Amp", AK4535_PM2, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic", AK4535_PM1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line Out", AK4535_PM1, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out", AK4535_PM1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX In", AK4535_PM1, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4535_MIC, 3, 0),
+ SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4535_MIC, 4, 0),
+ SND_SOC_DAPM_INPUT("MICIN"),
+ SND_SOC_DAPM_INPUT("MICEXT"),
+ SND_SOC_DAPM_INPUT("AUX"),
+ SND_SOC_DAPM_INPUT("MIN"),
+ SND_SOC_DAPM_INPUT("AIN"),
+};
+
+static const struct snd_soc_dapm_route ak4535_audio_map[] = {
+ /*stereo mixer */
+ {"Stereo Mixer", "Playback Switch", "DAC"},
+ {"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
+ {"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
+
+ /* mono1 mixer */
+ {"Mono1 Mixer", "Mic Sidetone Switch", "Mic"},
+ {"Mono1 Mixer", "Mono Playback Switch", "DAC"},
+
+ /* Mic */
+ {"Mic", NULL, "AIN"},
+ {"Input Mux", "Internal", "Mic Int Bias"},
+ {"Input Mux", "External", "Mic Ext Bias"},
+ {"Mic Int Bias", NULL, "MICIN"},
+ {"Mic Ext Bias", NULL, "MICEXT"},
+ {"MICOUT", NULL, "Input Mux"},
+
+ /* line out */
+ {"LOUT", NULL, "Line Out Enable"},
+ {"ROUT", NULL, "Line Out Enable"},
+ {"Line Out Enable", "Switch", "Line Out"},
+ {"Line Out", NULL, "Stereo Mixer"},
+
+ /* mono1 out */
+ {"MOUT1", NULL, "Mono Out"},
+ {"Mono Out", NULL, "Mono1 Mixer"},
+
+ /* left HP */
+ {"HPL", NULL, "Left HP Enable"},
+ {"Left HP Enable", "Switch", "HP L Amp"},
+ {"HP L Amp", NULL, "Stereo Mixer"},
+
+ /* right HP */
+ {"HPR", NULL, "Right HP Enable"},
+ {"Right HP Enable", "Switch", "HP R Amp"},
+ {"HP R Amp", NULL, "Stereo Mixer"},
+
+ /* speaker */
+ {"SPP", NULL, "Speaker Enable"},
+ {"SPN", NULL, "Speaker Enable"},
+ {"Speaker Enable", "Switch", "Spk Amp"},
+ {"Spk Amp", NULL, "MIN"},
+
+ /* mono 2 */
+ {"MOUT2", NULL, "Mono 2 Enable"},
+ {"Mono 2 Enable", "Switch", "Stereo Mixer"},
+
+ /* Aux In */
+ {"Aux In", NULL, "AUX"},
+
+ /* ADC */
+ {"ADC", NULL, "Input Mixer"},
+ {"Input Mixer", "Mic Capture Switch", "Mic"},
+ {"Input Mixer", "Aux Capture Switch", "Aux In"},
+};
+
+static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
+
+ ak4535->sysclk = freq;
+ return 0;
+}
+
+static int ak4535_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
+ u8 mode2 = snd_soc_read(codec, AK4535_MODE2) & ~(0x3 << 5);
+ int rate = params_rate(params), fs = 256;
+
+ if (rate)
+ fs = ak4535->sysclk / rate;
+
+ /* set fs */
+ switch (fs) {
+ case 1024:
+ mode2 |= (0x2 << 5);
+ break;
+ case 512:
+ mode2 |= (0x1 << 5);
+ break;
+ case 256:
+ break;
+ }
+
+ /* set rate */
+ snd_soc_write(codec, 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;
+ u8 mode1 = 0;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ mode1 = 0x0002;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode1 = 0x0001;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* use 32 fs for BCLK to save power */
+ mode1 |= 0x4;
+
+ snd_soc_write(codec, AK4535_MODE1, mode1);
+ return 0;
+}
+
+static int ak4535_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, AK4535_DAC);
+ if (!mute)
+ snd_soc_write(codec, AK4535_DAC, mute_reg & ~0x20);
+ else
+ snd_soc_write(codec, AK4535_DAC, mute_reg | 0x20);
+ return 0;
+}
+
+static int ak4535_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_update_bits(codec, AK4535_DAC, 0x20, 0);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_update_bits(codec, 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);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define AK4535_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+static const struct snd_soc_dai_ops ak4535_dai_ops = {
+ .hw_params = ak4535_hw_params,
+ .set_fmt = ak4535_set_dai_fmt,
+ .digital_mute = ak4535_mute,
+ .set_sysclk = ak4535_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver ak4535_dai = {
+ .name = "ak4535-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4535_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4535_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &ak4535_dai_ops,
+};
+
+static int ak4535_resume(struct snd_soc_codec *codec)
+{
+ snd_soc_cache_sync(codec);
+ return 0;
+}
+
+static const struct regmap_config ak4535_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK4535_STATUS,
+ .volatile_reg = ak4535_volatile,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = ak4535_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4535_reg_defaults),
+};
+
+static 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 int ak4535_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ak4535_priv *ak4535;
+ int ret;
+
+ ak4535 = devm_kzalloc(&i2c->dev, sizeof(struct ak4535_priv),
+ GFP_KERNEL);
+ if (ak4535 == NULL)
+ return -ENOMEM;
+
+ ak4535->regmap = devm_regmap_init_i2c(i2c, &ak4535_regmap);
+ if (IS_ERR(ak4535->regmap)) {
+ ret = PTR_ERR(ak4535->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, ak4535);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_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 },
+ { }
+};
+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,
+ .id_table = ak4535_i2c_id,
+};
+
+module_i2c_driver(ak4535_i2c_driver);
+
+MODULE_DESCRIPTION("Soc AK4535 driver");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ak4535.h b/kernel/sound/soc/codecs/ak4535.h
new file mode 100644
index 000000000..402de1d27
--- /dev/null
+++ b/kernel/sound/soc/codecs/ak4535.h
@@ -0,0 +1,37 @@
+/*
+ * ak4535.h -- AK4535 Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * 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
+#define _AK4535_H
+
+/* AK4535 register space */
+
+#define AK4535_PM1 0x0
+#define AK4535_PM2 0x1
+#define AK4535_SIG1 0x2
+#define AK4535_SIG2 0x3
+#define AK4535_MODE1 0x4
+#define AK4535_MODE2 0x5
+#define AK4535_DAC 0x6
+#define AK4535_MIC 0x7
+#define AK4535_TIMER 0x8
+#define AK4535_ALC1 0x9
+#define AK4535_ALC2 0xa
+#define AK4535_PGA 0xb
+#define AK4535_LATT 0xc
+#define AK4535_RATT 0xd
+#define AK4535_VOL 0xe
+#define AK4535_STATUS 0xf
+
+#endif
diff --git a/kernel/sound/soc/codecs/ak4554.c b/kernel/sound/soc/codecs/ak4554.c
new file mode 100644
index 000000000..298dedc05
--- /dev/null
+++ b/kernel/sound/soc/codecs/ak4554.c
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+/*
+ * ak4554 is very simple DA/AD converter which has no setting register.
+ *
+ * CAUTION
+ *
+ * ak4554 playback format is SND_SOC_DAIFMT_RIGHT_J,
+ * and, capture format is SND_SOC_DAIFMT_LEFT_J
+ * on same bit clock, LR clock.
+ * But, this driver doesn't have snd_soc_dai_ops :: set_fmt
+ *
+ * CPU/Codec DAI image
+ *
+ * CPU-DAI1 (plaback only fmt = RIGHT_J) --+-- ak4554
+ * |
+ * CPU-DAI2 (capture only fmt = LEFT_J) ---+
+ */
+
+static const struct snd_soc_dapm_widget ak4554_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+
+SND_SOC_DAPM_OUTPUT("AOUTL"),
+SND_SOC_DAPM_OUTPUT("AOUTR"),
+};
+
+static const struct snd_soc_dapm_route ak4554_dapm_routes[] = {
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
+
+ { "AOUTL", NULL, "Playback" },
+ { "AOUTR", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver ak4554_dai = {
+ .name = "ak4554-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .symmetric_rates = 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 int ak4554_soc_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_ak4554,
+ &ak4554_dai, 1);
+}
+
+static int ak4554_soc_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id ak4554_of_match[] = {
+ { .compatible = "asahi-kasei,ak4554" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4554_of_match);
+
+static struct platform_driver ak4554_driver = {
+ .driver = {
+ .name = "ak4554-adc-dac",
+ .of_match_table = ak4554_of_match,
+ },
+ .probe = ak4554_soc_probe,
+ .remove = ak4554_soc_remove,
+};
+module_platform_driver(ak4554_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SoC AK4554 driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/kernel/sound/soc/codecs/ak4641.c b/kernel/sound/soc/codecs/ak4641.c
new file mode 100644
index 000000000..81b54a270
--- /dev/null
+++ b/kernel/sound/soc/codecs/ak4641.c
@@ -0,0 +1,624 @@
+/*
+ * ak4641.c -- AK4641 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2008 Harald Welte <laforge@gnufiish.org>
+ * 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>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.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 <sound/ak4641.h>
+
+#include "ak4641.h"
+
+/* codec private data */
+struct ak4641_priv {
+ struct regmap *regmap;
+ unsigned int sysclk;
+ int deemph;
+ int playback_fs;
+};
+
+/*
+ * ak4641 register cache
+ */
+static const struct reg_default ak4641_reg_defaults[] = {
+ { 0, 0x00 }, { 1, 0x80 }, { 2, 0x00 }, { 3, 0x80 },
+ { 4, 0x02 }, { 5, 0x00 }, { 6, 0x11 }, { 7, 0x05 },
+ { 8, 0x00 }, { 9, 0x00 }, { 10, 0x36 }, { 11, 0x10 },
+ { 12, 0x00 }, { 13, 0x00 }, { 14, 0x57 }, { 15, 0x00 },
+ { 16, 0x88 }, { 17, 0x88 }, { 18, 0x08 }, { 19, 0x08 }
+};
+
+static const int deemph_settings[] = {44100, 0, 48000, 32000};
+
+static int ak4641_set_deemph(struct snd_soc_codec *codec)
+{
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ int i, best = 0;
+
+ for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) {
+ /* if deemphasis is on, select the nearest available rate */
+ if (ak4641->deemph && deemph_settings[i] != 0 &&
+ abs(deemph_settings[i] - ak4641->playback_fs) <
+ abs(deemph_settings[best] - ak4641->playback_fs))
+ best = i;
+
+ if (!ak4641->deemph && deemph_settings[i] == 0)
+ best = i;
+ }
+
+ dev_dbg(codec->dev, "Set deemphasis %d\n", best);
+
+ return snd_soc_update_bits(codec, 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.integer.value[0];
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ ak4641->deemph = deemph;
+
+ return ak4641_set_deemph(codec);
+}
+
+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);
+
+ ucontrol->value.integer.value[0] = ak4641->deemph;
+ return 0;
+};
+
+static const char *ak4641_mono_out[] = {"(L + R)/2", "Hi-Z"};
+static const char *ak4641_hp_out[] = {"Stereo", "Mono"};
+static const char *ak4641_mic_select[] = {"Internal", "External"};
+static const char *ak4641_mic_or_dac[] = {"Microphone", "Voice DAC"};
+
+
+static const DECLARE_TLV_DB_SCALE(mono_gain_tlv, -1700, 2300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 2000, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(master_tlv, -12750, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_stereo_sidetone_tlv, -2700, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_mono_sidetone_tlv, -400, 400, 0);
+static const DECLARE_TLV_DB_SCALE(capture_tlv, -800, 50, 0);
+static const DECLARE_TLV_DB_SCALE(alc_tlv, -800, 50, 0);
+static const DECLARE_TLV_DB_SCALE(aux_in_tlv, -2100, 300, 0);
+
+
+static SOC_ENUM_SINGLE_DECL(ak4641_mono_out_enum,
+ AK4641_SIG1, 6, ak4641_mono_out);
+static SOC_ENUM_SINGLE_DECL(ak4641_hp_out_enum,
+ AK4641_MODE2, 2, ak4641_hp_out);
+static SOC_ENUM_SINGLE_DECL(ak4641_mic_select_enum,
+ AK4641_MIC, 1, ak4641_mic_select);
+static SOC_ENUM_SINGLE_DECL(ak4641_mic_or_dac_enum,
+ AK4641_BTIF, 4, ak4641_mic_or_dac);
+
+static const struct snd_kcontrol_new ak4641_snd_controls[] = {
+ SOC_ENUM("Mono 1 Output", ak4641_mono_out_enum),
+ SOC_SINGLE_TLV("Mono 1 Gain Volume", AK4641_SIG1, 7, 1, 1,
+ mono_gain_tlv),
+ SOC_ENUM("Headphone Output", ak4641_hp_out_enum),
+ SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
+ ak4641_get_deemph, ak4641_put_deemph),
+
+ SOC_SINGLE_TLV("Mic Boost Volume", AK4641_MIC, 0, 1, 0, mic_boost_tlv),
+
+ SOC_SINGLE("ALC Operation Time", AK4641_TIMER, 0, 3, 0),
+ SOC_SINGLE("ALC Recovery Time", AK4641_TIMER, 2, 3, 0),
+ SOC_SINGLE("ALC ZC Time", AK4641_TIMER, 4, 3, 0),
+
+ SOC_SINGLE("ALC 1 Switch", AK4641_ALC1, 5, 1, 0),
+
+ SOC_SINGLE_TLV("ALC Volume", AK4641_ALC2, 0, 71, 0, alc_tlv),
+ SOC_SINGLE("Left Out Enable Switch", AK4641_SIG2, 1, 1, 0),
+ SOC_SINGLE("Right Out Enable Switch", AK4641_SIG2, 0, 1, 0),
+
+ SOC_SINGLE_TLV("Capture Volume", AK4641_PGA, 0, 71, 0, capture_tlv),
+
+ SOC_DOUBLE_R_TLV("Master Playback Volume", AK4641_LATT,
+ AK4641_RATT, 0, 255, 1, master_tlv),
+
+ SOC_SINGLE_TLV("AUX In Volume", AK4641_VOL, 0, 15, 0, aux_in_tlv),
+
+ SOC_SINGLE("Equalizer Switch", AK4641_DAC, 2, 1, 0),
+ SOC_SINGLE_TLV("EQ1 100 Hz Volume", AK4641_EQLO, 0, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ2 250 Hz Volume", AK4641_EQLO, 4, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ3 1 kHz Volume", AK4641_EQMID, 0, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ4 3.5 kHz Volume", AK4641_EQMID, 4, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ5 10 kHz Volume", AK4641_EQHI, 0, 15, 1, eq_tlv),
+};
+
+/* Mono 1 Mixer */
+static const struct snd_kcontrol_new ak4641_mono1_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Mic Mono Sidetone Volume", AK4641_VOL, 7, 1, 0,
+ mic_mono_sidetone_tlv),
+ SOC_DAPM_SINGLE("Mic Mono Sidetone Switch", AK4641_SIG1, 4, 1, 0),
+ SOC_DAPM_SINGLE("Mono Playback Switch", AK4641_SIG1, 5, 1, 0),
+};
+
+/* Stereo Mixer */
+static const struct snd_kcontrol_new ak4641_stereo_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Mic Sidetone Volume", AK4641_VOL, 4, 7, 0,
+ mic_stereo_sidetone_tlv),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4641_SIG2, 4, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", AK4641_SIG2, 7, 1, 0),
+ SOC_DAPM_SINGLE("Aux Bypass Switch", AK4641_SIG2, 5, 1, 0),
+};
+
+/* Input Mixer */
+static const struct snd_kcontrol_new ak4641_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Capture Switch", AK4641_MIC, 2, 1, 0),
+ SOC_DAPM_SINGLE("Aux Capture Switch", AK4641_MIC, 5, 1, 0),
+};
+
+/* Mic mux */
+static const struct snd_kcontrol_new ak4641_mic_mux_control =
+ SOC_DAPM_ENUM("Mic Select", ak4641_mic_select_enum);
+
+/* Input mux */
+static const struct snd_kcontrol_new ak4641_input_mux_control =
+ SOC_DAPM_ENUM("Input Select", ak4641_mic_or_dac_enum);
+
+/* mono 2 switch */
+static const struct snd_kcontrol_new ak4641_mono2_control =
+ SOC_DAPM_SINGLE("Switch", AK4641_SIG1, 0, 1, 0);
+
+/* ak4641 dapm widgets */
+static const struct snd_soc_dapm_widget ak4641_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_stereo_mixer_controls[0],
+ ARRAY_SIZE(ak4641_stereo_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_mono1_mixer_controls[0],
+ ARRAY_SIZE(ak4641_mono1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_input_mixer_controls[0],
+ ARRAY_SIZE(ak4641_input_mixer_controls)),
+ SND_SOC_DAPM_MUX("Mic Mux", SND_SOC_NOPM, 0, 0,
+ &ak4641_mic_mux_control),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &ak4641_input_mux_control),
+ SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
+ &ak4641_mono2_control),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("MOUT1"),
+ SND_SOC_DAPM_OUTPUT("MOUT2"),
+ SND_SOC_DAPM_OUTPUT("MICOUT"),
+
+ SND_SOC_DAPM_ADC("ADC", "HiFi Capture", AK4641_PM1, 0, 0),
+ SND_SOC_DAPM_PGA("Mic", AK4641_PM1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX In", AK4641_PM1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out", AK4641_PM1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line Out", AK4641_PM1, 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", AK4641_PM2, 0, 0),
+ SND_SOC_DAPM_PGA("Mono Out 2", AK4641_PM2, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("Voice ADC", "Voice Capture", AK4641_BTIF, 0, 0),
+ SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AK4641_BTIF, 1, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4641_MIC, 3, 0),
+ SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4641_MIC, 4, 0),
+
+ SND_SOC_DAPM_INPUT("MICIN"),
+ SND_SOC_DAPM_INPUT("MICEXT"),
+ SND_SOC_DAPM_INPUT("AUX"),
+ SND_SOC_DAPM_INPUT("AIN"),
+};
+
+static const struct snd_soc_dapm_route ak4641_audio_map[] = {
+ /* Stereo Mixer */
+ {"Stereo Mixer", "Playback Switch", "DAC"},
+ {"Stereo Mixer", "Mic Sidetone Switch", "Input Mux"},
+ {"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
+
+ /* Mono 1 Mixer */
+ {"Mono1 Mixer", "Mic Mono Sidetone Switch", "Input Mux"},
+ {"Mono1 Mixer", "Mono Playback Switch", "DAC"},
+
+ /* Mic */
+ {"Mic", NULL, "AIN"},
+ {"Mic Mux", "Internal", "Mic Int Bias"},
+ {"Mic Mux", "External", "Mic Ext Bias"},
+ {"Mic Int Bias", NULL, "MICIN"},
+ {"Mic Ext Bias", NULL, "MICEXT"},
+ {"MICOUT", NULL, "Mic Mux"},
+
+ /* Input Mux */
+ {"Input Mux", "Microphone", "Mic"},
+ {"Input Mux", "Voice DAC", "Voice DAC"},
+
+ /* Line Out */
+ {"LOUT", NULL, "Line Out"},
+ {"ROUT", NULL, "Line Out"},
+ {"Line Out", NULL, "Stereo Mixer"},
+
+ /* Mono 1 Out */
+ {"MOUT1", NULL, "Mono Out"},
+ {"Mono Out", NULL, "Mono1 Mixer"},
+
+ /* Mono 2 Out */
+ {"MOUT2", NULL, "Mono 2 Enable"},
+ {"Mono 2 Enable", "Switch", "Mono Out 2"},
+ {"Mono Out 2", NULL, "Stereo Mixer"},
+
+ {"Voice ADC", NULL, "Mono 2 Enable"},
+
+ /* Aux In */
+ {"AUX In", NULL, "AUX"},
+
+ /* ADC */
+ {"ADC", NULL, "Input Mixer"},
+ {"Input Mixer", "Mic Capture Switch", "Mic"},
+ {"Input Mixer", "Aux Capture Switch", "AUX In"},
+};
+
+static int ak4641_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+
+ ak4641->sysclk = freq;
+ return 0;
+}
+
+static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ int rate = params_rate(params), fs = 256;
+ u8 mode2;
+
+ if (rate)
+ fs = ak4641->sysclk / rate;
+ else
+ return -EINVAL;
+
+ /* set fs */
+ switch (fs) {
+ case 1024:
+ mode2 = (0x2 << 5);
+ break;
+ case 512:
+ mode2 = (0x1 << 5);
+ break;
+ case 256:
+ mode2 = (0x0 << 5);
+ break;
+ default:
+ dev_err(codec->dev, "Error: unsupported fs=%d\n", fs);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, 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);
+ }
+
+ return 0;
+}
+
+static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 btif;
+ int ret;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ btif = (0x3 << 5);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ btif = (0x2 << 5);
+ break;
+ case SND_SOC_DAIFMT_DSP_A: /* MSB after FRM */
+ btif = (0x0 << 5);
+ break;
+ case SND_SOC_DAIFMT_DSP_B: /* MSB during FRM */
+ btif = (0x1 << 5);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_update_bits(codec, AK4641_BTIF, (0x3 << 5), btif);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 mode1 = 0;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ mode1 = 0x02;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode1 = 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_write(codec, AK4641_MODE1, mode1);
+}
+
+static int ak4641_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ return snd_soc_update_bits(codec, AK4641_DAC, 0x20, mute ? 0x20 : 0);
+}
+
+static int ak4641_set_bias_level(struct snd_soc_codec *codec,
+ 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;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* unmute */
+ snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* mute */
+ snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0x20);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 1);
+ mdelay(1);
+ if (pdata && gpio_is_valid(pdata->gpio_npdn))
+ gpio_set_value(pdata->gpio_npdn, 1);
+ mdelay(1);
+
+ ret = regcache_sync(ak4641->regmap);
+ if (ret) {
+ dev_err(codec->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);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0);
+ if (pdata && gpio_is_valid(pdata->gpio_npdn))
+ gpio_set_value(pdata->gpio_npdn, 0);
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 0);
+ regcache_mark_dirty(ak4641->regmap);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define AK4641_RATES (SNDRV_PCM_RATE_8000_48000)
+#define AK4641_RATES_BT (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000)
+#define AK4641_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+static const struct snd_soc_dai_ops ak4641_i2s_dai_ops = {
+ .hw_params = ak4641_i2s_hw_params,
+ .set_fmt = ak4641_i2s_set_dai_fmt,
+ .digital_mute = ak4641_mute,
+ .set_sysclk = ak4641_set_dai_sysclk,
+};
+
+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,
+ .set_sysclk = ak4641_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver ak4641_dai[] = {
+{
+ .name = "ak4641-hifi",
+ .id = 1,
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4641_RATES,
+ .formats = AK4641_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4641_RATES,
+ .formats = AK4641_FORMATS,
+ },
+ .ops = &ak4641_i2s_dai_ops,
+ .symmetric_rates = 1,
+},
+{
+ .name = "ak4641-voice",
+ .id = 1,
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = AK4641_RATES_BT,
+ .formats = AK4641_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Voice Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = AK4641_RATES_BT,
+ .formats = AK4641_FORMATS,
+ },
+ .ops = &ak4641_pcm_dai_ops,
+ .symmetric_rates = 1,
+},
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
+ .controls = ak4641_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4641_snd_controls),
+ .dapm_widgets = ak4641_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets),
+ .dapm_routes = ak4641_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
+ .set_bias_level = ak4641_set_bias_level,
+ .suspend_bias_off = true,
+};
+
+static const struct regmap_config ak4641_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK4641_BTIF,
+ .reg_defaults = ak4641_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4641_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int ak4641_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ak4641_platform_data *pdata = i2c->dev.platform_data;
+ struct ak4641_priv *ak4641;
+ int ret;
+
+ ak4641 = devm_kzalloc(&i2c->dev, sizeof(struct ak4641_priv),
+ GFP_KERNEL);
+ if (!ak4641)
+ return -ENOMEM;
+
+ ak4641->regmap = devm_regmap_init_i2c(i2c, &ak4641_regmap);
+ if (IS_ERR(ak4641->regmap))
+ return PTR_ERR(ak4641->regmap);
+
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power)) {
+ ret = gpio_request_one(pdata->gpio_power,
+ GPIOF_OUT_INIT_LOW, "ak4641 power");
+ if (ret)
+ goto err_out;
+ }
+ if (gpio_is_valid(pdata->gpio_npdn)) {
+ ret = gpio_request_one(pdata->gpio_npdn,
+ GPIOF_OUT_INIT_LOW, "ak4641 npdn");
+ if (ret)
+ goto err_gpio;
+
+ udelay(1); /* > 150 ns */
+ gpio_set_value(pdata->gpio_npdn, 1);
+ }
+ }
+
+ i2c_set_clientdata(i2c, ak4641);
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ak4641,
+ ak4641_dai, ARRAY_SIZE(ak4641_dai));
+ if (ret != 0)
+ goto err_gpio2;
+
+ return 0;
+
+err_gpio2:
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 0);
+ if (gpio_is_valid(pdata->gpio_npdn))
+ gpio_free(pdata->gpio_npdn);
+ }
+err_gpio:
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_free(pdata->gpio_power);
+err_out:
+ return ret;
+}
+
+static int 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);
+ gpio_free(pdata->gpio_power);
+ }
+ 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 },
+ { }
+};
+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,
+ .id_table = ak4641_i2c_id,
+};
+
+module_i2c_driver(ak4641_i2c_driver);
+
+MODULE_DESCRIPTION("SoC AK4641 driver");
+MODULE_AUTHOR("Harald Welte <laforge@gnufiish.org>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ak4641.h b/kernel/sound/soc/codecs/ak4641.h
new file mode 100644
index 000000000..4a263248e
--- /dev/null
+++ b/kernel/sound/soc/codecs/ak4641.h
@@ -0,0 +1,47 @@
+/*
+ * 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/kernel/sound/soc/codecs/ak4642.c b/kernel/sound/soc/codecs/ak4642.c
new file mode 100644
index 000000000..13585e88f
--- /dev/null
+++ b/kernel/sound/soc/codecs/ak4642.c
@@ -0,0 +1,642 @@
+/*
+ * 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.
+ */
+
+/* ** CAUTION **
+ *
+ * This is very simple driver.
+ * It can use headphone output / stereo input only
+ *
+ * AK4642 is tested.
+ * AK4643 is tested.
+ * AK4648 is tested.
+ */
+
+#include <linux/delay.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/initval.h>
+#include <sound/tlv.h>
+
+#define PW_MGMT1 0x00
+#define PW_MGMT2 0x01
+#define SG_SL1 0x02
+#define SG_SL2 0x03
+#define MD_CTL1 0x04
+#define MD_CTL2 0x05
+#define TIMER 0x06
+#define ALC_CTL1 0x07
+#define ALC_CTL2 0x08
+#define L_IVC 0x09
+#define L_DVC 0x0a
+#define ALC_CTL3 0x0b
+#define R_IVC 0x0c
+#define R_DVC 0x0d
+#define MD_CTL3 0x0e
+#define MD_CTL4 0x0f
+#define PW_MGMT3 0x10
+#define DF_S 0x11
+#define FIL3_0 0x12
+#define FIL3_1 0x13
+#define FIL3_2 0x14
+#define FIL3_3 0x15
+#define EQ_0 0x16
+#define EQ_1 0x17
+#define EQ_2 0x18
+#define EQ_3 0x19
+#define EQ_4 0x1a
+#define EQ_5 0x1b
+#define FIL1_0 0x1c
+#define FIL1_1 0x1d
+#define FIL1_2 0x1e
+#define FIL1_3 0x1f
+#define PW_MGMT4 0x20
+#define MD_CTL5 0x21
+#define LO_MS 0x22
+#define HP_MS 0x23
+#define SPK_MS 0x24
+
+/* PW_MGMT1*/
+#define PMVCM (1 << 6) /* VCOM Power Management */
+#define PMMIN (1 << 5) /* MIN Input Power Management */
+#define PMDAC (1 << 2) /* DAC Power Management */
+#define PMADL (1 << 0) /* MIC Amp Lch and ADC Lch Power Management */
+
+/* PW_MGMT2 */
+#define HPMTN (1 << 6)
+#define PMHPL (1 << 5)
+#define PMHPR (1 << 4)
+#define MS (1 << 3) /* master/slave select */
+#define MCKO (1 << 1)
+#define PMPLL (1 << 0)
+
+#define PMHP_MASK (PMHPL | PMHPR)
+#define PMHP PMHP_MASK
+
+/* PW_MGMT3 */
+#define PMADR (1 << 0) /* MIC L / ADC R Power Management */
+
+/* SG_SL1 */
+#define MINS (1 << 6) /* Switch from MIN to Speaker */
+#define DACL (1 << 4) /* Switch from DAC to Stereo or Receiver */
+#define PMMP (1 << 2) /* MPWR pin Power Management */
+#define MGAIN0 (1 << 0) /* MIC amp gain*/
+
+/* SG_SL2 */
+#define LOPS (1 << 6) /* Stero Line-out Power Save Mode */
+
+/* TIMER */
+#define ZTM(param) ((param & 0x3) << 4) /* ALC Zero Crossing TimeOut */
+#define WTM(param) (((param & 0x4) << 4) | ((param & 0x3) << 2))
+
+/* ALC_CTL1 */
+#define ALC (1 << 5) /* ALC Enable */
+#define LMTH0 (1 << 0) /* ALC Limiter / Recovery Level */
+
+/* MD_CTL1 */
+#define PLL3 (1 << 7)
+#define PLL2 (1 << 6)
+#define PLL1 (1 << 5)
+#define PLL0 (1 << 4)
+#define PLL_MASK (PLL3 | PLL2 | PLL1 | PLL0)
+
+#define BCKO_MASK (1 << 3)
+#define BCKO_64 BCKO_MASK
+
+#define DIF_MASK (3 << 0)
+#define DSP (0 << 0)
+#define RIGHT_J (1 << 0)
+#define LEFT_J (2 << 0)
+#define I2S (3 << 0)
+
+/* MD_CTL2 */
+#define FS0 (1 << 0)
+#define FS1 (1 << 1)
+#define FS2 (1 << 2)
+#define FS3 (1 << 5)
+#define FS_MASK (FS0 | FS1 | FS2 | FS3)
+
+/* MD_CTL3 */
+#define BST1 (1 << 3)
+
+/* MD_CTL4 */
+#define DACH (1 << 0)
+
+struct ak4642_drvdata {
+ const struct regmap_config *regmap_config;
+ int extended_frequencies;
+};
+
+struct ak4642_priv {
+ const struct ak4642_drvdata *drvdata;
+};
+
+/*
+ * Playback Volume (table 39)
+ *
+ * max : 0x00 : +12.0 dB
+ * ( 0.5 dB step )
+ * min : 0xFE : -115.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(out_tlv, -11550, 50, 1);
+
+static const struct snd_kcontrol_new ak4642_snd_controls[] = {
+
+ SOC_DOUBLE_R_TLV("Digital Playback Volume", L_DVC, R_DVC,
+ 0, 0xFF, 1, out_tlv),
+ SOC_SINGLE("ALC Capture Switch", ALC_CTL1, 5, 1, 0),
+ SOC_SINGLE("ALC Capture ZC Switch", ALC_CTL1, 4, 1, 1),
+};
+
+static const struct snd_kcontrol_new ak4642_headphone_control =
+ SOC_DAPM_SINGLE("Switch", PW_MGMT2, 6, 1, 0);
+
+static const struct snd_kcontrol_new ak4642_lout_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACL", SG_SL1, 4, 1, 0),
+};
+
+/* event handlers */
+static int ak4642_lout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Power save mode ON */
+ snd_soc_update_bits(codec, SG_SL2, LOPS, LOPS);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ /* Power save mode OFF */
+ mdelay(300);
+ snd_soc_update_bits(codec, SG_SL2, LOPS, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = {
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HPOUTL"),
+ SND_SOC_DAPM_OUTPUT("HPOUTR"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT"),
+
+ SND_SOC_DAPM_PGA("HPL Out", PW_MGMT2, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPR Out", PW_MGMT2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("Headphone Enable", SND_SOC_NOPM, 0, 0,
+ &ak4642_headphone_control),
+
+ SND_SOC_DAPM_PGA("DACH", MD_CTL4, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER_E("LINEOUT Mixer", PW_MGMT1, 3, 0,
+ &ak4642_lout_mixer_controls[0],
+ ARRAY_SIZE(ak4642_lout_mixer_controls),
+ ak4642_lout_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("DAC", NULL, PW_MGMT1, 2, 0),
+};
+
+static const struct snd_soc_dapm_route ak4642_intercon[] = {
+
+ /* Outputs */
+ {"HPOUTL", NULL, "HPL Out"},
+ {"HPOUTR", NULL, "HPR Out"},
+ {"LINEOUT", NULL, "LINEOUT Mixer"},
+
+ {"HPL Out", NULL, "Headphone Enable"},
+ {"HPR Out", NULL, "Headphone Enable"},
+
+ {"Headphone Enable", "Switch", "DACH"},
+
+ {"DACH", NULL, "DAC"},
+
+ {"LINEOUT Mixer", "DACL", "DAC"},
+
+ { "DAC", NULL, "Playback" },
+};
+
+/*
+ * ak4642 register cache
+ */
+static const struct reg_default ak4642_reg[] = {
+ { 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 },
+ { 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
+ { 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 },
+ { 12, 0xe1 }, { 13, 0x18 }, { 14, 0x11 }, { 15, 0x08 },
+ { 16, 0x00 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x00 },
+ { 20, 0x00 }, { 21, 0x00 }, { 22, 0x00 }, { 23, 0x00 },
+ { 24, 0x00 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0x00 },
+ { 28, 0x00 }, { 29, 0x00 }, { 30, 0x00 }, { 31, 0x00 },
+ { 32, 0x00 }, { 33, 0x00 }, { 34, 0x00 }, { 35, 0x00 },
+ { 36, 0x00 },
+};
+
+static const struct reg_default ak4648_reg[] = {
+ { 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 },
+ { 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
+ { 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 },
+ { 12, 0xe1 }, { 13, 0x18 }, { 14, 0x11 }, { 15, 0xb8 },
+ { 16, 0x00 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x00 },
+ { 20, 0x00 }, { 21, 0x00 }, { 22, 0x00 }, { 23, 0x00 },
+ { 24, 0x00 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0x00 },
+ { 28, 0x00 }, { 29, 0x00 }, { 30, 0x00 }, { 31, 0x00 },
+ { 32, 0x00 }, { 33, 0x00 }, { 34, 0x00 }, { 35, 0x00 },
+ { 36, 0x00 }, { 37, 0x88 }, { 38, 0x88 }, { 39, 0x08 },
+};
+
+static int ak4642_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (is_play) {
+ /*
+ * start headphone output
+ *
+ * PLL, Master Mode
+ * Audio I/F Format :MSB justified (ADC & DAC)
+ * Bass Boost Level : Middle
+ *
+ * This operation came from example code of
+ * "ASAHI KASEI AK4642" (japanese) manual p97.
+ */
+ snd_soc_write(codec, L_IVC, 0x91); /* volume */
+ snd_soc_write(codec, R_IVC, 0x91); /* volume */
+ } else {
+ /*
+ * start stereo input
+ *
+ * PLL Master Mode
+ * Audio I/F Format:MSB justified (ADC & DAC)
+ * Pre MIC AMP:+20dB
+ * MIC Power On
+ * ALC setting:Refer to Table 35
+ * ALC bit=“1”
+ *
+ * This operation came from example code of
+ * "ASAHI KASEI AK4642" (japanese) manual p94.
+ */
+ snd_soc_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);
+ }
+
+ return 0;
+}
+
+static void ak4642_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct snd_soc_codec *codec = dai->codec;
+
+ 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);
+ }
+}
+
+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);
+ u8 pll;
+ int extended_freq = 0;
+
+ switch (freq) {
+ case 11289600:
+ pll = PLL2;
+ break;
+ case 12288000:
+ pll = PLL2 | PLL0;
+ break;
+ case 12000000:
+ pll = PLL2 | PLL1;
+ break;
+ case 24000000:
+ pll = PLL2 | PLL1 | PLL0;
+ break;
+ case 13500000:
+ pll = PLL3 | PLL2;
+ break;
+ case 27000000:
+ pll = PLL3 | PLL2 | PLL0;
+ break;
+ case 19200000:
+ pll = PLL3;
+ extended_freq = 1;
+ break;
+ case 13000000:
+ pll = PLL3 | PLL2 | PLL1;
+ extended_freq = 1;
+ break;
+ case 26000000:
+ pll = PLL3 | PLL2 | PLL1 | PLL0;
+ extended_freq = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (extended_freq && !priv->drvdata->extended_frequencies)
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, 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;
+ 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:
+ data |= MS;
+ bcko = BCKO_64;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ 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);
+
+ /* format type */
+ data = 0;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ data = LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ data = I2S;
+ break;
+ /* FIXME
+ * Please add RIGHT_J / DSP support here
+ */
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, MD_CTL1, DIF_MASK, data);
+
+ return 0;
+}
+
+static int ak4642_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 rate;
+
+ 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);
+
+ return 0;
+}
+
+static int ak4642_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, PW_MGMT1, 0x00);
+ break;
+ default:
+ snd_soc_update_bits(codec, PW_MGMT1, PMVCM, PMVCM);
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ak4642_dai_ops = {
+ .startup = ak4642_dai_startup,
+ .shutdown = ak4642_dai_shutdown,
+ .set_sysclk = ak4642_dai_set_sysclk,
+ .set_fmt = ak4642_dai_set_fmt,
+ .hw_params = ak4642_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver ak4642_dai = {
+ .name = "ak4642-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE },
+ .ops = &ak4642_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int ak4642_resume(struct snd_soc_codec *codec)
+{
+ struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+ regcache_mark_dirty(regmap);
+ regcache_sync(regmap);
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
+ .resume = ak4642_resume,
+ .set_bias_level = ak4642_set_bias_level,
+ .controls = ak4642_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4642_snd_controls),
+ .dapm_widgets = ak4642_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4642_dapm_widgets),
+ .dapm_routes = ak4642_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4642_intercon),
+};
+
+static const struct regmap_config ak4642_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ARRAY_SIZE(ak4642_reg) + 1,
+ .reg_defaults = ak4642_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak4642_reg),
+};
+
+static const struct regmap_config ak4648_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ARRAY_SIZE(ak4648_reg) + 1,
+ .reg_defaults = ak4648_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak4648_reg),
+};
+
+static const struct ak4642_drvdata ak4642_drvdata = {
+ .regmap_config = &ak4642_regmap,
+};
+
+static const struct ak4642_drvdata ak4643_drvdata = {
+ .regmap_config = &ak4642_regmap,
+};
+
+static const struct ak4642_drvdata ak4648_drvdata = {
+ .regmap_config = &ak4648_regmap,
+ .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)
+{
+ struct device_node *np = i2c->dev.of_node;
+ const struct ak4642_drvdata *drvdata = NULL;
+ struct regmap *regmap;
+ struct ak4642_priv *priv;
+
+ 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 (!drvdata) {
+ dev_err(&i2c->dev, "Unknown device type\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->drvdata = drvdata;
+
+ i2c_set_clientdata(i2c, priv);
+
+ regmap = devm_regmap_init_i2c(i2c, drvdata->regmap_config);
+ 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;
+}
+
+static const struct of_device_id ak4642_of_match[] = {
+ { .compatible = "asahi-kasei,ak4642", .data = &ak4642_drvdata},
+ { .compatible = "asahi-kasei,ak4643", .data = &ak4643_drvdata},
+ { .compatible = "asahi-kasei,ak4648", .data = &ak4648_drvdata},
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4642_of_match);
+
+static const struct i2c_device_id ak4642_i2c_id[] = {
+ { "ak4642", (kernel_ulong_t)&ak4642_drvdata },
+ { "ak4643", (kernel_ulong_t)&ak4643_drvdata },
+ { "ak4648", (kernel_ulong_t)&ak4648_drvdata },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id);
+
+static struct i2c_driver ak4642_i2c_driver = {
+ .driver = {
+ .name = "ak4642-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = ak4642_of_match,
+ },
+ .probe = ak4642_i2c_probe,
+ .remove = ak4642_i2c_remove,
+ .id_table = ak4642_i2c_id,
+};
+
+module_i2c_driver(ak4642_i2c_driver);
+
+MODULE_DESCRIPTION("Soc AK4642 driver");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ak4671.c b/kernel/sound/soc/codecs/ak4671.c
new file mode 100644
index 000000000..2a58b1dcc
--- /dev/null
+++ b/kernel/sound/soc/codecs/ak4671.c
@@ -0,0 +1,678 @@
+/*
+ * 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>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "ak4671.h"
+
+
+/* ak4671 register cache & default register settings */
+static const struct reg_default ak4671_reg_defaults[] = {
+ { 0x00, 0x00 }, /* AK4671_AD_DA_POWER_MANAGEMENT (0x00) */
+ { 0x01, 0xf6 }, /* AK4671_PLL_MODE_SELECT0 (0x01) */
+ { 0x02, 0x00 }, /* AK4671_PLL_MODE_SELECT1 (0x02) */
+ { 0x03, 0x02 }, /* AK4671_FORMAT_SELECT (0x03) */
+ { 0x04, 0x00 }, /* AK4671_MIC_SIGNAL_SELECT (0x04) */
+ { 0x05, 0x55 }, /* AK4671_MIC_AMP_GAIN (0x05) */
+ { 0x06, 0x00 }, /* AK4671_MIXING_POWER_MANAGEMENT0 (0x06) */
+ { 0x07, 0x00 }, /* AK4671_MIXING_POWER_MANAGEMENT1 (0x07) */
+ { 0x08, 0xb5 }, /* AK4671_OUTPUT_VOLUME_CONTROL (0x08) */
+ { 0x09, 0x00 }, /* AK4671_LOUT1_SIGNAL_SELECT (0x09) */
+ { 0x0a, 0x00 }, /* AK4671_ROUT1_SIGNAL_SELECT (0x0a) */
+ { 0x0b, 0x00 }, /* AK4671_LOUT2_SIGNAL_SELECT (0x0b) */
+ { 0x0c, 0x00 }, /* AK4671_ROUT2_SIGNAL_SELECT (0x0c) */
+ { 0x0d, 0x00 }, /* AK4671_LOUT3_SIGNAL_SELECT (0x0d) */
+ { 0x0e, 0x00 }, /* AK4671_ROUT3_SIGNAL_SELECT (0x0e) */
+ { 0x0f, 0x00 }, /* AK4671_LOUT1_POWER_MANAGERMENT (0x0f) */
+ { 0x10, 0x00 }, /* AK4671_LOUT2_POWER_MANAGERMENT (0x10) */
+ { 0x11, 0x80 }, /* AK4671_LOUT3_POWER_MANAGERMENT (0x11) */
+ { 0x12, 0x91 }, /* AK4671_LCH_INPUT_VOLUME_CONTROL (0x12) */
+ { 0x13, 0x91 }, /* AK4671_RCH_INPUT_VOLUME_CONTROL (0x13) */
+ { 0x14, 0xe1 }, /* AK4671_ALC_REFERENCE_SELECT (0x14) */
+ { 0x15, 0x00 }, /* AK4671_DIGITAL_MIXING_CONTROL (0x15) */
+ { 0x16, 0x00 }, /* AK4671_ALC_TIMER_SELECT (0x16) */
+ { 0x17, 0x00 }, /* AK4671_ALC_MODE_CONTROL (0x17) */
+ { 0x18, 0x02 }, /* AK4671_MODE_CONTROL1 (0x18) */
+ { 0x19, 0x01 }, /* AK4671_MODE_CONTROL2 (0x19) */
+ { 0x1a, 0x18 }, /* AK4671_LCH_OUTPUT_VOLUME_CONTROL (0x1a) */
+ { 0x1b, 0x18 }, /* AK4671_RCH_OUTPUT_VOLUME_CONTROL (0x1b) */
+ { 0x1c, 0x00 }, /* AK4671_SIDETONE_A_CONTROL (0x1c) */
+ { 0x1d, 0x02 }, /* AK4671_DIGITAL_FILTER_SELECT (0x1d) */
+ { 0x1e, 0x00 }, /* AK4671_FIL3_COEFFICIENT0 (0x1e) */
+ { 0x1f, 0x00 }, /* AK4671_FIL3_COEFFICIENT1 (0x1f) */
+ { 0x20, 0x00 }, /* AK4671_FIL3_COEFFICIENT2 (0x20) */
+ { 0x21, 0x00 }, /* AK4671_FIL3_COEFFICIENT3 (0x21) */
+ { 0x22, 0x00 }, /* AK4671_EQ_COEFFICIENT0 (0x22) */
+ { 0x23, 0x00 }, /* AK4671_EQ_COEFFICIENT1 (0x23) */
+ { 0x24, 0x00 }, /* AK4671_EQ_COEFFICIENT2 (0x24) */
+ { 0x25, 0x00 }, /* AK4671_EQ_COEFFICIENT3 (0x25) */
+ { 0x26, 0x00 }, /* AK4671_EQ_COEFFICIENT4 (0x26) */
+ { 0x27, 0x00 }, /* AK4671_EQ_COEFFICIENT5 (0x27) */
+ { 0x28, 0xa9 }, /* AK4671_FIL1_COEFFICIENT0 (0x28) */
+ { 0x29, 0x1f }, /* AK4671_FIL1_COEFFICIENT1 (0x29) */
+ { 0x2a, 0xad }, /* AK4671_FIL1_COEFFICIENT2 (0x2a) */
+ { 0x2b, 0x20 }, /* AK4671_FIL1_COEFFICIENT3 (0x2b) */
+ { 0x2c, 0x00 }, /* AK4671_FIL2_COEFFICIENT0 (0x2c) */
+ { 0x2d, 0x00 }, /* AK4671_FIL2_COEFFICIENT1 (0x2d) */
+ { 0x2e, 0x00 }, /* AK4671_FIL2_COEFFICIENT2 (0x2e) */
+ { 0x2f, 0x00 }, /* AK4671_FIL2_COEFFICIENT3 (0x2f) */
+ { 0x30, 0x00 }, /* AK4671_DIGITAL_FILTER_SELECT2 (0x30) */
+
+ { 0x32, 0x00 }, /* AK4671_E1_COEFFICIENT0 (0x32) */
+ { 0x33, 0x00 }, /* AK4671_E1_COEFFICIENT1 (0x33) */
+ { 0x34, 0x00 }, /* AK4671_E1_COEFFICIENT2 (0x34) */
+ { 0x35, 0x00 }, /* AK4671_E1_COEFFICIENT3 (0x35) */
+ { 0x36, 0x00 }, /* AK4671_E1_COEFFICIENT4 (0x36) */
+ { 0x37, 0x00 }, /* AK4671_E1_COEFFICIENT5 (0x37) */
+ { 0x38, 0x00 }, /* AK4671_E2_COEFFICIENT0 (0x38) */
+ { 0x39, 0x00 }, /* AK4671_E2_COEFFICIENT1 (0x39) */
+ { 0x3a, 0x00 }, /* AK4671_E2_COEFFICIENT2 (0x3a) */
+ { 0x3b, 0x00 }, /* AK4671_E2_COEFFICIENT3 (0x3b) */
+ { 0x3c, 0x00 }, /* AK4671_E2_COEFFICIENT4 (0x3c) */
+ { 0x3d, 0x00 }, /* AK4671_E2_COEFFICIENT5 (0x3d) */
+ { 0x3e, 0x00 }, /* AK4671_E3_COEFFICIENT0 (0x3e) */
+ { 0x3f, 0x00 }, /* AK4671_E3_COEFFICIENT1 (0x3f) */
+ { 0x40, 0x00 }, /* AK4671_E3_COEFFICIENT2 (0x40) */
+ { 0x41, 0x00 }, /* AK4671_E3_COEFFICIENT3 (0x41) */
+ { 0x42, 0x00 }, /* AK4671_E3_COEFFICIENT4 (0x42) */
+ { 0x43, 0x00 }, /* AK4671_E3_COEFFICIENT5 (0x43) */
+ { 0x44, 0x00 }, /* AK4671_E4_COEFFICIENT0 (0x44) */
+ { 0x45, 0x00 }, /* AK4671_E4_COEFFICIENT1 (0x45) */
+ { 0x46, 0x00 }, /* AK4671_E4_COEFFICIENT2 (0x46) */
+ { 0x47, 0x00 }, /* AK4671_E4_COEFFICIENT3 (0x47) */
+ { 0x48, 0x00 }, /* AK4671_E4_COEFFICIENT4 (0x48) */
+ { 0x49, 0x00 }, /* AK4671_E4_COEFFICIENT5 (0x49) */
+ { 0x4a, 0x00 }, /* AK4671_E5_COEFFICIENT0 (0x4a) */
+ { 0x4b, 0x00 }, /* AK4671_E5_COEFFICIENT1 (0x4b) */
+ { 0x4c, 0x00 }, /* AK4671_E5_COEFFICIENT2 (0x4c) */
+ { 0x4d, 0x00 }, /* AK4671_E5_COEFFICIENT3 (0x4d) */
+ { 0x4e, 0x00 }, /* AK4671_E5_COEFFICIENT4 (0x4e) */
+ { 0x4f, 0x00 }, /* AK4671_E5_COEFFICIENT5 (0x4f) */
+ { 0x50, 0x88 }, /* AK4671_EQ_CONTROL_250HZ_100HZ (0x50) */
+ { 0x51, 0x88 }, /* AK4671_EQ_CONTROL_3500HZ_1KHZ (0x51) */
+ { 0x52, 0x08 }, /* AK4671_EQ_CONTRO_10KHZ (0x52) */
+ { 0x53, 0x00 }, /* AK4671_PCM_IF_CONTROL0 (0x53) */
+ { 0x54, 0x00 }, /* AK4671_PCM_IF_CONTROL1 (0x54) */
+ { 0x55, 0x00 }, /* AK4671_PCM_IF_CONTROL2 (0x55) */
+ { 0x56, 0x18 }, /* AK4671_DIGITAL_VOLUME_B_CONTROL (0x56) */
+ { 0x57, 0x18 }, /* AK4671_DIGITAL_VOLUME_C_CONTROL (0x57) */
+ { 0x58, 0x00 }, /* AK4671_SIDETONE_VOLUME_CONTROL (0x58) */
+ { 0x59, 0x00 }, /* AK4671_DIGITAL_MIXING_CONTROL2 (0x59) */
+ { 0x5a, 0x00 }, /* AK4671_SAR_ADC_CONTROL (0x5a) */
+};
+
+/*
+ * LOUT1/ROUT1 output volume control:
+ * from -24 to 6 dB in 6 dB steps (mute instead of -30 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out1_tlv, -3000, 600, 1);
+
+/*
+ * LOUT2/ROUT2 output volume control:
+ * from -33 to 6 dB in 3 dB steps (mute instead of -33 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out2_tlv, -3300, 300, 1);
+
+/*
+ * LOUT3/ROUT3 output volume control:
+ * from -6 to 3 dB in 3 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(out3_tlv, -600, 300, 0);
+
+/*
+ * Mic amp gain control:
+ * from -15 to 30 dB in 3 dB steps
+ * REVISIT: The actual min value(0x01) is -12 dB and the reg value 0x00 is not
+ * available
+ */
+static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -1500, 300, 0);
+
+static const struct snd_kcontrol_new ak4671_snd_controls[] = {
+ /* Common playback gain controls */
+ SOC_SINGLE_TLV("Line Output1 Playback Volume",
+ AK4671_OUTPUT_VOLUME_CONTROL, 0, 0x6, 0, out1_tlv),
+ SOC_SINGLE_TLV("Headphone Output2 Playback Volume",
+ AK4671_OUTPUT_VOLUME_CONTROL, 4, 0xd, 0, out2_tlv),
+ SOC_SINGLE_TLV("Line Output3 Playback Volume",
+ AK4671_LOUT3_POWER_MANAGERMENT, 6, 0x3, 0, out3_tlv),
+
+ /* Common capture gain controls */
+ SOC_DOUBLE_TLV("Mic Amp Capture Volume",
+ AK4671_MIC_AMP_GAIN, 0, 4, 0xf, 0, mic_amp_tlv),
+};
+
+/* event handlers */
+static int ak4671_out2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, AK4671_LOUT2_POWER_MANAGERMENT,
+ AK4671_MUTEN, AK4671_MUTEN);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, AK4671_LOUT2_POWER_MANAGERMENT,
+ AK4671_MUTEN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+/* Output Mixers */
+static const struct snd_kcontrol_new ak4671_lout1_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACL", AK4671_LOUT1_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("LINL1", AK4671_LOUT1_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("LINL2", AK4671_LOUT1_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("LINL3", AK4671_LOUT1_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("LINL4", AK4671_LOUT1_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPL", AK4671_LOUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout1_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACR", AK4671_ROUT1_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("RINR1", AK4671_ROUT1_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("RINR2", AK4671_ROUT1_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("RINR3", AK4671_ROUT1_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("RINR4", AK4671_ROUT1_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPR", AK4671_ROUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout2_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACHL", AK4671_LOUT2_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("LINH1", AK4671_LOUT2_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("LINH2", AK4671_LOUT2_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("LINH3", AK4671_LOUT2_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("LINH4", AK4671_LOUT2_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPHL", AK4671_LOUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout2_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACHR", AK4671_ROUT2_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("RINH1", AK4671_ROUT2_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("RINH2", AK4671_ROUT2_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("RINH3", AK4671_ROUT2_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("RINH4", AK4671_ROUT2_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPHR", AK4671_ROUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout3_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACSL", AK4671_LOUT3_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("LINS1", AK4671_LOUT3_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("LINS2", AK4671_LOUT3_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("LINS3", AK4671_LOUT3_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("LINS4", AK4671_LOUT3_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPSL", AK4671_LOUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACSR", AK4671_ROUT3_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("RINS1", AK4671_ROUT3_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("RINS2", AK4671_ROUT3_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("RINS3", AK4671_ROUT3_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("RINS4", AK4671_ROUT3_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPSR", AK4671_ROUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+/* Input MUXs */
+static const char *ak4671_lin_mux_texts[] =
+ {"LIN1", "LIN2", "LIN3", "LIN4"};
+static SOC_ENUM_SINGLE_DECL(ak4671_lin_mux_enum,
+ AK4671_MIC_SIGNAL_SELECT, 0,
+ ak4671_lin_mux_texts);
+static const struct snd_kcontrol_new ak4671_lin_mux_control =
+ SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum);
+
+static const char *ak4671_rin_mux_texts[] =
+ {"RIN1", "RIN2", "RIN3", "RIN4"};
+static SOC_ENUM_SINGLE_DECL(ak4671_rin_mux_enum,
+ AK4671_MIC_SIGNAL_SELECT, 2,
+ ak4671_rin_mux_texts);
+static const struct snd_kcontrol_new ak4671_rin_mux_control =
+ SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum);
+
+static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("LIN1"),
+ SND_SOC_DAPM_INPUT("RIN1"),
+ SND_SOC_DAPM_INPUT("LIN2"),
+ SND_SOC_DAPM_INPUT("RIN2"),
+ SND_SOC_DAPM_INPUT("LIN3"),
+ SND_SOC_DAPM_INPUT("RIN3"),
+ SND_SOC_DAPM_INPUT("LIN4"),
+ SND_SOC_DAPM_INPUT("RIN4"),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("LOUT3"),
+ SND_SOC_DAPM_OUTPUT("ROUT3"),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("DAC Left", "Left HiFi Playback",
+ AK4671_AD_DA_POWER_MANAGEMENT, 6, 0),
+ SND_SOC_DAPM_DAC("DAC Right", "Right HiFi Playback",
+ AK4671_AD_DA_POWER_MANAGEMENT, 7, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("ADC Left", "Left HiFi Capture",
+ AK4671_AD_DA_POWER_MANAGEMENT, 4, 0),
+ SND_SOC_DAPM_ADC("ADC Right", "Right HiFi Capture",
+ AK4671_AD_DA_POWER_MANAGEMENT, 5, 0),
+
+ /* PGA */
+ SND_SOC_DAPM_PGA("LOUT2 Mix Amp",
+ AK4671_LOUT2_POWER_MANAGERMENT, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ROUT2 Mix Amp",
+ AK4671_LOUT2_POWER_MANAGERMENT, 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("LIN1 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RIN1 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LIN2 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RIN2 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LIN3 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RIN3 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LIN4 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RIN4 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 7, 0, NULL, 0),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("LOUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 0, 0,
+ &ak4671_lout1_mixer_controls[0],
+ ARRAY_SIZE(ak4671_lout1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("ROUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 1, 0,
+ &ak4671_rout1_mixer_controls[0],
+ ARRAY_SIZE(ak4671_rout1_mixer_controls)),
+ SND_SOC_DAPM_MIXER_E("LOUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+ 0, 0, &ak4671_lout2_mixer_controls[0],
+ ARRAY_SIZE(ak4671_lout2_mixer_controls),
+ ak4671_out2_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER_E("ROUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+ 1, 0, &ak4671_rout2_mixer_controls[0],
+ ARRAY_SIZE(ak4671_rout2_mixer_controls),
+ ak4671_out2_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER("LOUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 0, 0,
+ &ak4671_lout3_mixer_controls[0],
+ ARRAY_SIZE(ak4671_lout3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("ROUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 1, 0,
+ &ak4671_rout3_mixer_controls[0],
+ ARRAY_SIZE(ak4671_rout3_mixer_controls)),
+
+ /* Input MUXs */
+ SND_SOC_DAPM_MUX("LIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 2, 0,
+ &ak4671_lin_mux_control),
+ SND_SOC_DAPM_MUX("RIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 3, 0,
+ &ak4671_rin_mux_control),
+
+ /* Mic Power */
+ SND_SOC_DAPM_MICBIAS("Mic Bias", AK4671_AD_DA_POWER_MANAGEMENT, 1, 0),
+
+ /* Supply */
+ SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route ak4671_intercon[] = {
+ {"DAC Left", NULL, "PMPLL"},
+ {"DAC Right", NULL, "PMPLL"},
+ {"ADC Left", NULL, "PMPLL"},
+ {"ADC Right", NULL, "PMPLL"},
+
+ /* Outputs */
+ {"LOUT1", NULL, "LOUT1 Mixer"},
+ {"ROUT1", NULL, "ROUT1 Mixer"},
+ {"LOUT2", NULL, "LOUT2 Mix Amp"},
+ {"ROUT2", NULL, "ROUT2 Mix Amp"},
+ {"LOUT3", NULL, "LOUT3 Mixer"},
+ {"ROUT3", NULL, "ROUT3 Mixer"},
+
+ {"LOUT1 Mixer", "DACL", "DAC Left"},
+ {"ROUT1 Mixer", "DACR", "DAC Right"},
+ {"LOUT2 Mixer", "DACHL", "DAC Left"},
+ {"ROUT2 Mixer", "DACHR", "DAC Right"},
+ {"LOUT2 Mix Amp", NULL, "LOUT2 Mixer"},
+ {"ROUT2 Mix Amp", NULL, "ROUT2 Mixer"},
+ {"LOUT3 Mixer", "DACSL", "DAC Left"},
+ {"ROUT3 Mixer", "DACSR", "DAC Right"},
+
+ /* Inputs */
+ {"LIN MUX", "LIN1", "LIN1"},
+ {"LIN MUX", "LIN2", "LIN2"},
+ {"LIN MUX", "LIN3", "LIN3"},
+ {"LIN MUX", "LIN4", "LIN4"},
+
+ {"RIN MUX", "RIN1", "RIN1"},
+ {"RIN MUX", "RIN2", "RIN2"},
+ {"RIN MUX", "RIN3", "RIN3"},
+ {"RIN MUX", "RIN4", "RIN4"},
+
+ {"LIN1", NULL, "Mic Bias"},
+ {"RIN1", NULL, "Mic Bias"},
+ {"LIN2", NULL, "Mic Bias"},
+ {"RIN2", NULL, "Mic Bias"},
+
+ {"ADC Left", NULL, "LIN MUX"},
+ {"ADC Right", NULL, "RIN MUX"},
+
+ /* Analog Loops */
+ {"LIN1 Mixing Circuit", NULL, "LIN1"},
+ {"RIN1 Mixing Circuit", NULL, "RIN1"},
+ {"LIN2 Mixing Circuit", NULL, "LIN2"},
+ {"RIN2 Mixing Circuit", NULL, "RIN2"},
+ {"LIN3 Mixing Circuit", NULL, "LIN3"},
+ {"RIN3 Mixing Circuit", NULL, "RIN3"},
+ {"LIN4 Mixing Circuit", NULL, "LIN4"},
+ {"RIN4 Mixing Circuit", NULL, "RIN4"},
+
+ {"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"},
+ {"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"},
+ {"LOUT2 Mixer", "LINH1", "LIN1 Mixing Circuit"},
+ {"ROUT2 Mixer", "RINH1", "RIN1 Mixing Circuit"},
+ {"LOUT3 Mixer", "LINS1", "LIN1 Mixing Circuit"},
+ {"ROUT3 Mixer", "RINS1", "RIN1 Mixing Circuit"},
+
+ {"LOUT1 Mixer", "LINL2", "LIN2 Mixing Circuit"},
+ {"ROUT1 Mixer", "RINR2", "RIN2 Mixing Circuit"},
+ {"LOUT2 Mixer", "LINH2", "LIN2 Mixing Circuit"},
+ {"ROUT2 Mixer", "RINH2", "RIN2 Mixing Circuit"},
+ {"LOUT3 Mixer", "LINS2", "LIN2 Mixing Circuit"},
+ {"ROUT3 Mixer", "RINS2", "RIN2 Mixing Circuit"},
+
+ {"LOUT1 Mixer", "LINL3", "LIN3 Mixing Circuit"},
+ {"ROUT1 Mixer", "RINR3", "RIN3 Mixing Circuit"},
+ {"LOUT2 Mixer", "LINH3", "LIN3 Mixing Circuit"},
+ {"ROUT2 Mixer", "RINH3", "RIN3 Mixing Circuit"},
+ {"LOUT3 Mixer", "LINS3", "LIN3 Mixing Circuit"},
+ {"ROUT3 Mixer", "RINS3", "RIN3 Mixing Circuit"},
+
+ {"LOUT1 Mixer", "LINL4", "LIN4 Mixing Circuit"},
+ {"ROUT1 Mixer", "RINR4", "RIN4 Mixing Circuit"},
+ {"LOUT2 Mixer", "LINH4", "LIN4 Mixing Circuit"},
+ {"ROUT2 Mixer", "RINH4", "RIN4 Mixing Circuit"},
+ {"LOUT3 Mixer", "LINS4", "LIN4 Mixing Circuit"},
+ {"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
+};
+
+static int ak4671_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 fs;
+
+ fs = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+ fs &= ~AK4671_FS;
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs |= AK4671_FS_8KHZ;
+ break;
+ case 12000:
+ fs |= AK4671_FS_12KHZ;
+ break;
+ case 16000:
+ fs |= AK4671_FS_16KHZ;
+ break;
+ case 24000:
+ fs |= AK4671_FS_24KHZ;
+ break;
+ case 11025:
+ fs |= AK4671_FS_11_025KHZ;
+ break;
+ case 22050:
+ fs |= AK4671_FS_22_05KHZ;
+ break;
+ case 32000:
+ fs |= AK4671_FS_32KHZ;
+ break;
+ case 44100:
+ fs |= AK4671_FS_44_1KHZ;
+ break;
+ case 48000:
+ fs |= AK4671_FS_48KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, fs);
+
+ return 0;
+}
+
+static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 pll;
+
+ pll = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+ pll &= ~AK4671_PLL;
+
+ switch (freq) {
+ case 11289600:
+ pll |= AK4671_PLL_11_2896MHZ;
+ break;
+ case 12000000:
+ pll |= AK4671_PLL_12MHZ;
+ break;
+ case 12288000:
+ pll |= AK4671_PLL_12_288MHZ;
+ break;
+ case 13000000:
+ pll |= AK4671_PLL_13MHZ;
+ break;
+ case 13500000:
+ pll |= AK4671_PLL_13_5MHZ;
+ break;
+ case 19200000:
+ pll |= AK4671_PLL_19_2MHZ;
+ break;
+ case 24000000:
+ pll |= AK4671_PLL_24MHZ;
+ break;
+ case 26000000:
+ pll |= AK4671_PLL_26MHZ;
+ break;
+ case 27000000:
+ pll |= AK4671_PLL_27MHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, 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;
+ u8 mode;
+ u8 format;
+
+ /* set master/slave audio interface */
+ mode = snd_soc_read(codec, AK4671_PLL_MODE_SELECT1);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ mode |= AK4671_M_S;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ mode &= ~(AK4671_M_S);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ format = snd_soc_read(codec, AK4671_FORMAT_SELECT);
+ format &= ~AK4671_DIF;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format |= AK4671_DIF_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format |= AK4671_DIF_MSB_MODE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format |= AK4671_DIF_DSP_MODE;
+ format |= AK4671_BCKP;
+ format |= AK4671_MSBS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set mode and format */
+ snd_soc_write(codec, AK4671_PLL_MODE_SELECT1, mode);
+ snd_soc_write(codec, AK4671_FORMAT_SELECT, format);
+
+ return 0;
+}
+
+static int ak4671_set_bias_level(struct snd_soc_codec *codec,
+ 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,
+ AK4671_PMVCM, AK4671_PMVCM);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define AK4671_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000)
+
+#define AK4671_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+static const struct snd_soc_dai_ops ak4671_dai_ops = {
+ .hw_params = ak4671_hw_params,
+ .set_sysclk = ak4671_set_dai_sysclk,
+ .set_fmt = ak4671_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver ak4671_dai = {
+ .name = "ak4671-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4671_RATES,
+ .formats = AK4671_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4671_RATES,
+ .formats = AK4671_FORMATS,},
+ .ops = &ak4671_dai_ops,
+};
+
+static 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 regmap_config ak4671_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK4671_SAR_ADC_CONTROL,
+ .reg_defaults = ak4671_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4671_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int ak4671_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &ak4671_regmap);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&client->dev, "Failed to create regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&client->dev,
+ &soc_codec_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 },
+ { }
+};
+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,
+};
+
+module_i2c_driver(ak4671_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AK4671 codec driver");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ak4671.h b/kernel/sound/soc/codecs/ak4671.h
new file mode 100644
index 000000000..394a34d3f
--- /dev/null
+++ b/kernel/sound/soc/codecs/ak4671.h
@@ -0,0 +1,151 @@
+/*
+ * 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
+#define _AK4671_H
+
+#define AK4671_AD_DA_POWER_MANAGEMENT 0x00
+#define AK4671_PLL_MODE_SELECT0 0x01
+#define AK4671_PLL_MODE_SELECT1 0x02
+#define AK4671_FORMAT_SELECT 0x03
+#define AK4671_MIC_SIGNAL_SELECT 0x04
+#define AK4671_MIC_AMP_GAIN 0x05
+#define AK4671_MIXING_POWER_MANAGEMENT0 0x06
+#define AK4671_MIXING_POWER_MANAGEMENT1 0x07
+#define AK4671_OUTPUT_VOLUME_CONTROL 0x08
+#define AK4671_LOUT1_SIGNAL_SELECT 0x09
+#define AK4671_ROUT1_SIGNAL_SELECT 0x0a
+#define AK4671_LOUT2_SIGNAL_SELECT 0x0b
+#define AK4671_ROUT2_SIGNAL_SELECT 0x0c
+#define AK4671_LOUT3_SIGNAL_SELECT 0x0d
+#define AK4671_ROUT3_SIGNAL_SELECT 0x0e
+#define AK4671_LOUT1_POWER_MANAGERMENT 0x0f
+#define AK4671_LOUT2_POWER_MANAGERMENT 0x10
+#define AK4671_LOUT3_POWER_MANAGERMENT 0x11
+#define AK4671_LCH_INPUT_VOLUME_CONTROL 0x12
+#define AK4671_RCH_INPUT_VOLUME_CONTROL 0x13
+#define AK4671_ALC_REFERENCE_SELECT 0x14
+#define AK4671_DIGITAL_MIXING_CONTROL 0x15
+#define AK4671_ALC_TIMER_SELECT 0x16
+#define AK4671_ALC_MODE_CONTROL 0x17
+#define AK4671_MODE_CONTROL1 0x18
+#define AK4671_MODE_CONTROL2 0x19
+#define AK4671_LCH_OUTPUT_VOLUME_CONTROL 0x1a
+#define AK4671_RCH_OUTPUT_VOLUME_CONTROL 0x1b
+#define AK4671_SIDETONE_A_CONTROL 0x1c
+#define AK4671_DIGITAL_FILTER_SELECT 0x1d
+#define AK4671_FIL3_COEFFICIENT0 0x1e
+#define AK4671_FIL3_COEFFICIENT1 0x1f
+#define AK4671_FIL3_COEFFICIENT2 0x20
+#define AK4671_FIL3_COEFFICIENT3 0x21
+#define AK4671_EQ_COEFFICIENT0 0x22
+#define AK4671_EQ_COEFFICIENT1 0x23
+#define AK4671_EQ_COEFFICIENT2 0x24
+#define AK4671_EQ_COEFFICIENT3 0x25
+#define AK4671_EQ_COEFFICIENT4 0x26
+#define AK4671_EQ_COEFFICIENT5 0x27
+#define AK4671_FIL1_COEFFICIENT0 0x28
+#define AK4671_FIL1_COEFFICIENT1 0x29
+#define AK4671_FIL1_COEFFICIENT2 0x2a
+#define AK4671_FIL1_COEFFICIENT3 0x2b
+#define AK4671_FIL2_COEFFICIENT0 0x2c
+#define AK4671_FIL2_COEFFICIENT1 0x2d
+#define AK4671_FIL2_COEFFICIENT2 0x2e
+#define AK4671_FIL2_COEFFICIENT3 0x2f
+#define AK4671_DIGITAL_FILTER_SELECT2 0x30
+#define AK4671_E1_COEFFICIENT0 0x32
+#define AK4671_E1_COEFFICIENT1 0x33
+#define AK4671_E1_COEFFICIENT2 0x34
+#define AK4671_E1_COEFFICIENT3 0x35
+#define AK4671_E1_COEFFICIENT4 0x36
+#define AK4671_E1_COEFFICIENT5 0x37
+#define AK4671_E2_COEFFICIENT0 0x38
+#define AK4671_E2_COEFFICIENT1 0x39
+#define AK4671_E2_COEFFICIENT2 0x3a
+#define AK4671_E2_COEFFICIENT3 0x3b
+#define AK4671_E2_COEFFICIENT4 0x3c
+#define AK4671_E2_COEFFICIENT5 0x3d
+#define AK4671_E3_COEFFICIENT0 0x3e
+#define AK4671_E3_COEFFICIENT1 0x3f
+#define AK4671_E3_COEFFICIENT2 0x40
+#define AK4671_E3_COEFFICIENT3 0x41
+#define AK4671_E3_COEFFICIENT4 0x42
+#define AK4671_E3_COEFFICIENT5 0x43
+#define AK4671_E4_COEFFICIENT0 0x44
+#define AK4671_E4_COEFFICIENT1 0x45
+#define AK4671_E4_COEFFICIENT2 0x46
+#define AK4671_E4_COEFFICIENT3 0x47
+#define AK4671_E4_COEFFICIENT4 0x48
+#define AK4671_E4_COEFFICIENT5 0x49
+#define AK4671_E5_COEFFICIENT0 0x4a
+#define AK4671_E5_COEFFICIENT1 0x4b
+#define AK4671_E5_COEFFICIENT2 0x4c
+#define AK4671_E5_COEFFICIENT3 0x4d
+#define AK4671_E5_COEFFICIENT4 0x4e
+#define AK4671_E5_COEFFICIENT5 0x4f
+#define AK4671_EQ_CONTROL_250HZ_100HZ 0x50
+#define AK4671_EQ_CONTROL_3500HZ_1KHZ 0x51
+#define AK4671_EQ_CONTRO_10KHZ 0x52
+#define AK4671_PCM_IF_CONTROL0 0x53
+#define AK4671_PCM_IF_CONTROL1 0x54
+#define AK4671_PCM_IF_CONTROL2 0x55
+#define AK4671_DIGITAL_VOLUME_B_CONTROL 0x56
+#define AK4671_DIGITAL_VOLUME_C_CONTROL 0x57
+#define AK4671_SIDETONE_VOLUME_CONTROL 0x58
+#define AK4671_DIGITAL_MIXING_CONTROL2 0x59
+#define AK4671_SAR_ADC_CONTROL 0x5a
+
+/* Bitfield Definitions */
+
+/* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */
+#define AK4671_PMVCM 0x01
+
+/* AK4671_PLL_MODE_SELECT0 (0x01) Fields */
+#define AK4671_PLL 0x0f
+#define AK4671_PLL_11_2896MHZ (4 << 0)
+#define AK4671_PLL_12_288MHZ (5 << 0)
+#define AK4671_PLL_12MHZ (6 << 0)
+#define AK4671_PLL_24MHZ (7 << 0)
+#define AK4671_PLL_19_2MHZ (8 << 0)
+#define AK4671_PLL_13_5MHZ (12 << 0)
+#define AK4671_PLL_27MHZ (13 << 0)
+#define AK4671_PLL_13MHZ (14 << 0)
+#define AK4671_PLL_26MHZ (15 << 0)
+#define AK4671_FS 0xf0
+#define AK4671_FS_8KHZ (0 << 4)
+#define AK4671_FS_12KHZ (1 << 4)
+#define AK4671_FS_16KHZ (2 << 4)
+#define AK4671_FS_24KHZ (3 << 4)
+#define AK4671_FS_11_025KHZ (5 << 4)
+#define AK4671_FS_22_05KHZ (7 << 4)
+#define AK4671_FS_32KHZ (10 << 4)
+#define AK4671_FS_48KHZ (11 << 4)
+#define AK4671_FS_44_1KHZ (15 << 4)
+
+/* AK4671_PLL_MODE_SELECT1 (0x02) Fields */
+#define AK4671_PMPLL 0x01
+#define AK4671_M_S 0x02
+
+/* AK4671_FORMAT_SELECT (0x03) Fields */
+#define AK4671_DIF 0x03
+#define AK4671_DIF_DSP_MODE (0 << 0)
+#define AK4671_DIF_MSB_MODE (2 << 0)
+#define AK4671_DIF_I2S_MODE (3 << 0)
+#define AK4671_BCKP 0x04
+#define AK4671_MSBS 0x08
+#define AK4671_SDOD 0x10
+
+/* AK4671_LOUT2_POWER_MANAGEMENT (0x10) Fields */
+#define AK4671_MUTEN 0x04
+
+#endif
diff --git a/kernel/sound/soc/codecs/ak5386.c b/kernel/sound/soc/codecs/ak5386.c
new file mode 100644
index 000000000..afa953608
--- /dev/null
+++ b/kernel/sound/soc/codecs/ak5386.c
@@ -0,0 +1,216 @@
+/*
+ * 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/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 <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+static const char * const supply_names[] = {
+ "va", "vd"
+};
+
+struct ak5386_priv {
+ int reset_gpio;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+};
+
+static const struct snd_soc_dapm_widget ak5386_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+};
+
+static const struct snd_soc_dapm_route ak5386_dapm_routes[] = {
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
+};
+
+static int ak5386_soc_probe(struct snd_soc_codec *codec)
+{
+ struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+}
+
+static int ak5386_soc_remove(struct snd_soc_codec *codec)
+{
+ struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ak5386_soc_suspend(struct snd_soc_codec *codec)
+{
+ struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+ return 0;
+}
+
+static int ak5386_soc_resume(struct snd_soc_codec *codec)
+{
+ struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+}
+#else
+#define ak5386_soc_suspend NULL
+#define ak5386_soc_resume NULL
+#endif /* CONFIG_PM */
+
+static 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 int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ 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");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ak5386_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ /*
+ * From the datasheet:
+ *
+ * All external clocks (MCLK, SCLK and LRCK) must be present unless
+ * PDN pin = “L”. If these clocks are not provided, the AK5386 may
+ * draw excess current due to its use of internal dynamically
+ * refreshed logic. If the external clocks are not present, place
+ * the AK5386 in power-down mode (PDN pin = “L”).
+ */
+
+ if (gpio_is_valid(priv->reset_gpio))
+ gpio_set_value(priv->reset_gpio, 1);
+
+ return 0;
+}
+
+static int ak5386_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (gpio_is_valid(priv->reset_gpio))
+ gpio_set_value(priv->reset_gpio, 0);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ak5386_dai_ops = {
+ .set_fmt = ak5386_set_dai_fmt,
+ .hw_params = ak5386_hw_params,
+ .hw_free = ak5386_hw_free,
+};
+
+static struct snd_soc_dai_driver ak5386_dai = {
+ .name = "ak5386-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ },
+ .ops = &ak5386_dai_ops,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id ak5386_dt_ids[] = {
+ { .compatible = "asahi-kasei,ak5386", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ak5386_dt_ids);
+#endif
+
+static int ak5386_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ak5386_priv *priv;
+ int ret, i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->reset_gpio = -EINVAL;
+ dev_set_drvdata(dev, priv);
+
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ priv->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret < 0)
+ return ret;
+
+ if (of_match_device(of_match_ptr(ak5386_dt_ids), dev))
+ priv->reset_gpio = of_get_named_gpio(dev->of_node,
+ "reset-gpio", 0);
+
+ if (gpio_is_valid(priv->reset_gpio))
+ if (devm_gpio_request_one(dev, priv->reset_gpio,
+ GPIOF_OUT_INIT_LOW,
+ "AK5386 Reset"))
+ priv->reset_gpio = -EINVAL;
+
+ return snd_soc_register_codec(dev, &soc_codec_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),
+ },
+};
+
+module_platform_driver(ak5386_driver);
+
+MODULE_DESCRIPTION("ASoC driver for AK5386 ADC");
+MODULE_AUTHOR("Daniel Mack <zonque@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/alc5623.c b/kernel/sound/soc/codecs/alc5623.c
new file mode 100644
index 000000000..0e3579968
--- /dev/null
+++ b/kernel/sound/soc/codecs/alc5623.c
@@ -0,0 +1,1101 @@
+/*
+ * alc5623.c -- alc562[123] ALSA Soc Audio driver
+ *
+ * Copyright 2008 Realtek Microelectronics
+ * Author: flove <flove@realtek.com> Ethan <eku@marvell.com>
+ *
+ * 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>
+#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 <linux/of.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/alc5623.h>
+
+#include "alc5623.h"
+
+static int caps_charge = 2000;
+module_param(caps_charge, int, 0);
+MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)");
+
+/* codec private data */
+struct alc5623_priv {
+ struct regmap *regmap;
+ u8 id;
+ unsigned int sysclk;
+ unsigned int add_ctrl;
+ unsigned int jack_det_ctrl;
+};
+
+static inline int alc5623_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, 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);
+
+ /* 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);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_write(codec, ALC5623_HID_CTRL_DATA, 0xFFFF);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_write(codec, ALC5623_HID_CTRL_DATA, 0);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * ALC5623 Controls
+ */
+
+static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0);
+static const unsigned int boost_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new alc5621_vol_snd_controls[] = {
+ SOC_DOUBLE_TLV("Speaker Playback Volume",
+ ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Speaker Playback Switch",
+ ALC5623_SPK_OUT_VOL, 15, 7, 1, 1),
+ SOC_DOUBLE_TLV("Headphone Playback Volume",
+ ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Headphone Playback Switch",
+ ALC5623_HP_OUT_VOL, 15, 7, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5622_vol_snd_controls[] = {
+ SOC_DOUBLE_TLV("Speaker Playback Volume",
+ ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Speaker Playback Switch",
+ ALC5623_SPK_OUT_VOL, 15, 7, 1, 1),
+ SOC_DOUBLE_TLV("Line Playback Volume",
+ ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Line Playback Switch",
+ ALC5623_HP_OUT_VOL, 15, 7, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_vol_snd_controls[] = {
+ SOC_DOUBLE_TLV("Line Playback Volume",
+ ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Line Playback Switch",
+ ALC5623_SPK_OUT_VOL, 15, 7, 1, 1),
+ SOC_DOUBLE_TLV("Headphone Playback Volume",
+ ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Headphone Playback Switch",
+ ALC5623_HP_OUT_VOL, 15, 7, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_snd_controls[] = {
+ SOC_DOUBLE_TLV("Auxout Playback Volume",
+ ALC5623_MONO_AUX_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Auxout Playback Switch",
+ ALC5623_MONO_AUX_OUT_VOL, 15, 7, 1, 1),
+ SOC_DOUBLE_TLV("PCM Playback Volume",
+ ALC5623_STEREO_DAC_VOL, 8, 0, 31, 1, vol_tlv),
+ SOC_DOUBLE_TLV("AuxI Capture Volume",
+ ALC5623_AUXIN_VOL, 8, 0, 31, 1, vol_tlv),
+ SOC_DOUBLE_TLV("LineIn Capture Volume",
+ ALC5623_LINE_IN_VOL, 8, 0, 31, 1, vol_tlv),
+ SOC_SINGLE_TLV("Mic1 Capture Volume",
+ ALC5623_MIC_VOL, 8, 31, 1, vol_tlv),
+ SOC_SINGLE_TLV("Mic2 Capture Volume",
+ ALC5623_MIC_VOL, 0, 31, 1, vol_tlv),
+ SOC_DOUBLE_TLV("Rec Capture Volume",
+ ALC5623_ADC_REC_GAIN, 7, 0, 31, 0, adc_rec_tlv),
+ SOC_SINGLE_TLV("Mic 1 Boost Volume",
+ ALC5623_MIC_CTRL, 10, 2, 0, boost_tlv),
+ SOC_SINGLE_TLV("Mic 2 Boost Volume",
+ ALC5623_MIC_CTRL, 8, 2, 0, boost_tlv),
+ SOC_SINGLE_TLV("Digital Boost Volume",
+ ALC5623_ADD_CTRL_REG, 4, 3, 0, dig_tlv),
+};
+
+/*
+ * DAPM Controls
+ */
+static const struct snd_kcontrol_new alc5623_hp_mixer_controls[] = {
+SOC_DAPM_SINGLE("LI2HP Playback Switch", ALC5623_LINE_IN_VOL, 15, 1, 1),
+SOC_DAPM_SINGLE("AUXI2HP Playback Switch", ALC5623_AUXIN_VOL, 15, 1, 1),
+SOC_DAPM_SINGLE("MIC12HP Playback Switch", ALC5623_MIC_ROUTING_CTRL, 15, 1, 1),
+SOC_DAPM_SINGLE("MIC22HP Playback Switch", ALC5623_MIC_ROUTING_CTRL, 7, 1, 1),
+SOC_DAPM_SINGLE("DAC2HP Playback Switch", ALC5623_STEREO_DAC_VOL, 15, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_hpl_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2HP_L Playback Switch", ALC5623_ADC_REC_GAIN, 15, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_hpr_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2HP_R Playback Switch", ALC5623_ADC_REC_GAIN, 14, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2MONO_L Playback Switch", ALC5623_ADC_REC_GAIN, 13, 1, 1),
+SOC_DAPM_SINGLE("ADC2MONO_R Playback Switch", ALC5623_ADC_REC_GAIN, 12, 1, 1),
+SOC_DAPM_SINGLE("LI2MONO Playback Switch", ALC5623_LINE_IN_VOL, 13, 1, 1),
+SOC_DAPM_SINGLE("AUXI2MONO Playback Switch", ALC5623_AUXIN_VOL, 13, 1, 1),
+SOC_DAPM_SINGLE("MIC12MONO Playback Switch", ALC5623_MIC_ROUTING_CTRL, 13, 1, 1),
+SOC_DAPM_SINGLE("MIC22MONO Playback Switch", ALC5623_MIC_ROUTING_CTRL, 5, 1, 1),
+SOC_DAPM_SINGLE("DAC2MONO Playback Switch", ALC5623_STEREO_DAC_VOL, 13, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("LI2SPK Playback Switch", ALC5623_LINE_IN_VOL, 14, 1, 1),
+SOC_DAPM_SINGLE("AUXI2SPK Playback Switch", ALC5623_AUXIN_VOL, 14, 1, 1),
+SOC_DAPM_SINGLE("MIC12SPK Playback Switch", ALC5623_MIC_ROUTING_CTRL, 14, 1, 1),
+SOC_DAPM_SINGLE("MIC22SPK Playback Switch", ALC5623_MIC_ROUTING_CTRL, 6, 1, 1),
+SOC_DAPM_SINGLE("DAC2SPK Playback Switch", ALC5623_STEREO_DAC_VOL, 14, 1, 1),
+};
+
+/* Left Record Mixer */
+static const struct snd_kcontrol_new alc5623_captureL_mixer_controls[] = {
+SOC_DAPM_SINGLE("Mic1 Capture Switch", ALC5623_ADC_REC_MIXER, 14, 1, 1),
+SOC_DAPM_SINGLE("Mic2 Capture Switch", ALC5623_ADC_REC_MIXER, 13, 1, 1),
+SOC_DAPM_SINGLE("LineInL Capture Switch", ALC5623_ADC_REC_MIXER, 12, 1, 1),
+SOC_DAPM_SINGLE("Left AuxI Capture Switch", ALC5623_ADC_REC_MIXER, 11, 1, 1),
+SOC_DAPM_SINGLE("HPMixerL Capture Switch", ALC5623_ADC_REC_MIXER, 10, 1, 1),
+SOC_DAPM_SINGLE("SPKMixer Capture Switch", ALC5623_ADC_REC_MIXER, 9, 1, 1),
+SOC_DAPM_SINGLE("MonoMixer Capture Switch", ALC5623_ADC_REC_MIXER, 8, 1, 1),
+};
+
+/* Right Record Mixer */
+static const struct snd_kcontrol_new alc5623_captureR_mixer_controls[] = {
+SOC_DAPM_SINGLE("Mic1 Capture Switch", ALC5623_ADC_REC_MIXER, 6, 1, 1),
+SOC_DAPM_SINGLE("Mic2 Capture Switch", ALC5623_ADC_REC_MIXER, 5, 1, 1),
+SOC_DAPM_SINGLE("LineInR Capture Switch", ALC5623_ADC_REC_MIXER, 4, 1, 1),
+SOC_DAPM_SINGLE("Right AuxI Capture Switch", ALC5623_ADC_REC_MIXER, 3, 1, 1),
+SOC_DAPM_SINGLE("HPMixerR Capture Switch", ALC5623_ADC_REC_MIXER, 2, 1, 1),
+SOC_DAPM_SINGLE("SPKMixer Capture Switch", ALC5623_ADC_REC_MIXER, 1, 1, 1),
+SOC_DAPM_SINGLE("MonoMixer Capture Switch", ALC5623_ADC_REC_MIXER, 0, 1, 1),
+};
+
+static const char *alc5623_spk_n_sour_sel[] = {
+ "RN/-R", "RP/+R", "LN/-R", "Vmid" };
+static const char *alc5623_hpl_out_input_sel[] = {
+ "Vmid", "HP Left Mix"};
+static const char *alc5623_hpr_out_input_sel[] = {
+ "Vmid", "HP Right Mix"};
+static const char *alc5623_spkout_input_sel[] = {
+ "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"};
+static const char *alc5623_aux_out_input_sel[] = {
+ "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"};
+
+/* auxout output mux */
+static SOC_ENUM_SINGLE_DECL(alc5623_aux_out_input_enum,
+ ALC5623_OUTPUT_MIXER_CTRL, 6,
+ alc5623_aux_out_input_sel);
+static const struct snd_kcontrol_new alc5623_auxout_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_aux_out_input_enum);
+
+/* speaker output mux */
+static SOC_ENUM_SINGLE_DECL(alc5623_spkout_input_enum,
+ ALC5623_OUTPUT_MIXER_CTRL, 10,
+ alc5623_spkout_input_sel);
+static const struct snd_kcontrol_new alc5623_spkout_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_spkout_input_enum);
+
+/* headphone left output mux */
+static SOC_ENUM_SINGLE_DECL(alc5623_hpl_out_input_enum,
+ ALC5623_OUTPUT_MIXER_CTRL, 9,
+ alc5623_hpl_out_input_sel);
+static const struct snd_kcontrol_new alc5623_hpl_out_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_hpl_out_input_enum);
+
+/* headphone right output mux */
+static SOC_ENUM_SINGLE_DECL(alc5623_hpr_out_input_enum,
+ ALC5623_OUTPUT_MIXER_CTRL, 8,
+ alc5623_hpr_out_input_sel);
+static const struct snd_kcontrol_new alc5623_hpr_out_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_hpr_out_input_enum);
+
+/* speaker output N select */
+static SOC_ENUM_SINGLE_DECL(alc5623_spk_n_sour_enum,
+ ALC5623_OUTPUT_MIXER_CTRL, 14,
+ alc5623_spk_n_sour_sel);
+static const struct snd_kcontrol_new alc5623_spkoutn_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_spk_n_sour_enum);
+
+static const struct snd_soc_dapm_widget alc5623_dapm_widgets[] = {
+/* Muxes */
+SND_SOC_DAPM_MUX("AuxOut Mux", SND_SOC_NOPM, 0, 0,
+ &alc5623_auxout_mux_controls),
+SND_SOC_DAPM_MUX("SpeakerOut Mux", SND_SOC_NOPM, 0, 0,
+ &alc5623_spkout_mux_controls),
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &alc5623_hpl_out_mux_controls),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &alc5623_hpr_out_mux_controls),
+SND_SOC_DAPM_MUX("SpeakerOut N Mux", SND_SOC_NOPM, 0, 0,
+ &alc5623_spkoutn_mux_controls),
+
+/* output mixers */
+SND_SOC_DAPM_MIXER("HP Mix", SND_SOC_NOPM, 0, 0,
+ &alc5623_hp_mixer_controls[0],
+ ARRAY_SIZE(alc5623_hp_mixer_controls)),
+SND_SOC_DAPM_MIXER("HPR Mix", ALC5623_PWR_MANAG_ADD2, 4, 0,
+ &alc5623_hpr_mixer_controls[0],
+ ARRAY_SIZE(alc5623_hpr_mixer_controls)),
+SND_SOC_DAPM_MIXER("HPL Mix", ALC5623_PWR_MANAG_ADD2, 5, 0,
+ &alc5623_hpl_mixer_controls[0],
+ ARRAY_SIZE(alc5623_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("HPOut Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Mono Mix", ALC5623_PWR_MANAG_ADD2, 2, 0,
+ &alc5623_mono_mixer_controls[0],
+ ARRAY_SIZE(alc5623_mono_mixer_controls)),
+SND_SOC_DAPM_MIXER("Speaker Mix", ALC5623_PWR_MANAG_ADD2, 3, 0,
+ &alc5623_speaker_mixer_controls[0],
+ ARRAY_SIZE(alc5623_speaker_mixer_controls)),
+
+/* input mixers */
+SND_SOC_DAPM_MIXER("Left Capture Mix", ALC5623_PWR_MANAG_ADD2, 1, 0,
+ &alc5623_captureL_mixer_controls[0],
+ ARRAY_SIZE(alc5623_captureL_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right Capture Mix", ALC5623_PWR_MANAG_ADD2, 0, 0,
+ &alc5623_captureR_mixer_controls[0],
+ ARRAY_SIZE(alc5623_captureR_mixer_controls)),
+
+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback",
+ ALC5623_PWR_MANAG_ADD2, 9, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback",
+ ALC5623_PWR_MANAG_ADD2, 8, 0),
+SND_SOC_DAPM_MIXER("I2S Mix", ALC5623_PWR_MANAG_ADD1, 15, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("AuxI Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Line Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture",
+ ALC5623_PWR_MANAG_ADD2, 7, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture",
+ ALC5623_PWR_MANAG_ADD2, 6, 0),
+SND_SOC_DAPM_PGA("Left Headphone", ALC5623_PWR_MANAG_ADD3, 10, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Headphone", ALC5623_PWR_MANAG_ADD3, 9, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpeakerOut", ALC5623_PWR_MANAG_ADD3, 12, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left AuxOut", ALC5623_PWR_MANAG_ADD3, 14, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right AuxOut", ALC5623_PWR_MANAG_ADD3, 13, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left LineIn", ALC5623_PWR_MANAG_ADD3, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right LineIn", ALC5623_PWR_MANAG_ADD3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left AuxI", ALC5623_PWR_MANAG_ADD3, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right AuxI", ALC5623_PWR_MANAG_ADD3, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC1 PGA", ALC5623_PWR_MANAG_ADD3, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC2 PGA", ALC5623_PWR_MANAG_ADD3, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC1 Pre Amp", ALC5623_PWR_MANAG_ADD3, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC2 Pre Amp", ALC5623_PWR_MANAG_ADD3, 0, 0, NULL, 0),
+SND_SOC_DAPM_MICBIAS("Mic Bias1", ALC5623_PWR_MANAG_ADD1, 11, 0),
+
+SND_SOC_DAPM_OUTPUT("AUXOUTL"),
+SND_SOC_DAPM_OUTPUT("AUXOUTR"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+SND_SOC_DAPM_OUTPUT("SPKOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+SND_SOC_DAPM_INPUT("LINEINL"),
+SND_SOC_DAPM_INPUT("LINEINR"),
+SND_SOC_DAPM_INPUT("AUXINL"),
+SND_SOC_DAPM_INPUT("AUXINR"),
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2"),
+SND_SOC_DAPM_VMID("Vmid"),
+};
+
+static const char *alc5623_amp_names[] = {"AB Amp", "D Amp"};
+static SOC_ENUM_SINGLE_DECL(alc5623_amp_enum,
+ ALC5623_OUTPUT_MIXER_CTRL, 13,
+ alc5623_amp_names);
+static const struct snd_kcontrol_new alc5623_amp_mux_controls =
+ SOC_DAPM_ENUM("Route", alc5623_amp_enum);
+
+static const struct snd_soc_dapm_widget alc5623_dapm_amp_widgets[] = {
+SND_SOC_DAPM_PGA_E("D Amp", ALC5623_PWR_MANAG_ADD2, 14, 0, NULL, 0,
+ amp_mixer_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_PGA("AB Amp", ALC5623_PWR_MANAG_ADD2, 15, 0, NULL, 0),
+SND_SOC_DAPM_MUX("AB-D Amp Mux", SND_SOC_NOPM, 0, 0,
+ &alc5623_amp_mux_controls),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ /* virtual mixer - mixes left & right channels */
+ {"I2S Mix", NULL, "Left DAC"},
+ {"I2S Mix", NULL, "Right DAC"},
+ {"Line Mix", NULL, "Right LineIn"},
+ {"Line Mix", NULL, "Left LineIn"},
+ {"AuxI Mix", NULL, "Left AuxI"},
+ {"AuxI Mix", NULL, "Right AuxI"},
+ {"AUXOUTL", NULL, "Left AuxOut"},
+ {"AUXOUTR", NULL, "Right AuxOut"},
+
+ /* HP mixer */
+ {"HPL Mix", "ADC2HP_L Playback Switch", "Left Capture Mix"},
+ {"HPL Mix", NULL, "HP Mix"},
+ {"HPR Mix", "ADC2HP_R Playback Switch", "Right Capture Mix"},
+ {"HPR Mix", NULL, "HP Mix"},
+ {"HP Mix", "LI2HP Playback Switch", "Line Mix"},
+ {"HP Mix", "AUXI2HP Playback Switch", "AuxI Mix"},
+ {"HP Mix", "MIC12HP Playback Switch", "MIC1 PGA"},
+ {"HP Mix", "MIC22HP Playback Switch", "MIC2 PGA"},
+ {"HP Mix", "DAC2HP Playback Switch", "I2S Mix"},
+
+ /* speaker mixer */
+ {"Speaker Mix", "LI2SPK Playback Switch", "Line Mix"},
+ {"Speaker Mix", "AUXI2SPK Playback Switch", "AuxI Mix"},
+ {"Speaker Mix", "MIC12SPK Playback Switch", "MIC1 PGA"},
+ {"Speaker Mix", "MIC22SPK Playback Switch", "MIC2 PGA"},
+ {"Speaker Mix", "DAC2SPK Playback Switch", "I2S Mix"},
+
+ /* mono mixer */
+ {"Mono Mix", "ADC2MONO_L Playback Switch", "Left Capture Mix"},
+ {"Mono Mix", "ADC2MONO_R Playback Switch", "Right Capture Mix"},
+ {"Mono Mix", "LI2MONO Playback Switch", "Line Mix"},
+ {"Mono Mix", "AUXI2MONO Playback Switch", "AuxI Mix"},
+ {"Mono Mix", "MIC12MONO Playback Switch", "MIC1 PGA"},
+ {"Mono Mix", "MIC22MONO Playback Switch", "MIC2 PGA"},
+ {"Mono Mix", "DAC2MONO Playback Switch", "I2S Mix"},
+
+ /* Left record mixer */
+ {"Left Capture Mix", "LineInL Capture Switch", "LINEINL"},
+ {"Left Capture Mix", "Left AuxI Capture Switch", "AUXINL"},
+ {"Left Capture Mix", "Mic1 Capture Switch", "MIC1 Pre Amp"},
+ {"Left Capture Mix", "Mic2 Capture Switch", "MIC2 Pre Amp"},
+ {"Left Capture Mix", "HPMixerL Capture Switch", "HPL Mix"},
+ {"Left Capture Mix", "SPKMixer Capture Switch", "Speaker Mix"},
+ {"Left Capture Mix", "MonoMixer Capture Switch", "Mono Mix"},
+
+ /*Right record mixer */
+ {"Right Capture Mix", "LineInR Capture Switch", "LINEINR"},
+ {"Right Capture Mix", "Right AuxI Capture Switch", "AUXINR"},
+ {"Right Capture Mix", "Mic1 Capture Switch", "MIC1 Pre Amp"},
+ {"Right Capture Mix", "Mic2 Capture Switch", "MIC2 Pre Amp"},
+ {"Right Capture Mix", "HPMixerR Capture Switch", "HPR Mix"},
+ {"Right Capture Mix", "SPKMixer Capture Switch", "Speaker Mix"},
+ {"Right Capture Mix", "MonoMixer Capture Switch", "Mono Mix"},
+
+ /* headphone left mux */
+ {"Left Headphone Mux", "HP Left Mix", "HPL Mix"},
+ {"Left Headphone Mux", "Vmid", "Vmid"},
+
+ /* headphone right mux */
+ {"Right Headphone Mux", "HP Right Mix", "HPR Mix"},
+ {"Right Headphone Mux", "Vmid", "Vmid"},
+
+ /* speaker out mux */
+ {"SpeakerOut Mux", "Vmid", "Vmid"},
+ {"SpeakerOut Mux", "HPOut Mix", "HPOut Mix"},
+ {"SpeakerOut Mux", "Speaker Mix", "Speaker Mix"},
+ {"SpeakerOut Mux", "Mono Mix", "Mono Mix"},
+
+ /* Mono/Aux Out mux */
+ {"AuxOut Mux", "Vmid", "Vmid"},
+ {"AuxOut Mux", "HPOut Mix", "HPOut Mix"},
+ {"AuxOut Mux", "Speaker Mix", "Speaker Mix"},
+ {"AuxOut Mux", "Mono Mix", "Mono Mix"},
+
+ /* output pga */
+ {"HPL", NULL, "Left Headphone"},
+ {"Left Headphone", NULL, "Left Headphone Mux"},
+ {"HPR", NULL, "Right Headphone"},
+ {"Right Headphone", NULL, "Right Headphone Mux"},
+ {"Left AuxOut", NULL, "AuxOut Mux"},
+ {"Right AuxOut", NULL, "AuxOut Mux"},
+
+ /* input pga */
+ {"Left LineIn", NULL, "LINEINL"},
+ {"Right LineIn", NULL, "LINEINR"},
+ {"Left AuxI", NULL, "AUXINL"},
+ {"Right AuxI", NULL, "AUXINR"},
+ {"MIC1 Pre Amp", NULL, "MIC1"},
+ {"MIC2 Pre Amp", NULL, "MIC2"},
+ {"MIC1 PGA", NULL, "MIC1 Pre Amp"},
+ {"MIC2 PGA", NULL, "MIC2 Pre Amp"},
+
+ /* left ADC */
+ {"Left ADC", NULL, "Left Capture Mix"},
+
+ /* right ADC */
+ {"Right ADC", NULL, "Right Capture Mix"},
+
+ {"SpeakerOut N Mux", "RN/-R", "SpeakerOut"},
+ {"SpeakerOut N Mux", "RP/+R", "SpeakerOut"},
+ {"SpeakerOut N Mux", "LN/-R", "SpeakerOut"},
+ {"SpeakerOut N Mux", "Vmid", "Vmid"},
+
+ {"SPKOUT", NULL, "SpeakerOut"},
+ {"SPKOUTN", NULL, "SpeakerOut N Mux"},
+};
+
+static const struct snd_soc_dapm_route intercon_spk[] = {
+ {"SpeakerOut", NULL, "SpeakerOut Mux"},
+};
+
+static const struct snd_soc_dapm_route intercon_amp_spk[] = {
+ {"AB Amp", NULL, "SpeakerOut Mux"},
+ {"D Amp", NULL, "SpeakerOut Mux"},
+ {"AB-D Amp Mux", "AB Amp", "AB Amp"},
+ {"AB-D Amp Mux", "D Amp", "D Amp"},
+ {"SpeakerOut", NULL, "AB-D Amp Mux"},
+};
+
+/* PLL divisors */
+struct _pll_div {
+ u32 pll_in;
+ u32 pll_out;
+ u16 regvalue;
+};
+
+/* Note : pll code from original alc5623 driver. Not sure of how good it is */
+/* useful only for master mode */
+static const struct _pll_div codec_master_pll_div[] = {
+
+ { 2048000, 8192000, 0x0ea0},
+ { 3686400, 8192000, 0x4e27},
+ { 12000000, 8192000, 0x456b},
+ { 13000000, 8192000, 0x495f},
+ { 13100000, 8192000, 0x0320},
+ { 2048000, 11289600, 0xf637},
+ { 3686400, 11289600, 0x2f22},
+ { 12000000, 11289600, 0x3e2f},
+ { 13000000, 11289600, 0x4d5b},
+ { 13100000, 11289600, 0x363b},
+ { 2048000, 16384000, 0x1ea0},
+ { 3686400, 16384000, 0x9e27},
+ { 12000000, 16384000, 0x452b},
+ { 13000000, 16384000, 0x542f},
+ { 13100000, 16384000, 0x03a0},
+ { 2048000, 16934400, 0xe625},
+ { 3686400, 16934400, 0x9126},
+ { 12000000, 16934400, 0x4d2c},
+ { 13000000, 16934400, 0x742f},
+ { 13100000, 16934400, 0x3c27},
+ { 2048000, 22579200, 0x2aa0},
+ { 3686400, 22579200, 0x2f20},
+ { 12000000, 22579200, 0x7e2f},
+ { 13000000, 22579200, 0x742f},
+ { 13100000, 22579200, 0x3c27},
+ { 2048000, 24576000, 0x2ea0},
+ { 3686400, 24576000, 0xee27},
+ { 12000000, 24576000, 0x2915},
+ { 13000000, 24576000, 0x772e},
+ { 13100000, 24576000, 0x0d20},
+};
+
+static const struct _pll_div codec_slave_pll_div[] = {
+
+ { 1024000, 16384000, 0x3ea0},
+ { 1411200, 22579200, 0x3ea0},
+ { 1536000, 24576000, 0x3ea0},
+ { 2048000, 16384000, 0x1ea0},
+ { 2822400, 22579200, 0x1ea0},
+ { 3072000, 24576000, 0x1ea0},
+
+};
+
+static int alc5623_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ int i;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int gbl_clk = 0, pll_div = 0;
+ u16 reg;
+
+ if (pll_id < ALC5623_PLL_FR_MCLK || pll_id > ALC5623_PLL_FR_BCK)
+ return -ENODEV;
+
+ /* Disable PLL power */
+ snd_soc_update_bits(codec, ALC5623_PWR_MANAG_ADD2,
+ ALC5623_PWR_ADD2_PLL,
+ 0);
+
+ /* pll is not used in slave mode */
+ reg = snd_soc_read(codec, ALC5623_DAI_CONTROL);
+ if (reg & ALC5623_DAI_SDP_SLAVE_MODE)
+ return 0;
+
+ if (!freq_in || !freq_out)
+ return 0;
+
+ switch (pll_id) {
+ case ALC5623_PLL_FR_MCLK:
+ for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++) {
+ if (codec_master_pll_div[i].pll_in == freq_in
+ && codec_master_pll_div[i].pll_out == freq_out) {
+ /* PLL source from MCLK */
+ pll_div = codec_master_pll_div[i].regvalue;
+ break;
+ }
+ }
+ break;
+ case ALC5623_PLL_FR_BCK:
+ for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) {
+ if (codec_slave_pll_div[i].pll_in == freq_in
+ && codec_slave_pll_div[i].pll_out == freq_out) {
+ /* PLL source from Bitclk */
+ gbl_clk = ALC5623_GBL_CLK_PLL_SOUR_SEL_BITCLK;
+ pll_div = codec_slave_pll_div[i].regvalue;
+ break;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!pll_div)
+ return -EINVAL;
+
+ snd_soc_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,
+ 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);
+
+ return 0;
+}
+
+struct _coeff_div {
+ u16 fs;
+ u16 regvalue;
+};
+
+/* codec hifi mclk (after PLL) clock divider coefficients */
+/* values inspired from column BCLK=32Fs of Appendix A table */
+static const struct _coeff_div coeff_div[] = {
+ {256*8, 0x3a69},
+ {384*8, 0x3c6b},
+ {256*4, 0x2a69},
+ {384*4, 0x2c6b},
+ {256*2, 0x1a69},
+ {384*2, 0x1c6b},
+ {256*1, 0x0a69},
+ {384*1, 0x0c6b},
+};
+
+static int get_coeff(struct snd_soc_codec *codec, int rate)
+{
+ struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].fs * rate == alc5623->sysclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Clock after PLL and dividers
+ */
+static int alc5623_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+
+ switch (freq) {
+ case 8192000:
+ case 11289600:
+ case 12288000:
+ case 16384000:
+ case 16934400:
+ case 18432000:
+ case 22579200:
+ case 24576000:
+ alc5623->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface = ALC5623_DAI_SDP_MASTER_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ iface = ALC5623_DAI_SDP_SLAVE_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= ALC5623_DAI_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface |= ALC5623_DAI_I2S_DF_RIGHT;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= ALC5623_DAI_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= ALC5623_DAI_I2S_DF_PCM;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= ALC5623_DAI_I2S_DF_PCM | ALC5623_DAI_I2S_PCM_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_write(codec, 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);
+ int coeff, rate;
+ u16 iface;
+
+ iface = snd_soc_read(codec, ALC5623_DAI_CONTROL);
+ iface &= ~ALC5623_DAI_I2S_DL_MASK;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ iface |= ALC5623_DAI_I2S_DL_16;
+ break;
+ case 20:
+ iface |= ALC5623_DAI_I2S_DL_20;
+ break;
+ case 24:
+ iface |= ALC5623_DAI_I2S_DL_24;
+ break;
+ case 32:
+ iface |= ALC5623_DAI_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface & srate */
+ snd_soc_write(codec, ALC5623_DAI_CONTROL, iface);
+ rate = params_rate(params);
+ coeff = get_coeff(codec, rate);
+ if (coeff < 0)
+ return -EINVAL;
+
+ coeff = coeff_div[coeff].regvalue;
+ dev_dbg(codec->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);
+
+ return 0;
+}
+
+static int alc5623_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ 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;
+
+ if (mute)
+ mute_reg |= hp_mute;
+
+ return snd_soc_write(codec, ALC5623_MISC_CTRL, mute_reg);
+}
+
+#define ALC5623_ADD2_POWER_EN (ALC5623_PWR_ADD2_VREF \
+ | ALC5623_PWR_ADD2_DAC_REF_CIR)
+
+#define ALC5623_ADD3_POWER_EN (ALC5623_PWR_ADD3_MAIN_BIAS \
+ | ALC5623_PWR_ADD3_MIC1_BOOST_AD)
+
+#define ALC5623_ADD1_POWER_EN \
+ (ALC5623_PWR_ADD1_SHORT_CURR_DET_EN | ALC5623_PWR_ADD1_SOFTGEN_EN \
+ | ALC5623_PWR_ADD1_DEPOP_BUF_HP | ALC5623_PWR_ADD1_HP_OUT_AMP \
+ | ALC5623_PWR_ADD1_HP_OUT_ENH_AMP)
+
+#define ALC5623_ADD1_POWER_EN_5622 \
+ (ALC5623_PWR_ADD1_SHORT_CURR_DET_EN \
+ | ALC5623_PWR_ADD1_HP_OUT_AMP)
+
+static void enable_power_depop(struct snd_soc_codec *codec)
+{
+ struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_update_bits(codec, 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_update_bits(codec, 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);
+
+ /* avoid writing '1' into 5622 reserved bits */
+ if (alc5623->id == 0x22)
+ snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1,
+ ALC5623_ADD1_POWER_EN_5622);
+ else
+ snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1,
+ ALC5623_ADD1_POWER_EN);
+
+ /* disable HP Depop2 */
+ snd_soc_update_bits(codec, ALC5623_MISC_CTRL,
+ ALC5623_MISC_HP_DEPOP_MODE2_EN,
+ 0);
+
+}
+
+static int alc5623_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ enable_power_depop(codec);
+ 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,
+ ALC5623_PWR_ADD2_VREF);
+ snd_soc_write(codec, 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);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define ALC5623_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \
+ | SNDRV_PCM_FMTBIT_S24_LE \
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops alc5623_dai_ops = {
+ .hw_params = alc5623_pcm_hw_params,
+ .digital_mute = alc5623_mute,
+ .set_fmt = alc5623_set_dai_fmt,
+ .set_sysclk = alc5623_set_dai_sysclk,
+ .set_pll = alc5623_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver alc5623_dai = {
+ .name = "alc5623-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ALC5623_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ALC5623_FORMATS,},
+
+ .ops = &alc5623_dai_ops,
+};
+
+static int alc5623_suspend(struct snd_soc_codec *codec)
+{
+ struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(alc5623->regmap, true);
+
+ return 0;
+}
+
+static int alc5623_resume(struct snd_soc_codec *codec)
+{
+ struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ 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",
+ ret);
+ regcache_cache_only(alc5623->regmap, true);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int alc5623_probe(struct snd_soc_codec *codec)
+{
+ struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ alc5623_reset(codec);
+
+ if (alc5623->add_ctrl) {
+ snd_soc_write(codec, ALC5623_ADD_CTRL_REG,
+ alc5623->add_ctrl);
+ }
+
+ if (alc5623->jack_det_ctrl) {
+ snd_soc_write(codec, ALC5623_JACK_DET_CTRL,
+ alc5623->jack_det_ctrl);
+ }
+
+ switch (alc5623->id) {
+ case 0x21:
+ snd_soc_add_codec_controls(codec, alc5621_vol_snd_controls,
+ ARRAY_SIZE(alc5621_vol_snd_controls));
+ break;
+ case 0x22:
+ snd_soc_add_codec_controls(codec, alc5622_vol_snd_controls,
+ ARRAY_SIZE(alc5622_vol_snd_controls));
+ break;
+ case 0x23:
+ snd_soc_add_codec_controls(codec, alc5623_vol_snd_controls,
+ ARRAY_SIZE(alc5623_vol_snd_controls));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_add_codec_controls(codec, alc5623_snd_controls,
+ ARRAY_SIZE(alc5623_snd_controls));
+
+ snd_soc_dapm_new_controls(dapm, alc5623_dapm_widgets,
+ ARRAY_SIZE(alc5623_dapm_widgets));
+
+ /* set up audio path interconnects */
+ snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
+
+ switch (alc5623->id) {
+ case 0x21:
+ case 0x22:
+ snd_soc_dapm_new_controls(dapm, alc5623_dapm_amp_widgets,
+ ARRAY_SIZE(alc5623_dapm_amp_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon_amp_spk,
+ ARRAY_SIZE(intercon_amp_spk));
+ break;
+ case 0x23:
+ snd_soc_dapm_add_routes(dapm, intercon_spk,
+ ARRAY_SIZE(intercon_spk));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static 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 regmap_config alc5623_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .reg_stride = 2,
+
+ .max_register = ALC5623_VENDOR_ID2,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+/*
+ * 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)
+{
+ struct alc5623_platform_data *pdata;
+ struct alc5623_priv *alc5623;
+ struct device_node *np;
+ unsigned int vid1, vid2;
+ int ret;
+ u32 val32;
+
+ alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv),
+ GFP_KERNEL);
+ if (alc5623 == NULL)
+ return -ENOMEM;
+
+ alc5623->regmap = devm_regmap_init_i2c(client, &alc5623_regmap);
+ if (IS_ERR(alc5623->regmap)) {
+ ret = PTR_ERR(alc5623->regmap);
+ dev_err(&client->dev, "Failed to initialise I/O: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(alc5623->regmap, ALC5623_VENDOR_ID1, &vid1);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to read vendor ID1: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(alc5623->regmap, ALC5623_VENDOR_ID2, &vid2);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to read vendor ID2: %d\n", ret);
+ return ret;
+ }
+ vid2 >>= 8;
+
+ if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) {
+ dev_err(&client->dev, "unknown or wrong codec\n");
+ dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n",
+ 0x10ec, id->driver_data,
+ vid1, vid2);
+ return -ENODEV;
+ }
+
+ dev_dbg(&client->dev, "Found codec id : alc56%02x\n", vid2);
+
+ pdata = client->dev.platform_data;
+ if (pdata) {
+ alc5623->add_ctrl = pdata->add_ctrl;
+ alc5623->jack_det_ctrl = pdata->jack_det_ctrl;
+ } else {
+ if (client->dev.of_node) {
+ np = client->dev.of_node;
+ ret = of_property_read_u32(np, "add-ctrl", &val32);
+ if (!ret)
+ alc5623->add_ctrl = val32;
+ ret = of_property_read_u32(np, "jack-det-ctrl", &val32);
+ if (!ret)
+ alc5623->jack_det_ctrl = val32;
+ }
+ }
+
+ alc5623->id = vid2;
+ switch (alc5623->id) {
+ case 0x21:
+ alc5623_dai.name = "alc5621-hifi";
+ break;
+ case 0x22:
+ alc5623_dai.name = "alc5622-hifi";
+ break;
+ case 0x23:
+ alc5623_dai.name = "alc5623-hifi";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ i2c_set_clientdata(client, alc5623);
+
+ ret = snd_soc_register_codec(&client->dev,
+ &soc_codec_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);
+
+static const struct of_device_id alc5623_of_match[] = {
+ { .compatible = "realtek,alc5623", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, alc5623_of_match);
+
+/* 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,
+};
+
+module_i2c_driver(alc5623_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC alc5621/2/3 driver");
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/alc5623.h b/kernel/sound/soc/codecs/alc5623.h
new file mode 100644
index 000000000..f3d68260d
--- /dev/null
+++ b/kernel/sound/soc/codecs/alc5623.h
@@ -0,0 +1,161 @@
+/*
+ * alc5623.h -- alc562[123] ALSA Soc Audio driver
+ *
+ * Copyright 2008 Realtek Microelectronics
+ * Copyright 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ * 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
+#define _ALC5623_H
+
+#define ALC5623_RESET 0x00
+/* 5621 5622 5623 */
+/* speaker output vol 2 2 */
+/* line output vol 4 2 */
+/* HP output vol 4 0 4 */
+#define ALC5623_SPK_OUT_VOL 0x02
+#define ALC5623_HP_OUT_VOL 0x04
+#define ALC5623_MONO_AUX_OUT_VOL 0x06
+#define ALC5623_AUXIN_VOL 0x08
+#define ALC5623_LINE_IN_VOL 0x0A
+#define ALC5623_STEREO_DAC_VOL 0x0C
+#define ALC5623_MIC_VOL 0x0E
+#define ALC5623_MIC_ROUTING_CTRL 0x10
+#define ALC5623_ADC_REC_GAIN 0x12
+#define ALC5623_ADC_REC_MIXER 0x14
+#define ALC5623_SOFT_VOL_CTRL_TIME 0x16
+/* ALC5623_OUTPUT_MIXER_CTRL : */
+/* same remark as for reg 2 line vs speaker */
+#define ALC5623_OUTPUT_MIXER_CTRL 0x1C
+#define ALC5623_MIC_CTRL 0x22
+
+#define ALC5623_DAI_CONTROL 0x34
+#define ALC5623_DAI_SDP_MASTER_MODE (0 << 15)
+#define ALC5623_DAI_SDP_SLAVE_MODE (1 << 15)
+#define ALC5623_DAI_I2S_PCM_MODE (1 << 14)
+#define ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL (1 << 7)
+#define ALC5623_DAI_ADC_DATA_L_R_SWAP (1 << 5)
+#define ALC5623_DAI_DAC_DATA_L_R_SWAP (1 << 4)
+#define ALC5623_DAI_I2S_DL_MASK (3 << 2)
+#define ALC5623_DAI_I2S_DL_32 (3 << 2)
+#define ALC5623_DAI_I2S_DL_24 (2 << 2)
+#define ALC5623_DAI_I2S_DL_20 (1 << 2)
+#define ALC5623_DAI_I2S_DL_16 (0 << 2)
+#define ALC5623_DAI_I2S_DF_PCM (3 << 0)
+#define ALC5623_DAI_I2S_DF_LEFT (2 << 0)
+#define ALC5623_DAI_I2S_DF_RIGHT (1 << 0)
+#define ALC5623_DAI_I2S_DF_I2S (0 << 0)
+
+#define ALC5623_STEREO_AD_DA_CLK_CTRL 0x36
+#define ALC5623_COMPANDING_CTRL 0x38
+
+#define ALC5623_PWR_MANAG_ADD1 0x3A
+#define ALC5623_PWR_ADD1_MAIN_I2S_EN (1 << 15)
+#define ALC5623_PWR_ADD1_ZC_DET_PD_EN (1 << 14)
+#define ALC5623_PWR_ADD1_MIC1_BIAS_EN (1 << 11)
+#define ALC5623_PWR_ADD1_SHORT_CURR_DET_EN (1 << 10)
+#define ALC5623_PWR_ADD1_SOFTGEN_EN (1 << 8) /* rsvd on 5622 */
+#define ALC5623_PWR_ADD1_DEPOP_BUF_HP (1 << 6) /* rsvd on 5622 */
+#define ALC5623_PWR_ADD1_HP_OUT_AMP (1 << 5)
+#define ALC5623_PWR_ADD1_HP_OUT_ENH_AMP (1 << 4) /* rsvd on 5622 */
+#define ALC5623_PWR_ADD1_DEPOP_BUF_AUX (1 << 2)
+#define ALC5623_PWR_ADD1_AUX_OUT_AMP (1 << 1)
+#define ALC5623_PWR_ADD1_AUX_OUT_ENH_AMP (1 << 0) /* rsvd on 5622 */
+
+#define ALC5623_PWR_MANAG_ADD2 0x3C
+#define ALC5623_PWR_ADD2_LINEOUT (1 << 15) /* rt5623 */
+#define ALC5623_PWR_ADD2_CLASS_AB (1 << 15) /* rt5621 */
+#define ALC5623_PWR_ADD2_CLASS_D (1 << 14) /* rt5621 */
+#define ALC5623_PWR_ADD2_VREF (1 << 13)
+#define ALC5623_PWR_ADD2_PLL (1 << 12)
+#define ALC5623_PWR_ADD2_DAC_REF_CIR (1 << 10)
+#define ALC5623_PWR_ADD2_L_DAC_CLK (1 << 9)
+#define ALC5623_PWR_ADD2_R_DAC_CLK (1 << 8)
+#define ALC5623_PWR_ADD2_L_ADC_CLK_GAIN (1 << 7)
+#define ALC5623_PWR_ADD2_R_ADC_CLK_GAIN (1 << 6)
+#define ALC5623_PWR_ADD2_L_HP_MIXER (1 << 5)
+#define ALC5623_PWR_ADD2_R_HP_MIXER (1 << 4)
+#define ALC5623_PWR_ADD2_SPK_MIXER (1 << 3)
+#define ALC5623_PWR_ADD2_MONO_MIXER (1 << 2)
+#define ALC5623_PWR_ADD2_L_ADC_REC_MIXER (1 << 1)
+#define ALC5623_PWR_ADD2_R_ADC_REC_MIXER (1 << 0)
+
+#define ALC5623_PWR_MANAG_ADD3 0x3E
+#define ALC5623_PWR_ADD3_MAIN_BIAS (1 << 15)
+#define ALC5623_PWR_ADD3_AUXOUT_L_VOL_AMP (1 << 14)
+#define ALC5623_PWR_ADD3_AUXOUT_R_VOL_AMP (1 << 13)
+#define ALC5623_PWR_ADD3_SPK_OUT (1 << 12)
+#define ALC5623_PWR_ADD3_HP_L_OUT_VOL (1 << 10)
+#define ALC5623_PWR_ADD3_HP_R_OUT_VOL (1 << 9)
+#define ALC5623_PWR_ADD3_LINEIN_L_VOL (1 << 7)
+#define ALC5623_PWR_ADD3_LINEIN_R_VOL (1 << 6)
+#define ALC5623_PWR_ADD3_AUXIN_L_VOL (1 << 5)
+#define ALC5623_PWR_ADD3_AUXIN_R_VOL (1 << 4)
+#define ALC5623_PWR_ADD3_MIC1_FUN_CTRL (1 << 3)
+#define ALC5623_PWR_ADD3_MIC2_FUN_CTRL (1 << 2)
+#define ALC5623_PWR_ADD3_MIC1_BOOST_AD (1 << 1)
+#define ALC5623_PWR_ADD3_MIC2_BOOST_AD (1 << 0)
+
+#define ALC5623_ADD_CTRL_REG 0x40
+
+#define ALC5623_GLOBAL_CLK_CTRL_REG 0x42
+#define ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL (1 << 15)
+#define ALC5623_GBL_CLK_SYS_SOUR_SEL_MCLK (0 << 15)
+#define ALC5623_GBL_CLK_PLL_SOUR_SEL_BITCLK (1 << 14)
+#define ALC5623_GBL_CLK_PLL_SOUR_SEL_MCLK (0 << 14)
+#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV8 (3 << 1)
+#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV4 (2 << 1)
+#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV2 (1 << 1)
+#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV1 (0 << 1)
+#define ALC5623_GBL_CLK_PLL_PRE_DIV2 (1 << 0)
+#define ALC5623_GBL_CLK_PLL_PRE_DIV1 (0 << 0)
+
+#define ALC5623_PLL_CTRL 0x44
+#define ALC5623_PLL_CTRL_N_VAL(n) (((n)&0xff) << 8)
+#define ALC5623_PLL_CTRL_K_VAL(k) (((k)&0x7) << 4)
+#define ALC5623_PLL_CTRL_M_VAL(m) ((m)&0xf)
+
+#define ALC5623_GPIO_OUTPUT_PIN_CTRL 0x4A
+#define ALC5623_GPIO_PIN_CONFIG 0x4C
+#define ALC5623_GPIO_PIN_POLARITY 0x4E
+#define ALC5623_GPIO_PIN_STICKY 0x50
+#define ALC5623_GPIO_PIN_WAKEUP 0x52
+#define ALC5623_GPIO_PIN_STATUS 0x54
+#define ALC5623_GPIO_PIN_SHARING 0x56
+#define ALC5623_OVER_CURR_STATUS 0x58
+#define ALC5623_JACK_DET_CTRL 0x5A
+
+#define ALC5623_MISC_CTRL 0x5E
+#define ALC5623_MISC_DISABLE_FAST_VREG (1 << 15)
+#define ALC5623_MISC_SPK_CLASS_AB_OC_PD (1 << 13) /* 5621 */
+#define ALC5623_MISC_SPK_CLASS_AB_OC_DET (1 << 12) /* 5621 */
+#define ALC5623_MISC_HP_DEPOP_MODE3_EN (1 << 10)
+#define ALC5623_MISC_HP_DEPOP_MODE2_EN (1 << 9)
+#define ALC5623_MISC_HP_DEPOP_MODE1_EN (1 << 8)
+#define ALC5623_MISC_AUXOUT_DEPOP_MODE3_EN (1 << 6)
+#define ALC5623_MISC_AUXOUT_DEPOP_MODE2_EN (1 << 5)
+#define ALC5623_MISC_AUXOUT_DEPOP_MODE1_EN (1 << 4)
+#define ALC5623_MISC_M_DAC_L_INPUT (1 << 3)
+#define ALC5623_MISC_M_DAC_R_INPUT (1 << 2)
+#define ALC5623_MISC_IRQOUT_INV_CTRL (1 << 0)
+
+#define ALC5623_PSEDUEO_SPATIAL_CTRL 0x60
+#define ALC5623_EQ_CTRL 0x62
+#define ALC5623_EQ_MODE_ENABLE 0x66
+#define ALC5623_AVC_CTRL 0x68
+#define ALC5623_HID_CTRL_INDEX 0x6A
+#define ALC5623_HID_CTRL_DATA 0x6C
+#define ALC5623_VENDOR_ID1 0x7C
+#define ALC5623_VENDOR_ID2 0x7E
+
+#define ALC5623_PLL_FR_MCLK 0
+#define ALC5623_PLL_FR_BCK 1
+#endif
diff --git a/kernel/sound/soc/codecs/alc5632.c b/kernel/sound/soc/codecs/alc5632.c
new file mode 100644
index 000000000..db3283abb
--- /dev/null
+++ b/kernel/sound/soc/codecs/alc5632.c
@@ -0,0 +1,1199 @@
+/*
+* alc5632.c -- ALC5632 ALSA SoC Audio Codec
+*
+* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
+*
+* Authors: Leon Romanovsky <leon@leon.nu>
+* Andrey Danin <danindrey@mail.ru>
+* Ilya Petrov <ilya.muromec@gmail.com>
+* 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>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/slab.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 <sound/initval.h>
+
+#include "alc5632.h"
+
+/*
+ * ALC5632 register cache
+ */
+static struct reg_default alc5632_reg_defaults[] = {
+ { 2, 0x8080 }, /* R2 - Speaker Output Volume */
+ { 4, 0x8080 }, /* R4 - Headphone Output Volume */
+ { 6, 0x8080 }, /* R6 - AUXOUT Volume */
+ { 8, 0xC800 }, /* R8 - Phone Input */
+ { 10, 0xE808 }, /* R10 - LINE_IN Volume */
+ { 12, 0x1010 }, /* R12 - STEREO DAC Input Volume */
+ { 14, 0x0808 }, /* R14 - MIC Input Volume */
+ { 16, 0xEE0F }, /* R16 - Stereo DAC and MIC Routing Control */
+ { 18, 0xCBCB }, /* R18 - ADC Record Gain */
+ { 20, 0x7F7F }, /* R20 - ADC Record Mixer Control */
+ { 24, 0xE010 }, /* R24 - Voice DAC Volume */
+ { 28, 0x8008 }, /* R28 - Output Mixer Control */
+ { 34, 0x0000 }, /* R34 - Microphone Control */
+ { 36, 0x00C0 }, /* R36 - Codec Digital MIC/Digital Boost
+ Control */
+ { 46, 0x0000 }, /* R46 - Stereo DAC/Voice DAC/Stereo ADC
+ Function Select */
+ { 52, 0x8000 }, /* R52 - Main Serial Data Port Control
+ (Stereo I2S) */
+ { 54, 0x0000 }, /* R54 - Extend Serial Data Port Control
+ (VoDAC_I2S/PCM) */
+ { 58, 0x0000 }, /* R58 - Power Management Addition 1 */
+ { 60, 0x0000 }, /* R60 - Power Management Addition 2 */
+ { 62, 0x8000 }, /* R62 - Power Management Addition 3 */
+ { 64, 0x0C0A }, /* R64 - General Purpose Control Register 1 */
+ { 66, 0x0000 }, /* R66 - General Purpose Control Register 2 */
+ { 68, 0x0000 }, /* R68 - PLL1 Control */
+ { 70, 0x0000 }, /* R70 - PLL2 Control */
+ { 76, 0xBE3E }, /* R76 - GPIO Pin Configuration */
+ { 78, 0xBE3E }, /* R78 - GPIO Pin Polarity */
+ { 80, 0x0000 }, /* R80 - GPIO Pin Sticky */
+ { 82, 0x0000 }, /* R82 - GPIO Pin Wake Up */
+ { 86, 0x0000 }, /* R86 - Pin Sharing */
+ { 90, 0x0009 }, /* R90 - Soft Volume Control Setting */
+ { 92, 0x0000 }, /* R92 - GPIO_Output Pin Control */
+ { 94, 0x3000 }, /* R94 - MISC Control */
+ { 96, 0x3075 }, /* R96 - Stereo DAC Clock Control_1 */
+ { 98, 0x1010 }, /* R98 - Stereo DAC Clock Control_2 */
+ { 100, 0x3110 }, /* R100 - VoDAC_PCM Clock Control_1 */
+ { 104, 0x0553 }, /* R104 - Pseudo Stereo and Spatial Effect
+ Block Control */
+ { 106, 0x0000 }, /* R106 - Private Register Address */
+};
+
+/* codec private data */
+struct alc5632_priv {
+ struct regmap *regmap;
+ u8 id;
+ unsigned int sysclk;
+};
+
+static bool alc5632_volatile_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case ALC5632_RESET:
+ case ALC5632_PWR_DOWN_CTRL_STATUS:
+ case ALC5632_GPIO_PIN_STATUS:
+ case ALC5632_OVER_CURR_STATUS:
+ case ALC5632_HID_CTRL_DATA:
+ case ALC5632_EQ_CTRL:
+ case ALC5632_VENDOR_ID1:
+ case ALC5632_VENDOR_ID2:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static inline int alc5632_reset(struct regmap *map)
+{
+ return regmap_write(map, ALC5632_RESET, 0x59B4);
+}
+
+static int amp_mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(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);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_write(codec, ALC5632_HID_CTRL_DATA, 0xFFFF);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_write(codec, ALC5632_HID_CTRL_DATA, 0);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * ALC5632 Controls
+ */
+
+/* -34.5db min scale, 1.5db steps, no mute */
+static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0);
+/* -46.5db min scale, 1.5db steps, no mute */
+static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0);
+/* -16.5db min scale, 1.5db steps, no mute */
+static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0);
+static const unsigned int boost_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+ 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0),
+};
+/* 0db min scale, 6 db steps, no mute */
+static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0);
+/* 0db min scalem 0.75db steps, no mute */
+static const DECLARE_TLV_DB_SCALE(vdac_tlv, -3525, 75, 0);
+
+static const struct snd_kcontrol_new alc5632_vol_snd_controls[] = {
+ /* left starts at bit 8, right at bit 0 */
+ /* 31 steps (5 bit), -46.5db scale */
+ SOC_DOUBLE_TLV("Speaker Playback Volume",
+ ALC5632_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ /* bit 15 mutes left, bit 7 right */
+ SOC_DOUBLE("Speaker Playback Switch",
+ ALC5632_SPK_OUT_VOL, 15, 7, 1, 1),
+ SOC_DOUBLE_TLV("Headphone Playback Volume",
+ ALC5632_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Headphone Playback Switch",
+ ALC5632_HP_OUT_VOL, 15, 7, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5632_snd_controls[] = {
+ SOC_DOUBLE_TLV("Auxout Playback Volume",
+ ALC5632_AUX_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+ SOC_DOUBLE("Auxout Playback Switch",
+ ALC5632_AUX_OUT_VOL, 15, 7, 1, 1),
+ SOC_SINGLE_TLV("Voice DAC Playback Volume",
+ ALC5632_VOICE_DAC_VOL, 0, 63, 0, vdac_tlv),
+ SOC_SINGLE("Voice DAC Playback Switch",
+ ALC5632_VOICE_DAC_VOL, 12, 1, 1),
+ SOC_SINGLE_TLV("Phone Playback Volume",
+ ALC5632_PHONE_IN_VOL, 8, 31, 1, vol_tlv),
+ SOC_DOUBLE_TLV("LineIn Playback Volume",
+ ALC5632_LINE_IN_VOL, 8, 0, 31, 1, vol_tlv),
+ SOC_DOUBLE_TLV("Master Playback Volume",
+ ALC5632_STEREO_DAC_IN_VOL, 8, 0, 63, 1, vdac_tlv),
+ SOC_DOUBLE("Master Playback Switch",
+ ALC5632_STEREO_DAC_IN_VOL, 15, 7, 1, 1),
+ SOC_SINGLE_TLV("Mic1 Playback Volume",
+ ALC5632_MIC_VOL, 8, 31, 1, vol_tlv),
+ SOC_SINGLE_TLV("Mic2 Playback Volume",
+ ALC5632_MIC_VOL, 0, 31, 1, vol_tlv),
+ SOC_DOUBLE_TLV("Rec Capture Volume",
+ ALC5632_ADC_REC_GAIN, 8, 0, 31, 0, adc_rec_tlv),
+ SOC_SINGLE_TLV("Mic 1 Boost Volume",
+ ALC5632_MIC_CTRL, 10, 3, 0, boost_tlv),
+ SOC_SINGLE_TLV("Mic 2 Boost Volume",
+ ALC5632_MIC_CTRL, 8, 3, 0, boost_tlv),
+ SOC_SINGLE_TLV("DMIC Boost Capture Volume",
+ ALC5632_DIGI_BOOST_CTRL, 0, 7, 0, dig_tlv),
+ SOC_SINGLE("DMIC En Capture Switch",
+ ALC5632_DIGI_BOOST_CTRL, 15, 1, 0),
+ SOC_SINGLE("DMIC PreFilter Capture Switch",
+ ALC5632_DIGI_BOOST_CTRL, 12, 1, 0),
+};
+
+/*
+ * DAPM Controls
+ */
+static const struct snd_kcontrol_new alc5632_hp_mixer_controls[] = {
+SOC_DAPM_SINGLE("LI2HP Playback Switch", ALC5632_LINE_IN_VOL, 15, 1, 1),
+SOC_DAPM_SINGLE("PHONE2HP Playback Switch", ALC5632_PHONE_IN_VOL, 15, 1, 1),
+SOC_DAPM_SINGLE("MIC12HP Playback Switch", ALC5632_MIC_ROUTING_CTRL, 15, 1, 1),
+SOC_DAPM_SINGLE("MIC22HP Playback Switch", ALC5632_MIC_ROUTING_CTRL, 11, 1, 1),
+SOC_DAPM_SINGLE("VOICE2HP Playback Switch", ALC5632_VOICE_DAC_VOL, 15, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5632_hpl_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2HP_L Playback Switch", ALC5632_ADC_REC_GAIN, 15, 1, 1),
+SOC_DAPM_SINGLE("DACL2HP Playback Switch", ALC5632_MIC_ROUTING_CTRL, 3, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5632_hpr_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2HP_R Playback Switch", ALC5632_ADC_REC_GAIN, 7, 1, 1),
+SOC_DAPM_SINGLE("DACR2HP Playback Switch", ALC5632_MIC_ROUTING_CTRL, 2, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5632_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2MONO_L Playback Switch", ALC5632_ADC_REC_GAIN, 14, 1, 1),
+SOC_DAPM_SINGLE("ADC2MONO_R Playback Switch", ALC5632_ADC_REC_GAIN, 6, 1, 1),
+SOC_DAPM_SINGLE("LI2MONO Playback Switch", ALC5632_LINE_IN_VOL, 13, 1, 1),
+SOC_DAPM_SINGLE("MIC12MONO Playback Switch",
+ ALC5632_MIC_ROUTING_CTRL, 13, 1, 1),
+SOC_DAPM_SINGLE("MIC22MONO Playback Switch",
+ ALC5632_MIC_ROUTING_CTRL, 9, 1, 1),
+SOC_DAPM_SINGLE("DAC2MONO Playback Switch", ALC5632_MIC_ROUTING_CTRL, 0, 1, 1),
+SOC_DAPM_SINGLE("VOICE2MONO Playback Switch", ALC5632_VOICE_DAC_VOL, 13, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5632_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("LI2SPK Playback Switch", ALC5632_LINE_IN_VOL, 14, 1, 1),
+SOC_DAPM_SINGLE("PHONE2SPK Playback Switch", ALC5632_PHONE_IN_VOL, 14, 1, 1),
+SOC_DAPM_SINGLE("MIC12SPK Playback Switch",
+ ALC5632_MIC_ROUTING_CTRL, 14, 1, 1),
+SOC_DAPM_SINGLE("MIC22SPK Playback Switch",
+ ALC5632_MIC_ROUTING_CTRL, 10, 1, 1),
+SOC_DAPM_SINGLE("DAC2SPK Playback Switch", ALC5632_MIC_ROUTING_CTRL, 1, 1, 1),
+SOC_DAPM_SINGLE("VOICE2SPK Playback Switch", ALC5632_VOICE_DAC_VOL, 14, 1, 1),
+};
+
+/* Left Record Mixer */
+static const struct snd_kcontrol_new alc5632_captureL_mixer_controls[] = {
+SOC_DAPM_SINGLE("MIC12REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 14, 1, 1),
+SOC_DAPM_SINGLE("MIC22REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 13, 1, 1),
+SOC_DAPM_SINGLE("LIL2REC Capture Switch", ALC5632_ADC_REC_MIXER, 12, 1, 1),
+SOC_DAPM_SINGLE("PH2REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 11, 1, 1),
+SOC_DAPM_SINGLE("HPL2REC Capture Switch", ALC5632_ADC_REC_MIXER, 10, 1, 1),
+SOC_DAPM_SINGLE("SPK2REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 9, 1, 1),
+SOC_DAPM_SINGLE("MONO2REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 8, 1, 1),
+};
+
+/* Right Record Mixer */
+static const struct snd_kcontrol_new alc5632_captureR_mixer_controls[] = {
+SOC_DAPM_SINGLE("MIC12REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 6, 1, 1),
+SOC_DAPM_SINGLE("MIC22REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 5, 1, 1),
+SOC_DAPM_SINGLE("LIR2REC Capture Switch", ALC5632_ADC_REC_MIXER, 4, 1, 1),
+SOC_DAPM_SINGLE("PH2REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 3, 1, 1),
+SOC_DAPM_SINGLE("HPR2REC Capture Switch", ALC5632_ADC_REC_MIXER, 2, 1, 1),
+SOC_DAPM_SINGLE("SPK2REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 1, 1, 1),
+SOC_DAPM_SINGLE("MONO2REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 0, 1, 1),
+};
+
+/* Dmic Mixer */
+static const struct snd_kcontrol_new alc5632_dmicl_mixer_controls[] = {
+SOC_DAPM_SINGLE("DMICL2ADC Capture Switch", ALC5632_DIGI_BOOST_CTRL, 7, 1, 1),
+};
+static const struct snd_kcontrol_new alc5632_dmicr_mixer_controls[] = {
+SOC_DAPM_SINGLE("DMICR2ADC Capture Switch", ALC5632_DIGI_BOOST_CTRL, 6, 1, 1),
+};
+
+static const char * const alc5632_spk_n_sour_sel[] = {
+ "RN/-R", "RP/+R", "LN/-R", "Mute"};
+static const char * const alc5632_hpl_out_input_sel[] = {
+ "Vmid", "HP Left Mix"};
+static const char * const alc5632_hpr_out_input_sel[] = {
+ "Vmid", "HP Right Mix"};
+static const char * const alc5632_spkout_input_sel[] = {
+ "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"};
+static const char * const alc5632_aux_out_input_sel[] = {
+ "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"};
+static const char * const alc5632_adcr_func_sel[] = {
+ "Stereo ADC", "Voice ADC"};
+static const char * const alc5632_i2s_out_sel[] = {
+ "ADC LR", "Voice Stereo Digital"};
+
+/* auxout output mux */
+static SOC_ENUM_SINGLE_DECL(alc5632_aux_out_input_enum,
+ ALC5632_OUTPUT_MIXER_CTRL, 6,
+ alc5632_aux_out_input_sel);
+static const struct snd_kcontrol_new alc5632_auxout_mux_controls =
+SOC_DAPM_ENUM("AuxOut Mux", alc5632_aux_out_input_enum);
+
+/* speaker output mux */
+static SOC_ENUM_SINGLE_DECL(alc5632_spkout_input_enum,
+ ALC5632_OUTPUT_MIXER_CTRL, 10,
+ alc5632_spkout_input_sel);
+static const struct snd_kcontrol_new alc5632_spkout_mux_controls =
+SOC_DAPM_ENUM("SpeakerOut Mux", alc5632_spkout_input_enum);
+
+/* headphone left output mux */
+static SOC_ENUM_SINGLE_DECL(alc5632_hpl_out_input_enum,
+ ALC5632_OUTPUT_MIXER_CTRL, 9,
+ alc5632_hpl_out_input_sel);
+static const struct snd_kcontrol_new alc5632_hpl_out_mux_controls =
+SOC_DAPM_ENUM("Left Headphone Mux", alc5632_hpl_out_input_enum);
+
+/* headphone right output mux */
+static SOC_ENUM_SINGLE_DECL(alc5632_hpr_out_input_enum,
+ ALC5632_OUTPUT_MIXER_CTRL, 8,
+ alc5632_hpr_out_input_sel);
+static const struct snd_kcontrol_new alc5632_hpr_out_mux_controls =
+SOC_DAPM_ENUM("Right Headphone Mux", alc5632_hpr_out_input_enum);
+
+/* speaker output N select */
+static SOC_ENUM_SINGLE_DECL(alc5632_spk_n_sour_enum,
+ ALC5632_OUTPUT_MIXER_CTRL, 14,
+ alc5632_spk_n_sour_sel);
+static const struct snd_kcontrol_new alc5632_spkoutn_mux_controls =
+SOC_DAPM_ENUM("SpeakerOut N Mux", alc5632_spk_n_sour_enum);
+
+/* speaker amplifier */
+static const char *alc5632_amp_names[] = {"AB Amp", "D Amp"};
+static SOC_ENUM_SINGLE_DECL(alc5632_amp_enum,
+ ALC5632_OUTPUT_MIXER_CTRL, 13,
+ alc5632_amp_names);
+static const struct snd_kcontrol_new alc5632_amp_mux_controls =
+ SOC_DAPM_ENUM("AB-D Amp Mux", alc5632_amp_enum);
+
+/* ADC output select */
+static SOC_ENUM_SINGLE_DECL(alc5632_adcr_func_enum,
+ ALC5632_DAC_FUNC_SELECT, 5,
+ alc5632_adcr_func_sel);
+static const struct snd_kcontrol_new alc5632_adcr_func_controls =
+ SOC_DAPM_ENUM("ADCR Mux", alc5632_adcr_func_enum);
+
+/* I2S out select */
+static SOC_ENUM_SINGLE_DECL(alc5632_i2s_out_enum,
+ ALC5632_I2S_OUT_CTL, 5,
+ alc5632_i2s_out_sel);
+static const struct snd_kcontrol_new alc5632_i2s_out_controls =
+ SOC_DAPM_ENUM("I2SOut Mux", alc5632_i2s_out_enum);
+
+static const struct snd_soc_dapm_widget alc5632_dapm_widgets[] = {
+/* Muxes */
+SND_SOC_DAPM_MUX("AuxOut Mux", SND_SOC_NOPM, 0, 0,
+ &alc5632_auxout_mux_controls),
+SND_SOC_DAPM_MUX("SpeakerOut Mux", SND_SOC_NOPM, 0, 0,
+ &alc5632_spkout_mux_controls),
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &alc5632_hpl_out_mux_controls),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &alc5632_hpr_out_mux_controls),
+SND_SOC_DAPM_MUX("SpeakerOut N Mux", SND_SOC_NOPM, 0, 0,
+ &alc5632_spkoutn_mux_controls),
+SND_SOC_DAPM_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0,
+ &alc5632_adcr_func_controls),
+SND_SOC_DAPM_MUX("I2SOut Mux", ALC5632_PWR_MANAG_ADD1, 11, 0,
+ &alc5632_i2s_out_controls),
+
+/* output mixers */
+SND_SOC_DAPM_MIXER("HP Mix", SND_SOC_NOPM, 0, 0,
+ &alc5632_hp_mixer_controls[0],
+ ARRAY_SIZE(alc5632_hp_mixer_controls)),
+SND_SOC_DAPM_MIXER("HPR Mix", ALC5632_PWR_MANAG_ADD2, 4, 0,
+ &alc5632_hpr_mixer_controls[0],
+ ARRAY_SIZE(alc5632_hpr_mixer_controls)),
+SND_SOC_DAPM_MIXER("HPL Mix", ALC5632_PWR_MANAG_ADD2, 5, 0,
+ &alc5632_hpl_mixer_controls[0],
+ ARRAY_SIZE(alc5632_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("HPOut Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Mono Mix", ALC5632_PWR_MANAG_ADD2, 2, 0,
+ &alc5632_mono_mixer_controls[0],
+ ARRAY_SIZE(alc5632_mono_mixer_controls)),
+SND_SOC_DAPM_MIXER("Speaker Mix", ALC5632_PWR_MANAG_ADD2, 3, 0,
+ &alc5632_speaker_mixer_controls[0],
+ ARRAY_SIZE(alc5632_speaker_mixer_controls)),
+SND_SOC_DAPM_MIXER("DMICL Mix", SND_SOC_NOPM, 0, 0,
+ &alc5632_dmicl_mixer_controls[0],
+ ARRAY_SIZE(alc5632_dmicl_mixer_controls)),
+SND_SOC_DAPM_MIXER("DMICR Mix", SND_SOC_NOPM, 0, 0,
+ &alc5632_dmicr_mixer_controls[0],
+ ARRAY_SIZE(alc5632_dmicr_mixer_controls)),
+
+/* input mixers */
+SND_SOC_DAPM_MIXER("Left Capture Mix", ALC5632_PWR_MANAG_ADD2, 1, 0,
+ &alc5632_captureL_mixer_controls[0],
+ ARRAY_SIZE(alc5632_captureL_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right Capture Mix", ALC5632_PWR_MANAG_ADD2, 0, 0,
+ &alc5632_captureR_mixer_controls[0],
+ ARRAY_SIZE(alc5632_captureR_mixer_controls)),
+
+SND_SOC_DAPM_AIF_IN("AIFRXL", "Left HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFRXR", "Right HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFTXL", "Left HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFTXR", "Right HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("VAIFRX", "Voice Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("VAIFTX", "Voice Capture", 0, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_DAC("Voice DAC", NULL, ALC5632_PWR_MANAG_ADD2, 10, 0),
+SND_SOC_DAPM_DAC("Left DAC", NULL, ALC5632_PWR_MANAG_ADD2, 9, 0),
+SND_SOC_DAPM_DAC("Right DAC", NULL, ALC5632_PWR_MANAG_ADD2, 8, 0),
+SND_SOC_DAPM_ADC("Left ADC", NULL, ALC5632_PWR_MANAG_ADD2, 7, 0),
+SND_SOC_DAPM_ADC("Right ADC", NULL, ALC5632_PWR_MANAG_ADD2, 6, 0),
+
+SND_SOC_DAPM_MIXER("DAC Left Channel", ALC5632_PWR_MANAG_ADD1, 15, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("DAC Right Channel",
+ ALC5632_PWR_MANAG_ADD1, 14, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("I2S Mix", ALC5632_PWR_MANAG_ADD1, 11, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Phone Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Line Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Voice Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("ADCLR", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Left Headphone", ALC5632_PWR_MANAG_ADD3, 11, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Headphone", ALC5632_PWR_MANAG_ADD3, 10, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left Speaker", ALC5632_PWR_MANAG_ADD3, 13, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Speaker", ALC5632_PWR_MANAG_ADD3, 12, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Aux Out", ALC5632_PWR_MANAG_ADD3, 14, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left LineIn", ALC5632_PWR_MANAG_ADD3, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right LineIn", ALC5632_PWR_MANAG_ADD3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Phone", ALC5632_PWR_MANAG_ADD3, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Phone ADMix", ALC5632_PWR_MANAG_ADD3, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC1 PGA", ALC5632_PWR_MANAG_ADD3, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC2 PGA", ALC5632_PWR_MANAG_ADD3, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC1 Pre Amp", ALC5632_PWR_MANAG_ADD3, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC2 Pre Amp", ALC5632_PWR_MANAG_ADD3, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1", ALC5632_PWR_MANAG_ADD1, 3, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", ALC5632_PWR_MANAG_ADD1, 2, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("D Amp", ALC5632_PWR_MANAG_ADD2, 14, 0, NULL, 0,
+ amp_mixer_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_PGA("AB Amp", ALC5632_PWR_MANAG_ADD2, 15, 0, NULL, 0),
+SND_SOC_DAPM_MUX("AB-D Amp Mux", ALC5632_PWR_MANAG_ADD1, 10, 0,
+ &alc5632_amp_mux_controls),
+
+SND_SOC_DAPM_OUTPUT("AUXOUT"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+SND_SOC_DAPM_OUTPUT("SPKOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+
+SND_SOC_DAPM_INPUT("LINEINL"),
+SND_SOC_DAPM_INPUT("LINEINR"),
+SND_SOC_DAPM_INPUT("PHONEP"),
+SND_SOC_DAPM_INPUT("PHONEN"),
+SND_SOC_DAPM_INPUT("DMICDAT"),
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2"),
+SND_SOC_DAPM_VMID("Vmid"),
+};
+
+
+static const struct snd_soc_dapm_route alc5632_dapm_routes[] = {
+ /* Playback streams */
+ {"Left DAC", NULL, "AIFRXL"},
+ {"Right DAC", NULL, "AIFRXR"},
+
+ /* virtual mixer - mixes left & right channels */
+ {"I2S Mix", NULL, "Left DAC"},
+ {"I2S Mix", NULL, "Right DAC"},
+ {"Line Mix", NULL, "Right LineIn"},
+ {"Line Mix", NULL, "Left LineIn"},
+ {"Phone Mix", NULL, "Phone"},
+ {"Phone Mix", NULL, "Phone ADMix"},
+ {"AUXOUT", NULL, "Aux Out"},
+
+ /* DAC */
+ {"DAC Right Channel", NULL, "I2S Mix"},
+ {"DAC Left Channel", NULL, "I2S Mix"},
+
+ /* HP mixer */
+ {"HPL Mix", "ADC2HP_L Playback Switch", "Left Capture Mix"},
+ {"HPL Mix", NULL, "HP Mix"},
+ {"HPR Mix", "ADC2HP_R Playback Switch", "Right Capture Mix"},
+ {"HPR Mix", NULL, "HP Mix"},
+ {"HP Mix", "LI2HP Playback Switch", "Line Mix"},
+ {"HP Mix", "PHONE2HP Playback Switch", "Phone Mix"},
+ {"HP Mix", "MIC12HP Playback Switch", "MIC1 PGA"},
+ {"HP Mix", "MIC22HP Playback Switch", "MIC2 PGA"},
+ {"HP Mix", "VOICE2HP Playback Switch", "Voice Mix"},
+ {"HPR Mix", "DACR2HP Playback Switch", "DAC Right Channel"},
+ {"HPL Mix", "DACL2HP Playback Switch", "DAC Left Channel"},
+ {"HPOut Mix", NULL, "HP Mix"},
+ {"HPOut Mix", NULL, "HPR Mix"},
+ {"HPOut Mix", NULL, "HPL Mix"},
+
+ /* speaker mixer */
+ {"Speaker Mix", "LI2SPK Playback Switch", "Line Mix"},
+ {"Speaker Mix", "PHONE2SPK Playback Switch", "Phone Mix"},
+ {"Speaker Mix", "MIC12SPK Playback Switch", "MIC1 PGA"},
+ {"Speaker Mix", "MIC22SPK Playback Switch", "MIC2 PGA"},
+ {"Speaker Mix", "DAC2SPK Playback Switch", "DAC Left Channel"},
+ {"Speaker Mix", "VOICE2SPK Playback Switch", "Voice Mix"},
+
+ /* mono mixer */
+ {"Mono Mix", "ADC2MONO_L Playback Switch", "Left Capture Mix"},
+ {"Mono Mix", "ADC2MONO_R Playback Switch", "Right Capture Mix"},
+ {"Mono Mix", "LI2MONO Playback Switch", "Line Mix"},
+ {"Mono Mix", "MIC12MONO Playback Switch", "MIC1 PGA"},
+ {"Mono Mix", "MIC22MONO Playback Switch", "MIC2 PGA"},
+ {"Mono Mix", "DAC2MONO Playback Switch", "DAC Left Channel"},
+ {"Mono Mix", "VOICE2MONO Playback Switch", "Voice Mix"},
+
+ /* Left record mixer */
+ {"Left Capture Mix", "LIL2REC Capture Switch", "LINEINL"},
+ {"Left Capture Mix", "PH2REC_L Capture Switch", "PHONEN"},
+ {"Left Capture Mix", "MIC12REC_L Capture Switch", "MIC1 Pre Amp"},
+ {"Left Capture Mix", "MIC22REC_L Capture Switch", "MIC2 Pre Amp"},
+ {"Left Capture Mix", "HPL2REC Capture Switch", "HPL Mix"},
+ {"Left Capture Mix", "SPK2REC_L Capture Switch", "Speaker Mix"},
+ {"Left Capture Mix", "MONO2REC_L Capture Switch", "Mono Mix"},
+
+ /*Right record mixer */
+ {"Right Capture Mix", "LIR2REC Capture Switch", "LINEINR"},
+ {"Right Capture Mix", "PH2REC_R Capture Switch", "PHONEP"},
+ {"Right Capture Mix", "MIC12REC_R Capture Switch", "MIC1 Pre Amp"},
+ {"Right Capture Mix", "MIC22REC_R Capture Switch", "MIC2 Pre Amp"},
+ {"Right Capture Mix", "HPR2REC Capture Switch", "HPR Mix"},
+ {"Right Capture Mix", "SPK2REC_R Capture Switch", "Speaker Mix"},
+ {"Right Capture Mix", "MONO2REC_R Capture Switch", "Mono Mix"},
+
+ /* headphone left mux */
+ {"Left Headphone Mux", "HP Left Mix", "HPL Mix"},
+ {"Left Headphone Mux", "Vmid", "Vmid"},
+
+ /* headphone right mux */
+ {"Right Headphone Mux", "HP Right Mix", "HPR Mix"},
+ {"Right Headphone Mux", "Vmid", "Vmid"},
+
+ /* speaker out mux */
+ {"SpeakerOut Mux", "Vmid", "Vmid"},
+ {"SpeakerOut Mux", "HPOut Mix", "HPOut Mix"},
+ {"SpeakerOut Mux", "Speaker Mix", "Speaker Mix"},
+ {"SpeakerOut Mux", "Mono Mix", "Mono Mix"},
+
+ /* Mono/Aux Out mux */
+ {"AuxOut Mux", "Vmid", "Vmid"},
+ {"AuxOut Mux", "HPOut Mix", "HPOut Mix"},
+ {"AuxOut Mux", "Speaker Mix", "Speaker Mix"},
+ {"AuxOut Mux", "Mono Mix", "Mono Mix"},
+
+ /* output pga */
+ {"HPL", NULL, "Left Headphone"},
+ {"Left Headphone", NULL, "Left Headphone Mux"},
+ {"HPR", NULL, "Right Headphone"},
+ {"Right Headphone", NULL, "Right Headphone Mux"},
+ {"Aux Out", NULL, "AuxOut Mux"},
+
+ /* input pga */
+ {"Left LineIn", NULL, "LINEINL"},
+ {"Right LineIn", NULL, "LINEINR"},
+ {"Phone", NULL, "PHONEP"},
+ {"MIC1 Pre Amp", NULL, "MIC1"},
+ {"MIC2 Pre Amp", NULL, "MIC2"},
+ {"MIC1 PGA", NULL, "MIC1 Pre Amp"},
+ {"MIC2 PGA", NULL, "MIC2 Pre Amp"},
+
+ /* left ADC */
+ {"Left ADC", NULL, "Left Capture Mix"},
+ {"DMICL Mix", "DMICL2ADC Capture Switch", "DMICDAT"},
+ {"Left ADC", NULL, "DMICL Mix"},
+ {"ADCLR", NULL, "Left ADC"},
+
+ /* right ADC */
+ {"Right ADC", NULL, "Right Capture Mix"},
+ {"DMICR Mix", "DMICR2ADC Capture Switch", "DMICDAT"},
+ {"Right ADC", NULL, "DMICR Mix"},
+ {"ADCR Mux", "Stereo ADC", "Right ADC"},
+ {"ADCR Mux", "Voice ADC", "Right ADC"},
+ {"ADCLR", NULL, "ADCR Mux"},
+ {"VAIFTX", NULL, "ADCR Mux"},
+
+ /* Digital I2S out */
+ {"I2SOut Mux", "ADC LR", "ADCLR"},
+ {"I2SOut Mux", "Voice Stereo Digital", "VAIFRX"},
+ {"AIFTXL", NULL, "I2SOut Mux"},
+ {"AIFTXR", NULL, "I2SOut Mux"},
+
+ /* Voice Mix */
+ {"Voice DAC", NULL, "VAIFRX"},
+ {"Voice Mix", NULL, "Voice DAC"},
+
+ /* Speaker Output */
+ {"SpeakerOut N Mux", "RN/-R", "Left Speaker"},
+ {"SpeakerOut N Mux", "RP/+R", "Left Speaker"},
+ {"SpeakerOut N Mux", "LN/-R", "Left Speaker"},
+ {"SpeakerOut N Mux", "Mute", "Vmid"},
+
+ {"SpeakerOut N Mux", "RN/-R", "Right Speaker"},
+ {"SpeakerOut N Mux", "RP/+R", "Right Speaker"},
+ {"SpeakerOut N Mux", "LN/-R", "Right Speaker"},
+ {"SpeakerOut N Mux", "Mute", "Vmid"},
+
+ {"AB Amp", NULL, "SpeakerOut Mux"},
+ {"D Amp", NULL, "SpeakerOut Mux"},
+ {"AB-D Amp Mux", "AB Amp", "AB Amp"},
+ {"AB-D Amp Mux", "D Amp", "D Amp"},
+ {"Left Speaker", NULL, "AB-D Amp Mux"},
+ {"Right Speaker", NULL, "AB-D Amp Mux"},
+
+ {"SPKOUT", NULL, "Left Speaker"},
+ {"SPKOUT", NULL, "Right Speaker"},
+
+ {"SPKOUTN", NULL, "SpeakerOut N Mux"},
+
+};
+
+/* PLL divisors */
+struct _pll_div {
+ u32 pll_in;
+ u32 pll_out;
+ u16 regvalue;
+};
+
+/* Note : pll code from original alc5632 driver. Not sure of how good it is */
+/* useful only for master mode */
+static const struct _pll_div codec_master_pll_div[] = {
+
+ { 2048000, 8192000, 0x0ea0},
+ { 3686400, 8192000, 0x4e27},
+ { 12000000, 8192000, 0x456b},
+ { 13000000, 8192000, 0x495f},
+ { 13100000, 8192000, 0x0320},
+ { 2048000, 11289600, 0xf637},
+ { 3686400, 11289600, 0x2f22},
+ { 12000000, 11289600, 0x3e2f},
+ { 13000000, 11289600, 0x4d5b},
+ { 13100000, 11289600, 0x363b},
+ { 2048000, 16384000, 0x1ea0},
+ { 3686400, 16384000, 0x9e27},
+ { 12000000, 16384000, 0x452b},
+ { 13000000, 16384000, 0x542f},
+ { 13100000, 16384000, 0x03a0},
+ { 2048000, 16934400, 0xe625},
+ { 3686400, 16934400, 0x9126},
+ { 12000000, 16934400, 0x4d2c},
+ { 13000000, 16934400, 0x742f},
+ { 13100000, 16934400, 0x3c27},
+ { 2048000, 22579200, 0x2aa0},
+ { 3686400, 22579200, 0x2f20},
+ { 12000000, 22579200, 0x7e2f},
+ { 13000000, 22579200, 0x742f},
+ { 13100000, 22579200, 0x3c27},
+ { 2048000, 24576000, 0x2ea0},
+ { 3686400, 24576000, 0xee27},
+ { 12000000, 24576000, 0x2915},
+ { 13000000, 24576000, 0x772e},
+ { 13100000, 24576000, 0x0d20},
+};
+
+/* FOUT = MCLK*(N+2)/((M+2)*(K+2))
+ N: bit 15:8 (div 2 .. div 257)
+ K: bit 6:4 typical 2
+ M: bit 3:0 (div 2 .. div 17)
+
+ same as for 5623 - thanks!
+*/
+
+static const struct _pll_div codec_slave_pll_div[] = {
+
+ { 1024000, 16384000, 0x3ea0},
+ { 1411200, 22579200, 0x3ea0},
+ { 1536000, 24576000, 0x3ea0},
+ { 2048000, 16384000, 0x1ea0},
+ { 2822400, 22579200, 0x1ea0},
+ { 3072000, 24576000, 0x1ea0},
+
+};
+
+static int alc5632_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ int i;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int gbl_clk = 0, pll_div = 0;
+ u16 reg;
+
+ if (pll_id < ALC5632_PLL_FR_MCLK || pll_id > ALC5632_PLL_FR_VBCLK)
+ return -EINVAL;
+
+ /* Disable PLL power */
+ snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ ALC5632_PWR_ADD2_PLL1,
+ 0);
+ snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ ALC5632_PWR_ADD2_PLL2,
+ 0);
+
+ /* pll is not used in slave mode */
+ reg = snd_soc_read(codec, ALC5632_DAI_CONTROL);
+ if (reg & ALC5632_DAI_SDP_SLAVE_MODE)
+ return 0;
+
+ if (!freq_in || !freq_out)
+ return 0;
+
+ switch (pll_id) {
+ case ALC5632_PLL_FR_MCLK:
+ for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++) {
+ if (codec_master_pll_div[i].pll_in == freq_in
+ && codec_master_pll_div[i].pll_out == freq_out) {
+ /* PLL source from MCLK */
+ pll_div = codec_master_pll_div[i].regvalue;
+ break;
+ }
+ }
+ break;
+ case ALC5632_PLL_FR_BCLK:
+ for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) {
+ if (codec_slave_pll_div[i].pll_in == freq_in
+ && codec_slave_pll_div[i].pll_out == freq_out) {
+ /* PLL source from Bitclk */
+ gbl_clk = ALC5632_PLL_FR_BCLK;
+ pll_div = codec_slave_pll_div[i].regvalue;
+ break;
+ }
+ }
+ break;
+ case ALC5632_PLL_FR_VBCLK:
+ for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) {
+ if (codec_slave_pll_div[i].pll_in == freq_in
+ && codec_slave_pll_div[i].pll_out == freq_out) {
+ /* PLL source from voice clock */
+ gbl_clk = ALC5632_PLL_FR_VBCLK;
+ pll_div = codec_slave_pll_div[i].regvalue;
+ break;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!pll_div)
+ return -EINVAL;
+
+ /* choose MCLK/BCLK/VBCLK */
+ snd_soc_write(codec, ALC5632_GPCR2, gbl_clk);
+ /* choose PLL1 clock rate */
+ snd_soc_write(codec, ALC5632_PLL1_CTRL, pll_div);
+ /* enable PLL1 */
+ snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ ALC5632_PWR_ADD2_PLL1,
+ ALC5632_PWR_ADD2_PLL1);
+ /* enable PLL2 */
+ snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ ALC5632_PWR_ADD2_PLL2,
+ ALC5632_PWR_ADD2_PLL2);
+ /* use PLL1 as main SYSCLK */
+ snd_soc_update_bits(codec, ALC5632_GPCR1,
+ ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1,
+ ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1);
+
+ return 0;
+}
+
+struct _coeff_div {
+ u16 fs;
+ u16 regvalue;
+};
+
+/* codec hifi mclk (after PLL) clock divider coefficients */
+/* values inspired from column BCLK=32Fs of Appendix A table */
+static const struct _coeff_div coeff_div[] = {
+ {512*1, 0x3075},
+};
+
+static int get_coeff(struct snd_soc_codec *codec, int rate)
+{
+ struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].fs * rate == alc5632->sysclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Clock after PLL and dividers
+ */
+static int alc5632_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+
+ switch (freq) {
+ case 4096000:
+ case 8192000:
+ case 11289600:
+ case 12288000:
+ case 16384000:
+ case 16934400:
+ case 18432000:
+ case 22579200:
+ case 24576000:
+ alc5632->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int alc5632_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface = ALC5632_DAI_SDP_MASTER_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ iface = ALC5632_DAI_SDP_SLAVE_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= ALC5632_DAI_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= ALC5632_DAI_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= ALC5632_DAI_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= ALC5632_DAI_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= ALC5632_DAI_MAIN_I2S_BCLK_POL_CTRL;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= ALC5632_DAI_MAIN_I2S_BCLK_POL_CTRL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_write(codec, 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;
+ int coeff, rate;
+ u16 iface;
+
+ iface = snd_soc_read(codec, ALC5632_DAI_CONTROL);
+ iface &= ~ALC5632_DAI_I2S_DL_MASK;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ iface |= ALC5632_DAI_I2S_DL_16;
+ break;
+ case 20:
+ iface |= ALC5632_DAI_I2S_DL_20;
+ break;
+ case 24:
+ iface |= ALC5632_DAI_I2S_DL_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface & srate */
+ snd_soc_write(codec, ALC5632_DAI_CONTROL, iface);
+ rate = params_rate(params);
+ coeff = get_coeff(codec, rate);
+ if (coeff < 0)
+ return -EINVAL;
+
+ coeff = coeff_div[coeff].regvalue;
+ snd_soc_write(codec, ALC5632_DAC_CLK_CTRL1, coeff);
+
+ return 0;
+}
+
+static int alc5632_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ 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;
+
+ if (mute)
+ mute_reg |= hp_mute;
+
+ return snd_soc_write(codec, ALC5632_MISC_CTRL, mute_reg);
+}
+
+#define ALC5632_ADD2_POWER_EN (ALC5632_PWR_ADD2_VREF)
+
+#define ALC5632_ADD3_POWER_EN (ALC5632_PWR_ADD3_MIC1_BOOST_AD)
+
+#define ALC5632_ADD1_POWER_EN \
+ (ALC5632_PWR_ADD1_DAC_REF \
+ | ALC5632_PWR_ADD1_SOFTGEN_EN \
+ | ALC5632_PWR_ADD1_HP_OUT_AMP \
+ | ALC5632_PWR_ADD1_HP_OUT_ENH_AMP \
+ | ALC5632_PWR_ADD1_MAIN_BIAS)
+
+static void enable_power_depop(struct snd_soc_codec *codec)
+{
+ snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ ALC5632_PWR_ADD1_SOFTGEN_EN,
+ ALC5632_PWR_ADD1_SOFTGEN_EN);
+
+ snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD3,
+ ALC5632_ADD3_POWER_EN,
+ ALC5632_ADD3_POWER_EN);
+
+ snd_soc_update_bits(codec, 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,
+ ALC5632_PWR_DOWN_CTRL_STATUS_MASK,
+ 0);
+
+ msleep(500);
+
+ snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ ALC5632_ADD2_POWER_EN,
+ ALC5632_ADD2_POWER_EN);
+
+ snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ ALC5632_ADD1_POWER_EN,
+ ALC5632_ADD1_POWER_EN);
+
+ /* disable HP Depop2 */
+ snd_soc_update_bits(codec, ALC5632_MISC_CTRL,
+ ALC5632_MISC_HP_DEPOP_MODE2_EN,
+ 0);
+
+}
+
+static int alc5632_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ enable_power_depop(codec);
+ 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,
+ ALC5632_PWR_MANAG_ADD1_MASK,
+ ALC5632_PWR_ADD1_MAIN_BIAS);
+ snd_soc_update_bits(codec, 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,
+ 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,
+ ALC5632_PWR_MANAG_ADD2_MASK, 0);
+ snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD3,
+ ALC5632_PWR_MANAG_ADD3_MASK, 0);
+ snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ ALC5632_PWR_MANAG_ADD1_MASK, 0);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define ALC5632_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \
+ | SNDRV_PCM_FMTBIT_S24_LE \
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops alc5632_dai_ops = {
+ .hw_params = alc5632_pcm_hw_params,
+ .digital_mute = alc5632_mute,
+ .set_fmt = alc5632_set_dai_fmt,
+ .set_sysclk = alc5632_set_dai_sysclk,
+ .set_pll = alc5632_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver alc5632_dai = {
+ .name = "alc5632-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ALC5632_FORMATS,},
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = ALC5632_FORMATS,},
+
+ .ops = &alc5632_dai_ops,
+ .symmetric_rates = 1,
+};
+
+#ifdef CONFIG_PM
+static int alc5632_resume(struct snd_soc_codec *codec)
+{
+ struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_sync(alc5632->regmap);
+
+ return 0;
+}
+#else
+#define alc5632_resume NULL
+#endif
+
+static int alc5632_probe(struct snd_soc_codec *codec)
+{
+ struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+
+ switch (alc5632->id) {
+ case 0x5c:
+ snd_soc_add_codec_controls(codec, alc5632_vol_snd_controls,
+ ARRAY_SIZE(alc5632_vol_snd_controls));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ 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 regmap_config alc5632_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = ALC5632_MAX_REGISTER,
+ .reg_defaults = alc5632_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(alc5632_reg_defaults),
+ .volatile_reg = alc5632_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+/*
+ * 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)
+{
+ struct alc5632_priv *alc5632;
+ int ret, ret1, ret2;
+ unsigned int vid1, vid2;
+
+ alc5632 = devm_kzalloc(&client->dev,
+ sizeof(struct alc5632_priv), GFP_KERNEL);
+ if (alc5632 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, alc5632);
+
+ alc5632->regmap = devm_regmap_init_i2c(client, &alc5632_regmap);
+ if (IS_ERR(alc5632->regmap)) {
+ ret = PTR_ERR(alc5632->regmap);
+ dev_err(&client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret1 = regmap_read(alc5632->regmap, ALC5632_VENDOR_ID1, &vid1);
+ ret2 = regmap_read(alc5632->regmap, ALC5632_VENDOR_ID2, &vid2);
+ if (ret1 != 0 || ret2 != 0) {
+ dev_err(&client->dev,
+ "Failed to read chip ID: ret1=%d, ret2=%d\n", ret1, ret2);
+ return -EIO;
+ }
+
+ vid2 >>= 8;
+
+ if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) {
+ dev_err(&client->dev,
+ "Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2);
+ return -EINVAL;
+ }
+
+ ret = alc5632_reset(alc5632->regmap);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to issue reset\n");
+ return ret;
+ }
+
+ alc5632->id = vid2;
+ switch (alc5632->id) {
+ case 0x5c:
+ alc5632_dai.name = "alc5632-hifi";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_register_codec(&client->dev,
+ &soc_codec_device_alc5632, &alc5632_dai, 1);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to register codec: %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);
+
+static const struct of_device_id alc5632_of_match[] = {
+ { .compatible = "realtek,alc5632", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, alc5632_of_match);
+
+/* 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,
+};
+
+module_i2c_driver(alc5632_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ALC5632 driver");
+MODULE_AUTHOR("Leon Romanovsky <leon@leon.nu>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/alc5632.h b/kernel/sound/soc/codecs/alc5632.h
new file mode 100644
index 000000000..1b5bda594
--- /dev/null
+++ b/kernel/sound/soc/codecs/alc5632.h
@@ -0,0 +1,252 @@
+/*
+* alc5632.h -- ALC5632 ALSA SoC Audio Codec
+*
+* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
+*
+* Authors: Leon Romanovsky <leon@leon.nu>
+* Andrey Danin <danindrey@mail.ru>
+* Ilya Petrov <ilya.muromec@gmail.com>
+* 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
+#define _ALC5632_H
+
+#define ALC5632_RESET 0x00
+/* speaker output vol 2 2 */
+/* line output vol 4 2 */
+/* HP output vol 4 0 4 */
+#define ALC5632_SPK_OUT_VOL 0x02 /* spe out vol */
+#define ALC5632_SPK_OUT_VOL_STEP 1.5
+#define ALC5632_HP_OUT_VOL 0x04 /* hp out vol */
+#define ALC5632_AUX_OUT_VOL 0x06 /* aux out vol */
+#define ALC5632_PHONE_IN_VOL 0x08 /* phone in vol */
+#define ALC5632_LINE_IN_VOL 0x0A /* line in vol */
+#define ALC5632_STEREO_DAC_IN_VOL 0x0C /* stereo dac in vol */
+#define ALC5632_MIC_VOL 0x0E /* mic in vol */
+/* stero dac/mic routing */
+#define ALC5632_MIC_ROUTING_CTRL 0x10
+#define ALC5632_MIC_ROUTE_MONOMIX (1 << 0)
+#define ALC5632_MIC_ROUTE_SPK (1 << 1)
+#define ALC5632_MIC_ROUTE_HP (1 << 2)
+
+#define ALC5632_ADC_REC_GAIN 0x12 /* rec gain */
+#define ALC5632_ADC_REC_GAIN_RANGE 0x1F1F
+#define ALC5632_ADC_REC_GAIN_BASE (-16.5)
+#define ALC5632_ADC_REC_GAIN_STEP 1.5
+
+#define ALC5632_ADC_REC_MIXER 0x14 /* mixer control */
+#define ALC5632_ADC_REC_MIC1 (1 << 6)
+#define ALC5632_ADC_REC_MIC2 (1 << 5)
+#define ALC5632_ADC_REC_LINE_IN (1 << 4)
+#define ALC5632_ADC_REC_AUX (1 << 3)
+#define ALC5632_ADC_REC_HP (1 << 2)
+#define ALC5632_ADC_REC_SPK (1 << 1)
+#define ALC5632_ADC_REC_MONOMIX (1 << 0)
+
+#define ALC5632_VOICE_DAC_VOL 0x18 /* voice dac vol */
+#define ALC5632_I2S_OUT_CTL 0x1A /* undocumented reg. found in path scheme */
+/* ALC5632_OUTPUT_MIXER_CTRL : */
+/* same remark as for reg 2 line vs speaker */
+#define ALC5632_OUTPUT_MIXER_CTRL 0x1C /* out mix ctrl */
+#define ALC5632_OUTPUT_MIXER_RP (1 << 14)
+#define ALC5632_OUTPUT_MIXER_WEEK (1 << 12)
+#define ALC5632_OUTPUT_MIXER_HP (1 << 10)
+#define ALC5632_OUTPUT_MIXER_AUX_SPK (2 << 6)
+#define ALC5632_OUTPUT_MIXER_AUX_HP_LR (1 << 6)
+#define ALC5632_OUTPUT_MIXER_HP_R (1 << 8)
+#define ALC5632_OUTPUT_MIXER_HP_L (1 << 9)
+
+#define ALC5632_MIC_CTRL 0x22 /* mic phone ctrl */
+#define ALC5632_MIC_BOOST_BYPASS 0
+#define ALC5632_MIC_BOOST_20DB 1
+#define ALC5632_MIC_BOOST_30DB 2
+#define ALC5632_MIC_BOOST_40DB 3
+
+#define ALC5632_DIGI_BOOST_CTRL 0x24 /* digi mic / bost ctl */
+#define ALC5632_MIC_BOOST_RANGE 7
+#define ALC5632_MIC_BOOST_STEP 6
+#define ALC5632_PWR_DOWN_CTRL_STATUS 0x26
+#define ALC5632_PWR_DOWN_CTRL_STATUS_MASK 0xEF00
+#define ALC5632_PWR_VREF_PR3 (1 << 11)
+#define ALC5632_PWR_VREF_PR2 (1 << 10)
+#define ALC5632_PWR_VREF_STATUS (1 << 3)
+#define ALC5632_PWR_AMIX_STATUS (1 << 2)
+#define ALC5632_PWR_DAC_STATUS (1 << 1)
+#define ALC5632_PWR_ADC_STATUS (1 << 0)
+/* stereo/voice DAC / stereo adc func ctrl */
+#define ALC5632_DAC_FUNC_SELECT 0x2E
+
+/* Main serial data port ctrl (i2s) */
+#define ALC5632_DAI_CONTROL 0x34
+
+#define ALC5632_DAI_SDP_MASTER_MODE (0 << 15)
+#define ALC5632_DAI_SDP_SLAVE_MODE (1 << 15)
+#define ALC5632_DAI_SADLRCK_MODE (1 << 14)
+/* 0:voice, 1:main */
+#define ALC5632_DAI_MAIN_I2S_SYSCLK_SEL (1 << 8)
+#define ALC5632_DAI_MAIN_I2S_BCLK_POL_CTRL (1 << 7)
+/* 0:normal, 1:invert */
+#define ALC5632_DAI_MAIN_I2S_LRCK_INV (1 << 6)
+#define ALC5632_DAI_I2S_DL_MASK (3 << 2)
+#define ALC5632_DAI_I2S_DL_8 (3 << 2)
+#define ALC5632_DAI_I2S_DL_24 (2 << 2)
+#define ALC5632_DAI_I2S_DL_20 (1 << 2)
+#define ALC5632_DAI_I2S_DL_16 (0 << 2)
+#define ALC5632_DAI_I2S_DF_MASK (3 << 0)
+#define ALC5632_DAI_I2S_DF_PCM_B (3 << 0)
+#define ALC5632_DAI_I2S_DF_PCM_A (2 << 0)
+#define ALC5632_DAI_I2S_DF_LEFT (1 << 0)
+#define ALC5632_DAI_I2S_DF_I2S (0 << 0)
+/* extend serial data port control (VoDAC_i2c/pcm) */
+#define ALC5632_DAI_CONTROL2 0x36
+/* 0:gpio func, 1:voice pcm */
+#define ALC5632_DAI_VOICE_PCM_ENABLE (1 << 15)
+/* 0:master, 1:slave */
+#define ALC5632_DAI_VOICE_MODE_SEL (1 << 14)
+/* 0:disable, 1:enable */
+#define ALC5632_DAI_HPF_CLK_CTRL (1 << 13)
+/* 0:main, 1:voice */
+#define ALC5632_DAI_VOICE_I2S_SYSCLK_SEL (1 << 8)
+/* 0:normal, 1:invert */
+#define ALC5632_DAI_VOICE_VBCLK_SYSCLK_SEL (1 << 7)
+/* 0:normal, 1:invert */
+#define ALC5632_DAI_VOICE_I2S_LR_INV (1 << 6)
+#define ALC5632_DAI_VOICE_DL_MASK (3 << 2)
+#define ALC5632_DAI_VOICE_DL_16 (0 << 2)
+#define ALC5632_DAI_VOICE_DL_20 (1 << 2)
+#define ALC5632_DAI_VOICE_DL_24 (2 << 2)
+#define ALC5632_DAI_VOICE_DL_8 (3 << 2)
+#define ALC5632_DAI_VOICE_DF_MASK (3 << 0)
+#define ALC5632_DAI_VOICE_DF_I2S (0 << 0)
+#define ALC5632_DAI_VOICE_DF_LEFT (1 << 0)
+#define ALC5632_DAI_VOICE_DF_PCM_A (2 << 0)
+#define ALC5632_DAI_VOICE_DF_PCM_B (3 << 0)
+
+#define ALC5632_PWR_MANAG_ADD1 0x3A
+#define ALC5632_PWR_MANAG_ADD1_MASK 0xEFFF
+#define ALC5632_PWR_ADD1_DAC_L_EN (1 << 15)
+#define ALC5632_PWR_ADD1_DAC_R_EN (1 << 14)
+#define ALC5632_PWR_ADD1_ZERO_CROSS (1 << 13)
+#define ALC5632_PWR_ADD1_MAIN_I2S_EN (1 << 11)
+#define ALC5632_PWR_ADD1_SPK_AMP_EN (1 << 10)
+#define ALC5632_PWR_ADD1_HP_OUT_AMP (1 << 9)
+#define ALC5632_PWR_ADD1_HP_OUT_ENH_AMP (1 << 8)
+#define ALC5632_PWR_ADD1_VOICE_DAC_MIX (1 << 7)
+#define ALC5632_PWR_ADD1_SOFTGEN_EN (1 << 6)
+#define ALC5632_PWR_ADD1_MIC1_SHORT_CURR (1 << 5)
+#define ALC5632_PWR_ADD1_MIC2_SHORT_CURR (1 << 4)
+#define ALC5632_PWR_ADD1_MIC1_EN (1 << 3)
+#define ALC5632_PWR_ADD1_MIC2_EN (1 << 2)
+#define ALC5632_PWR_ADD1_MAIN_BIAS (1 << 1)
+#define ALC5632_PWR_ADD1_DAC_REF (1 << 0)
+
+#define ALC5632_PWR_MANAG_ADD2 0x3C
+#define ALC5632_PWR_MANAG_ADD2_MASK 0x7FFF
+#define ALC5632_PWR_ADD2_PLL1 (1 << 15)
+#define ALC5632_PWR_ADD2_PLL2 (1 << 14)
+#define ALC5632_PWR_ADD2_VREF (1 << 13)
+#define ALC5632_PWR_ADD2_OVT_DET (1 << 12)
+#define ALC5632_PWR_ADD2_VOICE_DAC (1 << 10)
+#define ALC5632_PWR_ADD2_L_DAC_CLK (1 << 9)
+#define ALC5632_PWR_ADD2_R_DAC_CLK (1 << 8)
+#define ALC5632_PWR_ADD2_L_ADC_CLK_GAIN (1 << 7)
+#define ALC5632_PWR_ADD2_R_ADC_CLK_GAIN (1 << 6)
+#define ALC5632_PWR_ADD2_L_HP_MIXER (1 << 5)
+#define ALC5632_PWR_ADD2_R_HP_MIXER (1 << 4)
+#define ALC5632_PWR_ADD2_SPK_MIXER (1 << 3)
+#define ALC5632_PWR_ADD2_MONO_MIXER (1 << 2)
+#define ALC5632_PWR_ADD2_L_ADC_REC_MIXER (1 << 1)
+#define ALC5632_PWR_ADD2_R_ADC_REC_MIXER (1 << 0)
+
+#define ALC5632_PWR_MANAG_ADD3 0x3E
+#define ALC5632_PWR_MANAG_ADD3_MASK 0x7CFF
+#define ALC5632_PWR_ADD3_AUXOUT_VOL (1 << 14)
+#define ALC5632_PWR_ADD3_SPK_L_OUT (1 << 13)
+#define ALC5632_PWR_ADD3_SPK_R_OUT (1 << 12)
+#define ALC5632_PWR_ADD3_HP_L_OUT_VOL (1 << 11)
+#define ALC5632_PWR_ADD3_HP_R_OUT_VOL (1 << 10)
+#define ALC5632_PWR_ADD3_LINEIN_L_VOL (1 << 7)
+#define ALC5632_PWR_ADD3_LINEIN_R_VOL (1 << 6)
+#define ALC5632_PWR_ADD3_AUXIN_VOL (1 << 5)
+#define ALC5632_PWR_ADD3_AUXIN_MIX (1 << 4)
+#define ALC5632_PWR_ADD3_MIC1_VOL (1 << 3)
+#define ALC5632_PWR_ADD3_MIC2_VOL (1 << 2)
+#define ALC5632_PWR_ADD3_MIC1_BOOST_AD (1 << 1)
+#define ALC5632_PWR_ADD3_MIC2_BOOST_AD (1 << 0)
+
+#define ALC5632_GPCR1 0x40
+#define ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1 (1 << 15)
+#define ALC5632_GPCR1_CLK_SYS_SRC_SEL_MCLK (0 << 15)
+#define ALC5632_GPCR1_DAC_HI_FLT_EN (1 << 10)
+#define ALC5632_GPCR1_SPK_AMP_CTRL (7 << 1)
+#define ALC5632_GPCR1_VDD_100 (5 << 1)
+#define ALC5632_GPCR1_VDD_125 (4 << 1)
+#define ALC5632_GPCR1_VDD_150 (3 << 1)
+#define ALC5632_GPCR1_VDD_175 (2 << 1)
+#define ALC5632_GPCR1_VDD_200 (1 << 1)
+#define ALC5632_GPCR1_VDD_225 (0 << 1)
+
+#define ALC5632_GPCR2 0x42
+#define ALC5632_GPCR2_PLL1_SOUR_SEL (3 << 12)
+#define ALC5632_PLL_FR_MCLK (0 << 12)
+#define ALC5632_PLL_FR_BCLK (2 << 12)
+#define ALC5632_PLL_FR_VBCLK (3 << 12)
+#define ALC5632_GPCR2_CLK_PLL_PRE_DIV1 (0 << 0)
+
+#define ALC5632_PLL1_CTRL 0x44
+#define ALC5632_PLL1_CTRL_N_VAL(n) (((n) & 0x0f) << 8)
+#define ALC5632_PLL1_M_BYPASS (1 << 7)
+#define ALC5632_PLL1_CTRL_K_VAL(k) (((k) & 0x07) << 4)
+#define ALC5632_PLL1_CTRL_M_VAL(m) (((m) & 0x0f) << 0)
+
+#define ALC5632_PLL2_CTRL 0x46
+#define ALC5632_PLL2_EN (1 << 15)
+#define ALC5632_PLL2_RATIO (0 << 15)
+
+#define ALC5632_GPIO_PIN_CONFIG 0x4C
+#define ALC5632_GPIO_PIN_POLARITY 0x4E
+#define ALC5632_GPIO_PIN_STICKY 0x50
+#define ALC5632_GPIO_PIN_WAKEUP 0x52
+#define ALC5632_GPIO_PIN_STATUS 0x54
+#define ALC5632_GPIO_PIN_SHARING 0x56
+#define ALC5632_OVER_CURR_STATUS 0x58
+#define ALC5632_SOFTVOL_CTRL 0x5A
+#define ALC5632_GPIO_OUPUT_PIN_CTRL 0x5C
+
+#define ALC5632_MISC_CTRL 0x5E
+#define ALC5632_MISC_DISABLE_FAST_VREG (1 << 15)
+#define ALC5632_MISC_AVC_TRGT_SEL (3 << 12)
+#define ALC5632_MISC_AVC_TRGT_RIGHT (1 << 12)
+#define ALC5632_MISC_AVC_TRGT_LEFT (2 << 12)
+#define ALC5632_MISC_AVC_TRGT_BOTH (3 << 12)
+#define ALC5632_MISC_HP_DEPOP_MODE1_EN (1 << 9)
+#define ALC5632_MISC_HP_DEPOP_MODE2_EN (1 << 8)
+#define ALC5632_MISC_HP_DEPOP_MUTE_L (1 << 7)
+#define ALC5632_MISC_HP_DEPOP_MUTE_R (1 << 6)
+#define ALC5632_MISC_HP_DEPOP_MUTE (1 << 5)
+#define ALC5632_MISC_GPIO_WAKEUP_CTRL (1 << 1)
+#define ALC5632_MISC_IRQOUT_INV_CTRL (1 << 0)
+
+#define ALC5632_DAC_CLK_CTRL1 0x60
+#define ALC5632_DAC_CLK_CTRL2 0x62
+#define ALC5632_DAC_CLK_CTRL2_DIV1_2 (1 << 0)
+#define ALC5632_VOICE_DAC_PCM_CLK_CTRL1 0x64
+#define ALC5632_PSEUDO_SPATIAL_CTRL 0x68
+#define ALC5632_HID_CTRL_INDEX 0x6A
+#define ALC5632_HID_CTRL_DATA 0x6C
+#define ALC5632_EQ_CTRL 0x6E
+
+/* undocumented */
+#define ALC5632_VENDOR_ID1 0x7C
+#define ALC5632_VENDOR_ID2 0x7E
+
+#define ALC5632_MAX_REGISTER 0x7E
+
+#endif
diff --git a/kernel/sound/soc/codecs/arizona.c b/kernel/sound/soc/codecs/arizona.c
new file mode 100644
index 000000000..eff4b4d51
--- /dev/null
+++ b/kernel/sound/soc/codecs/arizona.c
@@ -0,0 +1,2145 @@
+/*
+ * 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/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/registers.h>
+
+#include "arizona.h"
+
+#define ARIZONA_AIF_BCLK_CTRL 0x00
+#define ARIZONA_AIF_TX_PIN_CTRL 0x01
+#define ARIZONA_AIF_RX_PIN_CTRL 0x02
+#define ARIZONA_AIF_RATE_CTRL 0x03
+#define ARIZONA_AIF_FORMAT 0x04
+#define ARIZONA_AIF_TX_BCLK_RATE 0x05
+#define ARIZONA_AIF_RX_BCLK_RATE 0x06
+#define ARIZONA_AIF_FRAME_CTRL_1 0x07
+#define ARIZONA_AIF_FRAME_CTRL_2 0x08
+#define ARIZONA_AIF_FRAME_CTRL_3 0x09
+#define ARIZONA_AIF_FRAME_CTRL_4 0x0A
+#define ARIZONA_AIF_FRAME_CTRL_5 0x0B
+#define ARIZONA_AIF_FRAME_CTRL_6 0x0C
+#define ARIZONA_AIF_FRAME_CTRL_7 0x0D
+#define ARIZONA_AIF_FRAME_CTRL_8 0x0E
+#define ARIZONA_AIF_FRAME_CTRL_9 0x0F
+#define ARIZONA_AIF_FRAME_CTRL_10 0x10
+#define ARIZONA_AIF_FRAME_CTRL_11 0x11
+#define ARIZONA_AIF_FRAME_CTRL_12 0x12
+#define ARIZONA_AIF_FRAME_CTRL_13 0x13
+#define ARIZONA_AIF_FRAME_CTRL_14 0x14
+#define ARIZONA_AIF_FRAME_CTRL_15 0x15
+#define ARIZONA_AIF_FRAME_CTRL_16 0x16
+#define ARIZONA_AIF_FRAME_CTRL_17 0x17
+#define ARIZONA_AIF_FRAME_CTRL_18 0x18
+#define ARIZONA_AIF_TX_ENABLES 0x19
+#define ARIZONA_AIF_RX_ENABLES 0x1A
+#define ARIZONA_AIF_FORCE_WRITE 0x1B
+
+#define ARIZONA_FLL_VCO_CORNER 141900000
+#define ARIZONA_FLL_MAX_FREF 13500000
+#define ARIZONA_FLL_MIN_FVCO 90000000
+#define ARIZONA_FLL_MAX_FRATIO 16
+#define ARIZONA_FLL_MAX_REFDIV 8
+#define ARIZONA_FLL_MIN_OUTDIV 2
+#define ARIZONA_FLL_MAX_OUTDIV 7
+
+#define ARIZONA_FMT_DSP_MODE_A 0
+#define ARIZONA_FMT_DSP_MODE_B 1
+#define ARIZONA_FMT_I2S_MODE 2
+#define ARIZONA_FMT_LEFT_JUSTIFIED_MODE 3
+
+#define arizona_fll_err(_fll, fmt, ...) \
+ dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define arizona_fll_warn(_fll, fmt, ...) \
+ dev_warn(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define arizona_fll_dbg(_fll, fmt, ...) \
+ dev_dbg(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+
+#define arizona_aif_err(_dai, fmt, ...) \
+ dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define arizona_aif_warn(_dai, fmt, ...) \
+ dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define arizona_aif_dbg(_dai, fmt, ...) \
+ dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+
+static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_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;
+ 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);
+ if (val & ARIZONA_SPK_OVERHEAT_STS) {
+ dev_crit(arizona->dev,
+ "Speaker not enabled due to temperature\n");
+ return -EBUSY;
+ }
+
+ regmap_update_bits_async(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ 1 << w->shift, 1 << w->shift);
+
+ 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);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static irqreturn_t arizona_thermal_warn(int irq, void *data)
+{
+ struct arizona *arizona = data;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_3,
+ &val);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to read thermal status: %d\n",
+ ret);
+ } else if (val & ARIZONA_SPK_OVERHEAT_WARN_STS) {
+ dev_crit(arizona->dev, "Thermal warning\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t arizona_thermal_shutdown(int irq, void *data)
+{
+ struct arizona *arizona = data;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_3,
+ &val);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to read thermal status: %d\n",
+ ret);
+ } else if (val & ARIZONA_SPK_OVERHEAT_STS) {
+ dev_crit(arizona->dev, "Thermal shutdown\n");
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT4L_ENA |
+ ARIZONA_OUT4R_ENA, 0);
+ if (ret != 0)
+ dev_crit(arizona->dev,
+ "Failed to disable speaker outputs: %d\n",
+ ret);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct snd_soc_dapm_widget arizona_spkl =
+ SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM,
+ ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_spk_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU);
+
+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);
+
+int arizona_init_spk(struct snd_soc_codec *codec)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(&codec->dapm, &arizona_spkl, 1);
+ if (ret != 0)
+ return ret;
+
+ switch (arizona->type) {
+ case WM8997:
+ break;
+ default:
+ ret = snd_soc_dapm_new_controls(&codec->dapm,
+ &arizona_spkr, 1);
+ if (ret != 0)
+ return ret;
+ break;
+ }
+
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN,
+ "Thermal warning", arizona_thermal_warn,
+ arizona);
+ if (ret != 0)
+ dev_err(arizona->dev,
+ "Failed to get thermal warning IRQ: %d\n",
+ ret);
+
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT,
+ "Thermal shutdown", arizona_thermal_shutdown,
+ arizona);
+ if (ret != 0)
+ dev_err(arizona->dev,
+ "Failed to get thermal shutdown IRQ: %d\n",
+ ret);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_spk);
+
+static const struct snd_soc_dapm_route arizona_mono_routes[] = {
+ { "OUT1R", NULL, "OUT1L" },
+ { "OUT2R", NULL, "OUT2L" },
+ { "OUT3R", NULL, "OUT3L" },
+ { "OUT4R", NULL, "OUT4L" },
+ { "OUT5R", NULL, "OUT5L" },
+ { "OUT6R", NULL, "OUT6L" },
+};
+
+int arizona_init_mono(struct snd_soc_codec *codec)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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,
+ &arizona_mono_routes[i], 1);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_mono);
+
+int arizona_init_gpio(struct snd_soc_codec *codec)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ int i;
+
+ switch (arizona->type) {
+ case WM5110:
+ case WM8280:
+ snd_soc_dapm_disable_pin(&codec->dapm, "DRC2 Signal Activity");
+ break;
+ default:
+ break;
+ }
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "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");
+ break;
+ case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
+ snd_soc_dapm_enable_pin(&codec->dapm,
+ "DRC2 Signal Activity");
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_gpio);
+
+const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
+ "None",
+ "Tone Generator 1",
+ "Tone Generator 2",
+ "Haptics",
+ "AEC",
+ "Mic Mute Mixer",
+ "Noise Generator",
+ "IN1L",
+ "IN1R",
+ "IN2L",
+ "IN2R",
+ "IN3L",
+ "IN3R",
+ "IN4L",
+ "IN4R",
+ "AIF1RX1",
+ "AIF1RX2",
+ "AIF1RX3",
+ "AIF1RX4",
+ "AIF1RX5",
+ "AIF1RX6",
+ "AIF1RX7",
+ "AIF1RX8",
+ "AIF2RX1",
+ "AIF2RX2",
+ "AIF2RX3",
+ "AIF2RX4",
+ "AIF2RX5",
+ "AIF2RX6",
+ "AIF3RX1",
+ "AIF3RX2",
+ "SLIMRX1",
+ "SLIMRX2",
+ "SLIMRX3",
+ "SLIMRX4",
+ "SLIMRX5",
+ "SLIMRX6",
+ "SLIMRX7",
+ "SLIMRX8",
+ "EQ1",
+ "EQ2",
+ "EQ3",
+ "EQ4",
+ "DRC1L",
+ "DRC1R",
+ "DRC2L",
+ "DRC2R",
+ "LHPF1",
+ "LHPF2",
+ "LHPF3",
+ "LHPF4",
+ "DSP1.1",
+ "DSP1.2",
+ "DSP1.3",
+ "DSP1.4",
+ "DSP1.5",
+ "DSP1.6",
+ "DSP2.1",
+ "DSP2.2",
+ "DSP2.3",
+ "DSP2.4",
+ "DSP2.5",
+ "DSP2.6",
+ "DSP3.1",
+ "DSP3.2",
+ "DSP3.3",
+ "DSP3.4",
+ "DSP3.5",
+ "DSP3.6",
+ "DSP4.1",
+ "DSP4.2",
+ "DSP4.3",
+ "DSP4.4",
+ "DSP4.5",
+ "DSP4.6",
+ "ASRC1L",
+ "ASRC1R",
+ "ASRC2L",
+ "ASRC2R",
+ "ISRC1INT1",
+ "ISRC1INT2",
+ "ISRC1INT3",
+ "ISRC1INT4",
+ "ISRC1DEC1",
+ "ISRC1DEC2",
+ "ISRC1DEC3",
+ "ISRC1DEC4",
+ "ISRC2INT1",
+ "ISRC2INT2",
+ "ISRC2INT3",
+ "ISRC2INT4",
+ "ISRC2DEC1",
+ "ISRC2DEC2",
+ "ISRC2DEC3",
+ "ISRC2DEC4",
+ "ISRC3INT1",
+ "ISRC3INT2",
+ "ISRC3INT3",
+ "ISRC3INT4",
+ "ISRC3DEC1",
+ "ISRC3DEC2",
+ "ISRC3DEC3",
+ "ISRC3DEC4",
+};
+EXPORT_SYMBOL_GPL(arizona_mixer_texts);
+
+int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
+ 0x00, /* None */
+ 0x04, /* Tone */
+ 0x05,
+ 0x06, /* Haptics */
+ 0x08, /* AEC */
+ 0x0c, /* Noise mixer */
+ 0x0d, /* Comfort noise */
+ 0x10, /* IN1L */
+ 0x11,
+ 0x12,
+ 0x13,
+ 0x14,
+ 0x15,
+ 0x16,
+ 0x17,
+ 0x20, /* AIF1RX1 */
+ 0x21,
+ 0x22,
+ 0x23,
+ 0x24,
+ 0x25,
+ 0x26,
+ 0x27,
+ 0x28, /* AIF2RX1 */
+ 0x29,
+ 0x2a,
+ 0x2b,
+ 0x2c,
+ 0x2d,
+ 0x30, /* AIF3RX1 */
+ 0x31,
+ 0x38, /* SLIMRX1 */
+ 0x39,
+ 0x3a,
+ 0x3b,
+ 0x3c,
+ 0x3d,
+ 0x3e,
+ 0x3f,
+ 0x50, /* EQ1 */
+ 0x51,
+ 0x52,
+ 0x53,
+ 0x58, /* DRC1L */
+ 0x59,
+ 0x5a,
+ 0x5b,
+ 0x60, /* LHPF1 */
+ 0x61,
+ 0x62,
+ 0x63,
+ 0x68, /* DSP1.1 */
+ 0x69,
+ 0x6a,
+ 0x6b,
+ 0x6c,
+ 0x6d,
+ 0x70, /* DSP2.1 */
+ 0x71,
+ 0x72,
+ 0x73,
+ 0x74,
+ 0x75,
+ 0x78, /* DSP3.1 */
+ 0x79,
+ 0x7a,
+ 0x7b,
+ 0x7c,
+ 0x7d,
+ 0x80, /* DSP4.1 */
+ 0x81,
+ 0x82,
+ 0x83,
+ 0x84,
+ 0x85,
+ 0x90, /* ASRC1L */
+ 0x91,
+ 0x92,
+ 0x93,
+ 0xa0, /* ISRC1INT1 */
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4, /* ISRC1DEC1 */
+ 0xa5,
+ 0xa6,
+ 0xa7,
+ 0xa8, /* ISRC2DEC1 */
+ 0xa9,
+ 0xaa,
+ 0xab,
+ 0xac, /* ISRC2INT1 */
+ 0xad,
+ 0xae,
+ 0xaf,
+ 0xb0, /* ISRC3DEC1 */
+ 0xb1,
+ 0xb2,
+ 0xb3,
+ 0xb4, /* ISRC3INT1 */
+ 0xb5,
+ 0xb6,
+ 0xb7,
+};
+EXPORT_SYMBOL_GPL(arizona_mixer_values);
+
+const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0);
+EXPORT_SYMBOL_GPL(arizona_mixer_tlv);
+
+const char *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] = {
+ 0, 1, 2, 8,
+};
+EXPORT_SYMBOL_GPL(arizona_rate_val);
+
+
+const struct soc_enum arizona_isrc_fsh[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_1_CTRL_1,
+ ARIZONA_ISRC1_FSH_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_2_CTRL_1,
+ ARIZONA_ISRC2_FSH_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_3_CTRL_1,
+ ARIZONA_ISRC3_FSH_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+};
+EXPORT_SYMBOL_GPL(arizona_isrc_fsh);
+
+const struct soc_enum arizona_isrc_fsl[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_1_CTRL_2,
+ ARIZONA_ISRC1_FSL_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_2_CTRL_2,
+ ARIZONA_ISRC2_FSL_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_3_CTRL_2,
+ ARIZONA_ISRC3_FSL_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+};
+EXPORT_SYMBOL_GPL(arizona_isrc_fsl);
+
+const struct soc_enum arizona_asrc_rate1 =
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ASRC_RATE1,
+ ARIZONA_ASRC_RATE1_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE - 1,
+ arizona_rate_text, arizona_rate_val);
+EXPORT_SYMBOL_GPL(arizona_asrc_rate1);
+
+static const char *arizona_vol_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
+ "15ms/6dB", "30ms/6dB",
+};
+
+SOC_ENUM_SINGLE_DECL(arizona_in_vd_ramp,
+ ARIZONA_INPUT_VOLUME_RAMP,
+ ARIZONA_IN_VD_RAMP_SHIFT,
+ arizona_vol_ramp_text);
+EXPORT_SYMBOL_GPL(arizona_in_vd_ramp);
+
+SOC_ENUM_SINGLE_DECL(arizona_in_vi_ramp,
+ ARIZONA_INPUT_VOLUME_RAMP,
+ ARIZONA_IN_VI_RAMP_SHIFT,
+ arizona_vol_ramp_text);
+EXPORT_SYMBOL_GPL(arizona_in_vi_ramp);
+
+SOC_ENUM_SINGLE_DECL(arizona_out_vd_ramp,
+ ARIZONA_OUTPUT_VOLUME_RAMP,
+ ARIZONA_OUT_VD_RAMP_SHIFT,
+ arizona_vol_ramp_text);
+EXPORT_SYMBOL_GPL(arizona_out_vd_ramp);
+
+SOC_ENUM_SINGLE_DECL(arizona_out_vi_ramp,
+ ARIZONA_OUTPUT_VOLUME_RAMP,
+ ARIZONA_OUT_VI_RAMP_SHIFT,
+ arizona_vol_ramp_text);
+EXPORT_SYMBOL_GPL(arizona_out_vi_ramp);
+
+static const char *arizona_lhpf_mode_text[] = {
+ "Low-pass", "High-pass"
+};
+
+SOC_ENUM_SINGLE_DECL(arizona_lhpf1_mode,
+ ARIZONA_HPLPF1_1,
+ ARIZONA_LHPF1_MODE_SHIFT,
+ arizona_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(arizona_lhpf1_mode);
+
+SOC_ENUM_SINGLE_DECL(arizona_lhpf2_mode,
+ ARIZONA_HPLPF2_1,
+ ARIZONA_LHPF2_MODE_SHIFT,
+ arizona_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(arizona_lhpf2_mode);
+
+SOC_ENUM_SINGLE_DECL(arizona_lhpf3_mode,
+ ARIZONA_HPLPF3_1,
+ ARIZONA_LHPF3_MODE_SHIFT,
+ arizona_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(arizona_lhpf3_mode);
+
+SOC_ENUM_SINGLE_DECL(arizona_lhpf4_mode,
+ ARIZONA_HPLPF4_1,
+ ARIZONA_LHPF4_MODE_SHIFT,
+ arizona_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
+
+static const char *arizona_ng_hold_text[] = {
+ "30ms", "120ms", "250ms", "500ms",
+};
+
+SOC_ENUM_SINGLE_DECL(arizona_ng_hold,
+ ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_HOLD_SHIFT,
+ arizona_ng_hold_text);
+EXPORT_SYMBOL_GPL(arizona_ng_hold);
+
+static const char * const arizona_in_hpf_cut_text[] = {
+ "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz"
+};
+
+SOC_ENUM_SINGLE_DECL(arizona_in_hpf_cut_enum,
+ ARIZONA_HPF_CONTROL,
+ ARIZONA_IN_HPF_CUT_SHIFT,
+ arizona_in_hpf_cut_text);
+EXPORT_SYMBOL_GPL(arizona_in_hpf_cut_enum);
+
+static const char * const arizona_in_dmic_osr_text[] = {
+ "1.536MHz", "3.072MHz", "6.144MHz", "768kHz",
+};
+
+const struct soc_enum arizona_in_dmic_osr[] = {
+ SOC_ENUM_SINGLE(ARIZONA_IN1L_CONTROL, ARIZONA_IN1_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(ARIZONA_IN2L_CONTROL, ARIZONA_IN2_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(ARIZONA_IN3L_CONTROL, ARIZONA_IN3_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(ARIZONA_IN4L_CONTROL, ARIZONA_IN4_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+};
+EXPORT_SYMBOL_GPL(arizona_in_dmic_osr);
+
+static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+ int i;
+
+ if (ena)
+ val = ARIZONA_IN_VU;
+ else
+ val = 0;
+
+ for (i = 0; i < priv->num_inputs; i++)
+ snd_soc_update_bits(codec,
+ ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 4),
+ ARIZONA_IN_VU, val);
+}
+
+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);
+ unsigned int reg;
+
+ if (w->shift % 2)
+ reg = ARIZONA_ADC_DIGITAL_VOLUME_1L + ((w->shift / 2) * 8);
+ else
+ reg = ARIZONA_ADC_DIGITAL_VOLUME_1R + ((w->shift / 2) * 8);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ priv->in_pending++;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, 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);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, 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);
+ if (reg == 0)
+ arizona_in_set_vu(codec, 0);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_in_ev);
+
+int arizona_out_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (w->shift) {
+ case ARIZONA_OUT1L_ENA_SHIFT:
+ case ARIZONA_OUT1R_ENA_SHIFT:
+ case ARIZONA_OUT2L_ENA_SHIFT:
+ case ARIZONA_OUT2R_ENA_SHIFT:
+ case ARIZONA_OUT3L_ENA_SHIFT:
+ case ARIZONA_OUT3R_ENA_SHIFT:
+ priv->out_up_pending++;
+ priv->out_up_delay += 17;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ switch (w->shift) {
+ case ARIZONA_OUT1L_ENA_SHIFT:
+ case ARIZONA_OUT1R_ENA_SHIFT:
+ case ARIZONA_OUT2L_ENA_SHIFT:
+ case ARIZONA_OUT2R_ENA_SHIFT:
+ case ARIZONA_OUT3L_ENA_SHIFT:
+ case ARIZONA_OUT3R_ENA_SHIFT:
+ priv->out_up_pending--;
+ if (!priv->out_up_pending) {
+ msleep(priv->out_up_delay);
+ priv->out_up_delay = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ switch (w->shift) {
+ case ARIZONA_OUT1L_ENA_SHIFT:
+ case ARIZONA_OUT1R_ENA_SHIFT:
+ case ARIZONA_OUT2L_ENA_SHIFT:
+ case ARIZONA_OUT2R_ENA_SHIFT:
+ case ARIZONA_OUT3L_ENA_SHIFT:
+ case ARIZONA_OUT3R_ENA_SHIFT:
+ priv->out_down_pending++;
+ priv->out_down_delay++;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ switch (w->shift) {
+ case ARIZONA_OUT1L_ENA_SHIFT:
+ case ARIZONA_OUT1R_ENA_SHIFT:
+ case ARIZONA_OUT2L_ENA_SHIFT:
+ case ARIZONA_OUT2R_ENA_SHIFT:
+ case ARIZONA_OUT3L_ENA_SHIFT:
+ case ARIZONA_OUT3R_ENA_SHIFT:
+ priv->out_down_pending--;
+ if (!priv->out_down_pending) {
+ msleep(priv->out_down_delay);
+ priv->out_down_delay = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_out_ev);
+
+int arizona_hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ unsigned int mask = 1 << w->shift;
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = mask;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = 0;
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ return arizona_out_ev(w, kcontrol, event);
+ default:
+ return -EINVAL;
+ }
+
+ /* Store the desired state for the HP outputs */
+ priv->arizona->hp_ena &= ~mask;
+ priv->arizona->hp_ena |= val;
+
+ /* Force off if HPDET clamp is active */
+ if (priv->arizona->hpdet_clamp)
+ val = 0;
+
+ regmap_update_bits_async(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1,
+ mask, val);
+
+ return arizona_out_ev(w, kcontrol, event);
+}
+EXPORT_SYMBOL_GPL(arizona_hp_ev);
+
+static unsigned int arizona_sysclk_48k_rates[] = {
+ 6144000,
+ 12288000,
+ 24576000,
+ 49152000,
+ 73728000,
+ 98304000,
+ 147456000,
+};
+
+static unsigned int arizona_sysclk_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)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg;
+ unsigned int *rates;
+ int ref, div, refclk;
+
+ switch (clk) {
+ case ARIZONA_CLK_OPCLK:
+ reg = ARIZONA_OUTPUT_SYSTEM_CLOCK;
+ refclk = priv->sysclk;
+ break;
+ case ARIZONA_CLK_ASYNC_OPCLK:
+ reg = ARIZONA_OUTPUT_ASYNC_CLOCK;
+ refclk = priv->asyncclk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (refclk % 8000)
+ rates = arizona_sysclk_44k1_rates;
+ else
+ rates = arizona_sysclk_48k_rates;
+
+ for (ref = 0; ref < ARRAY_SIZE(arizona_sysclk_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",
+ freq);
+ snd_soc_update_bits(codec, reg,
+ ARIZONA_OPCLK_DIV_MASK |
+ ARIZONA_OPCLK_SEL_MASK,
+ (div <<
+ ARIZONA_OPCLK_DIV_SHIFT) |
+ ref);
+ return 0;
+ }
+ div++;
+ }
+ }
+
+ dev_err(codec->dev, "Unable to generate %dHz OPCLK\n", freq);
+ return -EINVAL;
+}
+
+int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
+
+ switch (clk_id) {
+ case ARIZONA_CLK_SYSCLK:
+ name = "SYSCLK";
+ reg = ARIZONA_SYSTEM_CLOCK_1;
+ clk = &priv->sysclk;
+ mask |= ARIZONA_SYSCLK_FRAC;
+ break;
+ case ARIZONA_CLK_ASYNCCLK:
+ name = "ASYNCCLK";
+ reg = ARIZONA_ASYNC_CLOCK_1;
+ clk = &priv->asyncclk;
+ break;
+ case ARIZONA_CLK_OPCLK:
+ case ARIZONA_CLK_ASYNC_OPCLK:
+ return arizona_set_opclk(codec, clk_id, freq);
+ default:
+ return -EINVAL;
+ }
+
+ switch (freq) {
+ case 5644800:
+ case 6144000:
+ break;
+ case 11289600:
+ case 12288000:
+ val |= ARIZONA_CLK_12MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 22579200:
+ case 24576000:
+ val |= ARIZONA_CLK_24MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 45158400:
+ case 49152000:
+ val |= ARIZONA_CLK_49MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 67737600:
+ case 73728000:
+ val |= ARIZONA_CLK_73MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 90316800:
+ case 98304000:
+ val |= ARIZONA_CLK_98MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 135475200:
+ case 147456000:
+ val |= ARIZONA_CLK_147MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 0:
+ dev_dbg(arizona->dev, "%s cleared\n", name);
+ *clk = freq;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ *clk = freq;
+
+ if (freq % 6144000)
+ val |= ARIZONA_SYSCLK_FRAC;
+
+ dev_dbg(arizona->dev, "%s set to %uHz", name, freq);
+
+ return regmap_update_bits(arizona->regmap, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(arizona_set_sysclk);
+
+static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ int lrclk, bclk, mode, base;
+
+ base = dai->driver->base;
+
+ lrclk = 0;
+ bclk = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ mode = ARIZONA_FMT_DSP_MODE_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
+ != SND_SOC_DAIFMT_CBM_CFM) {
+ arizona_aif_err(dai, "DSP_B not valid in slave mode\n");
+ return -EINVAL;
+ }
+ mode = ARIZONA_FMT_DSP_MODE_B;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ mode = ARIZONA_FMT_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
+ != SND_SOC_DAIFMT_CBM_CFM) {
+ arizona_aif_err(dai, "LEFT_J not valid in slave mode\n");
+ return -EINVAL;
+ }
+ mode = ARIZONA_FMT_LEFT_JUSTIFIED_MODE;
+ break;
+ default:
+ arizona_aif_err(dai, "Unsupported DAI format %d\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ bclk |= ARIZONA_AIF1_BCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ bclk |= ARIZONA_AIF1_BCLK_MSTR;
+ lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
+ break;
+ default:
+ arizona_aif_err(dai, "Unsupported master mode %d\n",
+ fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk |= ARIZONA_AIF1_BCLK_INV;
+ lrclk |= ARIZONA_AIF1TX_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk |= ARIZONA_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk |= ARIZONA_AIF1TX_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits_async(arizona->regmap, base + ARIZONA_AIF_BCLK_CTRL,
+ ARIZONA_AIF1_BCLK_INV |
+ ARIZONA_AIF1_BCLK_MSTR,
+ bclk);
+ regmap_update_bits_async(arizona->regmap, base + ARIZONA_AIF_TX_PIN_CTRL,
+ ARIZONA_AIF1TX_LRCLK_INV |
+ ARIZONA_AIF1TX_LRCLK_MSTR, lrclk);
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_RX_PIN_CTRL,
+ ARIZONA_AIF1RX_LRCLK_INV |
+ ARIZONA_AIF1RX_LRCLK_MSTR, lrclk);
+ regmap_update_bits(arizona->regmap, base + ARIZONA_AIF_FORMAT,
+ ARIZONA_AIF1_FMT_MASK, mode);
+
+ return 0;
+}
+
+static const int arizona_48k_bclk_rates[] = {
+ -1,
+ 48000,
+ 64000,
+ 96000,
+ 128000,
+ 192000,
+ 256000,
+ 384000,
+ 512000,
+ 768000,
+ 1024000,
+ 1536000,
+ 2048000,
+ 3072000,
+ 4096000,
+ 6144000,
+ 8192000,
+ 12288000,
+ 24576000,
+};
+
+static const 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,
+ 58800,
+ 88200,
+ 117600,
+ 177640,
+ 235200,
+ 352800,
+ 470400,
+ 705600,
+ 940800,
+ 1411200,
+ 1881600,
+ 2822400,
+ 3763200,
+ 5644800,
+ 7526400,
+ 11289600,
+ 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[] = {
+ 0,
+ 12000,
+ 24000,
+ 48000,
+ 96000,
+ 192000,
+ 384000,
+ 768000,
+ 0,
+ 11025,
+ 22050,
+ 44100,
+ 88200,
+ 176400,
+ 352800,
+ 705600,
+ 4000,
+ 8000,
+ 16000,
+ 32000,
+ 64000,
+ 128000,
+ 256000,
+ 512000,
+};
+
+static int 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 arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ const struct snd_pcm_hw_constraint_list *constraint;
+ unsigned int base_rate;
+
+ switch (dai_priv->clk) {
+ case ARIZONA_CLK_SYSCLK:
+ base_rate = priv->sysclk;
+ break;
+ case ARIZONA_CLK_ASYNCCLK:
+ base_rate = priv->asyncclk;
+ break;
+ default:
+ return 0;
+ }
+
+ if (base_rate == 0)
+ return 0;
+
+ if (base_rate % 8000)
+ constraint = &arizona_44k1_constraint;
+ else
+ constraint = &arizona_48k_constraint;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ constraint);
+}
+
+static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
+ unsigned int rate)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ struct reg_default dac_comp[] = {
+ { 0x80, 0x3 },
+ { ARIZONA_DAC_COMP_1, 0 },
+ { ARIZONA_DAC_COMP_2, 0 },
+ { 0x80, 0x0 },
+ };
+
+ mutex_lock(&arizona->dac_comp_lock);
+
+ dac_comp[1].def = arizona->dac_comp_coeff;
+ if (rate >= 176400)
+ dac_comp[2].def = arizona->dac_comp_enabled;
+
+ mutex_unlock(&arizona->dac_comp_lock);
+
+ regmap_multi_reg_write(arizona->regmap,
+ dac_comp,
+ ARRAY_SIZE(dac_comp));
+}
+
+static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ int base = dai->driver->base;
+ int i, sr_val;
+
+ /*
+ * We will need to be more flexible than this in future,
+ * currently we use a single sample rate for SYSCLK.
+ */
+ for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++)
+ if (arizona_sr_vals[i] == params_rate(params))
+ break;
+ if (i == ARRAY_SIZE(arizona_sr_vals)) {
+ arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ sr_val = i;
+
+ switch (dai_priv->clk) {
+ case ARIZONA_CLK_SYSCLK:
+ switch (priv->arizona->type) {
+ case WM5102:
+ arizona_wm5102_set_dac_comp(codec,
+ params_rate(params));
+ break;
+ default:
+ break;
+ }
+
+ snd_soc_update_bits(codec, 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);
+ break;
+ case ARIZONA_CLK_ASYNCCLK:
+ snd_soc_update_bits(codec, 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);
+ break;
+ default:
+ arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool arizona_aif_cfg_changed(struct snd_soc_codec *codec,
+ int base, int bclk, int lrclk, int frame)
+{
+ int val;
+
+ val = snd_soc_read(codec, 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))
+ return true;
+
+ val = snd_soc_read(codec, base + ARIZONA_AIF_FRAME_CTRL_1);
+ if (frame != (val & (ARIZONA_AIF1TX_WL_MASK |
+ ARIZONA_AIF1TX_SLOT_LEN_MASK)))
+ return true;
+
+ return false;
+}
+
+static int arizona_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ int base = dai->driver->base;
+ const int *rates;
+ int i, ret, val;
+ int channels = params_channels(params);
+ int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1];
+ int tdm_width = arizona->tdm_width[dai->id - 1];
+ int tdm_slots = arizona->tdm_slots[dai->id - 1];
+ int bclk, lrclk, wl, frame, bclk_target;
+ bool reconfig;
+ unsigned int aif_tx_state, aif_rx_state;
+
+ if (params_rate(params) % 8000)
+ rates = &arizona_44k1_bclk_rates[0];
+ else
+ rates = &arizona_48k_bclk_rates[0];
+
+ wl = snd_pcm_format_width(params_format(params));
+
+ if (tdm_slots) {
+ arizona_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n",
+ tdm_slots, tdm_width);
+ bclk_target = tdm_slots * tdm_width * params_rate(params);
+ channels = tdm_slots;
+ } else {
+ bclk_target = snd_soc_params_to_bclk(params);
+ tdm_width = wl;
+ }
+
+ if (chan_limit && chan_limit < channels) {
+ arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit);
+ bclk_target /= channels;
+ bclk_target *= chan_limit;
+ }
+
+ /* Force multiple of 2 channels for I2S mode */
+ val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT);
+ val &= ARIZONA_AIF1_FMT_MASK;
+ if ((channels & 1) && (val == ARIZONA_FMT_I2S_MODE)) {
+ arizona_aif_dbg(dai, "Forcing stereo mode\n");
+ bclk_target /= channels;
+ bclk_target *= channels + 1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) {
+ if (rates[i] >= bclk_target &&
+ rates[i] % params_rate(params) == 0) {
+ bclk = i;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(arizona_44k1_bclk_rates)) {
+ arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ lrclk = rates[bclk] / params_rate(params);
+
+ arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n",
+ rates[bclk], rates[bclk] / lrclk);
+
+ frame = wl << ARIZONA_AIF1TX_WL_SHIFT | tdm_width;
+
+ reconfig = arizona_aif_cfg_changed(codec, base, bclk, lrclk, frame);
+
+ if (reconfig) {
+ /* Save AIF TX/RX state */
+ aif_tx_state = snd_soc_read(codec,
+ base + ARIZONA_AIF_TX_ENABLES);
+ aif_rx_state = snd_soc_read(codec,
+ base + ARIZONA_AIF_RX_ENABLES);
+ /* Disable AIF TX/RX before reconfiguring it */
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_TX_ENABLES, 0xff, 0x0);
+ regmap_update_bits(arizona->regmap,
+ base + ARIZONA_AIF_RX_ENABLES, 0xff, 0x0);
+ }
+
+ ret = arizona_hw_params_rate(substream, params, dai);
+ if (ret != 0)
+ goto restore_aif;
+
+ if (reconfig) {
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_BCLK_CTRL,
+ ARIZONA_AIF1_BCLK_FREQ_MASK, bclk);
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_TX_BCLK_RATE,
+ ARIZONA_AIF1TX_BCPF_MASK, lrclk);
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_RX_BCLK_RATE,
+ ARIZONA_AIF1RX_BCPF_MASK, lrclk);
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_FRAME_CTRL_1,
+ ARIZONA_AIF1TX_WL_MASK |
+ ARIZONA_AIF1TX_SLOT_LEN_MASK, frame);
+ regmap_update_bits(arizona->regmap,
+ base + ARIZONA_AIF_FRAME_CTRL_2,
+ ARIZONA_AIF1RX_WL_MASK |
+ ARIZONA_AIF1RX_SLOT_LEN_MASK, frame);
+ }
+
+restore_aif:
+ if (reconfig) {
+ /* Restore AIF TX/RX state */
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_TX_ENABLES,
+ 0xff, aif_tx_state);
+ regmap_update_bits(arizona->regmap,
+ base + ARIZONA_AIF_RX_ENABLES,
+ 0xff, aif_rx_state);
+ }
+ return ret;
+}
+
+static const char *arizona_dai_clk_str(int clk_id)
+{
+ switch (clk_id) {
+ case ARIZONA_CLK_SYSCLK:
+ return "SYSCLK";
+ case ARIZONA_CLK_ASYNCCLK:
+ return "ASYNCCLK";
+ default:
+ return "Unknown clock";
+ }
+}
+
+static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ struct snd_soc_dapm_route routes[2];
+
+ switch (clk_id) {
+ case ARIZONA_CLK_SYSCLK:
+ case ARIZONA_CLK_ASYNCCLK:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (clk_id == dai_priv->clk)
+ return 0;
+
+ if (dai->active) {
+ dev_err(codec->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,
+ arizona_dai_clk_str(clk_id));
+
+ memset(&routes, 0, sizeof(routes));
+ routes[0].sink = dai->driver->capture.stream_name;
+ routes[1].sink = dai->driver->playback.stream_name;
+
+ routes[0].source = arizona_dai_clk_str(dai_priv->clk);
+ routes[1].source = arizona_dai_clk_str(dai_priv->clk);
+ snd_soc_dapm_del_routes(&codec->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));
+
+ dai_priv->clk = clk_id;
+
+ return snd_soc_dapm_sync(&codec->dapm);
+}
+
+static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int base = dai->driver->base;
+ unsigned int reg;
+
+ if (tristate)
+ reg = ARIZONA_AIF1_TRI;
+ else
+ reg = 0;
+
+ return snd_soc_update_bits(codec, 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 arizona *arizona = priv->arizona;
+ int slot, i;
+
+ for (i = 0; i < channels; ++i) {
+ slot = ffs(mask) - 1;
+ if (slot < 0)
+ return;
+
+ regmap_write(arizona->regmap, base + i, slot);
+
+ mask &= ~(1 << slot);
+ }
+
+ if (mask)
+ arizona_aif_warn(dai, "Too many channels in TDM mask\n");
+}
+
+static int arizona_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ int base = dai->driver->base;
+ int rx_max_chan = dai->driver->playback.channels_max;
+ int tx_max_chan = dai->driver->capture.channels_max;
+
+ /* Only support TDM for the physical AIFs */
+ if (dai->id > ARIZONA_MAX_AIF)
+ return -ENOTSUPP;
+
+ if (slots == 0) {
+ tx_mask = (1 << tx_max_chan) - 1;
+ rx_mask = (1 << rx_max_chan) - 1;
+ }
+
+ arizona_set_channels_to_mask(dai, base + ARIZONA_AIF_FRAME_CTRL_3,
+ tx_max_chan, tx_mask);
+ arizona_set_channels_to_mask(dai, base + ARIZONA_AIF_FRAME_CTRL_11,
+ rx_max_chan, rx_mask);
+
+ arizona->tdm_width[dai->id - 1] = slot_width;
+ arizona->tdm_slots[dai->id - 1] = slots;
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops arizona_dai_ops = {
+ .startup = arizona_startup,
+ .set_fmt = arizona_set_fmt,
+ .set_tdm_slot = arizona_set_tdm_slot,
+ .hw_params = arizona_hw_params,
+ .set_sysclk = arizona_dai_set_sysclk,
+ .set_tristate = arizona_set_tristate,
+};
+EXPORT_SYMBOL_GPL(arizona_dai_ops);
+
+const struct snd_soc_dai_ops arizona_simple_dai_ops = {
+ .startup = arizona_startup,
+ .hw_params = arizona_hw_params_rate,
+ .set_sysclk = arizona_dai_set_sysclk,
+};
+EXPORT_SYMBOL_GPL(arizona_simple_dai_ops);
+
+int arizona_init_dai(struct arizona_priv *priv, int id)
+{
+ struct arizona_dai_priv *dai_priv = &priv->dai[id];
+
+ dai_priv->clk = ARIZONA_CLK_SYSCLK;
+
+ 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;
+ u16 fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 gain;
+} fll_gains[] = {
+ { 0, 256000, 0 },
+ { 256000, 1000000, 2 },
+ { 1000000, 13500000, 4 },
+};
+
+struct arizona_fll_cfg {
+ int n;
+ int theta;
+ int lambda;
+ int refdiv;
+ int outdiv;
+ int fratio;
+ int gain;
+};
+
+static int arizona_validate_fll(struct arizona_fll *fll,
+ unsigned int Fref,
+ unsigned int Fout)
+{
+ unsigned int Fvco_min;
+
+ if (fll->fout && Fout != fll->fout) {
+ arizona_fll_err(fll,
+ "Can't change output on active FLL\n");
+ return -EINVAL;
+ }
+
+ if (Fref / ARIZONA_FLL_MAX_REFDIV > ARIZONA_FLL_MAX_FREF) {
+ arizona_fll_err(fll,
+ "Can't scale %dMHz in to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+
+ Fvco_min = ARIZONA_FLL_MIN_FVCO * fll->vco_mult;
+ if (Fout * ARIZONA_FLL_MAX_OUTDIV < Fvco_min) {
+ arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int arizona_find_fratio(unsigned int Fref, int *fratio)
+{
+ int i;
+
+ /* Find an appropriate FLL_FRATIO */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ if (fratio)
+ *fratio = fll_fratios[i].fratio;
+ return fll_fratios[i].ratio;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int arizona_calc_fratio(struct arizona_fll *fll,
+ struct arizona_fll_cfg *cfg,
+ unsigned int target,
+ unsigned int Fref, bool sync)
+{
+ int init_ratio, ratio;
+ int refdiv, div;
+
+ /* Fref must be <=13.5MHz, find initial refdiv */
+ div = 1;
+ cfg->refdiv = 0;
+ while (Fref > ARIZONA_FLL_MAX_FREF) {
+ div *= 2;
+ Fref /= 2;
+ cfg->refdiv++;
+
+ if (div > ARIZONA_FLL_MAX_REFDIV)
+ return -EINVAL;
+ }
+
+ /* Find an appropriate FLL_FRATIO */
+ init_ratio = arizona_find_fratio(Fref, &cfg->fratio);
+ if (init_ratio < 0) {
+ arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n",
+ Fref);
+ return init_ratio;
+ }
+
+ switch (fll->arizona->type) {
+ case WM5110:
+ case WM8280:
+ if (fll->arizona->rev < 3 || sync)
+ return init_ratio;
+ break;
+ default:
+ return init_ratio;
+ }
+
+ cfg->fratio = init_ratio - 1;
+
+ /* 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;
+
+ if (target % (ratio * Fref)) {
+ cfg->refdiv = refdiv;
+ cfg->fratio = ratio - 1;
+ return ratio;
+ }
+ }
+
+ for (ratio = init_ratio - 1; ratio > 0; ratio--) {
+ if (target % (ratio * Fref)) {
+ cfg->refdiv = refdiv;
+ cfg->fratio = ratio - 1;
+ return ratio;
+ }
+ }
+
+ div *= 2;
+ Fref /= 2;
+ refdiv++;
+ init_ratio = arizona_find_fratio(Fref, NULL);
+ }
+
+ arizona_fll_warn(fll, "Falling back to integer mode operation\n");
+ return cfg->fratio + 1;
+}
+
+static int arizona_calc_fll(struct arizona_fll *fll,
+ struct arizona_fll_cfg *cfg,
+ unsigned int Fref, bool sync)
+{
+ unsigned int target, div, gcd_fll;
+ int i, ratio;
+
+ arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, fll->fout);
+
+ /* Fvco should be over the targt; don't check the upper bound */
+ div = ARIZONA_FLL_MIN_OUTDIV;
+ while (fll->fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) {
+ div++;
+ if (div > ARIZONA_FLL_MAX_OUTDIV)
+ return -EINVAL;
+ }
+ target = fll->fout * div / fll->vco_mult;
+ cfg->outdiv = div;
+
+ arizona_fll_dbg(fll, "Fvco=%dHz\n", target);
+
+ /* Find an appropriate FLL_FRATIO and refdiv */
+ ratio = arizona_calc_fratio(fll, cfg, target, Fref, sync);
+ if (ratio < 0)
+ return ratio;
+
+ /* Apply the division for our remaining calculations */
+ Fref = Fref / (1 << cfg->refdiv);
+
+ cfg->n = target / (ratio * Fref);
+
+ if (target % (ratio * Fref)) {
+ gcd_fll = gcd(target, ratio * Fref);
+ arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll);
+
+ cfg->theta = (target - (cfg->n * ratio * Fref))
+ / gcd_fll;
+ cfg->lambda = (ratio * Fref) / gcd_fll;
+ } else {
+ cfg->theta = 0;
+ cfg->lambda = 0;
+ }
+
+ /* Round down to 16bit range with cost of accuracy lost.
+ * Denominator must be bigger than numerator so we only
+ * take care of it.
+ */
+ while (cfg->lambda >= (1 << 16)) {
+ cfg->theta >>= 1;
+ cfg->lambda >>= 1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(fll_gains); i++) {
+ if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) {
+ cfg->gain = fll_gains[i].gain;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_gains)) {
+ arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n",
+ Fref);
+ return -EINVAL;
+ }
+
+ arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\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);
+
+ return 0;
+
+}
+
+static void arizona_apply_fll(struct arizona *arizona, unsigned int base,
+ struct arizona_fll_cfg *cfg, int source,
+ bool sync)
+{
+ regmap_update_bits_async(arizona->regmap, base + 3,
+ ARIZONA_FLL1_THETA_MASK, cfg->theta);
+ regmap_update_bits_async(arizona->regmap, base + 4,
+ ARIZONA_FLL1_LAMBDA_MASK, cfg->lambda);
+ regmap_update_bits_async(arizona->regmap, base + 5,
+ ARIZONA_FLL1_FRATIO_MASK,
+ cfg->fratio << ARIZONA_FLL1_FRATIO_SHIFT);
+ regmap_update_bits_async(arizona->regmap, base + 6,
+ ARIZONA_FLL1_CLK_REF_DIV_MASK |
+ ARIZONA_FLL1_CLK_REF_SRC_MASK,
+ cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT |
+ source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT);
+
+ if (sync) {
+ regmap_update_bits(arizona->regmap, base + 0x7,
+ ARIZONA_FLL1_GAIN_MASK,
+ cfg->gain << ARIZONA_FLL1_GAIN_SHIFT);
+ } else {
+ regmap_update_bits(arizona->regmap, base + 0x5,
+ ARIZONA_FLL1_OUTDIV_MASK,
+ cfg->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);
+ regmap_update_bits(arizona->regmap, base + 0x9,
+ ARIZONA_FLL1_GAIN_MASK,
+ cfg->gain << ARIZONA_FLL1_GAIN_SHIFT);
+ }
+
+ regmap_update_bits_async(arizona->regmap, base + 2,
+ ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK,
+ ARIZONA_FLL1_CTRL_UPD | cfg->n);
+}
+
+static int arizona_is_enabled_fll(struct arizona_fll *fll)
+{
+ struct arizona *arizona = fll->arizona;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, fll->base + 1, &reg);
+ if (ret != 0) {
+ arizona_fll_err(fll, "Failed to read current state: %d\n",
+ ret);
+ return ret;
+ }
+
+ return reg & ARIZONA_FLL1_ENA;
+}
+
+static int arizona_enable_fll(struct arizona_fll *fll)
+{
+ struct arizona *arizona = fll->arizona;
+ unsigned long time_left;
+ bool use_sync = false;
+ int already_enabled = arizona_is_enabled_fll(fll);
+ struct arizona_fll_cfg cfg;
+
+ if (already_enabled < 0)
+ return already_enabled;
+
+ if (already_enabled) {
+ /* Facilitate smooth refclk across the transition */
+ regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x7,
+ ARIZONA_FLL1_GAIN_MASK, 0);
+ regmap_update_bits_async(fll->arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN,
+ ARIZONA_FLL1_FREERUN);
+ }
+
+ /*
+ * If we have both REFCLK and SYNCCLK then enable both,
+ * otherwise apply the SYNCCLK settings to REFCLK.
+ */
+ if (fll->ref_src >= 0 && fll->ref_freq &&
+ fll->ref_src != fll->sync_src) {
+ arizona_calc_fll(fll, &cfg, fll->ref_freq, false);
+
+ arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src,
+ false);
+ if (fll->sync_src >= 0) {
+ arizona_calc_fll(fll, &cfg, fll->sync_freq, true);
+
+ arizona_apply_fll(arizona, fll->base + 0x10, &cfg,
+ fll->sync_src, true);
+ use_sync = true;
+ }
+ } else if (fll->sync_src >= 0) {
+ arizona_calc_fll(fll, &cfg, fll->sync_freq, false);
+
+ arizona_apply_fll(arizona, fll->base, &cfg,
+ fll->sync_src, false);
+
+ regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
+ ARIZONA_FLL1_SYNC_ENA, 0);
+ } else {
+ arizona_fll_err(fll, "No clocks provided\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Increase the bandwidth if we're not using a low frequency
+ * sync source.
+ */
+ if (use_sync && fll->sync_freq > 100000)
+ regmap_update_bits_async(arizona->regmap, fll->base + 0x17,
+ ARIZONA_FLL1_SYNC_BW, 0);
+ else
+ regmap_update_bits_async(arizona->regmap, fll->base + 0x17,
+ ARIZONA_FLL1_SYNC_BW,
+ ARIZONA_FLL1_SYNC_BW);
+
+ if (!already_enabled)
+ pm_runtime_get(arizona->dev);
+
+ /* Clear any pending completions */
+ try_wait_for_completion(&fll->ok);
+
+ regmap_update_bits_async(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
+ if (use_sync)
+ regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
+ ARIZONA_FLL1_SYNC_ENA,
+ ARIZONA_FLL1_SYNC_ENA);
+
+ if (already_enabled)
+ regmap_update_bits_async(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN, 0);
+
+ time_left = wait_for_completion_timeout(&fll->ok,
+ msecs_to_jiffies(250));
+ if (time_left == 0)
+ arizona_fll_warn(fll, "Timed out waiting for lock\n");
+
+ return 0;
+}
+
+static void arizona_disable_fll(struct arizona_fll *fll)
+{
+ struct arizona *arizona = fll->arizona;
+ bool 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);
+ regmap_update_bits_async(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN, 0);
+
+ if (change)
+ pm_runtime_put_autosuspend(arizona->dev);
+}
+
+int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ int ret = 0;
+
+ if (fll->ref_src == source && fll->ref_freq == Fref)
+ return 0;
+
+ if (fll->fout && Fref > 0) {
+ ret = arizona_validate_fll(fll, Fref, fll->fout);
+ if (ret != 0)
+ return ret;
+ }
+
+ fll->ref_src = source;
+ fll->ref_freq = Fref;
+
+ if (fll->fout && Fref > 0) {
+ ret = arizona_enable_fll(fll);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_set_fll_refclk);
+
+int arizona_set_fll(struct arizona_fll *fll, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ int ret = 0;
+
+ if (fll->sync_src == source &&
+ fll->sync_freq == Fref && fll->fout == Fout)
+ return 0;
+
+ if (Fout) {
+ if (fll->ref_src >= 0) {
+ ret = arizona_validate_fll(fll, fll->ref_freq, Fout);
+ if (ret != 0)
+ return ret;
+ }
+
+ ret = arizona_validate_fll(fll, Fref, Fout);
+ if (ret != 0)
+ return ret;
+ }
+
+ fll->sync_src = source;
+ fll->sync_freq = Fref;
+ fll->fout = Fout;
+
+ if (Fout)
+ ret = arizona_enable_fll(fll);
+ else
+ arizona_disable_fll(fll);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_set_fll);
+
+int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
+ int ok_irq, struct arizona_fll *fll)
+{
+ int ret;
+ unsigned int val;
+
+ init_completion(&fll->ok);
+
+ fll->id = id;
+ fll->base = base;
+ fll->arizona = arizona;
+ fll->sync_src = ARIZONA_FLL_SRC_NONE;
+
+ /* Configure default refclk to 32kHz if we have one */
+ regmap_read(arizona->regmap, ARIZONA_CLOCK_32K_1, &val);
+ switch (val & ARIZONA_CLK_32K_SRC_MASK) {
+ case ARIZONA_CLK_SRC_MCLK1:
+ case ARIZONA_CLK_SRC_MCLK2:
+ fll->ref_src = val & ARIZONA_CLK_32K_SRC_MASK;
+ break;
+ default:
+ fll->ref_src = ARIZONA_FLL_SRC_NONE;
+ }
+ fll->ref_freq = 32768;
+
+ snprintf(fll->lock_name, sizeof(fll->lock_name), "FLL%d lock", id);
+ snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name),
+ "FLL%d clock OK", id);
+
+ 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);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_fll);
+
+/**
+ * arizona_set_output_mode - Set the mode of the specified output
+ *
+ * @codec: Device to configure
+ * @output: Output number
+ * @diff: True to set the output to differential mode
+ *
+ * Some systems use external analogue switches to connect more
+ * analogue devices to the CODEC than are supported by the device. In
+ * some systems this requires changing the switched output from single
+ * ended to differential mode dynamically at runtime, an operation
+ * supported using this function.
+ *
+ * Most systems have a single static configuration and should use
+ * platform data instead.
+ */
+int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff)
+{
+ unsigned int reg, val;
+
+ if (output < 1 || output > 6)
+ return -EINVAL;
+
+ reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8;
+
+ if (diff)
+ val = ARIZONA_OUT1_MONO;
+ else
+ val = 0;
+
+ return snd_soc_update_bits(codec, reg, ARIZONA_OUT1_MONO, val);
+}
+EXPORT_SYMBOL_GPL(arizona_set_output_mode);
+
+MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/arizona.h b/kernel/sound/soc/codecs/arizona.h
new file mode 100644
index 000000000..11ff899b0
--- /dev/null
+++ b/kernel/sound/soc/codecs/arizona.h
@@ -0,0 +1,264 @@
+/*
+ * 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 <sound/soc.h>
+
+#include "wm_adsp.h"
+
+#define ARIZONA_CLK_SYSCLK 1
+#define ARIZONA_CLK_ASYNCCLK 2
+#define ARIZONA_CLK_OPCLK 3
+#define ARIZONA_CLK_ASYNC_OPCLK 4
+
+#define ARIZONA_CLK_SRC_MCLK1 0x0
+#define ARIZONA_CLK_SRC_MCLK2 0x1
+#define ARIZONA_CLK_SRC_FLL1 0x4
+#define ARIZONA_CLK_SRC_FLL2 0x5
+#define ARIZONA_CLK_SRC_AIF1BCLK 0x8
+#define ARIZONA_CLK_SRC_AIF2BCLK 0x9
+#define ARIZONA_CLK_SRC_AIF3BCLK 0xa
+
+#define ARIZONA_FLL_SRC_NONE -1
+#define ARIZONA_FLL_SRC_MCLK1 0
+#define ARIZONA_FLL_SRC_MCLK2 1
+#define ARIZONA_FLL_SRC_SLIMCLK 3
+#define ARIZONA_FLL_SRC_FLL1 4
+#define ARIZONA_FLL_SRC_FLL2 5
+#define ARIZONA_FLL_SRC_AIF1BCLK 8
+#define ARIZONA_FLL_SRC_AIF2BCLK 9
+#define ARIZONA_FLL_SRC_AIF3BCLK 10
+#define ARIZONA_FLL_SRC_AIF1LRCLK 12
+#define ARIZONA_FLL_SRC_AIF2LRCLK 13
+#define ARIZONA_FLL_SRC_AIF3LRCLK 14
+
+#define ARIZONA_MIXER_VOL_MASK 0x00FE
+#define ARIZONA_MIXER_VOL_SHIFT 1
+#define ARIZONA_MIXER_VOL_WIDTH 7
+
+#define ARIZONA_CLK_6MHZ 0
+#define ARIZONA_CLK_12MHZ 1
+#define ARIZONA_CLK_24MHZ 2
+#define ARIZONA_CLK_49MHZ 3
+#define ARIZONA_CLK_73MHZ 4
+#define ARIZONA_CLK_98MHZ 5
+#define ARIZONA_CLK_147MHZ 6
+
+#define ARIZONA_MAX_DAI 6
+#define ARIZONA_MAX_ADSP 4
+
+struct arizona;
+struct wm_adsp;
+
+struct arizona_dai_priv {
+ int clk;
+};
+
+struct arizona_priv {
+ struct wm_adsp adsp[ARIZONA_MAX_ADSP];
+ struct arizona *arizona;
+ int sysclk;
+ int asyncclk;
+ struct arizona_dai_priv dai[ARIZONA_MAX_DAI];
+
+ int num_inputs;
+ unsigned int in_pending;
+
+ unsigned int out_up_pending;
+ unsigned int out_up_delay;
+ unsigned int out_down_pending;
+ unsigned int out_down_delay;
+
+ unsigned int spk_ena:2;
+ unsigned int spk_ena_pending:1;
+};
+
+#define ARIZONA_NUM_MIXER_INPUTS 103
+
+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];
+
+#define ARIZONA_MIXER_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \
+ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ arizona_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 3, \
+ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ arizona_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 5, \
+ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ arizona_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 7, \
+ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ arizona_mixer_tlv)
+
+#define ARIZONA_MUX_ENUM_DECL(name, reg) \
+ SOC_VALUE_ENUM_SINGLE_DECL(name, reg, 0, 0xff, \
+ arizona_mixer_texts, arizona_mixer_values)
+
+#define ARIZONA_MUX_CTL_DECL(name) \
+ const struct snd_kcontrol_new name##_mux = \
+ SOC_DAPM_ENUM("Route", name##_enum)
+
+#define ARIZONA_MUX_ENUMS(name, base_reg) \
+ static ARIZONA_MUX_ENUM_DECL(name##_enum, base_reg); \
+ static ARIZONA_MUX_CTL_DECL(name)
+
+#define ARIZONA_MIXER_ENUMS(name, base_reg) \
+ ARIZONA_MUX_ENUMS(name##_in1, base_reg); \
+ ARIZONA_MUX_ENUMS(name##_in2, base_reg + 2); \
+ ARIZONA_MUX_ENUMS(name##_in3, base_reg + 4); \
+ ARIZONA_MUX_ENUMS(name##_in4, base_reg + 6)
+
+#define ARIZONA_DSP_AUX_ENUMS(name, base_reg) \
+ ARIZONA_MUX_ENUMS(name##_aux1, base_reg); \
+ ARIZONA_MUX_ENUMS(name##_aux2, base_reg + 8); \
+ ARIZONA_MUX_ENUMS(name##_aux3, base_reg + 16); \
+ ARIZONA_MUX_ENUMS(name##_aux4, base_reg + 24); \
+ ARIZONA_MUX_ENUMS(name##_aux5, base_reg + 32); \
+ ARIZONA_MUX_ENUMS(name##_aux6, base_reg + 40)
+
+#define ARIZONA_MUX(name, ctrl) \
+ SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+#define ARIZONA_MUX_WIDGETS(name, name_str) \
+ ARIZONA_MUX(name_str " Input", &name##_mux)
+
+#define ARIZONA_MIXER_WIDGETS(name, name_str) \
+ ARIZONA_MUX(name_str " Input 1", &name##_in1_mux), \
+ ARIZONA_MUX(name_str " Input 2", &name##_in2_mux), \
+ ARIZONA_MUX(name_str " Input 3", &name##_in3_mux), \
+ ARIZONA_MUX(name_str " Input 4", &name##_in4_mux), \
+ SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define ARIZONA_DSP_WIDGETS(name, name_str) \
+ ARIZONA_MIXER_WIDGETS(name##L, name_str "L"), \
+ ARIZONA_MIXER_WIDGETS(name##R, name_str "R"), \
+ ARIZONA_MUX(name_str " Aux 1", &name##_aux1_mux), \
+ ARIZONA_MUX(name_str " Aux 2", &name##_aux2_mux), \
+ ARIZONA_MUX(name_str " Aux 3", &name##_aux3_mux), \
+ ARIZONA_MUX(name_str " Aux 4", &name##_aux4_mux), \
+ ARIZONA_MUX(name_str " Aux 5", &name##_aux5_mux), \
+ ARIZONA_MUX(name_str " Aux 6", &name##_aux6_mux)
+
+#define ARIZONA_MUX_ROUTES(widget, name) \
+ { widget, NULL, name " Input" }, \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Input")
+
+#define ARIZONA_MIXER_ROUTES(widget, name) \
+ { widget, NULL, name " Mixer" }, \
+ { name " Mixer", NULL, name " Input 1" }, \
+ { name " Mixer", NULL, name " Input 2" }, \
+ { name " Mixer", NULL, name " Input 3" }, \
+ { name " Mixer", NULL, name " Input 4" }, \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Input 1"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Input 2"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Input 3"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
+
+#define ARIZONA_DSP_ROUTES(name) \
+ { name, NULL, name " Preloader"}, \
+ { name " Preloader", NULL, 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" }, \
+ 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")
+
+#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];
+
+extern const struct soc_enum arizona_isrc_fsl[];
+extern const struct soc_enum arizona_isrc_fsh[];
+extern const struct soc_enum arizona_asrc_rate1;
+
+extern const struct soc_enum arizona_in_vi_ramp;
+extern const struct soc_enum arizona_in_vd_ramp;
+
+extern const struct soc_enum arizona_out_vi_ramp;
+extern const struct soc_enum arizona_out_vd_ramp;
+
+extern const struct soc_enum arizona_lhpf1_mode;
+extern const struct soc_enum arizona_lhpf2_mode;
+extern const struct soc_enum arizona_lhpf3_mode;
+extern const struct soc_enum arizona_lhpf4_mode;
+
+extern const struct soc_enum arizona_ng_hold;
+extern const struct soc_enum arizona_in_hpf_cut_enum;
+extern const struct soc_enum arizona_in_dmic_osr[];
+
+extern 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 int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir);
+
+extern const struct snd_soc_dai_ops arizona_dai_ops;
+extern const struct snd_soc_dai_ops arizona_simple_dai_ops;
+
+#define ARIZONA_FLL_NAME_LEN 20
+
+struct arizona_fll {
+ struct arizona *arizona;
+ int id;
+ unsigned int base;
+ unsigned int vco_mult;
+ struct completion ok;
+
+ unsigned int fout;
+ int sync_src;
+ unsigned int sync_freq;
+ int ref_src;
+ unsigned int ref_freq;
+
+ char lock_name[ARIZONA_FLL_NAME_LEN];
+ char clock_ok_name[ARIZONA_FLL_NAME_LEN];
+};
+
+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,
+ unsigned int Fref, unsigned int Fout);
+
+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);
+
+extern int arizona_init_dai(struct arizona_priv *priv, int dai);
+
+int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
+ bool diff);
+
+#endif
diff --git a/kernel/sound/soc/codecs/bt-sco.c b/kernel/sound/soc/codecs/bt-sco.c
new file mode 100644
index 000000000..e7238b890
--- /dev/null
+++ b/kernel/sound/soc/codecs/bt-sco.c
@@ -0,0 +1,90 @@
+/*
+ * 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>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget bt_sco_widgets[] = {
+ SND_SOC_DAPM_INPUT("RX"),
+ SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route bt_sco_routes[] = {
+ { "Capture", NULL, "RX" },
+ { "TX", NULL, "Playback" },
+};
+
+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_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 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;
+}
+
+static struct platform_device_id bt_sco_driver_ids[] = {
+ {
+ .name = "dfbmcs320",
+ },
+ {
+ .name = "bt-sco",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
+
+static struct platform_driver bt_sco_driver = {
+ .driver = {
+ .name = "bt-sco",
+ },
+ .probe = bt_sco_probe,
+ .remove = bt_sco_remove,
+ .id_table = bt_sco_driver_ids,
+};
+
+module_platform_driver(bt_sco_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ASoC generic bluetooth sco link driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cq93vc.c b/kernel/sound/soc/codecs/cq93vc.c
new file mode 100644
index 000000000..d6dedd4ea
--- /dev/null
+++ b/kernel/sound/soc/codecs/cq93vc.c
@@ -0,0 +1,164 @@
+/*
+ * 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>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/mfd/davinci_voicecodec.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/initval.h>
+
+static const struct snd_kcontrol_new cq93vc_snd_controls[] = {
+ SOC_SINGLE("PGA Capture Volume", DAVINCI_VC_REG05, 0, 0x03, 0),
+ SOC_SINGLE("Mono DAC Playback Volume", DAVINCI_VC_REG09, 0, 0x3f, 0),
+};
+
+static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 reg;
+
+ if (mute)
+ reg = DAVINCI_VC_REG09_MUTE;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, DAVINCI_VC_REG09, DAVINCI_VC_REG09_MUTE,
+ reg);
+
+ return 0;
+}
+
+static int cq93vc_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ switch (freq) {
+ case 22579200:
+ case 27000000:
+ case 33868800:
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int cq93vc_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_write(codec, 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,
+ DAVINCI_VC_REG12_POWER_ALL_OFF);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* force all power off */
+ snd_soc_write(codec, DAVINCI_VC_REG12,
+ DAVINCI_VC_REG12_POWER_ALL_OFF);
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+#define CQ93VC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
+#define CQ93VC_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
+
+static const struct snd_soc_dai_ops cq93vc_dai_ops = {
+ .digital_mute = cq93vc_mute,
+ .set_sysclk = cq93vc_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver cq93vc_dai = {
+ .name = "cq93vc-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CQ93VC_RATES,
+ .formats = CQ93VC_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CQ93VC_RATES,
+ .formats = CQ93VC_FORMATS,},
+ .ops = &cq93vc_dai_ops,
+};
+
+static struct regmap *cq93vc_get_regmap(struct device *dev)
+{
+ struct davinci_vc *davinci_vc = dev->platform_data;
+
+ return davinci_vc->regmap;
+}
+
+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 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;
+}
+
+static struct platform_driver cq93vc_codec_driver = {
+ .driver = {
+ .name = "cq93vc-codec",
+ },
+
+ .probe = cq93vc_platform_probe,
+ .remove = cq93vc_platform_remove,
+};
+
+module_platform_driver(cq93vc_codec_driver);
+
+MODULE_DESCRIPTION("Texas Instruments DaVinci ASoC CQ0093 Voice Codec Driver");
+MODULE_AUTHOR("Miguel Aguilar");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs35l32.c b/kernel/sound/soc/codecs/cs35l32.c
new file mode 100644
index 000000000..60598b230
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs35l32.c
@@ -0,0 +1,624 @@
+/*
+ * 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 <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/cs35l32.h>
+
+#include "cs35l32.h"
+
+#define CS35L32_NUM_SUPPLIES 2
+static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+};
+
+struct cs35l32_private {
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES];
+ struct cs35l32_platform_data pdata;
+ struct gpio_desc *reset_gpio;
+};
+
+static const struct reg_default cs35l32_reg_defaults[] = {
+
+ { 0x06, 0x04 }, /* Power Ctl 1 */
+ { 0x07, 0xE8 }, /* Power Ctl 2 */
+ { 0x08, 0x40 }, /* Clock Ctl */
+ { 0x09, 0x20 }, /* Low Battery Threshold */
+ { 0x0A, 0x00 }, /* Voltage Monitor [RO] */
+ { 0x0B, 0x40 }, /* Conv Peak Curr Protection CTL */
+ { 0x0C, 0x07 }, /* IMON Scaling */
+ { 0x0D, 0x03 }, /* Audio/LED Pwr Manager */
+ { 0x0F, 0x20 }, /* Serial Port Control */
+ { 0x10, 0x14 }, /* Class D Amp CTL */
+ { 0x11, 0x00 }, /* Protection Release CTL */
+ { 0x12, 0xFF }, /* Interrupt Mask 1 */
+ { 0x13, 0xFF }, /* Interrupt Mask 2 */
+ { 0x14, 0xFF }, /* Interrupt Mask 3 */
+ { 0x19, 0x00 }, /* LED Flash Mode Current */
+ { 0x1A, 0x00 }, /* LED Movie Mode Current */
+ { 0x1B, 0x20 }, /* LED Flash Timer */
+ { 0x1C, 0x00 }, /* LED Flash Inhibit Current */
+};
+
+static bool cs35l32_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L32_DEVID_AB:
+ 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:
+ return true;
+ default:
+ return false;
+ }
+}
+
+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:
+ return true;
+ default:
+ return false;
+ }
+}
+
+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:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 300, 0);
+
+static const struct snd_kcontrol_new imon_ctl =
+ SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 6, 1, 1);
+
+static const struct snd_kcontrol_new vmon_ctl =
+ SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 7, 1, 1);
+
+static const struct snd_kcontrol_new vpmon_ctl =
+ SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 5, 1, 1);
+
+static const struct snd_kcontrol_new cs35l32_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", CS35L32_CLASSD_CTL,
+ 3, 0x04, 1, classd_ctl_tlv),
+ SOC_SINGLE("Zero Cross Switch", CS35L32_CLASSD_CTL, 2, 1, 0),
+ SOC_SINGLE("Gain Manager Switch", CS35L32_AUDIO_LED_MNGR, 3, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget cs35l32_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SUPPLY("BOOST", CS35L32_PWRCTL1, 2, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Speaker", CS35L32_PWRCTL1, 7, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L32_PWRCTL2, 3, 1),
+
+ SND_SOC_DAPM_INPUT("VP"),
+ SND_SOC_DAPM_INPUT("ISENSE"),
+ SND_SOC_DAPM_INPUT("VSENSE"),
+
+ SND_SOC_DAPM_SWITCH("VMON ADC", CS35L32_PWRCTL2, 7, 1, &vmon_ctl),
+ SND_SOC_DAPM_SWITCH("IMON ADC", CS35L32_PWRCTL2, 6, 1, &imon_ctl),
+ SND_SOC_DAPM_SWITCH("VPMON ADC", CS35L32_PWRCTL2, 5, 1, &vpmon_ctl),
+};
+
+static const struct snd_soc_dapm_route cs35l32_audio_map[] = {
+
+ {"Speaker", NULL, "BOOST"},
+
+ {"VMON ADC", NULL, "VSENSE"},
+ {"IMON ADC", NULL, "ISENSE"},
+ {"VPMON ADC", NULL, "VP"},
+
+ {"SDOUT", "Switch", "VMON ADC"},
+ {"SDOUT", "Switch", "IMON ADC"},
+ {"SDOUT", "Switch", "VPMON ADC"},
+
+ {"Capture", NULL, "SDOUT"},
+};
+
+static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ snd_soc_update_bits(codec, 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,
+ CS35L32_ADSP_MASTER_MASK, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ return snd_soc_update_bits(codec, CS35L32_PWRCTL2,
+ CS35L32_SDOUT_3ST, tristate << 3);
+}
+
+static const struct snd_soc_dai_ops cs35l32_ops = {
+ .set_fmt = cs35l32_set_dai_fmt,
+ .set_tristate = cs35l32_set_tristate,
+};
+
+static struct snd_soc_dai_driver cs35l32_dai[] = {
+ {
+ .name = "cs35l32-monitor",
+ .id = 0,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CS35L32_RATES,
+ .formats = CS35L32_FORMATS,
+ },
+ .ops = &cs35l32_ops,
+ .symmetric_rates = 1,
+ }
+};
+
+static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ unsigned int val;
+
+ switch (freq) {
+ case 6000000:
+ val = CS35L32_MCLK_RATIO;
+ break;
+ case 12000000:
+ val = CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO;
+ break;
+ case 6144000:
+ val = 0;
+ break;
+ case 12288000:
+ val = CS35L32_MCLK_DIV2_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_update_bits(codec, 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),
+};
+
+/* Current and threshold powerup sequence Pg37 in datasheet */
+static const struct reg_default cs35l32_monitor_patch[] = {
+
+ { 0x00, 0x99 },
+ { 0x48, 0x17 },
+ { 0x49, 0x56 },
+ { 0x43, 0x01 },
+ { 0x3B, 0x62 },
+ { 0x3C, 0x80 },
+ { 0x00, 0x00 },
+};
+
+static const struct regmap_config cs35l32_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS35L32_MAX_REGISTER,
+ .reg_defaults = cs35l32_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l32_reg_defaults),
+ .volatile_reg = cs35l32_volatile_register,
+ .readable_reg = cs35l32_readable_register,
+ .precious_reg = cs35l32_precious_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
+ struct cs35l32_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ unsigned int val;
+
+ if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0)
+ pdata->sdout_share = val;
+
+ of_property_read_u32(np, "cirrus,boost-manager", &val);
+ switch (val) {
+ case CS35L32_BOOST_MGR_AUTO:
+ case CS35L32_BOOST_MGR_AUTO_AUDIO:
+ case CS35L32_BOOST_MGR_BYPASS:
+ case CS35L32_BOOST_MGR_FIXED:
+ pdata->boost_mng = val;
+ break;
+ 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);
+ switch (val) {
+ case CS35L32_DATA_CFG_LR_VP:
+ case CS35L32_DATA_CFG_LR_STAT:
+ case CS35L32_DATA_CFG_LR:
+ case CS35L32_DATA_CFG_LR_VPSTAT:
+ pdata->sdout_datacfg = val;
+ break;
+ 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);
+ switch (val) {
+ case CS35L32_BATT_THRESH_3_1V:
+ case CS35L32_BATT_THRESH_3_2V:
+ case CS35L32_BATT_THRESH_3_3V:
+ case CS35L32_BATT_THRESH_3_4V:
+ pdata->batt_thresh = val;
+ break;
+ 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);
+ switch (val) {
+ case CS35L32_BATT_RECOV_3_1V:
+ case CS35L32_BATT_RECOV_3_2V:
+ case CS35L32_BATT_RECOV_3_3V:
+ case CS35L32_BATT_RECOV_3_4V:
+ case CS35L32_BATT_RECOV_3_5V:
+ case CS35L32_BATT_RECOV_3_6V:
+ pdata->batt_recov = val;
+ break;
+ default:
+ dev_err(&i2c_client->dev,
+ "Wrong cirrus,battery-recovery DT value %d\n", val);
+ pdata->batt_recov = CS35L32_BATT_RECOV_3_4V;
+ }
+
+ return 0;
+}
+
+static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct cs35l32_private *cs35l32;
+ struct cs35l32_platform_data *pdata =
+ dev_get_platdata(&i2c_client->dev);
+ int ret, i;
+ unsigned int devid = 0;
+ 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");
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(i2c_client, cs35l32);
+
+ cs35l32->regmap = devm_regmap_init_i2c(i2c_client, &cs35l32_regmap);
+ if (IS_ERR(cs35l32->regmap)) {
+ ret = PTR_ERR(cs35l32->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l32->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(&i2c_client->dev,
+ sizeof(struct cs35l32_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 = cs35l32_handle_of_data(i2c_client,
+ &cs35l32->pdata);
+ if (ret != 0)
+ return ret;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l32->supplies); i++)
+ cs35l32->supplies[i].supply = cs35l32_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ ARRAY_SIZE(cs35l32->supplies),
+ cs35l32->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
+ cs35l32->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the Device */
+ cs35l32->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l32->reset_gpio))
+ return PTR_ERR(cs35l32->reset_gpio);
+
+ if (cs35l32->reset_gpio)
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
+
+ /* 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;
+
+ ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_E, &reg);
+ devid |= (reg & 0xF0) >> 4;
+
+ 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;
+ }
+
+ ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ return ret;
+ }
+
+ 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;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS35L32, Revision: %02X\n", reg & 0xFF);
+
+ /* Setup VBOOST Management */
+ if (cs35l32->pdata.boost_mng)
+ regmap_update_bits(cs35l32->regmap, CS35L32_AUDIO_LED_MNGR,
+ CS35L32_BOOST_MASK,
+ cs35l32->pdata.boost_mng);
+
+ /* Setup ADSP Format Config */
+ if (cs35l32->pdata.sdout_share)
+ regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
+ CS35L32_ADSP_SHARE_MASK,
+ cs35l32->pdata.sdout_share << 3);
+
+ /* Setup ADSP Data Configuration */
+ if (cs35l32->pdata.sdout_datacfg)
+ regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
+ CS35L32_ADSP_DATACFG_MASK,
+ cs35l32->pdata.sdout_datacfg << 4);
+
+ /* Setup Low Battery Recovery */
+ if (cs35l32->pdata.batt_recov)
+ regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
+ CS35L32_BATT_REC_MASK,
+ cs35l32->pdata.batt_recov << 1);
+
+ /* Setup Low Battery Threshold */
+ if (cs35l32->pdata.batt_thresh)
+ regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
+ CS35L32_BATT_THRESH_MASK,
+ cs35l32->pdata.batt_thresh << 4);
+
+ /* Power down the AMP */
+ regmap_update_bits(cs35l32->regmap, CS35L32_PWRCTL1, CS35L32_PDN_AMP,
+ CS35L32_PDN_AMP);
+
+ /* Clear MCLK Error Bit since we don't have the clock yet */
+ ret = regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg);
+
+ ret = snd_soc_register_codec(&i2c_client->dev,
+ &soc_codec_dev_cs35l32, cs35l32_dai,
+ ARRAY_SIZE(cs35l32_dai));
+ if (ret < 0)
+ goto err_disable;
+
+ return 0;
+
+err_disable:
+ regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
+ cs35l32->supplies);
+ return ret;
+}
+
+static int 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;
+}
+
+#ifdef CONFIG_PM
+static int cs35l32_runtime_suspend(struct device *dev)
+{
+ struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs35l32->regmap, true);
+ regcache_mark_dirty(cs35l32->regmap);
+
+ /* Hold down reset */
+ if (cs35l32->reset_gpio)
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
+
+ /* remove power */
+ regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
+ cs35l32->supplies);
+
+ return 0;
+}
+
+static int cs35l32_runtime_resume(struct device *dev)
+{
+ struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
+ int ret;
+
+ /* Enable power */
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
+ cs35l32->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (cs35l32->reset_gpio)
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
+
+ regcache_cache_only(cs35l32->regmap, false);
+ regcache_sync(cs35l32->regmap);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops cs35l32_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs35l32_of_match[] = {
+ { .compatible = "cirrus,cs35l32", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l32_of_match);
+
+
+static const struct i2c_device_id cs35l32_id[] = {
+ {"cs35l32", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l32_id);
+
+static struct i2c_driver cs35l32_i2c_driver = {
+ .driver = {
+ .name = "cs35l32",
+ .owner = THIS_MODULE,
+ .pm = &cs35l32_runtime_pm,
+ .of_match_table = cs35l32_of_match,
+ },
+ .id_table = cs35l32_id,
+ .probe = cs35l32_i2c_probe,
+ .remove = cs35l32_i2c_remove,
+};
+
+module_i2c_driver(cs35l32_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L32 driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs35l32.h b/kernel/sound/soc/codecs/cs35l32.h
new file mode 100644
index 000000000..31ab804a2
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs35l32.h
@@ -0,0 +1,93 @@
+/*
+ * 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__
+#define __CS35L32_H__
+
+struct cs35l32_platform_data {
+ /* Low Battery Threshold */
+ unsigned int batt_thresh;
+ /* Low Battery Recovery */
+ unsigned int batt_recov;
+ /* LED Current Management*/
+ unsigned int led_mng;
+ /* Audio Gain w/ LED */
+ unsigned int audiogain_mng;
+ /* Boost Management */
+ unsigned int boost_mng;
+ /* Data CFG for DUAL device */
+ unsigned int sdout_datacfg;
+ /* SDOUT Sharing */
+ unsigned int sdout_share;
+};
+
+#define CS35L32_CHIP_ID 0x00035A32
+#define CS35L32_DEVID_AB 0x01 /* Device ID A & B [RO] */
+#define CS35L32_DEVID_CD 0x02 /* Device ID C & D [RO] */
+#define CS35L32_DEVID_E 0x03 /* Device ID E [RO] */
+#define CS35L32_FAB_ID 0x04 /* Fab ID [RO] */
+#define CS35L32_REV_ID 0x05 /* Revision ID [RO] */
+#define CS35L32_PWRCTL1 0x06 /* Power Ctl 1 */
+#define CS35L32_PWRCTL2 0x07 /* Power Ctl 2 */
+#define CS35L32_CLK_CTL 0x08 /* Clock Ctl */
+#define CS35L32_BATT_THRESHOLD 0x09 /* Low Battery Threshold */
+#define CS35L32_VMON 0x0A /* Voltage Monitor [RO] */
+#define CS35L32_BST_CPCP_CTL 0x0B /* Conv Peak Curr Protection CTL */
+#define CS35L32_IMON_SCALING 0x0C /* IMON Scaling */
+#define CS35L32_AUDIO_LED_MNGR 0x0D /* Audio/LED Pwr Manager */
+#define CS35L32_ADSP_CTL 0x0F /* Serial Port Control */
+#define CS35L32_CLASSD_CTL 0x10 /* Class D Amp CTL */
+#define CS35L32_PROTECT_CTL 0x11 /* Protection Release CTL */
+#define CS35L32_INT_MASK_1 0x12 /* Interrupt Mask 1 */
+#define CS35L32_INT_MASK_2 0x13 /* Interrupt Mask 2 */
+#define CS35L32_INT_MASK_3 0x14 /* Interrupt Mask 3 */
+#define CS35L32_INT_STATUS_1 0x15 /* Interrupt Status 1 [RO] */
+#define CS35L32_INT_STATUS_2 0x16 /* Interrupt Status 2 [RO] */
+#define CS35L32_INT_STATUS_3 0x17 /* Interrupt Status 3 [RO] */
+#define CS35L32_LED_STATUS 0x18 /* LED Lighting Status [RO] */
+#define CS35L32_FLASH_MODE 0x19 /* LED Flash Mode Current */
+#define CS35L32_MOVIE_MODE 0x1A /* LED Movie Mode Current */
+#define CS35L32_FLASH_TIMER 0x1B /* LED Flash Timer */
+#define CS35L32_FLASH_INHIBIT 0x1C /* LED Flash Inhibit Current */
+#define CS35L32_MAX_REGISTER 0x1C
+
+#define CS35L32_MCLK_DIV2 0x01
+#define CS35L32_MCLK_RATIO 0x01
+#define CS35L32_MCLKDIS 0x80
+#define CS35L32_PDN_ALL 0x01
+#define CS35L32_PDN_AMP 0x80
+#define CS35L32_PDN_BOOST 0x04
+#define CS35L32_PDN_IMON 0x40
+#define CS35L32_PDN_VMON 0x80
+#define CS35L32_PDN_VPMON 0x20
+#define CS35L32_PDN_ADSP 0x08
+
+#define CS35L32_MCLK_DIV2_MASK 0x40
+#define CS35L32_MCLK_RATIO_MASK 0x01
+#define CS35L32_MCLK_MASK 0x41
+#define CS35L32_ADSP_MASTER_MASK 0x40
+#define CS35L32_BOOST_MASK 0x03
+#define CS35L32_GAIN_MGR_MASK 0x08
+#define CS35L32_ADSP_SHARE_MASK 0x08
+#define CS35L32_ADSP_DATACFG_MASK 0x30
+#define CS35L32_SDOUT_3ST 0x80
+#define CS35L32_BATT_REC_MASK 0x0E
+#define CS35L32_BATT_THRESH_MASK 0x30
+
+#define CS35L32_RATES (SNDRV_PCM_RATE_48000)
+#define CS35L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+
+#endif
diff --git a/kernel/sound/soc/codecs/cs4265.c b/kernel/sound/soc/codecs/cs4265.c
new file mode 100644
index 000000000..cac48ddf3
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs4265.c
@@ -0,0 +1,674 @@
+/*
+ * 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>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.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/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include "cs4265.h"
+
+struct cs4265_private {
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ u8 format;
+ u32 sysclk;
+};
+
+static const struct reg_default cs4265_reg_defaults[] = {
+ { CS4265_PWRCTL, 0x0F },
+ { CS4265_DAC_CTL, 0x08 },
+ { CS4265_ADC_CTL, 0x00 },
+ { CS4265_MCLK_FREQ, 0x00 },
+ { CS4265_SIG_SEL, 0x40 },
+ { CS4265_CHB_PGA_CTL, 0x00 },
+ { CS4265_CHA_PGA_CTL, 0x00 },
+ { CS4265_ADC_CTL2, 0x19 },
+ { CS4265_DAC_CHA_VOL, 0x00 },
+ { CS4265_DAC_CHB_VOL, 0x00 },
+ { CS4265_DAC_CTL2, 0xC0 },
+ { CS4265_SPDIF_CTL1, 0x00 },
+ { CS4265_SPDIF_CTL2, 0x00 },
+ { CS4265_INT_MASK, 0x00 },
+ { CS4265_STATUS_MODE_MSB, 0x00 },
+ { CS4265_STATUS_MODE_LSB, 0x00 },
+};
+
+static bool cs4265_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4265_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:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs4265_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4265_INT_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(pga_tlv, -1200, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 0);
+
+static const char * const digital_input_mux_text[] = {
+ "SDIN1", "SDIN2"
+};
+
+static SOC_ENUM_SINGLE_DECL(digital_input_mux_enum, CS4265_SIG_SEL, 7,
+ digital_input_mux_text);
+
+static const struct snd_kcontrol_new digital_input_mux =
+ SOC_DAPM_ENUM("Digital Input Mux", digital_input_mux_enum);
+
+static const char * const mic_linein_text[] = {
+ "MIC", "LINEIN"
+};
+
+static SOC_ENUM_SINGLE_DECL(mic_linein_enum, CS4265_ADC_CTL2, 0,
+ mic_linein_text);
+
+static const char * const cam_mode_text[] = {
+ "One Byte", "Two Byte"
+};
+
+static SOC_ENUM_SINGLE_DECL(cam_mode_enum, CS4265_SPDIF_CTL1, 5,
+ cam_mode_text);
+
+static const char * const cam_mono_stereo_text[] = {
+ "Stereo", "Mono"
+};
+
+static SOC_ENUM_SINGLE_DECL(spdif_mono_stereo_enum, CS4265_SPDIF_CTL2, 2,
+ cam_mono_stereo_text);
+
+static const char * const mono_select_text[] = {
+ "Channel A", "Channel B"
+};
+
+static SOC_ENUM_SINGLE_DECL(spdif_mono_select_enum, CS4265_SPDIF_CTL2, 0,
+ mono_select_text);
+
+static const struct snd_kcontrol_new mic_linein_mux =
+ SOC_DAPM_ENUM("ADC Input Capture Mux", mic_linein_enum);
+
+static const struct snd_kcontrol_new loopback_ctl =
+ SOC_DAPM_SINGLE("Switch", CS4265_SIG_SEL, 1, 1, 0);
+
+static const struct snd_kcontrol_new spdif_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 0, 0);
+
+static const struct snd_kcontrol_new dac_switch =
+ SOC_DAPM_SINGLE("Switch", CS4265_PWRCTL, 1, 1, 0);
+
+static const struct snd_kcontrol_new cs4265_snd_controls[] = {
+
+ SOC_DOUBLE_R_SX_TLV("PGA Volume", CS4265_CHA_PGA_CTL,
+ CS4265_CHB_PGA_CTL, 0, 0x28, 0x30, pga_tlv),
+ SOC_DOUBLE_R_TLV("DAC Volume", CS4265_DAC_CHA_VOL,
+ CS4265_DAC_CHB_VOL, 0, 0xFF, 1, dac_tlv),
+ SOC_SINGLE("De-emp 44.1kHz Switch", CS4265_DAC_CTL, 1,
+ 1, 0),
+ SOC_SINGLE("DAC INV Switch", CS4265_DAC_CTL2, 5,
+ 1, 0),
+ SOC_SINGLE("DAC Zero Cross Switch", CS4265_DAC_CTL2, 6,
+ 1, 0),
+ SOC_SINGLE("DAC Soft Ramp Switch", CS4265_DAC_CTL2, 7,
+ 1, 0),
+ SOC_SINGLE("ADC HPF Switch", CS4265_ADC_CTL, 1,
+ 1, 0),
+ SOC_SINGLE("ADC Zero Cross Switch", CS4265_ADC_CTL2, 3,
+ 1, 1),
+ SOC_SINGLE("ADC Soft Ramp Switch", CS4265_ADC_CTL2, 7,
+ 1, 0),
+ SOC_SINGLE("E to F Buffer Disable Switch", CS4265_SPDIF_CTL1,
+ 6, 1, 0),
+ SOC_ENUM("C Data Access", cam_mode_enum),
+ SOC_SINGLE("Validity Bit Control Switch", CS4265_SPDIF_CTL2,
+ 3, 1, 0),
+ SOC_ENUM("SPDIF Mono/Stereo", spdif_mono_stereo_enum),
+ SOC_SINGLE("MMTLR Data Switch", 0,
+ 1, 1, 0),
+ SOC_ENUM("Mono Channel Select", spdif_mono_select_enum),
+ SND_SOC_BYTES("C Data Buffer", CS4265_C_DATA_BUFF, 24),
+};
+
+static const struct snd_soc_dapm_widget cs4265_dapm_widgets[] = {
+
+ SND_SOC_DAPM_INPUT("LINEINL"),
+ SND_SOC_DAPM_INPUT("LINEINR"),
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+
+ SND_SOC_DAPM_AIF_OUT("DOUT", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SPDIFOUT", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("ADC Mux", SND_SOC_NOPM, 0, 0, &mic_linein_mux),
+
+ SND_SOC_DAPM_ADC("ADC", NULL, CS4265_PWRCTL, 2, 1),
+ SND_SOC_DAPM_PGA("Pre-amp MIC", CS4265_PWRCTL, 3,
+ 1, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM,
+ 0, 0, &digital_input_mux),
+
+ SND_SOC_DAPM_MIXER("SDIN1 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SDIN2 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SPDIF Transmitter", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("Loopback", SND_SOC_NOPM, 0, 0,
+ &loopback_ctl),
+ SND_SOC_DAPM_SWITCH("SPDIF", SND_SOC_NOPM, 0, 0,
+ &spdif_switch),
+ SND_SOC_DAPM_SWITCH("DAC", CS4265_PWRCTL, 1, 1,
+ &dac_switch),
+
+ SND_SOC_DAPM_AIF_IN("DIN1", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DIN2", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TXIN", NULL, 0,
+ CS4265_SPDIF_CTL2, 5, 1),
+
+ SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+ SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+
+};
+
+static const struct snd_soc_dapm_route cs4265_audio_map[] = {
+
+ {"DIN1", NULL, "DAI1 Playback"},
+ {"DIN2", NULL, "DAI2 Playback"},
+ {"SDIN1 Input Mixer", NULL, "DIN1"},
+ {"SDIN2 Input Mixer", NULL, "DIN2"},
+ {"Input Mux", "SDIN1", "SDIN1 Input Mixer"},
+ {"Input Mux", "SDIN2", "SDIN2 Input Mixer"},
+ {"DAC", "Switch", "Input Mux"},
+ {"SPDIF", "Switch", "Input Mux"},
+ {"LINEOUTL", NULL, "DAC"},
+ {"LINEOUTR", NULL, "DAC"},
+ {"SPDIFOUT", NULL, "SPDIF"},
+
+ {"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"},
+ {"DAI2 Capture", NULL, "DOUT"},
+
+ /* Loopback */
+ {"Loopback", "Switch", "ADC"},
+ {"DAC", NULL, "Loopback"},
+};
+
+struct cs4265_clk_para {
+ u32 mclk;
+ u32 rate;
+ u8 fm_mode; /* values 1, 2, or 4 */
+ u8 mclkdiv;
+};
+
+static const struct cs4265_clk_para clk_map_table[] = {
+ /*32k*/
+ {8192000, 32000, 0, 0},
+ {12288000, 32000, 0, 1},
+ {16384000, 32000, 0, 2},
+ {24576000, 32000, 0, 3},
+ {32768000, 32000, 0, 4},
+
+ /*44.1k*/
+ {11289600, 44100, 0, 0},
+ {16934400, 44100, 0, 1},
+ {22579200, 44100, 0, 2},
+ {33868000, 44100, 0, 3},
+ {45158400, 44100, 0, 4},
+
+ /*48k*/
+ {12288000, 48000, 0, 0},
+ {18432000, 48000, 0, 1},
+ {24576000, 48000, 0, 2},
+ {36864000, 48000, 0, 3},
+ {49152000, 48000, 0, 4},
+
+ /*64k*/
+ {8192000, 64000, 1, 0},
+ {12288000, 64000, 1, 1},
+ {16934400, 64000, 1, 2},
+ {24576000, 64000, 1, 3},
+ {32768000, 64000, 1, 4},
+
+ /* 88.2k */
+ {11289600, 88200, 1, 0},
+ {16934400, 88200, 1, 1},
+ {22579200, 88200, 1, 2},
+ {33868000, 88200, 1, 3},
+ {45158400, 88200, 1, 4},
+
+ /* 96k */
+ {12288000, 96000, 1, 0},
+ {18432000, 96000, 1, 1},
+ {24576000, 96000, 1, 2},
+ {36864000, 96000, 1, 3},
+ {49152000, 96000, 1, 4},
+
+ /* 128k */
+ {8192000, 128000, 2, 0},
+ {12288000, 128000, 2, 1},
+ {16934400, 128000, 2, 2},
+ {24576000, 128000, 2, 3},
+ {32768000, 128000, 2, 4},
+
+ /* 176.4k */
+ {11289600, 176400, 2, 0},
+ {16934400, 176400, 2, 1},
+ {22579200, 176400, 2, 2},
+ {33868000, 176400, 2, 3},
+ {49152000, 176400, 2, 4},
+
+ /* 192k */
+ {12288000, 192000, 2, 0},
+ {18432000, 192000, 2, 1},
+ {24576000, 192000, 2, 2},
+ {36864000, 192000, 2, 3},
+ {49152000, 192000, 2, 4},
+};
+
+static int cs4265_get_clk_index(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
+ if (clk_map_table[i].rate == rate &&
+ clk_map_table[i].mclk == mclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int cs4265_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ if (clk_id != 0) {
+ dev_err(codec->dev, "Invalid clk_id %d\n", clk_id);
+ return -EINVAL;
+ }
+ for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
+ if (clk_map_table[i].mclk == freq) {
+ cs4265->sysclk = freq;
+ return 0;
+ }
+ }
+ cs4265->sysclk = 0;
+ dev_err(codec->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);
+ u8 iface = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ CS4265_ADC_MASTER,
+ CS4265_ADC_MASTER);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ CS4265_ADC_MASTER,
+ 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= SND_SOC_DAIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface |= SND_SOC_DAIFMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= SND_SOC_DAIFMT_LEFT_J;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cs4265->format = iface;
+ return 0;
+}
+
+static int cs4265_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute) {
+ snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_MUTE,
+ CS4265_DAC_CTL_MUTE);
+ snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ CS4265_SPDIF_CTL2_MUTE,
+ CS4265_SPDIF_CTL2_MUTE);
+ } else {
+ snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_MUTE,
+ 0);
+ snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ CS4265_SPDIF_CTL2_MUTE,
+ 0);
+ }
+ return 0;
+}
+
+static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ int index;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+ ((cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK)
+ == SND_SOC_DAIFMT_RIGHT_J))
+ return -EINVAL;
+
+ index = cs4265_get_clk_index(cs4265->sysclk, params_rate(params));
+ if (index >= 0) {
+ snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ CS4265_ADC_FM, clk_map_table[index].fm_mode << 6);
+ snd_soc_update_bits(codec, CS4265_MCLK_FREQ,
+ CS4265_MCLK_FREQ_MASK,
+ clk_map_table[index].mclkdiv << 4);
+
+ } else {
+ dev_err(codec->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,
+ CS4265_DAC_CTL_DIF, (1 << 4));
+ snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ CS4265_ADC_DIF, (1 << 4));
+ snd_soc_update_bits(codec, 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));
+ } 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));
+ }
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_DIF, 0);
+ snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ CS4265_ADC_DIF, 0);
+ snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ CS4265_SPDIF_CTL2_DIF, (1 << 6));
+
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs4265_set_bias_level(struct snd_soc_codec *codec,
+ 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,
+ CS4265_PWRCTL_PDN, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_update_bits(codec, CS4265_PWRCTL,
+ CS4265_PWRCTL_PDN,
+ CS4265_PWRCTL_PDN);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, CS4265_PWRCTL,
+ CS4265_PWRCTL_PDN,
+ CS4265_PWRCTL_PDN);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define CS4265_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+#define CS4265_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE)
+
+static const struct snd_soc_dai_ops cs4265_ops = {
+ .hw_params = cs4265_pcm_hw_params,
+ .digital_mute = cs4265_digital_mute,
+ .set_fmt = cs4265_set_fmt,
+ .set_sysclk = cs4265_set_sysclk,
+};
+
+static struct snd_soc_dai_driver cs4265_dai[] = {
+ {
+ .name = "cs4265-dai1",
+ .playback = {
+ .stream_name = "DAI1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4265_RATES,
+ .formats = CS4265_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DAI1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4265_RATES,
+ .formats = CS4265_FORMATS,
+ },
+ .ops = &cs4265_ops,
+ },
+ {
+ .name = "cs4265-dai2",
+ .playback = {
+ .stream_name = "DAI2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4265_RATES,
+ .formats = CS4265_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DAI2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4265_RATES,
+ .formats = CS4265_FORMATS,
+ },
+ .ops = &cs4265_ops,
+ },
+};
+
+static const struct snd_soc_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 regmap_config cs4265_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS4265_MAX_REGISTER,
+ .reg_defaults = cs4265_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs4265_reg_defaults),
+ .readable_reg = cs4265_readable_register,
+ .volatile_reg = cs4265_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs4265_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct cs4265_private *cs4265;
+ int ret = 0;
+ unsigned int devid = 0;
+ unsigned int reg;
+
+ cs4265 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4265_private),
+ GFP_KERNEL);
+ if (cs4265 == NULL)
+ return -ENOMEM;
+
+ cs4265->regmap = devm_regmap_init_i2c(i2c_client, &cs4265_regmap);
+ if (IS_ERR(cs4265->regmap)) {
+ ret = PTR_ERR(cs4265->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ cs4265->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs4265->reset_gpio))
+ return PTR_ERR(cs4265->reset_gpio);
+
+ if (cs4265->reset_gpio) {
+ mdelay(1);
+ gpiod_set_value_cansleep(cs4265->reset_gpio, 1);
+ }
+
+ i2c_set_clientdata(i2c_client, cs4265);
+
+ ret = regmap_read(cs4265->regmap, CS4265_CHIP_ID, &reg);
+ 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);
+ return ret;
+ }
+ dev_info(&i2c_client->dev,
+ "CS4265 Version %x\n",
+ reg & CS4265_REV_ID_MASK);
+
+ regmap_write(cs4265->regmap, CS4265_PWRCTL, 0x0F);
+
+ ret = snd_soc_register_codec(&i2c_client->dev,
+ &soc_codec_cs4265, cs4265_dai,
+ ARRAY_SIZE(cs4265_dai));
+ return ret;
+}
+
+static int cs4265_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct of_device_id cs4265_of_match[] = {
+ { .compatible = "cirrus,cs4265", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs4265_of_match);
+
+static const struct i2c_device_id cs4265_id[] = {
+ { "cs4265", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs4265_id);
+
+static struct i2c_driver cs4265_i2c_driver = {
+ .driver = {
+ .name = "cs4265",
+ .owner = THIS_MODULE,
+ .of_match_table = cs4265_of_match,
+ },
+ .id_table = cs4265_id,
+ .probe = cs4265_i2c_probe,
+ .remove = cs4265_i2c_remove,
+};
+
+module_i2c_driver(cs4265_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS4265 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paul.handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs4265.h b/kernel/sound/soc/codecs/cs4265.h
new file mode 100644
index 000000000..0a80a8dce
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs4265.h
@@ -0,0 +1,64 @@
+/*
+ * 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__
+#define __CS4265_H__
+
+#define CS4265_CHIP_ID 0x1
+#define CS4265_CHIP_ID_VAL 0xD0
+#define CS4265_CHIP_ID_MASK 0xF0
+#define CS4265_REV_ID_MASK 0x0F
+
+#define CS4265_PWRCTL 0x02
+#define CS4265_PWRCTL_PDN 1
+
+#define CS4265_DAC_CTL 0x3
+#define CS4265_DAC_CTL_MUTE (1 << 2)
+#define CS4265_DAC_CTL_DIF (3 << 4)
+
+#define CS4265_ADC_CTL 0x4
+#define CS4265_ADC_MASTER 1
+#define CS4265_ADC_DIF (1 << 4)
+#define CS4265_ADC_FM (3 << 6)
+
+#define CS4265_MCLK_FREQ 0x5
+#define CS4265_MCLK_FREQ_MASK (7 << 4)
+
+#define CS4265_SIG_SEL 0x6
+#define CS4265_SIG_SEL_LOOP (1 << 1)
+
+#define CS4265_CHB_PGA_CTL 0x7
+#define CS4265_CHA_PGA_CTL 0x8
+
+#define CS4265_ADC_CTL2 0x9
+
+#define CS4265_DAC_CHA_VOL 0xA
+#define CS4265_DAC_CHB_VOL 0xB
+
+#define CS4265_DAC_CTL2 0xC
+
+#define CS4265_INT_STATUS 0xD
+#define CS4265_INT_MASK 0xE
+#define CS4265_STATUS_MODE_MSB 0xF
+#define CS4265_STATUS_MODE_LSB 0x10
+
+#define CS4265_SPDIF_CTL1 0x11
+
+#define CS4265_SPDIF_CTL2 0x12
+#define CS4265_SPDIF_CTL2_MUTE (1 << 4)
+#define CS4265_SPDIF_CTL2_DIF (3 << 6)
+
+#define CS4265_C_DATA_BUFF 0x13
+#define CS4265_MAX_REGISTER 0x2A
+
+#endif
diff --git a/kernel/sound/soc/codecs/cs4270.c b/kernel/sound/soc/codecs/cs4270.c
new file mode 100644
index 000000000..e6d4ff9fd
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs4270.c
@@ -0,0 +1,766 @@
+/*
+ * CS4270 ALSA SoC (ASoC) codec driver
+ *
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. This file is licensed
+ * under the terms of the GNU General Public License version 2. This
+ * program is licensed "as is" without any warranty of any kind, whether
+ * express or implied.
+ *
+ * This is an ASoC device driver for the Cirrus Logic CS4270 codec.
+ *
+ * Current features/limitations:
+ *
+ * - Software mode is supported. Stand-alone mode is not supported.
+ * - Only I2C is supported, not SPI
+ * - Support for master and slave mode
+ * - The machine driver's 'startup' function must call
+ * cs4270_set_dai_sysclk() with the value of MCLK.
+ * - Only I2S and left-justified modes are supported
+ * - Power management is supported
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.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)
+
+/* CS4270 registers addresses */
+#define CS4270_CHIPID 0x01 /* Chip ID */
+#define CS4270_PWRCTL 0x02 /* Power Control */
+#define CS4270_MODE 0x03 /* Mode Control */
+#define CS4270_FORMAT 0x04 /* Serial Format, ADC/DAC Control */
+#define CS4270_TRANS 0x05 /* Transition Control */
+#define CS4270_MUTE 0x06 /* Mute Control */
+#define CS4270_VOLA 0x07 /* DAC Channel A Volume Control */
+#define CS4270_VOLB 0x08 /* DAC Channel B Volume Control */
+
+#define CS4270_FIRSTREG 0x01
+#define CS4270_LASTREG 0x08
+#define CS4270_NUMREGS (CS4270_LASTREG - CS4270_FIRSTREG + 1)
+#define CS4270_I2C_INCR 0x80
+
+/* Bit masks for the CS4270 registers */
+#define CS4270_CHIPID_ID 0xF0
+#define CS4270_CHIPID_REV 0x0F
+#define CS4270_PWRCTL_FREEZE 0x80
+#define CS4270_PWRCTL_PDN_ADC 0x20
+#define CS4270_PWRCTL_PDN_DAC 0x02
+#define CS4270_PWRCTL_PDN 0x01
+#define CS4270_PWRCTL_PDN_ALL \
+ (CS4270_PWRCTL_PDN_ADC | CS4270_PWRCTL_PDN_DAC | CS4270_PWRCTL_PDN)
+#define CS4270_MODE_SPEED_MASK 0x30
+#define CS4270_MODE_1X 0x00
+#define CS4270_MODE_2X 0x10
+#define CS4270_MODE_4X 0x20
+#define CS4270_MODE_SLAVE 0x30
+#define CS4270_MODE_DIV_MASK 0x0E
+#define CS4270_MODE_DIV1 0x00
+#define CS4270_MODE_DIV15 0x02
+#define CS4270_MODE_DIV2 0x04
+#define CS4270_MODE_DIV3 0x06
+#define CS4270_MODE_DIV4 0x08
+#define CS4270_MODE_POPGUARD 0x01
+#define CS4270_FORMAT_FREEZE_A 0x80
+#define CS4270_FORMAT_FREEZE_B 0x40
+#define CS4270_FORMAT_LOOPBACK 0x20
+#define CS4270_FORMAT_DAC_MASK 0x18
+#define CS4270_FORMAT_DAC_LJ 0x00
+#define CS4270_FORMAT_DAC_I2S 0x08
+#define CS4270_FORMAT_DAC_RJ16 0x18
+#define CS4270_FORMAT_DAC_RJ24 0x10
+#define CS4270_FORMAT_ADC_MASK 0x01
+#define CS4270_FORMAT_ADC_LJ 0x00
+#define CS4270_FORMAT_ADC_I2S 0x01
+#define CS4270_TRANS_ONE_VOL 0x80
+#define CS4270_TRANS_SOFT 0x40
+#define CS4270_TRANS_ZERO 0x20
+#define CS4270_TRANS_INV_ADC_A 0x08
+#define CS4270_TRANS_INV_ADC_B 0x10
+#define CS4270_TRANS_INV_DAC_A 0x02
+#define CS4270_TRANS_INV_DAC_B 0x04
+#define CS4270_TRANS_DEEMPH 0x01
+#define CS4270_MUTE_AUTO 0x20
+#define CS4270_MUTE_ADC_A 0x08
+#define CS4270_MUTE_ADC_B 0x10
+#define CS4270_MUTE_POLARITY 0x04
+#define CS4270_MUTE_DAC_A 0x01
+#define CS4270_MUTE_DAC_B 0x02
+
+/* Power-on default values for the registers
+ *
+ * This array contains the power-on default values of the registers, with the
+ * exception of the "CHIPID" register (01h). The lower four bits of that
+ * register contain the hardware revision, so it is treated as volatile.
+ */
+static const struct reg_default cs4270_reg_defaults[] = {
+ { 2, 0x00 },
+ { 3, 0x30 },
+ { 4, 0x00 },
+ { 5, 0x60 },
+ { 6, 0x20 },
+ { 7, 0x00 },
+ { 8, 0x00 },
+};
+
+static const char *supply_names[] = {
+ "va", "vd", "vlc"
+};
+
+/* Private data for the CS4270 */
+struct cs4270_private {
+ struct regmap *regmap;
+ unsigned int mclk; /* Input frequency of the MCLK pin */
+ unsigned int mode; /* The mode (I2S or left-justified) */
+ unsigned int slave_mode;
+ unsigned int manual_mute;
+
+ /* power domain regulators */
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+};
+
+static const struct snd_soc_dapm_widget cs4270_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+
+SND_SOC_DAPM_OUTPUT("AOUTL"),
+SND_SOC_DAPM_OUTPUT("AOUTR"),
+};
+
+static const struct snd_soc_dapm_route cs4270_dapm_routes[] = {
+ { "Capture", NULL, "AINA" },
+ { "Capture", NULL, "AINB" },
+
+ { "AOUTA", NULL, "Playback" },
+ { "AOUTB", NULL, "Playback" },
+};
+
+/**
+ * struct cs4270_mode_ratios - clock ratio tables
+ * @ratio: the ratio of MCLK to the sample rate
+ * @speed_mode: the Speed Mode bits to set in the Mode Control register for
+ * this ratio
+ * @mclk: the Ratio Select bits to set in the Mode Control register for this
+ * ratio
+ *
+ * The data for this chart is taken from Table 5 of the CS4270 reference
+ * manual.
+ *
+ * This table is used to determine how to program the Mode Control register.
+ * It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling
+ * rates the CS4270 currently supports.
+ *
+ * @speed_mode is the corresponding bit pattern to be written to the
+ * MODE bits of the Mode Control Register
+ *
+ * @mclk is the corresponding bit pattern to be wirten to the MCLK bits of
+ * the Mode Control Register.
+ *
+ * In situations where a single ratio is represented by multiple speed
+ * modes, we favor the slowest speed. E.g, for a ratio of 128, we pick
+ * double-speed instead of quad-speed. However, the CS4270 errata states
+ * that divide-By-1.5 can cause failures, so we avoid that mode where
+ * possible.
+ *
+ * Errata: There is an errata for the CS4270 where divide-by-1.5 does not
+ * work if Vd is 3.3V. If this effects you, select the
+ * CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will
+ * never select any sample rates that require divide-by-1.5.
+ */
+struct cs4270_mode_ratios {
+ unsigned int ratio;
+ u8 speed_mode;
+ u8 mclk;
+};
+
+static struct cs4270_mode_ratios cs4270_mode_ratios[] = {
+ {64, CS4270_MODE_4X, CS4270_MODE_DIV1},
+#ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA
+ {96, CS4270_MODE_4X, CS4270_MODE_DIV15},
+#endif
+ {128, CS4270_MODE_2X, CS4270_MODE_DIV1},
+ {192, CS4270_MODE_4X, CS4270_MODE_DIV3},
+ {256, CS4270_MODE_1X, CS4270_MODE_DIV1},
+ {384, CS4270_MODE_2X, CS4270_MODE_DIV3},
+ {512, CS4270_MODE_1X, CS4270_MODE_DIV2},
+ {768, CS4270_MODE_1X, CS4270_MODE_DIV3},
+ {1024, CS4270_MODE_1X, CS4270_MODE_DIV4}
+};
+
+/* The number of MCLK/LRCK ratios supported by the CS4270 */
+#define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios)
+
+static bool cs4270_reg_is_readable(struct device *dev, unsigned int reg)
+{
+ return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG);
+}
+
+static bool cs4270_reg_is_volatile(struct device *dev, unsigned int reg)
+{
+ /* Unreadable registers are considered volatile */
+ if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG))
+ return 1;
+
+ return reg == CS4270_CHIPID;
+}
+
+/**
+ * cs4270_set_dai_sysclk - determine the CS4270 samples rates.
+ * @codec_dai: the codec DAI
+ * @clk_id: the clock ID (ignored)
+ * @freq: the MCLK input frequency
+ * @dir: the clock direction (ignored)
+ *
+ * This function is used to tell the codec driver what the input MCLK
+ * frequency is.
+ *
+ * The value of MCLK is used to determine which sample rates are supported
+ * by the CS4270. The ratio of MCLK / Fs must be equal to one of nine
+ * supported values - 64, 96, 128, 192, 256, 384, 512, 768, and 1024.
+ *
+ * This function calculates the nine ratios and determines which ones match
+ * a standard sample rate. If there's a match, then it is added to the list
+ * of supported sample rates.
+ *
+ * This function must be called by the machine driver's 'startup' function,
+ * otherwise the list of supported sample rates will not be available in
+ * time for ALSA.
+ *
+ * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause
+ * theoretically possible sample rates to be enabled. Call it again with a
+ * proper value set one the external clock is set (most probably you would do
+ * that from a machine's driver 'hw_param' hook.
+ */
+static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+
+ cs4270->mclk = freq;
+ return 0;
+}
+
+/**
+ * cs4270_set_dai_fmt - configure the codec for the selected audio format
+ * @codec_dai: the codec DAI
+ * @format: a SND_SOC_DAIFMT_x value indicating the data format
+ *
+ * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the
+ * codec accordingly.
+ *
+ * Currently, this function only supports SND_SOC_DAIFMT_I2S and
+ * SND_SOC_DAIFMT_LEFT_J. The CS4270 codec also supports right-justified
+ * data for playback only, but ASoC currently does not support different
+ * formats for playback vs. record.
+ */
+static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+
+ /* set DAI format */
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ cs4270->mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ dev_err(codec->dev, "invalid dai format\n");
+ return -EINVAL;
+ }
+
+ /* set master/slave audio interface */
+ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs4270->slave_mode = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs4270->slave_mode = 0;
+ break;
+ default:
+ /* all other modes are unsupported by the hardware */
+ dev_err(codec->dev, "Unknown master/slave configuration\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * cs4270_hw_params - program the CS4270 with the given hardware parameters.
+ * @substream: the audio stream
+ * @params: the hardware parameters to set
+ * @dai: the SOC DAI (ignored)
+ *
+ * This function programs the hardware with the values provided.
+ * Specifically, the sample rate and the data format.
+ *
+ * The .ops functions are used to provide board-specific data, like input
+ * frequencies, to this driver. This function takes that information,
+ * combines it with the hardware parameters provided, and programs the
+ * hardware accordingly.
+ */
+static int cs4270_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+ unsigned int i;
+ unsigned int rate;
+ unsigned int ratio;
+ int reg;
+
+ /* Figure out which MCLK/LRCK ratio to use */
+
+ rate = params_rate(params); /* Sampling rate, in Hz */
+ ratio = cs4270->mclk / rate; /* MCLK/LRCK ratio */
+
+ for (i = 0; i < NUM_MCLK_RATIOS; i++) {
+ if (cs4270_mode_ratios[i].ratio == ratio)
+ break;
+ }
+
+ if (i == NUM_MCLK_RATIOS) {
+ /* We did not find a matching ratio */
+ dev_err(codec->dev, "could not find matching ratio\n");
+ return -EINVAL;
+ }
+
+ /* Set the sample rate */
+
+ reg = snd_soc_read(codec, CS4270_MODE);
+ reg &= ~(CS4270_MODE_SPEED_MASK | CS4270_MODE_DIV_MASK);
+ reg |= cs4270_mode_ratios[i].mclk;
+
+ if (cs4270->slave_mode)
+ reg |= CS4270_MODE_SLAVE;
+ else
+ reg |= cs4270_mode_ratios[i].speed_mode;
+
+ ret = snd_soc_write(codec, CS4270_MODE, reg);
+ if (ret < 0) {
+ dev_err(codec->dev, "i2c write failed\n");
+ return ret;
+ }
+
+ /* Set the DAI format */
+
+ reg = snd_soc_read(codec, CS4270_FORMAT);
+ reg &= ~(CS4270_FORMAT_DAC_MASK | CS4270_FORMAT_ADC_MASK);
+
+ switch (cs4270->mode) {
+ case SND_SOC_DAIFMT_I2S:
+ reg |= CS4270_FORMAT_DAC_I2S | CS4270_FORMAT_ADC_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg |= CS4270_FORMAT_DAC_LJ | CS4270_FORMAT_ADC_LJ;
+ break;
+ default:
+ dev_err(codec->dev, "unknown dai format\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_write(codec, CS4270_FORMAT, reg);
+ if (ret < 0) {
+ dev_err(codec->dev, "i2c write failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * cs4270_dai_mute - enable/disable the CS4270 external mute
+ * @dai: the SOC DAI
+ * @mute: 0 = disable mute, 1 = enable mute
+ *
+ * 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)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ int reg6;
+
+ reg6 = snd_soc_read(codec, CS4270_MUTE);
+
+ if (mute)
+ reg6 |= CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B;
+ else {
+ reg6 &= ~(CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B);
+ reg6 |= cs4270->manual_mute;
+ }
+
+ return snd_soc_write(codec, CS4270_MUTE, reg6);
+}
+
+/**
+ * cs4270_soc_put_mute - put callback for the 'Master Playback switch'
+ * alsa control.
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * This function basically passes the arguments on to the generic
+ * snd_soc_put_volsw() function and saves the mute information in
+ * our private data structure. This is because we want to prevent
+ * cs4270_dai_mute() neglecting the user's decision to manually
+ * mute the codec's output.
+ *
+ * Returns 0 for success.
+ */
+static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ int left = !ucontrol->value.integer.value[0];
+ int right = !ucontrol->value.integer.value[1];
+
+ cs4270->manual_mute = (left ? CS4270_MUTE_DAC_A : 0) |
+ (right ? CS4270_MUTE_DAC_B : 0);
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+/* A list of non-DAPM controls that the CS4270 supports */
+static const struct snd_kcontrol_new cs4270_snd_controls[] = {
+ SOC_DOUBLE_R("Master Playback Volume",
+ CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1),
+ SOC_SINGLE("Digital Sidetone Switch", CS4270_FORMAT, 5, 1, 0),
+ SOC_SINGLE("Soft Ramp Switch", CS4270_TRANS, 6, 1, 0),
+ SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0),
+ SOC_SINGLE("De-emphasis filter", CS4270_TRANS, 0, 1, 0),
+ SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1),
+ SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0),
+ SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1),
+ SOC_DOUBLE_EXT("Master Playback Switch", CS4270_MUTE, 0, 1, 1, 1,
+ snd_soc_get_volsw, cs4270_soc_put_mute),
+};
+
+static const struct snd_soc_dai_ops cs4270_dai_ops = {
+ .hw_params = cs4270_hw_params,
+ .set_sysclk = cs4270_set_dai_sysclk,
+ .set_fmt = cs4270_set_dai_fmt,
+ .digital_mute = cs4270_dai_mute,
+};
+
+static struct snd_soc_dai_driver cs4270_dai = {
+ .name = "cs4270-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 4000,
+ .rate_max = 216000,
+ .formats = CS4270_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 4000,
+ .rate_max = 216000,
+ .formats = CS4270_FORMATS,
+ },
+ .ops = &cs4270_dai_ops,
+};
+
+/**
+ * cs4270_probe - ASoC probe function
+ * @pdev: platform device
+ *
+ * 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)
+{
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ /* Disable auto-mute. This feature appears to be buggy. In some
+ * situations, auto-mute will not deactivate when it should, so we want
+ * this feature disabled by default. An application (e.g. alsactl) can
+ * re-enabled it by using the controls.
+ */
+ ret = snd_soc_update_bits(codec, CS4270_MUTE, CS4270_MUTE_AUTO, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "i2c write failed\n");
+ return ret;
+ }
+
+ /* Disable automatic volume control. The hardware enables, and it
+ * causes volume change commands to be delayed, sometimes until after
+ * playback has started. An application (e.g. alsactl) can
+ * re-enabled it by using the controls.
+ */
+ ret = snd_soc_update_bits(codec, CS4270_TRANS,
+ CS4270_TRANS_SOFT | CS4270_TRANS_ZERO, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "i2c write failed\n");
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
+ cs4270->supplies);
+
+ return ret;
+}
+
+/**
+ * cs4270_remove - ASoC remove function
+ * @pdev: platform device
+ *
+ * This function is the counterpart to cs4270_probe().
+ */
+static int cs4270_remove(struct snd_soc_codec *codec)
+{
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+
+ regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
+
+ return 0;
+};
+
+#ifdef CONFIG_PM
+
+/* This suspend/resume implementation can handle both - a simple standby
+ * where the codec remains powered, and a full suspend, where the voltage
+ * domain the codec is connected to is teared down and/or any other hardware
+ * reset condition is asserted.
+ *
+ * The codec's own power saving features are enabled in the suspend callback,
+ * and all registers are written back to the hardware when resuming.
+ */
+
+static int cs4270_soc_suspend(struct snd_soc_codec *codec)
+{
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ int reg, ret;
+
+ reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
+ if (reg < 0)
+ return reg;
+
+ ret = snd_soc_write(codec, CS4270_PWRCTL, reg);
+ if (ret < 0)
+ return ret;
+
+ regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies),
+ cs4270->supplies);
+
+ return 0;
+}
+
+static int cs4270_soc_resume(struct snd_soc_codec *codec)
+{
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ int reg, ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
+ cs4270->supplies);
+ if (ret != 0)
+ return ret;
+
+ /* In case the device was put to hard reset during sleep, we need to
+ * wait 500ns here before any I2C communication. */
+ ndelay(500);
+
+ /* first restore the entire register cache ... */
+ regcache_sync(cs4270->regmap);
+
+ /* ... then disable the power-down bits */
+ reg = snd_soc_read(codec, CS4270_PWRCTL);
+ reg &= ~CS4270_PWRCTL_PDN_ALL;
+
+ return snd_soc_write(codec, CS4270_PWRCTL, reg);
+}
+#else
+#define cs4270_soc_suspend NULL
+#define cs4270_soc_resume NULL
+#endif /* CONFIG_PM */
+
+/*
+ * ASoC codec driver structure
+ */
+static const struct snd_soc_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),
+};
+
+/*
+ * cs4270_of_match - the device tree bindings
+ */
+static const struct of_device_id cs4270_of_match[] = {
+ { .compatible = "cirrus,cs4270", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs4270_of_match);
+
+static const struct regmap_config cs4270_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = CS4270_LASTREG,
+ .reg_defaults = cs4270_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs4270_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .readable_reg = cs4270_reg_is_readable,
+ .volatile_reg = cs4270_reg_is_volatile,
+};
+
+/**
+ * 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)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ struct cs4270_private *cs4270;
+ unsigned int val;
+ int ret, i;
+
+ cs4270 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4270_private),
+ GFP_KERNEL);
+ if (!cs4270)
+ return -ENOMEM;
+
+ /* get the power supply regulators */
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ cs4270->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ ARRAY_SIZE(cs4270->supplies),
+ cs4270->supplies);
+ if (ret < 0)
+ return ret;
+
+ /* 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;
+ }
+ }
+
+ cs4270->regmap = devm_regmap_init_i2c(i2c_client, &cs4270_regmap);
+ if (IS_ERR(cs4270->regmap))
+ return PTR_ERR(cs4270->regmap);
+
+ /* Verify that we have a CS4270 */
+ ret = regmap_read(cs4270->regmap, CS4270_CHIPID, &val);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n",
+ i2c_client->addr);
+ return ret;
+ }
+ /* The top four bits of the chip ID should be 1100. */
+ if ((val & 0xF0) != 0xC0) {
+ dev_err(&i2c_client->dev, "device at addr %X is not a CS4270\n",
+ i2c_client->addr);
+ return -ENODEV;
+ }
+
+ dev_info(&i2c_client->dev, "found device at i2c address %X\n",
+ i2c_client->addr);
+ dev_info(&i2c_client->dev, "hardware revision %X\n", val & 0xF);
+
+ i2c_set_clientdata(i2c_client, cs4270);
+
+ ret = snd_soc_register_codec(&i2c_client->dev,
+ &soc_codec_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},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs4270_id);
+
+/*
+ * cs4270_i2c_driver - I2C device identification
+ *
+ * This structure tells the I2C subsystem how to identify and support a
+ * given I2C device type.
+ */
+static struct i2c_driver cs4270_i2c_driver = {
+ .driver = {
+ .name = "cs4270",
+ .owner = THIS_MODULE,
+ .of_match_table = cs4270_of_match,
+ },
+ .id_table = cs4270_id,
+ .probe = cs4270_i2c_probe,
+ .remove = cs4270_i2c_remove,
+};
+
+module_i2c_driver(cs4270_i2c_driver);
+
+MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
+MODULE_DESCRIPTION("Cirrus Logic CS4270 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs4271-i2c.c b/kernel/sound/soc/codecs/cs4271-i2c.c
new file mode 100644
index 000000000..b264da030
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs4271-i2c.c
@@ -0,0 +1,62 @@
+/*
+ * 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>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "cs4271.h"
+
+static int cs4271_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ 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 },
+ { }
+};
+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);
+
+MODULE_DESCRIPTION("ASoC CS4271 I2C Driver");
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs4271-spi.c b/kernel/sound/soc/codecs/cs4271-spi.c
new file mode 100644
index 000000000..acd49d86e
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs4271-spi.c
@@ -0,0 +1,55 @@
+/*
+ * 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>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "cs4271.h"
+
+static int cs4271_spi_probe(struct spi_device *spi)
+{
+ struct regmap_config config;
+
+ config = cs4271_regmap_config;
+ config.reg_bits = 16;
+ config.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);
+
+MODULE_DESCRIPTION("ASoC CS4271 SPI Driver");
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs4271.c b/kernel/sound/soc/codecs/cs4271.c
new file mode 100644
index 000000000..e770ee6f3
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs4271.c
@@ -0,0 +1,678 @@
+/*
+ * 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.
+ * DAPM support not implemented.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/cs4271.h>
+#include "cs4271.h"
+
+#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define CS4271_PCM_RATES SNDRV_PCM_RATE_8000_192000
+
+/*
+ * CS4271 registers
+ */
+#define CS4271_MODE1 0x01 /* Mode Control 1 */
+#define CS4271_DACCTL 0x02 /* DAC Control */
+#define CS4271_DACVOL 0x03 /* DAC Volume & Mixing Control */
+#define CS4271_VOLA 0x04 /* DAC Channel A Volume Control */
+#define CS4271_VOLB 0x05 /* DAC Channel B Volume Control */
+#define CS4271_ADCCTL 0x06 /* ADC Control */
+#define CS4271_MODE2 0x07 /* Mode Control 2 */
+#define CS4271_CHIPID 0x08 /* Chip ID */
+
+#define CS4271_FIRSTREG CS4271_MODE1
+#define CS4271_LASTREG CS4271_MODE2
+#define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1)
+
+/* Bit masks for the CS4271 registers */
+#define CS4271_MODE1_MODE_MASK 0xC0
+#define CS4271_MODE1_MODE_1X 0x00
+#define CS4271_MODE1_MODE_2X 0x80
+#define CS4271_MODE1_MODE_4X 0xC0
+
+#define CS4271_MODE1_DIV_MASK 0x30
+#define CS4271_MODE1_DIV_1 0x00
+#define CS4271_MODE1_DIV_15 0x10
+#define CS4271_MODE1_DIV_2 0x20
+#define CS4271_MODE1_DIV_3 0x30
+
+#define CS4271_MODE1_MASTER 0x08
+
+#define CS4271_MODE1_DAC_DIF_MASK 0x07
+#define CS4271_MODE1_DAC_DIF_LJ 0x00
+#define CS4271_MODE1_DAC_DIF_I2S 0x01
+#define CS4271_MODE1_DAC_DIF_RJ16 0x02
+#define CS4271_MODE1_DAC_DIF_RJ24 0x03
+#define CS4271_MODE1_DAC_DIF_RJ20 0x04
+#define CS4271_MODE1_DAC_DIF_RJ18 0x05
+
+#define CS4271_DACCTL_AMUTE 0x80
+#define CS4271_DACCTL_IF_SLOW 0x40
+
+#define CS4271_DACCTL_DEM_MASK 0x30
+#define CS4271_DACCTL_DEM_DIS 0x00
+#define CS4271_DACCTL_DEM_441 0x10
+#define CS4271_DACCTL_DEM_48 0x20
+#define CS4271_DACCTL_DEM_32 0x30
+
+#define CS4271_DACCTL_SVRU 0x08
+#define CS4271_DACCTL_SRD 0x04
+#define CS4271_DACCTL_INVA 0x02
+#define CS4271_DACCTL_INVB 0x01
+
+#define CS4271_DACVOL_BEQUA 0x40
+#define CS4271_DACVOL_SOFT 0x20
+#define CS4271_DACVOL_ZEROC 0x10
+
+#define CS4271_DACVOL_ATAPI_MASK 0x0F
+#define CS4271_DACVOL_ATAPI_M_M 0x00
+#define CS4271_DACVOL_ATAPI_M_BR 0x01
+#define CS4271_DACVOL_ATAPI_M_BL 0x02
+#define CS4271_DACVOL_ATAPI_M_BLR2 0x03
+#define CS4271_DACVOL_ATAPI_AR_M 0x04
+#define CS4271_DACVOL_ATAPI_AR_BR 0x05
+#define CS4271_DACVOL_ATAPI_AR_BL 0x06
+#define CS4271_DACVOL_ATAPI_AR_BLR2 0x07
+#define CS4271_DACVOL_ATAPI_AL_M 0x08
+#define CS4271_DACVOL_ATAPI_AL_BR 0x09
+#define CS4271_DACVOL_ATAPI_AL_BL 0x0A
+#define CS4271_DACVOL_ATAPI_AL_BLR2 0x0B
+#define CS4271_DACVOL_ATAPI_ALR2_M 0x0C
+#define CS4271_DACVOL_ATAPI_ALR2_BR 0x0D
+#define CS4271_DACVOL_ATAPI_ALR2_BL 0x0E
+#define CS4271_DACVOL_ATAPI_ALR2_BLR2 0x0F
+
+#define CS4271_VOLA_MUTE 0x80
+#define CS4271_VOLA_VOL_MASK 0x7F
+#define CS4271_VOLB_MUTE 0x80
+#define CS4271_VOLB_VOL_MASK 0x7F
+
+#define CS4271_ADCCTL_DITHER16 0x20
+
+#define CS4271_ADCCTL_ADC_DIF_MASK 0x10
+#define CS4271_ADCCTL_ADC_DIF_LJ 0x00
+#define CS4271_ADCCTL_ADC_DIF_I2S 0x10
+
+#define CS4271_ADCCTL_MUTEA 0x08
+#define CS4271_ADCCTL_MUTEB 0x04
+#define CS4271_ADCCTL_HPFDA 0x02
+#define CS4271_ADCCTL_HPFDB 0x01
+
+#define CS4271_MODE2_LOOP 0x10
+#define CS4271_MODE2_MUTECAEQUB 0x08
+#define CS4271_MODE2_FREEZE 0x04
+#define CS4271_MODE2_CPEN 0x02
+#define CS4271_MODE2_PDN 0x01
+
+#define CS4271_CHIPID_PART_MASK 0xF0
+#define CS4271_CHIPID_REV_MASK 0x0F
+
+/*
+ * Default CS4271 power-up configuration
+ * Array contains non-existing in hw register at address 0
+ * Array do not include Chip ID, as codec driver does not use
+ * registers read operations at all
+ */
+static const struct reg_default cs4271_reg_defaults[] = {
+ { CS4271_MODE1, 0, },
+ { CS4271_DACCTL, CS4271_DACCTL_AMUTE, },
+ { CS4271_DACVOL, CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR, },
+ { CS4271_VOLA, 0, },
+ { CS4271_VOLB, 0, },
+ { CS4271_ADCCTL, 0, },
+ { CS4271_MODE2, 0, },
+};
+
+static bool cs4271_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg == CS4271_CHIPID;
+}
+
+struct cs4271_private {
+ unsigned int mclk;
+ bool master;
+ bool deemph;
+ struct regmap *regmap;
+ /* Current sample rate for de-emphasis control */
+ int rate;
+ /* GPIO driving Reset pin, if any */
+ int gpio_nreset;
+ /* GPIO that disable serial bus, if any */
+ int gpio_disable;
+ /* enable soft reset workaround */
+ bool enable_soft_reset;
+};
+
+static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINA"),
+SND_SOC_DAPM_INPUT("AINB"),
+
+SND_SOC_DAPM_OUTPUT("AOUTA+"),
+SND_SOC_DAPM_OUTPUT("AOUTA-"),
+SND_SOC_DAPM_OUTPUT("AOUTB+"),
+SND_SOC_DAPM_OUTPUT("AOUTB-"),
+};
+
+static const struct snd_soc_dapm_route cs4271_dapm_routes[] = {
+ { "Capture", NULL, "AINA" },
+ { "Capture", NULL, "AINB" },
+
+ { "AOUTA+", NULL, "Playback" },
+ { "AOUTA-", NULL, "Playback" },
+ { "AOUTB+", NULL, "Playback" },
+ { "AOUTB-", NULL, "Playback" },
+};
+
+/*
+ * @freq is the desired MCLK rate
+ * MCLK rate should (c) be the sample rate, multiplied by one of the
+ * ratios listed in cs4271_mclk_fs_ratios table
+ */
+static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+ cs4271->mclk = freq;
+ return 0;
+}
+
+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);
+ unsigned int val = 0;
+ int ret;
+
+ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs4271->master = 0;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs4271->master = 1;
+ val |= CS4271_MODE1_MASTER;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ val |= CS4271_MODE1_DAC_DIF_LJ;
+ ret = regmap_update_bits(cs4271->regmap, CS4271_ADCCTL,
+ CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ);
+ if (ret < 0)
+ return ret;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val |= CS4271_MODE1_DAC_DIF_I2S;
+ ret = regmap_update_bits(cs4271->regmap, CS4271_ADCCTL,
+ CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE1,
+ CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int cs4271_deemph[] = {0, 44100, 48000, 32000};
+
+static int cs4271_set_deemph(struct snd_soc_codec *codec)
+{
+ struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
+ int val = CS4271_DACCTL_DEM_DIS;
+
+ if (cs4271->deemph) {
+ /* Find closest de-emphasis freq */
+ val = 1;
+ for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++)
+ if (abs(cs4271_deemph[i] - cs4271->rate) <
+ abs(cs4271_deemph[val] - cs4271->rate))
+ val = i;
+ val <<= 4;
+ }
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_DACCTL,
+ CS4271_DACCTL_DEM_MASK, val);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+ 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);
+
+ cs4271->deemph = ucontrol->value.integer.value[0];
+ return cs4271_set_deemph(codec);
+}
+
+struct cs4271_clk_cfg {
+ bool master; /* codec mode */
+ u8 speed_mode; /* codec speed mode: 1x, 2x, 4x */
+ unsigned short ratio; /* MCLK / sample rate */
+ u8 ratio_mask; /* ratio bit mask for Master mode */
+};
+
+static struct cs4271_clk_cfg cs4271_clk_tab[] = {
+ {1, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1},
+ {1, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_15},
+ {1, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_2},
+ {1, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_3},
+ {1, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1},
+ {1, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_15},
+ {1, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_2},
+ {1, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_3},
+ {1, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1},
+ {1, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_15},
+ {1, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_2},
+ {1, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_3},
+ {0, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_2},
+ {0, CS4271_MODE1_MODE_1X, 1024, CS4271_MODE1_DIV_2},
+ {0, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_2},
+ {0, CS4271_MODE1_MODE_2X, 512, CS4271_MODE1_DIV_2},
+ {0, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_1},
+ {0, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_2},
+ {0, CS4271_MODE1_MODE_4X, 256, CS4271_MODE1_DIV_2},
+};
+
+#define CS4171_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);
+ int i, ret;
+ unsigned int ratio, val;
+
+ if (cs4271->enable_soft_reset) {
+ /*
+ * Put the codec in soft reset and back again in case it's not
+ * currently streaming data. This way of bringing the codec in
+ * sync to the current clocks is not explicitly documented in
+ * the data sheet, but it seems to work fine, and in contrast
+ * to a read hardware reset, we don't have to sync back all
+ * registers every time.
+ */
+
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ !dai->capture_active) ||
+ (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+ !dai->playback_active)) {
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_PDN,
+ CS4271_MODE2_PDN);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_PDN, 0);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ cs4271->rate = params_rate(params);
+
+ /* Configure DAC */
+ if (cs4271->rate < 50000)
+ val = CS4271_MODE1_MODE_1X;
+ else if (cs4271->rate < 100000)
+ val = CS4271_MODE1_MODE_2X;
+ else
+ val = CS4271_MODE1_MODE_4X;
+
+ ratio = cs4271->mclk / cs4271->rate;
+ for (i = 0; i < CS4171_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");
+ return -EINVAL;
+ }
+
+ val |= cs4271_clk_tab[i].ratio_mask;
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE1,
+ CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return cs4271_set_deemph(codec);
+}
+
+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);
+ int ret;
+ int val_a = 0;
+ int val_b = 0;
+
+ if (stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ if (mute) {
+ val_a = CS4271_VOLA_MUTE;
+ val_b = CS4271_VOLB_MUTE;
+ }
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_VOLA,
+ CS4271_VOLA_MUTE, val_a);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_VOLB,
+ CS4271_VOLB_MUTE, val_b);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* CS4271 controls */
+static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0);
+
+static const struct snd_kcontrol_new cs4271_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume", CS4271_VOLA, CS4271_VOLB,
+ 0, 0x7F, 1, cs4271_dac_tlv),
+ SOC_SINGLE("Digital Loopback Switch", CS4271_MODE2, 4, 1, 0),
+ SOC_SINGLE("Soft Ramp Switch", CS4271_DACVOL, 5, 1, 0),
+ SOC_SINGLE("Zero Cross Switch", CS4271_DACVOL, 4, 1, 0),
+ SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
+ cs4271_get_deemph, cs4271_put_deemph),
+ SOC_SINGLE("Auto-Mute Switch", CS4271_DACCTL, 7, 1, 0),
+ SOC_SINGLE("Slow Roll Off Filter Switch", CS4271_DACCTL, 6, 1, 0),
+ SOC_SINGLE("Soft Volume Ramp-Up Switch", CS4271_DACCTL, 3, 1, 0),
+ SOC_SINGLE("Soft Ramp-Down Switch", CS4271_DACCTL, 2, 1, 0),
+ SOC_SINGLE("Left Channel Inversion Switch", CS4271_DACCTL, 1, 1, 0),
+ SOC_SINGLE("Right Channel Inversion Switch", CS4271_DACCTL, 0, 1, 0),
+ SOC_DOUBLE("Master Capture Switch", CS4271_ADCCTL, 3, 2, 1, 1),
+ SOC_SINGLE("Dither 16-Bit Data Switch", CS4271_ADCCTL, 5, 1, 0),
+ SOC_DOUBLE("High Pass Filter Switch", CS4271_ADCCTL, 1, 0, 1, 1),
+ SOC_DOUBLE_R("Master Playback Switch", CS4271_VOLA, CS4271_VOLB,
+ 7, 1, 1),
+};
+
+static const struct snd_soc_dai_ops cs4271_dai_ops = {
+ .hw_params = cs4271_hw_params,
+ .set_sysclk = cs4271_set_dai_sysclk,
+ .set_fmt = cs4271_set_dai_fmt,
+ .mute_stream = cs4271_mute_stream,
+};
+
+static struct snd_soc_dai_driver cs4271_dai = {
+ .name = "cs4271-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CS4271_PCM_RATES,
+ .formats = CS4271_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CS4271_PCM_RATES,
+ .formats = CS4271_PCM_FORMATS,
+ },
+ .ops = &cs4271_dai_ops,
+ .symmetric_rates = 1,
+};
+
+#ifdef CONFIG_PM
+static int cs4271_soc_suspend(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+ /* Set power-down bit */
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_PDN, CS4271_MODE2_PDN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cs4271_soc_resume(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+ /* Restore codec state */
+ ret = regcache_sync(cs4271->regmap);
+ if (ret < 0)
+ return ret;
+
+ /* then disable the power-down bit */
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_PDN, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+#else
+#define cs4271_soc_suspend NULL
+#define cs4271_soc_resume NULL
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_OF
+const struct of_device_id cs4271_dt_ids[] = {
+ { .compatible = "cirrus,cs4271", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs4271_dt_ids);
+EXPORT_SYMBOL_GPL(cs4271_dt_ids);
+#endif
+
+static int cs4271_codec_probe(struct snd_soc_codec *codec)
+{
+ struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
+ int ret;
+ bool amutec_eq_bmutec = false;
+
+#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;
+ }
+#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);
+ mdelay(1);
+ gpio_set_value(cs4271->gpio_nreset, 1);
+ /* Give the codec time to wake up */
+ mdelay(1);
+ }
+
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_PDN | CS4271_MODE2_CPEN,
+ CS4271_MODE2_PDN | CS4271_MODE2_CPEN);
+ if (ret < 0)
+ return ret;
+ ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_PDN, 0);
+ if (ret < 0)
+ return ret;
+ /* Power-up sequence requires 85 uS */
+ udelay(85);
+
+ if (amutec_eq_bmutec)
+ regmap_update_bits(cs4271->regmap, CS4271_MODE2,
+ CS4271_MODE2_MUTECAEQUB,
+ CS4271_MODE2_MUTECAEQUB);
+
+ return 0;
+}
+
+static int cs4271_codec_remove(struct snd_soc_codec *codec)
+{
+ struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+ if (gpio_is_valid(cs4271->gpio_nreset))
+ /* Set codec to the reset state */
+ gpio_set_value(cs4271->gpio_nreset, 0);
+
+ return 0;
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
+ .probe = cs4271_codec_probe,
+ .remove = cs4271_codec_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),
+};
+
+static int cs4271_common_probe(struct device *dev,
+ struct cs4271_private **c)
+{
+ struct cs4271_platform_data *cs4271plat = dev->platform_data;
+ struct cs4271_private *cs4271;
+
+ cs4271 = devm_kzalloc(dev, sizeof(*cs4271), GFP_KERNEL);
+ if (!cs4271)
+ return -ENOMEM;
+
+ if (of_match_device(cs4271_dt_ids, dev))
+ cs4271->gpio_nreset =
+ of_get_named_gpio(dev->of_node, "reset-gpio", 0);
+
+ if (cs4271plat)
+ cs4271->gpio_nreset = cs4271plat->gpio_nreset;
+
+ if (gpio_is_valid(cs4271->gpio_nreset)) {
+ int ret;
+
+ ret = devm_gpio_request(dev, cs4271->gpio_nreset,
+ "CS4271 Reset");
+ if (ret < 0)
+ return ret;
+ }
+
+ *c = cs4271;
+ return 0;
+}
+
+const struct regmap_config cs4271_regmap_config = {
+ .max_register = CS4271_LASTREG,
+
+ .reg_defaults = cs4271_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = cs4271_volatile_reg,
+};
+EXPORT_SYMBOL_GPL(cs4271_regmap_config);
+
+int cs4271_probe(struct device *dev, struct regmap *regmap)
+{
+ struct cs4271_private *cs4271;
+ int ret;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = cs4271_common_probe(dev, &cs4271);
+ if (ret < 0)
+ return ret;
+
+ dev_set_drvdata(dev, cs4271);
+ cs4271->regmap = regmap;
+
+ return snd_soc_register_codec(dev, &soc_codec_dev_cs4271, &cs4271_dai,
+ 1);
+}
+EXPORT_SYMBOL_GPL(cs4271_probe);
+
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs4271.h b/kernel/sound/soc/codecs/cs4271.h
new file mode 100644
index 000000000..9adad8eef
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs4271.h
@@ -0,0 +1,11 @@
+#ifndef _CS4271_PRIV_H
+#define _CS4271_PRIV_H
+
+#include <linux/regmap.h>
+
+extern const struct of_device_id cs4271_dt_ids[];
+extern const struct regmap_config cs4271_regmap_config;
+
+int cs4271_probe(struct device *dev, struct regmap *regmap);
+
+#endif
diff --git a/kernel/sound/soc/codecs/cs42l51-i2c.c b/kernel/sound/soc/codecs/cs42l51-i2c.c
new file mode 100644
index 000000000..c40428f25
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs42l51-i2c.c
@@ -0,0 +1,60 @@
+/*
+ * 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>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "cs42l51.h"
+
+static struct i2c_device_id cs42l51_i2c_id[] = {
+ {"cs42l51", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);
+
+static int cs42l51_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ 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)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+
+ return 0;
+}
+
+static struct i2c_driver cs42l51_i2c_driver = {
+ .driver = {
+ .name = "cs42l51",
+ .owner = THIS_MODULE,
+ .of_match_table = cs42l51_of_match,
+ },
+ .probe = cs42l51_i2c_probe,
+ .remove = cs42l51_i2c_remove,
+ .id_table = cs42l51_i2c_id,
+};
+
+module_i2c_driver(cs42l51_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L51 I2C Driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs42l51.c b/kernel/sound/soc/codecs/cs42l51.c
new file mode 100644
index 000000000..b39515243
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs42l51.c
@@ -0,0 +1,572 @@
+/*
+ * cs42l51.c
+ *
+ * ASoC Driver for Cirrus Logic CS42L51 codecs
+ *
+ * Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com>
+ *
+ * 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/module.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm.h>
+#include <linux/regmap.h>
+
+#include "cs42l51.h"
+
+enum master_slave_mode {
+ MODE_SLAVE,
+ MODE_SLAVE_AUTO,
+ MODE_MASTER,
+};
+
+struct cs42l51_private {
+ unsigned int mclk;
+ unsigned int audio_mode; /* The mode (I2S or left-justified) */
+ enum master_slave_mode func;
+};
+
+#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)
+
+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;
+
+ switch (value) {
+ default:
+ case 0:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ /* same value : (L+R)/2 and (R+L)/2 */
+ case 1:
+ case 2:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case 3:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ }
+
+ return 0;
+}
+
+#define CHAN_MIX_NORMAL 0x00
+#define CHAN_MIX_BOTH 0x55
+#define CHAN_MIX_SWAP 0xFF
+
+static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned char val;
+
+ switch (ucontrol->value.integer.value[0]) {
+ default:
+ case 0:
+ val = CHAN_MIX_NORMAL;
+ break;
+ case 1:
+ val = CHAN_MIX_BOTH;
+ break;
+ case 2:
+ val = CHAN_MIX_SWAP;
+ break;
+ }
+
+ snd_soc_write(codec, CS42L51_PCM_MIXER, val);
+
+ return 1;
+}
+
+static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0);
+static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
+
+static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0);
+
+static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
+static const char *chan_mix[] = {
+ "L R",
+ "L+R",
+ "R L",
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix);
+
+static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
+ SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
+ CS42L51_PCMA_VOL, CS42L51_PCMB_VOL,
+ 0, 0x19, 0x7F, adc_pcm_tlv),
+ SOC_DOUBLE_R("PCM Playback Switch",
+ CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1),
+ SOC_DOUBLE_R_SX_TLV("Analog Playback Volume",
+ CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL,
+ 0, 0x34, 0xE4, aout_tlv),
+ SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
+ CS42L51_ADCA_VOL, CS42L51_ADCB_VOL,
+ 0, 0x19, 0x7F, adc_pcm_tlv),
+ SOC_DOUBLE_R("ADC Mixer Switch",
+ CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
+ SOC_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_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
+ SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
+ SOC_ENUM_EXT("PCM channel mixer",
+ cs42l51_chan_mix,
+ cs42l51_get_chan_mix, cs42l51_set_chan_mix),
+};
+
+/*
+ * to power down, one must:
+ * 1.) Enable the PDN bit
+ * 2.) enable power-down for the select channels
+ * 3.) disable the PDN bit.
+ */
+static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, 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,
+ CS42L51_POWER_CTL1_PDN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static const char *cs42l51_dac_names[] = {"Direct PCM",
+ "DSP PCM", "ADC"};
+static SOC_ENUM_SINGLE_DECL(cs42l51_dac_mux_enum,
+ CS42L51_DAC_CTL, 6, cs42l51_dac_names);
+static const struct snd_kcontrol_new cs42l51_dac_mux_controls =
+ SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum);
+
+static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left",
+ "MIC Left", "MIC+preamp Left"};
+static SOC_ENUM_SINGLE_DECL(cs42l51_adcl_mux_enum,
+ CS42L51_ADC_INPUT, 4, cs42l51_adcl_names);
+static const struct snd_kcontrol_new cs42l51_adcl_mux_controls =
+ SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum);
+
+static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right",
+ "MIC Right", "MIC+preamp Right"};
+static SOC_ENUM_SINGLE_DECL(cs42l51_adcr_mux_enum,
+ CS42L51_ADC_INPUT, 6, cs42l51_adcr_names);
+static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
+ SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
+
+static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
+ SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
+ SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
+ cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
+ cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_ADC_E("Left ADC", "Left HiFi Capture",
+ CS42L51_POWER_CTL1, 1, 1,
+ cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture",
+ CS42L51_POWER_CTL1, 2, 1,
+ cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+ SND_SOC_DAPM_DAC_E("Left DAC", "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),
+
+ /* analog/mic */
+ SND_SOC_DAPM_INPUT("AIN1L"),
+ SND_SOC_DAPM_INPUT("AIN1R"),
+ SND_SOC_DAPM_INPUT("AIN2L"),
+ SND_SOC_DAPM_INPUT("AIN2R"),
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+
+ SND_SOC_DAPM_MIXER("Mic Preamp Left",
+ CS42L51_MIC_POWER_CTL, 2, 1, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mic Preamp Right",
+ CS42L51_MIC_POWER_CTL, 3, 1, NULL, 0),
+
+ /* HP */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+
+ /* mux */
+ SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0,
+ &cs42l51_dac_mux_controls),
+ SND_SOC_DAPM_MUX("PGA-ADC Mux Left", SND_SOC_NOPM, 0, 0,
+ &cs42l51_adcl_mux_controls),
+ SND_SOC_DAPM_MUX("PGA-ADC Mux Right", SND_SOC_NOPM, 0, 0,
+ &cs42l51_adcr_mux_controls),
+};
+
+static const struct snd_soc_dapm_route cs42l51_routes[] = {
+ {"HPL", NULL, "Left DAC"},
+ {"HPR", NULL, "Right DAC"},
+
+ {"Left ADC", NULL, "Left PGA"},
+ {"Right ADC", NULL, "Right PGA"},
+
+ {"Mic Preamp Left", NULL, "MICL"},
+ {"Mic Preamp Right", NULL, "MICR"},
+
+ {"PGA-ADC Mux Left", "AIN1 Left", "AIN1L" },
+ {"PGA-ADC Mux Left", "AIN2 Left", "AIN2L" },
+ {"PGA-ADC Mux Left", "MIC Left", "MICL" },
+ {"PGA-ADC Mux Left", "MIC+preamp Left", "Mic Preamp Left" },
+ {"PGA-ADC Mux Right", "AIN1 Right", "AIN1R" },
+ {"PGA-ADC Mux Right", "AIN2 Right", "AIN2R" },
+ {"PGA-ADC Mux Right", "MIC Right", "MICR" },
+ {"PGA-ADC Mux Right", "MIC+preamp Right", "Mic Preamp Right" },
+
+ {"Left PGA", NULL, "PGA-ADC Mux Left"},
+ {"Right PGA", NULL, "PGA-ADC Mux Right"},
+};
+
+static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
+
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ dev_err(codec->dev, "invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs42l51->func = MODE_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs42l51->func = MODE_SLAVE_AUTO;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown master/slave configuration\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct cs42l51_ratios {
+ unsigned int ratio;
+ unsigned char speed_mode;
+ unsigned char mclk;
+};
+
+static struct cs42l51_ratios slave_ratios[] = {
+ { 512, CS42L51_QSM_MODE, 0 }, { 768, CS42L51_QSM_MODE, 0 },
+ { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
+ { 2048, CS42L51_QSM_MODE, 0 }, { 3072, CS42L51_QSM_MODE, 0 },
+ { 256, CS42L51_HSM_MODE, 0 }, { 384, CS42L51_HSM_MODE, 0 },
+ { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 },
+ { 1024, CS42L51_HSM_MODE, 0 }, { 1536, CS42L51_HSM_MODE, 0 },
+ { 128, CS42L51_SSM_MODE, 0 }, { 192, CS42L51_SSM_MODE, 0 },
+ { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 },
+ { 512, CS42L51_SSM_MODE, 0 }, { 768, CS42L51_SSM_MODE, 0 },
+ { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 },
+ { 256, CS42L51_DSM_MODE, 0 }, { 384, CS42L51_DSM_MODE, 0 },
+};
+
+static struct cs42l51_ratios slave_auto_ratios[] = {
+ { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
+ { 2048, CS42L51_QSM_MODE, 1 }, { 3072, CS42L51_QSM_MODE, 1 },
+ { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 },
+ { 1024, CS42L51_HSM_MODE, 1 }, { 1536, CS42L51_HSM_MODE, 1 },
+ { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 },
+ { 512, CS42L51_SSM_MODE, 1 }, { 768, CS42L51_SSM_MODE, 1 },
+ { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 },
+ { 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 },
+};
+
+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);
+
+ cs42l51->mclk = freq;
+ return 0;
+}
+
+static int cs42l51_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
+ 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;
+
+ switch (cs42l51->func) {
+ case MODE_MASTER:
+ return -EINVAL;
+ case MODE_SLAVE:
+ ratios = slave_ratios;
+ nr_ratios = ARRAY_SIZE(slave_ratios);
+ break;
+ case MODE_SLAVE_AUTO:
+ ratios = slave_auto_ratios;
+ nr_ratios = ARRAY_SIZE(slave_auto_ratios);
+ break;
+ }
+
+ /* Figure out which MCLK/LRCK ratio to use */
+ rate = params_rate(params); /* Sampling rate, in Hz */
+ ratio = cs42l51->mclk / rate; /* MCLK/LRCK ratio */
+ for (i = 0; i < nr_ratios; i++) {
+ if (ratios[i].ratio == ratio)
+ break;
+ }
+
+ if (i == nr_ratios) {
+ /* We did not find a matching ratio */
+ dev_err(codec->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 &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S
+ | CS42L51_INTF_CTL_DAC_FORMAT(7));
+ power_ctl &= ~(CS42L51_MIC_POWER_CTL_SPEED(3)
+ | CS42L51_MIC_POWER_CTL_MCLK_DIV2);
+
+ switch (cs42l51->func) {
+ case MODE_MASTER:
+ intf_ctl |= CS42L51_INTF_CTL_MASTER;
+ power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
+ break;
+ case MODE_SLAVE:
+ power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
+ break;
+ case MODE_SLAVE_AUTO:
+ power_ctl |= CS42L51_MIC_POWER_CTL_AUTO;
+ break;
+ }
+
+ switch (cs42l51->audio_mode) {
+ case SND_SOC_DAIFMT_I2S:
+ intf_ctl |= CS42L51_INTF_CTL_ADC_I2S;
+ intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_I2S);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ fmt = CS42L51_DAC_DIF_RJ16;
+ break;
+ case 18:
+ fmt = CS42L51_DAC_DIF_RJ18;
+ break;
+ case 20:
+ fmt = CS42L51_DAC_DIF_RJ20;
+ break;
+ case 24:
+ fmt = CS42L51_DAC_DIF_RJ24;
+ break;
+ default:
+ dev_err(codec->dev, "unknown format\n");
+ return -EINVAL;
+ }
+ intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt);
+ break;
+ default:
+ dev_err(codec->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);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_write(codec, 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)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ 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);
+
+ if (mute)
+ reg |= mask;
+ else
+ reg &= ~mask;
+
+ return snd_soc_write(codec, CS42L51_DAC_OUT_CTL, reg);
+}
+
+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,
+};
+
+static struct snd_soc_dai_driver cs42l51_dai = {
+ .name = "cs42l51-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = CS42L51_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = CS42L51_FORMATS,
+ },
+ .ops = &cs42l51_dai_ops,
+};
+
+static int cs42l51_codec_probe(struct snd_soc_codec *codec)
+{
+ int ret, reg;
+
+ /*
+ * DAC configuration
+ * - Use signal processor
+ * - auto mute
+ * - vol changes immediate
+ * - no de-emphasize
+ */
+ reg = CS42L51_DAC_CTL_DATA_SEL(1)
+ | CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0);
+ ret = snd_soc_write(codec, 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),
+};
+
+const struct regmap_config cs42l51_regmap = {
+ .max_register = CS42L51_CHARGE_FREQ,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(cs42l51_regmap);
+
+int cs42l51_probe(struct device *dev, struct regmap *regmap)
+{
+ struct cs42l51_private *cs42l51;
+ unsigned int val;
+ int ret;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ cs42l51 = devm_kzalloc(dev, sizeof(struct cs42l51_private),
+ GFP_KERNEL);
+ if (!cs42l51)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, cs42l51);
+
+ /* Verify that we have a CS42L51 */
+ ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
+ if (ret < 0) {
+ dev_err(dev, "failed to read I2C\n");
+ goto error;
+ }
+
+ if ((val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
+ (val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
+ dev_err(dev, "Invalid chip id: %x\n", val);
+ ret = -ENODEV;
+ goto error;
+ }
+ dev_info(dev, "Cirrus Logic CS42L51, Revision: %02X\n",
+ val & CS42L51_CHIP_REV_MASK);
+
+ ret = snd_soc_register_codec(dev,
+ &soc_codec_device_cs42l51, &cs42l51_dai, 1);
+error:
+ 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);
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
+MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs42l51.h b/kernel/sound/soc/codecs/cs42l51.h
new file mode 100644
index 000000000..0ca805492
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs42l51.h
@@ -0,0 +1,168 @@
+/*
+ * 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
+
+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[];
+
+#define CS42L51_CHIP_ID 0x1B
+#define CS42L51_CHIP_REV_A 0x00
+#define CS42L51_CHIP_REV_B 0x01
+#define CS42L51_CHIP_REV_MASK 0x07
+
+#define CS42L51_CHIP_REV_ID 0x01
+#define CS42L51_MK_CHIP_REV(a, b) ((a)<<3|(b))
+
+#define CS42L51_POWER_CTL1 0x02
+#define CS42L51_POWER_CTL1_PDN_DACB (1<<6)
+#define CS42L51_POWER_CTL1_PDN_DACA (1<<5)
+#define CS42L51_POWER_CTL1_PDN_PGAB (1<<4)
+#define CS42L51_POWER_CTL1_PDN_PGAA (1<<3)
+#define CS42L51_POWER_CTL1_PDN_ADCB (1<<2)
+#define CS42L51_POWER_CTL1_PDN_ADCA (1<<1)
+#define CS42L51_POWER_CTL1_PDN (1<<0)
+
+#define CS42L51_MIC_POWER_CTL 0x03
+#define CS42L51_MIC_POWER_CTL_AUTO (1<<7)
+#define CS42L51_MIC_POWER_CTL_SPEED(x) (((x)&3)<<5)
+#define CS42L51_QSM_MODE 3
+#define CS42L51_HSM_MODE 2
+#define CS42L51_SSM_MODE 1
+#define CS42L51_DSM_MODE 0
+#define CS42L51_MIC_POWER_CTL_3ST_SP (1<<4)
+#define CS42L51_MIC_POWER_CTL_PDN_MICB (1<<3)
+#define CS42L51_MIC_POWER_CTL_PDN_MICA (1<<2)
+#define CS42L51_MIC_POWER_CTL_PDN_BIAS (1<<1)
+#define CS42L51_MIC_POWER_CTL_MCLK_DIV2 (1<<0)
+
+#define CS42L51_INTF_CTL 0x04
+#define CS42L51_INTF_CTL_LOOPBACK (1<<7)
+#define CS42L51_INTF_CTL_MASTER (1<<6)
+#define CS42L51_INTF_CTL_DAC_FORMAT(x) (((x)&7)<<3)
+#define CS42L51_DAC_DIF_LJ24 0x00
+#define CS42L51_DAC_DIF_I2S 0x01
+#define CS42L51_DAC_DIF_RJ24 0x02
+#define CS42L51_DAC_DIF_RJ20 0x03
+#define CS42L51_DAC_DIF_RJ18 0x04
+#define CS42L51_DAC_DIF_RJ16 0x05
+#define CS42L51_INTF_CTL_ADC_I2S (1<<2)
+#define CS42L51_INTF_CTL_DIGMIX (1<<1)
+#define CS42L51_INTF_CTL_MICMIX (1<<0)
+
+#define CS42L51_MIC_CTL 0x05
+#define CS42L51_MIC_CTL_ADC_SNGVOL (1<<7)
+#define CS42L51_MIC_CTL_ADCD_DBOOST (1<<6)
+#define CS42L51_MIC_CTL_ADCA_DBOOST (1<<5)
+#define CS42L51_MIC_CTL_MICBIAS_SEL (1<<4)
+#define CS42L51_MIC_CTL_MICBIAS_LVL(x) (((x)&3)<<2)
+#define CS42L51_MIC_CTL_MICB_BOOST (1<<1)
+#define CS42L51_MIC_CTL_MICA_BOOST (1<<0)
+
+#define CS42L51_ADC_CTL 0x06
+#define CS42L51_ADC_CTL_ADCB_HPFEN (1<<7)
+#define CS42L51_ADC_CTL_ADCB_HPFRZ (1<<6)
+#define CS42L51_ADC_CTL_ADCA_HPFEN (1<<5)
+#define CS42L51_ADC_CTL_ADCA_HPFRZ (1<<4)
+#define CS42L51_ADC_CTL_SOFTB (1<<3)
+#define CS42L51_ADC_CTL_ZCROSSB (1<<2)
+#define CS42L51_ADC_CTL_SOFTA (1<<1)
+#define CS42L51_ADC_CTL_ZCROSSA (1<<0)
+
+#define CS42L51_ADC_INPUT 0x07
+#define CS42L51_ADC_INPUT_AINB_MUX(x) (((x)&3)<<6)
+#define CS42L51_ADC_INPUT_AINA_MUX(x) (((x)&3)<<4)
+#define CS42L51_ADC_INPUT_INV_ADCB (1<<3)
+#define CS42L51_ADC_INPUT_INV_ADCA (1<<2)
+#define CS42L51_ADC_INPUT_ADCB_MUTE (1<<1)
+#define CS42L51_ADC_INPUT_ADCA_MUTE (1<<0)
+
+#define CS42L51_DAC_OUT_CTL 0x08
+#define CS42L51_DAC_OUT_CTL_HP_GAIN(x) (((x)&7)<<5)
+#define CS42L51_DAC_OUT_CTL_DAC_SNGVOL (1<<4)
+#define CS42L51_DAC_OUT_CTL_INV_PCMB (1<<3)
+#define CS42L51_DAC_OUT_CTL_INV_PCMA (1<<2)
+#define CS42L51_DAC_OUT_CTL_DACB_MUTE (1<<1)
+#define CS42L51_DAC_OUT_CTL_DACA_MUTE (1<<0)
+
+#define CS42L51_DAC_CTL 0x09
+#define CS42L51_DAC_CTL_DATA_SEL(x) (((x)&3)<<6)
+#define CS42L51_DAC_CTL_FREEZE (1<<5)
+#define CS42L51_DAC_CTL_DEEMPH (1<<3)
+#define CS42L51_DAC_CTL_AMUTE (1<<2)
+#define CS42L51_DAC_CTL_DACSZ(x) (((x)&3)<<0)
+
+#define CS42L51_ALC_PGA_CTL 0x0A
+#define CS42L51_ALC_PGB_CTL 0x0B
+#define CS42L51_ALC_PGX_ALCX_SRDIS (1<<7)
+#define CS42L51_ALC_PGX_ALCX_ZCDIS (1<<6)
+#define CS42L51_ALC_PGX_PGX_VOL(x) (((x)&0x1f)<<0)
+
+#define CS42L51_ADCA_ATT 0x0C
+#define CS42L51_ADCB_ATT 0x0D
+
+#define CS42L51_ADCA_VOL 0x0E
+#define CS42L51_ADCB_VOL 0x0F
+#define CS42L51_PCMA_VOL 0x10
+#define CS42L51_PCMB_VOL 0x11
+#define CS42L51_MIX_MUTE_ADCMIX (1<<7)
+#define CS42L51_MIX_VOLUME(x) (((x)&0x7f)<<0)
+
+#define CS42L51_BEEP_FREQ 0x12
+#define CS42L51_BEEP_VOL 0x13
+#define CS42L51_BEEP_CONF 0x14
+
+#define CS42L51_TONE_CTL 0x15
+#define CS42L51_TONE_CTL_TREB(x) (((x)&0xf)<<4)
+#define CS42L51_TONE_CTL_BASS(x) (((x)&0xf)<<0)
+
+#define CS42L51_AOUTA_VOL 0x16
+#define CS42L51_AOUTB_VOL 0x17
+#define CS42L51_PCM_MIXER 0x18
+#define CS42L51_LIMIT_THRES_DIS 0x19
+#define CS42L51_LIMIT_REL 0x1A
+#define CS42L51_LIMIT_ATT 0x1B
+#define CS42L51_ALC_EN 0x1C
+#define CS42L51_ALC_REL 0x1D
+#define CS42L51_ALC_THRES 0x1E
+#define CS42L51_NOISE_CONF 0x1F
+
+#define CS42L51_STATUS 0x20
+#define CS42L51_STATUS_SP_CLKERR (1<<6)
+#define CS42L51_STATUS_SPEA_OVFL (1<<5)
+#define CS42L51_STATUS_SPEB_OVFL (1<<4)
+#define CS42L51_STATUS_PCMA_OVFL (1<<3)
+#define CS42L51_STATUS_PCMB_OVFL (1<<2)
+#define CS42L51_STATUS_ADCA_OVFL (1<<1)
+#define CS42L51_STATUS_ADCB_OVFL (1<<0)
+
+#define CS42L51_CHARGE_FREQ 0x21
+
+#define CS42L51_FIRSTREG 0x01
+/*
+ * Hack: with register 0x21, it makes 33 registers. Looks like someone in the
+ * i2c layer doesn't like i2c smbus block read of 33 regs. Workaround by using
+ * 32 regs
+ */
+#define CS42L51_LASTREG 0x20
+#define CS42L51_NUMREGS (CS42L51_LASTREG - CS42L51_FIRSTREG + 1)
+
+#endif
diff --git a/kernel/sound/soc/codecs/cs42l52.c b/kernel/sound/soc/codecs/cs42l52.c
new file mode 100644
index 000000000..1589e7a88
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs42l52.c
@@ -0,0 +1,1302 @@
+/*
+ * cs42l52.c -- CS42L52 ALSA SoC audio driver
+ *
+ * Copyright 2012 CirrusLogic, Inc.
+ *
+ * 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/i2c.h>
+#include <linux/input.h>
+#include <linux/regmap.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 <sound/cs42l52.h>
+#include "cs42l52.h"
+
+struct sp_config {
+ u8 spc, format, spfs;
+ u32 srate;
+};
+
+struct cs42l52_private {
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ struct device *dev;
+ struct sp_config config;
+ struct cs42l52_platform_data pdata;
+ u32 sysclk;
+ u8 mclksel;
+ u32 mclk;
+ u8 flags;
+ struct input_dev *beep;
+ struct work_struct beep_work;
+ int beep_rate;
+};
+
+static const struct reg_default cs42l52_reg_defaults[] = {
+ { CS42L52_PWRCTL1, 0x9F }, /* r02 PWRCTL 1 */
+ { CS42L52_PWRCTL2, 0x07 }, /* r03 PWRCTL 2 */
+ { CS42L52_PWRCTL3, 0xFF }, /* r04 PWRCTL 3 */
+ { CS42L52_CLK_CTL, 0xA0 }, /* r05 Clocking Ctl */
+ { CS42L52_IFACE_CTL1, 0x00 }, /* r06 Interface Ctl 1 */
+ { CS42L52_ADC_PGA_A, 0x80 }, /* r08 Input A Select */
+ { CS42L52_ADC_PGA_B, 0x80 }, /* r09 Input B Select */
+ { CS42L52_ANALOG_HPF_CTL, 0xA5 }, /* r0A Analog HPF Ctl */
+ { CS42L52_ADC_HPF_FREQ, 0x00 }, /* r0B ADC HPF Corner Freq */
+ { CS42L52_ADC_MISC_CTL, 0x00 }, /* r0C Misc. ADC Ctl */
+ { CS42L52_PB_CTL1, 0x60 }, /* r0D Playback Ctl 1 */
+ { CS42L52_MISC_CTL, 0x02 }, /* r0E Misc. Ctl */
+ { CS42L52_PB_CTL2, 0x00 }, /* r0F Playback Ctl 2 */
+ { CS42L52_MICA_CTL, 0x00 }, /* r10 MICA Amp Ctl */
+ { CS42L52_MICB_CTL, 0x00 }, /* r11 MICB Amp Ctl */
+ { CS42L52_PGAA_CTL, 0x00 }, /* r12 PGAA Vol, Misc. */
+ { CS42L52_PGAB_CTL, 0x00 }, /* r13 PGAB Vol, Misc. */
+ { CS42L52_PASSTHRUA_VOL, 0x00 }, /* r14 Bypass A Vol */
+ { CS42L52_PASSTHRUB_VOL, 0x00 }, /* r15 Bypass B Vol */
+ { CS42L52_ADCA_VOL, 0x00 }, /* r16 ADCA Volume */
+ { CS42L52_ADCB_VOL, 0x00 }, /* r17 ADCB Volume */
+ { CS42L52_ADCA_MIXER_VOL, 0x80 }, /* r18 ADCA Mixer Volume */
+ { CS42L52_ADCB_MIXER_VOL, 0x80 }, /* r19 ADCB Mixer Volume */
+ { CS42L52_PCMA_MIXER_VOL, 0x00 }, /* r1A PCMA Mixer Volume */
+ { CS42L52_PCMB_MIXER_VOL, 0x00 }, /* r1B PCMB Mixer Volume */
+ { CS42L52_BEEP_FREQ, 0x00 }, /* r1C Beep Freq on Time */
+ { CS42L52_BEEP_VOL, 0x00 }, /* r1D Beep Volume off Time */
+ { CS42L52_BEEP_TONE_CTL, 0x00 }, /* r1E Beep Tone Cfg. */
+ { CS42L52_TONE_CTL, 0x00 }, /* r1F Tone Ctl */
+ { CS42L52_MASTERA_VOL, 0x00 }, /* r20 Master A Volume */
+ { CS42L52_MASTERB_VOL, 0x00 }, /* r21 Master B Volume */
+ { CS42L52_HPA_VOL, 0x00 }, /* r22 Headphone A Volume */
+ { CS42L52_HPB_VOL, 0x00 }, /* r23 Headphone B Volume */
+ { CS42L52_SPKA_VOL, 0x00 }, /* r24 Speaker A Volume */
+ { CS42L52_SPKB_VOL, 0x00 }, /* r25 Speaker B Volume */
+ { CS42L52_ADC_PCM_MIXER, 0x00 }, /* r26 Channel Mixer and Swap */
+ { CS42L52_LIMITER_CTL1, 0x00 }, /* r27 Limit Ctl 1 Thresholds */
+ { CS42L52_LIMITER_CTL2, 0x7F }, /* r28 Limit Ctl 2 Release Rate */
+ { CS42L52_LIMITER_AT_RATE, 0xC0 }, /* r29 Limiter Attack Rate */
+ { CS42L52_ALC_CTL, 0x00 }, /* r2A ALC Ctl 1 Attack Rate */
+ { CS42L52_ALC_RATE, 0x3F }, /* r2B ALC Release Rate */
+ { CS42L52_ALC_THRESHOLD, 0x3f }, /* r2C ALC Thresholds */
+ { CS42L52_NOISE_GATE_CTL, 0x00 }, /* r2D Noise Gate Ctl */
+ { CS42L52_CLK_STATUS, 0x00 }, /* r2E Overflow and Clock Status */
+ { CS42L52_BATT_COMPEN, 0x00 }, /* r2F battery Compensation */
+ { CS42L52_BATT_LEVEL, 0x00 }, /* r30 VP Battery Level */
+ { CS42L52_SPK_STATUS, 0x00 }, /* r31 Speaker Status */
+ { CS42L52_TEM_CTL, 0x3B }, /* r32 Temp Ctl */
+ { CS42L52_THE_FOLDBACK, 0x00 }, /* r33 Foldback */
+};
+
+static bool cs42l52_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L52_CHIP:
+ 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:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l52_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L52_IFACE_CTL2:
+ case CS42L52_CLK_STATUS:
+ case CS42L52_BATT_LEVEL:
+ case CS42L52_SPK_STATUS:
+ case CS42L52_CHARGE_PUMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(hl_tlv, -10200, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(hpd_tlv, -9600, 50, 1);
+
+static DECLARE_TLV_DB_SCALE(ipd_tlv, -9600, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(mic_tlv, 1600, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(mix_tlv, -50, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(beep_tlv, -56, 200, 0);
+
+static const unsigned int limiter_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0),
+ 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0),
+};
+
+static const char * const cs42l52_adca_text[] = {
+ "Input1A", "Input2A", "Input3A", "Input4A", "PGA Input Left"};
+
+static const char * const cs42l52_adcb_text[] = {
+ "Input1B", "Input2B", "Input3B", "Input4B", "PGA Input Right"};
+
+static SOC_ENUM_SINGLE_DECL(adca_enum,
+ CS42L52_ADC_PGA_A, 5, cs42l52_adca_text);
+
+static SOC_ENUM_SINGLE_DECL(adcb_enum,
+ CS42L52_ADC_PGA_B, 5, cs42l52_adcb_text);
+
+static const struct snd_kcontrol_new adca_mux =
+ SOC_DAPM_ENUM("Left ADC Input Capture Mux", adca_enum);
+
+static const struct snd_kcontrol_new adcb_mux =
+ SOC_DAPM_ENUM("Right ADC Input Capture Mux", adcb_enum);
+
+static const char * const mic_bias_level_text[] = {
+ "0.5 +VA", "0.6 +VA", "0.7 +VA",
+ "0.8 +VA", "0.83 +VA", "0.91 +VA"
+};
+
+static SOC_ENUM_SINGLE_DECL(mic_bias_level_enum,
+ CS42L52_IFACE_CTL2, 0, mic_bias_level_text);
+
+static const char * const cs42l52_mic_text[] = { "MIC1", "MIC2" };
+
+static SOC_ENUM_SINGLE_DECL(mica_enum,
+ CS42L52_MICA_CTL, 5, cs42l52_mic_text);
+
+static SOC_ENUM_SINGLE_DECL(micb_enum,
+ CS42L52_MICB_CTL, 5, cs42l52_mic_text);
+
+static const char * const digital_output_mux_text[] = {"ADC", "DSP"};
+
+static SOC_ENUM_SINGLE_DECL(digital_output_mux_enum,
+ CS42L52_ADC_MISC_CTL, 6,
+ digital_output_mux_text);
+
+static const struct snd_kcontrol_new digital_output_mux =
+ SOC_DAPM_ENUM("Digital Output Mux", digital_output_mux_enum);
+
+static const char * const hp_gain_num_text[] = {
+ "0.3959", "0.4571", "0.5111", "0.6047",
+ "0.7099", "0.8399", "1.000", "1.1430"
+};
+
+static SOC_ENUM_SINGLE_DECL(hp_gain_enum,
+ CS42L52_PB_CTL1, 5,
+ hp_gain_num_text);
+
+static const char * const beep_pitch_text[] = {
+ "C4", "C5", "D5", "E5", "F5", "G5", "A5", "B5",
+ "C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7"
+};
+
+static SOC_ENUM_SINGLE_DECL(beep_pitch_enum,
+ CS42L52_BEEP_FREQ, 4,
+ beep_pitch_text);
+
+static const char * const beep_ontime_text[] = {
+ "86 ms", "430 ms", "780 ms", "1.20 s", "1.50 s",
+ "1.80 s", "2.20 s", "2.50 s", "2.80 s", "3.20 s",
+ "3.50 s", "3.80 s", "4.20 s", "4.50 s", "4.80 s", "5.20 s"
+};
+
+static SOC_ENUM_SINGLE_DECL(beep_ontime_enum,
+ CS42L52_BEEP_FREQ, 0,
+ beep_ontime_text);
+
+static const char * const beep_offtime_text[] = {
+ "1.23 s", "2.58 s", "3.90 s", "5.20 s",
+ "6.60 s", "8.05 s", "9.35 s", "10.80 s"
+};
+
+static SOC_ENUM_SINGLE_DECL(beep_offtime_enum,
+ CS42L52_BEEP_VOL, 5,
+ beep_offtime_text);
+
+static const char * const beep_config_text[] = {
+ "Off", "Single", "Multiple", "Continuous"
+};
+
+static SOC_ENUM_SINGLE_DECL(beep_config_enum,
+ CS42L52_BEEP_TONE_CTL, 6,
+ beep_config_text);
+
+static const char * const beep_bass_text[] = {
+ "50 Hz", "100 Hz", "200 Hz", "250 Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(beep_bass_enum,
+ CS42L52_BEEP_TONE_CTL, 1,
+ beep_bass_text);
+
+static const char * const beep_treble_text[] = {
+ "5 kHz", "7 kHz", "10 kHz", " 15 kHz"
+};
+
+static SOC_ENUM_SINGLE_DECL(beep_treble_enum,
+ CS42L52_BEEP_TONE_CTL, 3,
+ beep_treble_text);
+
+static const char * const ng_threshold_text[] = {
+ "-34dB", "-37dB", "-40dB", "-43dB",
+ "-46dB", "-52dB", "-58dB", "-64dB"
+};
+
+static SOC_ENUM_SINGLE_DECL(ng_threshold_enum,
+ CS42L52_NOISE_GATE_CTL, 2,
+ ng_threshold_text);
+
+static const char * const cs42l52_ng_delay_text[] = {
+ "50ms", "100ms", "150ms", "200ms"};
+
+static SOC_ENUM_SINGLE_DECL(ng_delay_enum,
+ CS42L52_NOISE_GATE_CTL, 0,
+ cs42l52_ng_delay_text);
+
+static const char * const cs42l52_ng_type_text[] = {
+ "Apply Specific", "Apply All"
+};
+
+static SOC_ENUM_SINGLE_DECL(ng_type_enum,
+ CS42L52_NOISE_GATE_CTL, 6,
+ cs42l52_ng_type_text);
+
+static const char * const left_swap_text[] = {
+ "Left", "LR 2", "Right"};
+
+static const char * const right_swap_text[] = {
+ "Right", "LR 2", "Left"};
+
+static const unsigned int swap_values[] = { 0, 1, 3 };
+
+static const struct soc_enum adca_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 2, 3,
+ ARRAY_SIZE(left_swap_text),
+ left_swap_text,
+ swap_values);
+
+static const struct snd_kcontrol_new adca_mixer =
+ SOC_DAPM_ENUM("Route", adca_swap_enum);
+
+static const struct soc_enum pcma_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 6, 3,
+ ARRAY_SIZE(left_swap_text),
+ left_swap_text,
+ swap_values);
+
+static const struct snd_kcontrol_new pcma_mixer =
+ SOC_DAPM_ENUM("Route", pcma_swap_enum);
+
+static const struct soc_enum adcb_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 0, 3,
+ ARRAY_SIZE(right_swap_text),
+ right_swap_text,
+ swap_values);
+
+static const struct snd_kcontrol_new adcb_mixer =
+ SOC_DAPM_ENUM("Route", adcb_swap_enum);
+
+static const struct soc_enum pcmb_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 4, 3,
+ ARRAY_SIZE(right_swap_text),
+ right_swap_text,
+ swap_values);
+
+static const struct snd_kcontrol_new pcmb_mixer =
+ SOC_DAPM_ENUM("Route", pcmb_swap_enum);
+
+
+static const struct snd_kcontrol_new passthrul_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_MISC_CTL, 6, 1, 0);
+
+static const struct snd_kcontrol_new passthrur_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_MISC_CTL, 7, 1, 0);
+
+static const struct snd_kcontrol_new spkl_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 0, 1, 1);
+
+static const struct snd_kcontrol_new spkr_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 2, 1, 1);
+
+static const struct snd_kcontrol_new hpl_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 4, 1, 1);
+
+static const struct snd_kcontrol_new hpr_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 6, 1, 1);
+
+static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
+
+ SOC_DOUBLE_R_SX_TLV("Master Volume", CS42L52_MASTERA_VOL,
+ CS42L52_MASTERB_VOL, 0, 0x34, 0xE4, hl_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L52_HPA_VOL,
+ CS42L52_HPB_VOL, 0, 0x34, 0xC0, hpd_tlv),
+
+ SOC_ENUM("Headphone Analog Gain", hp_gain_enum),
+
+ SOC_DOUBLE_R_SX_TLV("Speaker Volume", CS42L52_SPKA_VOL,
+ CS42L52_SPKB_VOL, 0, 0x40, 0xC0, hl_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("Bypass Volume", CS42L52_PASSTHRUA_VOL,
+ CS42L52_PASSTHRUB_VOL, 0, 0x88, 0x90, pga_tlv),
+
+ SOC_DOUBLE("Bypass Mute", CS42L52_MISC_CTL, 4, 5, 1, 0),
+
+ SOC_DOUBLE_R_TLV("MIC Gain Volume", CS42L52_MICA_CTL,
+ CS42L52_MICB_CTL, 0, 0x10, 0, mic_tlv),
+
+ SOC_ENUM("MIC Bias Level", mic_bias_level_enum),
+
+ SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L52_ADCA_VOL,
+ CS42L52_ADCB_VOL, 0, 0xA0, 0x78, ipd_tlv),
+ SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
+ CS42L52_ADCA_MIXER_VOL, CS42L52_ADCB_MIXER_VOL,
+ 0, 0x19, 0x7F, ipd_tlv),
+
+ SOC_DOUBLE("ADC Switch", CS42L52_ADC_MISC_CTL, 0, 1, 1, 0),
+
+ SOC_DOUBLE_R("ADC Mixer Switch", CS42L52_ADCA_MIXER_VOL,
+ CS42L52_ADCB_MIXER_VOL, 7, 1, 1),
+
+ SOC_DOUBLE_R_SX_TLV("PGA Volume", CS42L52_PGAA_CTL,
+ CS42L52_PGAB_CTL, 0, 0x28, 0x24, pga_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("PCM Mixer Volume",
+ CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL,
+ 0, 0x19, 0x7f, mix_tlv),
+ SOC_DOUBLE_R("PCM Mixer Switch",
+ CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL, 7, 1, 1),
+
+ SOC_ENUM("Beep Config", beep_config_enum),
+ SOC_ENUM("Beep Pitch", beep_pitch_enum),
+ SOC_ENUM("Beep on Time", beep_ontime_enum),
+ SOC_ENUM("Beep off Time", beep_offtime_enum),
+ SOC_SINGLE_SX_TLV("Beep Volume", CS42L52_BEEP_VOL,
+ 0, 0x07, 0x1f, beep_tlv),
+ SOC_SINGLE("Beep Mixer Switch", CS42L52_BEEP_TONE_CTL, 5, 1, 1),
+ SOC_ENUM("Beep Treble Corner Freq", beep_treble_enum),
+ SOC_ENUM("Beep Bass Corner Freq", beep_bass_enum),
+
+ SOC_SINGLE("Tone Control Switch", CS42L52_BEEP_TONE_CTL, 0, 1, 1),
+ SOC_SINGLE_TLV("Treble Gain Volume",
+ CS42L52_TONE_CTL, 4, 15, 1, hl_tlv),
+ SOC_SINGLE_TLV("Bass Gain Volume",
+ CS42L52_TONE_CTL, 0, 15, 1, hl_tlv),
+
+ /* Limiter */
+ SOC_SINGLE_TLV("Limiter Max Threshold Volume",
+ CS42L52_LIMITER_CTL1, 5, 7, 0, limiter_tlv),
+ SOC_SINGLE_TLV("Limiter Cushion Threshold Volume",
+ CS42L52_LIMITER_CTL1, 2, 7, 0, limiter_tlv),
+ SOC_SINGLE_TLV("Limiter Release Rate Volume",
+ CS42L52_LIMITER_CTL2, 0, 63, 0, limiter_tlv),
+ SOC_SINGLE_TLV("Limiter Attack Rate Volume",
+ CS42L52_LIMITER_AT_RATE, 0, 63, 0, limiter_tlv),
+
+ SOC_SINGLE("Limiter SR Switch", CS42L52_LIMITER_CTL1, 1, 1, 0),
+ SOC_SINGLE("Limiter ZC Switch", CS42L52_LIMITER_CTL1, 0, 1, 0),
+ SOC_SINGLE("Limiter Switch", CS42L52_LIMITER_CTL2, 7, 1, 0),
+
+ /* ALC */
+ SOC_SINGLE_TLV("ALC Attack Rate Volume", CS42L52_ALC_CTL,
+ 0, 63, 0, limiter_tlv),
+ SOC_SINGLE_TLV("ALC Release Rate Volume", CS42L52_ALC_RATE,
+ 0, 63, 0, limiter_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold Volume", CS42L52_ALC_THRESHOLD,
+ 5, 7, 0, limiter_tlv),
+ SOC_SINGLE_TLV("ALC Min Threshold Volume", CS42L52_ALC_THRESHOLD,
+ 2, 7, 0, limiter_tlv),
+
+ SOC_DOUBLE_R("ALC SR Capture Switch", CS42L52_PGAA_CTL,
+ CS42L52_PGAB_CTL, 7, 1, 1),
+ SOC_DOUBLE_R("ALC ZC Capture Switch", CS42L52_PGAA_CTL,
+ CS42L52_PGAB_CTL, 6, 1, 1),
+ SOC_DOUBLE("ALC Capture Switch", CS42L52_ALC_CTL, 6, 7, 1, 0),
+
+ /* Noise gate */
+ SOC_ENUM("NG Type Switch", ng_type_enum),
+ SOC_SINGLE("NG Enable Switch", CS42L52_NOISE_GATE_CTL, 6, 1, 0),
+ SOC_SINGLE("NG Boost Switch", CS42L52_NOISE_GATE_CTL, 5, 1, 1),
+ SOC_ENUM("NG Threshold", ng_threshold_enum),
+ SOC_ENUM("NG Delay", ng_delay_enum),
+
+ SOC_DOUBLE("HPF Switch", CS42L52_ANALOG_HPF_CTL, 5, 7, 1, 0),
+
+ SOC_DOUBLE("Analog SR Switch", CS42L52_ANALOG_HPF_CTL, 1, 3, 1, 1),
+ SOC_DOUBLE("Analog ZC Switch", CS42L52_ANALOG_HPF_CTL, 0, 2, 1, 1),
+ SOC_SINGLE("Digital SR Switch", CS42L52_MISC_CTL, 1, 1, 0),
+ SOC_SINGLE("Digital ZC Switch", CS42L52_MISC_CTL, 0, 1, 0),
+ SOC_SINGLE("Deemphasis Switch", CS42L52_MISC_CTL, 2, 1, 0),
+
+ SOC_SINGLE("Batt Compensation Switch", CS42L52_BATT_COMPEN, 7, 1, 0),
+ SOC_SINGLE("Batt VP Monitor Switch", CS42L52_BATT_COMPEN, 6, 1, 0),
+ SOC_SINGLE("Batt VP ref", CS42L52_BATT_COMPEN, 0, 0x0f, 0),
+
+ SOC_SINGLE("PGA AIN1L Switch", CS42L52_ADC_PGA_A, 0, 1, 0),
+ SOC_SINGLE("PGA AIN1R Switch", CS42L52_ADC_PGA_B, 0, 1, 0),
+ SOC_SINGLE("PGA AIN2L Switch", CS42L52_ADC_PGA_A, 1, 1, 0),
+ SOC_SINGLE("PGA AIN2R Switch", CS42L52_ADC_PGA_B, 1, 1, 0),
+
+ SOC_SINGLE("PGA AIN3L Switch", CS42L52_ADC_PGA_A, 2, 1, 0),
+ SOC_SINGLE("PGA AIN3R Switch", CS42L52_ADC_PGA_B, 2, 1, 0),
+
+ SOC_SINGLE("PGA AIN4L Switch", CS42L52_ADC_PGA_A, 3, 1, 0),
+ SOC_SINGLE("PGA AIN4R Switch", CS42L52_ADC_PGA_B, 3, 1, 0),
+
+ SOC_SINGLE("PGA MICA Switch", CS42L52_ADC_PGA_A, 4, 1, 0),
+ SOC_SINGLE("PGA MICB Switch", CS42L52_ADC_PGA_B, 4, 1, 0),
+
+};
+
+static const struct snd_kcontrol_new cs42l52_mica_controls[] = {
+ SOC_ENUM("MICA Select", mica_enum),
+};
+
+static const struct snd_kcontrol_new cs42l52_micb_controls[] = {
+ SOC_ENUM("MICB Select", micb_enum),
+};
+
+static int cs42l52_add_mic_controls(struct snd_soc_codec *codec)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l52_platform_data *pdata = &cs42l52->pdata;
+
+ if (!pdata->mica_diff_cfg)
+ snd_soc_add_codec_controls(codec, cs42l52_mica_controls,
+ ARRAY_SIZE(cs42l52_mica_controls));
+
+ if (!pdata->micb_diff_cfg)
+ snd_soc_add_codec_controls(codec, cs42l52_micb_controls,
+ ARRAY_SIZE(cs42l52_micb_controls));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs42l52_dapm_widgets[] = {
+
+ SND_SOC_DAPM_INPUT("AIN1L"),
+ SND_SOC_DAPM_INPUT("AIN1R"),
+ SND_SOC_DAPM_INPUT("AIN2L"),
+ SND_SOC_DAPM_INPUT("AIN2R"),
+ SND_SOC_DAPM_INPUT("AIN3L"),
+ SND_SOC_DAPM_INPUT("AIN3R"),
+ SND_SOC_DAPM_INPUT("AIN4L"),
+ SND_SOC_DAPM_INPUT("AIN4R"),
+ SND_SOC_DAPM_INPUT("MICA"),
+ SND_SOC_DAPM_INPUT("MICB"),
+ SND_SOC_DAPM_SIGGEN("Beep"),
+
+ SND_SOC_DAPM_AIF_OUT("AIFOUTL", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIFOUTR", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_ADC("ADC Left", NULL, CS42L52_PWRCTL1, 1, 1),
+ SND_SOC_DAPM_ADC("ADC Right", NULL, CS42L52_PWRCTL1, 2, 1),
+ SND_SOC_DAPM_PGA("PGA Left", CS42L52_PWRCTL1, 3, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA Right", CS42L52_PWRCTL1, 4, 1, NULL, 0),
+
+ SND_SOC_DAPM_MUX("ADC Left Mux", SND_SOC_NOPM, 0, 0, &adca_mux),
+ SND_SOC_DAPM_MUX("ADC Right Mux", SND_SOC_NOPM, 0, 0, &adcb_mux),
+
+ SND_SOC_DAPM_MUX("ADC Left Swap", SND_SOC_NOPM,
+ 0, 0, &adca_mixer),
+ SND_SOC_DAPM_MUX("ADC Right Swap", SND_SOC_NOPM,
+ 0, 0, &adcb_mixer),
+
+ SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM,
+ 0, 0, &digital_output_mux),
+
+ SND_SOC_DAPM_PGA("PGA MICA", CS42L52_PWRCTL2, 1, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA MICB", CS42L52_PWRCTL2, 2, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L52_PWRCTL2, 0, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Charge Pump", CS42L52_PWRCTL1, 7, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_IN("AIFINL", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFINR", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DAC Left", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC Right", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SWITCH("Bypass Left", CS42L52_MISC_CTL,
+ 6, 0, &passthrul_ctl),
+ SND_SOC_DAPM_SWITCH("Bypass Right", CS42L52_MISC_CTL,
+ 7, 0, &passthrur_ctl),
+
+ SND_SOC_DAPM_MUX("PCM Left Swap", SND_SOC_NOPM,
+ 0, 0, &pcma_mixer),
+ SND_SOC_DAPM_MUX("PCM Right Swap", SND_SOC_NOPM,
+ 0, 0, &pcmb_mixer),
+
+ SND_SOC_DAPM_SWITCH("HP Left Amp", SND_SOC_NOPM, 0, 0, &hpl_ctl),
+ SND_SOC_DAPM_SWITCH("HP Right Amp", SND_SOC_NOPM, 0, 0, &hpr_ctl),
+
+ SND_SOC_DAPM_SWITCH("SPK Left Amp", SND_SOC_NOPM, 0, 0, &spkl_ctl),
+ SND_SOC_DAPM_SWITCH("SPK Right Amp", SND_SOC_NOPM, 0, 0, &spkr_ctl),
+
+ SND_SOC_DAPM_OUTPUT("HPOUTA"),
+ SND_SOC_DAPM_OUTPUT("HPOUTB"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTA"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTB"),
+
+};
+
+static const struct snd_soc_dapm_route cs42l52_audio_map[] = {
+
+ {"Capture", NULL, "AIFOUTL"},
+ {"Capture", NULL, "AIFOUTL"},
+
+ {"AIFOUTL", NULL, "Output Mux"},
+ {"AIFOUTR", NULL, "Output Mux"},
+
+ {"Output Mux", "ADC", "ADC Left"},
+ {"Output Mux", "ADC", "ADC Right"},
+
+ {"ADC Left", NULL, "Charge Pump"},
+ {"ADC Right", NULL, "Charge Pump"},
+
+ {"Charge Pump", NULL, "ADC Left Mux"},
+ {"Charge Pump", NULL, "ADC Right Mux"},
+
+ {"ADC Left Mux", "Input1A", "AIN1L"},
+ {"ADC Right Mux", "Input1B", "AIN1R"},
+ {"ADC Left Mux", "Input2A", "AIN2L"},
+ {"ADC Right Mux", "Input2B", "AIN2R"},
+ {"ADC Left Mux", "Input3A", "AIN3L"},
+ {"ADC Right Mux", "Input3B", "AIN3R"},
+ {"ADC Left Mux", "Input4A", "AIN4L"},
+ {"ADC Right Mux", "Input4B", "AIN4R"},
+ {"ADC Left Mux", "PGA Input Left", "PGA Left"},
+ {"ADC Right Mux", "PGA Input Right" , "PGA Right"},
+
+ {"PGA Left", "Switch", "AIN1L"},
+ {"PGA Right", "Switch", "AIN1R"},
+ {"PGA Left", "Switch", "AIN2L"},
+ {"PGA Right", "Switch", "AIN2R"},
+ {"PGA Left", "Switch", "AIN3L"},
+ {"PGA Right", "Switch", "AIN3R"},
+ {"PGA Left", "Switch", "AIN4L"},
+ {"PGA Right", "Switch", "AIN4R"},
+
+ {"PGA Left", "Switch", "PGA MICA"},
+ {"PGA MICA", NULL, "MICA"},
+
+ {"PGA Right", "Switch", "PGA MICB"},
+ {"PGA MICB", NULL, "MICB"},
+
+ {"HPOUTA", NULL, "HP Left Amp"},
+ {"HPOUTB", NULL, "HP Right Amp"},
+ {"HP Left Amp", NULL, "Bypass Left"},
+ {"HP Right Amp", NULL, "Bypass Right"},
+ {"Bypass Left", "Switch", "PGA Left"},
+ {"Bypass Right", "Switch", "PGA Right"},
+ {"HP Left Amp", "Switch", "DAC Left"},
+ {"HP Right Amp", "Switch", "DAC Right"},
+
+ {"SPKOUTA", NULL, "SPK Left Amp"},
+ {"SPKOUTB", NULL, "SPK Right Amp"},
+
+ {"SPK Left Amp", NULL, "Beep"},
+ {"SPK Right Amp", NULL, "Beep"},
+ {"SPK Left Amp", "Switch", "Playback"},
+ {"SPK Right Amp", "Switch", "Playback"},
+
+ {"DAC Left", NULL, "Beep"},
+ {"DAC Right", NULL, "Beep"},
+ {"DAC Left", NULL, "Playback"},
+ {"DAC Right", NULL, "Playback"},
+
+ {"Output Mux", "DSP", "Playback"},
+ {"Output Mux", "DSP", "Playback"},
+
+ {"AIFINL", NULL, "Playback"},
+ {"AIFINR", NULL, "Playback"},
+
+};
+
+struct cs42l52_clk_para {
+ u32 mclk;
+ u32 rate;
+ u8 speed;
+ u8 group;
+ u8 videoclk;
+ u8 ratio;
+ u8 mclkdiv2;
+};
+
+static const struct cs42l52_clk_para clk_map_table[] = {
+ /*8k*/
+ {12288000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1},
+ {27000000, 8000, CLK_QS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 0},
+
+ /*11.025k*/
+ {11289600, 11025, CLK_QS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {16934400, 11025, CLK_QS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+ /*16k*/
+ {12288000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1},
+ {27000000, 16000, CLK_HS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 1},
+
+ /*22.05k*/
+ {11289600, 22050, CLK_HS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {16934400, 22050, CLK_HS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+ /* 32k */
+ {12288000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1},
+ {27000000, 32000, CLK_SS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 0},
+
+ /* 44.1k */
+ {11289600, 44100, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {16934400, 44100, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+ /* 48k */
+ {12288000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 1},
+ {27000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_27M_MCLK, CLK_R_125, 1},
+
+ /* 88.2k */
+ {11289600, 88200, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {16934400, 88200, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+ /* 96k */
+ {12288000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {18432000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+ {12000000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 0},
+ {24000000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 1},
+};
+
+static int cs42l52_get_clk(int mclk, int rate)
+{
+ int i, ret = -EINVAL;
+ u_int mclk1, mclk2 = 0;
+
+ for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
+ if (clk_map_table[i].rate == rate) {
+ mclk1 = clk_map_table[i].mclk;
+ if (abs(mclk - mclk1) < abs(mclk - mclk2)) {
+ mclk2 = mclk1;
+ ret = i;
+ }
+ }
+ }
+ return ret;
+}
+
+static int cs42l52_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+
+ if ((freq >= CS42L52_MIN_CLK) && (freq <= CS42L52_MAX_CLK)) {
+ cs42l52->sysclk = freq;
+ } else {
+ dev_err(codec->dev, "Invalid freq parameter\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs42l52_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ u8 iface = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface = CS42L52_IFACE_CTL1_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ iface = CS42L52_IFACE_CTL1_SLAVE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= CS42L52_IFACE_CTL1_ADC_FMT_I2S |
+ CS42L52_IFACE_CTL1_DAC_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface |= CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J |
+ CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= CS42L52_IFACE_CTL1_DSP_MODE_EN;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= CS42L52_IFACE_CTL1_INV_SCLK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= CS42L52_IFACE_CTL1_INV_SCLK;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+ cs42l52->config.format = iface;
+ snd_soc_write(codec, CS42L52_IFACE_CTL1, cs42l52->config.format);
+
+ return 0;
+}
+
+static int cs42l52_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute)
+ snd_soc_update_bits(codec, CS42L52_PB_CTL1,
+ CS42L52_PB_CTL1_MUTE_MASK,
+ CS42L52_PB_CTL1_MUTE);
+ else
+ snd_soc_update_bits(codec, CS42L52_PB_CTL1,
+ CS42L52_PB_CTL1_MUTE_MASK,
+ CS42L52_PB_CTL1_UNMUTE);
+
+ return 0;
+}
+
+static int cs42l52_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ u32 clk = 0;
+ int index;
+
+ index = cs42l52_get_clk(cs42l52->sysclk, params_rate(params));
+ if (index >= 0) {
+ cs42l52->sysclk = clk_map_table[index].mclk;
+
+ clk |= (clk_map_table[index].speed << CLK_SPEED_SHIFT) |
+ (clk_map_table[index].group << CLK_32K_SR_SHIFT) |
+ (clk_map_table[index].videoclk << CLK_27M_MCLK_SHIFT) |
+ (clk_map_table[index].ratio << CLK_RATIO_SHIFT) |
+ clk_map_table[index].mclkdiv2;
+
+ snd_soc_write(codec, CS42L52_CLK_CTL, clk);
+ } else {
+ dev_err(codec->dev, "can't get correct mclk\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_update_bits(codec, CS42L52_PWRCTL1,
+ CS42L52_PWRCTL1_PDN_CODEC, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ regcache_cache_only(cs42l52->regmap, false);
+ regcache_sync(cs42l52->regmap);
+ }
+ snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+ regcache_cache_only(cs42l52->regmap, true);
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+#define CS42L52_RATES (SNDRV_PCM_RATE_8000_96000)
+
+#define CS42L52_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE)
+
+static struct snd_soc_dai_ops cs42l52_ops = {
+ .hw_params = cs42l52_pcm_hw_params,
+ .digital_mute = cs42l52_digital_mute,
+ .set_fmt = cs42l52_set_fmt,
+ .set_sysclk = cs42l52_set_sysclk,
+};
+
+static struct snd_soc_dai_driver cs42l52_dai = {
+ .name = "cs42l52",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS42L52_RATES,
+ .formats = CS42L52_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS42L52_RATES,
+ .formats = CS42L52_FORMATS,
+ },
+ .ops = &cs42l52_ops,
+};
+
+static int beep_rates[] = {
+ 261, 522, 585, 667, 706, 774, 889, 1000,
+ 1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182
+};
+
+static void cs42l52_beep_work(struct work_struct *work)
+{
+ struct cs42l52_private *cs42l52 =
+ container_of(work, struct cs42l52_private, beep_work);
+ struct snd_soc_codec *codec = cs42l52->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int i;
+ int val = 0;
+ int best = 0;
+
+ if (cs42l52->beep_rate) {
+ for (i = 0; i < ARRAY_SIZE(beep_rates); i++) {
+ if (abs(cs42l52->beep_rate - beep_rates[i]) <
+ abs(cs42l52->beep_rate - beep_rates[best]))
+ best = i;
+ }
+
+ dev_dbg(codec->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");
+ snd_soc_dapm_disable_pin(dapm, "Beep");
+ }
+
+ snd_soc_update_bits(codec, CS42L52_BEEP_FREQ,
+ CS42L52_BEEP_RATE_MASK, val);
+
+ snd_soc_dapm_sync(dapm);
+}
+
+/* For usability define a way of injecting beep events for the device -
+ * many systems will not have a keyboard.
+ */
+static int cs42l52_beep_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int hz)
+{
+ struct snd_soc_codec *codec = input_get_drvdata(dev);
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "Beep event %x %x\n", code, hz);
+
+ switch (code) {
+ case SND_BELL:
+ if (hz)
+ hz = 261;
+ case SND_TONE:
+ break;
+ default:
+ return -1;
+ }
+
+ /* Kick the beep from a workqueue */
+ cs42l52->beep_rate = hz;
+ schedule_work(&cs42l52->beep_work);
+ return 0;
+}
+
+static ssize_t cs42l52_beep_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cs42l52_private *cs42l52 = dev_get_drvdata(dev);
+ long int time;
+ int ret;
+
+ ret = kstrtol(buf, 10, &time);
+ if (ret != 0)
+ return ret;
+
+ input_event(cs42l52->beep, EV_SND, SND_TONE, time);
+
+ return count;
+}
+
+static DEVICE_ATTR(beep, 0200, NULL, cs42l52_beep_set);
+
+static void cs42l52_init_beep(struct snd_soc_codec *codec)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ cs42l52->beep = devm_input_allocate_device(codec->dev);
+ if (!cs42l52->beep) {
+ dev_err(codec->dev, "Failed to allocate beep device\n");
+ return;
+ }
+
+ INIT_WORK(&cs42l52->beep_work, cs42l52_beep_work);
+ cs42l52->beep_rate = 0;
+
+ cs42l52->beep->name = "CS42L52 Beep Generator";
+ cs42l52->beep->phys = dev_name(codec->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);
+
+ ret = input_register_device(cs42l52->beep);
+ if (ret != 0) {
+ cs42l52->beep = NULL;
+ dev_err(codec->dev, "Failed to register beep device\n");
+ }
+
+ ret = device_create_file(codec->dev, &dev_attr_beep);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to create keyclick file: %d\n",
+ ret);
+ }
+}
+
+static void cs42l52_free_beep(struct snd_soc_codec *codec)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+
+ device_remove_file(codec->dev, &dev_attr_beep);
+ cancel_work_sync(&cs42l52->beep_work);
+ cs42l52->beep = NULL;
+
+ snd_soc_update_bits(codec, CS42L52_BEEP_TONE_CTL,
+ CS42L52_BEEP_EN_MASK, 0);
+}
+
+static int cs42l52_probe(struct snd_soc_codec *codec)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(cs42l52->regmap, true);
+
+ cs42l52_add_mic_controls(codec);
+
+ cs42l52_init_beep(codec);
+
+ cs42l52->sysclk = CS42L52_DEFAULT_CLK;
+ cs42l52->config.format = CS42L52_DEFAULT_FORMAT;
+
+ return 0;
+}
+
+static int cs42l52_remove(struct snd_soc_codec *codec)
+{
+ cs42l52_free_beep(codec);
+
+ return 0;
+}
+
+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),
+};
+
+/* Current and threshold powerup sequence Pg37 */
+static const struct reg_default cs42l52_threshold_patch[] = {
+
+ { 0x00, 0x99 },
+ { 0x3E, 0xBA },
+ { 0x47, 0x80 },
+ { 0x32, 0xBB },
+ { 0x32, 0x3B },
+ { 0x00, 0x00 },
+
+};
+
+static const struct regmap_config cs42l52_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS42L52_MAX_REGISTER,
+ .reg_defaults = cs42l52_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs42l52_reg_defaults),
+ .readable_reg = cs42l52_readable_register,
+ .volatile_reg = cs42l52_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct cs42l52_private *cs42l52;
+ struct cs42l52_platform_data *pdata = dev_get_platdata(&i2c_client->dev);
+ int ret;
+ unsigned int devid = 0;
+ unsigned int reg;
+ u32 val32;
+
+ cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l52_private),
+ GFP_KERNEL);
+ if (cs42l52 == NULL)
+ return -ENOMEM;
+ cs42l52->dev = &i2c_client->dev;
+
+ cs42l52->regmap = devm_regmap_init_i2c(i2c_client, &cs42l52_regmap);
+ if (IS_ERR(cs42l52->regmap)) {
+ ret = PTR_ERR(cs42l52->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+ if (pdata) {
+ cs42l52->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(&i2c_client->dev,
+ sizeof(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;
+ }
+
+ if (cs42l52->pdata.reset_gpio) {
+ ret = devm_gpio_request_one(&i2c_client->dev,
+ cs42l52->pdata.reset_gpio,
+ GPIOF_OUT_INIT_HIGH,
+ "CS42L52 /RST");
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n",
+ cs42l52->pdata.reset_gpio, ret);
+ return ret;
+ }
+ gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 0);
+ gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 1);
+ }
+
+ i2c_set_clientdata(i2c_client, cs42l52);
+
+ ret = regmap_register_patch(cs42l52->regmap, cs42l52_threshold_patch,
+ ARRAY_SIZE(cs42l52_threshold_patch));
+ if (ret != 0)
+ dev_warn(cs42l52->dev, "Failed to apply regmap patch: %d\n",
+ ret);
+
+ ret = regmap_read(cs42l52->regmap, CS42L52_CHIP, &reg);
+ devid = reg & CS42L52_CHIP_ID_MASK;
+ if (devid != CS42L52_CHIP_ID) {
+ ret = -ENODEV;
+ dev_err(&i2c_client->dev,
+ "CS42L52 Device ID (%X). Expected %X\n",
+ devid, CS42L52_CHIP_ID);
+ return ret;
+ }
+
+ dev_info(&i2c_client->dev, "Cirrus Logic CS42L52, Revision: %02X\n",
+ reg & CS42L52_CHIP_REV_MASK);
+
+ /* Set Platform Data */
+ if (cs42l52->pdata.mica_diff_cfg)
+ regmap_update_bits(cs42l52->regmap, CS42L52_MICA_CTL,
+ CS42L52_MIC_CTL_TYPE_MASK,
+ cs42l52->pdata.mica_diff_cfg <<
+ CS42L52_MIC_CTL_TYPE_SHIFT);
+
+ if (cs42l52->pdata.micb_diff_cfg)
+ regmap_update_bits(cs42l52->regmap, CS42L52_MICB_CTL,
+ CS42L52_MIC_CTL_TYPE_MASK,
+ cs42l52->pdata.micb_diff_cfg <<
+ CS42L52_MIC_CTL_TYPE_SHIFT);
+
+ if (cs42l52->pdata.chgfreq)
+ regmap_update_bits(cs42l52->regmap, CS42L52_CHARGE_PUMP,
+ CS42L52_CHARGE_PUMP_MASK,
+ cs42l52->pdata.chgfreq <<
+ CS42L52_CHARGE_PUMP_SHIFT);
+
+ if (cs42l52->pdata.micbias_lvl)
+ regmap_update_bits(cs42l52->regmap, CS42L52_IFACE_CTL2,
+ CS42L52_IFACE_CTL2_BIAS_LVL,
+ cs42l52->pdata.micbias_lvl);
+
+ 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;
+}
+
+static const struct of_device_id cs42l52_of_match[] = {
+ { .compatible = "cirrus,cs42l52", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs42l52_of_match);
+
+
+static const struct i2c_device_id cs42l52_id[] = {
+ { "cs42l52", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs42l52_id);
+
+static struct i2c_driver cs42l52_i2c_driver = {
+ .driver = {
+ .name = "cs42l52",
+ .owner = THIS_MODULE,
+ .of_match_table = cs42l52_of_match,
+ },
+ .id_table = cs42l52_id,
+ .probe = cs42l52_i2c_probe,
+ .remove = cs42l52_i2c_remove,
+};
+
+module_i2c_driver(cs42l52_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L52 driver");
+MODULE_AUTHOR("Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs42l52.h b/kernel/sound/soc/codecs/cs42l52.h
new file mode 100644
index 000000000..ac445993e
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs42l52.h
@@ -0,0 +1,274 @@
+/*
+ * cs42l52.h -- CS42L52 ALSA SoC audio driver
+ *
+ * Copyright 2012 CirrusLogic, Inc.
+ *
+ * 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__
+#define __CS42L52_H__
+
+#define CS42L52_NAME "CS42L52"
+#define CS42L52_DEFAULT_CLK 12000000
+#define CS42L52_MIN_CLK 11000000
+#define CS42L52_MAX_CLK 27000000
+#define CS42L52_DEFAULT_FORMAT SNDRV_PCM_FMTBIT_S16_LE
+#define CS42L52_DEFAULT_MAX_CHANS 2
+#define CS42L52_SYSCLK 1
+
+#define CS42L52_CHIP_SWICTH (1 << 17)
+#define CS42L52_ALL_IN_ONE (1 << 16)
+#define CS42L52_CHIP_ONE 0x00
+#define CS42L52_CHIP_TWO 0x01
+#define CS42L52_CHIP_THR 0x02
+#define CS42L52_CHIP_MASK 0x0f
+
+#define CS42L52_FIX_BITS_CTL 0x00
+#define CS42L52_CHIP 0x01
+#define CS42L52_CHIP_ID 0xE0
+#define CS42L52_CHIP_ID_MASK 0xF8
+#define CS42L52_CHIP_REV_A0 0x00
+#define CS42L52_CHIP_REV_A1 0x01
+#define CS42L52_CHIP_REV_B0 0x02
+#define CS42L52_CHIP_REV_MASK 0x07
+
+#define CS42L52_PWRCTL1 0x02
+#define CS42L52_PWRCTL1_PDN_ALL 0x9F
+#define CS42L52_PWRCTL1_PDN_CHRG 0x80
+#define CS42L52_PWRCTL1_PDN_PGAB 0x10
+#define CS42L52_PWRCTL1_PDN_PGAA 0x08
+#define CS42L52_PWRCTL1_PDN_ADCB 0x04
+#define CS42L52_PWRCTL1_PDN_ADCA 0x02
+#define CS42L52_PWRCTL1_PDN_CODEC 0x01
+
+#define CS42L52_PWRCTL2 0x03
+#define CS42L52_PWRCTL2_OVRDB (1 << 4)
+#define CS42L52_PWRCTL2_OVRDA (1 << 3)
+#define CS42L52_PWRCTL2_PDN_MICB (1 << 2)
+#define CS42L52_PWRCTL2_PDN_MICB_SHIFT 2
+#define CS42L52_PWRCTL2_PDN_MICA (1 << 1)
+#define CS42L52_PWRCTL2_PDN_MICA_SHIFT 1
+#define CS42L52_PWRCTL2_PDN_MICBIAS (1 << 0)
+#define CS42L52_PWRCTL2_PDN_MICBIAS_SHIFT 0
+
+#define CS42L52_PWRCTL3 0x04
+#define CS42L52_PWRCTL3_HPB_PDN_SHIFT 6
+#define CS42L52_PWRCTL3_HPB_ON_LOW 0x00
+#define CS42L52_PWRCTL3_HPB_ON_HIGH 0x01
+#define CS42L52_PWRCTL3_HPB_ALWAYS_ON 0x02
+#define CS42L52_PWRCTL3_HPB_ALWAYS_OFF 0x03
+#define CS42L52_PWRCTL3_HPA_PDN_SHIFT 4
+#define CS42L52_PWRCTL3_HPA_ON_LOW 0x00
+#define CS42L52_PWRCTL3_HPA_ON_HIGH 0x01
+#define CS42L52_PWRCTL3_HPA_ALWAYS_ON 0x02
+#define CS42L52_PWRCTL3_HPA_ALWAYS_OFF 0x03
+#define CS42L52_PWRCTL3_SPKB_PDN_SHIFT 2
+#define CS42L52_PWRCTL3_SPKB_ON_LOW 0x00
+#define CS42L52_PWRCTL3_SPKB_ON_HIGH 0x01
+#define CS42L52_PWRCTL3_SPKB_ALWAYS_ON 0x02
+#define CS42L52_PWRCTL3_PDN_SPKB (1 << 2)
+#define CS42L52_PWRCTL3_PDN_SPKA (1 << 0)
+#define CS42L52_PWRCTL3_SPKA_PDN_SHIFT 0
+#define CS42L52_PWRCTL3_SPKA_ON_LOW 0x00
+#define CS42L52_PWRCTL3_SPKA_ON_HIGH 0x01
+#define CS42L52_PWRCTL3_SPKA_ALWAYS_ON 0x02
+
+#define CS42L52_DEFAULT_OUTPUT_STATE 0x05
+#define CS42L52_PWRCTL3_CONF_MASK 0x03
+
+#define CS42L52_CLK_CTL 0x05
+#define CLK_AUTODECT_ENABLE (1 << 7)
+#define CLK_SPEED_SHIFT 5
+#define CLK_DS_MODE 0x00
+#define CLK_SS_MODE 0x01
+#define CLK_HS_MODE 0x02
+#define CLK_QS_MODE 0x03
+#define CLK_32K_SR_SHIFT 4
+#define CLK_32K 0x01
+#define CLK_NO_32K 0x00
+#define CLK_27M_MCLK_SHIFT 3
+#define CLK_27M_MCLK 0x01
+#define CLK_NO_27M 0x00
+#define CLK_RATIO_SHIFT 1
+#define CLK_R_128 0x00
+#define CLK_R_125 0x01
+#define CLK_R_132 0x02
+#define CLK_R_136 0x03
+
+#define CS42L52_IFACE_CTL1 0x06
+#define CS42L52_IFACE_CTL1_MASTER (1 << 7)
+#define CS42L52_IFACE_CTL1_SLAVE (0 << 7)
+#define CS42L52_IFACE_CTL1_INV_SCLK (1 << 6)
+#define CS42L52_IFACE_CTL1_ADC_FMT_I2S (1 << 5)
+#define CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J (0 << 5)
+#define CS42L52_IFACE_CTL1_DSP_MODE_EN (1 << 4)
+#define CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J (0 << 2)
+#define CS42L52_IFACE_CTL1_DAC_FMT_I2S (1 << 2)
+#define CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J (2 << 2)
+#define CS42L52_IFACE_CTL1_WL_32BIT (0x00)
+#define CS42L52_IFACE_CTL1_WL_24BIT (0x01)
+#define CS42L52_IFACE_CTL1_WL_20BIT (0x02)
+#define CS42L52_IFACE_CTL1_WL_16BIT (0x03)
+#define CS42L52_IFACE_CTL1_WL_MASK 0xFFFF
+
+#define CS42L52_IFACE_CTL2 0x07
+#define CS42L52_IFACE_CTL2_SC_MC_EQ (1 << 6)
+#define CS42L52_IFACE_CTL2_LOOPBACK (1 << 5)
+#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_EN (0 << 4)
+#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_HIZ (1 << 4)
+#define CS42L52_IFACE_CTL2_HP_SW_INV (1 << 3)
+#define CS42L52_IFACE_CTL2_BIAS_LVL 0x07
+
+#define CS42L52_ADC_PGA_A 0x08
+#define CS42L52_ADC_PGA_B 0x09
+#define CS42L52_ADC_SEL_SHIFT 5
+#define CS42L52_ADC_SEL_AIN1 0x00
+#define CS42L52_ADC_SEL_AIN2 0x01
+#define CS42L52_ADC_SEL_AIN3 0x02
+#define CS42L52_ADC_SEL_AIN4 0x03
+#define CS42L52_ADC_SEL_PGA 0x04
+
+#define CS42L52_ANALOG_HPF_CTL 0x0A
+#define CS42L52_HPF_CTL_ANLGSFTB (1 << 3)
+#define CS42L52_HPF_CTL_ANLGSFTA (1 << 0)
+
+#define CS42L52_ADC_HPF_FREQ 0x0B
+#define CS42L52_ADC_MISC_CTL 0x0C
+#define CS42L52_ADC_MISC_CTL_SOURCE_DSP (1 << 6)
+
+#define CS42L52_PB_CTL1 0x0D
+#define CS42L52_PB_CTL1_HP_GAIN_SHIFT 5
+#define CS42L52_PB_CTL1_HP_GAIN_03959 0x00
+#define CS42L52_PB_CTL1_HP_GAIN_04571 0x01
+#define CS42L52_PB_CTL1_HP_GAIN_05111 0x02
+#define CS42L52_PB_CTL1_HP_GAIN_06047 0x03
+#define CS42L52_PB_CTL1_HP_GAIN_07099 0x04
+#define CS42L52_PB_CTL1_HP_GAIN_08399 0x05
+#define CS42L52_PB_CTL1_HP_GAIN_10000 0x06
+#define CS42L52_PB_CTL1_HP_GAIN_11430 0x07
+#define CS42L52_PB_CTL1_INV_PCMB (1 << 3)
+#define CS42L52_PB_CTL1_INV_PCMA (1 << 2)
+#define CS42L52_PB_CTL1_MSTB_MUTE (1 << 1)
+#define CS42L52_PB_CTL1_MSTA_MUTE (1 << 0)
+#define CS42L52_PB_CTL1_MUTE_MASK 0x03
+#define CS42L52_PB_CTL1_MUTE 3
+#define CS42L52_PB_CTL1_UNMUTE 0
+
+#define CS42L52_MISC_CTL 0x0E
+#define CS42L52_MISC_CTL_DEEMPH (1 << 2)
+#define CS42L52_MISC_CTL_DIGSFT (1 << 1)
+#define CS42L52_MISC_CTL_DIGZC (1 << 0)
+
+#define CS42L52_PB_CTL2 0x0F
+#define CS42L52_PB_CTL2_HPB_MUTE (1 << 7)
+#define CS42L52_PB_CTL2_HPA_MUTE (1 << 6)
+#define CS42L52_PB_CTL2_SPKB_MUTE (1 << 5)
+#define CS42L52_PB_CTL2_SPKA_MUTE (1 << 4)
+#define CS42L52_PB_CTL2_SPK_SWAP (1 << 2)
+#define CS42L52_PB_CTL2_SPK_MONO (1 << 1)
+#define CS42L52_PB_CTL2_SPK_MUTE50 (1 << 0)
+
+#define CS42L52_MICA_CTL 0x10
+#define CS42L52_MICB_CTL 0x11
+#define CS42L52_MIC_CTL_MIC_SEL_MASK 0xBF
+#define CS42L52_MIC_CTL_MIC_SEL_SHIFT 6
+#define CS42L52_MIC_CTL_TYPE_MASK 0x20
+#define CS42L52_MIC_CTL_TYPE_SHIFT 5
+
+
+#define CS42L52_PGAA_CTL 0x12
+#define CS42L52_PGAB_CTL 0x13
+#define CS42L52_PGAX_CTL_VOL_12DB 24
+#define CS42L52_PGAX_CTL_VOL_6DB 12 /*step size 0.5db*/
+
+#define CS42L52_PASSTHRUA_VOL 0x14
+#define CS42L52_PASSTHRUB_VOL 0x15
+
+#define CS42L52_ADCA_VOL 0x16
+#define CS42L52_ADCB_VOL 0x17
+#define CS42L52_ADCX_VOL_24DB 24 /*step size 1db*/
+#define CS42L52_ADCX_VOL_12DB 12
+#define CS42L52_ADCX_VOL_6DB 6
+
+#define CS42L52_ADCA_MIXER_VOL 0x18
+#define CS42L52_ADCB_MIXER_VOL 0x19
+#define CS42L52_ADC_MIXER_VOL_12DB 0x18
+
+#define CS42L52_PCMA_MIXER_VOL 0x1A
+#define CS42L52_PCMB_MIXER_VOL 0x1B
+
+#define CS42L52_BEEP_FREQ 0x1C
+#define CS42L52_BEEP_VOL 0x1D
+#define CS42L52_BEEP_TONE_CTL 0x1E
+#define CS42L52_BEEP_RATE_SHIFT 4
+#define CS42L52_BEEP_RATE_MASK 0x0F
+
+#define CS42L52_TONE_CTL 0x1F
+#define CS42L52_BEEP_EN_MASK 0x3F
+
+#define CS42L52_MASTERA_VOL 0x20
+#define CS42L52_MASTERB_VOL 0x21
+
+#define CS42L52_HPA_VOL 0x22
+#define CS42L52_HPB_VOL 0x23
+#define CS42L52_DEFAULT_HP_VOL 0xF0
+
+#define CS42L52_SPKA_VOL 0x24
+#define CS42L52_SPKB_VOL 0x25
+#define CS42L52_DEFAULT_SPK_VOL 0xF0
+
+#define CS42L52_ADC_PCM_MIXER 0x26
+
+#define CS42L52_LIMITER_CTL1 0x27
+#define CS42L52_LIMITER_CTL2 0x28
+#define CS42L52_LIMITER_AT_RATE 0x29
+
+#define CS42L52_ALC_CTL 0x2A
+#define CS42L52_ALC_CTL_ALCB_ENABLE_SHIFT 7
+#define CS42L52_ALC_CTL_ALCA_ENABLE_SHIFT 6
+#define CS42L52_ALC_CTL_FASTEST_ATTACK 0
+
+#define CS42L52_ALC_RATE 0x2B
+#define CS42L52_ALC_SLOWEST_RELEASE 0x3F
+
+#define CS42L52_ALC_THRESHOLD 0x2C
+#define CS42L52_ALC_MAX_RATE_SHIFT 5
+#define CS42L52_ALC_MIN_RATE_SHIFT 2
+#define CS42L52_ALC_RATE_0DB 0
+#define CS42L52_ALC_RATE_3DB 1
+#define CS42L52_ALC_RATE_6DB 2
+
+#define CS42L52_NOISE_GATE_CTL 0x2D
+#define CS42L52_NG_ENABLE_SHIFT 6
+#define CS42L52_NG_THRESHOLD_SHIFT 2
+#define CS42L52_NG_MIN_70DB 2
+#define CS42L52_NG_DELAY_SHIFT 0
+#define CS42L52_NG_DELAY_100MS 1
+
+#define CS42L52_CLK_STATUS 0x2E
+#define CS42L52_BATT_COMPEN 0x2F
+
+#define CS42L52_BATT_LEVEL 0x30
+#define CS42L52_SPK_STATUS 0x31
+#define CS42L52_SPK_STATUS_PIN_SHIFT 3
+#define CS42L52_SPK_STATUS_PIN_HIGH 1
+
+#define CS42L52_TEM_CTL 0x32
+#define CS42L52_TEM_CTL_SET 0x80
+#define CS42L52_THE_FOLDBACK 0x33
+#define CS42L52_CHARGE_PUMP 0x34
+#define CS42L52_CHARGE_PUMP_MASK 0xF0
+#define CS42L52_CHARGE_PUMP_SHIFT 4
+#define CS42L52_FIX_BITS1 0x3E
+#define CS42L52_FIX_BITS2 0x47
+
+#define CS42L52_MAX_REGISTER 0x47
+
+#endif
diff --git a/kernel/sound/soc/codecs/cs42l56.c b/kernel/sound/soc/codecs/cs42l56.c
new file mode 100644
index 000000000..cbc654fe4
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs42l56.c
@@ -0,0 +1,1424 @@
+/*
+ * 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/i2c.h>
+#include <linux/input.h>
+#include <linux/regmap.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/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
+static const char *const cs42l56_supply_names[CS42L56_NUM_SUPPLIES] = {
+ "VA",
+ "VCP",
+ "VLDO",
+};
+
+struct cs42l56_private {
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ struct device *dev;
+ struct cs42l56_platform_data pdata;
+ struct regulator_bulk_data supplies[CS42L56_NUM_SUPPLIES];
+ u32 mclk;
+ u8 mclk_prediv;
+ u8 mclk_div2;
+ u8 mclk_ratio;
+ u8 iface;
+ u8 iface_fmt;
+ u8 iface_inv;
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ struct input_dev *beep;
+ struct work_struct beep_work;
+ int beep_rate;
+#endif
+};
+
+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 */
+ { 6, 0x0b }, /* r06 - Clocking Ctl 2 */
+ { 7, 0x00 }, /* r07 - Serial Format */
+ { 8, 0x05 }, /* r08 - Class H Ctl */
+ { 9, 0x0c }, /* r09 - Misc Ctl */
+ { 10, 0x80 }, /* r0a - INT Status */
+ { 11, 0x00 }, /* r0b - Playback Ctl */
+ { 12, 0x0c }, /* r0c - DSP Mute Ctl */
+ { 13, 0x00 }, /* r0d - ADCA Mixer Volume */
+ { 14, 0x00 }, /* r0e - ADCB Mixer Volume */
+ { 15, 0x00 }, /* r0f - PCMA Mixer Volume */
+ { 16, 0x00 }, /* r10 - PCMB Mixer Volume */
+ { 17, 0x00 }, /* r11 - Analog Input Advisory Volume */
+ { 18, 0x00 }, /* r12 - Digital Input Advisory Volume */
+ { 19, 0x00 }, /* r13 - Master A Volume */
+ { 20, 0x00 }, /* r14 - Master B Volume */
+ { 21, 0x00 }, /* r15 - Beep Freq / On Time */
+ { 22, 0x00 }, /* r16 - Beep Volume / Off Time */
+ { 23, 0x00 }, /* r17 - Beep Tone Ctl */
+ { 24, 0x88 }, /* r18 - Tone Ctl */
+ { 25, 0x00 }, /* r19 - Channel Mixer & Swap */
+ { 26, 0x00 }, /* r1a - AIN Ref Config / ADC Mux */
+ { 27, 0xa0 }, /* r1b - High-Pass Filter Ctl */
+ { 28, 0x00 }, /* r1c - Misc ADC Ctl */
+ { 29, 0x00 }, /* r1d - Gain & Bias Ctl */
+ { 30, 0x00 }, /* r1e - PGAA Mux & Volume */
+ { 31, 0x00 }, /* r1f - PGAB Mux & Volume */
+ { 32, 0x00 }, /* r20 - ADCA Attenuator */
+ { 33, 0x00 }, /* r21 - ADCB Attenuator */
+ { 34, 0x00 }, /* r22 - ALC Enable & Attack Rate */
+ { 35, 0xbf }, /* r23 - ALC Release Rate */
+ { 36, 0x00 }, /* r24 - ALC Threshold */
+ { 37, 0x00 }, /* r25 - Noise Gate Ctl */
+ { 38, 0x00 }, /* r26 - ALC, Limiter, SFT, ZeroCross */
+ { 39, 0x00 }, /* r27 - Analog Mute, LO & HP Mux */
+ { 40, 0x00 }, /* r28 - HP A Volume */
+ { 41, 0x00 }, /* r29 - HP B Volume */
+ { 42, 0x00 }, /* r2a - LINEOUT A Volume */
+ { 43, 0x00 }, /* r2b - LINEOUT B Volume */
+ { 44, 0x00 }, /* r2c - Limit Threshold Ctl */
+ { 45, 0x7f }, /* r2d - Limiter Ctl & Release Rate */
+ { 46, 0x00 }, /* r2e - Limiter Attack Rate */
+};
+
+static bool cs42l56_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L56_CHIP_ID_1:
+ 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:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l56_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L56_INT_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(beep_tlv, -5000, 200, 0);
+static DECLARE_TLV_DB_SCALE(hl_tlv, -6000, 50, 0);
+static DECLARE_TLV_DB_SCALE(adv_tlv, -10200, 50, 0);
+static DECLARE_TLV_DB_SCALE(adc_tlv, -9600, 100, 0);
+static DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
+static DECLARE_TLV_DB_SCALE(preamp_tlv, 0, 1000, 0);
+static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
+
+static const unsigned int ngnb_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 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),
+ 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),
+ 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0),
+ 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0),
+};
+
+static const char * const beep_config_text[] = {
+ "Off", "Single", "Multiple", "Continuous"
+};
+
+static const struct soc_enum beep_config_enum =
+ SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 6,
+ ARRAY_SIZE(beep_config_text), beep_config_text);
+
+static const char * const beep_pitch_text[] = {
+ "C4", "C5", "D5", "E5", "F5", "G5", "A5", "B5",
+ "C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7"
+};
+
+static const struct soc_enum beep_pitch_enum =
+ SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_ONTIME, 4,
+ ARRAY_SIZE(beep_pitch_text), beep_pitch_text);
+
+static const char * const beep_ontime_text[] = {
+ "86 ms", "430 ms", "780 ms", "1.20 s", "1.50 s",
+ "1.80 s", "2.20 s", "2.50 s", "2.80 s", "3.20 s",
+ "3.50 s", "3.80 s", "4.20 s", "4.50 s", "4.80 s", "5.20 s"
+};
+
+static const struct soc_enum beep_ontime_enum =
+ SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_ONTIME, 0,
+ ARRAY_SIZE(beep_ontime_text), beep_ontime_text);
+
+static const char * const beep_offtime_text[] = {
+ "1.23 s", "2.58 s", "3.90 s", "5.20 s",
+ "6.60 s", "8.05 s", "9.35 s", "10.80 s"
+};
+
+static const struct soc_enum beep_offtime_enum =
+ SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_OFFTIME, 5,
+ ARRAY_SIZE(beep_offtime_text), beep_offtime_text);
+
+static const char * const beep_treble_text[] = {
+ "5kHz", "7kHz", "10kHz", "15kHz"
+};
+
+static const struct soc_enum beep_treble_enum =
+ SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 3,
+ ARRAY_SIZE(beep_treble_text), beep_treble_text);
+
+static const char * const beep_bass_text[] = {
+ "50Hz", "100Hz", "200Hz", "250Hz"
+};
+
+static const struct soc_enum beep_bass_enum =
+ SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 1,
+ ARRAY_SIZE(beep_bass_text), beep_bass_text);
+
+static const char * const 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"};
+
+static const struct soc_enum pgaa_mux_enum =
+ SOC_ENUM_SINGLE(CS42L56_PGAA_MUX_VOLUME, 0,
+ ARRAY_SIZE(pgaa_mux_text),
+ pgaa_mux_text);
+
+static const struct snd_kcontrol_new pgaa_mux =
+ SOC_DAPM_ENUM("Route", pgaa_mux_enum);
+
+static const char * const pgab_mux_text[] = {
+ "AIN1B", "AIN2B", "AIN3B"};
+
+static const struct soc_enum pgab_mux_enum =
+ SOC_ENUM_SINGLE(CS42L56_PGAB_MUX_VOLUME, 0,
+ ARRAY_SIZE(pgab_mux_text),
+ pgab_mux_text);
+
+static const struct snd_kcontrol_new pgab_mux =
+ SOC_DAPM_ENUM("Route", pgab_mux_enum);
+
+static const char * const adca_mux_text[] = {
+ "PGAA", "AIN1A", "AIN2A", "AIN3A"};
+
+static const struct soc_enum adca_mux_enum =
+ SOC_ENUM_SINGLE(CS42L56_AIN_REFCFG_ADC_MUX, 0,
+ ARRAY_SIZE(adca_mux_text),
+ adca_mux_text);
+
+static const struct snd_kcontrol_new adca_mux =
+ SOC_DAPM_ENUM("Route", adca_mux_enum);
+
+static const char * const adcb_mux_text[] = {
+ "PGAB", "AIN1B", "AIN2B", "AIN3B"};
+
+static const struct soc_enum adcb_mux_enum =
+ SOC_ENUM_SINGLE(CS42L56_AIN_REFCFG_ADC_MUX, 2,
+ ARRAY_SIZE(adcb_mux_text),
+ adcb_mux_text);
+
+static const struct snd_kcontrol_new adcb_mux =
+ SOC_DAPM_ENUM("Route", adcb_mux_enum);
+
+static const char * const left_swap_text[] = {
+ "Left", "LR 2", "Right"};
+
+static const char * const right_swap_text[] = {
+ "Right", "LR 2", "Left"};
+
+static const unsigned int swap_values[] = { 0, 1, 3 };
+
+static const struct soc_enum adca_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 0, 3,
+ ARRAY_SIZE(left_swap_text),
+ left_swap_text,
+ swap_values);
+static const struct snd_kcontrol_new adca_swap_mux =
+ SOC_DAPM_ENUM("Route", adca_swap_enum);
+
+static const struct soc_enum pcma_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 4, 3,
+ ARRAY_SIZE(left_swap_text),
+ left_swap_text,
+ swap_values);
+static const struct snd_kcontrol_new pcma_swap_mux =
+ SOC_DAPM_ENUM("Route", pcma_swap_enum);
+
+static const struct soc_enum adcb_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 2, 3,
+ ARRAY_SIZE(right_swap_text),
+ right_swap_text,
+ swap_values);
+static const struct snd_kcontrol_new adcb_swap_mux =
+ SOC_DAPM_ENUM("Route", adcb_swap_enum);
+
+static const struct soc_enum pcmb_swap_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 6, 3,
+ ARRAY_SIZE(right_swap_text),
+ right_swap_text,
+ swap_values);
+static const struct snd_kcontrol_new pcmb_swap_mux =
+ SOC_DAPM_ENUM("Route", pcmb_swap_enum);
+
+static const struct snd_kcontrol_new hpa_switch =
+ SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 6, 1, 1);
+
+static const struct snd_kcontrol_new hpb_switch =
+ SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 4, 1, 1);
+
+static const struct snd_kcontrol_new loa_switch =
+ SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 2, 1, 1);
+
+static const struct snd_kcontrol_new lob_switch =
+ SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 0, 1, 1);
+
+static const char * const hploa_input_text[] = {
+ "DACA", "PGAA"};
+
+static const struct soc_enum lineouta_input_enum =
+ SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 2,
+ ARRAY_SIZE(hploa_input_text),
+ hploa_input_text);
+
+static const struct snd_kcontrol_new lineouta_input =
+ SOC_DAPM_ENUM("Route", lineouta_input_enum);
+
+static const struct soc_enum hpa_input_enum =
+ SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 0,
+ ARRAY_SIZE(hploa_input_text),
+ hploa_input_text);
+
+static const struct snd_kcontrol_new hpa_input =
+ SOC_DAPM_ENUM("Route", hpa_input_enum);
+
+static const char * const hplob_input_text[] = {
+ "DACB", "PGAB"};
+
+static const struct soc_enum lineoutb_input_enum =
+ SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 3,
+ ARRAY_SIZE(hplob_input_text),
+ hplob_input_text);
+
+static const struct snd_kcontrol_new lineoutb_input =
+ SOC_DAPM_ENUM("Route", lineoutb_input_enum);
+
+static const struct soc_enum hpb_input_enum =
+ SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 1,
+ ARRAY_SIZE(hplob_input_text),
+ hplob_input_text);
+
+static const struct snd_kcontrol_new hpb_input =
+ SOC_DAPM_ENUM("Route", hpb_input_enum);
+
+static const char * const dig_mux_text[] = {
+ "ADC", "DSP"};
+
+static const struct soc_enum dig_mux_enum =
+ SOC_ENUM_SINGLE(CS42L56_MISC_CTL, 7,
+ ARRAY_SIZE(dig_mux_text),
+ dig_mux_text);
+
+static const struct snd_kcontrol_new dig_mux =
+ SOC_DAPM_ENUM("Route", dig_mux_enum);
+
+static const char * const hpf_freq_text[] = {
+ "1.8Hz", "119Hz", "236Hz", "464Hz"
+};
+
+static const struct soc_enum hpfa_freq_enum =
+ SOC_ENUM_SINGLE(CS42L56_HPF_CTL, 0,
+ ARRAY_SIZE(hpf_freq_text), hpf_freq_text);
+
+static const struct soc_enum hpfb_freq_enum =
+ SOC_ENUM_SINGLE(CS42L56_HPF_CTL, 2,
+ ARRAY_SIZE(hpf_freq_text), hpf_freq_text);
+
+static const char * const ng_delay_text[] = {
+ "50ms", "100ms", "150ms", "200ms"
+};
+
+static const struct soc_enum ng_delay_enum =
+ SOC_ENUM_SINGLE(CS42L56_NOISE_GATE_CTL, 0,
+ ARRAY_SIZE(ng_delay_text), ng_delay_text);
+
+static const struct snd_kcontrol_new cs42l56_snd_controls[] = {
+
+ SOC_DOUBLE_R_SX_TLV("Master Volume", CS42L56_MASTER_A_VOLUME,
+ CS42L56_MASTER_B_VOLUME, 0, 0x34, 0xE4, adv_tlv),
+ SOC_DOUBLE("Master Mute Switch", CS42L56_DSP_MUTE_CTL, 0, 1, 1, 1),
+
+ SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume", CS42L56_ADCA_MIX_VOLUME,
+ CS42L56_ADCB_MIX_VOLUME, 0, 0x88, 0x90, hl_tlv),
+ SOC_DOUBLE("ADC Mixer Mute Switch", CS42L56_DSP_MUTE_CTL, 6, 7, 1, 1),
+
+ SOC_DOUBLE_R_SX_TLV("PCM Mixer Volume", CS42L56_PCMA_MIX_VOLUME,
+ CS42L56_PCMB_MIX_VOLUME, 0, 0x88, 0x90, hl_tlv),
+ SOC_DOUBLE("PCM Mixer Mute Switch", CS42L56_DSP_MUTE_CTL, 4, 5, 1, 1),
+
+ SOC_SINGLE_TLV("Analog Advisory Volume",
+ CS42L56_ANAINPUT_ADV_VOLUME, 0, 0x00, 1, adv_tlv),
+ SOC_SINGLE_TLV("Digital Advisory Volume",
+ CS42L56_DIGINPUT_ADV_VOLUME, 0, 0x00, 1, adv_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("PGA Volume", CS42L56_PGAA_MUX_VOLUME,
+ CS42L56_PGAB_MUX_VOLUME, 0, 0x34, 0x24, pga_tlv),
+ SOC_DOUBLE_R_TLV("ADC Volume", CS42L56_ADCA_ATTENUATOR,
+ CS42L56_ADCB_ATTENUATOR, 0, 0x00, 1, adc_tlv),
+ SOC_DOUBLE("ADC Mute Switch", CS42L56_MISC_ADC_CTL, 2, 3, 1, 1),
+ SOC_DOUBLE("ADC Boost Switch", CS42L56_GAIN_BIAS_CTL, 3, 2, 1, 1),
+
+ SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L56_HPA_VOLUME,
+ CS42L56_HPB_VOLUME, 0, 0x84, 0x48, hl_tlv),
+ SOC_DOUBLE_R_SX_TLV("LineOut Volume", CS42L56_LOA_VOLUME,
+ CS42L56_LOB_VOLUME, 0, 0x84, 0x48, hl_tlv),
+
+ SOC_SINGLE_TLV("Bass Shelving Volume", CS42L56_TONE_CTL,
+ 0, 0x00, 1, tone_tlv),
+ SOC_SINGLE_TLV("Treble Shelving Volume", CS42L56_TONE_CTL,
+ 4, 0x00, 1, tone_tlv),
+
+ SOC_DOUBLE_TLV("PGA Preamp Volume", CS42L56_GAIN_BIAS_CTL,
+ 4, 6, 0x02, 1, preamp_tlv),
+
+ SOC_SINGLE("DSP Switch", CS42L56_PLAYBACK_CTL, 7, 1, 1),
+ SOC_SINGLE("Gang Playback Switch", CS42L56_PLAYBACK_CTL, 4, 1, 1),
+ SOC_SINGLE("Gang ADC Switch", CS42L56_MISC_ADC_CTL, 7, 1, 1),
+ SOC_SINGLE("Gang PGA Switch", CS42L56_MISC_ADC_CTL, 6, 1, 1),
+
+ SOC_SINGLE("PCMA Invert", CS42L56_PLAYBACK_CTL, 2, 1, 1),
+ SOC_SINGLE("PCMB Invert", CS42L56_PLAYBACK_CTL, 3, 1, 1),
+ SOC_SINGLE("ADCA Invert", CS42L56_MISC_ADC_CTL, 2, 1, 1),
+ SOC_SINGLE("ADCB Invert", CS42L56_MISC_ADC_CTL, 3, 1, 1),
+
+ SOC_DOUBLE("HPF Switch", CS42L56_HPF_CTL, 5, 7, 1, 1),
+ SOC_DOUBLE("HPF Freeze Switch", CS42L56_HPF_CTL, 4, 6, 1, 1),
+ SOC_ENUM("HPFA Corner Freq", hpfa_freq_enum),
+ SOC_ENUM("HPFB Corner Freq", hpfb_freq_enum),
+
+ SOC_SINGLE("Analog Soft Ramp", CS42L56_MISC_CTL, 4, 1, 1),
+ SOC_DOUBLE("Analog Soft Ramp Disable", CS42L56_ALC_LIM_SFT_ZC,
+ 7, 5, 1, 1),
+ SOC_SINGLE("Analog Zero Cross", CS42L56_MISC_CTL, 3, 1, 1),
+ SOC_DOUBLE("Analog Zero Cross Disable", CS42L56_ALC_LIM_SFT_ZC,
+ 6, 4, 1, 1),
+ SOC_SINGLE("Digital Soft Ramp", CS42L56_MISC_CTL, 2, 1, 1),
+ SOC_SINGLE("Digital Soft Ramp Disable", CS42L56_ALC_LIM_SFT_ZC,
+ 3, 1, 1),
+
+ SOC_SINGLE("HL Deemphasis", CS42L56_PLAYBACK_CTL, 6, 1, 1),
+
+ SOC_SINGLE("ALC Switch", CS42L56_ALC_EN_ATTACK_RATE, 6, 1, 1),
+ SOC_SINGLE("ALC Limit All Switch", CS42L56_ALC_RELEASE_RATE, 7, 1, 1),
+ SOC_SINGLE_RANGE("ALC Attack", CS42L56_ALC_EN_ATTACK_RATE,
+ 0, 0, 0x3f, 0),
+ SOC_SINGLE_RANGE("ALC Release", CS42L56_ALC_RELEASE_RATE,
+ 0, 0x3f, 0, 0),
+ SOC_SINGLE_TLV("ALC MAX", CS42L56_ALC_THRESHOLD,
+ 5, 0x07, 1, alc_tlv),
+ SOC_SINGLE_TLV("ALC MIN", CS42L56_ALC_THRESHOLD,
+ 2, 0x07, 1, alc_tlv),
+
+ SOC_SINGLE("Limiter Switch", CS42L56_LIM_CTL_RELEASE_RATE, 7, 1, 1),
+ SOC_SINGLE("Limit All Switch", CS42L56_LIM_CTL_RELEASE_RATE, 6, 1, 1),
+ SOC_SINGLE_RANGE("Limiter Attack", CS42L56_LIM_ATTACK_RATE,
+ 0, 0, 0x3f, 0),
+ SOC_SINGLE_RANGE("Limiter Release", CS42L56_LIM_CTL_RELEASE_RATE,
+ 0, 0x3f, 0, 0),
+ SOC_SINGLE_TLV("Limiter MAX", CS42L56_LIM_THRESHOLD_CTL,
+ 5, 0x07, 1, alc_tlv),
+ SOC_SINGLE_TLV("Limiter Cushion", CS42L56_ALC_THRESHOLD,
+ 2, 0x07, 1, alc_tlv),
+
+ SOC_SINGLE("NG Switch", CS42L56_NOISE_GATE_CTL, 6, 1, 1),
+ SOC_SINGLE("NG All Switch", CS42L56_NOISE_GATE_CTL, 7, 1, 1),
+ SOC_SINGLE("NG Boost Switch", CS42L56_NOISE_GATE_CTL, 5, 1, 1),
+ SOC_SINGLE_TLV("NG Unboost Threshold", CS42L56_NOISE_GATE_CTL,
+ 2, 0x07, 1, ngnb_tlv),
+ SOC_SINGLE_TLV("NG Boost Threshold", CS42L56_NOISE_GATE_CTL,
+ 2, 0x07, 1, ngb_tlv),
+ SOC_ENUM("NG Delay", ng_delay_enum),
+
+ SOC_ENUM("Beep Config", beep_config_enum),
+ SOC_ENUM("Beep Pitch", beep_pitch_enum),
+ SOC_ENUM("Beep on Time", beep_ontime_enum),
+ SOC_ENUM("Beep off Time", beep_offtime_enum),
+ SOC_SINGLE_SX_TLV("Beep Volume", CS42L56_BEEP_FREQ_OFFTIME,
+ 0, 0x07, 0x23, beep_tlv),
+ SOC_SINGLE("Beep Tone Ctl Switch", CS42L56_BEEP_TONE_CFG, 0, 1, 1),
+ SOC_ENUM("Beep Treble Corner Freq", beep_treble_enum),
+ SOC_ENUM("Beep Bass Corner Freq", beep_bass_enum),
+
+};
+
+static const struct snd_soc_dapm_widget cs42l56_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SIGGEN("Beep"),
+ SND_SOC_DAPM_SUPPLY("VBUF", CS42L56_PWRCTL_1, 5, 1, NULL, 0),
+ SND_SOC_DAPM_MICBIAS("MIC1 Bias", CS42L56_PWRCTL_1, 4, 1),
+ SND_SOC_DAPM_SUPPLY("Charge Pump", CS42L56_PWRCTL_1, 3, 1, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("AIN1A"),
+ SND_SOC_DAPM_INPUT("AIN2A"),
+ SND_SOC_DAPM_INPUT("AIN1B"),
+ SND_SOC_DAPM_INPUT("AIN2B"),
+ SND_SOC_DAPM_INPUT("AIN3A"),
+ SND_SOC_DAPM_INPUT("AIN3B"),
+
+ SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("Digital Output Mux", SND_SOC_NOPM,
+ 0, 0, &dig_mux),
+
+ SND_SOC_DAPM_PGA("PGAA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGAB", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("PGAA Input Mux",
+ SND_SOC_NOPM, 0, 0, &pgaa_mux),
+ SND_SOC_DAPM_MUX("PGAB Input Mux",
+ SND_SOC_NOPM, 0, 0, &pgab_mux),
+
+ SND_SOC_DAPM_MUX("ADCA Mux", SND_SOC_NOPM,
+ 0, 0, &adca_mux),
+ SND_SOC_DAPM_MUX("ADCB Mux", SND_SOC_NOPM,
+ 0, 0, &adcb_mux),
+
+ SND_SOC_DAPM_ADC("ADCA", NULL, CS42L56_PWRCTL_1, 1, 1),
+ SND_SOC_DAPM_ADC("ADCB", NULL, CS42L56_PWRCTL_1, 2, 1),
+
+ SND_SOC_DAPM_MUX("ADCA Swap Mux", SND_SOC_NOPM, 0, 0,
+ &adca_swap_mux),
+ SND_SOC_DAPM_MUX("ADCB Swap Mux", SND_SOC_NOPM, 0, 0,
+ &adcb_swap_mux),
+
+ SND_SOC_DAPM_MUX("PCMA Swap Mux", SND_SOC_NOPM, 0, 0,
+ &pcma_swap_mux),
+ SND_SOC_DAPM_MUX("PCMB Swap Mux", SND_SOC_NOPM, 0, 0,
+ &pcmb_swap_mux),
+
+ SND_SOC_DAPM_DAC("DACA", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DACB", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPA"),
+ SND_SOC_DAPM_OUTPUT("LOA"),
+ SND_SOC_DAPM_OUTPUT("HPB"),
+ SND_SOC_DAPM_OUTPUT("LOB"),
+
+ SND_SOC_DAPM_SWITCH("Headphone Right",
+ CS42L56_PWRCTL_2, 4, 1, &hpb_switch),
+ SND_SOC_DAPM_SWITCH("Headphone Left",
+ CS42L56_PWRCTL_2, 6, 1, &hpa_switch),
+
+ SND_SOC_DAPM_SWITCH("Lineout Right",
+ CS42L56_PWRCTL_2, 0, 1, &lob_switch),
+ SND_SOC_DAPM_SWITCH("Lineout Left",
+ CS42L56_PWRCTL_2, 2, 1, &loa_switch),
+
+ SND_SOC_DAPM_MUX("LINEOUTA Input Mux", SND_SOC_NOPM,
+ 0, 0, &lineouta_input),
+ SND_SOC_DAPM_MUX("LINEOUTB Input Mux", SND_SOC_NOPM,
+ 0, 0, &lineoutb_input),
+ SND_SOC_DAPM_MUX("HPA Input Mux", SND_SOC_NOPM,
+ 0, 0, &hpa_input),
+ SND_SOC_DAPM_MUX("HPB Input Mux", SND_SOC_NOPM,
+ 0, 0, &hpb_input),
+
+};
+
+static const struct snd_soc_dapm_route cs42l56_audio_map[] = {
+
+ {"HiFi Capture", "DSP", "Digital Output Mux"},
+ {"HiFi Capture", "ADC", "Digital Output Mux"},
+
+ {"Digital Output Mux", NULL, "ADCA"},
+ {"Digital Output Mux", NULL, "ADCB"},
+
+ {"ADCB", NULL, "ADCB Swap Mux"},
+ {"ADCA", NULL, "ADCA Swap Mux"},
+
+ {"ADCA Swap Mux", NULL, "ADCA"},
+ {"ADCB Swap Mux", NULL, "ADCB"},
+
+ {"DACA", "Left", "ADCA Swap Mux"},
+ {"DACA", "LR 2", "ADCA Swap Mux"},
+ {"DACA", "Right", "ADCA Swap Mux"},
+
+ {"DACB", "Left", "ADCB Swap Mux"},
+ {"DACB", "LR 2", "ADCB Swap Mux"},
+ {"DACB", "Right", "ADCB Swap Mux"},
+
+ {"ADCA Mux", NULL, "AIN3A"},
+ {"ADCA Mux", NULL, "AIN2A"},
+ {"ADCA Mux", NULL, "AIN1A"},
+ {"ADCA Mux", NULL, "PGAA"},
+ {"ADCB Mux", NULL, "AIN3B"},
+ {"ADCB Mux", NULL, "AIN2B"},
+ {"ADCB Mux", NULL, "AIN1B"},
+ {"ADCB Mux", NULL, "PGAB"},
+
+ {"PGAA", "AIN1A", "PGAA Input Mux"},
+ {"PGAA", "AIN2A", "PGAA Input Mux"},
+ {"PGAA", "AIN3A", "PGAA Input Mux"},
+ {"PGAB", "AIN1B", "PGAB Input Mux"},
+ {"PGAB", "AIN2B", "PGAB Input Mux"},
+ {"PGAB", "AIN3B", "PGAB Input Mux"},
+
+ {"PGAA Input Mux", NULL, "AIN1A"},
+ {"PGAA Input Mux", NULL, "AIN2A"},
+ {"PGAA Input Mux", NULL, "AIN3A"},
+ {"PGAB Input Mux", NULL, "AIN1B"},
+ {"PGAB Input Mux", NULL, "AIN2B"},
+ {"PGAB Input Mux", NULL, "AIN3B"},
+
+ {"LOB", "Switch", "LINEOUTB Input Mux"},
+ {"LOA", "Switch", "LINEOUTA Input Mux"},
+
+ {"LINEOUTA Input Mux", "PGAA", "PGAA"},
+ {"LINEOUTB Input Mux", "PGAB", "PGAB"},
+ {"LINEOUTA Input Mux", "DACA", "DACA"},
+ {"LINEOUTB Input Mux", "DACB", "DACB"},
+
+ {"HPA", "Switch", "HPB Input Mux"},
+ {"HPB", "Switch", "HPA Input Mux"},
+
+ {"HPA Input Mux", "PGAA", "PGAA"},
+ {"HPB Input Mux", "PGAB", "PGAB"},
+ {"HPA Input Mux", "DACA", "DACA"},
+ {"HPB Input Mux", "DACB", "DACB"},
+
+ {"DACA", NULL, "PCMA Swap Mux"},
+ {"DACB", NULL, "PCMB Swap Mux"},
+
+ {"PCMB Swap Mux", "Left", "HiFi Playback"},
+ {"PCMB Swap Mux", "LR 2", "HiFi Playback"},
+ {"PCMB Swap Mux", "Right", "HiFi Playback"},
+
+ {"PCMA Swap Mux", "Left", "HiFi Playback"},
+ {"PCMA Swap Mux", "LR 2", "HiFi Playback"},
+ {"PCMA Swap Mux", "Right", "HiFi Playback"},
+
+};
+
+struct cs42l56_clk_para {
+ u32 mclk;
+ u32 srate;
+ u8 ratio;
+};
+
+static const struct cs42l56_clk_para clk_ratio_table[] = {
+ /* 8k */
+ { 6000000, 8000, CS42L56_MCLK_LRCLK_768 },
+ { 6144000, 8000, CS42L56_MCLK_LRCLK_750 },
+ { 12000000, 8000, CS42L56_MCLK_LRCLK_768 },
+ { 12288000, 8000, CS42L56_MCLK_LRCLK_750 },
+ { 24000000, 8000, CS42L56_MCLK_LRCLK_768 },
+ { 24576000, 8000, CS42L56_MCLK_LRCLK_750 },
+ /* 11.025k */
+ { 5644800, 11025, CS42L56_MCLK_LRCLK_512},
+ { 11289600, 11025, CS42L56_MCLK_LRCLK_512},
+ { 22579200, 11025, CS42L56_MCLK_LRCLK_512 },
+ /* 11.0294k */
+ { 6000000, 110294, CS42L56_MCLK_LRCLK_544 },
+ { 12000000, 110294, CS42L56_MCLK_LRCLK_544 },
+ { 24000000, 110294, CS42L56_MCLK_LRCLK_544 },
+ /* 12k */
+ { 6000000, 12000, CS42L56_MCLK_LRCLK_500 },
+ { 6144000, 12000, CS42L56_MCLK_LRCLK_512 },
+ { 12000000, 12000, CS42L56_MCLK_LRCLK_500 },
+ { 12288000, 12000, CS42L56_MCLK_LRCLK_512 },
+ { 24000000, 12000, CS42L56_MCLK_LRCLK_500 },
+ { 24576000, 12000, CS42L56_MCLK_LRCLK_512 },
+ /* 16k */
+ { 6000000, 16000, CS42L56_MCLK_LRCLK_375 },
+ { 6144000, 16000, CS42L56_MCLK_LRCLK_384 },
+ { 12000000, 16000, CS42L56_MCLK_LRCLK_375 },
+ { 12288000, 16000, CS42L56_MCLK_LRCLK_384 },
+ { 24000000, 16000, CS42L56_MCLK_LRCLK_375 },
+ { 24576000, 16000, CS42L56_MCLK_LRCLK_384 },
+ /* 22.050k */
+ { 5644800, 22050, CS42L56_MCLK_LRCLK_256 },
+ { 11289600, 22050, CS42L56_MCLK_LRCLK_256 },
+ { 22579200, 22050, CS42L56_MCLK_LRCLK_256 },
+ /* 22.0588k */
+ { 6000000, 220588, CS42L56_MCLK_LRCLK_272 },
+ { 12000000, 220588, CS42L56_MCLK_LRCLK_272 },
+ { 24000000, 220588, CS42L56_MCLK_LRCLK_272 },
+ /* 24k */
+ { 6000000, 24000, CS42L56_MCLK_LRCLK_250 },
+ { 6144000, 24000, CS42L56_MCLK_LRCLK_256 },
+ { 12000000, 24000, CS42L56_MCLK_LRCLK_250 },
+ { 12288000, 24000, CS42L56_MCLK_LRCLK_256 },
+ { 24000000, 24000, CS42L56_MCLK_LRCLK_250 },
+ { 24576000, 24000, CS42L56_MCLK_LRCLK_256 },
+ /* 32k */
+ { 6000000, 32000, CS42L56_MCLK_LRCLK_187P5 },
+ { 6144000, 32000, CS42L56_MCLK_LRCLK_192 },
+ { 12000000, 32000, CS42L56_MCLK_LRCLK_187P5 },
+ { 12288000, 32000, CS42L56_MCLK_LRCLK_192 },
+ { 24000000, 32000, CS42L56_MCLK_LRCLK_187P5 },
+ { 24576000, 32000, CS42L56_MCLK_LRCLK_192 },
+ /* 44.118k */
+ { 6000000, 44118, CS42L56_MCLK_LRCLK_136 },
+ { 12000000, 44118, CS42L56_MCLK_LRCLK_136 },
+ { 24000000, 44118, CS42L56_MCLK_LRCLK_136 },
+ /* 44.1k */
+ { 5644800, 44100, CS42L56_MCLK_LRCLK_128 },
+ { 11289600, 44100, CS42L56_MCLK_LRCLK_128 },
+ { 22579200, 44100, CS42L56_MCLK_LRCLK_128 },
+ /* 48k */
+ { 6000000, 48000, CS42L56_MCLK_LRCLK_125 },
+ { 6144000, 48000, CS42L56_MCLK_LRCLK_128 },
+ { 12000000, 48000, CS42L56_MCLK_LRCLK_125 },
+ { 12288000, 48000, CS42L56_MCLK_LRCLK_128 },
+ { 24000000, 48000, CS42L56_MCLK_LRCLK_125 },
+ { 24576000, 48000, CS42L56_MCLK_LRCLK_128 },
+};
+
+static int cs42l56_get_mclk_ratio(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(clk_ratio_table); i++) {
+ if (clk_ratio_table[i].mclk == mclk &&
+ clk_ratio_table[i].srate == rate)
+ return clk_ratio_table[i].ratio;
+ }
+ return -EINVAL;
+}
+
+static int cs42l56_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+
+ switch (freq) {
+ case CS42L56_MCLK_5P6448MHZ:
+ case CS42L56_MCLK_6MHZ:
+ case CS42L56_MCLK_6P144MHZ:
+ cs42l56->mclk_div2 = 0;
+ cs42l56->mclk_prediv = 0;
+ break;
+ case CS42L56_MCLK_11P2896MHZ:
+ case CS42L56_MCLK_12MHZ:
+ case CS42L56_MCLK_12P288MHZ:
+ cs42l56->mclk_div2 = CS42L56_MCLK_DIV2;
+ cs42l56->mclk_prediv = 0;
+ break;
+ case CS42L56_MCLK_22P5792MHZ:
+ case CS42L56_MCLK_24MHZ:
+ case CS42L56_MCLK_24P576MHZ:
+ cs42l56->mclk_div2 = CS42L56_MCLK_DIV2;
+ cs42l56->mclk_prediv = CS42L56_MCLK_PREDIV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ cs42l56->mclk = freq;
+
+ snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ CS42L56_MCLK_PREDIV_MASK,
+ cs42l56->mclk_prediv);
+ snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ CS42L56_MCLK_DIV2_MASK,
+ cs42l56->mclk_div2);
+
+ return 0;
+}
+
+static int cs42l56_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs42l56->iface = CS42L56_MASTER_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs42l56->iface = CS42L56_SLAVE_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cs42l56->iface_fmt = CS42L56_DIG_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ cs42l56->iface_fmt = CS42L56_DIG_FMT_LEFT_J;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* sclk inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ cs42l56->iface_inv = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cs42l56->iface_inv = CS42L56_SCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ CS42L56_MS_MODE_MASK, cs42l56->iface);
+ snd_soc_update_bits(codec, CS42L56_SERIAL_FMT,
+ CS42L56_DIG_FMT_MASK, cs42l56->iface_fmt);
+ snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ CS42L56_SCLK_INV_MASK, cs42l56->iface_inv);
+ return 0;
+}
+
+static int cs42l56_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute) {
+ /* Hit the DSP Mixer first */
+ snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL,
+ CS42L56_ADCAMIX_MUTE_MASK |
+ CS42L56_ADCBMIX_MUTE_MASK |
+ CS42L56_PCMAMIX_MUTE_MASK |
+ CS42L56_PCMBMIX_MUTE_MASK |
+ CS42L56_MSTB_MUTE_MASK |
+ CS42L56_MSTA_MUTE_MASK,
+ CS42L56_MUTE_ALL);
+ /* Mute ADC's */
+ snd_soc_update_bits(codec, 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,
+ CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
+ snd_soc_update_bits(codec, CS42L56_HPB_VOLUME,
+ CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
+ snd_soc_update_bits(codec, CS42L56_LOA_VOLUME,
+ CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
+ snd_soc_update_bits(codec, CS42L56_LOB_VOLUME,
+ CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
+ } else {
+ snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL,
+ CS42L56_ADCAMIX_MUTE_MASK |
+ CS42L56_ADCBMIX_MUTE_MASK |
+ CS42L56_PCMAMIX_MUTE_MASK |
+ CS42L56_PCMBMIX_MUTE_MASK |
+ CS42L56_MSTB_MUTE_MASK |
+ CS42L56_MSTA_MUTE_MASK,
+ CS42L56_UNMUTE);
+
+ snd_soc_update_bits(codec, CS42L56_MISC_ADC_CTL,
+ CS42L56_ADCA_MUTE_MASK |
+ CS42L56_ADCB_MUTE_MASK,
+ CS42L56_UNMUTE);
+
+ snd_soc_update_bits(codec, CS42L56_HPA_VOLUME,
+ CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
+ snd_soc_update_bits(codec, CS42L56_HPB_VOLUME,
+ CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
+ snd_soc_update_bits(codec, CS42L56_LOA_VOLUME,
+ CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
+ snd_soc_update_bits(codec, CS42L56_LOB_VOLUME,
+ CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
+ }
+ return 0;
+}
+
+static int cs42l56_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ int ratio;
+
+ ratio = cs42l56_get_mclk_ratio(cs42l56->mclk, params_rate(params));
+ if (ratio >= 0) {
+ snd_soc_update_bits(codec, CS42L56_CLKCTL_2,
+ CS42L56_CLK_RATIO_MASK, ratio);
+ } else {
+ dev_err(codec->dev, "unsupported mclk/sclk/lrclk ratio\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ CS42L56_MCLK_DIS_MASK, 0);
+ snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+ CS42L56_PDN_ALL_MASK, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ regcache_cache_only(cs42l56->regmap, false);
+ regcache_sync(cs42l56->regmap);
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies),
+ cs42l56->supplies);
+ if (ret != 0) {
+ dev_err(cs42l56->dev,
+ "Failed to enable regulators: %d\n",
+ ret);
+ return ret;
+ }
+ }
+ snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+ CS42L56_PDN_ALL_MASK, 1);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+ CS42L56_PDN_ALL_MASK, 1);
+ snd_soc_update_bits(codec, 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;
+}
+
+#define CS42L56_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define CS42L56_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+
+static struct snd_soc_dai_ops cs42l56_ops = {
+ .hw_params = cs42l56_pcm_hw_params,
+ .digital_mute = cs42l56_digital_mute,
+ .set_fmt = cs42l56_set_dai_fmt,
+ .set_sysclk = cs42l56_set_sysclk,
+};
+
+static struct snd_soc_dai_driver cs42l56_dai = {
+ .name = "cs42l56",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS42L56_RATES,
+ .formats = CS42L56_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS42L56_RATES,
+ .formats = CS42L56_FORMATS,
+ },
+ .ops = &cs42l56_ops,
+};
+
+static int beep_freq[] = {
+ 261, 522, 585, 667, 706, 774, 889, 1000,
+ 1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182
+};
+
+static void cs42l56_beep_work(struct work_struct *work)
+{
+ struct cs42l56_private *cs42l56 =
+ container_of(work, struct cs42l56_private, beep_work);
+ struct snd_soc_codec *codec = cs42l56->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int i;
+ int val = 0;
+ int best = 0;
+
+ if (cs42l56->beep_rate) {
+ for (i = 0; i < ARRAY_SIZE(beep_freq); i++) {
+ if (abs(cs42l56->beep_rate - beep_freq[i]) <
+ abs(cs42l56->beep_rate - beep_freq[best]))
+ best = i;
+ }
+
+ dev_dbg(codec->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");
+ snd_soc_dapm_disable_pin(dapm, "Beep");
+ }
+
+ snd_soc_update_bits(codec, CS42L56_BEEP_FREQ_ONTIME,
+ CS42L56_BEEP_FREQ_MASK, val);
+
+ snd_soc_dapm_sync(dapm);
+}
+
+/* For usability define a way of injecting beep events for the device -
+ * many systems will not have a keyboard.
+ */
+static int cs42l56_beep_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int hz)
+{
+ struct snd_soc_codec *codec = input_get_drvdata(dev);
+ struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "Beep event %x %x\n", code, hz);
+
+ switch (code) {
+ case SND_BELL:
+ if (hz)
+ hz = 261;
+ case SND_TONE:
+ break;
+ default:
+ return -1;
+ }
+
+ /* Kick the beep from a workqueue */
+ cs42l56->beep_rate = hz;
+ schedule_work(&cs42l56->beep_work);
+ return 0;
+}
+
+static ssize_t cs42l56_beep_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cs42l56_private *cs42l56 = dev_get_drvdata(dev);
+ long int time;
+ int ret;
+
+ ret = kstrtol(buf, 10, &time);
+ if (ret != 0)
+ return ret;
+
+ input_event(cs42l56->beep, EV_SND, SND_TONE, time);
+
+ return count;
+}
+
+static DEVICE_ATTR(beep, 0200, NULL, cs42l56_beep_set);
+
+static void cs42l56_init_beep(struct snd_soc_codec *codec)
+{
+ struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ cs42l56->beep = devm_input_allocate_device(codec->dev);
+ if (!cs42l56->beep) {
+ dev_err(codec->dev, "Failed to allocate beep device\n");
+ return;
+ }
+
+ INIT_WORK(&cs42l56->beep_work, cs42l56_beep_work);
+ cs42l56->beep_rate = 0;
+
+ cs42l56->beep->name = "CS42L56 Beep Generator";
+ cs42l56->beep->phys = dev_name(codec->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);
+
+ ret = input_register_device(cs42l56->beep);
+ if (ret != 0) {
+ cs42l56->beep = NULL;
+ dev_err(codec->dev, "Failed to register beep device\n");
+ }
+
+ ret = device_create_file(codec->dev, &dev_attr_beep);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to create keyclick file: %d\n",
+ ret);
+ }
+}
+
+static void cs42l56_free_beep(struct snd_soc_codec *codec)
+{
+ struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+
+ device_remove_file(codec->dev, &dev_attr_beep);
+ cancel_work_sync(&cs42l56->beep_work);
+ cs42l56->beep = NULL;
+
+ snd_soc_update_bits(codec, CS42L56_BEEP_TONE_CFG,
+ CS42L56_BEEP_EN_MASK, 0);
+}
+
+static int cs42l56_probe(struct snd_soc_codec *codec)
+{
+ cs42l56_init_beep(codec);
+
+ return 0;
+}
+
+static int cs42l56_remove(struct snd_soc_codec *codec)
+{
+ cs42l56_free_beep(codec);
+
+ return 0;
+}
+
+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 regmap_config cs42l56_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS42L56_MAX_REGISTER,
+ .reg_defaults = cs42l56_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs42l56_reg_defaults),
+ .readable_reg = cs42l56_readable_register,
+ .volatile_reg = cs42l56_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
+ struct cs42l56_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ u32 val32;
+
+ if (of_property_read_bool(np, "cirrus,ain1a-reference-cfg"))
+ pdata->ain1a_ref_cfg = true;
+
+ if (of_property_read_bool(np, "cirrus,ain2a-reference-cfg"))
+ pdata->ain2a_ref_cfg = true;
+
+ if (of_property_read_bool(np, "cirrus,ain1b-reference-cfg"))
+ pdata->ain1b_ref_cfg = true;
+
+ if (of_property_read_bool(np, "cirrus,ain2b-reference-cfg"))
+ pdata->ain2b_ref_cfg = true;
+
+ if (of_property_read_u32(np, "cirrus,micbias-lvl", &val32) >= 0)
+ pdata->micbias_lvl = val32;
+
+ if (of_property_read_u32(np, "cirrus,chgfreq-divisor", &val32) >= 0)
+ pdata->chgfreq = val32;
+
+ if (of_property_read_u32(np, "cirrus,adaptive-pwr-cfg", &val32) >= 0)
+ pdata->adaptive_pwr = val32;
+
+ if (of_property_read_u32(np, "cirrus,hpf-left-freq", &val32) >= 0)
+ pdata->hpfa_freq = val32;
+
+ if (of_property_read_u32(np, "cirrus,hpf-left-freq", &val32) >= 0)
+ pdata->hpfb_freq = val32;
+
+ pdata->gpio_nreset = of_get_named_gpio(np, "cirrus,gpio-nreset", 0);
+
+ return 0;
+}
+
+static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct cs42l56_private *cs42l56;
+ struct cs42l56_platform_data *pdata =
+ dev_get_platdata(&i2c_client->dev);
+ int ret, i;
+ unsigned int devid = 0;
+ unsigned int alpha_rev, metal_rev;
+ unsigned int reg;
+
+ cs42l56 = devm_kzalloc(&i2c_client->dev,
+ sizeof(struct cs42l56_private),
+ GFP_KERNEL);
+ if (cs42l56 == NULL)
+ return -ENOMEM;
+ cs42l56->dev = &i2c_client->dev;
+
+ cs42l56->regmap = devm_regmap_init_i2c(i2c_client, &cs42l56_regmap);
+ if (IS_ERR(cs42l56->regmap)) {
+ ret = PTR_ERR(cs42l56->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs42l56->pdata = *pdata;
+ } else {
+ 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 (cs42l56->pdata.gpio_nreset) {
+ ret = gpio_request_one(cs42l56->pdata.gpio_nreset,
+ GPIOF_OUT_INIT_HIGH, "CS42L56 /RST");
+ if (ret < 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request /RST %d: %d\n",
+ cs42l56->pdata.gpio_nreset, ret);
+ return ret;
+ }
+ gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 0);
+ gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 1);
+ }
+
+
+ i2c_set_clientdata(i2c_client, cs42l56);
+
+ for (i = 0; i < ARRAY_SIZE(cs42l56->supplies); i++)
+ cs42l56->supplies[i].supply = cs42l56_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ ARRAY_SIZE(cs42l56->supplies),
+ cs42l56->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies),
+ cs42l56->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_bypass(cs42l56->regmap, true);
+
+ ret = regmap_read(cs42l56->regmap, CS42L56_CHIP_ID_1, &reg);
+ 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);
+ goto err_enable;
+ }
+ alpha_rev = reg & CS42L56_AREV_MASK;
+ metal_rev = reg & CS42L56_MTLREV_MASK;
+
+ dev_info(&i2c_client->dev, "Cirrus Logic CS42L56 ");
+ dev_info(&i2c_client->dev, "Alpha Rev %X Metal Rev %X\n",
+ alpha_rev, metal_rev);
+
+ 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);
+
+ if (cs42l56->pdata.ain1b_ref_cfg)
+ regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
+ CS42L56_AIN1B_REF_MASK, 1);
+
+ if (cs42l56->pdata.ain2a_ref_cfg)
+ regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
+ CS42L56_AIN2A_REF_MASK, 1);
+
+ if (cs42l56->pdata.ain2b_ref_cfg)
+ regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
+ CS42L56_AIN2B_REF_MASK, 1);
+
+ if (cs42l56->pdata.micbias_lvl)
+ regmap_update_bits(cs42l56->regmap, CS42L56_GAIN_BIAS_CTL,
+ CS42L56_MIC_BIAS_MASK,
+ cs42l56->pdata.micbias_lvl);
+
+ if (cs42l56->pdata.chgfreq)
+ regmap_update_bits(cs42l56->regmap, CS42L56_CLASSH_CTL,
+ CS42L56_CHRG_FREQ_MASK,
+ cs42l56->pdata.chgfreq);
+
+ if (cs42l56->pdata.hpfb_freq)
+ regmap_update_bits(cs42l56->regmap, CS42L56_HPF_CTL,
+ CS42L56_HPFB_FREQ_MASK,
+ cs42l56->pdata.hpfb_freq);
+
+ if (cs42l56->pdata.hpfa_freq)
+ regmap_update_bits(cs42l56->regmap, CS42L56_HPF_CTL,
+ CS42L56_HPFA_FREQ_MASK,
+ cs42l56->pdata.hpfa_freq);
+
+ if (cs42l56->pdata.adaptive_pwr)
+ regmap_update_bits(cs42l56->regmap, CS42L56_CLASSH_CTL,
+ CS42L56_ADAPT_PWR_MASK,
+ cs42l56->pdata.adaptive_pwr);
+
+ ret = snd_soc_register_codec(&i2c_client->dev,
+ &soc_codec_dev_cs42l56, &cs42l56_dai, 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies),
+ cs42l56->supplies);
+ return ret;
+}
+
+static int 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[] = {
+ { .compatible = "cirrus,cs42l56", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs42l56_of_match);
+
+
+static const struct i2c_device_id cs42l56_id[] = {
+ { "cs42l56", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs42l56_id);
+
+static struct i2c_driver cs42l56_i2c_driver = {
+ .driver = {
+ .name = "cs42l56",
+ .owner = THIS_MODULE,
+ .of_match_table = cs42l56_of_match,
+ },
+ .id_table = cs42l56_id,
+ .probe = cs42l56_i2c_probe,
+ .remove = cs42l56_i2c_remove,
+};
+
+module_i2c_driver(cs42l56_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L56 driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs42l56.h b/kernel/sound/soc/codecs/cs42l56.h
new file mode 100644
index 000000000..5025ec9be
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs42l56.h
@@ -0,0 +1,177 @@
+/*
+ * 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__
+#define __CS42L56_H__
+
+#define CS42L56_CHIP_ID_1 0x01
+#define CS42L56_CHIP_ID_2 0x02
+#define CS42L56_PWRCTL_1 0x03
+#define CS42L56_PWRCTL_2 0x04
+#define CS42L56_CLKCTL_1 0x05
+#define CS42L56_CLKCTL_2 0x06
+#define CS42L56_SERIAL_FMT 0x07
+#define CS42L56_CLASSH_CTL 0x08
+#define CS42L56_MISC_CTL 0x09
+#define CS42L56_INT_STATUS 0x0a
+#define CS42L56_PLAYBACK_CTL 0x0b
+#define CS42L56_DSP_MUTE_CTL 0x0c
+#define CS42L56_ADCA_MIX_VOLUME 0x0d
+#define CS42L56_ADCB_MIX_VOLUME 0x0e
+#define CS42L56_PCMA_MIX_VOLUME 0x0f
+#define CS42L56_PCMB_MIX_VOLUME 0x10
+#define CS42L56_ANAINPUT_ADV_VOLUME 0x11
+#define CS42L56_DIGINPUT_ADV_VOLUME 0x12
+#define CS42L56_MASTER_A_VOLUME 0x13
+#define CS42L56_MASTER_B_VOLUME 0x14
+#define CS42L56_BEEP_FREQ_ONTIME 0x15
+#define CS42L56_BEEP_FREQ_OFFTIME 0x16
+#define CS42L56_BEEP_TONE_CFG 0x17
+#define CS42L56_TONE_CTL 0x18
+#define CS42L56_CHAN_MIX_SWAP 0x19
+#define CS42L56_AIN_REFCFG_ADC_MUX 0x1a
+#define CS42L56_HPF_CTL 0x1b
+#define CS42L56_MISC_ADC_CTL 0x1c
+#define CS42L56_GAIN_BIAS_CTL 0x1d
+#define CS42L56_PGAA_MUX_VOLUME 0x1e
+#define CS42L56_PGAB_MUX_VOLUME 0x1f
+#define CS42L56_ADCA_ATTENUATOR 0x20
+#define CS42L56_ADCB_ATTENUATOR 0x21
+#define CS42L56_ALC_EN_ATTACK_RATE 0x22
+#define CS42L56_ALC_RELEASE_RATE 0x23
+#define CS42L56_ALC_THRESHOLD 0x24
+#define CS42L56_NOISE_GATE_CTL 0x25
+#define CS42L56_ALC_LIM_SFT_ZC 0x26
+#define CS42L56_AMUTE_HPLO_MUX 0x27
+#define CS42L56_HPA_VOLUME 0x28
+#define CS42L56_HPB_VOLUME 0x29
+#define CS42L56_LOA_VOLUME 0x2a
+#define CS42L56_LOB_VOLUME 0x2b
+#define CS42L56_LIM_THRESHOLD_CTL 0x2c
+#define CS42L56_LIM_CTL_RELEASE_RATE 0x2d
+#define CS42L56_LIM_ATTACK_RATE 0x2e
+
+/* Device ID and Rev ID Masks */
+#define CS42L56_DEVID 0x56
+#define CS42L56_CHIP_ID_MASK 0xff
+#define CS42L56_AREV_MASK 0x1c
+#define CS42L56_MTLREV_MASK 0x03
+
+/* Power bit masks */
+#define CS42L56_PDN_ALL_MASK 0x01
+#define CS42L56_PDN_ADCA_MASK 0x02
+#define CS42L56_PDN_ADCB_MASK 0x04
+#define CS42L56_PDN_CHRG_MASK 0x08
+#define CS42L56_PDN_BIAS_MASK 0x10
+#define CS42L56_PDN_VBUF_MASK 0x20
+#define CS42L56_PDN_LOA_MASK 0x03
+#define CS42L56_PDN_LOB_MASK 0x0c
+#define CS42L56_PDN_HPA_MASK 0x30
+#define CS42L56_PDN_HPB_MASK 0xc0
+
+/* serial port and clk masks */
+#define CS42L56_MASTER_MODE 0x40
+#define CS42L56_SLAVE_MODE 0
+#define CS42L56_MS_MODE_MASK 0x40
+#define CS42L56_SCLK_INV 0x20
+#define CS42L56_SCLK_INV_MASK 0x20
+#define CS42L56_SCLK_MCLK_MASK 0x18
+#define CS42L56_MCLK_PREDIV 0x04
+#define CS42L56_MCLK_PREDIV_MASK 0x04
+#define CS42L56_MCLK_DIV2 0x02
+#define CS42L56_MCLK_DIV2_MASK 0x02
+#define CS42L56_MCLK_DIS_MASK 0x01
+#define CS42L56_CLK_AUTO_MASK 0x20
+#define CS42L56_CLK_RATIO_MASK 0x1f
+#define CS42L56_DIG_FMT_I2S 0
+#define CS42L56_DIG_FMT_LEFT_J 0x08
+#define CS42L56_DIG_FMT_MASK 0x08
+
+/* Class H and misc ctl masks */
+#define CS42L56_ADAPT_PWR_MASK 0xc0
+#define CS42L56_CHRG_FREQ_MASK 0x0f
+#define CS42L56_DIG_MUX_MASK 0x80
+#define CS42L56_ANLGSFT_MASK 0x10
+#define CS42L56_ANLGZC_MASK 0x08
+#define CS42L56_DIGSFT_MASK 0x04
+#define CS42L56_FREEZE_MASK 0x01
+#define CS42L56_MIC_BIAS_MASK 0x03
+#define CS42L56_HPFA_FREQ_MASK 0x03
+#define CS42L56_HPFB_FREQ_MASK 0xc0
+#define CS42L56_AIN1A_REF_MASK 0x10
+#define CS42L56_AIN2A_REF_MASK 0x40
+#define CS42L56_AIN1B_REF_MASK 0x20
+#define CS42L56_AIN2B_REF_MASK 0x80
+
+/* Playback Capture ctl masks */
+#define CS42L56_PDN_DSP_MASK 0x80
+#define CS42L56_DEEMPH_MASK 0x40
+#define CS42L56_PLYBCK_GANG_MASK 0x10
+#define CS42L56_PCM_INV_MASK 0x0c
+#define CS42L56_MUTE_ALL 0xff
+#define CS42L56_UNMUTE 0
+#define CS42L56_ADCAMIX_MUTE_MASK 0x40
+#define CS42L56_ADCBMIX_MUTE_MASK 0x80
+#define CS42L56_PCMAMIX_MUTE_MASK 0x10
+#define CS42L56_PCMBMIX_MUTE_MASK 0x20
+#define CS42L56_MSTB_MUTE_MASK 0x02
+#define CS42L56_MSTA_MUTE_MASK 0x01
+#define CS42L56_ADCA_MUTE_MASK 0x01
+#define CS42L56_ADCB_MUTE_MASK 0x02
+#define CS42L56_HP_MUTE_MASK 0x80
+#define CS42L56_LO_MUTE_MASK 0x80
+
+/* Beep masks */
+#define CS42L56_BEEP_FREQ_MASK 0xf0
+#define CS42L56_BEEP_ONTIME_MASK 0x0f
+#define CS42L56_BEEP_OFFTIME_MASK 0xe0
+#define CS42L56_BEEP_CFG_MASK 0xc0
+#define CS42L56_BEEP_TREBCF_MASK 0x18
+#define CS42L56_BEEP_BASSCF_MASK 0x06
+#define CS42L56_BEEP_TCEN_MASK 0x01
+#define CS42L56_BEEP_RATE_SHIFT 4
+#define CS42L56_BEEP_EN_MASK 0x3f
+
+
+/* Supported MCLKS */
+#define CS42L56_MCLK_5P6448MHZ 5644800
+#define CS42L56_MCLK_6MHZ 6000000
+#define CS42L56_MCLK_6P144MHZ 6144000
+#define CS42L56_MCLK_11P2896MHZ 11289600
+#define CS42L56_MCLK_12MHZ 12000000
+#define CS42L56_MCLK_12P288MHZ 12288000
+#define CS42L56_MCLK_22P5792MHZ 22579200
+#define CS42L56_MCLK_24MHZ 24000000
+#define CS42L56_MCLK_24P576MHZ 24576000
+
+/* Clock ratios */
+#define CS42L56_MCLK_LRCLK_128 0x08
+#define CS42L56_MCLK_LRCLK_125 0x09
+#define CS42L56_MCLK_LRCLK_136 0x0b
+#define CS42L56_MCLK_LRCLK_192 0x0c
+#define CS42L56_MCLK_LRCLK_187P5 0x0d
+#define CS42L56_MCLK_LRCLK_256 0x10
+#define CS42L56_MCLK_LRCLK_250 0x11
+#define CS42L56_MCLK_LRCLK_272 0x13
+#define CS42L56_MCLK_LRCLK_384 0x14
+#define CS42L56_MCLK_LRCLK_375 0x15
+#define CS42L56_MCLK_LRCLK_512 0x18
+#define CS42L56_MCLK_LRCLK_500 0x19
+#define CS42L56_MCLK_LRCLK_544 0x1b
+#define CS42L56_MCLK_LRCLK_750 0x1c
+#define CS42L56_MCLK_LRCLK_768 0x1d
+
+
+#define CS42L56_MAX_REGISTER 0x34
+
+#endif
diff --git a/kernel/sound/soc/codecs/cs42l73.c b/kernel/sound/soc/codecs/cs42l73.c
new file mode 100644
index 000000000..8ecedba79
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs42l73.c
@@ -0,0 +1,1509 @@
+/*
+ * cs42l73.c -- CS42L73 ALSA Soc Audio driver
+ *
+ * Copyright 2011 Cirrus Logic, Inc.
+ *
+ * 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/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/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 "cs42l73.h"
+
+struct sp_config {
+ u8 spc, mmcc, spfs;
+ u32 srate;
+};
+struct cs42l73_private {
+ struct cs42l73_platform_data pdata;
+ struct sp_config config[3];
+ struct regmap *regmap;
+ u32 sysclk;
+ u8 mclksel;
+ u32 mclk;
+ int shutdwn_delay;
+};
+
+static const struct reg_default cs42l73_reg_defaults[] = {
+ { 6, 0xF1 }, /* r06 - Power Ctl 1 */
+ { 7, 0xDF }, /* r07 - Power Ctl 2 */
+ { 8, 0x3F }, /* r08 - Power Ctl 3 */
+ { 9, 0x50 }, /* r09 - Charge Pump Freq */
+ { 10, 0x53 }, /* r0A - Output Load MicBias Short Detect */
+ { 11, 0x00 }, /* r0B - DMIC Master Clock Ctl */
+ { 12, 0x00 }, /* r0C - Aux PCM Ctl */
+ { 13, 0x15 }, /* r0D - Aux PCM Master Clock Ctl */
+ { 14, 0x00 }, /* r0E - Audio PCM Ctl */
+ { 15, 0x15 }, /* r0F - Audio PCM Master Clock Ctl */
+ { 16, 0x00 }, /* r10 - Voice PCM Ctl */
+ { 17, 0x15 }, /* r11 - Voice PCM Master Clock Ctl */
+ { 18, 0x00 }, /* r12 - Voice/Aux Sample Rate */
+ { 19, 0x06 }, /* r13 - Misc I/O Path Ctl */
+ { 20, 0x00 }, /* r14 - ADC Input Path Ctl */
+ { 21, 0x00 }, /* r15 - MICA Preamp, PGA Volume */
+ { 22, 0x00 }, /* r16 - MICB Preamp, PGA Volume */
+ { 23, 0x00 }, /* r17 - Input Path A Digital Volume */
+ { 24, 0x00 }, /* r18 - Input Path B Digital Volume */
+ { 25, 0x00 }, /* r19 - Playback Digital Ctl */
+ { 26, 0x00 }, /* r1A - HP/LO Left Digital Volume */
+ { 27, 0x00 }, /* r1B - HP/LO Right Digital Volume */
+ { 28, 0x00 }, /* r1C - Speakerphone Digital Volume */
+ { 29, 0x00 }, /* r1D - Ear/SPKLO Digital Volume */
+ { 30, 0x00 }, /* r1E - HP Left Analog Volume */
+ { 31, 0x00 }, /* r1F - HP Right Analog Volume */
+ { 32, 0x00 }, /* r20 - LO Left Analog Volume */
+ { 33, 0x00 }, /* r21 - LO Right Analog Volume */
+ { 34, 0x00 }, /* r22 - Stereo Input Path Advisory Volume */
+ { 35, 0x00 }, /* r23 - Aux PCM Input Advisory Volume */
+ { 36, 0x00 }, /* r24 - Audio PCM Input Advisory Volume */
+ { 37, 0x00 }, /* r25 - Voice PCM Input Advisory Volume */
+ { 38, 0x00 }, /* r26 - Limiter Attack Rate HP/LO */
+ { 39, 0x7F }, /* r27 - Limter Ctl, Release Rate HP/LO */
+ { 40, 0x00 }, /* r28 - Limter Threshold HP/LO */
+ { 41, 0x00 }, /* r29 - Limiter Attack Rate Speakerphone */
+ { 42, 0x3F }, /* r2A - Limter Ctl, Release Rate Speakerphone */
+ { 43, 0x00 }, /* r2B - Limter Threshold Speakerphone */
+ { 44, 0x00 }, /* r2C - Limiter Attack Rate Ear/SPKLO */
+ { 45, 0x3F }, /* r2D - Limter Ctl, Release Rate Ear/SPKLO */
+ { 46, 0x00 }, /* r2E - Limter Threshold Ear/SPKLO */
+ { 47, 0x00 }, /* r2F - ALC Enable, Attack Rate Left/Right */
+ { 48, 0x3F }, /* r30 - ALC Release Rate Left/Right */
+ { 49, 0x00 }, /* r31 - ALC Threshold Left/Right */
+ { 50, 0x00 }, /* r32 - Noise Gate Ctl Left/Right */
+ { 51, 0x00 }, /* r33 - ALC/NG Misc Ctl */
+ { 52, 0x18 }, /* r34 - Mixer Ctl */
+ { 53, 0x3F }, /* r35 - HP/LO Left Mixer Input Path Volume */
+ { 54, 0x3F }, /* r36 - HP/LO Right Mixer Input Path Volume */
+ { 55, 0x3F }, /* r37 - HP/LO Left Mixer Aux PCM Volume */
+ { 56, 0x3F }, /* r38 - HP/LO Right Mixer Aux PCM Volume */
+ { 57, 0x3F }, /* r39 - HP/LO Left Mixer Audio PCM Volume */
+ { 58, 0x3F }, /* r3A - HP/LO Right Mixer Audio PCM Volume */
+ { 59, 0x3F }, /* r3B - HP/LO Left Mixer Voice PCM Mono Volume */
+ { 60, 0x3F }, /* r3C - HP/LO Right Mixer Voice PCM Mono Volume */
+ { 61, 0x3F }, /* r3D - Aux PCM Left Mixer Input Path Volume */
+ { 62, 0x3F }, /* r3E - Aux PCM Right Mixer Input Path Volume */
+ { 63, 0x3F }, /* r3F - Aux PCM Left Mixer Volume */
+ { 64, 0x3F }, /* r40 - Aux PCM Left Mixer Volume */
+ { 65, 0x3F }, /* r41 - Aux PCM Left Mixer Audio PCM L Volume */
+ { 66, 0x3F }, /* r42 - Aux PCM Right Mixer Audio PCM R Volume */
+ { 67, 0x3F }, /* r43 - Aux PCM Left Mixer Voice PCM Volume */
+ { 68, 0x3F }, /* r44 - Aux PCM Right Mixer Voice PCM Volume */
+ { 69, 0x3F }, /* r45 - Audio PCM Left Input Path Volume */
+ { 70, 0x3F }, /* r46 - Audio PCM Right Input Path Volume */
+ { 71, 0x3F }, /* r47 - Audio PCM Left Mixer Aux PCM L Volume */
+ { 72, 0x3F }, /* r48 - Audio PCM Right Mixer Aux PCM R Volume */
+ { 73, 0x3F }, /* r49 - Audio PCM Left Mixer Volume */
+ { 74, 0x3F }, /* r4A - Audio PCM Right Mixer Volume */
+ { 75, 0x3F }, /* r4B - Audio PCM Left Mixer Voice PCM Volume */
+ { 76, 0x3F }, /* r4C - Audio PCM Right Mixer Voice PCM Volume */
+ { 77, 0x3F }, /* r4D - Voice PCM Left Input Path Volume */
+ { 78, 0x3F }, /* r4E - Voice PCM Right Input Path Volume */
+ { 79, 0x3F }, /* r4F - Voice PCM Left Mixer Aux PCM L Volume */
+ { 80, 0x3F }, /* r50 - Voice PCM Right Mixer Aux PCM R Volume */
+ { 81, 0x3F }, /* r51 - Voice PCM Left Mixer Audio PCM L Volume */
+ { 82, 0x3F }, /* r52 - Voice PCM Right Mixer Audio PCM R Volume */
+ { 83, 0x3F }, /* r53 - Voice PCM Left Mixer Voice PCM Volume */
+ { 84, 0x3F }, /* r54 - Voice PCM Right Mixer Voice PCM Volume */
+ { 85, 0xAA }, /* r55 - Mono Mixer Ctl */
+ { 86, 0x3F }, /* r56 - SPK Mono Mixer Input Path Volume */
+ { 87, 0x3F }, /* r57 - SPK Mono Mixer Aux PCM Mono/L/R Volume */
+ { 88, 0x3F }, /* r58 - SPK Mono Mixer Audio PCM Mono/L/R Volume */
+ { 89, 0x3F }, /* r59 - SPK Mono Mixer Voice PCM Mono Volume */
+ { 90, 0x3F }, /* r5A - SPKLO Mono Mixer Input Path Mono Volume */
+ { 91, 0x3F }, /* r5B - SPKLO Mono Mixer Aux Mono/L/R Volume */
+ { 92, 0x3F }, /* r5C - SPKLO Mono Mixer Audio Mono/L/R Volume */
+ { 93, 0x3F }, /* r5D - SPKLO Mono Mixer Voice Mono Volume */
+ { 94, 0x00 }, /* r5E - Interrupt Mask 1 */
+ { 95, 0x00 }, /* r5F - Interrupt Mask 2 */
+};
+
+static bool cs42l73_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L73_IS1:
+ case CS42L73_IS2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l73_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L73_DEVID_AB:
+ 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:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const unsigned int hpaloa_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 13, TLV_DB_SCALE_ITEM(-7600, 200, 0),
+ 14, 75, TLV_DB_SCALE_ITEM(-4900, 100, 0),
+};
+
+static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2500, 0);
+
+static DECLARE_TLV_DB_SCALE(hl_tlv, -10200, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(ipd_tlv, -9600, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(micpga_tlv, -600, 50, 0);
+
+static const unsigned int limiter_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0),
+ 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0),
+};
+
+static const DECLARE_TLV_DB_SCALE(attn_tlv, -6300, 100, 1);
+
+static const char * const cs42l73_pgaa_text[] = { "Line A", "Mic 1" };
+static const char * const cs42l73_pgab_text[] = { "Line B", "Mic 2" };
+
+static SOC_ENUM_SINGLE_DECL(pgaa_enum,
+ CS42L73_ADCIPC, 3,
+ cs42l73_pgaa_text);
+
+static SOC_ENUM_SINGLE_DECL(pgab_enum,
+ CS42L73_ADCIPC, 7,
+ cs42l73_pgab_text);
+
+static const struct snd_kcontrol_new pgaa_mux =
+ SOC_DAPM_ENUM("Left Analog Input Capture Mux", pgaa_enum);
+
+static const struct snd_kcontrol_new pgab_mux =
+ SOC_DAPM_ENUM("Right Analog Input Capture Mux", pgab_enum);
+
+static const struct snd_kcontrol_new input_left_mixer[] = {
+ SOC_DAPM_SINGLE("ADC Left Input", CS42L73_PWRCTL1,
+ 5, 1, 1),
+ SOC_DAPM_SINGLE("DMIC Left Input", CS42L73_PWRCTL1,
+ 4, 1, 1),
+};
+
+static const struct snd_kcontrol_new input_right_mixer[] = {
+ SOC_DAPM_SINGLE("ADC Right Input", CS42L73_PWRCTL1,
+ 7, 1, 1),
+ SOC_DAPM_SINGLE("DMIC Right Input", CS42L73_PWRCTL1,
+ 6, 1, 1),
+};
+
+static const char * const cs42l73_ng_delay_text[] = {
+ "50ms", "100ms", "150ms", "200ms" };
+
+static SOC_ENUM_SINGLE_DECL(ng_delay_enum,
+ CS42L73_NGCAB, 0,
+ cs42l73_ng_delay_text);
+
+static const char * const cs42l73_mono_mix_texts[] = {
+ "Left", "Right", "Mono Mix"};
+
+static const unsigned int cs42l73_mono_mix_values[] = { 0, 1, 2 };
+
+static const struct soc_enum spk_asp_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L73_MMIXCTL, 6, 3,
+ ARRAY_SIZE(cs42l73_mono_mix_texts),
+ cs42l73_mono_mix_texts,
+ cs42l73_mono_mix_values);
+
+static const struct snd_kcontrol_new spk_asp_mixer =
+ SOC_DAPM_ENUM("Route", spk_asp_enum);
+
+static const struct soc_enum spk_xsp_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L73_MMIXCTL, 4, 3,
+ ARRAY_SIZE(cs42l73_mono_mix_texts),
+ cs42l73_mono_mix_texts,
+ cs42l73_mono_mix_values);
+
+static const struct snd_kcontrol_new spk_xsp_mixer =
+ SOC_DAPM_ENUM("Route", spk_xsp_enum);
+
+static const struct soc_enum esl_asp_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L73_MMIXCTL, 2, 3,
+ ARRAY_SIZE(cs42l73_mono_mix_texts),
+ cs42l73_mono_mix_texts,
+ cs42l73_mono_mix_values);
+
+static const struct snd_kcontrol_new esl_asp_mixer =
+ SOC_DAPM_ENUM("Route", esl_asp_enum);
+
+static const struct soc_enum esl_xsp_enum =
+ SOC_VALUE_ENUM_SINGLE(CS42L73_MMIXCTL, 0, 3,
+ ARRAY_SIZE(cs42l73_mono_mix_texts),
+ cs42l73_mono_mix_texts,
+ cs42l73_mono_mix_values);
+
+static const struct snd_kcontrol_new esl_xsp_mixer =
+ SOC_DAPM_ENUM("Route", esl_xsp_enum);
+
+static const char * const cs42l73_ip_swap_text[] = {
+ "Stereo", "Mono A", "Mono B", "Swap A-B"};
+
+static SOC_ENUM_SINGLE_DECL(ip_swap_enum,
+ CS42L73_MIOPC, 6,
+ cs42l73_ip_swap_text);
+
+static const char * const cs42l73_spo_mixer_text[] = {"Mono", "Stereo"};
+
+static SOC_ENUM_SINGLE_DECL(vsp_output_mux_enum,
+ CS42L73_MIXERCTL, 5,
+ cs42l73_spo_mixer_text);
+
+static SOC_ENUM_SINGLE_DECL(xsp_output_mux_enum,
+ CS42L73_MIXERCTL, 4,
+ cs42l73_spo_mixer_text);
+
+static const struct snd_kcontrol_new 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);
+
+static const struct snd_kcontrol_new lo_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 1, 1, 1);
+
+static const struct snd_kcontrol_new spk_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 2, 1, 1);
+
+static const struct snd_kcontrol_new spklo_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 4, 1, 1);
+
+static const struct snd_kcontrol_new ear_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 3, 1, 1);
+
+static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
+ SOC_DOUBLE_R_SX_TLV("Headphone Analog Playback Volume",
+ CS42L73_HPAAVOL, CS42L73_HPBAVOL, 0,
+ 0x41, 0x4B, hpaloa_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("LineOut Analog Playback Volume", CS42L73_LOAAVOL,
+ CS42L73_LOBAVOL, 0, 0x41, 0x4B, hpaloa_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("Input PGA Analog Volume", CS42L73_MICAPREPGAAVOL,
+ CS42L73_MICBPREPGABVOL, 0, 0x34,
+ 0x24, micpga_tlv),
+
+ SOC_DOUBLE_R("MIC Preamp Switch", CS42L73_MICAPREPGAAVOL,
+ CS42L73_MICBPREPGABVOL, 6, 1, 1),
+
+ SOC_DOUBLE_R_SX_TLV("Input Path Digital Volume", CS42L73_IPADVOL,
+ CS42L73_IPBDVOL, 0, 0xA0, 0x6C, ipd_tlv),
+
+ SOC_DOUBLE_R_SX_TLV("HL Digital Playback Volume",
+ CS42L73_HLADVOL, CS42L73_HLBDVOL,
+ 0, 0x34, 0xE4, hl_tlv),
+
+ SOC_SINGLE_TLV("ADC A Boost Volume",
+ CS42L73_ADCIPC, 2, 0x01, 1, adc_boost_tlv),
+
+ SOC_SINGLE_TLV("ADC B Boost Volume",
+ CS42L73_ADCIPC, 6, 0x01, 1, adc_boost_tlv),
+
+ SOC_SINGLE_SX_TLV("Speakerphone Digital Volume",
+ CS42L73_SPKDVOL, 0, 0x34, 0xE4, hl_tlv),
+
+ SOC_SINGLE_SX_TLV("Ear Speaker Digital Volume",
+ CS42L73_ESLDVOL, 0, 0x34, 0xE4, hl_tlv),
+
+ SOC_DOUBLE_R("Headphone Analog Playback Switch", CS42L73_HPAAVOL,
+ CS42L73_HPBAVOL, 7, 1, 1),
+
+ SOC_DOUBLE_R("LineOut Analog Playback Switch", CS42L73_LOAAVOL,
+ CS42L73_LOBAVOL, 7, 1, 1),
+ SOC_DOUBLE("Input Path Digital Switch", CS42L73_ADCIPC, 0, 4, 1, 1),
+ SOC_DOUBLE("HL Digital Playback Switch", CS42L73_PBDC, 0,
+ 1, 1, 1),
+ SOC_SINGLE("Speakerphone Digital Playback Switch", CS42L73_PBDC, 2, 1,
+ 1),
+ SOC_SINGLE("Ear Speaker Digital Playback Switch", CS42L73_PBDC, 3, 1,
+ 1),
+
+ SOC_SINGLE("PGA Soft-Ramp Switch", CS42L73_MIOPC, 3, 1, 0),
+ SOC_SINGLE("Analog Zero Cross Switch", CS42L73_MIOPC, 2, 1, 0),
+ SOC_SINGLE("Digital Soft-Ramp Switch", CS42L73_MIOPC, 1, 1, 0),
+ SOC_SINGLE("Analog Output Soft-Ramp Switch", CS42L73_MIOPC, 0, 1, 0),
+
+ SOC_DOUBLE("ADC Signal Polarity Switch", CS42L73_ADCIPC, 1, 5, 1,
+ 0),
+
+ SOC_SINGLE("HL Limiter Attack Rate", CS42L73_LIMARATEHL, 0, 0x3F,
+ 0),
+ SOC_SINGLE("HL Limiter Release Rate", CS42L73_LIMRRATEHL, 0,
+ 0x3F, 0),
+
+
+ SOC_SINGLE("HL Limiter Switch", CS42L73_LIMRRATEHL, 7, 1, 0),
+ SOC_SINGLE("HL Limiter All Channels Switch", CS42L73_LIMRRATEHL, 6, 1,
+ 0),
+
+ SOC_SINGLE_TLV("HL Limiter Max Threshold Volume", CS42L73_LMAXHL, 5, 7,
+ 1, limiter_tlv),
+
+ SOC_SINGLE_TLV("HL Limiter Cushion Volume", CS42L73_LMAXHL, 2, 7, 1,
+ limiter_tlv),
+
+ SOC_SINGLE("SPK Limiter Attack Rate Volume", CS42L73_LIMARATESPK, 0,
+ 0x3F, 0),
+ SOC_SINGLE("SPK Limiter Release Rate Volume", CS42L73_LIMRRATESPK, 0,
+ 0x3F, 0),
+ SOC_SINGLE("SPK Limiter Switch", CS42L73_LIMRRATESPK, 7, 1, 0),
+ SOC_SINGLE("SPK Limiter All Channels Switch", CS42L73_LIMRRATESPK,
+ 6, 1, 0),
+ SOC_SINGLE_TLV("SPK Limiter Max Threshold Volume", CS42L73_LMAXSPK, 5,
+ 7, 1, limiter_tlv),
+
+ SOC_SINGLE_TLV("SPK Limiter Cushion Volume", CS42L73_LMAXSPK, 2, 7, 1,
+ limiter_tlv),
+
+ SOC_SINGLE("ESL Limiter Attack Rate Volume", CS42L73_LIMARATEESL, 0,
+ 0x3F, 0),
+ SOC_SINGLE("ESL Limiter Release Rate Volume", CS42L73_LIMRRATEESL, 0,
+ 0x3F, 0),
+ SOC_SINGLE("ESL Limiter Switch", CS42L73_LIMRRATEESL, 7, 1, 0),
+ SOC_SINGLE_TLV("ESL Limiter Max Threshold Volume", CS42L73_LMAXESL, 5,
+ 7, 1, limiter_tlv),
+
+ SOC_SINGLE_TLV("ESL Limiter Cushion Volume", CS42L73_LMAXESL, 2, 7, 1,
+ limiter_tlv),
+
+ SOC_SINGLE("ALC Attack Rate Volume", CS42L73_ALCARATE, 0, 0x3F, 0),
+ SOC_SINGLE("ALC Release Rate Volume", CS42L73_ALCRRATE, 0, 0x3F, 0),
+ SOC_DOUBLE("ALC Switch", CS42L73_ALCARATE, 6, 7, 1, 0),
+ SOC_SINGLE_TLV("ALC Max Threshold Volume", CS42L73_ALCMINMAX, 5, 7, 0,
+ limiter_tlv),
+ SOC_SINGLE_TLV("ALC Min Threshold Volume", CS42L73_ALCMINMAX, 2, 7, 0,
+ limiter_tlv),
+
+ SOC_DOUBLE("NG Enable Switch", CS42L73_NGCAB, 6, 7, 1, 0),
+ SOC_SINGLE("NG Boost Switch", CS42L73_NGCAB, 5, 1, 0),
+ /*
+ NG Threshold depends on NG_BOOTSAB, which selects
+ between two threshold scales in decibels.
+ Set linear values for now ..
+ */
+ SOC_SINGLE("NG Threshold", CS42L73_NGCAB, 2, 7, 0),
+ SOC_ENUM("NG Delay", ng_delay_enum),
+
+ SOC_DOUBLE_R_TLV("XSP-IP Volume",
+ CS42L73_XSPAIPAA, CS42L73_XSPBIPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("XSP-XSP Volume",
+ CS42L73_XSPAXSPAA, CS42L73_XSPBXSPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("XSP-ASP Volume",
+ CS42L73_XSPAASPAA, CS42L73_XSPAASPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("XSP-VSP Volume",
+ CS42L73_XSPAVSPMA, CS42L73_XSPBVSPMA, 0, 0x3F, 1,
+ attn_tlv),
+
+ SOC_DOUBLE_R_TLV("ASP-IP Volume",
+ CS42L73_ASPAIPAA, CS42L73_ASPBIPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("ASP-XSP Volume",
+ CS42L73_ASPAXSPAA, CS42L73_ASPBXSPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("ASP-ASP Volume",
+ CS42L73_ASPAASPAA, CS42L73_ASPBASPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("ASP-VSP Volume",
+ CS42L73_ASPAVSPMA, CS42L73_ASPBVSPMA, 0, 0x3F, 1,
+ attn_tlv),
+
+ SOC_DOUBLE_R_TLV("VSP-IP Volume",
+ CS42L73_VSPAIPAA, CS42L73_VSPBIPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("VSP-XSP Volume",
+ CS42L73_VSPAXSPAA, CS42L73_VSPBXSPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("VSP-ASP Volume",
+ CS42L73_VSPAASPAA, CS42L73_VSPBASPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("VSP-VSP Volume",
+ CS42L73_VSPAVSPMA, CS42L73_VSPBVSPMA, 0, 0x3F, 1,
+ attn_tlv),
+
+ SOC_DOUBLE_R_TLV("HL-IP Volume",
+ CS42L73_HLAIPAA, CS42L73_HLBIPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("HL-XSP Volume",
+ CS42L73_HLAXSPAA, CS42L73_HLBXSPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("HL-ASP Volume",
+ CS42L73_HLAASPAA, CS42L73_HLBASPBA, 0, 0x3F, 1,
+ attn_tlv),
+ SOC_DOUBLE_R_TLV("HL-VSP Volume",
+ CS42L73_HLAVSPMA, CS42L73_HLBVSPMA, 0, 0x3F, 1,
+ attn_tlv),
+
+ SOC_SINGLE_TLV("SPK-IP Mono Volume",
+ CS42L73_SPKMIPMA, 0, 0x3F, 1, attn_tlv),
+ SOC_SINGLE_TLV("SPK-XSP Mono Volume",
+ CS42L73_SPKMXSPA, 0, 0x3F, 1, attn_tlv),
+ SOC_SINGLE_TLV("SPK-ASP Mono Volume",
+ CS42L73_SPKMASPA, 0, 0x3F, 1, attn_tlv),
+ SOC_SINGLE_TLV("SPK-VSP Mono Volume",
+ CS42L73_SPKMVSPMA, 0, 0x3F, 1, attn_tlv),
+
+ SOC_SINGLE_TLV("ESL-IP Mono Volume",
+ CS42L73_ESLMIPMA, 0, 0x3F, 1, attn_tlv),
+ SOC_SINGLE_TLV("ESL-XSP Mono Volume",
+ CS42L73_ESLMXSPA, 0, 0x3F, 1, attn_tlv),
+ SOC_SINGLE_TLV("ESL-ASP Mono Volume",
+ CS42L73_ESLMASPA, 0, 0x3F, 1, attn_tlv),
+ SOC_SINGLE_TLV("ESL-VSP Mono Volume",
+ CS42L73_ESLMVSPMA, 0, 0x3F, 1, attn_tlv),
+
+ SOC_ENUM("IP Digital Swap/Mono Select", ip_swap_enum),
+
+ SOC_ENUM("VSPOUT Mono/Stereo Select", vsp_output_mux_enum),
+ SOC_ENUM("XSPOUT Mono/Stereo Select", xsp_output_mux_enum),
+};
+
+static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ /* 150 ms delay between setting PDN and MCLKDIS */
+ priv->shutdwn_delay = 150;
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ /* 50 ms delay between setting PDN and MCLKDIS */
+ if (priv->shutdwn_delay < 50)
+ priv->shutdwn_delay = 50;
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+
+static int cs42l73_hp_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ /* 30 ms delay between setting PDN and MCLKDIS */
+ if (priv->shutdwn_delay < 30)
+ priv->shutdwn_delay = 30;
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs42l73_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMICA"),
+ SND_SOC_DAPM_INPUT("DMICB"),
+ SND_SOC_DAPM_INPUT("LINEINA"),
+ SND_SOC_DAPM_INPUT("LINEINB"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_SUPPLY("MIC1 Bias", CS42L73_PWRCTL2, 6, 1, NULL, 0),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS42L73_PWRCTL2, 7, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT("XSPOUTL", NULL, 0,
+ CS42L73_PWRCTL2, 1, 1),
+ SND_SOC_DAPM_AIF_OUT("XSPOUTR", NULL, 0,
+ CS42L73_PWRCTL2, 1, 1),
+ SND_SOC_DAPM_AIF_OUT("ASPOUTL", NULL, 0,
+ CS42L73_PWRCTL2, 3, 1),
+ SND_SOC_DAPM_AIF_OUT("ASPOUTR", NULL, 0,
+ CS42L73_PWRCTL2, 3, 1),
+ SND_SOC_DAPM_AIF_OUT("VSPINOUT", NULL, 0,
+ CS42L73_PWRCTL2, 4, 1),
+
+ SND_SOC_DAPM_PGA("PGA Left", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA Right", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("PGA Left Mux", SND_SOC_NOPM, 0, 0, &pgaa_mux),
+ SND_SOC_DAPM_MUX("PGA Right Mux", SND_SOC_NOPM, 0, 0, &pgab_mux),
+
+ SND_SOC_DAPM_ADC("ADC Left", NULL, CS42L73_PWRCTL1, 7, 1),
+ SND_SOC_DAPM_ADC("ADC Right", NULL, CS42L73_PWRCTL1, 5, 1),
+ SND_SOC_DAPM_ADC("DMIC Left", NULL, CS42L73_PWRCTL1, 6, 1),
+ SND_SOC_DAPM_ADC("DMIC Right", NULL, CS42L73_PWRCTL1, 4, 1),
+
+ SND_SOC_DAPM_MIXER_NAMED_CTL("Input Left Capture", SND_SOC_NOPM,
+ 0, 0, input_left_mixer,
+ ARRAY_SIZE(input_left_mixer)),
+
+ SND_SOC_DAPM_MIXER_NAMED_CTL("Input Right Capture", SND_SOC_NOPM,
+ 0, 0, input_right_mixer,
+ ARRAY_SIZE(input_right_mixer)),
+
+ SND_SOC_DAPM_MIXER("ASPL Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("ASPR Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("XSPL Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("XSPR Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("VSP Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_AIF_IN("XSPINL", NULL, 0,
+ CS42L73_PWRCTL2, 0, 1),
+ SND_SOC_DAPM_AIF_IN("XSPINR", NULL, 0,
+ CS42L73_PWRCTL2, 0, 1),
+ SND_SOC_DAPM_AIF_IN("XSPINM", NULL, 0,
+ CS42L73_PWRCTL2, 0, 1),
+
+ SND_SOC_DAPM_AIF_IN("ASPINL", NULL, 0,
+ CS42L73_PWRCTL2, 2, 1),
+ SND_SOC_DAPM_AIF_IN("ASPINR", NULL, 0,
+ CS42L73_PWRCTL2, 2, 1),
+ SND_SOC_DAPM_AIF_IN("ASPINM", NULL, 0,
+ CS42L73_PWRCTL2, 2, 1),
+
+ SND_SOC_DAPM_AIF_IN("VSPINOUT", NULL, 0,
+ CS42L73_PWRCTL2, 4, 1),
+
+ SND_SOC_DAPM_MIXER("HL Left Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("HL Right Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SPK Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("ESL Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("ESL-XSP Mux", SND_SOC_NOPM,
+ 0, 0, &esl_xsp_mixer),
+
+ SND_SOC_DAPM_MUX("ESL-ASP Mux", SND_SOC_NOPM,
+ 0, 0, &esl_asp_mixer),
+
+ SND_SOC_DAPM_MUX("SPK-ASP Mux", SND_SOC_NOPM,
+ 0, 0, &spk_asp_mixer),
+
+ SND_SOC_DAPM_MUX("SPK-XSP Mux", SND_SOC_NOPM,
+ 0, 0, &spk_xsp_mixer),
+
+ SND_SOC_DAPM_PGA("HL Left DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HL Right DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPK DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ESL DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH_E("HP Amp", CS42L73_PWRCTL3, 0, 1,
+ &hp_amp_ctl, cs42l73_hp_amp_event,
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("LO Amp", CS42L73_PWRCTL3, 1, 1,
+ &lo_amp_ctl),
+ SND_SOC_DAPM_SWITCH_E("SPK Amp", CS42L73_PWRCTL3, 2, 1,
+ &spk_amp_ctl, cs42l73_spklo_spk_amp_event,
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH_E("EAR Amp", CS42L73_PWRCTL3, 3, 1,
+ &ear_amp_ctl, cs42l73_ear_amp_event,
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH_E("SPKLO Amp", CS42L73_PWRCTL3, 4, 1,
+ &spklo_amp_ctl, cs42l73_spklo_spk_amp_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("HPOUTA"),
+ SND_SOC_DAPM_OUTPUT("HPOUTB"),
+ SND_SOC_DAPM_OUTPUT("LINEOUTA"),
+ SND_SOC_DAPM_OUTPUT("LINEOUTB"),
+ SND_SOC_DAPM_OUTPUT("EAROUT"),
+ SND_SOC_DAPM_OUTPUT("SPKOUT"),
+ SND_SOC_DAPM_OUTPUT("SPKLINEOUT"),
+};
+
+static const struct snd_soc_dapm_route cs42l73_audio_map[] = {
+
+ /* SPKLO EARSPK Paths */
+ {"EAROUT", NULL, "EAR Amp"},
+ {"SPKLINEOUT", NULL, "SPKLO Amp"},
+
+ {"EAR Amp", "Switch", "ESL DAC"},
+ {"SPKLO Amp", "Switch", "ESL DAC"},
+
+ {"ESL DAC", "ESL-ASP Mono Volume", "ESL Mixer"},
+ {"ESL DAC", "ESL-XSP Mono Volume", "ESL Mixer"},
+ {"ESL DAC", "ESL-VSP Mono Volume", "VSPINOUT"},
+ /* Loopback */
+ {"ESL DAC", "ESL-IP Mono Volume", "Input Left Capture"},
+ {"ESL DAC", "ESL-IP Mono Volume", "Input Right Capture"},
+
+ {"ESL Mixer", NULL, "ESL-ASP Mux"},
+ {"ESL Mixer", NULL, "ESL-XSP Mux"},
+
+ {"ESL-ASP Mux", "Left", "ASPINL"},
+ {"ESL-ASP Mux", "Right", "ASPINR"},
+ {"ESL-ASP Mux", "Mono Mix", "ASPINM"},
+
+ {"ESL-XSP Mux", "Left", "XSPINL"},
+ {"ESL-XSP Mux", "Right", "XSPINR"},
+ {"ESL-XSP Mux", "Mono Mix", "XSPINM"},
+
+ /* Speakerphone Paths */
+ {"SPKOUT", NULL, "SPK Amp"},
+ {"SPK Amp", "Switch", "SPK DAC"},
+
+ {"SPK DAC", "SPK-ASP Mono Volume", "SPK Mixer"},
+ {"SPK DAC", "SPK-XSP Mono Volume", "SPK Mixer"},
+ {"SPK DAC", "SPK-VSP Mono Volume", "VSPINOUT"},
+ /* Loopback */
+ {"SPK DAC", "SPK-IP Mono Volume", "Input Left Capture"},
+ {"SPK DAC", "SPK-IP Mono Volume", "Input Right Capture"},
+
+ {"SPK Mixer", NULL, "SPK-ASP Mux"},
+ {"SPK Mixer", NULL, "SPK-XSP Mux"},
+
+ {"SPK-ASP Mux", "Left", "ASPINL"},
+ {"SPK-ASP Mux", "Mono Mix", "ASPINM"},
+ {"SPK-ASP Mux", "Right", "ASPINR"},
+
+ {"SPK-XSP Mux", "Left", "XSPINL"},
+ {"SPK-XSP Mux", "Mono Mix", "XSPINM"},
+ {"SPK-XSP Mux", "Right", "XSPINR"},
+
+ /* HP LineOUT Paths */
+ {"HPOUTA", NULL, "HP Amp"},
+ {"HPOUTB", NULL, "HP Amp"},
+ {"LINEOUTA", NULL, "LO Amp"},
+ {"LINEOUTB", NULL, "LO Amp"},
+
+ {"HP Amp", "Switch", "HL Left DAC"},
+ {"HP Amp", "Switch", "HL Right DAC"},
+ {"LO Amp", "Switch", "HL Left DAC"},
+ {"LO Amp", "Switch", "HL Right DAC"},
+
+ {"HL Left DAC", "HL-XSP Volume", "HL Left Mixer"},
+ {"HL Right DAC", "HL-XSP Volume", "HL Right Mixer"},
+ {"HL Left DAC", "HL-ASP Volume", "HL Left Mixer"},
+ {"HL Right DAC", "HL-ASP Volume", "HL Right Mixer"},
+ {"HL Left DAC", "HL-VSP Volume", "HL Left Mixer"},
+ {"HL Right DAC", "HL-VSP Volume", "HL Right Mixer"},
+ /* Loopback */
+ {"HL Left DAC", "HL-IP Volume", "HL Left Mixer"},
+ {"HL Right DAC", "HL-IP Volume", "HL Right Mixer"},
+ {"HL Left Mixer", NULL, "Input Left Capture"},
+ {"HL Right Mixer", NULL, "Input Right Capture"},
+
+ {"HL Left Mixer", NULL, "ASPINL"},
+ {"HL Right Mixer", NULL, "ASPINR"},
+ {"HL Left Mixer", NULL, "XSPINL"},
+ {"HL Right Mixer", NULL, "XSPINR"},
+ {"HL Left Mixer", NULL, "VSPINOUT"},
+ {"HL Right Mixer", NULL, "VSPINOUT"},
+
+ {"ASPINL", NULL, "ASP Playback"},
+ {"ASPINM", NULL, "ASP Playback"},
+ {"ASPINR", NULL, "ASP Playback"},
+ {"XSPINL", NULL, "XSP Playback"},
+ {"XSPINM", NULL, "XSP Playback"},
+ {"XSPINR", NULL, "XSP Playback"},
+ {"VSPINOUT", NULL, "VSP Playback"},
+
+ /* Capture Paths */
+ {"MIC1", NULL, "MIC1 Bias"},
+ {"PGA Left Mux", "Mic 1", "MIC1"},
+ {"MIC2", NULL, "MIC2 Bias"},
+ {"PGA Right Mux", "Mic 2", "MIC2"},
+
+ {"PGA Left Mux", "Line A", "LINEINA"},
+ {"PGA Right Mux", "Line B", "LINEINB"},
+
+ {"PGA Left", NULL, "PGA Left Mux"},
+ {"PGA Right", NULL, "PGA Right Mux"},
+
+ {"ADC Left", NULL, "PGA Left"},
+ {"ADC Right", NULL, "PGA Right"},
+ {"DMIC Left", NULL, "DMICA"},
+ {"DMIC Right", NULL, "DMICB"},
+
+ {"Input Left Capture", "ADC Left Input", "ADC Left"},
+ {"Input Right Capture", "ADC Right Input", "ADC Right"},
+ {"Input Left Capture", "DMIC Left Input", "DMIC Left"},
+ {"Input Right Capture", "DMIC Right Input", "DMIC Right"},
+
+ /* Audio Capture */
+ {"ASPL Output Mixer", NULL, "Input Left Capture"},
+ {"ASPR Output Mixer", NULL, "Input Right Capture"},
+
+ {"ASPOUTL", "ASP-IP Volume", "ASPL Output Mixer"},
+ {"ASPOUTR", "ASP-IP Volume", "ASPR Output Mixer"},
+
+ /* Auxillary Capture */
+ {"XSPL Output Mixer", NULL, "Input Left Capture"},
+ {"XSPR Output Mixer", NULL, "Input Right Capture"},
+
+ {"XSPOUTL", "XSP-IP Volume", "XSPL Output Mixer"},
+ {"XSPOUTR", "XSP-IP Volume", "XSPR Output Mixer"},
+
+ {"XSPOUTL", NULL, "XSPL Output Mixer"},
+ {"XSPOUTR", NULL, "XSPR Output Mixer"},
+
+ /* Voice Capture */
+ {"VSP Output Mixer", NULL, "Input Left Capture"},
+ {"VSP Output Mixer", NULL, "Input Right Capture"},
+
+ {"VSPINOUT", "VSP-IP Volume", "VSP Output Mixer"},
+
+ {"VSPINOUT", NULL, "VSP Output Mixer"},
+
+ {"ASP Capture", NULL, "ASPOUTL"},
+ {"ASP Capture", NULL, "ASPOUTR"},
+ {"XSP Capture", NULL, "XSPOUTL"},
+ {"XSP Capture", NULL, "XSPOUTR"},
+ {"VSP Capture", NULL, "VSPINOUT"},
+};
+
+struct cs42l73_mclk_div {
+ u32 mclk;
+ u32 srate;
+ u8 mmcc;
+};
+
+static struct cs42l73_mclk_div cs42l73_mclk_coeffs[] = {
+ /* MCLK, Sample Rate, xMMCC[5:0] */
+ {5644800, 11025, 0x30},
+ {5644800, 22050, 0x20},
+ {5644800, 44100, 0x10},
+
+ {6000000, 8000, 0x39},
+ {6000000, 11025, 0x33},
+ {6000000, 12000, 0x31},
+ {6000000, 16000, 0x29},
+ {6000000, 22050, 0x23},
+ {6000000, 24000, 0x21},
+ {6000000, 32000, 0x19},
+ {6000000, 44100, 0x13},
+ {6000000, 48000, 0x11},
+
+ {6144000, 8000, 0x38},
+ {6144000, 12000, 0x30},
+ {6144000, 16000, 0x28},
+ {6144000, 24000, 0x20},
+ {6144000, 32000, 0x18},
+ {6144000, 48000, 0x10},
+
+ {6500000, 8000, 0x3C},
+ {6500000, 11025, 0x35},
+ {6500000, 12000, 0x34},
+ {6500000, 16000, 0x2C},
+ {6500000, 22050, 0x25},
+ {6500000, 24000, 0x24},
+ {6500000, 32000, 0x1C},
+ {6500000, 44100, 0x15},
+ {6500000, 48000, 0x14},
+
+ {6400000, 8000, 0x3E},
+ {6400000, 11025, 0x37},
+ {6400000, 12000, 0x36},
+ {6400000, 16000, 0x2E},
+ {6400000, 22050, 0x27},
+ {6400000, 24000, 0x26},
+ {6400000, 32000, 0x1E},
+ {6400000, 44100, 0x17},
+ {6400000, 48000, 0x16},
+};
+
+struct cs42l73_mclkx_div {
+ u32 mclkx;
+ u8 ratio;
+ u8 mclkdiv;
+};
+
+static struct cs42l73_mclkx_div cs42l73_mclkx_coeffs[] = {
+ {5644800, 1, 0}, /* 5644800 */
+ {6000000, 1, 0}, /* 6000000 */
+ {6144000, 1, 0}, /* 6144000 */
+ {11289600, 2, 2}, /* 5644800 */
+ {12288000, 2, 2}, /* 6144000 */
+ {12000000, 2, 2}, /* 6000000 */
+ {13000000, 2, 2}, /* 6500000 */
+ {19200000, 3, 3}, /* 6400000 */
+ {24000000, 4, 4}, /* 6000000 */
+ {26000000, 4, 4}, /* 6500000 */
+ {38400000, 6, 5} /* 6400000 */
+};
+
+static int cs42l73_get_mclkx_coeff(int mclkx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l73_mclkx_coeffs); i++) {
+ if (cs42l73_mclkx_coeffs[i].mclkx == mclkx)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int cs42l73_get_mclk_coeff(int mclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l73_mclk_coeffs); i++) {
+ if (cs42l73_mclk_coeffs[i].mclk == mclk &&
+ cs42l73_mclk_coeffs[i].srate == srate)
+ return i;
+ }
+ return -EINVAL;
+
+}
+
+static int cs42l73_set_mclk(struct snd_soc_dai *dai, unsigned int freq)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ int mclkx_coeff;
+ u32 mclk = 0;
+ u8 dmmcc = 0;
+
+ /* MCLKX -> MCLK */
+ mclkx_coeff = cs42l73_get_mclkx_coeff(freq);
+ if (mclkx_coeff < 0)
+ return mclkx_coeff;
+
+ mclk = cs42l73_mclkx_coeffs[mclkx_coeff].mclkx /
+ cs42l73_mclkx_coeffs[mclkx_coeff].ratio;
+
+ dev_dbg(codec->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);
+
+ priv->sysclk = mclkx_coeff;
+ priv->mclk = mclk;
+
+ return 0;
+}
+
+static int cs42l73_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case CS42L73_CLKID_MCLK1:
+ break;
+ case CS42L73_CLKID_MCLK2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((cs42l73_set_mclk(dai, freq)) < 0) {
+ dev_err(codec->dev, "Unable to set MCLK for dai %s\n",
+ dai->name);
+ return -EINVAL;
+ }
+
+ priv->mclksel = clk_id;
+
+ return 0;
+}
+
+static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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));
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ mmcc |= CS42L73_MS_MASTER;
+ break;
+
+ case SND_SOC_DAIFMT_CBS_CFS:
+ mmcc &= ~CS42L73_MS_MASTER;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ inv = (fmt & SND_SOC_DAIFMT_INV_MASK);
+
+ switch (format) {
+ case SND_SOC_DAIFMT_I2S:
+ spc &= ~CS42L73_SPDIF_PCM;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ if (mmcc & CS42L73_MS_MASTER) {
+ dev_err(codec->dev,
+ "PCM format in slave mode only\n");
+ return -EINVAL;
+ }
+ if (id == CS42L73_ASP) {
+ dev_err(codec->dev,
+ "PCM format is not supported on ASP port\n");
+ return -EINVAL;
+ }
+ spc |= CS42L73_SPDIF_PCM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (spc & CS42L73_SPDIF_PCM) {
+ /* Clear PCM mode, clear PCM_BIT_ORDER bit for MSB->LSB */
+ spc &= ~(CS42L73_PCM_MODE_MASK | CS42L73_PCM_BIT_ORDER);
+ switch (format) {
+ case SND_SOC_DAIFMT_DSP_B:
+ if (inv == SND_SOC_DAIFMT_IB_IF)
+ spc |= CS42L73_PCM_MODE0;
+ if (inv == SND_SOC_DAIFMT_IB_NF)
+ spc |= CS42L73_PCM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ if (inv == SND_SOC_DAIFMT_IB_IF)
+ spc |= CS42L73_PCM_MODE1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ priv->config[id].spc = spc;
+ priv->config[id].mmcc = mmcc;
+
+ return 0;
+}
+
+static const unsigned int cs42l73_asrc_rates[] = {
+ 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000
+};
+
+static unsigned int cs42l73_get_xspfs_coeff(u32 rate)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(cs42l73_asrc_rates); i++) {
+ if (cs42l73_asrc_rates[i] == rate)
+ return i + 1;
+ }
+ return 0; /* 0 = Don't know */
+}
+
+static void cs42l73_update_asrc(struct snd_soc_codec *codec, int id, int srate)
+{
+ u8 spfs = 0;
+
+ if (srate > 0)
+ spfs = cs42l73_get_xspfs_coeff(srate);
+
+ switch (id) {
+ case CS42L73_XSP:
+ snd_soc_update_bits(codec, CS42L73_VXSPFS, 0x0f, spfs);
+ break;
+ case CS42L73_ASP:
+ snd_soc_update_bits(codec, CS42L73_ASPC, 0x3c, spfs << 2);
+ break;
+ case CS42L73_VSP:
+ snd_soc_update_bits(codec, CS42L73_VXSPFS, 0xf0, spfs << 4);
+ break;
+ default:
+ break;
+ }
+}
+
+static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ int id = dai->id;
+ int mclk_coeff;
+ int srate = params_rate(params);
+
+ if (priv->config[id].mmcc & CS42L73_MS_MASTER) {
+ /* CS42L73 Master */
+ /* MCLK -> srate */
+ mclk_coeff =
+ cs42l73_get_mclk_coeff(priv->mclk, srate);
+
+ if (mclk_coeff < 0)
+ return -EINVAL;
+
+ dev_dbg(codec->dev,
+ "DAI[%d]: MCLK %u, srate %u, MMCC[5:0] = %x\n",
+ id, priv->mclk, srate,
+ cs42l73_mclk_coeffs[mclk_coeff].mmcc);
+
+ priv->config[id].mmcc &= 0xC0;
+ priv->config[id].mmcc |= cs42l73_mclk_coeffs[mclk_coeff].mmcc;
+ priv->config[id].spc &= 0xFC;
+ /* Use SCLK=64*Fs if internal MCLK >= 6.4MHz */
+ if (priv->mclk >= 6400000)
+ priv->config[id].spc |= CS42L73_MCK_SCLK_64FS;
+ else
+ priv->config[id].spc |= CS42L73_MCK_SCLK_MCLK;
+ } else {
+ /* CS42L73 Slave */
+ priv->config[id].spc &= 0xFC;
+ priv->config[id].spc |= CS42L73_MCK_SCLK_64FS;
+ }
+ /* Update ASRCs */
+ priv->config[id].srate = srate;
+
+ snd_soc_write(codec, CS42L73_SPC(id), priv->config[id].spc);
+ snd_soc_write(codec, CS42L73_MMCC(id), priv->config[id].mmcc);
+
+ cs42l73_update_asrc(codec, id, srate);
+
+ return 0;
+}
+
+static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct cs42l73_private *cs42l73 = snd_soc_codec_get_drvdata(codec);
+
+ 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);
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ regcache_cache_only(cs42l73->regmap, false);
+ regcache_sync(cs42l73->regmap);
+ }
+ snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 1);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 1);
+ if (cs42l73->shutdwn_delay > 0) {
+ mdelay(cs42l73->shutdwn_delay);
+ cs42l73->shutdwn_delay = 0;
+ } else {
+ mdelay(15); /* Min amount of time requred to power
+ * down.
+ */
+ }
+ snd_soc_update_bits(codec, 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;
+ int id = dai->id;
+
+ return snd_soc_update_bits(codec, CS42L73_SPC(id),
+ 0x7F, tristate << 7);
+}
+
+static const struct snd_pcm_hw_constraint_list constraints_12_24 = {
+ .count = ARRAY_SIZE(cs42l73_asrc_rates),
+ .list = cs42l73_asrc_rates,
+};
+
+static int cs42l73_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_12_24);
+ return 0;
+}
+
+
+#define CS42L73_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops cs42l73_ops = {
+ .startup = cs42l73_pcm_startup,
+ .hw_params = cs42l73_pcm_hw_params,
+ .set_fmt = cs42l73_set_dai_fmt,
+ .set_sysclk = cs42l73_set_sysclk,
+ .set_tristate = cs42l73_set_tristate,
+};
+
+static struct snd_soc_dai_driver cs42l73_dai[] = {
+ {
+ .name = "cs42l73-xsp",
+ .id = CS42L73_XSP,
+ .playback = {
+ .stream_name = "XSP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L73_FORMATS,
+ },
+ .capture = {
+ .stream_name = "XSP Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L73_FORMATS,
+ },
+ .ops = &cs42l73_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "cs42l73-asp",
+ .id = CS42L73_ASP,
+ .playback = {
+ .stream_name = "ASP Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L73_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASP Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L73_FORMATS,
+ },
+ .ops = &cs42l73_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "cs42l73-vsp",
+ .id = CS42L73_VSP,
+ .playback = {
+ .stream_name = "VSP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L73_FORMATS,
+ },
+ .capture = {
+ .stream_name = "VSP Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L73_FORMATS,
+ },
+ .ops = &cs42l73_ops,
+ .symmetric_rates = 1,
+ }
+};
+
+static int cs42l73_probe(struct snd_soc_codec *codec)
+{
+ struct cs42l73_private *cs42l73 = snd_soc_codec_get_drvdata(codec);
+
+ /* Set Charge Pump Frequency */
+ if (cs42l73->pdata.chgfreq)
+ snd_soc_update_bits(codec, CS42L73_CPFCHC,
+ CS42L73_CHARGEPUMP_MASK,
+ cs42l73->pdata.chgfreq << 4);
+
+ /* MCLK1 as master clk */
+ cs42l73->mclksel = CS42L73_CLKID_MCLK1;
+ cs42l73->mclk = 0;
+
+ return 0;
+}
+
+static const struct snd_soc_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 regmap_config cs42l73_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS42L73_MAX_REGISTER,
+ .reg_defaults = cs42l73_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs42l73_reg_defaults),
+ .volatile_reg = cs42l73_volatile_register,
+ .readable_reg = cs42l73_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct cs42l73_private *cs42l73;
+ struct cs42l73_platform_data *pdata = dev_get_platdata(&i2c_client->dev);
+ int ret;
+ unsigned int devid = 0;
+ unsigned int reg;
+ u32 val32;
+
+ cs42l73 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l73_private),
+ GFP_KERNEL);
+ if (!cs42l73)
+ return -ENOMEM;
+
+ cs42l73->regmap = devm_regmap_init_i2c(i2c_client, &cs42l73_regmap);
+ if (IS_ERR(cs42l73->regmap)) {
+ ret = PTR_ERR(cs42l73->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs42l73->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(&i2c_client->dev,
+ sizeof(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;
+ }
+
+ 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);
+ }
+
+ 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;
+
+ if (devid != CS42L73_DEVID) {
+ ret = -ENODEV;
+ dev_err(&i2c_client->dev,
+ "CS42L73 Device ID (%X). Expected %X\n",
+ devid, CS42L73_DEVID);
+ return ret;
+ }
+
+ ret = regmap_read(cs42l73->regmap, CS42L73_REVID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ return ret;;
+ }
+
+ 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,
+ ARRAY_SIZE(cs42l73_dai));
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int cs42l73_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct of_device_id cs42l73_of_match[] = {
+ { .compatible = "cirrus,cs42l73", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs42l73_of_match);
+
+static const struct i2c_device_id cs42l73_id[] = {
+ {"cs42l73", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs42l73_id);
+
+static struct i2c_driver cs42l73_i2c_driver = {
+ .driver = {
+ .name = "cs42l73",
+ .owner = THIS_MODULE,
+ .of_match_table = cs42l73_of_match,
+ },
+ .id_table = cs42l73_id,
+ .probe = cs42l73_i2c_probe,
+ .remove = cs42l73_i2c_remove,
+
+};
+
+module_i2c_driver(cs42l73_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L73 driver");
+MODULE_AUTHOR("Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs42l73.h b/kernel/sound/soc/codecs/cs42l73.h
new file mode 100644
index 000000000..45746186a
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs42l73.h
@@ -0,0 +1,226 @@
+/*
+ * ALSA SoC CS42L73 codec driver
+ *
+ * Copyright 2011 Cirrus Logic, Inc.
+ *
+ * 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__
+#define __CS42L73_H__
+
+/* I2C Registers */
+/* I2C Address: 1001010[R/W] - 10010100 = 0x94(Write); 10010101 = 0x95(Read) */
+#define CS42L73_CHIP_ID 0x4a
+#define CS42L73_DEVID_AB 0x01 /* Device ID A & B [RO]. */
+#define CS42L73_DEVID_CD 0x02 /* Device ID C & D [RO]. */
+#define CS42L73_DEVID_E 0x03 /* Device ID E [RO]. */
+#define CS42L73_REVID 0x05 /* Revision ID [RO]. */
+#define CS42L73_PWRCTL1 0x06 /* Power Control 1. */
+#define CS42L73_PWRCTL2 0x07 /* Power Control 2. */
+#define CS42L73_PWRCTL3 0x08 /* Power Control 3. */
+#define CS42L73_CPFCHC 0x09 /* Charge Pump Freq. Class H Ctl. */
+#define CS42L73_OLMBMSDC 0x0A /* Output Load, MIC Bias, MIC2 SDT */
+#define CS42L73_DMMCC 0x0B /* Digital MIC & Master Clock Ctl. */
+#define CS42L73_XSPC 0x0C /* Auxiliary Serial Port (XSP) Ctl. */
+#define CS42L73_XSPMMCC 0x0D /* XSP Master Mode Clocking Control. */
+#define CS42L73_ASPC 0x0E /* Audio Serial Port (ASP) Control. */
+#define CS42L73_ASPMMCC 0x0F /* ASP Master Mode Clocking Control. */
+#define CS42L73_VSPC 0x10 /* Voice Serial Port (VSP) Control. */
+#define CS42L73_VSPMMCC 0x11 /* VSP Master Mode Clocking Control. */
+#define CS42L73_VXSPFS 0x12 /* VSP & XSP Sample Rate. */
+#define CS42L73_MIOPC 0x13 /* Misc. Input & Output Path Control. */
+#define CS42L73_ADCIPC 0x14 /* ADC/IP Control. */
+#define CS42L73_MICAPREPGAAVOL 0x15 /* MIC 1 [A] PreAmp, PGAA Vol. */
+#define CS42L73_MICBPREPGABVOL 0x16 /* MIC 2 [B] PreAmp, PGAB Vol. */
+#define CS42L73_IPADVOL 0x17 /* Input Pat7h A Digital Volume. */
+#define CS42L73_IPBDVOL 0x18 /* Input Path B Digital Volume. */
+#define CS42L73_PBDC 0x19 /* Playback Digital Control. */
+#define CS42L73_HLADVOL 0x1A /* HP/Line A Out Digital Vol. */
+#define CS42L73_HLBDVOL 0x1B /* HP/Line B Out Digital Vol. */
+#define CS42L73_SPKDVOL 0x1C /* Spkphone Out [A] Digital Vol. */
+#define CS42L73_ESLDVOL 0x1D /* Ear/Spkphone LO [B] Digital */
+#define CS42L73_HPAAVOL 0x1E /* HP A Analog Volume. */
+#define CS42L73_HPBAVOL 0x1F /* HP B Analog Volume. */
+#define CS42L73_LOAAVOL 0x20 /* Line Out A Analog Volume. */
+#define CS42L73_LOBAVOL 0x21 /* Line Out B Analog Volume. */
+#define CS42L73_STRINV 0x22 /* Stereo Input Path Adv. Vol. */
+#define CS42L73_XSPINV 0x23 /* Auxiliary Port Input Advisory Vol. */
+#define CS42L73_ASPINV 0x24 /* Audio Port Input Advisory Vol. */
+#define CS42L73_VSPINV 0x25 /* Voice Port Input Advisory Vol. */
+#define CS42L73_LIMARATEHL 0x26 /* Lmtr Attack Rate HP/Line. */
+#define CS42L73_LIMRRATEHL 0x27 /* Lmtr Ctl, Rel.Rate HP/Line. */
+#define CS42L73_LMAXHL 0x28 /* Lmtr Thresholds HP/Line. */
+#define CS42L73_LIMARATESPK 0x29 /* Lmtr Attack Rate Spkphone [A]. */
+#define CS42L73_LIMRRATESPK 0x2A /* Lmtr Ctl,Release Rate Spk. [A]. */
+#define CS42L73_LMAXSPK 0x2B /* Lmtr Thresholds Spkphone [A]. */
+#define CS42L73_LIMARATEESL 0x2C /* Lmtr Attack Rate */
+#define CS42L73_LIMRRATEESL 0x2D /* Lmtr Ctl,Release Rate */
+#define CS42L73_LMAXESL 0x2E /* Lmtr Thresholds */
+#define CS42L73_ALCARATE 0x2F /* ALC Enable, Attack Rate AB. */
+#define CS42L73_ALCRRATE 0x30 /* ALC Release Rate AB. */
+#define CS42L73_ALCMINMAX 0x31 /* ALC Thresholds AB. */
+#define CS42L73_NGCAB 0x32 /* Noise Gate Ctl AB. */
+#define CS42L73_ALCNGMC 0x33 /* ALC & Noise Gate Misc Ctl. */
+#define CS42L73_MIXERCTL 0x34 /* Mixer Control. */
+#define CS42L73_HLAIPAA 0x35 /* HP/LO Left Mixer: L. */
+#define CS42L73_HLBIPBA 0x36 /* HP/LO Right Mixer: R. */
+#define CS42L73_HLAXSPAA 0x37 /* HP/LO Left Mixer: XSP L */
+#define CS42L73_HLBXSPBA 0x38 /* HP/LO Right Mixer: XSP R */
+#define CS42L73_HLAASPAA 0x39 /* HP/LO Left Mixer: ASP L */
+#define CS42L73_HLBASPBA 0x3A /* HP/LO Right Mixer: ASP R */
+#define CS42L73_HLAVSPMA 0x3B /* HP/LO Left Mixer: VSP. */
+#define CS42L73_HLBVSPMA 0x3C /* HP/LO Right Mixer: VSP */
+#define CS42L73_XSPAIPAA 0x3D /* XSP Left Mixer: Left */
+#define CS42L73_XSPBIPBA 0x3E /* XSP Rt. Mixer: Right */
+#define CS42L73_XSPAXSPAA 0x3F /* XSP Left Mixer: XSP L */
+#define CS42L73_XSPBXSPBA 0x40 /* XSP Rt. Mixer: XSP R */
+#define CS42L73_XSPAASPAA 0x41 /* XSP Left Mixer: ASP L */
+#define CS42L73_XSPAASPBA 0x42 /* XSP Rt. Mixer: ASP R */
+#define CS42L73_XSPAVSPMA 0x43 /* XSP Left Mixer: VSP */
+#define CS42L73_XSPBVSPMA 0x44 /* XSP Rt. Mixer: VSP */
+#define CS42L73_ASPAIPAA 0x45 /* ASP Left Mixer: Left */
+#define CS42L73_ASPBIPBA 0x46 /* ASP Rt. Mixer: Right */
+#define CS42L73_ASPAXSPAA 0x47 /* ASP Left Mixer: XSP L */
+#define CS42L73_ASPBXSPBA 0x48 /* ASP Rt. Mixer: XSP R */
+#define CS42L73_ASPAASPAA 0x49 /* ASP Left Mixer: ASP L */
+#define CS42L73_ASPBASPBA 0x4A /* ASP Rt. Mixer: ASP R */
+#define CS42L73_ASPAVSPMA 0x4B /* ASP Left Mixer: VSP */
+#define CS42L73_ASPBVSPMA 0x4C /* ASP Rt. Mixer: VSP */
+#define CS42L73_VSPAIPAA 0x4D /* VSP Left Mixer: Left */
+#define CS42L73_VSPBIPBA 0x4E /* VSP Rt. Mixer: Right */
+#define CS42L73_VSPAXSPAA 0x4F /* VSP Left Mixer: XSP L */
+#define CS42L73_VSPBXSPBA 0x50 /* VSP Rt. Mixer: XSP R */
+#define CS42L73_VSPAASPAA 0x51 /* VSP Left Mixer: ASP Left */
+#define CS42L73_VSPBASPBA 0x52 /* VSP Rt. Mixer: ASP Right */
+#define CS42L73_VSPAVSPMA 0x53 /* VSP Left Mixer: VSP */
+#define CS42L73_VSPBVSPMA 0x54 /* VSP Rt. Mixer: VSP */
+#define CS42L73_MMIXCTL 0x55 /* Mono Mixer Controls. */
+#define CS42L73_SPKMIPMA 0x56 /* SPK Mono Mixer: In. Path */
+#define CS42L73_SPKMXSPA 0x57 /* SPK Mono Mixer: XSP Mono/L/R Att. */
+#define CS42L73_SPKMASPA 0x58 /* SPK Mono Mixer: ASP Mono/L/R Att. */
+#define CS42L73_SPKMVSPMA 0x59 /* SPK Mono Mixer: VSP Mono Atten. */
+#define CS42L73_ESLMIPMA 0x5A /* Ear/SpLO Mono Mixer: */
+#define CS42L73_ESLMXSPA 0x5B /* Ear/SpLO Mono Mixer: XSP */
+#define CS42L73_ESLMASPA 0x5C /* Ear/SpLO Mono Mixer: ASP */
+#define CS42L73_ESLMVSPMA 0x5D /* Ear/SpLO Mono Mixer: VSP */
+#define CS42L73_IM1 0x5E /* Interrupt Mask 1. */
+#define CS42L73_IM2 0x5F /* Interrupt Mask 2. */
+#define CS42L73_IS1 0x60 /* Interrupt Status 1 [RO]. */
+#define CS42L73_IS2 0x61 /* Interrupt Status 2 [RO]. */
+#define CS42L73_MAX_REGISTER 0x61 /* Total Registers */
+/* Bitfield Definitions */
+
+/* CS42L73_PWRCTL1 */
+#define CS42L73_PDN_ADCB (1 << 7)
+#define CS42L73_PDN_DMICB (1 << 6)
+#define CS42L73_PDN_ADCA (1 << 5)
+#define CS42L73_PDN_DMICA (1 << 4)
+#define CS42L73_PDN_LDO (1 << 2)
+#define CS42L73_DISCHG_FILT (1 << 1)
+#define CS42L73_PDN (1 << 0)
+
+/* CS42L73_PWRCTL2 */
+#define CS42L73_PDN_MIC2_BIAS (1 << 7)
+#define CS42L73_PDN_MIC1_BIAS (1 << 6)
+#define CS42L73_PDN_VSP (1 << 4)
+#define CS42L73_PDN_ASP_SDOUT (1 << 3)
+#define CS42L73_PDN_ASP_SDIN (1 << 2)
+#define CS42L73_PDN_XSP_SDOUT (1 << 1)
+#define CS42L73_PDN_XSP_SDIN (1 << 0)
+
+/* CS42L73_PWRCTL3 */
+#define CS42L73_PDN_THMS (1 << 5)
+#define CS42L73_PDN_SPKLO (1 << 4)
+#define CS42L73_PDN_EAR (1 << 3)
+#define CS42L73_PDN_SPK (1 << 2)
+#define CS42L73_PDN_LO (1 << 1)
+#define CS42L73_PDN_HP (1 << 0)
+
+/* Thermal Overload Detect. Requires interrupt ... */
+#define CS42L73_THMOVLD_150C 0
+#define CS42L73_THMOVLD_132C 1
+#define CS42L73_THMOVLD_115C 2
+#define CS42L73_THMOVLD_098C 3
+
+#define CS42L73_CHARGEPUMP_MASK (0xF0)
+
+/* CS42L73_ASPC, CS42L73_XSPC, CS42L73_VSPC */
+#define CS42L73_SP_3ST (1 << 7)
+#define CS42L73_SPDIF_I2S (0 << 6)
+#define CS42L73_SPDIF_PCM (1 << 6)
+#define CS42L73_PCM_MODE0 (0 << 4)
+#define CS42L73_PCM_MODE1 (1 << 4)
+#define CS42L73_PCM_MODE2 (2 << 4)
+#define CS42L73_PCM_MODE_MASK (3 << 4)
+#define CS42L73_PCM_BIT_ORDER (1 << 3)
+#define CS42L73_MCK_SCLK_64FS (0 << 0)
+#define CS42L73_MCK_SCLK_MCLK (2 << 0)
+#define CS42L73_MCK_SCLK_PREMCLK (3 << 0)
+
+/* CS42L73_xSPMMCC */
+#define CS42L73_MS_MASTER (1 << 7)
+
+
+/* CS42L73_DMMCC */
+#define CS42L73_MCLKDIS (1 << 0)
+#define CS42L73_MCLKSEL_MCLK2 (1 << 4)
+#define CS42L73_MCLKSEL_MCLK1 (0 << 4)
+
+/* CS42L73 MCLK derived from MCLK1 or MCLK2 */
+#define CS42L73_CLKID_MCLK1 0
+#define CS42L73_CLKID_MCLK2 1
+
+#define CS42L73_MCLKXDIV 0
+#define CS42L73_MMCCDIV 1
+
+#define CS42L73_XSP 0
+#define CS42L73_ASP 1
+#define CS42L73_VSP 2
+
+/* IS1, IM1 */
+#define CS42L73_MIC2_SDET (1 << 6)
+#define CS42L73_THMOVLD (1 << 4)
+#define CS42L73_DIGMIXOVFL (1 << 3)
+#define CS42L73_IPBOVFL (1 << 1)
+#define CS42L73_IPAOVFL (1 << 0)
+
+/* Analog Softramp */
+#define CS42L73_ANLGOSFT (1 << 0)
+
+/* HP A/B Analog Mute */
+#define CS42L73_HPA_MUTE (1 << 7)
+/* LO A/B Analog Mute */
+#define CS42L73_LOA_MUTE (1 << 7)
+/* Digital Mute */
+#define CS42L73_HLAD_MUTE (1 << 0)
+#define CS42L73_HLBD_MUTE (1 << 1)
+#define CS42L73_SPKD_MUTE (1 << 2)
+#define CS42L73_ESLD_MUTE (1 << 3)
+
+/* Misc defines for codec */
+#define CS42L73_DEVID 0x00042A73
+#define CS42L73_MCLKX_MIN 5644800
+#define CS42L73_MCLKX_MAX 38400000
+
+#define CS42L73_SPC(id) (CS42L73_XSPC + (id << 1))
+#define CS42L73_MMCC(id) (CS42L73_XSPMMCC + (id << 1))
+#define CS42L73_SPFS(id) ((id == CS42L73_ASP) ? CS42L73_ASPC : CS42L73_VXSPFS)
+
+#endif /* __CS42L73_H__ */
diff --git a/kernel/sound/soc/codecs/cs42xx8-i2c.c b/kernel/sound/soc/codecs/cs42xx8-i2c.c
new file mode 100644
index 000000000..657dce27e
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs42xx8-i2c.c
@@ -0,0 +1,64 @@
+/*
+ * Cirrus Logic CS42448/CS42888 Audio CODEC DAI I2C driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <Guangyu.Chen@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.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)
+{
+ u32 ret = cs42xx8_probe(&i2c->dev,
+ devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config));
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(&i2c->dev);
+ pm_request_idle(&i2c->dev);
+
+ return 0;
+}
+
+static int 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[] = {
+ {"cs42448", (kernel_ulong_t)&cs42448_data},
+ {"cs42888", (kernel_ulong_t)&cs42888_data},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id);
+
+static struct i2c_driver cs42xx8_i2c_driver = {
+ .driver = {
+ .name = "cs42xx8",
+ .owner = THIS_MODULE,
+ .pm = &cs42xx8_pm,
+ },
+ .probe = cs42xx8_i2c_probe,
+ .remove = cs42xx8_i2c_remove,
+ .id_table = cs42xx8_i2c_id,
+};
+
+module_i2c_driver(cs42xx8_i2c_driver);
+
+MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec I2C Driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs42xx8.c b/kernel/sound/soc/codecs/cs42xx8.c
new file mode 100644
index 000000000..670ebfe12
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs42xx8.c
@@ -0,0 +1,603 @@
+/*
+ * Cirrus Logic CS42448/CS42888 Audio CODEC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <Guangyu.Chen@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_device.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 "cs42xx8.h"
+
+#define CS42XX8_NUM_SUPPLIES 4
+static const char *const cs42xx8_supply_names[CS42XX8_NUM_SUPPLIES] = {
+ "VA",
+ "VD",
+ "VLS",
+ "VLC",
+};
+
+#define CS42XX8_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/* codec private data */
+struct cs42xx8_priv {
+ struct regulator_bulk_data supplies[CS42XX8_NUM_SUPPLIES];
+ const struct cs42xx8_driver_data *drvdata;
+ struct regmap *regmap;
+ struct clk *clk;
+
+ bool slave_mode;
+ unsigned long sysclk;
+};
+
+/* -127.5dB to 0dB with step of 0.5dB */
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+/* -64dB to 24dB with step of 0.5dB */
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -6400, 50, 0);
+
+static const char *const cs42xx8_adc_single[] = { "Differential", "Single-Ended" };
+static const char *const cs42xx8_szc[] = { "Immediate Change", "Zero Cross",
+ "Soft Ramp", "Soft Ramp on Zero Cross" };
+
+static const struct soc_enum adc1_single_enum =
+ SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 4, 2, cs42xx8_adc_single);
+static const struct soc_enum adc2_single_enum =
+ SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 3, 2, cs42xx8_adc_single);
+static const struct soc_enum adc3_single_enum =
+ SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 2, 2, cs42xx8_adc_single);
+static const struct soc_enum dac_szc_enum =
+ SOC_ENUM_SINGLE(CS42XX8_TXCTL, 5, 4, cs42xx8_szc);
+static const struct soc_enum adc_szc_enum =
+ SOC_ENUM_SINGLE(CS42XX8_TXCTL, 0, 4, cs42xx8_szc);
+
+static const struct snd_kcontrol_new cs42xx8_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC1 Playback Volume", CS42XX8_VOLAOUT1,
+ CS42XX8_VOLAOUT2, 0, 0xff, 1, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Playback Volume", CS42XX8_VOLAOUT3,
+ CS42XX8_VOLAOUT4, 0, 0xff, 1, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC3 Playback Volume", CS42XX8_VOLAOUT5,
+ CS42XX8_VOLAOUT6, 0, 0xff, 1, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC4 Playback Volume", CS42XX8_VOLAOUT7,
+ CS42XX8_VOLAOUT8, 0, 0xff, 1, dac_tlv),
+ SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", CS42XX8_VOLAIN1,
+ CS42XX8_VOLAIN2, 0, -0x80, 0x30, 7, 0, adc_tlv),
+ SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", CS42XX8_VOLAIN3,
+ CS42XX8_VOLAIN4, 0, -0x80, 0x30, 7, 0, adc_tlv),
+ SOC_DOUBLE("DAC1 Invert Switch", CS42XX8_DACINV, 0, 1, 1, 0),
+ SOC_DOUBLE("DAC2 Invert Switch", CS42XX8_DACINV, 2, 3, 1, 0),
+ SOC_DOUBLE("DAC3 Invert Switch", CS42XX8_DACINV, 4, 5, 1, 0),
+ SOC_DOUBLE("DAC4 Invert Switch", CS42XX8_DACINV, 6, 7, 1, 0),
+ SOC_DOUBLE("ADC1 Invert Switch", CS42XX8_ADCINV, 0, 1, 1, 0),
+ SOC_DOUBLE("ADC2 Invert Switch", CS42XX8_ADCINV, 2, 3, 1, 0),
+ SOC_SINGLE("ADC High-Pass Filter Switch", CS42XX8_ADCCTL, 7, 1, 1),
+ SOC_SINGLE("DAC De-emphasis Switch", CS42XX8_ADCCTL, 5, 1, 0),
+ SOC_ENUM("ADC1 Single Ended Mode Switch", adc1_single_enum),
+ SOC_ENUM("ADC2 Single Ended Mode Switch", adc2_single_enum),
+ SOC_SINGLE("DAC Single Volume Control Switch", CS42XX8_TXCTL, 7, 1, 0),
+ SOC_ENUM("DAC Soft Ramp & Zero Cross Control Switch", dac_szc_enum),
+ SOC_SINGLE("DAC Auto Mute Switch", CS42XX8_TXCTL, 4, 1, 0),
+ SOC_SINGLE("Mute ADC Serial Port Switch", CS42XX8_TXCTL, 3, 1, 0),
+ SOC_SINGLE("ADC Single Volume Control Switch", CS42XX8_TXCTL, 2, 1, 0),
+ SOC_ENUM("ADC Soft Ramp & Zero Cross Control Switch", adc_szc_enum),
+};
+
+static const struct snd_kcontrol_new cs42xx8_adc3_snd_controls[] = {
+ SOC_DOUBLE_R_S_TLV("ADC3 Capture Volume", CS42XX8_VOLAIN5,
+ CS42XX8_VOLAIN6, 0, -0x80, 0x30, 7, 0, adc_tlv),
+ SOC_DOUBLE("ADC3 Invert Switch", CS42XX8_ADCINV, 4, 5, 1, 0),
+ SOC_ENUM("ADC3 Single Ended Mode Switch", adc3_single_enum),
+};
+
+static const struct snd_soc_dapm_widget cs42xx8_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC1", "Playback", CS42XX8_PWRCTL, 1, 1),
+ SND_SOC_DAPM_DAC("DAC2", "Playback", CS42XX8_PWRCTL, 2, 1),
+ SND_SOC_DAPM_DAC("DAC3", "Playback", CS42XX8_PWRCTL, 3, 1),
+ SND_SOC_DAPM_DAC("DAC4", "Playback", CS42XX8_PWRCTL, 4, 1),
+
+ SND_SOC_DAPM_OUTPUT("AOUT1L"),
+ SND_SOC_DAPM_OUTPUT("AOUT1R"),
+ SND_SOC_DAPM_OUTPUT("AOUT2L"),
+ SND_SOC_DAPM_OUTPUT("AOUT2R"),
+ SND_SOC_DAPM_OUTPUT("AOUT3L"),
+ SND_SOC_DAPM_OUTPUT("AOUT3R"),
+ SND_SOC_DAPM_OUTPUT("AOUT4L"),
+ SND_SOC_DAPM_OUTPUT("AOUT4R"),
+
+ SND_SOC_DAPM_ADC("ADC1", "Capture", CS42XX8_PWRCTL, 5, 1),
+ SND_SOC_DAPM_ADC("ADC2", "Capture", CS42XX8_PWRCTL, 6, 1),
+
+ SND_SOC_DAPM_INPUT("AIN1L"),
+ SND_SOC_DAPM_INPUT("AIN1R"),
+ SND_SOC_DAPM_INPUT("AIN2L"),
+ SND_SOC_DAPM_INPUT("AIN2R"),
+
+ SND_SOC_DAPM_SUPPLY("PWR", CS42XX8_PWRCTL, 0, 1, NULL, 0),
+};
+
+static const struct snd_soc_dapm_widget cs42xx8_adc3_dapm_widgets[] = {
+ SND_SOC_DAPM_ADC("ADC3", "Capture", CS42XX8_PWRCTL, 7, 1),
+
+ SND_SOC_DAPM_INPUT("AIN3L"),
+ SND_SOC_DAPM_INPUT("AIN3R"),
+};
+
+static const struct snd_soc_dapm_route cs42xx8_dapm_routes[] = {
+ /* Playback */
+ { "AOUT1L", NULL, "DAC1" },
+ { "AOUT1R", NULL, "DAC1" },
+ { "DAC1", NULL, "PWR" },
+
+ { "AOUT2L", NULL, "DAC2" },
+ { "AOUT2R", NULL, "DAC2" },
+ { "DAC2", NULL, "PWR" },
+
+ { "AOUT3L", NULL, "DAC3" },
+ { "AOUT3R", NULL, "DAC3" },
+ { "DAC3", NULL, "PWR" },
+
+ { "AOUT4L", NULL, "DAC4" },
+ { "AOUT4R", NULL, "DAC4" },
+ { "DAC4", NULL, "PWR" },
+
+ /* Capture */
+ { "ADC1", NULL, "AIN1L" },
+ { "ADC1", NULL, "AIN1R" },
+ { "ADC1", NULL, "PWR" },
+
+ { "ADC2", NULL, "AIN2L" },
+ { "ADC2", NULL, "AIN2R" },
+ { "ADC2", NULL, "PWR" },
+};
+
+static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = {
+ /* Capture */
+ { "ADC3", NULL, "AIN3L" },
+ { "ADC3", NULL, "AIN3R" },
+ { "ADC3", NULL, "PWR" },
+};
+
+struct cs42xx8_ratios {
+ unsigned int ratio;
+ unsigned char speed;
+ unsigned char mclk;
+};
+
+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) }
+};
+
+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);
+
+ cs42xx8->sysclk = freq;
+
+ return 0;
+}
+
+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);
+ u32 val;
+
+ /* Set DAI format */
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = CS42XX8_INTF_DAC_DIF_LEFTJ | CS42XX8_INTF_ADC_DIF_LEFTJ;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = CS42XX8_INTF_DAC_DIF_I2S | CS42XX8_INTF_ADC_DIF_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = CS42XX8_INTF_DAC_DIF_RIGHTJ | CS42XX8_INTF_ADC_DIF_RIGHTJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported dai format\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs42xx8->regmap, CS42XX8_INTF,
+ CS42XX8_INTF_DAC_DIF_MASK |
+ CS42XX8_INTF_ADC_DIF_MASK, val);
+
+ /* Set master/slave audio interface */
+ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs42xx8->slave_mode = true;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs42xx8->slave_mode = false;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported master/slave mode\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ u32 ratio = cs42xx8->sysclk / params_rate(params);
+ u32 i, fm, val, mask;
+
+ for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) {
+ if (cs42xx8_ratios[i].ratio == ratio)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(cs42xx8_ratios)) {
+ dev_err(codec->dev, "unsupported sysclk ratio\n");
+ return -EINVAL;
+ }
+
+ mask = CS42XX8_FUNCMOD_MFREQ_MASK;
+ val = cs42xx8_ratios[i].mclk;
+
+ fm = cs42xx8->slave_mode ? CS42XX8_FM_AUTO : cs42xx8_ratios[i].speed;
+
+ regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD,
+ CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask,
+ CS42XX8_FUNCMOD_xC_FM(tx, fm) | val);
+
+ return 0;
+}
+
+static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+
+ regmap_update_bits(cs42xx8->regmap, CS42XX8_DACMUTE,
+ CS42XX8_DACMUTE_ALL, mute ? CS42XX8_DACMUTE_ALL : 0);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs42xx8_dai_ops = {
+ .set_fmt = cs42xx8_set_dai_fmt,
+ .set_sysclk = cs42xx8_set_dai_sysclk,
+ .hw_params = cs42xx8_hw_params,
+ .digital_mute = cs42xx8_digital_mute,
+};
+
+static struct snd_soc_dai_driver cs42xx8_dai = {
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = CS42XX8_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = CS42XX8_FORMATS,
+ },
+ .ops = &cs42xx8_dai_ops,
+};
+
+static const struct reg_default cs42xx8_reg[] = {
+ { 0x01, 0x01 }, /* Chip I.D. and Revision Register */
+ { 0x02, 0x00 }, /* Power Control */
+ { 0x03, 0xF0 }, /* Functional Mode */
+ { 0x04, 0x46 }, /* Interface Formats */
+ { 0x05, 0x00 }, /* ADC Control & DAC De-Emphasis */
+ { 0x06, 0x10 }, /* Transition Control */
+ { 0x07, 0x00 }, /* DAC Channel Mute */
+ { 0x08, 0x00 }, /* Volume Control AOUT1 */
+ { 0x09, 0x00 }, /* Volume Control AOUT2 */
+ { 0x0a, 0x00 }, /* Volume Control AOUT3 */
+ { 0x0b, 0x00 }, /* Volume Control AOUT4 */
+ { 0x0c, 0x00 }, /* Volume Control AOUT5 */
+ { 0x0d, 0x00 }, /* Volume Control AOUT6 */
+ { 0x0e, 0x00 }, /* Volume Control AOUT7 */
+ { 0x0f, 0x00 }, /* Volume Control AOUT8 */
+ { 0x10, 0x00 }, /* DAC Channel Invert */
+ { 0x11, 0x00 }, /* Volume Control AIN1 */
+ { 0x12, 0x00 }, /* Volume Control AIN2 */
+ { 0x13, 0x00 }, /* Volume Control AIN3 */
+ { 0x14, 0x00 }, /* Volume Control AIN4 */
+ { 0x15, 0x00 }, /* Volume Control AIN5 */
+ { 0x16, 0x00 }, /* Volume Control AIN6 */
+ { 0x17, 0x00 }, /* ADC Channel Invert */
+ { 0x18, 0x00 }, /* Status Control */
+ { 0x1a, 0x00 }, /* Status Mask */
+ { 0x1b, 0x00 }, /* MUTEC Pin Control */
+};
+
+static bool cs42xx8_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42XX8_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42xx8_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42XX8_CHIPID:
+ case CS42XX8_STATUS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+const struct regmap_config cs42xx8_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS42XX8_LASTREG,
+ .reg_defaults = cs42xx8_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs42xx8_reg),
+ .volatile_reg = cs42xx8_volatile_register,
+ .writeable_reg = cs42xx8_writeable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(cs42xx8_regmap_config);
+
+static int cs42xx8_codec_probe(struct snd_soc_codec *codec)
+{
+ struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ switch (cs42xx8->drvdata->num_adcs) {
+ case 3:
+ snd_soc_add_codec_controls(codec, cs42xx8_adc3_snd_controls,
+ ARRAY_SIZE(cs42xx8_adc3_snd_controls));
+ snd_soc_dapm_new_controls(dapm, cs42xx8_adc3_dapm_widgets,
+ ARRAY_SIZE(cs42xx8_adc3_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, cs42xx8_adc3_dapm_routes,
+ ARRAY_SIZE(cs42xx8_adc3_dapm_routes));
+ break;
+ default:
+ break;
+ }
+
+ /* Mute all DAC channels */
+ regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, CS42XX8_DACMUTE_ALL);
+
+ return 0;
+}
+
+static const struct snd_soc_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),
+};
+
+const struct cs42xx8_driver_data cs42448_data = {
+ .name = "cs42448",
+ .num_adcs = 3,
+};
+EXPORT_SYMBOL_GPL(cs42448_data);
+
+const struct cs42xx8_driver_data cs42888_data = {
+ .name = "cs42888",
+ .num_adcs = 2,
+};
+EXPORT_SYMBOL_GPL(cs42888_data);
+
+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)
+{
+ const struct of_device_id *of_id = of_match_device(cs42xx8_of_match, dev);
+ struct cs42xx8_priv *cs42xx8;
+ int ret, val, i;
+
+ 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;
+
+ if (!cs42xx8->drvdata) {
+ dev_err(dev, "failed to find driver data\n");
+ return -EINVAL;
+ }
+
+ cs42xx8->clk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(cs42xx8->clk)) {
+ dev_err(dev, "failed to get the clock: %ld\n",
+ PTR_ERR(cs42xx8->clk));
+ return -EINVAL;
+ }
+
+ cs42xx8->sysclk = clk_get_rate(cs42xx8->clk);
+
+ for (i = 0; i < ARRAY_SIZE(cs42xx8->supplies); i++)
+ cs42xx8->supplies[i].supply = cs42xx8_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev,
+ ARRAY_SIZE(cs42xx8->supplies), cs42xx8->supplies);
+ if (ret) {
+ dev_err(dev, "failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies),
+ cs42xx8->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Make sure hardware reset done */
+ msleep(5);
+
+ 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) {
+ dev_err(dev, "failed to get device ID, ret = %d", ret);
+ goto err_enable;
+ }
+
+ /* The top four bits of the chip ID should be 0000 */
+ if (((val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4) != 0x00) {
+ dev_err(dev, "unmatched chip ID: %d\n",
+ (val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ dev_info(dev, "found device, revision %X\n",
+ val & CS42XX8_CHIPID_REV_ID_MASK);
+
+ 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);
+ if (ret) {
+ dev_err(dev, "failed to register codec:%d\n", ret);
+ goto err_enable;
+ }
+
+ regcache_cache_only(cs42xx8->regmap, true);
+
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
+ cs42xx8->supplies);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs42xx8_probe);
+
+#ifdef CONFIG_PM
+static int cs42xx8_runtime_resume(struct device *dev)
+{
+ struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(cs42xx8->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable mclk: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies),
+ cs42xx8->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable supplies: %d\n", ret);
+ goto err_clk;
+ }
+
+ /* Make sure hardware reset done */
+ msleep(5);
+
+ regcache_cache_only(cs42xx8->regmap, false);
+
+ ret = regcache_sync(cs42xx8->regmap);
+ if (ret) {
+ dev_err(dev, "failed to sync regmap: %d\n", ret);
+ goto err_bulk;
+ }
+
+ return 0;
+
+err_bulk:
+ regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
+ cs42xx8->supplies);
+err_clk:
+ clk_disable_unprepare(cs42xx8->clk);
+
+ return ret;
+}
+
+static int cs42xx8_runtime_suspend(struct device *dev)
+{
+ struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs42xx8->regmap, true);
+
+ regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
+ cs42xx8->supplies);
+
+ 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_SYMBOL_GPL(cs42xx8_pm);
+
+MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec Driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/cs42xx8.h b/kernel/sound/soc/codecs/cs42xx8.h
new file mode 100644
index 000000000..b2c10e537
--- /dev/null
+++ b/kernel/sound/soc/codecs/cs42xx8.h
@@ -0,0 +1,238 @@
+/*
+ * cs42xx8.h - Cirrus Logic CS42448/CS42888 Audio CODEC driver header file
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <Guangyu.Chen@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _CS42XX8_H
+#define _CS42XX8_H
+
+struct cs42xx8_driver_data {
+ char name[32];
+ int num_adcs;
+};
+
+extern const struct dev_pm_ops cs42xx8_pm;
+extern const struct cs42xx8_driver_data cs42448_data;
+extern const struct cs42xx8_driver_data cs42888_data;
+extern const struct regmap_config cs42xx8_regmap_config;
+int cs42xx8_probe(struct device *dev, struct regmap *regmap);
+
+/* CS42888 register map */
+#define CS42XX8_CHIPID 0x01 /* Chip ID */
+#define CS42XX8_PWRCTL 0x02 /* Power Control */
+#define CS42XX8_FUNCMOD 0x03 /* Functional Mode */
+#define CS42XX8_INTF 0x04 /* Interface Formats */
+#define CS42XX8_ADCCTL 0x05 /* ADC Control */
+#define CS42XX8_TXCTL 0x06 /* Transition Control */
+#define CS42XX8_DACMUTE 0x07 /* DAC Mute Control */
+#define CS42XX8_VOLAOUT1 0x08 /* Volume Control AOUT1 */
+#define CS42XX8_VOLAOUT2 0x09 /* Volume Control AOUT2 */
+#define CS42XX8_VOLAOUT3 0x0A /* Volume Control AOUT3 */
+#define CS42XX8_VOLAOUT4 0x0B /* Volume Control AOUT4 */
+#define CS42XX8_VOLAOUT5 0x0C /* Volume Control AOUT5 */
+#define CS42XX8_VOLAOUT6 0x0D /* Volume Control AOUT6 */
+#define CS42XX8_VOLAOUT7 0x0E /* Volume Control AOUT7 */
+#define CS42XX8_VOLAOUT8 0x0F /* Volume Control AOUT8 */
+#define CS42XX8_DACINV 0x10 /* DAC Channel Invert */
+#define CS42XX8_VOLAIN1 0x11 /* Volume Control AIN1 */
+#define CS42XX8_VOLAIN2 0x12 /* Volume Control AIN2 */
+#define CS42XX8_VOLAIN3 0x13 /* Volume Control AIN3 */
+#define CS42XX8_VOLAIN4 0x14 /* Volume Control AIN4 */
+#define CS42XX8_VOLAIN5 0x15 /* Volume Control AIN5 */
+#define CS42XX8_VOLAIN6 0x16 /* Volume Control AIN6 */
+#define CS42XX8_ADCINV 0x17 /* ADC Channel Invert */
+#define CS42XX8_STATUSCTL 0x18 /* Status Control */
+#define CS42XX8_STATUS 0x19 /* Status */
+#define CS42XX8_STATUSM 0x1A /* Status Mask */
+#define CS42XX8_MUTEC 0x1B /* MUTEC Pin Control */
+
+#define CS42XX8_FIRSTREG CS42XX8_CHIPID
+#define CS42XX8_LASTREG CS42XX8_MUTEC
+#define CS42XX8_NUMREGS (CS42XX8_LASTREG - CS42XX8_FIRSTREG + 1)
+#define CS42XX8_I2C_INCR 0x80
+
+/* Chip I.D. and Revision Register (Address 01h) */
+#define CS42XX8_CHIPID_CHIP_ID_MASK 0xF0
+#define CS42XX8_CHIPID_REV_ID_MASK 0x0F
+
+/* Power Control (Address 02h) */
+#define CS42XX8_PWRCTL_PDN_ADC3_SHIFT 7
+#define CS42XX8_PWRCTL_PDN_ADC3_MASK (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT)
+#define CS42XX8_PWRCTL_PDN_ADC3 (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT)
+#define CS42XX8_PWRCTL_PDN_ADC2_SHIFT 6
+#define CS42XX8_PWRCTL_PDN_ADC2_MASK (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT)
+#define CS42XX8_PWRCTL_PDN_ADC2 (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT)
+#define CS42XX8_PWRCTL_PDN_ADC1_SHIFT 5
+#define CS42XX8_PWRCTL_PDN_ADC1_MASK (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT)
+#define CS42XX8_PWRCTL_PDN_ADC1 (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC4_SHIFT 4
+#define CS42XX8_PWRCTL_PDN_DAC4_MASK (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC4 (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC3_SHIFT 3
+#define CS42XX8_PWRCTL_PDN_DAC3_MASK (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC3 (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC2_SHIFT 2
+#define CS42XX8_PWRCTL_PDN_DAC2_MASK (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC2 (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC1_SHIFT 1
+#define CS42XX8_PWRCTL_PDN_DAC1_MASK (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT)
+#define CS42XX8_PWRCTL_PDN_DAC1 (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT)
+#define CS42XX8_PWRCTL_PDN_SHIFT 0
+#define CS42XX8_PWRCTL_PDN_MASK (1 << CS42XX8_PWRCTL_PDN_SHIFT)
+#define CS42XX8_PWRCTL_PDN (1 << CS42XX8_PWRCTL_PDN_SHIFT)
+
+/* Functional Mode (Address 03h) */
+#define CS42XX8_FUNCMOD_DAC_FM_SHIFT 6
+#define CS42XX8_FUNCMOD_DAC_FM_WIDTH 2
+#define CS42XX8_FUNCMOD_DAC_FM_MASK (((1 << CS42XX8_FUNCMOD_DAC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_DAC_FM_SHIFT)
+#define CS42XX8_FUNCMOD_DAC_FM(v) ((v) << CS42XX8_FUNCMOD_DAC_FM_SHIFT)
+#define CS42XX8_FUNCMOD_ADC_FM_SHIFT 4
+#define CS42XX8_FUNCMOD_ADC_FM_WIDTH 2
+#define CS42XX8_FUNCMOD_ADC_FM_MASK (((1 << CS42XX8_FUNCMOD_ADC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_ADC_FM_SHIFT)
+#define CS42XX8_FUNCMOD_ADC_FM(v) ((v) << CS42XX8_FUNCMOD_ADC_FM_SHIFT)
+#define CS42XX8_FUNCMOD_xC_FM_MASK(x) ((x) ? CS42XX8_FUNCMOD_DAC_FM_MASK : CS42XX8_FUNCMOD_ADC_FM_MASK)
+#define CS42XX8_FUNCMOD_xC_FM(x, v) ((x) ? CS42XX8_FUNCMOD_DAC_FM(v) : CS42XX8_FUNCMOD_ADC_FM(v))
+#define CS42XX8_FUNCMOD_MFREQ_SHIFT 1
+#define CS42XX8_FUNCMOD_MFREQ_WIDTH 3
+#define CS42XX8_FUNCMOD_MFREQ_MASK (((1 << CS42XX8_FUNCMOD_MFREQ_WIDTH) - 1) << CS42XX8_FUNCMOD_MFREQ_SHIFT)
+#define CS42XX8_FUNCMOD_MFREQ_256(s) ((0 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
+#define CS42XX8_FUNCMOD_MFREQ_384(s) ((1 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
+#define CS42XX8_FUNCMOD_MFREQ_512(s) ((2 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
+#define CS42XX8_FUNCMOD_MFREQ_768(s) ((3 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
+#define CS42XX8_FUNCMOD_MFREQ_1024(s) ((4 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
+
+#define CS42XX8_FM_SINGLE 0
+#define CS42XX8_FM_DOUBLE 1
+#define CS42XX8_FM_QUAD 2
+#define CS42XX8_FM_AUTO 3
+
+/* Interface Formats (Address 04h) */
+#define CS42XX8_INTF_FREEZE_SHIFT 7
+#define CS42XX8_INTF_FREEZE_MASK (1 << CS42XX8_INTF_FREEZE_SHIFT)
+#define CS42XX8_INTF_FREEZE (1 << CS42XX8_INTF_FREEZE_SHIFT)
+#define CS42XX8_INTF_AUX_DIF_SHIFT 6
+#define CS42XX8_INTF_AUX_DIF_MASK (1 << CS42XX8_INTF_AUX_DIF_SHIFT)
+#define CS42XX8_INTF_AUX_DIF (1 << CS42XX8_INTF_AUX_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_SHIFT 3
+#define CS42XX8_INTF_DAC_DIF_WIDTH 3
+#define CS42XX8_INTF_DAC_DIF_MASK (((1 << CS42XX8_INTF_DAC_DIF_WIDTH) - 1) << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_LEFTJ (0 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_I2S (1 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_RIGHTJ (2 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_ONELINE_20 (4 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_ONELINE_24 (5 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_TDM (6 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_SHIFT 0
+#define CS42XX8_INTF_ADC_DIF_WIDTH 3
+#define CS42XX8_INTF_ADC_DIF_MASK (((1 << CS42XX8_INTF_ADC_DIF_WIDTH) - 1) << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_LEFTJ (0 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_I2S (1 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_RIGHTJ (2 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_ONELINE_20 (4 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_ONELINE_24 (5 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_TDM (6 << CS42XX8_INTF_ADC_DIF_SHIFT)
+
+/* ADC Control & DAC De-Emphasis (Address 05h) */
+#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT 7
+#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_MASK (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT)
+#define CS42XX8_ADCCTL_ADC_HPF_FREEZE (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT)
+#define CS42XX8_ADCCTL_DAC_DEM_SHIFT 5
+#define CS42XX8_ADCCTL_DAC_DEM_MASK (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT)
+#define CS42XX8_ADCCTL_DAC_DEM (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT)
+#define CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT 4
+#define CS42XX8_ADCCTL_ADC1_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT)
+#define CS42XX8_ADCCTL_ADC1_SINGLE (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT)
+#define CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT 3
+#define CS42XX8_ADCCTL_ADC2_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT)
+#define CS42XX8_ADCCTL_ADC2_SINGLE (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT)
+#define CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT 2
+#define CS42XX8_ADCCTL_ADC3_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT)
+#define CS42XX8_ADCCTL_ADC3_SINGLE (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT)
+#define CS42XX8_ADCCTL_AIN5_MUX_SHIFT 1
+#define CS42XX8_ADCCTL_AIN5_MUX_MASK (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT)
+#define CS42XX8_ADCCTL_AIN5_MUX (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT)
+#define CS42XX8_ADCCTL_AIN6_MUX_SHIFT 0
+#define CS42XX8_ADCCTL_AIN6_MUX_MASK (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT)
+#define CS42XX8_ADCCTL_AIN6_MUX (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT)
+
+/* Transition Control (Address 06h) */
+#define CS42XX8_TXCTL_DAC_SNGVOL_SHIFT 7
+#define CS42XX8_TXCTL_DAC_SNGVOL_MASK (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT)
+#define CS42XX8_TXCTL_DAC_SNGVOL (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT)
+#define CS42XX8_TXCTL_DAC_SZC_SHIFT 5
+#define CS42XX8_TXCTL_DAC_SZC_WIDTH 2
+#define CS42XX8_TXCTL_DAC_SZC_MASK (((1 << CS42XX8_TXCTL_DAC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_DAC_SZC_SHIFT)
+#define CS42XX8_TXCTL_DAC_SZC_IC (0 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
+#define CS42XX8_TXCTL_DAC_SZC_ZC (1 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
+#define CS42XX8_TXCTL_DAC_SZC_SR (2 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
+#define CS42XX8_TXCTL_DAC_SZC_SRZC (3 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
+#define CS42XX8_TXCTL_AMUTE_SHIFT 4
+#define CS42XX8_TXCTL_AMUTE_MASK (1 << CS42XX8_TXCTL_AMUTE_SHIFT)
+#define CS42XX8_TXCTL_AMUTE (1 << CS42XX8_TXCTL_AMUTE_SHIFT)
+#define CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT 3
+#define CS42XX8_TXCTL_MUTE_ADC_SP_MASK (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT)
+#define CS42XX8_TXCTL_MUTE_ADC_SP (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT)
+#define CS42XX8_TXCTL_ADC_SNGVOL_SHIFT 2
+#define CS42XX8_TXCTL_ADC_SNGVOL_MASK (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT)
+#define CS42XX8_TXCTL_ADC_SNGVOL (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT)
+#define CS42XX8_TXCTL_ADC_SZC_SHIFT 0
+#define CS42XX8_TXCTL_ADC_SZC_MASK (((1 << CS42XX8_TXCTL_ADC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_ADC_SZC_SHIFT)
+#define CS42XX8_TXCTL_ADC_SZC_IC (0 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
+#define CS42XX8_TXCTL_ADC_SZC_ZC (1 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
+#define CS42XX8_TXCTL_ADC_SZC_SR (2 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
+#define CS42XX8_TXCTL_ADC_SZC_SRZC (3 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
+
+/* DAC Channel Mute (Address 07h) */
+#define CS42XX8_DACMUTE_AOUT(n) (0x1 << n)
+#define CS42XX8_DACMUTE_ALL 0xff
+
+/* Status Control (Address 18h)*/
+#define CS42XX8_STATUSCTL_INI_SHIFT 2
+#define CS42XX8_STATUSCTL_INI_WIDTH 2
+#define CS42XX8_STATUSCTL_INI_MASK (((1 << CS42XX8_STATUSCTL_INI_WIDTH) - 1) << CS42XX8_STATUSCTL_INI_SHIFT)
+#define CS42XX8_STATUSCTL_INT_ACTIVE_HIGH (0 << CS42XX8_STATUSCTL_INI_SHIFT)
+#define CS42XX8_STATUSCTL_INT_ACTIVE_LOW (1 << CS42XX8_STATUSCTL_INI_SHIFT)
+#define CS42XX8_STATUSCTL_INT_OPEN_DRAIN (2 << CS42XX8_STATUSCTL_INI_SHIFT)
+
+/* Status (Address 19h)*/
+#define CS42XX8_STATUS_DAC_CLK_ERR_SHIFT 4
+#define CS42XX8_STATUS_DAC_CLK_ERR_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_SHIFT)
+#define CS42XX8_STATUS_ADC_CLK_ERR_SHIFT 3
+#define CS42XX8_STATUS_ADC_CLK_ERR_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_SHIFT)
+#define CS42XX8_STATUS_ADC3_OVFL_SHIFT 2
+#define CS42XX8_STATUS_ADC3_OVFL_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_SHIFT)
+#define CS42XX8_STATUS_ADC2_OVFL_SHIFT 1
+#define CS42XX8_STATUS_ADC2_OVFL_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_SHIFT)
+#define CS42XX8_STATUS_ADC1_OVFL_SHIFT 0
+#define CS42XX8_STATUS_ADC1_OVFL_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_SHIFT)
+
+/* Status Mask (Address 1Ah) */
+#define CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT 4
+#define CS42XX8_STATUS_DAC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT)
+#define CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT 3
+#define CS42XX8_STATUS_ADC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT)
+#define CS42XX8_STATUS_ADC3_OVFL_M_SHIFT 2
+#define CS42XX8_STATUS_ADC3_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_M_SHIFT)
+#define CS42XX8_STATUS_ADC2_OVFL_M_SHIFT 1
+#define CS42XX8_STATUS_ADC2_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_M_SHIFT)
+#define CS42XX8_STATUS_ADC1_OVFL_M_SHIFT 0
+#define CS42XX8_STATUS_ADC1_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_M_SHIFT)
+
+/* MUTEC Pin Control (Address 1Bh) */
+#define CS42XX8_MUTEC_MCPOLARITY_SHIFT 1
+#define CS42XX8_MUTEC_MCPOLARITY_MASK (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
+#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_LOW (0 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
+#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_HIGH (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
+#define CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT 0
+#define CS42XX8_MUTEC_MUTEC_ACTIVE_MASK (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT)
+#define CS42XX8_MUTEC_MUTEC_ACTIVE (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT)
+#endif /* _CS42XX8_H */
diff --git a/kernel/sound/soc/codecs/cx20442.c b/kernel/sound/soc/codecs/cx20442.c
new file mode 100644
index 000000000..0f334bc1b
--- /dev/null
+++ b/kernel/sound/soc/codecs/cx20442.c
@@ -0,0 +1,442 @@
+/*
+ * cx20442.c -- CX20442 ALSA Soc Audio driver
+ *
+ * Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * 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>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "cx20442.h"
+
+
+struct cx20442_priv {
+ void *control_data;
+ struct regulator *por;
+};
+
+#define CX20442_PM 0x0
+
+#define CX20442_TELIN 0
+#define CX20442_TELOUT 1
+#define CX20442_MIC 2
+#define CX20442_SPKOUT 3
+#define CX20442_AGC 4
+
+static const struct snd_soc_dapm_widget cx20442_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("TELOUT"),
+ SND_SOC_DAPM_OUTPUT("SPKOUT"),
+ SND_SOC_DAPM_OUTPUT("AGCOUT"),
+
+ SND_SOC_DAPM_MIXER("SPKOUT Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("TELOUT Amp", CX20442_PM, CX20442_TELOUT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPKOUT Amp", CX20442_PM, CX20442_SPKOUT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPKOUT AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MICBIAS("TELIN Bias", CX20442_PM, CX20442_TELIN, 0),
+ SND_SOC_DAPM_MICBIAS("MIC Bias", CX20442_PM, CX20442_MIC, 0),
+
+ SND_SOC_DAPM_PGA("MIC AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("TELIN"),
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_INPUT("AGCIN"),
+};
+
+static const struct snd_soc_dapm_route cx20442_audio_map[] = {
+ {"TELOUT", NULL, "TELOUT Amp"},
+
+ {"SPKOUT", NULL, "SPKOUT Mixer"},
+ {"SPKOUT Mixer", NULL, "SPKOUT Amp"},
+
+ {"TELOUT Amp", NULL, "DAC"},
+ {"SPKOUT Amp", NULL, "DAC"},
+
+ {"SPKOUT Mixer", NULL, "SPKOUT AGC"},
+ {"SPKOUT AGC", NULL, "AGCIN"},
+
+ {"AGCOUT", NULL, "MIC AGC"},
+ {"MIC AGC", NULL, "MIC"},
+
+ {"MIC Bias", NULL, "MIC"},
+ {"Input Mixer", NULL, "MIC Bias"},
+
+ {"TELIN Bias", NULL, "TELIN"},
+ {"Input Mixer", NULL, "TELIN Bias"},
+
+ {"ADC", NULL, "Input Mixer"},
+};
+
+static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 *reg_cache = codec->reg_cache;
+
+ if (reg >= codec->driver->reg_cache_size)
+ return -EINVAL;
+
+ return reg_cache[reg];
+}
+
+enum v253_vls {
+ V253_VLS_NONE = 0,
+ V253_VLS_T,
+ V253_VLS_L,
+ V253_VLS_LT,
+ V253_VLS_S,
+ V253_VLS_ST,
+ V253_VLS_M,
+ V253_VLS_MST,
+ V253_VLS_S1,
+ V253_VLS_S1T,
+ V253_VLS_MS1T,
+ V253_VLS_M1,
+ V253_VLS_M1ST,
+ V253_VLS_M1S1T,
+ V253_VLS_H,
+ V253_VLS_HT,
+ V253_VLS_MS,
+ V253_VLS_MS1,
+ V253_VLS_M1S,
+ V253_VLS_M1S1,
+ V253_VLS_TEST,
+};
+
+static int cx20442_pm_to_v253_vls(u8 value)
+{
+ switch (value & ~(1 << CX20442_AGC)) {
+ case 0:
+ return V253_VLS_T;
+ case (1 << CX20442_SPKOUT):
+ case (1 << CX20442_MIC):
+ case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC):
+ return V253_VLS_M1S1;
+ case (1 << CX20442_TELOUT):
+ case (1 << CX20442_TELIN):
+ case (1 << CX20442_TELOUT) | (1 << CX20442_TELIN):
+ return V253_VLS_L;
+ case (1 << CX20442_TELOUT) | (1 << CX20442_MIC):
+ return V253_VLS_NONE;
+ }
+ return -EINVAL;
+}
+static int cx20442_pm_to_v253_vsp(u8 value)
+{
+ switch (value & ~(1 << CX20442_AGC)) {
+ case (1 << CX20442_SPKOUT):
+ case (1 << CX20442_MIC):
+ case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC):
+ return (bool)(value & (1 << CX20442_AGC));
+ }
+ return (value & (1 << CX20442_AGC)) ? -EINVAL : 0;
+}
+
+static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
+ u8 *reg_cache = codec->reg_cache;
+ int vls, vsp, old, len;
+ char buf[18];
+
+ if (reg >= codec->driver->reg_cache_size)
+ return -EINVAL;
+
+ /* hw_write and control_data 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)
+ return -EIO;
+
+ old = reg_cache[reg];
+ reg_cache[reg] = value;
+
+ vls = cx20442_pm_to_v253_vls(value);
+ if (vls < 0)
+ return vls;
+
+ vsp = cx20442_pm_to_v253_vsp(value);
+ if (vsp < 0)
+ return vsp;
+
+ if ((vls == V253_VLS_T) ||
+ (vls == cx20442_pm_to_v253_vls(old))) {
+ if (vsp == cx20442_pm_to_v253_vsp(old))
+ return 0;
+ len = snprintf(buf, ARRAY_SIZE(buf), "at+vsp=%d\r", vsp);
+ } else if (vsp == cx20442_pm_to_v253_vsp(old))
+ len = snprintf(buf, ARRAY_SIZE(buf), "at+vls=%d\r", vls);
+ else
+ len = snprintf(buf, ARRAY_SIZE(buf),
+ "at+vls=%d;+vsp=%d\r", vls, vsp);
+
+ if (unlikely(len > (ARRAY_SIZE(buf) - 1)))
+ return -ENOMEM;
+
+ dev_dbg(codec->dev, "%s: %s\n", __func__, buf);
+ if (codec->hw_write(cx20442->control_data, buf, len) != len)
+ return -EIO;
+
+ return 0;
+}
+
+
+/*
+ * Line discpline related code
+ *
+ * Any of the callback functions below can be used in two ways:
+ * 1) registerd by a machine driver as one of line discipline operations,
+ * 2) called from a machine's provided line discipline callback function
+ * in case when extra machine specific code must be run as well.
+ */
+
+/* Modem init: echo off, digital speaker off, quiet off, voice mode */
+static const char *v253_init = "ate0m0q0+fclass=8\r";
+
+/* Line discipline .open() */
+static int v253_open(struct tty_struct *tty)
+{
+ int ret, len = strlen(v253_init);
+
+ /* Doesn't make sense without write callback */
+ if (!tty->ops->write)
+ return -EINVAL;
+
+ /* Won't work if no codec pointer has been passed by a card driver */
+ if (!tty->disc_data)
+ return -ENODEV;
+
+ if (tty->ops->write(tty, v253_init, len) != len) {
+ ret = -EIO;
+ goto err;
+ }
+ /* Actual setup will be performed after the modem responds. */
+ return 0;
+err:
+ tty->disc_data = NULL;
+ return ret;
+}
+
+/* Line discipline .close() */
+static void v253_close(struct tty_struct *tty)
+{
+ struct snd_soc_codec *codec = tty->disc_data;
+ struct cx20442_priv *cx20442;
+
+ tty->disc_data = NULL;
+
+ if (!codec)
+ return;
+
+ cx20442 = snd_soc_codec_get_drvdata(codec);
+
+ /* Prevent the codec driver from further accessing the modem */
+ codec->hw_write = NULL;
+ cx20442->control_data = NULL;
+ codec->component.card->pop_time = 0;
+}
+
+/* Line discipline .hangup() */
+static int 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)
+{
+ struct snd_soc_codec *codec = tty->disc_data;
+ struct cx20442_priv *cx20442;
+
+ if (!codec)
+ return;
+
+ cx20442 = snd_soc_codec_get_drvdata(codec);
+
+ if (!cx20442->control_data) {
+ /* 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;
+ }
+}
+
+/* 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);
+
+
+/*
+ * Codec DAI
+ */
+
+static struct snd_soc_dai_driver cx20442_dai = {
+ .name = "cx20442-voice",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static int cx20442_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
+ int err = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (codec->dapm.bias_level != SND_SOC_BIAS_STANDBY)
+ break;
+ if (IS_ERR(cx20442->por))
+ err = PTR_ERR(cx20442->por);
+ else
+ err = regulator_enable(cx20442->por);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level != SND_SOC_BIAS_PREPARE)
+ break;
+ if (IS_ERR(cx20442->por))
+ err = PTR_ERR(cx20442->por);
+ else
+ err = regulator_disable(cx20442->por);
+ break;
+ default:
+ break;
+ }
+ if (!err)
+ codec->dapm.bias_level = level;
+
+ return err;
+}
+
+static int cx20442_codec_probe(struct snd_soc_codec *codec)
+{
+ struct cx20442_priv *cx20442;
+
+ cx20442 = kzalloc(sizeof(struct cx20442_priv), GFP_KERNEL);
+ 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;
+
+ snd_soc_codec_set_drvdata(codec, cx20442);
+ codec->hw_write = NULL;
+ codec->component.card->pop_time = 0;
+
+ return 0;
+}
+
+/* power down chip */
+static int cx20442_codec_remove(struct snd_soc_codec *codec)
+{
+ struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
+
+ if (cx20442->control_data) {
+ struct tty_struct *tty = cx20442->control_data;
+ tty_hangup(tty);
+ }
+
+ if (!IS_ERR(cx20442->por)) {
+ /* should be already in STANDBY, hence disabled */
+ regulator_put(cx20442->por);
+ }
+
+ snd_soc_codec_set_drvdata(codec, 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 int cx20442_platform_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &cx20442_codec_dev, &cx20442_dai, 1);
+}
+
+static int cx20442_platform_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver cx20442_platform_driver = {
+ .driver = {
+ .name = "cx20442-codec",
+ },
+ .probe = cx20442_platform_probe,
+ .remove = cx20442_platform_remove,
+};
+
+module_platform_driver(cx20442_platform_driver);
+
+MODULE_DESCRIPTION("ASoC CX20442-11 voice modem codec driver");
+MODULE_AUTHOR("Janusz Krzysztofik");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:cx20442-codec");
diff --git a/kernel/sound/soc/codecs/cx20442.h b/kernel/sound/soc/codecs/cx20442.h
new file mode 100644
index 000000000..c7a7c79ef
--- /dev/null
+++ b/kernel/sound/soc/codecs/cx20442.h
@@ -0,0 +1,18 @@
+/*
+ * 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
+#define _CX20442_CODEC_H
+
+extern struct tty_ldisc_ops v253_ops;
+
+#endif
diff --git a/kernel/sound/soc/codecs/da7210.c b/kernel/sound/soc/codecs/da7210.c
new file mode 100644
index 000000000..21810e5f3
--- /dev/null
+++ b/kernel/sound/soc/codecs/da7210.c
@@ -0,0 +1,1384 @@
+/*
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+/* DA7210 register space */
+#define DA7210_PAGE_CONTROL 0x00
+#define DA7210_CONTROL 0x01
+#define DA7210_STATUS 0x02
+#define DA7210_STARTUP1 0x03
+#define DA7210_STARTUP2 0x04
+#define DA7210_STARTUP3 0x05
+#define DA7210_MIC_L 0x07
+#define DA7210_MIC_R 0x08
+#define DA7210_AUX1_L 0x09
+#define DA7210_AUX1_R 0x0A
+#define DA7210_AUX2 0x0B
+#define DA7210_IN_GAIN 0x0C
+#define DA7210_INMIX_L 0x0D
+#define DA7210_INMIX_R 0x0E
+#define DA7210_ADC_HPF 0x0F
+#define DA7210_ADC 0x10
+#define DA7210_ADC_EQ1_2 0X11
+#define DA7210_ADC_EQ3_4 0x12
+#define DA7210_ADC_EQ5 0x13
+#define DA7210_DAC_HPF 0x14
+#define DA7210_DAC_L 0x15
+#define DA7210_DAC_R 0x16
+#define DA7210_DAC_SEL 0x17
+#define DA7210_SOFTMUTE 0x18
+#define DA7210_DAC_EQ1_2 0x19
+#define DA7210_DAC_EQ3_4 0x1A
+#define DA7210_DAC_EQ5 0x1B
+#define DA7210_OUTMIX_L 0x1C
+#define DA7210_OUTMIX_R 0x1D
+#define DA7210_OUT1_L 0x1E
+#define DA7210_OUT1_R 0x1F
+#define DA7210_OUT2 0x20
+#define DA7210_HP_L_VOL 0x21
+#define DA7210_HP_R_VOL 0x22
+#define DA7210_HP_CFG 0x23
+#define DA7210_ZERO_CROSS 0x24
+#define DA7210_DAI_SRC_SEL 0x25
+#define DA7210_DAI_CFG1 0x26
+#define DA7210_DAI_CFG3 0x28
+#define DA7210_PLL_DIV1 0x29
+#define DA7210_PLL_DIV2 0x2A
+#define DA7210_PLL_DIV3 0x2B
+#define DA7210_PLL 0x2C
+#define DA7210_ALC_MAX 0x83
+#define DA7210_ALC_MIN 0x84
+#define DA7210_ALC_NOIS 0x85
+#define DA7210_ALC_ATT 0x86
+#define DA7210_ALC_REL 0x87
+#define DA7210_ALC_DEL 0x88
+#define DA7210_A_HID_UNLOCK 0x8A
+#define DA7210_A_TEST_UNLOCK 0x8B
+#define DA7210_A_PLL1 0x90
+#define DA7210_A_CP_MODE 0xA7
+
+/* STARTUP1 bit fields */
+#define DA7210_SC_MST_EN (1 << 0)
+
+/* MIC_L bit fields */
+#define DA7210_MICBIAS_EN (1 << 6)
+#define DA7210_MIC_L_EN (1 << 7)
+
+/* MIC_R bit fields */
+#define DA7210_MIC_R_EN (1 << 7)
+
+/* INMIX_L bit fields */
+#define DA7210_IN_L_EN (1 << 7)
+
+/* INMIX_R bit fields */
+#define DA7210_IN_R_EN (1 << 7)
+
+/* ADC bit fields */
+#define DA7210_ADC_ALC_EN (1 << 0)
+#define DA7210_ADC_L_EN (1 << 3)
+#define DA7210_ADC_R_EN (1 << 7)
+
+/* DAC/ADC HPF fields */
+#define DA7210_VOICE_F0_MASK (0x7 << 4)
+#define DA7210_VOICE_F0_25 (1 << 4)
+#define DA7210_VOICE_EN (1 << 7)
+
+/* DAC_SEL bit fields */
+#define DA7210_DAC_L_SRC_DAI_L (4 << 0)
+#define DA7210_DAC_L_EN (1 << 3)
+#define DA7210_DAC_R_SRC_DAI_R (5 << 4)
+#define DA7210_DAC_R_EN (1 << 7)
+
+/* OUTMIX_L bit fields */
+#define DA7210_OUT_L_EN (1 << 7)
+
+/* OUTMIX_R bit fields */
+#define DA7210_OUT_R_EN (1 << 7)
+
+/* HP_CFG bit fields */
+#define DA7210_HP_2CAP_MODE (1 << 1)
+#define DA7210_HP_SENSE_EN (1 << 2)
+#define DA7210_HP_L_EN (1 << 3)
+#define DA7210_HP_MODE (1 << 6)
+#define DA7210_HP_R_EN (1 << 7)
+
+/* DAI_SRC_SEL bit fields */
+#define DA7210_DAI_OUT_L_SRC (6 << 0)
+#define DA7210_DAI_OUT_R_SRC (7 << 4)
+
+/* DAI_CFG1 bit fields */
+#define DA7210_DAI_WORD_S16_LE (0 << 0)
+#define DA7210_DAI_WORD_S20_3LE (1 << 0)
+#define DA7210_DAI_WORD_S24_LE (2 << 0)
+#define DA7210_DAI_WORD_S32_LE (3 << 0)
+#define DA7210_DAI_FLEN_64BIT (1 << 2)
+#define DA7210_DAI_MODE_SLAVE (0 << 7)
+#define DA7210_DAI_MODE_MASTER (1 << 7)
+
+/* DAI_CFG3 bit fields */
+#define DA7210_DAI_FORMAT_I2SMODE (0 << 0)
+#define DA7210_DAI_FORMAT_LEFT_J (1 << 0)
+#define DA7210_DAI_FORMAT_RIGHT_J (2 << 0)
+#define DA7210_DAI_OE (1 << 3)
+#define DA7210_DAI_EN (1 << 7)
+
+/*PLL_DIV3 bit fields */
+#define DA7210_PLL_DIV_L_MASK (0xF << 0)
+#define DA7210_MCLK_RANGE_10_20_MHZ (1 << 4)
+#define DA7210_PLL_BYP (1 << 6)
+
+/* PLL bit fields */
+#define DA7210_PLL_FS_MASK (0xF << 0)
+#define DA7210_PLL_FS_8000 (0x1 << 0)
+#define DA7210_PLL_FS_11025 (0x2 << 0)
+#define DA7210_PLL_FS_12000 (0x3 << 0)
+#define DA7210_PLL_FS_16000 (0x5 << 0)
+#define DA7210_PLL_FS_22050 (0x6 << 0)
+#define DA7210_PLL_FS_24000 (0x7 << 0)
+#define DA7210_PLL_FS_32000 (0x9 << 0)
+#define DA7210_PLL_FS_44100 (0xA << 0)
+#define DA7210_PLL_FS_48000 (0xB << 0)
+#define DA7210_PLL_FS_88200 (0xE << 0)
+#define DA7210_PLL_FS_96000 (0xF << 0)
+#define DA7210_MCLK_DET_EN (0x1 << 5)
+#define DA7210_MCLK_SRM_EN (0x1 << 6)
+#define DA7210_PLL_EN (0x1 << 7)
+
+/* SOFTMUTE bit fields */
+#define DA7210_RAMP_EN (1 << 6)
+
+/* CONTROL bit fields */
+#define DA7210_REG_EN (1 << 0)
+#define DA7210_BIAS_EN (1 << 2)
+#define DA7210_NOISE_SUP_EN (1 << 3)
+
+/* IN_GAIN bit fields */
+#define DA7210_INPGA_L_VOL (0x0F << 0)
+#define DA7210_INPGA_R_VOL (0xF0 << 0)
+
+/* ZERO_CROSS bit fields */
+#define DA7210_AUX1_L_ZC (1 << 0)
+#define DA7210_AUX1_R_ZC (1 << 1)
+#define DA7210_HP_L_ZC (1 << 6)
+#define DA7210_HP_R_ZC (1 << 7)
+
+/* AUX1_L bit fields */
+#define DA7210_AUX1_L_VOL (0x3F << 0)
+#define DA7210_AUX1_L_EN (1 << 7)
+
+/* AUX1_R bit fields */
+#define DA7210_AUX1_R_VOL (0x3F << 0)
+#define DA7210_AUX1_R_EN (1 << 7)
+
+/* AUX2 bit fields */
+#define DA7210_AUX2_EN (1 << 3)
+
+/* Minimum INPGA and AUX1 volume to enable noise suppression */
+#define DA7210_INPGA_MIN_VOL_NS 0x0A /* 10.5dB */
+#define DA7210_AUX1_MIN_VOL_NS 0x35 /* 6dB */
+
+/* OUT1_L bit fields */
+#define DA7210_OUT1_L_EN (1 << 7)
+
+/* OUT1_R bit fields */
+#define DA7210_OUT1_R_EN (1 << 7)
+
+/* OUT2 bit fields */
+#define DA7210_OUT2_OUTMIX_R (1 << 5)
+#define DA7210_OUT2_OUTMIX_L (1 << 6)
+#define DA7210_OUT2_EN (1 << 7)
+
+struct pll_div {
+ int fref;
+ int fout;
+ u8 div1;
+ u8 div2;
+ u8 div3;
+ u8 mode; /* 0 = slave, 1 = master */
+};
+
+/* PLL dividers table */
+static const struct pll_div da7210_pll_div[] = {
+ /* for MASTER mode, fs = 44.1Khz */
+ { 12000000, 2822400, 0xE8, 0x6C, 0x2, 1}, /* MCLK=12Mhz */
+ { 13000000, 2822400, 0xDF, 0x28, 0xC, 1}, /* MCLK=13Mhz */
+ { 13500000, 2822400, 0xDB, 0x0A, 0xD, 1}, /* MCLK=13.5Mhz */
+ { 14400000, 2822400, 0xD4, 0x5A, 0x2, 1}, /* MCLK=14.4Mhz */
+ { 19200000, 2822400, 0xBB, 0x43, 0x9, 1}, /* MCLK=19.2Mhz */
+ { 19680000, 2822400, 0xB9, 0x6D, 0xA, 1}, /* MCLK=19.68Mhz */
+ { 19800000, 2822400, 0xB8, 0xFB, 0xB, 1}, /* MCLK=19.8Mhz */
+ /* for MASTER mode, fs = 48Khz */
+ { 12000000, 3072000, 0xF3, 0x12, 0x7, 1}, /* MCLK=12Mhz */
+ { 13000000, 3072000, 0xE8, 0xFD, 0x5, 1}, /* MCLK=13Mhz */
+ { 13500000, 3072000, 0xE4, 0x82, 0x3, 1}, /* MCLK=13.5Mhz */
+ { 14400000, 3072000, 0xDD, 0x3A, 0x0, 1}, /* MCLK=14.4Mhz */
+ { 19200000, 3072000, 0xC1, 0xEB, 0x8, 1}, /* MCLK=19.2Mhz */
+ { 19680000, 3072000, 0xBF, 0xEC, 0x0, 1}, /* MCLK=19.68Mhz */
+ { 19800000, 3072000, 0xBF, 0x70, 0x0, 1}, /* MCLK=19.8Mhz */
+ /* for SLAVE mode with SRM */
+ { 12000000, 2822400, 0xED, 0xBF, 0x5, 0}, /* MCLK=12Mhz */
+ { 13000000, 2822400, 0xE4, 0x13, 0x0, 0}, /* MCLK=13Mhz */
+ { 13500000, 2822400, 0xDF, 0xC6, 0x8, 0}, /* MCLK=13.5Mhz */
+ { 14400000, 2822400, 0xD8, 0xCA, 0x1, 0}, /* MCLK=14.4Mhz */
+ { 19200000, 2822400, 0xBE, 0x97, 0x9, 0}, /* MCLK=19.2Mhz */
+ { 19680000, 2822400, 0xBC, 0xAC, 0xD, 0}, /* MCLK=19.68Mhz */
+ { 19800000, 2822400, 0xBC, 0x35, 0xE, 0}, /* MCLK=19.8Mhz */
+};
+
+enum clk_src {
+ DA7210_CLKSRC_MCLK
+};
+
+#define DA7210_VERSION "0.0.1"
+
+/*
+ * Playback Volume
+ *
+ * max : 0x3F (+15.0 dB)
+ * (1.5 dB step)
+ * min : 0x11 (-54.0 dB)
+ * mute : 0x10
+ * reserved : 0x00 - 0x0F
+ *
+ * Reserved area are considered as "mute".
+ */
+static const unsigned int hp_out_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* -54 dB to +15 dB */
+ 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0),
+};
+
+static const unsigned int lineout_vol_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 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),
+ 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),
+ 0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* -48dB to 21dB */
+ 0x11, 0x3f, TLV_DB_SCALE_ITEM(-4800, 150, 0)
+};
+
+static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adc_eq_master_gain_tlv, -1800, 600, 1);
+static const DECLARE_TLV_DB_SCALE(dac_gain_tlv, -7725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(aux2_vol_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(inpga_gain_tlv, -450, 150, 0);
+
+/* ADC and DAC high pass filter f0 value */
+static const char * const da7210_hpf_cutoff_txt[] = {
+ "Fs/8192*pi", "Fs/4096*pi", "Fs/2048*pi", "Fs/1024*pi"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7210_dac_hpf_cutoff,
+ DA7210_DAC_HPF, 0, da7210_hpf_cutoff_txt);
+
+static SOC_ENUM_SINGLE_DECL(da7210_adc_hpf_cutoff,
+ DA7210_ADC_HPF, 0, da7210_hpf_cutoff_txt);
+
+/* ADC and DAC voice (8kHz) high pass cutoff value */
+static const char * const da7210_vf_cutoff_txt[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7210_dac_vf_cutoff,
+ DA7210_DAC_HPF, 4, da7210_vf_cutoff_txt);
+
+static SOC_ENUM_SINGLE_DECL(da7210_adc_vf_cutoff,
+ DA7210_ADC_HPF, 4, da7210_vf_cutoff_txt);
+
+static const char *da7210_hp_mode_txt[] = {
+ "Class H", "Class G"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7210_hp_mode_sel,
+ DA7210_HP_CFG, 0, da7210_hp_mode_txt);
+
+/* ALC can be enabled only if noise suppression is disabled */
+static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(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,
+ "Disable noise suppression to enable ALC\n");
+ return -EINVAL;
+ }
+ }
+ /* If all conditions are met or we are actually disabling ALC */
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+/* Noise suppression can be enabled only if following conditions are met
+ * ALC disabled
+ * ZC enabled for HP and AUX1 PGA
+ * INPGA_L_VOL and INPGA_R_VOL >= 10.5 dB
+ * AUX1_L_VOL and AUX1_R_VOL >= 6 dB
+ */
+static int da7210_put_noise_sup_sw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(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)
+ goto err;
+
+ /* Check ZC for HP and AUX1 PGA */
+ if ((snd_soc_read(codec, 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);
+ 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) <
+ DA7210_AUX1_MIN_VOL_NS) ||
+ ((snd_soc_read(codec, DA7210_AUX1_R) & DA7210_AUX1_R_VOL) <
+ DA7210_AUX1_MIN_VOL_NS))
+ goto err;
+ }
+ /* If all conditions are met or we are actually disabling Noise sup */
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+
+err:
+ return -EINVAL;
+}
+
+static const struct snd_kcontrol_new da7210_snd_controls[] = {
+
+ SOC_DOUBLE_R_TLV("HeadPhone Playback Volume",
+ DA7210_HP_L_VOL, DA7210_HP_R_VOL,
+ 0, 0x3F, 0, hp_out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume",
+ DA7210_DAC_L, DA7210_DAC_R,
+ 0, 0x77, 1, dac_gain_tlv),
+ SOC_DOUBLE_R_TLV("Lineout Playback Volume",
+ DA7210_OUT1_L, DA7210_OUT1_R,
+ 0, 0x3f, 0, lineout_vol_tlv),
+ SOC_SINGLE_TLV("Mono Playback Volume", DA7210_OUT2, 0, 0x7, 0,
+ mono_vol_tlv),
+
+ SOC_DOUBLE_R_TLV("Mic Capture Volume",
+ DA7210_MIC_L, DA7210_MIC_R,
+ 0, 0x5, 0, mic_vol_tlv),
+ SOC_DOUBLE_R_TLV("Aux1 Capture Volume",
+ DA7210_AUX1_L, DA7210_AUX1_R,
+ 0, 0x3f, 0, aux1_vol_tlv),
+ SOC_SINGLE_TLV("Aux2 Capture Volume", DA7210_AUX2, 0, 0x3, 0,
+ aux2_vol_tlv),
+ SOC_DOUBLE_TLV("In PGA Capture Volume", DA7210_IN_GAIN, 0, 4, 0xF, 0,
+ inpga_gain_tlv),
+
+ /* DAC Equalizer controls */
+ SOC_SINGLE("DAC EQ Switch", DA7210_DAC_EQ5, 7, 1, 0),
+ SOC_SINGLE_TLV("DAC EQ1 Volume", DA7210_DAC_EQ1_2, 0, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ2 Volume", DA7210_DAC_EQ1_2, 4, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ3 Volume", DA7210_DAC_EQ3_4, 0, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ4 Volume", DA7210_DAC_EQ3_4, 4, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ5 Volume", DA7210_DAC_EQ5, 0, 0xf, 1,
+ eq_gain_tlv),
+
+ /* ADC Equalizer controls */
+ SOC_SINGLE("ADC EQ Switch", DA7210_ADC_EQ5, 7, 1, 0),
+ SOC_SINGLE_TLV("ADC EQ Master Volume", DA7210_ADC_EQ5, 4, 0x3,
+ 1, adc_eq_master_gain_tlv),
+ SOC_SINGLE_TLV("ADC EQ1 Volume", DA7210_ADC_EQ1_2, 0, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("ADC EQ2 Volume", DA7210_ADC_EQ1_2, 4, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("ADC EQ3 Volume", DA7210_ADC_EQ3_4, 0, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("ADC EQ4 Volume", DA7210_ADC_EQ3_4, 4, 0xf, 1,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("ADC EQ5 Volume", DA7210_ADC_EQ5, 0, 0xf, 1,
+ eq_gain_tlv),
+
+ SOC_SINGLE("DAC HPF Switch", DA7210_DAC_HPF, 3, 1, 0),
+ SOC_ENUM("DAC HPF Cutoff", da7210_dac_hpf_cutoff),
+ SOC_SINGLE("DAC Voice Mode Switch", DA7210_DAC_HPF, 7, 1, 0),
+ SOC_ENUM("DAC Voice Cutoff", da7210_dac_vf_cutoff),
+
+ SOC_SINGLE("ADC HPF Switch", DA7210_ADC_HPF, 3, 1, 0),
+ SOC_ENUM("ADC HPF Cutoff", da7210_adc_hpf_cutoff),
+ SOC_SINGLE("ADC Voice Mode Switch", DA7210_ADC_HPF, 7, 1, 0),
+ SOC_ENUM("ADC Voice Cutoff", da7210_adc_vf_cutoff),
+
+ /* Mute controls */
+ SOC_DOUBLE_R("Mic Capture Switch", DA7210_MIC_L, DA7210_MIC_R, 3, 1, 0),
+ SOC_SINGLE("Aux2 Capture Switch", DA7210_AUX2, 2, 1, 0),
+ SOC_DOUBLE("ADC Capture Switch", DA7210_ADC, 2, 6, 1, 0),
+ SOC_SINGLE("Digital Soft Mute Switch", DA7210_SOFTMUTE, 7, 1, 0),
+ SOC_SINGLE("Digital Soft Mute Rate", DA7210_SOFTMUTE, 0, 0x7, 0),
+
+ /* Zero cross controls */
+ SOC_DOUBLE("Aux1 ZC Switch", DA7210_ZERO_CROSS, 0, 1, 1, 0),
+ SOC_DOUBLE("In PGA ZC Switch", DA7210_ZERO_CROSS, 2, 3, 1, 0),
+ SOC_DOUBLE("Lineout ZC Switch", DA7210_ZERO_CROSS, 4, 5, 1, 0),
+ SOC_DOUBLE("Headphone ZC Switch", DA7210_ZERO_CROSS, 6, 7, 1, 0),
+
+ SOC_ENUM("Headphone Class", da7210_hp_mode_sel),
+
+ /* ALC controls */
+ SOC_SINGLE_EXT("ALC Enable Switch", DA7210_ADC, 0, 1, 0,
+ snd_soc_get_volsw, da7210_put_alc_sw),
+ SOC_SINGLE("ALC Capture Max Volume", DA7210_ALC_MAX, 0, 0x3F, 0),
+ SOC_SINGLE("ALC Capture Min Volume", DA7210_ALC_MIN, 0, 0x3F, 0),
+ SOC_SINGLE("ALC Capture Noise Volume", DA7210_ALC_NOIS, 0, 0x3F, 0),
+ SOC_SINGLE("ALC Capture Attack Rate", DA7210_ALC_ATT, 0, 0xFF, 0),
+ SOC_SINGLE("ALC Capture Release Rate", DA7210_ALC_REL, 0, 0xFF, 0),
+ SOC_SINGLE("ALC Capture Release Delay", DA7210_ALC_DEL, 0, 0xFF, 0),
+
+ SOC_SINGLE_EXT("Noise Suppression Enable Switch", DA7210_CONTROL, 3, 1,
+ 0, snd_soc_get_volsw, da7210_put_noise_sup_sw),
+};
+
+/*
+ * DAPM Controls
+ *
+ * Current DAPM implementation covers almost all codec components e.g. IOs,
+ * mixers, PGAs,ADC and DAC.
+ */
+/* In Mixer Left */
+static const struct snd_kcontrol_new da7210_dapm_inmixl_controls[] = {
+ SOC_DAPM_SINGLE("Mic Left Switch", DA7210_INMIX_L, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mic Right Switch", DA7210_INMIX_L, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux1 Left Switch", DA7210_INMIX_L, 2, 1, 0),
+ SOC_DAPM_SINGLE("Aux2 Switch", DA7210_INMIX_L, 3, 1, 0),
+ SOC_DAPM_SINGLE("Outmix Left Switch", DA7210_INMIX_L, 4, 1, 0),
+};
+
+/* In Mixer Right */
+static const struct snd_kcontrol_new da7210_dapm_inmixr_controls[] = {
+ SOC_DAPM_SINGLE("Mic Right Switch", DA7210_INMIX_R, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mic Left Switch", DA7210_INMIX_R, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux1 Right Switch", DA7210_INMIX_R, 2, 1, 0),
+ SOC_DAPM_SINGLE("Aux2 Switch", DA7210_INMIX_R, 3, 1, 0),
+ SOC_DAPM_SINGLE("Outmix Right Switch", DA7210_INMIX_R, 4, 1, 0),
+};
+
+/* Out Mixer Left */
+static const struct snd_kcontrol_new da7210_dapm_outmixl_controls[] = {
+ SOC_DAPM_SINGLE("Aux1 Left Switch", DA7210_OUTMIX_L, 0, 1, 0),
+ SOC_DAPM_SINGLE("Aux2 Switch", DA7210_OUTMIX_L, 1, 1, 0),
+ SOC_DAPM_SINGLE("INPGA Left Switch", DA7210_OUTMIX_L, 2, 1, 0),
+ SOC_DAPM_SINGLE("INPGA Right Switch", DA7210_OUTMIX_L, 3, 1, 0),
+ SOC_DAPM_SINGLE("DAC Left Switch", DA7210_OUTMIX_L, 4, 1, 0),
+};
+
+/* Out Mixer Right */
+static const struct snd_kcontrol_new da7210_dapm_outmixr_controls[] = {
+ SOC_DAPM_SINGLE("Aux1 Right Switch", DA7210_OUTMIX_R, 0, 1, 0),
+ SOC_DAPM_SINGLE("Aux2 Switch", DA7210_OUTMIX_R, 1, 1, 0),
+ SOC_DAPM_SINGLE("INPGA Left Switch", DA7210_OUTMIX_R, 2, 1, 0),
+ SOC_DAPM_SINGLE("INPGA Right Switch", DA7210_OUTMIX_R, 3, 1, 0),
+ SOC_DAPM_SINGLE("DAC Right Switch", DA7210_OUTMIX_R, 4, 1, 0),
+};
+
+/* Mono Mixer */
+static const struct snd_kcontrol_new da7210_dapm_monomix_controls[] = {
+ SOC_DAPM_SINGLE("INPGA Right Switch", DA7210_OUT2, 3, 1, 0),
+ SOC_DAPM_SINGLE("INPGA Left Switch", DA7210_OUT2, 4, 1, 0),
+ SOC_DAPM_SINGLE("Outmix Right Switch", DA7210_OUT2, 5, 1, 0),
+ SOC_DAPM_SINGLE("Outmix Left Switch", DA7210_OUT2, 6, 1, 0),
+};
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget da7210_dapm_widgets[] = {
+ /* Input Side */
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+ SND_SOC_DAPM_INPUT("AUX1L"),
+ SND_SOC_DAPM_INPUT("AUX1R"),
+ SND_SOC_DAPM_INPUT("AUX2"),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_PGA("Mic Left", DA7210_STARTUP3, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic Right", DA7210_STARTUP3, 1, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux1 Left", DA7210_STARTUP3, 2, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux1 Right", DA7210_STARTUP3, 3, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux2 Mono", DA7210_STARTUP3, 4, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("INPGA Left", DA7210_INMIX_L, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INPGA Right", DA7210_INMIX_R, 7, 0, NULL, 0),
+
+ /* MICBIAS */
+ SND_SOC_DAPM_SUPPLY("Mic Bias", DA7210_MIC_L, 6, 0, NULL, 0),
+
+ /* Input Mixers */
+ SND_SOC_DAPM_MIXER("In Mixer Left", SND_SOC_NOPM, 0, 0,
+ &da7210_dapm_inmixl_controls[0],
+ ARRAY_SIZE(da7210_dapm_inmixl_controls)),
+
+ SND_SOC_DAPM_MIXER("In Mixer Right", SND_SOC_NOPM, 0, 0,
+ &da7210_dapm_inmixr_controls[0],
+ ARRAY_SIZE(da7210_dapm_inmixr_controls)),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC Left", "Capture", DA7210_STARTUP3, 5, 1),
+ SND_SOC_DAPM_ADC("ADC Right", "Capture", DA7210_STARTUP3, 6, 1),
+
+ /* Output Side */
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC Left", "Playback", DA7210_STARTUP2, 5, 1),
+ SND_SOC_DAPM_DAC("DAC Right", "Playback", DA7210_STARTUP2, 6, 1),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Out Mixer Left", SND_SOC_NOPM, 0, 0,
+ &da7210_dapm_outmixl_controls[0],
+ ARRAY_SIZE(da7210_dapm_outmixl_controls)),
+
+ SND_SOC_DAPM_MIXER("Out Mixer Right", SND_SOC_NOPM, 0, 0,
+ &da7210_dapm_outmixr_controls[0],
+ ARRAY_SIZE(da7210_dapm_outmixr_controls)),
+
+ SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0,
+ &da7210_dapm_monomix_controls[0],
+ ARRAY_SIZE(da7210_dapm_monomix_controls)),
+
+ /* Output PGAs */
+ SND_SOC_DAPM_PGA("OUTPGA Left Enable", DA7210_OUTMIX_L, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OUTPGA Right Enable", DA7210_OUTMIX_R, 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Out1 Left", DA7210_STARTUP2, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Out1 Right", DA7210_STARTUP2, 1, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Out2 Mono", DA7210_STARTUP2, 2, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Left", DA7210_STARTUP2, 3, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Right", DA7210_STARTUP2, 4, 1, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("OUT1L"),
+ SND_SOC_DAPM_OUTPUT("OUT1R"),
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+};
+
+/* DAPM audio route definition */
+static const struct snd_soc_dapm_route da7210_audio_map[] = {
+ /* Dest Connecting Widget source */
+ /* Input path */
+ {"Mic Left", NULL, "MICL"},
+ {"Mic Right", NULL, "MICR"},
+ {"Aux1 Left", NULL, "AUX1L"},
+ {"Aux1 Right", NULL, "AUX1R"},
+ {"Aux2 Mono", NULL, "AUX2"},
+
+ {"In Mixer Left", "Mic Left Switch", "Mic Left"},
+ {"In Mixer Left", "Mic Right Switch", "Mic Right"},
+ {"In Mixer Left", "Aux1 Left Switch", "Aux1 Left"},
+ {"In Mixer Left", "Aux2 Switch", "Aux2 Mono"},
+ {"In Mixer Left", "Outmix Left Switch", "Out Mixer Left"},
+
+ {"In Mixer Right", "Mic Right Switch", "Mic Right"},
+ {"In Mixer Right", "Mic Left Switch", "Mic Left"},
+ {"In Mixer Right", "Aux1 Right Switch", "Aux1 Right"},
+ {"In Mixer Right", "Aux2 Switch", "Aux2 Mono"},
+ {"In Mixer Right", "Outmix Right Switch", "Out Mixer Right"},
+
+ {"INPGA Left", NULL, "In Mixer Left"},
+ {"ADC Left", NULL, "INPGA Left"},
+
+ {"INPGA Right", NULL, "In Mixer Right"},
+ {"ADC Right", NULL, "INPGA Right"},
+
+ /* Output path */
+ {"Out Mixer Left", "Aux1 Left Switch", "Aux1 Left"},
+ {"Out Mixer Left", "Aux2 Switch", "Aux2 Mono"},
+ {"Out Mixer Left", "INPGA Left Switch", "INPGA Left"},
+ {"Out Mixer Left", "INPGA Right Switch", "INPGA Right"},
+ {"Out Mixer Left", "DAC Left Switch", "DAC Left"},
+
+ {"Out Mixer Right", "Aux1 Right Switch", "Aux1 Right"},
+ {"Out Mixer Right", "Aux2 Switch", "Aux2 Mono"},
+ {"Out Mixer Right", "INPGA Right Switch", "INPGA Right"},
+ {"Out Mixer Right", "INPGA Left Switch", "INPGA Left"},
+ {"Out Mixer Right", "DAC Right Switch", "DAC Right"},
+
+ {"Mono Mixer", "INPGA Right Switch", "INPGA Right"},
+ {"Mono Mixer", "INPGA Left Switch", "INPGA Left"},
+ {"Mono Mixer", "Outmix Right Switch", "Out Mixer Right"},
+ {"Mono Mixer", "Outmix Left Switch", "Out Mixer Left"},
+
+ {"OUTPGA Left Enable", NULL, "Out Mixer Left"},
+ {"OUTPGA Right Enable", NULL, "Out Mixer Right"},
+
+ {"Out1 Left", NULL, "OUTPGA Left Enable"},
+ {"OUT1L", NULL, "Out1 Left"},
+
+ {"Out1 Right", NULL, "OUTPGA Right Enable"},
+ {"OUT1R", NULL, "Out1 Right"},
+
+ {"Headphone Left", NULL, "OUTPGA Left Enable"},
+ {"HPL", NULL, "Headphone Left"},
+
+ {"Headphone Right", NULL, "OUTPGA Right Enable"},
+ {"HPR", NULL, "Headphone Right"},
+
+ {"Out2 Mono", NULL, "Mono Mixer"},
+ {"OUT2", NULL, "Out2 Mono"},
+};
+
+/* Codec private data */
+struct da7210_priv {
+ struct regmap *regmap;
+ unsigned int mclk_rate;
+ int master;
+};
+
+static struct reg_default da7210_reg_defaults[] = {
+ { 0x00, 0x00 },
+ { 0x01, 0x11 },
+ { 0x03, 0x00 },
+ { 0x04, 0x00 },
+ { 0x05, 0x00 },
+ { 0x06, 0x00 },
+ { 0x07, 0x00 },
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x0c, 0x00 },
+ { 0x0d, 0x00 },
+ { 0x0e, 0x00 },
+ { 0x0f, 0x08 },
+ { 0x10, 0x00 },
+ { 0x11, 0x00 },
+ { 0x12, 0x00 },
+ { 0x13, 0x00 },
+ { 0x14, 0x08 },
+ { 0x15, 0x10 },
+ { 0x16, 0x10 },
+ { 0x17, 0x54 },
+ { 0x18, 0x40 },
+ { 0x19, 0x00 },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x00 },
+ { 0x1e, 0x00 },
+ { 0x1f, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x23, 0x02 },
+ { 0x24, 0x00 },
+ { 0x25, 0x76 },
+ { 0x26, 0x00 },
+ { 0x27, 0x00 },
+ { 0x28, 0x04 },
+ { 0x29, 0x00 },
+ { 0x2a, 0x00 },
+ { 0x2b, 0x30 },
+ { 0x2c, 0x2A },
+ { 0x83, 0x00 },
+ { 0x84, 0x00 },
+ { 0x85, 0x00 },
+ { 0x86, 0x00 },
+ { 0x87, 0x00 },
+ { 0x88, 0x00 },
+};
+
+static bool da7210_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA7210_A_HID_UNLOCK:
+ case DA7210_A_TEST_UNLOCK:
+ case DA7210_A_PLL1:
+ case DA7210_A_CP_MODE:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool da7210_volatile_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case DA7210_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * Set PCM DAI word length.
+ */
+static int da7210_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ u32 dai_cfg1;
+ u32 fs, sysclk;
+
+ /* set DAI source to Left and Right ADC */
+ snd_soc_write(codec, 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);
+
+ dai_cfg1 = 0xFC & snd_soc_read(codec, DA7210_DAI_CFG1);
+
+ switch (params_width(params)) {
+ case 16:
+ dai_cfg1 |= DA7210_DAI_WORD_S16_LE;
+ break;
+ case 20:
+ dai_cfg1 |= DA7210_DAI_WORD_S20_3LE;
+ break;
+ case 24:
+ dai_cfg1 |= DA7210_DAI_WORD_S24_LE;
+ break;
+ case 32:
+ dai_cfg1 |= DA7210_DAI_WORD_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, DA7210_DAI_CFG1, dai_cfg1);
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA7210_PLL_FS_8000;
+ sysclk = 3072000;
+ break;
+ case 11025:
+ fs = DA7210_PLL_FS_11025;
+ sysclk = 2822400;
+ break;
+ case 12000:
+ fs = DA7210_PLL_FS_12000;
+ sysclk = 3072000;
+ break;
+ case 16000:
+ fs = DA7210_PLL_FS_16000;
+ sysclk = 3072000;
+ break;
+ case 22050:
+ fs = DA7210_PLL_FS_22050;
+ sysclk = 2822400;
+ break;
+ case 32000:
+ fs = DA7210_PLL_FS_32000;
+ sysclk = 3072000;
+ break;
+ case 44100:
+ fs = DA7210_PLL_FS_44100;
+ sysclk = 2822400;
+ break;
+ case 48000:
+ fs = DA7210_PLL_FS_48000;
+ sysclk = 3072000;
+ break;
+ case 88200:
+ fs = DA7210_PLL_FS_88200;
+ sysclk = 2822400;
+ break;
+ case 96000:
+ fs = DA7210_PLL_FS_96000;
+ sysclk = 3072000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Disable active mode */
+ snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
+
+ snd_soc_update_bits(codec, 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);
+
+ if (!da7210->master) {
+ /* PLL slave mode, also enable SRM */
+ snd_soc_update_bits(codec, DA7210_PLL,
+ (DA7210_MCLK_SRM_EN |
+ DA7210_MCLK_DET_EN),
+ (DA7210_MCLK_SRM_EN |
+ DA7210_MCLK_DET_EN));
+ }
+ } else {
+ /* PLL bypass mode, enable PLL bypass and Auto Detection */
+ snd_soc_update_bits(codec, DA7210_PLL, DA7210_MCLK_DET_EN,
+ DA7210_MCLK_DET_EN);
+ snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP,
+ DA7210_PLL_BYP);
+ }
+ /* Enable active mode */
+ snd_soc_update_bits(codec, DA7210_STARTUP1,
+ DA7210_SC_MST_EN, DA7210_SC_MST_EN);
+
+ return 0;
+}
+
+/*
+ * Set DAI mode and Format
+ */
+static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ 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);
+
+ if ((snd_soc_read(codec, DA7210_PLL) & DA7210_PLL_EN) &&
+ (!(snd_soc_read(codec, DA7210_PLL_DIV3) & DA7210_PLL_BYP)))
+ return -EINVAL;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ da7210->master = 1;
+ dai_cfg1 |= DA7210_DAI_MODE_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ da7210->master = 0;
+ dai_cfg1 |= DA7210_DAI_MODE_SLAVE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* FIXME
+ *
+ * It support I2S only now
+ */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dai_cfg3 |= DA7210_DAI_FORMAT_I2SMODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dai_cfg3 |= DA7210_DAI_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dai_cfg3 |= DA7210_DAI_FORMAT_RIGHT_J;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* FIXME
+ *
+ * It support 64bit data transmission only now
+ */
+ dai_cfg1 |= DA7210_DAI_FLEN_64BIT;
+
+ snd_soc_write(codec, DA7210_DAI_CFG1, dai_cfg1);
+ snd_soc_write(codec, DA7210_DAI_CFG3, dai_cfg3);
+
+ return 0;
+}
+
+static int da7210_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 mute_reg = snd_soc_read(codec, DA7210_DAC_HPF) & 0xFB;
+
+ if (mute)
+ snd_soc_write(codec, DA7210_DAC_HPF, mute_reg | 0x4);
+ else
+ snd_soc_write(codec, DA7210_DAC_HPF, mute_reg);
+ return 0;
+}
+
+#define DA7210_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int da7210_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case DA7210_CLKSRC_MCLK:
+ switch (freq) {
+ case 12000000:
+ case 13000000:
+ case 13500000:
+ case 14400000:
+ case 19200000:
+ case 19680000:
+ case 19800000:
+ da7210->mclk_rate = freq;
+ return 0;
+ default:
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+}
+
+/**
+ * da7210_set_dai_pll :Configure the codec PLL
+ * @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
+ *
+ * Note: Supported PLL input frequencies are 12MHz, 13MHz, 13.5MHz, 14.4MHz,
+ * 19.2MHz, 19.6MHz and 19.8MHz
+ */
+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);
+
+ u8 pll_div1, pll_div2, pll_div3, cnt;
+
+ /* In slave mode, there is only one set of divisors */
+ if (!da7210->master)
+ fout = 2822400;
+
+ /* Search pll div array for correct divisors */
+ for (cnt = 0; cnt < ARRAY_SIZE(da7210_pll_div); cnt++) {
+ /* check fref, mode and fout */
+ if ((fref == da7210_pll_div[cnt].fref) &&
+ (da7210->master == da7210_pll_div[cnt].mode) &&
+ (fout == da7210_pll_div[cnt].fout)) {
+ /* all match, pick up divisors */
+ pll_div1 = da7210_pll_div[cnt].div1;
+ pll_div2 = da7210_pll_div[cnt].div2;
+ pll_div3 = da7210_pll_div[cnt].div3;
+ break;
+ }
+ }
+ if (cnt >= ARRAY_SIZE(da7210_pll_div))
+ goto err;
+
+ /* Disable active mode */
+ snd_soc_update_bits(codec, 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,
+ DA7210_PLL_DIV_L_MASK, pll_div3);
+
+ /* Enable PLL */
+ snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
+
+ /* Enable active mode */
+ snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN,
+ DA7210_SC_MST_EN);
+ return 0;
+err:
+ dev_err(codec_dai->dev, "Unsupported PLL input frequency %d\n", fref);
+ return -EINVAL;
+}
+
+/* DAI operations */
+static const struct snd_soc_dai_ops da7210_dai_ops = {
+ .hw_params = da7210_hw_params,
+ .set_fmt = da7210_set_dai_fmt,
+ .set_sysclk = da7210_set_dai_sysclk,
+ .set_pll = da7210_set_dai_pll,
+ .digital_mute = da7210_mute,
+};
+
+static struct snd_soc_dai_driver da7210_dai = {
+ .name = "da7210-hifi",
+ /* playback capabilities */
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7210_FORMATS,
+ },
+ /* capture capabilities */
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7210_FORMATS,
+ },
+ .ops = &da7210_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int da7210_probe(struct snd_soc_codec *codec)
+{
+ struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+
+ dev_info(codec->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);
+
+ /*
+ * 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);
+
+ /* 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);
+
+ /* Enable Left and Right ADC */
+ snd_soc_write(codec, DA7210_ADC, DA7210_ADC_L_EN | DA7210_ADC_R_EN);
+
+ /*
+ * DAC settings
+ */
+
+ /* Enable Left and Right DAC */
+ snd_soc_write(codec, 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);
+
+ /* Enable Left and Right HeadPhone PGA */
+ snd_soc_write(codec, 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);
+
+ /*
+ * For DA7210 codec, there are two ways to enable/disable analog IOs
+ * and ADC/DAC,
+ * (1) Using "Enable Bit" of register associated with that IO
+ * (or ADC/DAC)
+ * e.g. Mic Left can be enabled using bit 7 of MIC_L(0x7) reg
+ *
+ * (2) Using "Standby Bit" of STARTUP2 or STARTUP3 register
+ * e.g. Mic left can be put to STANDBY using bit 0 of STARTUP3(0x5)
+ *
+ * Out of these two methods, the one using STANDBY bits is preferred
+ * way to enable/disable individual blocks. This is because STANDBY
+ * registers are part of system controller which allows system power
+ * up/down in a controlled, pop-free manner. Also, as per application
+ * note of DA7210, STANDBY register bits are only effective if a
+ * particular IO (or ADC/DAC) is already enabled using enable/disable
+ * register bits. Keeping these things in mind, current DAPM
+ * implementation manipulates only STANDBY bits.
+ *
+ * Overall implementation can be outlined as below,
+ *
+ * - "Enable bit" of an IO or ADC/DAC is used to enable it in probe()
+ * - "STANDBY bit" is controlled by DAPM
+ */
+
+ /* Enable Line out amplifiers */
+ snd_soc_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 |
+ 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);
+ /* Enable Aux2 */
+ snd_soc_write(codec, 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 |
+ DA7210_PLL_BYP);
+
+ /* Diable PLL and bypass it */
+ snd_soc_write(codec, DA7210_PLL, DA7210_PLL_FS_48000);
+
+ /* Activate all enabled subsystem */
+ snd_soc_write(codec, DA7210_STARTUP1, DA7210_SC_MST_EN);
+
+ dev_info(codec->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_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),
+};
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static struct reg_default da7210_regmap_i2c_patch[] = {
+
+ /* System controller master disable */
+ { DA7210_STARTUP1, 0x00 },
+ /* Set PLL Master clock range 10-20 MHz */
+ { DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ },
+
+ /* to unlock */
+ { DA7210_A_HID_UNLOCK, 0x8B},
+ { DA7210_A_TEST_UNLOCK, 0xB4},
+ { DA7210_A_PLL1, 0x01},
+ { DA7210_A_CP_MODE, 0x7C},
+ /* to re-lock */
+ { DA7210_A_HID_UNLOCK, 0x00},
+ { DA7210_A_TEST_UNLOCK, 0x00},
+};
+
+static const struct regmap_config da7210_regmap_config_i2c = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = da7210_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da7210_reg_defaults),
+ .volatile_reg = da7210_volatile_register,
+ .readable_reg = da7210_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int da7210_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da7210_priv *da7210;
+ int ret;
+
+ da7210 = devm_kzalloc(&i2c->dev, sizeof(struct da7210_priv),
+ GFP_KERNEL);
+ if (!da7210)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, da7210);
+
+ da7210->regmap = devm_regmap_init_i2c(i2c, &da7210_regmap_config_i2c);
+ if (IS_ERR(da7210->regmap)) {
+ ret = PTR_ERR(da7210->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_register_patch(da7210->regmap, da7210_regmap_i2c_patch,
+ ARRAY_SIZE(da7210_regmap_i2c_patch));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_da7210, &da7210_dai, 1);
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %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 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
+
+/* I2C codec control layer */
+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[] = {
+ /* Dummy read to give two pulses over nCS for SPI */
+ { DA7210_AUX2, 0x00 },
+ { DA7210_AUX2, 0x00 },
+
+ /* System controller master disable */
+ { DA7210_STARTUP1, 0x00 },
+ /* Set PLL Master clock range 10-20 MHz */
+ { DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ },
+
+ /* to set PAGE1 of SPI register space */
+ { DA7210_PAGE_CONTROL, 0x80 },
+ /* to unlock */
+ { DA7210_A_HID_UNLOCK, 0x8B},
+ { DA7210_A_TEST_UNLOCK, 0xB4},
+ { DA7210_A_PLL1, 0x01},
+ { DA7210_A_CP_MODE, 0x7C},
+ /* to re-lock */
+ { DA7210_A_HID_UNLOCK, 0x00},
+ { DA7210_A_TEST_UNLOCK, 0x00},
+ /* to set back PAGE0 of SPI register space */
+ { DA7210_PAGE_CONTROL, 0x00 },
+};
+
+static const struct regmap_config da7210_regmap_config_spi = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .read_flag_mask = 0x01,
+ .write_flag_mask = 0x00,
+
+ .reg_defaults = da7210_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da7210_reg_defaults),
+ .volatile_reg = da7210_volatile_register,
+ .readable_reg = da7210_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int da7210_spi_probe(struct spi_device *spi)
+{
+ struct da7210_priv *da7210;
+ int ret;
+
+ da7210 = devm_kzalloc(&spi->dev, sizeof(struct da7210_priv),
+ GFP_KERNEL);
+ if (!da7210)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, da7210);
+ da7210->regmap = devm_regmap_init_spi(spi, &da7210_regmap_config_spi);
+ if (IS_ERR(da7210->regmap)) {
+ ret = PTR_ERR(da7210->regmap);
+ dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_register_patch(da7210->regmap, da7210_regmap_spi_patch,
+ ARRAY_SIZE(da7210_regmap_spi_patch));
+ if (ret != 0)
+ dev_warn(&spi->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_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
+
+static int __init da7210_modinit(void)
+{
+ int ret = 0;
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&da7210_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&da7210_spi_driver);
+ if (ret) {
+ printk(KERN_ERR "Failed to register da7210 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(da7210_modinit);
+
+static void __exit da7210_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&da7210_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&da7210_spi_driver);
+#endif
+}
+module_exit(da7210_exit);
+
+MODULE_DESCRIPTION("ASoC DA7210 driver");
+MODULE_AUTHOR("David Chen, Kuninori Morimoto");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/da7213.c b/kernel/sound/soc/codecs/da7213.c
new file mode 100644
index 000000000..9ec577f0e
--- /dev/null
+++ b/kernel/sound/soc/codecs/da7213.c
@@ -0,0 +1,1600 @@
+/*
+ * DA7213 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2013 Dialog Semiconductor
+ *
+ * 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <sound/da7213.h>
+#include "da7213.h"
+
+
+/* Gain and Volume */
+static const unsigned int aux_vol_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ /* -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),
+ 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),
+ 0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* 0dB to 36dB */
+ 0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0)
+};
+
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(lineout_vol_tlv, -4800, 100, 0);
+static const DECLARE_TLV_DB_SCALE(alc_threshold_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(alc_gain_tlv, 0, 600, 0);
+
+/* ADC and DAC voice mode (8kHz) high pass cutoff value */
+static const char * const da7213_voice_hpf_corner_txt[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_voice_hpf_corner,
+ DA7213_DAC_FILTERS1,
+ DA7213_VOICE_HPF_CORNER_SHIFT,
+ da7213_voice_hpf_corner_txt);
+
+static SOC_ENUM_SINGLE_DECL(da7213_adc_voice_hpf_corner,
+ DA7213_ADC_FILTERS1,
+ DA7213_VOICE_HPF_CORNER_SHIFT,
+ da7213_voice_hpf_corner_txt);
+
+/* ADC and DAC high pass filter cutoff value */
+static const char * const da7213_audio_hpf_corner_txt[] = {
+ "Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_audio_hpf_corner,
+ DA7213_DAC_FILTERS1
+ , DA7213_AUDIO_HPF_CORNER_SHIFT,
+ da7213_audio_hpf_corner_txt);
+
+static SOC_ENUM_SINGLE_DECL(da7213_adc_audio_hpf_corner,
+ DA7213_ADC_FILTERS1,
+ DA7213_AUDIO_HPF_CORNER_SHIFT,
+ da7213_audio_hpf_corner_txt);
+
+/* Gain ramping rate value */
+static const char * const da7213_gain_ramp_rate_txt[] = {
+ "nominal rate * 8", "nominal rate * 16", "nominal rate / 16",
+ "nominal rate / 32"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_gain_ramp_rate,
+ DA7213_GAIN_RAMP_CTRL,
+ DA7213_GAIN_RAMP_RATE_SHIFT,
+ da7213_gain_ramp_rate_txt);
+
+/* DAC noise gate setup time value */
+static const char * const da7213_dac_ng_setup_time_txt[] = {
+ "256 samples", "512 samples", "1024 samples", "2048 samples"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_setup_time,
+ DA7213_DAC_NG_SETUP_TIME,
+ DA7213_DAC_NG_SETUP_TIME_SHIFT,
+ da7213_dac_ng_setup_time_txt);
+
+/* DAC noise gate rampup rate value */
+static const char * const da7213_dac_ng_rampup_txt[] = {
+ "0.02 ms/dB", "0.16 ms/dB"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_rampup_rate,
+ DA7213_DAC_NG_SETUP_TIME,
+ DA7213_DAC_NG_RAMPUP_RATE_SHIFT,
+ da7213_dac_ng_rampup_txt);
+
+/* DAC noise gate rampdown rate value */
+static const char * const da7213_dac_ng_rampdown_txt[] = {
+ "0.64 ms/dB", "20.48 ms/dB"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_rampdown_rate,
+ DA7213_DAC_NG_SETUP_TIME,
+ DA7213_DAC_NG_RAMPDN_RATE_SHIFT,
+ da7213_dac_ng_rampdown_txt);
+
+/* DAC soft mute rate value */
+static const char * const da7213_dac_soft_mute_rate_txt[] = {
+ "1", "2", "4", "8", "16", "32", "64"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_soft_mute_rate,
+ DA7213_DAC_FILTERS5,
+ DA7213_DAC_SOFTMUTE_RATE_SHIFT,
+ da7213_dac_soft_mute_rate_txt);
+
+/* ALC Attack Rate select */
+static const char * const da7213_alc_attack_rate_txt[] = {
+ "44/fs", "88/fs", "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs",
+ "5632/fs", "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_alc_attack_rate,
+ DA7213_ALC_CTRL2,
+ DA7213_ALC_ATTACK_SHIFT,
+ da7213_alc_attack_rate_txt);
+
+/* ALC Release Rate select */
+static const char * const da7213_alc_release_rate_txt[] = {
+ "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs", "5632/fs",
+ "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_alc_release_rate,
+ DA7213_ALC_CTRL2,
+ DA7213_ALC_RELEASE_SHIFT,
+ da7213_alc_release_rate_txt);
+
+/* ALC Hold Time select */
+static const char * const da7213_alc_hold_time_txt[] = {
+ "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs",
+ "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs",
+ "253952/fs", "507904/fs", "1015808/fs", "2031616/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_alc_hold_time,
+ DA7213_ALC_CTRL3,
+ DA7213_ALC_HOLD_SHIFT,
+ da7213_alc_hold_time_txt);
+
+/* ALC Input Signal Tracking rate select */
+static const char * const da7213_alc_integ_rate_txt[] = {
+ "1/4", "1/16", "1/256", "1/65536"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_alc_integ_attack_rate,
+ DA7213_ALC_CTRL3,
+ DA7213_ALC_INTEG_ATTACK_SHIFT,
+ da7213_alc_integ_rate_txt);
+
+static SOC_ENUM_SINGLE_DECL(da7213_alc_integ_release_rate,
+ DA7213_ALC_CTRL3,
+ DA7213_ALC_INTEG_RELEASE_SHIFT,
+ da7213_alc_integ_rate_txt);
+
+
+/*
+ * Control Functions
+ */
+
+static int da7213_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
+{
+ int mid_data, top_data;
+ int sum = 0;
+ u8 iteration;
+
+ for (iteration = 0; iteration < DA7213_ALC_AVG_ITERATIONS;
+ iteration++) {
+ /* Select the left or right channel and capture data */
+ snd_soc_write(codec, 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,
+ reg_val | DA7213_ALC_DATA_MIDDLE);
+ mid_data = snd_soc_read(codec, 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,
+ reg_val | DA7213_ALC_DATA_TOP);
+ top_data = snd_soc_read(codec, DA7213_ALC_CIC_OP_LVL_DATA);
+
+ sum += ((mid_data << 8) | (top_data << 16));
+ }
+
+ return sum / DA7213_ALC_AVG_ITERATIONS;
+}
+
+static void da7213_alc_calib_man(struct snd_soc_codec *codec)
+{
+ 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,
+ DA7213_ALC_CIC_OP_CHANNEL_LEFT);
+ /* Right Data */
+ avg_right_data = da7213_get_alc_data(codec,
+ DA7213_ALC_CIC_OP_CHANNEL_RIGHT);
+
+ /* Calculate DC offset */
+ offset_l = -avg_left_data;
+ offset_r = -avg_right_data;
+
+ reg_val = (offset_l & DA7213_ALC_OFFSET_15_8) >> 8;
+ snd_soc_write(codec, 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);
+
+ reg_val = (offset_r & DA7213_ALC_OFFSET_15_8) >> 8;
+ snd_soc_write(codec, 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);
+
+ /* Enable analog/digital gain mode & offset cancellation */
+ snd_soc_update_bits(codec, 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)
+{
+ u8 alc_ctrl1;
+
+ /* Begin auto calibration and wait for completion */
+ snd_soc_update_bits(codec, DA7213_ALC_CTRL1, DA7213_ALC_AUTO_CALIB_EN,
+ DA7213_ALC_AUTO_CALIB_EN);
+ do {
+ alc_ctrl1 = snd_soc_read(codec, 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,
+ "ALC auto calibration failed with overflow\n");
+ snd_soc_update_bits(codec, 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,
+ 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)
+{
+ struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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);
+
+ /* 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);
+
+ /* 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);
+
+ /* Enable ADC Left and Right */
+ snd_soc_update_bits(codec, DA7213_ADC_L_CTRL, DA7213_ADC_EN,
+ DA7213_ADC_EN);
+ snd_soc_update_bits(codec, DA7213_ADC_R_CTRL, DA7213_ADC_EN,
+ DA7213_ADC_EN);
+
+ /* Enable MIC paths */
+ snd_soc_update_bits(codec, 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,
+ 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,
+ DA7213_MUTE_EN);
+ snd_soc_update_bits(codec, DA7213_MIC_2_CTRL, DA7213_MUTE_EN,
+ DA7213_MUTE_EN);
+
+ /* Perform calibration */
+ if (da7213->alc_calib_auto)
+ da7213_alc_calib_auto(codec);
+ else
+ da7213_alc_calib_man(codec);
+
+ /* 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);
+
+ /* 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);
+
+ /* 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);
+}
+
+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);
+ 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);
+
+ return ret;
+}
+
+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);
+
+ /* 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_en = true;
+ }
+ } else {
+ da7213->alc_en = false;
+ }
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+
+/*
+ * KControls
+ */
+
+static const struct snd_kcontrol_new da7213_snd_controls[] = {
+
+ /* Volume controls */
+ SOC_SINGLE_TLV("Mic 1 Volume", DA7213_MIC_1_GAIN,
+ DA7213_MIC_AMP_GAIN_SHIFT, DA7213_MIC_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, mic_vol_tlv),
+ SOC_SINGLE_TLV("Mic 2 Volume", DA7213_MIC_2_GAIN,
+ DA7213_MIC_AMP_GAIN_SHIFT, DA7213_MIC_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, mic_vol_tlv),
+ SOC_DOUBLE_R_TLV("Aux Volume", DA7213_AUX_L_GAIN, DA7213_AUX_R_GAIN,
+ DA7213_AUX_AMP_GAIN_SHIFT, DA7213_AUX_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, aux_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("Mixin PGA Volume", DA7213_MIXIN_L_GAIN,
+ DA7213_MIXIN_R_GAIN, DA7213_MIXIN_AMP_GAIN_SHIFT,
+ DA7213_MIXIN_AMP_GAIN_MAX, DA7213_NO_INVERT,
+ snd_soc_get_volsw_2r, da7213_put_mixin_gain,
+ mixin_gain_tlv),
+ SOC_DOUBLE_R_TLV("ADC Volume", DA7213_ADC_L_GAIN, DA7213_ADC_R_GAIN,
+ DA7213_ADC_AMP_GAIN_SHIFT, DA7213_ADC_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, digital_gain_tlv),
+ SOC_DOUBLE_R_TLV("DAC Volume", DA7213_DAC_L_GAIN, DA7213_DAC_R_GAIN,
+ DA7213_DAC_AMP_GAIN_SHIFT, DA7213_DAC_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, digital_gain_tlv),
+ SOC_DOUBLE_R_TLV("Headphone Volume", DA7213_HP_L_GAIN, DA7213_HP_R_GAIN,
+ DA7213_HP_AMP_GAIN_SHIFT, DA7213_HP_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, hp_vol_tlv),
+ SOC_SINGLE_TLV("Lineout Volume", DA7213_LINE_GAIN,
+ DA7213_LINE_AMP_GAIN_SHIFT, DA7213_LINE_AMP_GAIN_MAX,
+ DA7213_NO_INVERT, lineout_vol_tlv),
+
+ /* DAC Equalizer controls */
+ SOC_SINGLE("DAC EQ Switch", DA7213_DAC_FILTERS4, DA7213_DAC_EQ_EN_SHIFT,
+ DA7213_DAC_EQ_EN_MAX, DA7213_NO_INVERT),
+ SOC_SINGLE_TLV("DAC EQ1 Volume", DA7213_DAC_FILTERS2,
+ DA7213_DAC_EQ_BAND1_SHIFT, DA7213_DAC_EQ_BAND_MAX,
+ DA7213_NO_INVERT, eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ2 Volume", DA7213_DAC_FILTERS2,
+ DA7213_DAC_EQ_BAND2_SHIFT, DA7213_DAC_EQ_BAND_MAX,
+ DA7213_NO_INVERT, eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ3 Volume", DA7213_DAC_FILTERS3,
+ DA7213_DAC_EQ_BAND3_SHIFT, DA7213_DAC_EQ_BAND_MAX,
+ DA7213_NO_INVERT, eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ4 Volume", DA7213_DAC_FILTERS3,
+ DA7213_DAC_EQ_BAND4_SHIFT, DA7213_DAC_EQ_BAND_MAX,
+ DA7213_NO_INVERT, eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ5 Volume", DA7213_DAC_FILTERS4,
+ DA7213_DAC_EQ_BAND5_SHIFT, DA7213_DAC_EQ_BAND_MAX,
+ DA7213_NO_INVERT, eq_gain_tlv),
+
+ /* High Pass Filter and Voice Mode controls */
+ SOC_SINGLE("ADC HPF Switch", DA7213_ADC_FILTERS1, DA7213_HPF_EN_SHIFT,
+ DA7213_HPF_EN_MAX, DA7213_NO_INVERT),
+ SOC_ENUM("ADC HPF Cutoff", da7213_adc_audio_hpf_corner),
+ SOC_SINGLE("ADC Voice Mode Switch", DA7213_ADC_FILTERS1,
+ DA7213_VOICE_EN_SHIFT, DA7213_VOICE_EN_MAX,
+ DA7213_NO_INVERT),
+ SOC_ENUM("ADC Voice Cutoff", da7213_adc_voice_hpf_corner),
+
+ SOC_SINGLE("DAC HPF Switch", DA7213_DAC_FILTERS1, DA7213_HPF_EN_SHIFT,
+ DA7213_HPF_EN_MAX, DA7213_NO_INVERT),
+ SOC_ENUM("DAC HPF Cutoff", da7213_dac_audio_hpf_corner),
+ SOC_SINGLE("DAC Voice Mode Switch", DA7213_DAC_FILTERS1,
+ DA7213_VOICE_EN_SHIFT, DA7213_VOICE_EN_MAX,
+ DA7213_NO_INVERT),
+ SOC_ENUM("DAC Voice Cutoff", da7213_dac_voice_hpf_corner),
+
+ /* Mute controls */
+ SOC_SINGLE("Mic 1 Switch", DA7213_MIC_1_CTRL, DA7213_MUTE_EN_SHIFT,
+ DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_SINGLE("Mic 2 Switch", DA7213_MIC_2_CTRL, DA7213_MUTE_EN_SHIFT,
+ DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_DOUBLE_R("Aux Switch", DA7213_AUX_L_CTRL, DA7213_AUX_R_CTRL,
+ DA7213_MUTE_EN_SHIFT, DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_DOUBLE_R("Mixin PGA Switch", DA7213_MIXIN_L_CTRL,
+ DA7213_MIXIN_R_CTRL, DA7213_MUTE_EN_SHIFT,
+ DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_DOUBLE_R("ADC Switch", DA7213_ADC_L_CTRL, DA7213_ADC_R_CTRL,
+ DA7213_MUTE_EN_SHIFT, DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_DOUBLE_R("Headphone Switch", DA7213_HP_L_CTRL, DA7213_HP_R_CTRL,
+ DA7213_MUTE_EN_SHIFT, DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_SINGLE("Lineout Switch", DA7213_LINE_CTRL, DA7213_MUTE_EN_SHIFT,
+ DA7213_MUTE_EN_MAX, DA7213_INVERT),
+ SOC_SINGLE("DAC Soft Mute Switch", DA7213_DAC_FILTERS5,
+ DA7213_DAC_SOFTMUTE_EN_SHIFT, DA7213_DAC_SOFTMUTE_EN_MAX,
+ DA7213_NO_INVERT),
+ SOC_ENUM("DAC Soft Mute Rate", da7213_dac_soft_mute_rate),
+
+ /* Zero Cross controls */
+ SOC_DOUBLE_R("Aux ZC Switch", DA7213_AUX_L_CTRL, DA7213_AUX_R_CTRL,
+ DA7213_ZC_EN_SHIFT, DA7213_ZC_EN_MAX, DA7213_NO_INVERT),
+ SOC_DOUBLE_R("Mixin PGA ZC Switch", DA7213_MIXIN_L_CTRL,
+ DA7213_MIXIN_R_CTRL, DA7213_ZC_EN_SHIFT, DA7213_ZC_EN_MAX,
+ DA7213_NO_INVERT),
+ SOC_DOUBLE_R("Headphone ZC Switch", DA7213_HP_L_CTRL, DA7213_HP_R_CTRL,
+ DA7213_ZC_EN_SHIFT, DA7213_ZC_EN_MAX, DA7213_NO_INVERT),
+
+ /* Gain Ramping controls */
+ SOC_DOUBLE_R("Aux Gain Ramping Switch", DA7213_AUX_L_CTRL,
+ DA7213_AUX_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT,
+ DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT),
+ SOC_DOUBLE_R("Mixin Gain Ramping Switch", DA7213_MIXIN_L_CTRL,
+ DA7213_MIXIN_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT,
+ DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT),
+ SOC_DOUBLE_R("ADC Gain Ramping Switch", DA7213_ADC_L_CTRL,
+ DA7213_ADC_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT,
+ DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT),
+ SOC_DOUBLE_R("DAC Gain Ramping Switch", DA7213_DAC_L_CTRL,
+ DA7213_DAC_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT,
+ DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT),
+ SOC_DOUBLE_R("Headphone Gain Ramping Switch", DA7213_HP_L_CTRL,
+ DA7213_HP_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT,
+ DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT),
+ SOC_SINGLE("Lineout Gain Ramping Switch", DA7213_LINE_CTRL,
+ DA7213_GAIN_RAMP_EN_SHIFT, DA7213_GAIN_RAMP_EN_MAX,
+ DA7213_NO_INVERT),
+ SOC_ENUM("Gain Ramping Rate", da7213_gain_ramp_rate),
+
+ /* DAC Noise Gate controls */
+ SOC_SINGLE("DAC NG Switch", DA7213_DAC_NG_CTRL, DA7213_DAC_NG_EN_SHIFT,
+ DA7213_DAC_NG_EN_MAX, DA7213_NO_INVERT),
+ SOC_ENUM("DAC NG Setup Time", da7213_dac_ng_setup_time),
+ SOC_ENUM("DAC NG Rampup Rate", da7213_dac_ng_rampup_rate),
+ SOC_ENUM("DAC NG Rampdown Rate", da7213_dac_ng_rampdown_rate),
+ SOC_SINGLE("DAC NG OFF Threshold", DA7213_DAC_NG_OFF_THRESHOLD,
+ DA7213_DAC_NG_THRESHOLD_SHIFT, DA7213_DAC_NG_THRESHOLD_MAX,
+ DA7213_NO_INVERT),
+ SOC_SINGLE("DAC NG ON Threshold", DA7213_DAC_NG_ON_THRESHOLD,
+ DA7213_DAC_NG_THRESHOLD_SHIFT, DA7213_DAC_NG_THRESHOLD_MAX,
+ DA7213_NO_INVERT),
+
+ /* DAC Routing & Inversion */
+ SOC_DOUBLE("DAC Mono Switch", DA7213_DIG_ROUTING_DAC,
+ DA7213_DAC_L_MONO_SHIFT, DA7213_DAC_R_MONO_SHIFT,
+ DA7213_DAC_MONO_MAX, DA7213_NO_INVERT),
+ SOC_DOUBLE("DAC Invert Switch", DA7213_DIG_CTRL, DA7213_DAC_L_INV_SHIFT,
+ DA7213_DAC_R_INV_SHIFT, DA7213_DAC_INV_MAX,
+ DA7213_NO_INVERT),
+
+ /* DMIC controls */
+ SOC_DOUBLE_R("DMIC Switch", DA7213_MIXIN_L_SELECT,
+ DA7213_MIXIN_R_SELECT, DA7213_DMIC_EN_SHIFT,
+ DA7213_DMIC_EN_MAX, DA7213_NO_INVERT),
+
+ /* ALC Controls */
+ SOC_DOUBLE_EXT("ALC Switch", DA7213_ALC_CTRL1, DA7213_ALC_L_EN_SHIFT,
+ DA7213_ALC_R_EN_SHIFT, DA7213_ALC_EN_MAX,
+ DA7213_NO_INVERT, snd_soc_get_volsw, da7213_put_alc_sw),
+ SOC_ENUM("ALC Attack Rate", da7213_alc_attack_rate),
+ SOC_ENUM("ALC Release Rate", da7213_alc_release_rate),
+ SOC_ENUM("ALC Hold Time", da7213_alc_hold_time),
+ /*
+ * Rate at which input signal envelope is tracked as the signal gets
+ * larger
+ */
+ SOC_ENUM("ALC Integ Attack Rate", da7213_alc_integ_attack_rate),
+ /*
+ * Rate at which input signal envelope is tracked as the signal gets
+ * smaller
+ */
+ SOC_ENUM("ALC Integ Release Rate", da7213_alc_integ_release_rate),
+ SOC_SINGLE_TLV("ALC Noise Threshold Volume", DA7213_ALC_NOISE,
+ DA7213_ALC_THRESHOLD_SHIFT, DA7213_ALC_THRESHOLD_MAX,
+ DA7213_INVERT, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Min Threshold Volume", DA7213_ALC_TARGET_MIN,
+ DA7213_ALC_THRESHOLD_SHIFT, DA7213_ALC_THRESHOLD_MAX,
+ DA7213_INVERT, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold Volume", DA7213_ALC_TARGET_MAX,
+ DA7213_ALC_THRESHOLD_SHIFT, DA7213_ALC_THRESHOLD_MAX,
+ DA7213_INVERT, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Attenuation Volume", DA7213_ALC_GAIN_LIMITS,
+ DA7213_ALC_ATTEN_MAX_SHIFT,
+ DA7213_ALC_ATTEN_GAIN_MAX_MAX, DA7213_NO_INVERT,
+ alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Gain Volume", DA7213_ALC_GAIN_LIMITS,
+ DA7213_ALC_GAIN_MAX_SHIFT, DA7213_ALC_ATTEN_GAIN_MAX_MAX,
+ DA7213_NO_INVERT, alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Min Analog Gain Volume", DA7213_ALC_ANA_GAIN_LIMITS,
+ DA7213_ALC_ANA_GAIN_MIN_SHIFT, DA7213_ALC_ANA_GAIN_MAX,
+ DA7213_NO_INVERT, alc_analog_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Analog Gain Volume", DA7213_ALC_ANA_GAIN_LIMITS,
+ DA7213_ALC_ANA_GAIN_MAX_SHIFT, DA7213_ALC_ANA_GAIN_MAX,
+ DA7213_NO_INVERT, alc_analog_gain_tlv),
+ SOC_SINGLE("ALC Anticlip Mode Switch", DA7213_ALC_ANTICLIP_CTRL,
+ DA7213_ALC_ANTICLIP_EN_SHIFT, DA7213_ALC_ANTICLIP_EN_MAX,
+ DA7213_NO_INVERT),
+ SOC_SINGLE("ALC Anticlip Level", DA7213_ALC_ANTICLIP_LEVEL,
+ DA7213_ALC_ANTICLIP_LEVEL_SHIFT,
+ DA7213_ALC_ANTICLIP_LEVEL_MAX, DA7213_NO_INVERT),
+};
+
+
+/*
+ * DAPM
+ */
+
+/*
+ * Enums
+ */
+
+/* MIC PGA source select */
+static const char * const da7213_mic_amp_in_sel_txt[] = {
+ "Differential", "MIC_P", "MIC_N"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_mic_1_amp_in_sel,
+ DA7213_MIC_1_CTRL,
+ DA7213_MIC_AMP_IN_SEL_SHIFT,
+ da7213_mic_amp_in_sel_txt);
+static const struct snd_kcontrol_new da7213_mic_1_amp_in_sel_mux =
+ SOC_DAPM_ENUM("Mic 1 Amp Source MUX", da7213_mic_1_amp_in_sel);
+
+static SOC_ENUM_SINGLE_DECL(da7213_mic_2_amp_in_sel,
+ DA7213_MIC_2_CTRL,
+ DA7213_MIC_AMP_IN_SEL_SHIFT,
+ da7213_mic_amp_in_sel_txt);
+static const struct snd_kcontrol_new da7213_mic_2_amp_in_sel_mux =
+ SOC_DAPM_ENUM("Mic 2 Amp Source MUX", da7213_mic_2_amp_in_sel);
+
+/* DAI routing select */
+static const char * const da7213_dai_src_txt[] = {
+ "ADC Left", "ADC Right", "DAI Input Left", "DAI Input Right"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dai_l_src,
+ DA7213_DIG_ROUTING_DAI,
+ DA7213_DAI_L_SRC_SHIFT,
+ da7213_dai_src_txt);
+static const struct snd_kcontrol_new da7213_dai_l_src_mux =
+ SOC_DAPM_ENUM("DAI Left Source MUX", da7213_dai_l_src);
+
+static SOC_ENUM_SINGLE_DECL(da7213_dai_r_src,
+ DA7213_DIG_ROUTING_DAI,
+ DA7213_DAI_R_SRC_SHIFT,
+ da7213_dai_src_txt);
+static const struct snd_kcontrol_new da7213_dai_r_src_mux =
+ SOC_DAPM_ENUM("DAI Right Source MUX", da7213_dai_r_src);
+
+/* DAC routing select */
+static const char * const da7213_dac_src_txt[] = {
+ "ADC Output Left", "ADC Output Right", "DAI Input Left",
+ "DAI Input Right"
+};
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_l_src,
+ DA7213_DIG_ROUTING_DAC,
+ DA7213_DAC_L_SRC_SHIFT,
+ da7213_dac_src_txt);
+static const struct snd_kcontrol_new da7213_dac_l_src_mux =
+ SOC_DAPM_ENUM("DAC Left Source MUX", da7213_dac_l_src);
+
+static SOC_ENUM_SINGLE_DECL(da7213_dac_r_src,
+ DA7213_DIG_ROUTING_DAC,
+ DA7213_DAC_R_SRC_SHIFT,
+ da7213_dac_src_txt);
+static const struct snd_kcontrol_new da7213_dac_r_src_mux =
+ SOC_DAPM_ENUM("DAC Right Source MUX", da7213_dac_r_src);
+
+/*
+ * Mixer Controls
+ */
+
+/* Mixin Left */
+static const struct snd_kcontrol_new da7213_dapm_mixinl_controls[] = {
+ SOC_DAPM_SINGLE("Aux Left Switch", DA7213_MIXIN_L_SELECT,
+ DA7213_MIXIN_L_MIX_SELECT_AUX_L_SHIFT,
+ DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mic 1 Switch", DA7213_MIXIN_L_SELECT,
+ DA7213_MIXIN_L_MIX_SELECT_MIC_1_SHIFT,
+ DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mic 2 Switch", DA7213_MIXIN_L_SELECT,
+ DA7213_MIXIN_L_MIX_SELECT_MIC_2_SHIFT,
+ DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Right Switch", DA7213_MIXIN_L_SELECT,
+ DA7213_MIXIN_L_MIX_SELECT_MIXIN_R_SHIFT,
+ DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+};
+
+/* Mixin Right */
+static const struct snd_kcontrol_new da7213_dapm_mixinr_controls[] = {
+ SOC_DAPM_SINGLE("Aux Right Switch", DA7213_MIXIN_R_SELECT,
+ DA7213_MIXIN_R_MIX_SELECT_AUX_R_SHIFT,
+ DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mic 2 Switch", DA7213_MIXIN_R_SELECT,
+ DA7213_MIXIN_R_MIX_SELECT_MIC_2_SHIFT,
+ DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mic 1 Switch", DA7213_MIXIN_R_SELECT,
+ DA7213_MIXIN_R_MIX_SELECT_MIC_1_SHIFT,
+ DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA7213_MIXIN_R_SELECT,
+ DA7213_MIXIN_R_MIX_SELECT_MIXIN_L_SHIFT,
+ DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+};
+
+/* Mixout Left */
+static const struct snd_kcontrol_new da7213_dapm_mixoutl_controls[] = {
+ SOC_DAPM_SINGLE("Aux Left Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_AUX_L_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Right Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("DAC Left Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_DAC_L_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Aux Left Invert Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_AUX_L_INVERTED_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_INVERTED_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA7213_MIXOUT_L_SELECT,
+ DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_INVERTED_SHIFT,
+ DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT),
+};
+
+/* Mixout Right */
+static const struct snd_kcontrol_new da7213_dapm_mixoutr_controls[] = {
+ SOC_DAPM_SINGLE("Aux Right Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_AUX_R_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Right Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("DAC Right Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_DAC_R_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Aux Right Invert Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_AUX_R_INVERTED_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_INVERTED_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+ SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA7213_MIXOUT_R_SELECT,
+ DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_INVERTED_SHIFT,
+ DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT),
+};
+
+
+/*
+ * DAPM widgets
+ */
+
+static const struct snd_soc_dapm_widget da7213_dapm_widgets[] = {
+ /*
+ * 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),
+
+ /*
+ * Input
+ */
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+
+ /* MUXs for Mic PGA source selection */
+ SND_SOC_DAPM_MUX("Mic 1 Amp Source MUX", SND_SOC_NOPM, 0, 0,
+ &da7213_mic_1_amp_in_sel_mux),
+ SND_SOC_DAPM_MUX("Mic 2 Amp Source MUX", SND_SOC_NOPM, 0, 0,
+ &da7213_mic_2_amp_in_sel_mux),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_PGA("Mic 1 PGA", DA7213_MIC_1_CTRL, DA7213_AMP_EN_SHIFT,
+ DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic 2 PGA", DA7213_MIC_2_CTRL, DA7213_AMP_EN_SHIFT,
+ DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux Left PGA", DA7213_AUX_L_CTRL, DA7213_AMP_EN_SHIFT,
+ DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux Right PGA", DA7213_AUX_R_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Mixin Left PGA", DA7213_MIXIN_L_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Mixin Right PGA", DA7213_MIXIN_R_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+
+ /* Mic Biases */
+ SND_SOC_DAPM_SUPPLY("Mic Bias 1", DA7213_MICBIAS_CTRL,
+ DA7213_MICBIAS1_EN_SHIFT, DA7213_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias 2", DA7213_MICBIAS_CTRL,
+ DA7213_MICBIAS2_EN_SHIFT, DA7213_NO_INVERT,
+ NULL, 0),
+
+ /* Input Mixers */
+ SND_SOC_DAPM_MIXER("Mixin Left", SND_SOC_NOPM, 0, 0,
+ &da7213_dapm_mixinl_controls[0],
+ ARRAY_SIZE(da7213_dapm_mixinl_controls)),
+ SND_SOC_DAPM_MIXER("Mixin Right", SND_SOC_NOPM, 0, 0,
+ &da7213_dapm_mixinr_controls[0],
+ ARRAY_SIZE(da7213_dapm_mixinr_controls)),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC Left", NULL, DA7213_ADC_L_CTRL,
+ DA7213_ADC_EN_SHIFT, DA7213_NO_INVERT),
+ SND_SOC_DAPM_ADC("ADC Right", NULL, DA7213_ADC_R_CTRL,
+ DA7213_ADC_EN_SHIFT, DA7213_NO_INVERT),
+
+ /* DAI */
+ SND_SOC_DAPM_MUX("DAI Left Source MUX", SND_SOC_NOPM, 0, 0,
+ &da7213_dai_l_src_mux),
+ SND_SOC_DAPM_MUX("DAI Right Source MUX", SND_SOC_NOPM, 0, 0,
+ &da7213_dai_r_src_mux),
+ SND_SOC_DAPM_AIF_OUT("DAIOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DAIOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
+
+ /*
+ * Output
+ */
+
+ /* DAI */
+ SND_SOC_DAPM_AIF_IN("DAIINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DAIINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("DAC Left Source MUX", SND_SOC_NOPM, 0, 0,
+ &da7213_dac_l_src_mux),
+ SND_SOC_DAPM_MUX("DAC Right Source MUX", SND_SOC_NOPM, 0, 0,
+ &da7213_dac_r_src_mux),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC Left", NULL, DA7213_DAC_L_CTRL,
+ DA7213_DAC_EN_SHIFT, DA7213_NO_INVERT),
+ SND_SOC_DAPM_DAC("DAC Right", NULL, DA7213_DAC_R_CTRL,
+ DA7213_DAC_EN_SHIFT, DA7213_NO_INVERT),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Mixout Left", SND_SOC_NOPM, 0, 0,
+ &da7213_dapm_mixoutl_controls[0],
+ ARRAY_SIZE(da7213_dapm_mixoutl_controls)),
+ SND_SOC_DAPM_MIXER("Mixout Right", SND_SOC_NOPM, 0, 0,
+ &da7213_dapm_mixoutr_controls[0],
+ ARRAY_SIZE(da7213_dapm_mixoutr_controls)),
+
+ /* Output PGAs */
+ SND_SOC_DAPM_PGA("Mixout Left PGA", DA7213_MIXOUT_L_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Mixout Right PGA", DA7213_MIXOUT_R_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Lineout PGA", DA7213_LINE_CTRL, DA7213_AMP_EN_SHIFT,
+ DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Left PGA", DA7213_HP_L_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Right PGA", DA7213_HP_R_CTRL,
+ DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0),
+
+ /* Charge Pump */
+ SND_SOC_DAPM_SUPPLY("Charge Pump", DA7213_CP_CTRL, DA7213_CP_EN_SHIFT,
+ DA7213_NO_INVERT, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("LINE"),
+};
+
+
+/*
+ * DAPM audio route definition
+ */
+
+static const struct snd_soc_dapm_route da7213_audio_map[] = {
+ /* Dest Connecting Widget source */
+
+ /* Input path */
+ {"MIC1", NULL, "Mic Bias 1"},
+ {"MIC2", NULL, "Mic Bias 2"},
+
+ {"Mic 1 Amp Source MUX", "Differential", "MIC1"},
+ {"Mic 1 Amp Source MUX", "MIC_P", "MIC1"},
+ {"Mic 1 Amp Source MUX", "MIC_N", "MIC1"},
+
+ {"Mic 2 Amp Source MUX", "Differential", "MIC2"},
+ {"Mic 2 Amp Source MUX", "MIC_P", "MIC2"},
+ {"Mic 2 Amp Source MUX", "MIC_N", "MIC2"},
+
+ {"Mic 1 PGA", NULL, "Mic 1 Amp Source MUX"},
+ {"Mic 2 PGA", NULL, "Mic 2 Amp Source MUX"},
+
+ {"Aux Left PGA", NULL, "AUXL"},
+ {"Aux Right PGA", NULL, "AUXR"},
+
+ {"Mixin Left", "Aux Left Switch", "Aux Left PGA"},
+ {"Mixin Left", "Mic 1 Switch", "Mic 1 PGA"},
+ {"Mixin Left", "Mic 2 Switch", "Mic 2 PGA"},
+ {"Mixin Left", "Mixin Right Switch", "Mixin Right PGA"},
+
+ {"Mixin Right", "Aux Right Switch", "Aux Right PGA"},
+ {"Mixin Right", "Mic 2 Switch", "Mic 2 PGA"},
+ {"Mixin Right", "Mic 1 Switch", "Mic 1 PGA"},
+ {"Mixin Right", "Mixin Left Switch", "Mixin Left PGA"},
+
+ {"Mixin Left PGA", NULL, "Mixin Left"},
+ {"ADC Left", NULL, "Mixin Left PGA"},
+
+ {"Mixin Right PGA", NULL, "Mixin Right"},
+ {"ADC Right", NULL, "Mixin Right PGA"},
+
+ {"DAI Left Source MUX", "ADC Left", "ADC Left"},
+ {"DAI Left Source MUX", "ADC Right", "ADC Right"},
+ {"DAI Left Source MUX", "DAI Input Left", "DAIINL"},
+ {"DAI Left Source MUX", "DAI Input Right", "DAIINR"},
+
+ {"DAI Right Source MUX", "ADC Left", "ADC Left"},
+ {"DAI Right Source MUX", "ADC Right", "ADC Right"},
+ {"DAI Right Source MUX", "DAI Input Left", "DAIINL"},
+ {"DAI Right Source MUX", "DAI Input Right", "DAIINR"},
+
+ {"DAIOUTL", NULL, "DAI Left Source MUX"},
+ {"DAIOUTR", NULL, "DAI Right Source MUX"},
+
+ {"DAIOUTL", NULL, "DAI"},
+ {"DAIOUTR", NULL, "DAI"},
+
+ /* Output path */
+ {"DAIINL", NULL, "DAI"},
+ {"DAIINR", NULL, "DAI"},
+
+ {"DAC Left Source MUX", "ADC Output Left", "ADC Left"},
+ {"DAC Left Source MUX", "ADC Output Right", "ADC Right"},
+ {"DAC Left Source MUX", "DAI Input Left", "DAIINL"},
+ {"DAC Left Source MUX", "DAI Input Right", "DAIINR"},
+
+ {"DAC Right Source MUX", "ADC Output Left", "ADC Left"},
+ {"DAC Right Source MUX", "ADC Output Right", "ADC Right"},
+ {"DAC Right Source MUX", "DAI Input Left", "DAIINL"},
+ {"DAC Right Source MUX", "DAI Input Right", "DAIINR"},
+
+ {"DAC Left", NULL, "DAC Left Source MUX"},
+ {"DAC Right", NULL, "DAC Right Source MUX"},
+
+ {"Mixout Left", "Aux Left Switch", "Aux Left PGA"},
+ {"Mixout Left", "Mixin Left Switch", "Mixin Left PGA"},
+ {"Mixout Left", "Mixin Right Switch", "Mixin Right PGA"},
+ {"Mixout Left", "DAC Left Switch", "DAC Left"},
+ {"Mixout Left", "Aux Left Invert Switch", "Aux Left PGA"},
+ {"Mixout Left", "Mixin Left Invert Switch", "Mixin Left PGA"},
+ {"Mixout Left", "Mixin Right Invert Switch", "Mixin Right PGA"},
+
+ {"Mixout Right", "Aux Right Switch", "Aux Right PGA"},
+ {"Mixout Right", "Mixin Right Switch", "Mixin Right PGA"},
+ {"Mixout Right", "Mixin Left Switch", "Mixin Left PGA"},
+ {"Mixout Right", "DAC Right Switch", "DAC Right"},
+ {"Mixout Right", "Aux Right Invert Switch", "Aux Right PGA"},
+ {"Mixout Right", "Mixin Right Invert Switch", "Mixin Right PGA"},
+ {"Mixout Right", "Mixin Left Invert Switch", "Mixin Left PGA"},
+
+ {"Mixout Left PGA", NULL, "Mixout Left"},
+ {"Mixout Right PGA", NULL, "Mixout Right"},
+
+ {"Headphone Left PGA", NULL, "Mixout Left PGA"},
+ {"Headphone Left PGA", NULL, "Charge Pump"},
+ {"HPL", NULL, "Headphone Left PGA"},
+
+ {"Headphone Right PGA", NULL, "Mixout Right PGA"},
+ {"Headphone Right PGA", NULL, "Charge Pump"},
+ {"HPR", NULL, "Headphone Right PGA"},
+
+ {"Lineout PGA", NULL, "Mixout Right PGA"},
+ {"LINE", NULL, "Lineout PGA"},
+};
+
+static struct reg_default da7213_reg_defaults[] = {
+ { DA7213_DIG_ROUTING_DAI, 0x10 },
+ { DA7213_SR, 0x0A },
+ { DA7213_REFERENCES, 0x80 },
+ { DA7213_PLL_FRAC_TOP, 0x00 },
+ { DA7213_PLL_FRAC_BOT, 0x00 },
+ { DA7213_PLL_INTEGER, 0x20 },
+ { DA7213_PLL_CTRL, 0x0C },
+ { DA7213_DAI_CLK_MODE, 0x01 },
+ { DA7213_DAI_CTRL, 0x08 },
+ { DA7213_DIG_ROUTING_DAC, 0x32 },
+ { DA7213_AUX_L_GAIN, 0x35 },
+ { DA7213_AUX_R_GAIN, 0x35 },
+ { DA7213_MIXIN_L_SELECT, 0x00 },
+ { DA7213_MIXIN_R_SELECT, 0x00 },
+ { DA7213_MIXIN_L_GAIN, 0x03 },
+ { DA7213_MIXIN_R_GAIN, 0x03 },
+ { DA7213_ADC_L_GAIN, 0x6F },
+ { DA7213_ADC_R_GAIN, 0x6F },
+ { DA7213_ADC_FILTERS1, 0x80 },
+ { DA7213_MIC_1_GAIN, 0x01 },
+ { DA7213_MIC_2_GAIN, 0x01 },
+ { DA7213_DAC_FILTERS5, 0x00 },
+ { DA7213_DAC_FILTERS2, 0x88 },
+ { DA7213_DAC_FILTERS3, 0x88 },
+ { DA7213_DAC_FILTERS4, 0x08 },
+ { DA7213_DAC_FILTERS1, 0x80 },
+ { DA7213_DAC_L_GAIN, 0x6F },
+ { DA7213_DAC_R_GAIN, 0x6F },
+ { DA7213_CP_CTRL, 0x61 },
+ { DA7213_HP_L_GAIN, 0x39 },
+ { DA7213_HP_R_GAIN, 0x39 },
+ { DA7213_LINE_GAIN, 0x30 },
+ { DA7213_MIXOUT_L_SELECT, 0x00 },
+ { DA7213_MIXOUT_R_SELECT, 0x00 },
+ { DA7213_SYSTEM_MODES_INPUT, 0x00 },
+ { DA7213_SYSTEM_MODES_OUTPUT, 0x00 },
+ { DA7213_AUX_L_CTRL, 0x44 },
+ { DA7213_AUX_R_CTRL, 0x44 },
+ { DA7213_MICBIAS_CTRL, 0x11 },
+ { DA7213_MIC_1_CTRL, 0x40 },
+ { DA7213_MIC_2_CTRL, 0x40 },
+ { DA7213_MIXIN_L_CTRL, 0x40 },
+ { DA7213_MIXIN_R_CTRL, 0x40 },
+ { DA7213_ADC_L_CTRL, 0x40 },
+ { DA7213_ADC_R_CTRL, 0x40 },
+ { DA7213_DAC_L_CTRL, 0x48 },
+ { DA7213_DAC_R_CTRL, 0x40 },
+ { DA7213_HP_L_CTRL, 0x41 },
+ { DA7213_HP_R_CTRL, 0x40 },
+ { DA7213_LINE_CTRL, 0x40 },
+ { DA7213_MIXOUT_L_CTRL, 0x10 },
+ { DA7213_MIXOUT_R_CTRL, 0x10 },
+ { DA7213_LDO_CTRL, 0x00 },
+ { DA7213_IO_CTRL, 0x00 },
+ { DA7213_GAIN_RAMP_CTRL, 0x00},
+ { DA7213_MIC_CONFIG, 0x00 },
+ { DA7213_PC_COUNT, 0x00 },
+ { DA7213_CP_VOL_THRESHOLD1, 0x32 },
+ { DA7213_CP_DELAY, 0x95 },
+ { DA7213_CP_DETECTOR, 0x00 },
+ { DA7213_DAI_OFFSET, 0x00 },
+ { DA7213_DIG_CTRL, 0x00 },
+ { DA7213_ALC_CTRL2, 0x00 },
+ { DA7213_ALC_CTRL3, 0x00 },
+ { DA7213_ALC_NOISE, 0x3F },
+ { DA7213_ALC_TARGET_MIN, 0x3F },
+ { DA7213_ALC_TARGET_MAX, 0x00 },
+ { DA7213_ALC_GAIN_LIMITS, 0xFF },
+ { DA7213_ALC_ANA_GAIN_LIMITS, 0x71 },
+ { DA7213_ALC_ANTICLIP_CTRL, 0x00 },
+ { DA7213_ALC_ANTICLIP_LEVEL, 0x00 },
+ { DA7213_ALC_OFFSET_MAN_M_L, 0x00 },
+ { DA7213_ALC_OFFSET_MAN_U_L, 0x00 },
+ { DA7213_ALC_OFFSET_MAN_M_R, 0x00 },
+ { DA7213_ALC_OFFSET_MAN_U_R, 0x00 },
+ { DA7213_ALC_CIC_OP_LVL_CTRL, 0x00 },
+ { DA7213_DAC_NG_SETUP_TIME, 0x00 },
+ { DA7213_DAC_NG_OFF_THRESHOLD, 0x00 },
+ { DA7213_DAC_NG_ON_THRESHOLD, 0x00 },
+ { DA7213_DAC_NG_CTRL, 0x00 },
+};
+
+static bool da7213_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA7213_STATUS1:
+ case DA7213_PLL_STATUS:
+ case DA7213_AUX_L_GAIN_STATUS:
+ case DA7213_AUX_R_GAIN_STATUS:
+ case DA7213_MIC_1_GAIN_STATUS:
+ case DA7213_MIC_2_GAIN_STATUS:
+ case DA7213_MIXIN_L_GAIN_STATUS:
+ case DA7213_MIXIN_R_GAIN_STATUS:
+ case DA7213_ADC_L_GAIN_STATUS:
+ case DA7213_ADC_R_GAIN_STATUS:
+ case DA7213_DAC_L_GAIN_STATUS:
+ case DA7213_DAC_R_GAIN_STATUS:
+ case DA7213_HP_L_GAIN_STATUS:
+ case DA7213_HP_R_GAIN_STATUS:
+ case DA7213_LINE_GAIN_STATUS:
+ case DA7213_ALC_CTRL1:
+ case DA7213_ALC_OFFSET_AUTO_M_L:
+ case DA7213_ALC_OFFSET_AUTO_U_L:
+ case DA7213_ALC_OFFSET_AUTO_M_R:
+ case DA7213_ALC_OFFSET_AUTO_U_R:
+ case DA7213_ALC_CIC_OP_LVL_DATA:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+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;
+ u8 dai_ctrl = 0;
+ u8 fs;
+
+ /* Set DAI format */
+ switch (params_width(params)) {
+ case 16:
+ dai_ctrl |= DA7213_DAI_WORD_LENGTH_S16_LE;
+ break;
+ case 20:
+ dai_ctrl |= DA7213_DAI_WORD_LENGTH_S20_LE;
+ break;
+ case 24:
+ dai_ctrl |= DA7213_DAI_WORD_LENGTH_S24_LE;
+ break;
+ case 32:
+ dai_ctrl |= DA7213_DAI_WORD_LENGTH_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set sampling rate */
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA7213_SR_8000;
+ break;
+ case 11025:
+ fs = DA7213_SR_11025;
+ break;
+ case 12000:
+ fs = DA7213_SR_12000;
+ break;
+ case 16000:
+ fs = DA7213_SR_16000;
+ break;
+ case 22050:
+ fs = DA7213_SR_22050;
+ break;
+ case 32000:
+ fs = DA7213_SR_32000;
+ break;
+ case 44100:
+ fs = DA7213_SR_44100;
+ break;
+ case 48000:
+ fs = DA7213_SR_48000;
+ break;
+ case 88200:
+ fs = DA7213_SR_88200;
+ break;
+ case 96000:
+ fs = DA7213_SR_96000;
+ 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);
+
+ 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);
+ u8 dai_clk_mode = 0, dai_ctrl = 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;
+ da7213->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ dai_clk_mode |= DA7213_DAI_CLK_EN_SLAVE_MODE;
+ da7213->master = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* 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;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7213_DAI_WCLK_POL_INV | DA7213_DAI_CLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Only I2S is supported */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dai_ctrl |= DA7213_DAI_FORMAT_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dai_ctrl |= DA7213_DAI_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dai_ctrl |= DA7213_DAI_FORMAT_RIGHT_J;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* By default only 32 BCLK per WCLK is supported */
+ dai_clk_mode |= DA7213_DAI_BCLKS_PER_WCLK_32;
+
+ snd_soc_write(codec, DA7213_DAI_CLK_MODE, dai_clk_mode);
+ snd_soc_update_bits(codec, DA7213_DAI_CTRL, DA7213_DAI_FORMAT_MASK,
+ dai_ctrl);
+
+ return 0;
+}
+
+static int da7213_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute) {
+ snd_soc_update_bits(codec, DA7213_DAC_L_CTRL,
+ DA7213_MUTE_EN, DA7213_MUTE_EN);
+ snd_soc_update_bits(codec, DA7213_DAC_R_CTRL,
+ DA7213_MUTE_EN, DA7213_MUTE_EN);
+ } else {
+ snd_soc_update_bits(codec, DA7213_DAC_L_CTRL,
+ DA7213_MUTE_EN, 0);
+ snd_soc_update_bits(codec, DA7213_DAC_R_CTRL,
+ DA7213_MUTE_EN, 0);
+ }
+
+ return 0;
+}
+
+#define DA7213_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int da7213_set_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 da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+
+ 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;
+ }
+ break;
+ default:
+ dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+}
+
+/* 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)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+
+ 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)) {
+ /* 32KHz PLL Mode */
+ indiv_bits = DA7213_PLL_INDIV_10_20_MHZ;
+ indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL;
+ 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;
+ } else if (da7213->mclk_rate <= 54000000) {
+ indiv_bits = DA7213_PLL_INDIV_40_54_MHZ;
+ indiv = DA7213_PLL_INDIV_40_54_MHZ_VAL;
+ } else {
+ goto pll_err;
+ }
+ freq_ref = (da7213->mclk_rate / indiv);
+ }
+
+ pll_ctrl |= indiv_bits;
+
+ /* PLL Bypass mode */
+ if (source == DA7213_SYSCLK_MCLK) {
+ snd_soc_write(codec, DA7213_PLL_CTRL, pll_ctrl);
+ return 0;
+ }
+
+ /*
+ * If Codec is slave and SRM enabled,
+ * freq_out is (98304000 + 90316800)/2 = 94310400
+ */
+ if (!da7213->master && da7213->srm_en) {
+ fout = DA7213_PLL_FREQ_OUT_94310400;
+ pll_ctrl |= DA7213_PLL_SRM_EN;
+ }
+
+ /* 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;
+ do_div(frac_div, freq_ref);
+ pll_frac_top = (frac_div >> DA7213_BYTE_SHIFT) & DA7213_BYTE_MASK;
+ pll_frac_bot = (frac_div) & DA7213_BYTE_MASK;
+
+ /* Write PLL dividers */
+ snd_soc_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);
+
+ /* Enable PLL */
+ pll_ctrl |= DA7213_PLL_EN;
+ snd_soc_write(codec, DA7213_PLL_CTRL, pll_ctrl);
+
+ return 0;
+
+pll_err:
+ dev_err(codec_dai->dev, "Unsupported PLL input frequency %d\n",
+ da7213->mclk_rate);
+ return -EINVAL;
+}
+
+/* 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,
+};
+
+static struct snd_soc_dai_driver da7213_dai = {
+ .name = "da7213-hifi",
+ /* Playback Capabilities */
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7213_FORMATS,
+ },
+ /* Capture Capabilities */
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7213_FORMATS,
+ },
+ .ops = &da7213_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int da7213_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ /* Enable VMID reference & master bias */
+ snd_soc_update_bits(codec, DA7213_REFERENCES,
+ DA7213_VMID_EN | DA7213_BIAS_EN,
+ DA7213_VMID_EN | DA7213_BIAS_EN);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Disable VMID reference & master bias */
+ snd_soc_update_bits(codec, 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)
+{
+ struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ struct da7213_platform_data *pdata = da7213->pdata;
+
+ /* Default to using ALC auto offset calibration mode. */
+ snd_soc_update_bits(codec, 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;
+
+ /* Enable all Gain Ramps */
+ snd_soc_update_bits(codec, DA7213_AUX_L_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_update_bits(codec, DA7213_AUX_R_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_update_bits(codec, DA7213_MIXIN_L_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_update_bits(codec, DA7213_MIXIN_R_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_update_bits(codec, DA7213_ADC_L_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_update_bits(codec, DA7213_ADC_R_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_update_bits(codec, DA7213_DAC_L_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_update_bits(codec, DA7213_DAC_R_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_update_bits(codec, DA7213_HP_L_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_update_bits(codec, DA7213_HP_R_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+ snd_soc_update_bits(codec, DA7213_LINE_CTRL,
+ DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
+
+ /*
+ * There are two separate control bits for input and output mixers as
+ * well as headphone and line outs.
+ * One to enable corresponding amplifier and other to enable its
+ * output. As amplifier bits are related to power control, they are
+ * being managed by DAPM while other (non power related) bits are
+ * enabled here
+ */
+ snd_soc_update_bits(codec, DA7213_MIXIN_L_CTRL,
+ DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN);
+ snd_soc_update_bits(codec, DA7213_MIXIN_R_CTRL,
+ DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN);
+
+ snd_soc_update_bits(codec, DA7213_MIXOUT_L_CTRL,
+ DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN);
+ snd_soc_update_bits(codec, DA7213_MIXOUT_R_CTRL,
+ DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN);
+
+ snd_soc_update_bits(codec, DA7213_HP_L_CTRL,
+ DA7213_HP_AMP_OE, DA7213_HP_AMP_OE);
+ snd_soc_update_bits(codec, DA7213_HP_R_CTRL,
+ DA7213_HP_AMP_OE, DA7213_HP_AMP_OE);
+
+ snd_soc_update_bits(codec, DA7213_LINE_CTRL,
+ DA7213_LINE_AMP_OE, DA7213_LINE_AMP_OE);
+
+ /* Set platform data values */
+ if (da7213->pdata) {
+ u8 micbias_lvl = 0, dmic_cfg = 0;
+
+ /* Set Mic Bias voltages */
+ switch (pdata->micbias1_lvl) {
+ case DA7213_MICBIAS_1_6V:
+ case DA7213_MICBIAS_2_2V:
+ case DA7213_MICBIAS_2_5V:
+ case DA7213_MICBIAS_3_0V:
+ micbias_lvl |= (pdata->micbias1_lvl <<
+ DA7213_MICBIAS1_LEVEL_SHIFT);
+ break;
+ }
+ switch (pdata->micbias2_lvl) {
+ case DA7213_MICBIAS_1_6V:
+ case DA7213_MICBIAS_2_2V:
+ case DA7213_MICBIAS_2_5V:
+ case DA7213_MICBIAS_3_0V:
+ micbias_lvl |= (pdata->micbias2_lvl <<
+ DA7213_MICBIAS2_LEVEL_SHIFT);
+ break;
+ }
+ snd_soc_update_bits(codec, DA7213_MICBIAS_CTRL,
+ DA7213_MICBIAS1_LEVEL_MASK |
+ DA7213_MICBIAS2_LEVEL_MASK, micbias_lvl);
+
+ /* Set DMIC configuration */
+ switch (pdata->dmic_data_sel) {
+ case DA7213_DMIC_DATA_LFALL_RRISE:
+ case DA7213_DMIC_DATA_LRISE_RFALL:
+ dmic_cfg |= (pdata->dmic_data_sel <<
+ DA7213_DMIC_DATA_SEL_SHIFT);
+ break;
+ }
+ switch (pdata->dmic_samplephase) {
+ case DA7213_DMIC_SAMPLE_ON_CLKEDGE:
+ case DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE:
+ dmic_cfg |= (pdata->dmic_samplephase <<
+ DA7213_DMIC_SAMPLEPHASE_SHIFT);
+ break;
+ }
+ switch (pdata->dmic_clk_rate) {
+ case DA7213_DMIC_CLK_3_0MHZ:
+ case DA7213_DMIC_CLK_1_5MHZ:
+ dmic_cfg |= (pdata->dmic_clk_rate <<
+ DA7213_DMIC_CLK_RATE_SHIFT);
+ break;
+ }
+ snd_soc_update_bits(codec, 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;
+ }
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_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),
+};
+
+static const struct regmap_config da7213_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = da7213_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da7213_reg_defaults),
+ .volatile_reg = da7213_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int da7213_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da7213_priv *da7213;
+ struct da7213_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ int ret;
+
+ da7213 = devm_kzalloc(&i2c->dev, sizeof(struct da7213_priv),
+ GFP_KERNEL);
+ if (!da7213)
+ return -ENOMEM;
+
+ if (pdata)
+ da7213->pdata = pdata;
+
+ i2c_set_clientdata(i2c, da7213);
+
+ da7213->regmap = devm_regmap_init_i2c(i2c, &da7213_regmap_config);
+ if (IS_ERR(da7213->regmap)) {
+ ret = PTR_ERR(da7213->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_da7213, &da7213_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register da7213 codec: %d\n",
+ ret);
+ }
+ return ret;
+}
+
+static int da7213_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id da7213_i2c_id[] = {
+ { "da7213", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da7213_i2c_id);
+
+/* I2C codec control layer */
+static struct i2c_driver da7213_i2c_driver = {
+ .driver = {
+ .name = "da7213",
+ .owner = THIS_MODULE,
+ },
+ .probe = da7213_i2c_probe,
+ .remove = da7213_remove,
+ .id_table = da7213_i2c_id,
+};
+
+module_i2c_driver(da7213_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC DA7213 Codec driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/da7213.h b/kernel/sound/soc/codecs/da7213.h
new file mode 100644
index 000000000..9cb9ddd01
--- /dev/null
+++ b/kernel/sound/soc/codecs/da7213.h
@@ -0,0 +1,523 @@
+/*
+ * 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.
+ */
+
+#ifndef _DA7213_H
+#define _DA7213_H
+
+#include <linux/regmap.h>
+#include <sound/da7213.h>
+
+/*
+ * Registers
+ */
+
+/* Status Registers */
+#define DA7213_STATUS1 0x02
+#define DA7213_PLL_STATUS 0x03
+#define DA7213_AUX_L_GAIN_STATUS 0x04
+#define DA7213_AUX_R_GAIN_STATUS 0x05
+#define DA7213_MIC_1_GAIN_STATUS 0x06
+#define DA7213_MIC_2_GAIN_STATUS 0x07
+#define DA7213_MIXIN_L_GAIN_STATUS 0x08
+#define DA7213_MIXIN_R_GAIN_STATUS 0x09
+#define DA7213_ADC_L_GAIN_STATUS 0x0A
+#define DA7213_ADC_R_GAIN_STATUS 0x0B
+#define DA7213_DAC_L_GAIN_STATUS 0x0C
+#define DA7213_DAC_R_GAIN_STATUS 0x0D
+#define DA7213_HP_L_GAIN_STATUS 0x0E
+#define DA7213_HP_R_GAIN_STATUS 0x0F
+#define DA7213_LINE_GAIN_STATUS 0x10
+
+/* System Initialisation Registers */
+#define DA7213_DIG_ROUTING_DAI 0x21
+#define DA7213_SR 0x22
+#define DA7213_REFERENCES 0x23
+#define DA7213_PLL_FRAC_TOP 0x24
+#define DA7213_PLL_FRAC_BOT 0x25
+#define DA7213_PLL_INTEGER 0x26
+#define DA7213_PLL_CTRL 0x27
+#define DA7213_DAI_CLK_MODE 0x28
+#define DA7213_DAI_CTRL 0x29
+#define DA7213_DIG_ROUTING_DAC 0x2A
+#define DA7213_ALC_CTRL1 0x2B
+
+/* Input - Gain, Select and Filter Registers */
+#define DA7213_AUX_L_GAIN 0x30
+#define DA7213_AUX_R_GAIN 0x31
+#define DA7213_MIXIN_L_SELECT 0x32
+#define DA7213_MIXIN_R_SELECT 0x33
+#define DA7213_MIXIN_L_GAIN 0x34
+#define DA7213_MIXIN_R_GAIN 0x35
+#define DA7213_ADC_L_GAIN 0x36
+#define DA7213_ADC_R_GAIN 0x37
+#define DA7213_ADC_FILTERS1 0x38
+#define DA7213_MIC_1_GAIN 0x39
+#define DA7213_MIC_2_GAIN 0x3A
+
+/* Output - Gain, Select and Filter Registers */
+#define DA7213_DAC_FILTERS5 0x40
+#define DA7213_DAC_FILTERS2 0x41
+#define DA7213_DAC_FILTERS3 0x42
+#define DA7213_DAC_FILTERS4 0x43
+#define DA7213_DAC_FILTERS1 0x44
+#define DA7213_DAC_L_GAIN 0x45
+#define DA7213_DAC_R_GAIN 0x46
+#define DA7213_CP_CTRL 0x47
+#define DA7213_HP_L_GAIN 0x48
+#define DA7213_HP_R_GAIN 0x49
+#define DA7213_LINE_GAIN 0x4A
+#define DA7213_MIXOUT_L_SELECT 0x4B
+#define DA7213_MIXOUT_R_SELECT 0x4C
+
+/* System Controller Registers */
+#define DA7213_SYSTEM_MODES_INPUT 0x50
+#define DA7213_SYSTEM_MODES_OUTPUT 0x51
+
+/* Control Registers */
+#define DA7213_AUX_L_CTRL 0x60
+#define DA7213_AUX_R_CTRL 0x61
+#define DA7213_MICBIAS_CTRL 0x62
+#define DA7213_MIC_1_CTRL 0x63
+#define DA7213_MIC_2_CTRL 0x64
+#define DA7213_MIXIN_L_CTRL 0x65
+#define DA7213_MIXIN_R_CTRL 0x66
+#define DA7213_ADC_L_CTRL 0x67
+#define DA7213_ADC_R_CTRL 0x68
+#define DA7213_DAC_L_CTRL 0x69
+#define DA7213_DAC_R_CTRL 0x6A
+#define DA7213_HP_L_CTRL 0x6B
+#define DA7213_HP_R_CTRL 0x6C
+#define DA7213_LINE_CTRL 0x6D
+#define DA7213_MIXOUT_L_CTRL 0x6E
+#define DA7213_MIXOUT_R_CTRL 0x6F
+
+/* Configuration Registers */
+#define DA7213_LDO_CTRL 0x90
+#define DA7213_IO_CTRL 0x91
+#define DA7213_GAIN_RAMP_CTRL 0x92
+#define DA7213_MIC_CONFIG 0x93
+#define DA7213_PC_COUNT 0x94
+#define DA7213_CP_VOL_THRESHOLD1 0x95
+#define DA7213_CP_DELAY 0x96
+#define DA7213_CP_DETECTOR 0x97
+#define DA7213_DAI_OFFSET 0x98
+#define DA7213_DIG_CTRL 0x99
+#define DA7213_ALC_CTRL2 0x9A
+#define DA7213_ALC_CTRL3 0x9B
+#define DA7213_ALC_NOISE 0x9C
+#define DA7213_ALC_TARGET_MIN 0x9D
+#define DA7213_ALC_TARGET_MAX 0x9E
+#define DA7213_ALC_GAIN_LIMITS 0x9F
+#define DA7213_ALC_ANA_GAIN_LIMITS 0xA0
+#define DA7213_ALC_ANTICLIP_CTRL 0xA1
+#define DA7213_ALC_ANTICLIP_LEVEL 0xA2
+
+#define DA7213_ALC_OFFSET_AUTO_M_L 0xA3
+#define DA7213_ALC_OFFSET_AUTO_U_L 0xA4
+#define DA7213_ALC_OFFSET_MAN_M_L 0xA6
+#define DA7213_ALC_OFFSET_MAN_U_L 0xA7
+#define DA7213_ALC_OFFSET_AUTO_M_R 0xA8
+#define DA7213_ALC_OFFSET_AUTO_U_R 0xA9
+#define DA7213_ALC_OFFSET_MAN_M_R 0xAB
+#define DA7213_ALC_OFFSET_MAN_U_R 0xAC
+#define DA7213_ALC_CIC_OP_LVL_CTRL 0xAD
+#define DA7213_ALC_CIC_OP_LVL_DATA 0xAE
+#define DA7213_DAC_NG_SETUP_TIME 0xAF
+#define DA7213_DAC_NG_OFF_THRESHOLD 0xB0
+#define DA7213_DAC_NG_ON_THRESHOLD 0xB1
+#define DA7213_DAC_NG_CTRL 0xB2
+
+
+/*
+ * Bit fields
+ */
+
+/* DA7213_SR = 0x22 */
+#define DA7213_SR_8000 (0x1 << 0)
+#define DA7213_SR_11025 (0x2 << 0)
+#define DA7213_SR_12000 (0x3 << 0)
+#define DA7213_SR_16000 (0x5 << 0)
+#define DA7213_SR_22050 (0x6 << 0)
+#define DA7213_SR_24000 (0x7 << 0)
+#define DA7213_SR_32000 (0x9 << 0)
+#define DA7213_SR_44100 (0xA << 0)
+#define DA7213_SR_48000 (0xB << 0)
+#define DA7213_SR_88200 (0xE << 0)
+#define DA7213_SR_96000 (0xF << 0)
+
+/* DA7213_REFERENCES = 0x23 */
+#define DA7213_BIAS_EN (0x1 << 3)
+#define DA7213_VMID_EN (0x1 << 7)
+
+/* DA7213_PLL_CTRL = 0x27 */
+#define DA7213_PLL_INDIV_5_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_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)
+
+/* DA7213_DAI_CLK_MODE = 0x28 */
+#define DA7213_DAI_BCLKS_PER_WCLK_32 (0x0 << 0)
+#define DA7213_DAI_BCLKS_PER_WCLK_64 (0x1 << 0)
+#define DA7213_DAI_BCLKS_PER_WCLK_128 (0x2 << 0)
+#define DA7213_DAI_BCLKS_PER_WCLK_256 (0x3 << 0)
+#define DA7213_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
+#define DA7213_DAI_CLK_POL_INV (0x1 << 2)
+#define DA7213_DAI_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_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_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_EN_SHIFT 7
+
+/* DA7213_DIG_ROUTING_DAI = 0x21 */
+#define DA7213_DAI_L_SRC_SHIFT 0
+#define DA7213_DAI_R_SRC_SHIFT 4
+#define DA7213_DAI_SRC_MAX 4
+
+/* DA7213_DIG_ROUTING_DAC = 0x2A */
+#define DA7213_DAC_L_SRC_SHIFT 0
+#define DA7213_DAC_L_MONO_SHIFT 3
+#define DA7213_DAC_R_SRC_SHIFT 4
+#define DA7213_DAC_R_MONO_SHIFT 7
+#define DA7213_DAC_SRC_MAX 4
+#define DA7213_DAC_MONO_MAX 0x1
+
+/* DA7213_ALC_CTRL1 = 0x2B */
+#define DA7213_ALC_OFFSET_EN_SHIFT 0
+#define DA7213_ALC_OFFSET_EN_MAX 0x1
+#define DA7213_ALC_OFFSET_EN (0x1 << 0)
+#define DA7213_ALC_SYNC_MODE (0x1 << 1)
+#define DA7213_ALC_CALIB_MODE_MAN (0x1 << 2)
+#define DA7213_ALC_L_EN_SHIFT 3
+#define DA7213_ALC_AUTO_CALIB_EN (0x1 << 4)
+#define DA7213_ALC_CALIB_OVERFLOW (0x1 << 5)
+#define DA7213_ALC_R_EN_SHIFT 7
+#define DA7213_ALC_EN_MAX 0x1
+
+/* DA7213_AUX_L/R_GAIN = 0x30/0x31 */
+#define DA7213_AUX_AMP_GAIN_SHIFT 0
+#define DA7213_AUX_AMP_GAIN_MAX 0x3F
+
+/* DA7213_MIXIN_L/R_SELECT = 0x32/0x33 */
+#define DA7213_DMIC_EN_SHIFT 7
+#define DA7213_DMIC_EN_MAX 0x1
+
+/* DA7213_MIXIN_L_SELECT = 0x32 */
+#define DA7213_MIXIN_L_MIX_SELECT_AUX_L_SHIFT 0
+#define DA7213_MIXIN_L_MIX_SELECT_MIC_1_SHIFT 1
+#define DA7213_MIXIN_L_MIX_SELECT_MIC_1 (0x1 << 1)
+#define DA7213_MIXIN_L_MIX_SELECT_MIC_2_SHIFT 2
+#define DA7213_MIXIN_L_MIX_SELECT_MIC_2 (0x1 << 2)
+#define DA7213_MIXIN_L_MIX_SELECT_MIXIN_R_SHIFT 3
+#define DA7213_MIXIN_L_MIX_SELECT_MAX 0x1
+
+/* DA7213_MIXIN_R_SELECT = 0x33 */
+#define DA7213_MIXIN_R_MIX_SELECT_AUX_R_SHIFT 0
+#define DA7213_MIXIN_R_MIX_SELECT_MIC_2_SHIFT 1
+#define DA7213_MIXIN_R_MIX_SELECT_MIC_2 (0x1 << 1)
+#define DA7213_MIXIN_R_MIX_SELECT_MIC_1_SHIFT 2
+#define DA7213_MIXIN_R_MIX_SELECT_MIC_1 (0x1 << 2)
+#define DA7213_MIXIN_R_MIX_SELECT_MIXIN_L_SHIFT 3
+#define DA7213_MIXIN_R_MIX_SELECT_MAX 0x1
+#define DA7213_MIC_BIAS_OUTPUT_SELECT_2 (0x1 << 6)
+
+/* DA7213_MIXIN_L/R_GAIN = 0x34/0x35 */
+#define DA7213_MIXIN_AMP_GAIN_SHIFT 0
+#define DA7213_MIXIN_AMP_GAIN_MAX 0xF
+
+/* DA7213_ADC_L/R_GAIN = 0x36/0x37 */
+#define DA7213_ADC_AMP_GAIN_SHIFT 0
+#define DA7213_ADC_AMP_GAIN_MAX 0x7F
+
+/* DA7213_ADC/DAC_FILTERS1 = 0x38/0x44 */
+#define DA7213_VOICE_HPF_CORNER_SHIFT 0
+#define DA7213_VOICE_HPF_CORNER_MAX 8
+#define DA7213_VOICE_EN_SHIFT 3
+#define DA7213_VOICE_EN_MAX 0x1
+#define DA7213_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7213_AUDIO_HPF_CORNER_MAX 4
+#define DA7213_HPF_EN_SHIFT 7
+#define DA7213_HPF_EN_MAX 0x1
+
+/* DA7213_MIC_1/2_GAIN = 0x39/0x3A */
+#define DA7213_MIC_AMP_GAIN_SHIFT 0
+#define DA7213_MIC_AMP_GAIN_MAX 0x7
+
+/* DA7213_DAC_FILTERS5 = 0x40 */
+#define DA7213_DAC_SOFTMUTE_EN_SHIFT 7
+#define DA7213_DAC_SOFTMUTE_EN_MAX 0x1
+#define DA7213_DAC_SOFTMUTE_RATE_SHIFT 4
+#define DA7213_DAC_SOFTMUTE_RATE_MAX 7
+
+/* DA7213_DAC_FILTERS2/3/4 = 0x41/0x42/0x43 */
+#define DA7213_DAC_EQ_BAND_MAX 0xF
+
+/* DA7213_DAC_FILTERS2 = 0x41 */
+#define DA7213_DAC_EQ_BAND1_SHIFT 0
+#define DA7213_DAC_EQ_BAND2_SHIFT 4
+
+/* DA7213_DAC_FILTERS2 = 0x42 */
+#define DA7213_DAC_EQ_BAND3_SHIFT 0
+#define DA7213_DAC_EQ_BAND4_SHIFT 4
+
+/* DA7213_DAC_FILTERS4 = 0x43 */
+#define DA7213_DAC_EQ_BAND5_SHIFT 0
+#define DA7213_DAC_EQ_EN_SHIFT 7
+#define DA7213_DAC_EQ_EN_MAX 0x1
+
+/* DA7213_DAC_L/R_GAIN = 0x45/0x46 */
+#define DA7213_DAC_AMP_GAIN_SHIFT 0
+#define DA7213_DAC_AMP_GAIN_MAX 0x7F
+
+/* DA7213_HP_L/R_GAIN = 0x45/0x46 */
+#define DA7213_HP_AMP_GAIN_SHIFT 0
+#define DA7213_HP_AMP_GAIN_MAX 0x3F
+
+/* DA7213_CP_CTRL = 0x47 */
+#define DA7213_CP_EN_SHIFT 7
+
+/* DA7213_LINE_GAIN = 0x4A */
+#define DA7213_LINE_AMP_GAIN_SHIFT 0
+#define DA7213_LINE_AMP_GAIN_MAX 0x3F
+
+/* DA7213_MIXOUT_L_SELECT = 0x4B */
+#define DA7213_MIXOUT_L_MIX_SELECT_AUX_L_SHIFT 0
+#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_SHIFT 1
+#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_SHIFT 2
+#define DA7213_MIXOUT_L_MIX_SELECT_DAC_L_SHIFT 3
+#define DA7213_MIXOUT_L_MIX_SELECT_AUX_L_INVERTED_SHIFT 4
+#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_INVERTED_SHIFT 5
+#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_INVERTED_SHIFT 6
+#define DA7213_MIXOUT_L_MIX_SELECT_MAX 0x1
+
+/* DA7213_MIXOUT_R_SELECT = 0x4C */
+#define DA7213_MIXOUT_R_MIX_SELECT_AUX_R_SHIFT 0
+#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_SHIFT 1
+#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_SHIFT 2
+#define DA7213_MIXOUT_R_MIX_SELECT_DAC_R_SHIFT 3
+#define DA7213_MIXOUT_R_MIX_SELECT_AUX_R_INVERTED_SHIFT 4
+#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_INVERTED_SHIFT 5
+#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_INVERTED_SHIFT 6
+#define DA7213_MIXOUT_R_MIX_SELECT_MAX 0x1
+
+/*
+ * DA7213_AUX_L/R_CTRL = 0x60/0x61,
+ * DA7213_MIC_1/2_CTRL = 0x63/0x64,
+ * DA7213_MIXIN_L/R_CTRL = 0x65/0x66,
+ * DA7213_ADC_L/R_CTRL = 0x65/0x66,
+ * DA7213_DAC_L/R_CTRL = 0x69/0x6A,
+ * DA7213_HP_L/R_CTRL = 0x6B/0x6C,
+ * DA7213_LINE_CTRL = 0x6D
+ */
+#define DA7213_MUTE_EN_SHIFT 6
+#define DA7213_MUTE_EN_MAX 0x1
+#define DA7213_MUTE_EN (0x1 << 6)
+
+/*
+ * DA7213_AUX_L/R_CTRL = 0x60/0x61,
+ * DA7213_MIXIN_L/R_CTRL = 0x65/0x66,
+ * DA7213_ADC_L/R_CTRL = 0x65/0x66,
+ * DA7213_DAC_L/R_CTRL = 0x69/0x6A,
+ * DA7213_HP_L/R_CTRL = 0x6B/0x6C,
+ * DA7213_LINE_CTRL = 0x6D
+ */
+#define DA7213_GAIN_RAMP_EN_SHIFT 5
+#define DA7213_GAIN_RAMP_EN_MAX 0x1
+#define DA7213_GAIN_RAMP_EN (0x1 << 5)
+
+/*
+ * DA7213_AUX_L/R_CTRL = 0x60/0x61,
+ * DA7213_MIXIN_L/R_CTRL = 0x65/0x66,
+ * DA7213_HP_L/R_CTRL = 0x6B/0x6C,
+ * DA7213_LINE_CTRL = 0x6D
+ */
+#define DA7213_ZC_EN_SHIFT 4
+#define DA7213_ZC_EN_MAX 0x1
+
+/*
+ * DA7213_AUX_L/R_CTRL = 0x60/0x61,
+ * DA7213_MIC_1/2_CTRL = 0x63/0x64,
+ * DA7213_MIXIN_L/R_CTRL = 0x65/0x66,
+ * DA7213_HP_L/R_CTRL = 0x6B/0x6C,
+ * DA7213_MIXOUT_L/R_CTRL = 0x6E/0x6F,
+ * DA7213_LINE_CTRL = 0x6D
+ */
+#define DA7213_AMP_EN_SHIFT 7
+
+/* DA7213_MIC_1/2_CTRL = 0x63/0x64 */
+#define DA7213_MIC_AMP_IN_SEL_SHIFT 2
+#define DA7213_MIC_AMP_IN_SEL_MAX 3
+
+/* DA7213_MICBIAS_CTRL = 0x62 */
+#define DA7213_MICBIAS1_LEVEL_SHIFT 0
+#define DA7213_MICBIAS1_LEVEL_MASK (0x3 << 0)
+#define DA7213_MICBIAS1_EN_SHIFT 3
+#define DA7213_MICBIAS2_LEVEL_SHIFT 4
+#define DA7213_MICBIAS2_LEVEL_MASK (0x3 << 4)
+#define DA7213_MICBIAS2_EN_SHIFT 7
+
+/* DA7213_MIXIN_L/R_CTRL = 0x65/0x66 */
+#define DA7213_MIXIN_MIX_EN (0x1 << 3)
+
+/* DA7213_ADC_L/R_CTRL = 0x67/0x68 */
+#define DA7213_ADC_EN_SHIFT 7
+#define DA7213_ADC_EN (0x1 << 7)
+
+/* DA7213_DAC_L/R_CTRL = 0x69/0x6A*/
+#define DA7213_DAC_EN_SHIFT 7
+
+/* DA7213_HP_L/R_CTRL = 0x6B/0x6C */
+#define DA7213_HP_AMP_OE (0x1 << 3)
+
+/* DA7213_LINE_CTRL = 0x6D */
+#define DA7213_LINE_AMP_OE (0x1 << 3)
+
+/* DA7213_MIXOUT_L/R_CTRL = 0x6E/0x6F */
+#define DA7213_MIXOUT_MIX_EN (0x1 << 3)
+
+/* DA7213_GAIN_RAMP_CTRL = 0x92 */
+#define DA7213_GAIN_RAMP_RATE_SHIFT 0
+#define DA7213_GAIN_RAMP_RATE_MAX 4
+
+/* DA7213_MIC_CONFIG = 0x93 */
+#define DA7213_DMIC_DATA_SEL_SHIFT 0
+#define DA7213_DMIC_DATA_SEL_MASK (0x1 << 0)
+#define DA7213_DMIC_SAMPLEPHASE_SHIFT 1
+#define DA7213_DMIC_SAMPLEPHASE_MASK (0x1 << 1)
+#define DA7213_DMIC_CLK_RATE_SHIFT 2
+#define DA7213_DMIC_CLK_RATE_MASK (0x1 << 2)
+
+/* DA7213_DIG_CTRL = 0x99 */
+#define DA7213_DAC_L_INV_SHIFT 3
+#define DA7213_DAC_R_INV_SHIFT 7
+#define DA7213_DAC_INV_MAX 0x1
+
+/* DA7213_ALC_CTRL2 = 0x9A */
+#define DA7213_ALC_ATTACK_SHIFT 0
+#define DA7213_ALC_ATTACK_MAX 13
+#define DA7213_ALC_RELEASE_SHIFT 4
+#define DA7213_ALC_RELEASE_MAX 11
+
+/* DA7213_ALC_CTRL3 = 0x9B */
+#define DA7213_ALC_HOLD_SHIFT 0
+#define DA7213_ALC_HOLD_MAX 16
+#define DA7213_ALC_INTEG_ATTACK_SHIFT 4
+#define DA7213_ALC_INTEG_RELEASE_SHIFT 6
+#define DA7213_ALC_INTEG_MAX 4
+
+/*
+ * DA7213_ALC_NOISE = 0x9C,
+ * DA7213_ALC_TARGET_MIN/MAX = 0x9D/0x9E
+ */
+#define DA7213_ALC_THRESHOLD_SHIFT 0
+#define DA7213_ALC_THRESHOLD_MAX 0x3F
+
+/* DA7213_ALC_GAIN_LIMITS = 0x9F */
+#define DA7213_ALC_ATTEN_MAX_SHIFT 0
+#define DA7213_ALC_GAIN_MAX_SHIFT 4
+#define DA7213_ALC_ATTEN_GAIN_MAX_MAX 0xF
+
+/* DA7213_ALC_ANA_GAIN_LIMITS = 0xA0 */
+#define DA7213_ALC_ANA_GAIN_MIN_SHIFT 0
+#define DA7213_ALC_ANA_GAIN_MAX_SHIFT 4
+#define DA7213_ALC_ANA_GAIN_MAX 0x7
+
+/* DA7213_ALC_ANTICLIP_CTRL = 0xA1 */
+#define DA7213_ALC_ANTICLIP_EN_SHIFT 7
+#define DA7213_ALC_ANTICLIP_EN_MAX 0x1
+
+/* DA7213_ALC_ANTICLIP_LEVEL = 0xA2 */
+#define DA7213_ALC_ANTICLIP_LEVEL_SHIFT 0
+#define DA7213_ALC_ANTICLIP_LEVEL_MAX 0x7F
+
+/* DA7213_ALC_CIC_OP_LVL_CTRL = 0xAD */
+#define DA7213_ALC_DATA_MIDDLE (0x2 << 0)
+#define DA7213_ALC_DATA_TOP (0x3 << 0)
+#define DA7213_ALC_CIC_OP_CHANNEL_LEFT (0x0 << 7)
+#define DA7213_ALC_CIC_OP_CHANNEL_RIGHT (0x1 << 7)
+
+/* DA7213_DAC_NG_SETUP_TIME = 0xAF */
+#define DA7213_DAC_NG_SETUP_TIME_SHIFT 0
+#define DA7213_DAC_NG_SETUP_TIME_MAX 4
+#define DA7213_DAC_NG_RAMPUP_RATE_SHIFT 2
+#define DA7213_DAC_NG_RAMPDN_RATE_SHIFT 3
+#define DA7213_DAC_NG_RAMP_RATE_MAX 2
+
+/* DA7213_DAC_NG_OFF/ON_THRESH = 0xB0/0xB1 */
+#define DA7213_DAC_NG_THRESHOLD_SHIFT 0
+#define DA7213_DAC_NG_THRESHOLD_MAX 0x7
+
+/* DA7213_DAC_NG_CTRL = 0xB2 */
+#define DA7213_DAC_NG_EN_SHIFT 7
+#define DA7213_DAC_NG_EN_MAX 0x1
+
+
+/*
+ * General defines
+ */
+
+/* Register inversion */
+#define DA7213_NO_INVERT 0
+#define DA7213_INVERT 1
+
+/* Byte related defines */
+#define DA7213_BYTE_SHIFT 8
+#define DA7213_BYTE_MASK 0xFF
+
+/* ALC related */
+#define DA7213_ALC_OFFSET_15_8 0x00FF00
+#define DA7213_ALC_OFFSET_19_16 0x0F0000
+#define DA7213_ALC_AVG_ITERATIONS 5
+
+/* PLL related */
+#define DA7213_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
+};
+
+/* Codec private data */
+struct da7213_priv {
+ struct regmap *regmap;
+ unsigned int mclk_rate;
+ bool master;
+ bool mclk_squarer_en;
+ bool srm_en;
+ bool alc_calib_auto;
+ bool alc_en;
+ struct da7213_platform_data *pdata;
+};
+
+#endif /* _DA7213_H */
diff --git a/kernel/sound/soc/codecs/da732x.c b/kernel/sound/soc/codecs/da732x.c
new file mode 100644
index 000000000..911c26c70
--- /dev/null
+++ b/kernel/sound/soc/codecs/da732x.c
@@ -0,0 +1,1589 @@
+/*
+ * 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>
+#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/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sysfs.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 <asm/div64.h>
+
+#include "da732x.h"
+#include "da732x_reg.h"
+
+
+struct da732x_priv {
+ struct regmap *regmap;
+
+ unsigned int sysclk;
+ bool pll_en;
+};
+
+/*
+ * da732x register cache - default settings
+ */
+static struct reg_default da732x_reg_cache[] = {
+ { DA732X_REG_REF1 , 0x02 },
+ { DA732X_REG_BIAS_EN , 0x80 },
+ { DA732X_REG_BIAS1 , 0x00 },
+ { DA732X_REG_BIAS2 , 0x00 },
+ { DA732X_REG_BIAS3 , 0x00 },
+ { DA732X_REG_BIAS4 , 0x00 },
+ { DA732X_REG_MICBIAS2 , 0x00 },
+ { DA732X_REG_MICBIAS1 , 0x00 },
+ { DA732X_REG_MICDET , 0x00 },
+ { DA732X_REG_MIC1_PRE , 0x01 },
+ { DA732X_REG_MIC1 , 0x40 },
+ { DA732X_REG_MIC2_PRE , 0x01 },
+ { DA732X_REG_MIC2 , 0x40 },
+ { DA732X_REG_AUX1L , 0x75 },
+ { DA732X_REG_AUX1R , 0x75 },
+ { DA732X_REG_MIC3_PRE , 0x01 },
+ { DA732X_REG_MIC3 , 0x40 },
+ { DA732X_REG_INP_PINBIAS , 0x00 },
+ { DA732X_REG_INP_ZC_EN , 0x00 },
+ { DA732X_REG_INP_MUX , 0x50 },
+ { DA732X_REG_HP_DET , 0x00 },
+ { DA732X_REG_HPL_DAC_OFFSET , 0x00 },
+ { DA732X_REG_HPL_DAC_OFF_CNTL , 0x00 },
+ { DA732X_REG_HPL_OUT_OFFSET , 0x00 },
+ { DA732X_REG_HPL , 0x40 },
+ { DA732X_REG_HPL_VOL , 0x0F },
+ { DA732X_REG_HPR_DAC_OFFSET , 0x00 },
+ { DA732X_REG_HPR_DAC_OFF_CNTL , 0x00 },
+ { DA732X_REG_HPR_OUT_OFFSET , 0x00 },
+ { DA732X_REG_HPR , 0x40 },
+ { DA732X_REG_HPR_VOL , 0x0F },
+ { DA732X_REG_LIN2 , 0x4F },
+ { DA732X_REG_LIN3 , 0x4F },
+ { DA732X_REG_LIN4 , 0x4F },
+ { DA732X_REG_OUT_ZC_EN , 0x00 },
+ { DA732X_REG_HP_LIN1_GNDSEL , 0x00 },
+ { DA732X_REG_CP_HP1 , 0x0C },
+ { DA732X_REG_CP_HP2 , 0x03 },
+ { DA732X_REG_CP_CTRL1 , 0x00 },
+ { DA732X_REG_CP_CTRL2 , 0x99 },
+ { DA732X_REG_CP_CTRL3 , 0x25 },
+ { DA732X_REG_CP_LEVEL_MASK , 0x3F },
+ { DA732X_REG_CP_DET , 0x00 },
+ { DA732X_REG_CP_STATUS , 0x00 },
+ { DA732X_REG_CP_THRESH1 , 0x00 },
+ { DA732X_REG_CP_THRESH2 , 0x00 },
+ { DA732X_REG_CP_THRESH3 , 0x00 },
+ { DA732X_REG_CP_THRESH4 , 0x00 },
+ { DA732X_REG_CP_THRESH5 , 0x00 },
+ { DA732X_REG_CP_THRESH6 , 0x00 },
+ { DA732X_REG_CP_THRESH7 , 0x00 },
+ { DA732X_REG_CP_THRESH8 , 0x00 },
+ { DA732X_REG_PLL_DIV_LO , 0x00 },
+ { DA732X_REG_PLL_DIV_MID , 0x00 },
+ { DA732X_REG_PLL_DIV_HI , 0x00 },
+ { DA732X_REG_PLL_CTRL , 0x02 },
+ { DA732X_REG_CLK_CTRL , 0xaa },
+ { DA732X_REG_CLK_DSP , 0x07 },
+ { DA732X_REG_CLK_EN1 , 0x00 },
+ { DA732X_REG_CLK_EN2 , 0x00 },
+ { DA732X_REG_CLK_EN3 , 0x00 },
+ { DA732X_REG_CLK_EN4 , 0x00 },
+ { DA732X_REG_CLK_EN5 , 0x00 },
+ { DA732X_REG_AIF_MCLK , 0x00 },
+ { DA732X_REG_AIFA1 , 0x02 },
+ { DA732X_REG_AIFA2 , 0x00 },
+ { DA732X_REG_AIFA3 , 0x08 },
+ { DA732X_REG_AIFB1 , 0x02 },
+ { DA732X_REG_AIFB2 , 0x00 },
+ { DA732X_REG_AIFB3 , 0x08 },
+ { DA732X_REG_PC_CTRL , 0xC0 },
+ { DA732X_REG_DATA_ROUTE , 0x00 },
+ { DA732X_REG_DSP_CTRL , 0x00 },
+ { DA732X_REG_CIF_CTRL2 , 0x00 },
+ { DA732X_REG_HANDSHAKE , 0x00 },
+ { DA732X_REG_SPARE1_OUT , 0x00 },
+ { DA732X_REG_SPARE2_OUT , 0x00 },
+ { DA732X_REG_SPARE1_IN , 0x00 },
+ { DA732X_REG_ADC1_PD , 0x00 },
+ { DA732X_REG_ADC1_HPF , 0x00 },
+ { DA732X_REG_ADC1_SEL , 0x00 },
+ { DA732X_REG_ADC1_EQ12 , 0x00 },
+ { DA732X_REG_ADC1_EQ34 , 0x00 },
+ { DA732X_REG_ADC1_EQ5 , 0x00 },
+ { DA732X_REG_ADC2_PD , 0x00 },
+ { DA732X_REG_ADC2_HPF , 0x00 },
+ { DA732X_REG_ADC2_SEL , 0x00 },
+ { DA732X_REG_ADC2_EQ12 , 0x00 },
+ { DA732X_REG_ADC2_EQ34 , 0x00 },
+ { DA732X_REG_ADC2_EQ5 , 0x00 },
+ { DA732X_REG_DAC1_HPF , 0x00 },
+ { DA732X_REG_DAC1_L_VOL , 0x00 },
+ { DA732X_REG_DAC1_R_VOL , 0x00 },
+ { DA732X_REG_DAC1_SEL , 0x00 },
+ { DA732X_REG_DAC1_SOFTMUTE , 0x00 },
+ { DA732X_REG_DAC1_EQ12 , 0x00 },
+ { DA732X_REG_DAC1_EQ34 , 0x00 },
+ { DA732X_REG_DAC1_EQ5 , 0x00 },
+ { DA732X_REG_DAC2_HPF , 0x00 },
+ { DA732X_REG_DAC2_L_VOL , 0x00 },
+ { DA732X_REG_DAC2_R_VOL , 0x00 },
+ { DA732X_REG_DAC2_SEL , 0x00 },
+ { DA732X_REG_DAC2_SOFTMUTE , 0x00 },
+ { DA732X_REG_DAC2_EQ12 , 0x00 },
+ { DA732X_REG_DAC2_EQ34 , 0x00 },
+ { DA732X_REG_DAC2_EQ5 , 0x00 },
+ { DA732X_REG_DAC3_HPF , 0x00 },
+ { DA732X_REG_DAC3_VOL , 0x00 },
+ { DA732X_REG_DAC3_SEL , 0x00 },
+ { DA732X_REG_DAC3_SOFTMUTE , 0x00 },
+ { DA732X_REG_DAC3_EQ12 , 0x00 },
+ { DA732X_REG_DAC3_EQ34 , 0x00 },
+ { DA732X_REG_DAC3_EQ5 , 0x00 },
+ { DA732X_REG_BIQ_BYP , 0x00 },
+ { DA732X_REG_DMA_CMD , 0x00 },
+ { DA732X_REG_DMA_ADDR0 , 0x00 },
+ { DA732X_REG_DMA_ADDR1 , 0x00 },
+ { DA732X_REG_DMA_DATA0 , 0x00 },
+ { DA732X_REG_DMA_DATA1 , 0x00 },
+ { DA732X_REG_DMA_DATA2 , 0x00 },
+ { DA732X_REG_DMA_DATA3 , 0x00 },
+ { DA732X_REG_UNLOCK , 0x00 },
+};
+
+static inline int da732x_get_input_div(struct snd_soc_codec *codec, int sysclk)
+{
+ int val;
+ int ret;
+
+ if (sysclk < DA732X_MCLK_10MHZ) {
+ val = DA732X_MCLK_RET_0_10MHZ;
+ ret = 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;
+ } else if ((sysclk >= DA732X_MCLK_20MHZ) &&
+ (sysclk < DA732X_MCLK_40MHZ)) {
+ val = DA732X_MCLK_RET_20_40MHZ;
+ ret = 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;
+ } else {
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, DA732X_REG_PLL_CTRL, val);
+
+ return ret;
+}
+
+static void da732x_set_charge_pump(struct snd_soc_codec *codec, 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 |
+ DA732X_HP_CP_REG | DA732X_HP_CP_PULSESKIP);
+ snd_soc_write(codec, DA732X_REG_CP_CTRL1, DA732X_CP_EN |
+ DA732X_CP_CTRL_CPVDD1);
+ snd_soc_write(codec, DA732X_REG_CP_CTRL2,
+ DA732X_CP_MANAGE_MAGNITUDE | DA732X_CP_BOOST);
+ snd_soc_write(codec, 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);
+ break;
+ default:
+ pr_err("Wrong charge pump state\n");
+ break;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, DA732X_MIC_PRE_VOL_DB_MIN,
+ DA732X_MIC_PRE_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(mic_pga_tlv, DA732X_MIC_VOL_DB_MIN,
+ DA732X_MIC_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(aux_pga_tlv, DA732X_AUX_VOL_DB_MIN,
+ DA732X_AUX_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(hp_pga_tlv, DA732X_HP_VOL_DB_MIN,
+ DA732X_AUX_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(lin2_pga_tlv, DA732X_LIN2_VOL_DB_MIN,
+ DA732X_LIN2_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(lin3_pga_tlv, DA732X_LIN3_VOL_DB_MIN,
+ DA732X_LIN3_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(lin4_pga_tlv, DA732X_LIN4_VOL_DB_MIN,
+ DA732X_LIN4_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(adc_pga_tlv, DA732X_ADC_VOL_DB_MIN,
+ DA732X_ADC_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(dac_pga_tlv, DA732X_DAC_VOL_DB_MIN,
+ DA732X_DAC_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(eq_band_pga_tlv, DA732X_EQ_BAND_VOL_DB_MIN,
+ DA732X_EQ_BAND_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(eq_overall_tlv, DA732X_EQ_OVERALL_VOL_DB_MIN,
+ DA732X_EQ_OVERALL_VOL_DB_INC, 0);
+
+/* High Pass Filter */
+static const char *da732x_hpf_mode[] = {
+ "Disable", "Music", "Voice",
+};
+
+static const char *da732x_hpf_music[] = {
+ "1.8Hz", "3.75Hz", "7.5Hz", "15Hz",
+};
+
+static const char *da732x_hpf_voice[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz",
+ "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac1_hpf_mode_enum,
+ DA732X_REG_DAC1_HPF, DA732X_HPF_MODE_SHIFT,
+ da732x_hpf_mode);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac2_hpf_mode_enum,
+ DA732X_REG_DAC2_HPF, DA732X_HPF_MODE_SHIFT,
+ da732x_hpf_mode);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac3_hpf_mode_enum,
+ DA732X_REG_DAC3_HPF, DA732X_HPF_MODE_SHIFT,
+ da732x_hpf_mode);
+
+static SOC_ENUM_SINGLE_DECL(da732x_adc1_hpf_mode_enum,
+ DA732X_REG_ADC1_HPF, DA732X_HPF_MODE_SHIFT,
+ da732x_hpf_mode);
+
+static SOC_ENUM_SINGLE_DECL(da732x_adc2_hpf_mode_enum,
+ DA732X_REG_ADC2_HPF, DA732X_HPF_MODE_SHIFT,
+ da732x_hpf_mode);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac1_hp_filter_enum,
+ DA732X_REG_DAC1_HPF, DA732X_HPF_MUSIC_SHIFT,
+ da732x_hpf_music);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac2_hp_filter_enum,
+ DA732X_REG_DAC2_HPF, DA732X_HPF_MUSIC_SHIFT,
+ da732x_hpf_music);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac3_hp_filter_enum,
+ DA732X_REG_DAC3_HPF, DA732X_HPF_MUSIC_SHIFT,
+ da732x_hpf_music);
+
+static SOC_ENUM_SINGLE_DECL(da732x_adc1_hp_filter_enum,
+ DA732X_REG_ADC1_HPF, DA732X_HPF_MUSIC_SHIFT,
+ da732x_hpf_music);
+
+static SOC_ENUM_SINGLE_DECL(da732x_adc2_hp_filter_enum,
+ DA732X_REG_ADC2_HPF, DA732X_HPF_MUSIC_SHIFT,
+ da732x_hpf_music);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac1_voice_filter_enum,
+ DA732X_REG_DAC1_HPF, DA732X_HPF_VOICE_SHIFT,
+ da732x_hpf_voice);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac2_voice_filter_enum,
+ DA732X_REG_DAC2_HPF, DA732X_HPF_VOICE_SHIFT,
+ da732x_hpf_voice);
+
+static SOC_ENUM_SINGLE_DECL(da732x_dac3_voice_filter_enum,
+ DA732X_REG_DAC3_HPF, DA732X_HPF_VOICE_SHIFT,
+ da732x_hpf_voice);
+
+static SOC_ENUM_SINGLE_DECL(da732x_adc1_voice_filter_enum,
+ DA732X_REG_ADC1_HPF, DA732X_HPF_VOICE_SHIFT,
+ da732x_hpf_voice);
+
+static SOC_ENUM_SINGLE_DECL(da732x_adc2_voice_filter_enum,
+ DA732X_REG_ADC2_HPF, DA732X_HPF_VOICE_SHIFT,
+ da732x_hpf_voice);
+
+static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(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 bits;
+
+ switch (sel) {
+ case DA732X_HPF_DISABLED:
+ bits = DA732X_HPF_DIS;
+ break;
+ case DA732X_HPF_VOICE:
+ bits = DA732X_HPF_VOICE_EN;
+ break;
+ case DA732X_HPF_MUSIC:
+ bits = DA732X_HPF_MUSIC_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, reg, DA732X_HPF_MASK, bits);
+
+ return 0;
+}
+
+static int da732x_hpf_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(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;
+
+ switch (val) {
+ case DA732X_HPF_VOICE_EN:
+ ucontrol->value.integer.value[0] = DA732X_HPF_VOICE;
+ break;
+ case DA732X_HPF_MUSIC_EN:
+ ucontrol->value.integer.value[0] = DA732X_HPF_MUSIC;
+ break;
+ default:
+ ucontrol->value.integer.value[0] = DA732X_HPF_DISABLED;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new da732x_snd_controls[] = {
+ /* Input PGAs */
+ SOC_SINGLE_RANGE_TLV("MIC1 Boost Volume", DA732X_REG_MIC1_PRE,
+ DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN,
+ DA732X_MICBOOST_MAX, 0, mic_boost_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC2 Boost Volume", DA732X_REG_MIC2_PRE,
+ DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN,
+ DA732X_MICBOOST_MAX, 0, mic_boost_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC3 Boost Volume", DA732X_REG_MIC3_PRE,
+ DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN,
+ DA732X_MICBOOST_MAX, 0, mic_boost_tlv),
+
+ /* MICs */
+ SOC_SINGLE("MIC1 Switch", DA732X_REG_MIC1, DA732X_MIC_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_RANGE_TLV("MIC1 Volume", DA732X_REG_MIC1,
+ DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN,
+ DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv),
+ SOC_SINGLE("MIC2 Switch", DA732X_REG_MIC2, DA732X_MIC_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_RANGE_TLV("MIC2 Volume", DA732X_REG_MIC2,
+ DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN,
+ DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv),
+ SOC_SINGLE("MIC3 Switch", DA732X_REG_MIC3, DA732X_MIC_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_RANGE_TLV("MIC3 Volume", DA732X_REG_MIC3,
+ DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN,
+ DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv),
+
+ /* AUXs */
+ SOC_SINGLE("AUX1L Switch", DA732X_REG_AUX1L, DA732X_AUX_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("AUX1L Volume", DA732X_REG_AUX1L,
+ DA732X_AUX_VOL_SHIFT, DA732X_AUX_VOL_VAL_MAX,
+ DA732X_NO_INVERT, aux_pga_tlv),
+ SOC_SINGLE("AUX1R Switch", DA732X_REG_AUX1R, DA732X_AUX_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("AUX1R Volume", DA732X_REG_AUX1R,
+ DA732X_AUX_VOL_SHIFT, DA732X_AUX_VOL_VAL_MAX,
+ DA732X_NO_INVERT, aux_pga_tlv),
+
+ /* ADCs */
+ SOC_DOUBLE_TLV("ADC1 Volume", DA732X_REG_ADC1_SEL,
+ DA732X_ADCL_VOL_SHIFT, DA732X_ADCR_VOL_SHIFT,
+ DA732X_ADC_VOL_VAL_MAX, DA732X_INVERT, adc_pga_tlv),
+
+ SOC_DOUBLE_TLV("ADC2 Volume", DA732X_REG_ADC2_SEL,
+ DA732X_ADCL_VOL_SHIFT, DA732X_ADCR_VOL_SHIFT,
+ DA732X_ADC_VOL_VAL_MAX, DA732X_INVERT, adc_pga_tlv),
+
+ /* DACs */
+ SOC_DOUBLE("Digital Playback DAC12 Switch", DA732X_REG_DAC1_SEL,
+ DA732X_DACL_MUTE_SHIFT, DA732X_DACR_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_DOUBLE_R_TLV("Digital Playback DAC12 Volume", DA732X_REG_DAC1_L_VOL,
+ DA732X_REG_DAC1_R_VOL, DA732X_DAC_VOL_SHIFT,
+ DA732X_DAC_VOL_VAL_MAX, DA732X_INVERT, dac_pga_tlv),
+ SOC_SINGLE("Digital Playback DAC3 Switch", DA732X_REG_DAC2_SEL,
+ DA732X_DACL_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("Digital Playback DAC3 Volume", DA732X_REG_DAC2_L_VOL,
+ DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX,
+ DA732X_INVERT, dac_pga_tlv),
+ SOC_SINGLE("Digital Playback DAC4 Switch", DA732X_REG_DAC2_SEL,
+ DA732X_DACR_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("Digital Playback DAC4 Volume", DA732X_REG_DAC2_R_VOL,
+ DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX,
+ DA732X_INVERT, dac_pga_tlv),
+ SOC_SINGLE("Digital Playback DAC5 Switch", DA732X_REG_DAC3_SEL,
+ DA732X_DACL_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("Digital Playback DAC5 Volume", DA732X_REG_DAC3_VOL,
+ DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX,
+ DA732X_INVERT, dac_pga_tlv),
+
+ /* High Pass Filters */
+ SOC_ENUM_EXT("DAC1 High Pass Filter Mode",
+ da732x_dac1_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+ SOC_ENUM("DAC1 High Pass Filter", da732x_dac1_hp_filter_enum),
+ SOC_ENUM("DAC1 Voice Filter", da732x_dac1_voice_filter_enum),
+
+ SOC_ENUM_EXT("DAC2 High Pass Filter Mode",
+ da732x_dac2_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+ SOC_ENUM("DAC2 High Pass Filter", da732x_dac2_hp_filter_enum),
+ SOC_ENUM("DAC2 Voice Filter", da732x_dac2_voice_filter_enum),
+
+ SOC_ENUM_EXT("DAC3 High Pass Filter Mode",
+ da732x_dac3_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+ SOC_ENUM("DAC3 High Pass Filter", da732x_dac3_hp_filter_enum),
+ SOC_ENUM("DAC3 Filter Mode", da732x_dac3_voice_filter_enum),
+
+ SOC_ENUM_EXT("ADC1 High Pass Filter Mode",
+ da732x_adc1_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+ SOC_ENUM("ADC1 High Pass Filter", da732x_adc1_hp_filter_enum),
+ SOC_ENUM("ADC1 Voice Filter", da732x_adc1_voice_filter_enum),
+
+ SOC_ENUM_EXT("ADC2 High Pass Filter Mode",
+ da732x_adc2_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+ SOC_ENUM("ADC2 High Pass Filter", da732x_adc2_hp_filter_enum),
+ SOC_ENUM("ADC2 Voice Filter", da732x_adc2_voice_filter_enum),
+
+ /* Equalizers */
+ SOC_SINGLE("ADC1 EQ Switch", DA732X_REG_ADC1_EQ5,
+ DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+ SOC_SINGLE_TLV("ADC1 EQ Band 1 Volume", DA732X_REG_ADC1_EQ12,
+ DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC1 EQ Band 2 Volume", DA732X_REG_ADC1_EQ12,
+ DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC1 EQ Band 3 Volume", DA732X_REG_ADC1_EQ34,
+ DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC1 EQ Band 4 Volume", DA732X_REG_ADC1_EQ34,
+ DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC1 EQ Band 5 Volume", DA732X_REG_ADC1_EQ5,
+ DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC1 EQ Overall Volume", DA732X_REG_ADC1_EQ5,
+ DA732X_EQ_OVERALL_SHIFT, DA732X_EQ_OVERALL_VOL_VAL_MAX,
+ DA732X_INVERT, eq_overall_tlv),
+
+ SOC_SINGLE("ADC2 EQ Switch", DA732X_REG_ADC2_EQ5,
+ DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+ SOC_SINGLE_TLV("ADC2 EQ Band 1 Volume", DA732X_REG_ADC2_EQ12,
+ DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC2 EQ Band 2 Volume", DA732X_REG_ADC2_EQ12,
+ DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC2 EQ Band 3 Volume", DA732X_REG_ADC2_EQ34,
+ DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ACD2 EQ Band 4 Volume", DA732X_REG_ADC2_EQ34,
+ DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ACD2 EQ Band 5 Volume", DA732X_REG_ADC2_EQ5,
+ DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("ADC2 EQ Overall Volume", DA732X_REG_ADC1_EQ5,
+ DA732X_EQ_OVERALL_SHIFT, DA732X_EQ_OVERALL_VOL_VAL_MAX,
+ DA732X_INVERT, eq_overall_tlv),
+
+ SOC_SINGLE("DAC1 EQ Switch", DA732X_REG_DAC1_EQ5,
+ DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+ SOC_SINGLE_TLV("DAC1 EQ Band 1 Volume", DA732X_REG_DAC1_EQ12,
+ DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC1 EQ Band 2 Volume", DA732X_REG_DAC1_EQ12,
+ DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC1 EQ Band 3 Volume", DA732X_REG_DAC1_EQ34,
+ DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC1 EQ Band 4 Volume", DA732X_REG_DAC1_EQ34,
+ DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC1 EQ Band 5 Volume", DA732X_REG_DAC1_EQ5,
+ DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+
+ SOC_SINGLE("DAC2 EQ Switch", DA732X_REG_DAC2_EQ5,
+ DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+ SOC_SINGLE_TLV("DAC2 EQ Band 1 Volume", DA732X_REG_DAC2_EQ12,
+ DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC2 EQ Band 2 Volume", DA732X_REG_DAC2_EQ12,
+ DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC2 EQ Band 3 Volume", DA732X_REG_DAC2_EQ34,
+ DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC2 EQ Band 4 Volume", DA732X_REG_DAC2_EQ34,
+ DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC2 EQ Band 5 Volume", DA732X_REG_DAC2_EQ5,
+ DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+
+ SOC_SINGLE("DAC3 EQ Switch", DA732X_REG_DAC3_EQ5,
+ DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+ SOC_SINGLE_TLV("DAC3 EQ Band 1 Volume", DA732X_REG_DAC3_EQ12,
+ DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC3 EQ Band 2 Volume", DA732X_REG_DAC3_EQ12,
+ DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC3 EQ Band 3 Volume", DA732X_REG_DAC3_EQ34,
+ DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC3 EQ Band 4 Volume", DA732X_REG_DAC3_EQ34,
+ DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+ SOC_SINGLE_TLV("DAC3 EQ Band 5 Volume", DA732X_REG_DAC3_EQ5,
+ DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+ DA732X_INVERT, eq_band_pga_tlv),
+
+ /* Lineout 2 Reciever*/
+ SOC_SINGLE("Lineout 2 Switch", DA732X_REG_LIN2, DA732X_LOUT_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("Lineout 2 Volume", DA732X_REG_LIN2,
+ DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX,
+ DA732X_NO_INVERT, lin2_pga_tlv),
+
+ /* Lineout 3 SPEAKER*/
+ SOC_SINGLE("Lineout 3 Switch", DA732X_REG_LIN3, DA732X_LOUT_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("Lineout 3 Volume", DA732X_REG_LIN3,
+ DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX,
+ DA732X_NO_INVERT, lin3_pga_tlv),
+
+ /* Lineout 4 */
+ SOC_SINGLE("Lineout 4 Switch", DA732X_REG_LIN4, DA732X_LOUT_MUTE_SHIFT,
+ DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_SINGLE_TLV("Lineout 4 Volume", DA732X_REG_LIN4,
+ DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX,
+ DA732X_NO_INVERT, lin4_pga_tlv),
+
+ /* Headphones */
+ SOC_DOUBLE_R("Headphone Switch", DA732X_REG_HPR, DA732X_REG_HPL,
+ DA732X_HP_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT),
+ SOC_DOUBLE_R_TLV("Headphone Volume", DA732X_REG_HPL_VOL,
+ DA732X_REG_HPR_VOL, DA732X_HP_VOL_SHIFT,
+ DA732X_HP_VOL_VAL_MAX, DA732X_NO_INVERT, hp_pga_tlv),
+};
+
+static int da732x_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(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,
+ 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,
+ DA732X_ADCC_BB_CLK_EN,
+ DA732X_ADCC_BB_CLK_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, w->reg, DA732X_ADC_RST_MASK,
+ DA732X_ADC_SET_ACT);
+ snd_soc_update_bits(codec, 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,
+ DA732X_ADC_OFF);
+ snd_soc_update_bits(codec, 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,
+ DA732X_ADCA_BB_CLK_EN, 0);
+ break;
+ case DA732X_REG_ADC2_PD:
+ snd_soc_update_bits(codec, DA732X_REG_CLK_EN3,
+ DA732X_ADCC_BB_CLK_EN, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int da732x_out_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, 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,
+ (1 << w->shift) | DA732X_OUT_HIZ_EN,
+ (1 << w->shift) | DA732X_OUT_HIZ_DIS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char *adcl_text[] = {
+ "AUX1L", "MIC1"
+};
+
+static const char *adcr_text[] = {
+ "AUX1R", "MIC2", "MIC3"
+};
+
+static const char *enable_text[] = {
+ "Disabled",
+ "Enabled"
+};
+
+/* ADC1LMUX */
+static SOC_ENUM_SINGLE_DECL(adc1l_enum,
+ DA732X_REG_INP_MUX, DA732X_ADC1L_MUX_SEL_SHIFT,
+ adcl_text);
+static const struct snd_kcontrol_new adc1l_mux =
+ SOC_DAPM_ENUM("ADC Route", adc1l_enum);
+
+/* ADC1RMUX */
+static SOC_ENUM_SINGLE_DECL(adc1r_enum,
+ DA732X_REG_INP_MUX, DA732X_ADC1R_MUX_SEL_SHIFT,
+ adcr_text);
+static const struct snd_kcontrol_new adc1r_mux =
+ SOC_DAPM_ENUM("ADC Route", adc1r_enum);
+
+/* ADC2LMUX */
+static SOC_ENUM_SINGLE_DECL(adc2l_enum,
+ DA732X_REG_INP_MUX, DA732X_ADC2L_MUX_SEL_SHIFT,
+ adcl_text);
+static const struct snd_kcontrol_new adc2l_mux =
+ SOC_DAPM_ENUM("ADC Route", adc2l_enum);
+
+/* ADC2RMUX */
+static SOC_ENUM_SINGLE_DECL(adc2r_enum,
+ DA732X_REG_INP_MUX, DA732X_ADC2R_MUX_SEL_SHIFT,
+ adcr_text);
+
+static const struct snd_kcontrol_new adc2r_mux =
+ SOC_DAPM_ENUM("ADC Route", adc2r_enum);
+
+static SOC_ENUM_SINGLE_DECL(da732x_hp_left_output,
+ DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN_SHIFT,
+ enable_text);
+
+static const struct snd_kcontrol_new hpl_mux =
+ SOC_DAPM_ENUM("HPL Switch", da732x_hp_left_output);
+
+static SOC_ENUM_SINGLE_DECL(da732x_hp_right_output,
+ DA732X_REG_HPR, DA732X_HP_OUT_DAC_EN_SHIFT,
+ enable_text);
+
+static const struct snd_kcontrol_new hpr_mux =
+ SOC_DAPM_ENUM("HPR Switch", da732x_hp_right_output);
+
+static SOC_ENUM_SINGLE_DECL(da732x_speaker_output,
+ DA732X_REG_LIN3, DA732X_LOUT_DAC_EN_SHIFT,
+ enable_text);
+
+static const struct snd_kcontrol_new spk_mux =
+ SOC_DAPM_ENUM("SPK Switch", da732x_speaker_output);
+
+static SOC_ENUM_SINGLE_DECL(da732x_lout4_output,
+ DA732X_REG_LIN4, DA732X_LOUT_DAC_EN_SHIFT,
+ enable_text);
+
+static const struct snd_kcontrol_new lout4_mux =
+ SOC_DAPM_ENUM("LOUT4 Switch", da732x_lout4_output);
+
+static SOC_ENUM_SINGLE_DECL(da732x_lout2_output,
+ DA732X_REG_LIN2, DA732X_LOUT_DAC_EN_SHIFT,
+ enable_text);
+
+static const struct snd_kcontrol_new lout2_mux =
+ SOC_DAPM_ENUM("LOUT2 Switch", da732x_lout2_output);
+
+static const struct snd_soc_dapm_widget da732x_dapm_widgets[] = {
+ /* Supplies */
+ SND_SOC_DAPM_SUPPLY("ADC1 Supply", DA732X_REG_ADC1_PD, 0,
+ DA732X_NO_INVERT, da732x_adc_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("ADC2 Supply", DA732X_REG_ADC2_PD, 0,
+ DA732X_NO_INVERT, da732x_adc_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("DAC1 CLK", DA732X_REG_CLK_EN4,
+ DA732X_DACA_BB_CLK_SHIFT, DA732X_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC2 CLK", DA732X_REG_CLK_EN4,
+ DA732X_DACC_BB_CLK_SHIFT, DA732X_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC3 CLK", DA732X_REG_CLK_EN5,
+ DA732X_DACE_BB_CLK_SHIFT, DA732X_NO_INVERT,
+ NULL, 0),
+
+ /* Micbias */
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", DA732X_REG_MICBIAS1,
+ DA732X_MICBIAS_EN_SHIFT,
+ DA732X_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", DA732X_REG_MICBIAS2,
+ DA732X_MICBIAS_EN_SHIFT,
+ DA732X_NO_INVERT, NULL, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("MIC3"),
+ SND_SOC_DAPM_INPUT("AUX1L"),
+ SND_SOC_DAPM_INPUT("AUX1R"),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("LOUTL"),
+ SND_SOC_DAPM_OUTPUT("LOUTR"),
+ SND_SOC_DAPM_OUTPUT("ClassD"),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1L", NULL, DA732X_REG_ADC1_SEL,
+ DA732X_ADCL_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_ADC("ADC1R", NULL, DA732X_REG_ADC1_SEL,
+ DA732X_ADCR_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_ADC("ADC2L", NULL, DA732X_REG_ADC2_SEL,
+ DA732X_ADCL_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_ADC("ADC2R", NULL, DA732X_REG_ADC2_SEL,
+ DA732X_ADCR_EN_SHIFT, DA732X_NO_INVERT),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC1L", NULL, DA732X_REG_DAC1_SEL,
+ DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_DAC("DAC1R", NULL, DA732X_REG_DAC1_SEL,
+ DA732X_DACR_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_DAC("DAC2L", NULL, DA732X_REG_DAC2_SEL,
+ DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_DAC("DAC2R", NULL, DA732X_REG_DAC2_SEL,
+ DA732X_DACR_EN_SHIFT, DA732X_NO_INVERT),
+ SND_SOC_DAPM_DAC("DAC3", NULL, DA732X_REG_DAC3_SEL,
+ DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT),
+
+ /* Input Pgas */
+ SND_SOC_DAPM_PGA("MIC1 PGA", DA732X_REG_MIC1, DA732X_MIC_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC2 PGA", DA732X_REG_MIC2, DA732X_MIC_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC3 PGA", DA732X_REG_MIC3, DA732X_MIC_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX1L PGA", DA732X_REG_AUX1L, DA732X_AUX_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX1R PGA", DA732X_REG_AUX1R, DA732X_AUX_EN_SHIFT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_E("HP Left", DA732X_REG_HPL, DA732X_HP_OUT_EN_SHIFT,
+ 0, NULL, 0, da732x_out_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HP Right", DA732X_REG_HPR, DA732X_HP_OUT_EN_SHIFT,
+ 0, NULL, 0, da732x_out_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LIN2", DA732X_REG_LIN2, DA732X_LIN_OUT_EN_SHIFT,
+ 0, NULL, 0, da732x_out_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LIN3", DA732X_REG_LIN3, DA732X_LIN_OUT_EN_SHIFT,
+ 0, NULL, 0, da732x_out_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LIN4", DA732X_REG_LIN4, DA732X_LIN_OUT_EN_SHIFT,
+ 0, NULL, 0, da732x_out_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* MUXs */
+ SND_SOC_DAPM_MUX("ADC1 Left MUX", SND_SOC_NOPM, 0, 0, &adc1l_mux),
+ SND_SOC_DAPM_MUX("ADC1 Right MUX", SND_SOC_NOPM, 0, 0, &adc1r_mux),
+ SND_SOC_DAPM_MUX("ADC2 Left MUX", SND_SOC_NOPM, 0, 0, &adc2l_mux),
+ SND_SOC_DAPM_MUX("ADC2 Right MUX", SND_SOC_NOPM, 0, 0, &adc2r_mux),
+
+ SND_SOC_DAPM_MUX("HP Left MUX", SND_SOC_NOPM, 0, 0, &hpl_mux),
+ SND_SOC_DAPM_MUX("HP Right MUX", SND_SOC_NOPM, 0, 0, &hpr_mux),
+ SND_SOC_DAPM_MUX("Speaker MUX", SND_SOC_NOPM, 0, 0, &spk_mux),
+ SND_SOC_DAPM_MUX("LOUT2 MUX", SND_SOC_NOPM, 0, 0, &lout2_mux),
+ SND_SOC_DAPM_MUX("LOUT4 MUX", SND_SOC_NOPM, 0, 0, &lout4_mux),
+
+ /* AIF interfaces */
+ SND_SOC_DAPM_AIF_OUT("AIFA Output", "AIFA Capture", 0, DA732X_REG_AIFA3,
+ DA732X_AIF_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("AIFA Input", "AIFA Playback", 0, DA732X_REG_AIFA3,
+ DA732X_AIF_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_AIF_OUT("AIFB Output", "AIFB Capture", 0, DA732X_REG_AIFB3,
+ DA732X_AIF_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("AIFB Input", "AIFB Playback", 0, DA732X_REG_AIFB3,
+ DA732X_AIF_EN_SHIFT, 0),
+};
+
+static const struct snd_soc_dapm_route da732x_dapm_routes[] = {
+ /* Inputs */
+ {"AUX1L PGA", NULL, "AUX1L"},
+ {"AUX1R PGA", NULL, "AUX1R"},
+ {"MIC1 PGA", NULL, "MIC1"},
+ {"MIC2 PGA", NULL, "MIC2"},
+ {"MIC3 PGA", NULL, "MIC3"},
+
+ /* Capture Path */
+ {"ADC1 Left MUX", "MIC1", "MIC1 PGA"},
+ {"ADC1 Left MUX", "AUX1L", "AUX1L PGA"},
+
+ {"ADC1 Right MUX", "AUX1R", "AUX1R PGA"},
+ {"ADC1 Right MUX", "MIC2", "MIC2 PGA"},
+ {"ADC1 Right MUX", "MIC3", "MIC3 PGA"},
+
+ {"ADC2 Left MUX", "AUX1L", "AUX1L PGA"},
+ {"ADC2 Left MUX", "MIC1", "MIC1 PGA"},
+
+ {"ADC2 Right MUX", "AUX1R", "AUX1R PGA"},
+ {"ADC2 Right MUX", "MIC2", "MIC2 PGA"},
+ {"ADC2 Right MUX", "MIC3", "MIC3 PGA"},
+
+ {"ADC1L", NULL, "ADC1 Supply"},
+ {"ADC1R", NULL, "ADC1 Supply"},
+ {"ADC2L", NULL, "ADC2 Supply"},
+ {"ADC2R", NULL, "ADC2 Supply"},
+
+ {"ADC1L", NULL, "ADC1 Left MUX"},
+ {"ADC1R", NULL, "ADC1 Right MUX"},
+ {"ADC2L", NULL, "ADC2 Left MUX"},
+ {"ADC2R", NULL, "ADC2 Right MUX"},
+
+ {"AIFA Output", NULL, "ADC1L"},
+ {"AIFA Output", NULL, "ADC1R"},
+ {"AIFB Output", NULL, "ADC2L"},
+ {"AIFB Output", NULL, "ADC2R"},
+
+ {"HP Left MUX", "Enabled", "AIFA Input"},
+ {"HP Right MUX", "Enabled", "AIFA Input"},
+ {"Speaker MUX", "Enabled", "AIFB Input"},
+ {"LOUT2 MUX", "Enabled", "AIFB Input"},
+ {"LOUT4 MUX", "Enabled", "AIFB Input"},
+
+ {"DAC1L", NULL, "DAC1 CLK"},
+ {"DAC1R", NULL, "DAC1 CLK"},
+ {"DAC2L", NULL, "DAC2 CLK"},
+ {"DAC2R", NULL, "DAC2 CLK"},
+ {"DAC3", NULL, "DAC3 CLK"},
+
+ {"DAC1L", NULL, "HP Left MUX"},
+ {"DAC1R", NULL, "HP Right MUX"},
+ {"DAC2L", NULL, "Speaker MUX"},
+ {"DAC2R", NULL, "LOUT4 MUX"},
+ {"DAC3", NULL, "LOUT2 MUX"},
+
+ /* Output Pgas */
+ {"HP Left", NULL, "DAC1L"},
+ {"HP Right", NULL, "DAC1R"},
+ {"LIN3", NULL, "DAC2L"},
+ {"LIN4", NULL, "DAC2R"},
+ {"LIN2", NULL, "DAC3"},
+
+ /* Outputs */
+ {"ClassD", NULL, "LIN3"},
+ {"LOUTL", NULL, "LIN2"},
+ {"LOUTR", NULL, "LIN4"},
+ {"HPL", NULL, "HP Left"},
+ {"HPR", NULL, "HP Right"},
+};
+
+static int da732x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u32 aif = 0;
+ u32 reg_aif;
+ u32 fs;
+
+ reg_aif = dai->driver->base;
+
+ switch (params_width(params)) {
+ case 16:
+ aif |= DA732X_AIF_WORD_16;
+ break;
+ case 20:
+ aif |= DA732X_AIF_WORD_20;
+ break;
+ case 24:
+ aif |= DA732X_AIF_WORD_24;
+ break;
+ case 32:
+ aif |= DA732X_AIF_WORD_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA732X_SR_8KHZ;
+ break;
+ case 11025:
+ fs = DA732X_SR_11_025KHZ;
+ break;
+ case 12000:
+ fs = DA732X_SR_12KHZ;
+ break;
+ case 16000:
+ fs = DA732X_SR_16KHZ;
+ break;
+ case 22050:
+ fs = DA732X_SR_22_05KHZ;
+ break;
+ case 24000:
+ fs = DA732X_SR_24KHZ;
+ break;
+ case 32000:
+ fs = DA732X_SR_32KHZ;
+ break;
+ case 44100:
+ fs = DA732X_SR_44_1KHZ;
+ break;
+ case 48000:
+ fs = DA732X_SR_48KHZ;
+ break;
+ case 88100:
+ fs = DA732X_SR_88_1KHZ;
+ break;
+ case 96000:
+ fs = DA732X_SR_96KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, reg_aif, DA732X_AIF_WORD_MASK, aif);
+ snd_soc_update_bits(codec, 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;
+ u32 aif_mclk, pc_count;
+ u32 reg_aif1, aif1;
+ u32 reg_aif3, aif3;
+
+ switch (dai->id) {
+ case DA732X_DAI_ID1:
+ reg_aif1 = DA732X_REG_AIFA1;
+ reg_aif3 = DA732X_REG_AIFA3;
+ pc_count = DA732X_PC_PULSE_AIFA | DA732X_PC_RESYNC_NOT_AUT |
+ DA732X_PC_SAME;
+ break;
+ case DA732X_DAI_ID2:
+ reg_aif1 = DA732X_REG_AIFB1;
+ reg_aif3 = DA732X_REG_AIFB3;
+ pc_count = DA732X_PC_PULSE_AIFB | DA732X_PC_RESYNC_NOT_AUT |
+ DA732X_PC_SAME;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ aif1 = DA732X_AIF_SLAVE;
+ aif_mclk = DA732X_AIFM_FRAME_64 | DA732X_AIFM_SRC_SEL_AIFA;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif1 = DA732X_AIF_CLK_FROM_SRC;
+ aif_mclk = DA732X_CLK_GENERATION_AIF_A;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ aif3 = DA732X_AIF_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ aif3 = DA732X_AIF_RIGHT_J_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif3 = DA732X_AIF_LEFT_J_MODE;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aif3 = DA732X_AIF_DSP_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_B:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif3 |= DA732X_AIF_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif3 |= DA732X_AIF_BCLK_INV | DA732X_AIF_WCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif3 |= DA732X_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif3 |= DA732X_AIF_WCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_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 |
+ DA732X_AIF_WCLK_INV | DA732X_AIF_MODE_MASK, aif3);
+ snd_soc_write(codec, DA732X_REG_PC_CTRL, pc_count);
+
+ return 0;
+}
+
+
+
+static int da732x_set_dai_pll(struct snd_soc_codec *codec, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+ 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,
+ DA732X_PLL_EN, 0);
+ da732x->pll_en = false;
+ return 0;
+ }
+
+ if (da732x->pll_en)
+ return -EBUSY;
+
+ if (source == DA732X_SRCCLK_MCLK) {
+ /* Validate Sysclk rate */
+ switch (da732x->sysclk) {
+ case 11290000:
+ case 12288000:
+ case 22580000:
+ case 24576000:
+ case 45160000:
+ case 49152000:
+ snd_soc_write(codec, DA732X_REG_PLL_CTRL,
+ DA732X_PLL_BYPASS);
+ return 0;
+ default:
+ dev_err(codec->dev,
+ "Cannot use PLL Bypass, invalid SYSCLK rate\n");
+ return -EINVAL;
+ }
+ }
+
+ indiv = da732x_get_input_div(codec, da732x->sysclk);
+ if (indiv < 0)
+ return indiv;
+
+ fref = (da732x->sysclk / 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_update_bits(codec, DA732X_REG_PLL_CTRL, DA732X_PLL_EN,
+ DA732X_PLL_EN);
+
+ da732x->pll_en = true;
+
+ return 0;
+}
+
+static int da732x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+
+ da732x->sysclk = freq;
+
+ return 0;
+}
+
+#define DA732X_RATES SNDRV_PCM_RATE_8000_96000
+
+#define DA732X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static 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 = {
+ .hw_params = da732x_hw_params,
+ .set_fmt = da732x_set_dai_fmt,
+ .set_sysclk = da732x_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver da732x_dai[] = {
+ {
+ .name = "DA732X_AIFA",
+ .id = DA732X_DAI_ID1,
+ .base = DA732X_REG_AIFA1,
+ .playback = {
+ .stream_name = "AIFA Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = DA732X_RATES,
+ .formats = DA732X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIFA Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = DA732X_RATES,
+ .formats = DA732X_FORMATS,
+ },
+ .ops = &da732x_dai1_ops,
+ },
+ {
+ .name = "DA732X_AIFB",
+ .id = DA732X_DAI_ID2,
+ .base = DA732X_REG_AIFB1,
+ .playback = {
+ .stream_name = "AIFB Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = DA732X_RATES,
+ .formats = DA732X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIFB Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = DA732X_RATES,
+ .formats = DA732X_FORMATS,
+ },
+ .ops = &da732x_dai2_ops,
+ },
+};
+
+static bool da732x_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA732X_REG_HPL_DAC_OFF_CNTL:
+ case DA732X_REG_HPR_DAC_OFF_CNTL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config da732x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = DA732X_MAX_REG,
+ .volatile_reg = da732x_volatile,
+ .reg_defaults = da732x_reg_cache,
+ .num_reg_defaults = ARRAY_SIZE(da732x_reg_cache),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+
+static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
+{
+ 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,
+ DA732X_HP_DAC_OFFSET_TRIM_VAL);
+ snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET,
+ DA732X_HP_DAC_OFFSET_TRIM_VAL);
+ snd_soc_write(codec, 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,
+ DA732X_HP_DAC_OFF_CALIBRATION |
+ DA732X_HP_DAC_OFF_SCALE_STEPS);
+
+ /* Wait for voltage stabilization */
+ msleep(DA732X_WAIT_FOR_STABILIZATION);
+
+ /* Check DAC offset sign */
+ sign[DA732X_HPL_DAC] = (snd_soc_read(codec, 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) &
+ DA732X_HP_DAC_OFF_CNTL_COMPO);
+
+ /* Binary search DAC offset values (both channels at once) */
+ offset[DA732X_HPL_DAC] = sign[DA732X_HPL_DAC] << DA732X_HP_DAC_COMPO_SHIFT;
+ offset[DA732X_HPR_DAC] = sign[DA732X_HPR_DAC] << DA732X_HP_DAC_COMPO_SHIFT;
+
+ do {
+ offset[DA732X_HPL_DAC] |= step;
+ offset[DA732X_HPR_DAC] |= step;
+ snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+ ~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK);
+ snd_soc_write(codec, 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) &
+ 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) &
+ DA732X_HP_DAC_OFF_CNTL_COMPO) ^ sign[DA732X_HPR_DAC])
+ offset[DA732X_HPR_DAC] &= ~step;
+
+ step >>= 1;
+ } while (step);
+
+ /* Write final DAC offsets to registers */
+ snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+ ~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK);
+ snd_soc_write(codec, 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,
+ DA732X_HP_DAC_OFF_SCALE_STEPS);
+ snd_soc_write(codec, DA732X_REG_HPR_DAC_OFF_CNTL,
+ DA732X_HP_DAC_OFF_SCALE_STEPS);
+}
+
+static void da732x_output_offset_adjust(struct snd_soc_codec *codec)
+{
+ u8 offset[DA732X_HP_AMPS];
+ u8 sign[DA732X_HP_AMPS];
+ u8 step = DA732X_OUTPUT_OFFSET_STEP;
+
+ offset[DA732X_HPL_AMP] = DA732X_HP_OUT_TRIM_VAL;
+ offset[DA732X_HPR_AMP] = DA732X_HP_OUT_TRIM_VAL;
+
+ /* Initialize output offset calibration circuits and registers */
+ snd_soc_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,
+ DA732X_HP_OUT_COMP | DA732X_HP_OUT_EN);
+ snd_soc_write(codec, 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) &
+ DA732X_HP_OUT_COMPO;
+ sign[DA732X_HPR_AMP] = snd_soc_read(codec, DA732X_REG_HPR) &
+ DA732X_HP_OUT_COMPO;
+
+ snd_soc_write(codec, 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 |
+ (sign[DA732X_HPR_AMP] >> DA732X_HP_OUT_COMPO_SHIFT) |
+ DA732X_HP_OUT_EN);
+
+ /* Binary search output offset values (both channels at once) */
+ do {
+ offset[DA732X_HPL_AMP] |= step;
+ offset[DA732X_HPR_AMP] |= step;
+ snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET,
+ offset[DA732X_HPL_AMP]);
+ snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET,
+ offset[DA732X_HPR_AMP]);
+
+ msleep(DA732X_WAIT_FOR_STABILIZATION);
+
+ if ((snd_soc_read(codec, DA732X_REG_HPL) &
+ DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPL_AMP])
+ offset[DA732X_HPL_AMP] &= ~step;
+ if ((snd_soc_read(codec, DA732X_REG_HPR) &
+ DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPR_AMP])
+ offset[DA732X_HPR_AMP] &= ~step;
+
+ step >>= 1;
+ } while (step);
+
+ /* Write final DAC offsets to registers */
+ snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET, offset[DA732X_HPL_AMP]);
+ snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET, offset[DA732X_HPR_AMP]);
+}
+
+static void da732x_hp_dc_offset_cancellation(struct snd_soc_codec *codec)
+{
+ /* Make sure that we have Soft Mute enabled */
+ snd_soc_write(codec, DA732X_REG_DAC1_SOFTMUTE, DA732X_SOFTMUTE_EN |
+ DA732X_GAIN_RAMPED | DA732X_16_SAMPLES);
+ snd_soc_write(codec, 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 |
+ DA732X_HP_OUT_MUTE | DA732X_HP_OUT_EN);
+ snd_soc_write(codec, 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);
+
+ 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);
+}
+
+static int da732x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_update_bits(codec, 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) {
+ /* Init Codec */
+ snd_soc_write(codec, DA732X_REG_REF1,
+ DA732X_VMID_FASTCHG);
+ snd_soc_write(codec, 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,
+ DA732X_REFBUFX2_EN);
+
+ /* Enable bypass DSP routing */
+ snd_soc_write(codec, DA732X_REG_DATA_ROUTE,
+ DA732X_BYPASS_DSP);
+
+ /* Enable Digital subsystem */
+ snd_soc_write(codec, DA732X_REG_DSP_CTRL,
+ DA732X_DIGITAL_EN);
+
+ snd_soc_write(codec, 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,
+ DA732X_HP_OUT_GNDSEL);
+
+ da732x_set_charge_pump(codec, DA732X_ENABLE_CP);
+
+ snd_soc_write(codec, DA732X_REG_CLK_EN1,
+ DA732X_SYS3_CLK_EN | DA732X_PC_CLK_EN);
+
+ /* Enable Zero Crossing */
+ snd_soc_write(codec, DA732X_REG_INP_ZC_EN,
+ DA732X_MIC1_PRE_ZC_EN |
+ DA732X_MIC1_ZC_EN |
+ DA732X_MIC2_PRE_ZC_EN |
+ DA732X_MIC2_ZC_EN |
+ DA732X_AUXL_ZC_EN |
+ DA732X_AUXR_ZC_EN |
+ DA732X_MIC3_PRE_ZC_EN |
+ DA732X_MIC3_ZC_EN);
+ snd_soc_write(codec, 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);
+
+ regcache_cache_only(da732x->regmap, false);
+ regcache_sync(da732x->regmap);
+ } else {
+ snd_soc_update_bits(codec, DA732X_REG_BIAS_EN,
+ DA732X_BIAS_BOOST_MASK,
+ DA732X_BIAS_BOOST_50PC);
+ snd_soc_update_bits(codec, 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_BIAS_DIS);
+ da732x->pll_en = false;
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_da732x = {
+ .set_bias_level = da732x_set_bias_level,
+ .controls = da732x_snd_controls,
+ .num_controls = ARRAY_SIZE(da732x_snd_controls),
+ .dapm_widgets = da732x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da732x_dapm_widgets),
+ .dapm_routes = da732x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(da732x_dapm_routes),
+ .set_pll = da732x_set_dai_pll,
+};
+
+static int da732x_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da732x_priv *da732x;
+ unsigned int reg;
+ int ret;
+
+ da732x = devm_kzalloc(&i2c->dev, sizeof(struct da732x_priv),
+ GFP_KERNEL);
+ if (!da732x)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, da732x);
+
+ da732x->regmap = devm_regmap_init_i2c(i2c, &da732x_regmap);
+ if (IS_ERR(da732x->regmap)) {
+ ret = PTR_ERR(da732x->regmap);
+ dev_err(&i2c->dev, "Failed to initialize regmap\n");
+ goto err;
+ }
+
+ ret = regmap_read(da732x->regmap, DA732X_REG_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret);
+ goto err;
+ }
+
+ dev_info(&i2c->dev, "Revision: %d.%d\n",
+ (reg & DA732X_ID_MAJOR_MASK) >> 4,
+ (reg & DA732X_ID_MINOR_MASK));
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da732x,
+ da732x_dai, ARRAY_SIZE(da732x_dai));
+ if (ret != 0)
+ dev_err(&i2c->dev, "Failed to register codec.\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},
+ { }
+};
+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,
+};
+
+module_i2c_driver(da732x_i2c_driver);
+
+
+MODULE_DESCRIPTION("ASoC DA732X driver");
+MODULE_AUTHOR("Michal Hajduk <michal.hajduk@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/da732x.h b/kernel/sound/soc/codecs/da732x.h
new file mode 100644
index 000000000..f586cbd30
--- /dev/null
+++ b/kernel/sound/soc/codecs/da732x.h
@@ -0,0 +1,130 @@
+/*
+ * 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_
+#define __DA732X_H_
+
+#include <sound/soc.h>
+
+/* General */
+#define DA732X_U8_MASK 0xFF
+#define DA732X_4BYTES 4
+#define DA732X_3BYTES 3
+#define DA732X_2BYTES 2
+#define DA732X_1BYTE 1
+#define DA732X_1BYTE_SHIFT 8
+#define DA732X_2BYTES_SHIFT 16
+#define DA732X_3BYTES_SHIFT 24
+#define DA732X_4BYTES_SHIFT 32
+
+#define DA732X_DACS_DIS 0x0
+#define DA732X_HP_DIS 0x0
+#define DA732X_CLEAR_REG 0x0
+
+/* Calibration */
+#define DA732X_DAC_OFFSET_STEP 0x20
+#define DA732X_OUTPUT_OFFSET_STEP 0x80
+#define DA732X_HP_OUT_TRIM_VAL 0x0
+#define DA732X_WAIT_FOR_STABILIZATION 1
+#define DA732X_HPL_DAC 0
+#define DA732X_HPR_DAC 1
+#define DA732X_HP_DACS 2
+#define DA732X_HPL_AMP 0
+#define DA732X_HPR_AMP 1
+#define DA732X_HP_AMPS 2
+
+/* Clock settings */
+#define DA732X_STARTUP_DELAY 100
+#define DA732X_PLL_OUT_196608 196608000
+#define DA732X_PLL_OUT_180634 180633600
+#define DA732X_PLL_OUT_SRM 188620800
+#define DA732X_MCLK_10MHZ 10000000
+#define DA732X_MCLK_20MHZ 20000000
+#define DA732X_MCLK_40MHZ 40000000
+#define DA732X_MCLK_54MHZ 54000000
+#define DA732X_MCLK_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_DAI_ID1 0
+#define DA732X_DAI_ID2 1
+#define DA732X_SRCCLK_PLL 0
+#define DA732X_SRCCLK_MCLK 1
+
+#define DA732X_LIN_LP_VOL 0x4F
+#define DA732X_LP_VOL 0x40
+
+/* Kcontrols */
+#define DA732X_DAC_EN_MAX 2
+#define DA732X_ADCL_MUX_MAX 2
+#define DA732X_ADCR_MUX_MAX 3
+#define DA732X_HPF_MODE_MAX 3
+#define DA732X_HPF_MODE_SHIFT 4
+#define DA732X_HPF_MUSIC_SHIFT 0
+#define DA732X_HPF_MUSIC_MAX 4
+#define DA732X_HPF_VOICE_SHIFT 4
+#define DA732X_HPF_VOICE_MAX 8
+#define DA732X_EQ_EN_MAX 1
+#define DA732X_HPF_VOICE 1
+#define DA732X_HPF_MUSIC 2
+#define DA732X_HPF_DISABLED 0
+#define DA732X_NO_INVERT 0
+#define DA732X_INVERT 1
+#define DA732X_SWITCH_MAX 1
+#define DA732X_ENABLE_CP 1
+#define DA732X_DISABLE_CP 0
+#define DA732X_DISABLE_ALL_CLKS 0
+#define DA732X_RESET_ADCS 0
+
+/* dB values */
+#define DA732X_MIC_VOL_DB_MIN 0
+#define DA732X_MIC_VOL_DB_INC 50
+#define DA732X_MIC_PRE_VOL_DB_MIN 0
+#define DA732X_MIC_PRE_VOL_DB_INC 600
+#define DA732X_AUX_VOL_DB_MIN -6000
+#define DA732X_AUX_VOL_DB_INC 150
+#define DA732X_HP_VOL_DB_MIN -2250
+#define DA732X_HP_VOL_DB_INC 150
+#define DA732X_LIN2_VOL_DB_MIN -1650
+#define DA732X_LIN2_VOL_DB_INC 150
+#define DA732X_LIN3_VOL_DB_MIN -1650
+#define DA732X_LIN3_VOL_DB_INC 150
+#define DA732X_LIN4_VOL_DB_MIN -2250
+#define DA732X_LIN4_VOL_DB_INC 150
+#define DA732X_EQ_BAND_VOL_DB_MIN -1050
+#define DA732X_EQ_BAND_VOL_DB_INC 150
+#define DA732X_DAC_VOL_DB_MIN -7725
+#define DA732X_DAC_VOL_DB_INC 75
+#define DA732X_ADC_VOL_DB_MIN 0
+#define DA732X_ADC_VOL_DB_INC -1
+#define DA732X_EQ_OVERALL_VOL_DB_MIN -1800
+#define DA732X_EQ_OVERALL_VOL_DB_INC 600
+
+enum da732x_sysctl {
+ DA732X_SR_8KHZ = 0x1,
+ DA732X_SR_11_025KHZ = 0x2,
+ DA732X_SR_12KHZ = 0x3,
+ DA732X_SR_16KHZ = 0x5,
+ DA732X_SR_22_05KHZ = 0x6,
+ DA732X_SR_24KHZ = 0x7,
+ DA732X_SR_32KHZ = 0x9,
+ DA732X_SR_44_1KHZ = 0xA,
+ DA732X_SR_48KHZ = 0xB,
+ DA732X_SR_88_1KHZ = 0xE,
+ DA732X_SR_96KHZ = 0xF,
+};
+
+#endif /* __DA732X_H_ */
diff --git a/kernel/sound/soc/codecs/da732x_reg.h b/kernel/sound/soc/codecs/da732x_reg.h
new file mode 100644
index 000000000..bdd03ca4b
--- /dev/null
+++ b/kernel/sound/soc/codecs/da732x_reg.h
@@ -0,0 +1,654 @@
+/*
+ * 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_
+#define __DA732X_REG_H_
+
+/* DA732X registers */
+#define DA732X_REG_STATUS_EXT 0x00
+#define DA732X_REG_STATUS 0x01
+#define DA732X_REG_REF1 0x02
+#define DA732X_REG_BIAS_EN 0x03
+#define DA732X_REG_BIAS1 0x04
+#define DA732X_REG_BIAS2 0x05
+#define DA732X_REG_BIAS3 0x06
+#define DA732X_REG_BIAS4 0x07
+#define DA732X_REG_MICBIAS2 0x0F
+#define DA732X_REG_MICBIAS1 0x10
+#define DA732X_REG_MICDET 0x11
+#define DA732X_REG_MIC1_PRE 0x12
+#define DA732X_REG_MIC1 0x13
+#define DA732X_REG_MIC2_PRE 0x14
+#define DA732X_REG_MIC2 0x15
+#define DA732X_REG_AUX1L 0x16
+#define DA732X_REG_AUX1R 0x17
+#define DA732X_REG_MIC3_PRE 0x18
+#define DA732X_REG_MIC3 0x19
+#define DA732X_REG_INP_PINBIAS 0x1A
+#define DA732X_REG_INP_ZC_EN 0x1B
+#define DA732X_REG_INP_MUX 0x1D
+#define DA732X_REG_HP_DET 0x20
+#define DA732X_REG_HPL_DAC_OFFSET 0x21
+#define DA732X_REG_HPL_DAC_OFF_CNTL 0x22
+#define DA732X_REG_HPL_OUT_OFFSET 0x23
+#define DA732X_REG_HPL 0x24
+#define DA732X_REG_HPL_VOL 0x25
+#define DA732X_REG_HPR_DAC_OFFSET 0x26
+#define DA732X_REG_HPR_DAC_OFF_CNTL 0x27
+#define DA732X_REG_HPR_OUT_OFFSET 0x28
+#define DA732X_REG_HPR 0x29
+#define DA732X_REG_HPR_VOL 0x2A
+#define DA732X_REG_LIN2 0x2B
+#define DA732X_REG_LIN3 0x2C
+#define DA732X_REG_LIN4 0x2D
+#define DA732X_REG_OUT_ZC_EN 0x2E
+#define DA732X_REG_HP_LIN1_GNDSEL 0x37
+#define DA732X_REG_CP_HP1 0x3A
+#define DA732X_REG_CP_HP2 0x3B
+#define DA732X_REG_CP_CTRL1 0x40
+#define DA732X_REG_CP_CTRL2 0x41
+#define DA732X_REG_CP_CTRL3 0x42
+#define DA732X_REG_CP_LEVEL_MASK 0x43
+#define DA732X_REG_CP_DET 0x44
+#define DA732X_REG_CP_STATUS 0x45
+#define DA732X_REG_CP_THRESH1 0x46
+#define DA732X_REG_CP_THRESH2 0x47
+#define DA732X_REG_CP_THRESH3 0x48
+#define DA732X_REG_CP_THRESH4 0x49
+#define DA732X_REG_CP_THRESH5 0x4A
+#define DA732X_REG_CP_THRESH6 0x4B
+#define DA732X_REG_CP_THRESH7 0x4C
+#define DA732X_REG_CP_THRESH8 0x4D
+#define DA732X_REG_PLL_DIV_LO 0x50
+#define DA732X_REG_PLL_DIV_MID 0x51
+#define DA732X_REG_PLL_DIV_HI 0x52
+#define DA732X_REG_PLL_CTRL 0x53
+#define DA732X_REG_CLK_CTRL 0x54
+#define DA732X_REG_CLK_DSP 0x5A
+#define DA732X_REG_CLK_EN1 0x5B
+#define DA732X_REG_CLK_EN2 0x5C
+#define DA732X_REG_CLK_EN3 0x5D
+#define DA732X_REG_CLK_EN4 0x5E
+#define DA732X_REG_CLK_EN5 0x5F
+#define DA732X_REG_AIF_MCLK 0x60
+#define DA732X_REG_AIFA1 0x61
+#define DA732X_REG_AIFA2 0x62
+#define DA732X_REG_AIFA3 0x63
+#define DA732X_REG_AIFB1 0x64
+#define DA732X_REG_AIFB2 0x65
+#define DA732X_REG_AIFB3 0x66
+#define DA732X_REG_PC_CTRL 0x6A
+#define DA732X_REG_DATA_ROUTE 0x70
+#define DA732X_REG_DSP_CTRL 0x71
+#define DA732X_REG_CIF_CTRL2 0x74
+#define DA732X_REG_HANDSHAKE 0x75
+#define DA732X_REG_MBOX0 0x76
+#define DA732X_REG_MBOX1 0x77
+#define DA732X_REG_MBOX2 0x78
+#define DA732X_REG_MBOX_STATUS 0x79
+#define DA732X_REG_SPARE1_OUT 0x7D
+#define DA732X_REG_SPARE2_OUT 0x7E
+#define DA732X_REG_SPARE1_IN 0x7F
+#define DA732X_REG_ID 0x81
+#define DA732X_REG_ADC1_PD 0x90
+#define DA732X_REG_ADC1_HPF 0x93
+#define DA732X_REG_ADC1_SEL 0x94
+#define DA732X_REG_ADC1_EQ12 0x95
+#define DA732X_REG_ADC1_EQ34 0x96
+#define DA732X_REG_ADC1_EQ5 0x97
+#define DA732X_REG_ADC2_PD 0x98
+#define DA732X_REG_ADC2_HPF 0x9B
+#define DA732X_REG_ADC2_SEL 0x9C
+#define DA732X_REG_ADC2_EQ12 0x9D
+#define DA732X_REG_ADC2_EQ34 0x9E
+#define DA732X_REG_ADC2_EQ5 0x9F
+#define DA732X_REG_DAC1_HPF 0xA0
+#define DA732X_REG_DAC1_L_VOL 0xA1
+#define DA732X_REG_DAC1_R_VOL 0xA2
+#define DA732X_REG_DAC1_SEL 0xA3
+#define DA732X_REG_DAC1_SOFTMUTE 0xA4
+#define DA732X_REG_DAC1_EQ12 0xA5
+#define DA732X_REG_DAC1_EQ34 0xA6
+#define DA732X_REG_DAC1_EQ5 0xA7
+#define DA732X_REG_DAC2_HPF 0xB0
+#define DA732X_REG_DAC2_L_VOL 0xB1
+#define DA732X_REG_DAC2_R_VOL 0xB2
+#define DA732X_REG_DAC2_SEL 0xB3
+#define DA732X_REG_DAC2_SOFTMUTE 0xB4
+#define DA732X_REG_DAC2_EQ12 0xB5
+#define DA732X_REG_DAC2_EQ34 0xB6
+#define DA732X_REG_DAC2_EQ5 0xB7
+#define DA732X_REG_DAC3_HPF 0xC0
+#define DA732X_REG_DAC3_VOL 0xC1
+#define DA732X_REG_DAC3_SEL 0xC3
+#define DA732X_REG_DAC3_SOFTMUTE 0xC4
+#define DA732X_REG_DAC3_EQ12 0xC5
+#define DA732X_REG_DAC3_EQ34 0xC6
+#define DA732X_REG_DAC3_EQ5 0xC7
+#define DA732X_REG_BIQ_BYP 0xD2
+#define DA732X_REG_DMA_CMD 0xD3
+#define DA732X_REG_DMA_ADDR0 0xD4
+#define DA732X_REG_DMA_ADDR1 0xD5
+#define DA732X_REG_DMA_DATA0 0xD6
+#define DA732X_REG_DMA_DATA1 0xD7
+#define DA732X_REG_DMA_DATA2 0xD8
+#define DA732X_REG_DMA_DATA3 0xD9
+#define DA732X_REG_DMA_STATUS 0xDA
+#define DA732X_REG_BROWNOUT 0xDF
+#define DA732X_REG_UNLOCK 0xE0
+
+#define DA732X_MAX_REG DA732X_REG_UNLOCK
+/*
+ * Bits
+ */
+
+/* DA732X_REG_STATUS_EXT (addr=0x00) */
+#define DA732X_STATUS_EXT_DSP (1 << 4)
+#define DA732X_STATUS_EXT_CLEAR (0 << 0)
+
+/* DA732X_REG_STATUS (addr=0x01) */
+#define DA732X_STATUS_PLL_LOCK (1 << 0)
+#define DA732X_STATUS_PLL_MCLK_DET (1 << 1)
+#define DA732X_STATUS_HPDET_OUT (1 << 2)
+#define DA732X_STATUS_INP_MIXDET_1 (1 << 3)
+#define DA732X_STATUS_INP_MIXDET_2 (1 << 4)
+#define DA732X_STATUS_BO_STATUS (1 << 5)
+
+/* DA732X_REG_REF1 (addr=0x02) */
+#define DA732X_VMID_FASTCHG (1 << 1)
+#define DA732X_VMID_FASTDISCHG (1 << 2)
+#define DA732X_REFBUFX2_EN (1 << 6)
+#define DA732X_REFBUFX2_DIS (0 << 6)
+
+/* DA732X_REG_BIAS_EN (addr=0x03) */
+#define DA732X_BIAS_BOOST_MASK (3 << 0)
+#define DA732X_BIAS_BOOST_100PC (0 << 0)
+#define DA732X_BIAS_BOOST_133PC (1 << 0)
+#define DA732X_BIAS_BOOST_88PC (2 << 0)
+#define DA732X_BIAS_BOOST_50PC (3 << 0)
+#define DA732X_BIAS_EN (1 << 7)
+#define DA732X_BIAS_DIS (0 << 7)
+
+/* DA732X_REG_BIAS1 (addr=0x04) */
+#define DA732X_BIAS1_HP_DAC_BIAS_MASK (3 << 0)
+#define DA732X_BIAS1_HP_DAC_BIAS_100PC (0 << 0)
+#define DA732X_BIAS1_HP_DAC_BIAS_150PC (1 << 0)
+#define DA732X_BIAS1_HP_DAC_BIAS_50PC (2 << 0)
+#define DA732X_BIAS1_HP_DAC_BIAS_75PC (3 << 0)
+#define DA732X_BIAS1_HP_OUT_BIAS_MASK (7 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_100PC (0 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_125PC (1 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_150PC (2 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_175PC (3 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_200PC (4 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_250PC (5 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_300PC (6 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_350PC (7 << 4)
+
+/* DA732X_REG_BIAS2 (addr=0x05) */
+#define DA732X_BIAS2_LINE2_DAC_BIAS_MASK (3 << 0)
+#define DA732X_BIAS2_LINE2_DAC_BIAS_100PC (0 << 0)
+#define DA732X_BIAS2_LINE2_DAC_BIAS_150PC (1 << 0)
+#define DA732X_BIAS2_LINE2_DAC_BIAS_50PC (2 << 0)
+#define DA732X_BIAS2_LINE2_DAC_BIAS_75PC (3 << 0)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_MASK (7 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_100PC (0 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_125PC (1 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_150PC (2 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_175PC (3 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_200PC (4 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_250PC (5 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_300PC (6 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_350PC (7 << 4)
+
+/* DA732X_REG_BIAS3 (addr=0x06) */
+#define DA732X_BIAS3_LINE3_DAC_BIAS_MASK (3 << 0)
+#define DA732X_BIAS3_LINE3_DAC_BIAS_100PC (0 << 0)
+#define DA732X_BIAS3_LINE3_DAC_BIAS_150PC (1 << 0)
+#define DA732X_BIAS3_LINE3_DAC_BIAS_50PC (2 << 0)
+#define DA732X_BIAS3_LINE3_DAC_BIAS_75PC (3 << 0)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_MASK (7 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_100PC (0 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_125PC (1 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_150PC (2 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_175PC (3 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_200PC (4 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_250PC (5 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_300PC (6 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_350PC (7 << 4)
+
+/* DA732X_REG_BIAS4 (addr=0x07) */
+#define DA732X_BIAS4_LINE4_DAC_BIAS_MASK (3 << 0)
+#define DA732X_BIAS4_LINE4_DAC_BIAS_100PC (0 << 0)
+#define DA732X_BIAS4_LINE4_DAC_BIAS_150PC (1 << 0)
+#define DA732X_BIAS4_LINE4_DAC_BIAS_50PC (2 << 0)
+#define DA732X_BIAS4_LINE4_DAC_BIAS_75PC (3 << 0)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_MASK (7 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_100PC (0 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_125PC (1 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_150PC (2 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_175PC (3 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_200PC (4 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_250PC (5 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_300PC (6 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_350PC (7 << 4)
+
+/* DA732X_REG_SIF_VDD_SEL (addr=0x08) */
+#define DA732X_SIF_VDD_SEL_AIFA_VDD2 (1 << 0)
+#define DA732X_SIF_VDD_SEL_AIFB_VDD2 (1 << 1)
+#define DA732X_SIF_VDD_SEL_CIFA_VDD2 (1 << 4)
+
+/* DA732X_REG_MICBIAS2/1 (addr=0x0F/0x10) */
+#define DA732X_MICBIAS_VOLTAGE_MASK (0x0F << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V (0x00 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V05 (0x01 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V1 (0x02 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V15 (0x03 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V2 (0x04 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V25 (0x05 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V3 (0x06 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V35 (0x07 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V4 (0x08 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V45 (0x09 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V5 (0x0A << 0)
+#define DA732X_MICBIAS_EN (1 << 7)
+#define DA732X_MICBIAS_EN_SHIFT 7
+#define DA732X_MICBIAS_VOLTAGE_SHIFT 0
+#define DA732X_MICBIAS_VOLTAGE_MAX 0x0B
+
+/* DA732X_REG_MICDET (addr=0x11) */
+#define DA732X_MICDET_INP_MICRES (1 << 0)
+#define DA732X_MICDET_INP_MICHOOK (1 << 1)
+#define DA732X_MICDET_INP_DEBOUNCE_PRD_8MS (0 << 0)
+#define DA732X_MICDET_INP_DEBOUNCE_PRD_16MS (1 << 0)
+#define DA732X_MICDET_INP_DEBOUNCE_PRD_32MS (2 << 0)
+#define DA732X_MICDET_INP_DEBOUNCE_PRD_64MS (3 << 0)
+#define DA732X_MICDET_INP_MICDET_EN (1 << 7)
+
+/* DA732X_REG_MIC1/2/3_PRE (addr=0x11/0x14/0x18) */
+#define DA732X_MICBOOST_MASK 0x7
+#define DA732X_MICBOOST_SHIFT 0
+#define DA732X_MICBOOST_MIN 0x1
+#define DA732X_MICBOOST_MAX DA732X_MICBOOST_MASK
+
+/* DA732X_REG_MIC1/2/3 (addr=0x13/0x15/0x19) */
+#define DA732X_MIC_VOL_SHIFT 0
+#define DA732X_MIC_VOL_VAL_MASK 0x1F
+#define DA732X_MIC_MUTE_SHIFT 6
+#define DA732X_MIC_EN_SHIFT 7
+#define DA732X_MIC_VOL_VAL_MIN 0x7
+#define DA732X_MIC_VOL_VAL_MAX DA732X_MIC_VOL_VAL_MASK
+
+/* DA732X_REG_AUX1L/R (addr=0x16/0x17) */
+#define DA732X_AUX_VOL_SHIFT 0
+#define DA732X_AUX_VOL_MASK 0x7
+#define DA732X_AUX_MUTE_SHIFT 6
+#define DA732X_AUX_EN_SHIFT 7
+#define DA732X_AUX_VOL_VAL_MAX DA732X_AUX_VOL_MASK
+
+/* DA732X_REG_INP_PINBIAS (addr=0x1A) */
+#define DA732X_INP_MICL_PINBIAS_EN (1 << 0)
+#define DA732X_INP_MICR_PINBIAS_EN (1 << 1)
+#define DA732X_INP_AUX1L_PINBIAS_EN (1 << 2)
+#define DA732X_INP_AUX1R_PINBIAS_EN (1 << 3)
+#define DA732X_INP_AUX2_PINBIAS_EN (1 << 4)
+
+/* DA732X_REG_INP_ZC_EN (addr=0x1B) */
+#define DA732X_MIC1_PRE_ZC_EN (1 << 0)
+#define DA732X_MIC1_ZC_EN (1 << 1)
+#define DA732X_MIC2_PRE_ZC_EN (1 << 2)
+#define DA732X_MIC2_ZC_EN (1 << 3)
+#define DA732X_AUXL_ZC_EN (1 << 4)
+#define DA732X_AUXR_ZC_EN (1 << 5)
+#define DA732X_MIC3_PRE_ZC_EN (1 << 6)
+#define DA732X_MIC3_ZC_EN (1 << 7)
+
+/* DA732X_REG_INP_MUX (addr=0x1D) */
+#define DA732X_INP_ADC1L_MUX_SEL_AUX1L (0 << 0)
+#define DA732X_INP_ADC1L_MUX_SEL_MIC1 (1 << 0)
+#define DA732X_INP_ADC1R_MUX_SEL_MASK (3 << 2)
+#define DA732X_INP_ADC1R_MUX_SEL_AUX1R (0 << 2)
+#define DA732X_INP_ADC1R_MUX_SEL_MIC2 (1 << 2)
+#define DA732X_INP_ADC1R_MUX_SEL_MIC3 (2 << 2)
+#define DA732X_INP_ADC2L_MUX_SEL_AUX1L (0 << 4)
+#define DA732X_INP_ADC2L_MUX_SEL_MICL (1 << 4)
+#define DA732X_INP_ADC2R_MUX_SEL_MASK (3 << 6)
+#define DA732X_INP_ADC2R_MUX_SEL_AUX1R (0 << 6)
+#define DA732X_INP_ADC2R_MUX_SEL_MICR (1 << 6)
+#define DA732X_INP_ADC2R_MUX_SEL_AUX2 (2 << 6)
+#define DA732X_ADC1L_MUX_SEL_SHIFT 0
+#define DA732X_ADC1R_MUX_SEL_SHIFT 2
+#define DA732X_ADC2L_MUX_SEL_SHIFT 4
+#define DA732X_ADC2R_MUX_SEL_SHIFT 6
+
+/* DA732X_REG_HP_DET (addr=0x20) */
+#define DA732X_HP_DET_AZ (1 << 0)
+#define DA732X_HP_DET_SEL1 (1 << 1)
+#define DA732X_HP_DET_IS_MASK (3 << 2)
+#define DA732X_HP_DET_IS_0_5UA (0 << 2)
+#define DA732X_HP_DET_IS_1UA (1 << 2)
+#define DA732X_HP_DET_IS_2UA (2 << 2)
+#define DA732X_HP_DET_IS_4UA (3 << 2)
+#define DA732X_HP_DET_RS_MASK (3 << 4)
+#define DA732X_HP_DET_RS_INFINITE (0 << 4)
+#define DA732X_HP_DET_RS_100KOHM (1 << 4)
+#define DA732X_HP_DET_RS_10KOHM (2 << 4)
+#define DA732X_HP_DET_RS_1KOHM (3 << 4)
+#define DA732X_HP_DET_EN (1 << 7)
+
+/* DA732X_REG_HPL_DAC_OFFSET (addr=0x21/0x26) */
+#define DA732X_HP_DAC_OFFSET_TRIM_MASK (0x3F << 0)
+#define DA732X_HP_DAC_OFFSET_DAC_SIGN (1 << 6)
+
+/* DA732X_REG_HPL_DAC_OFF_CNTL (addr=0x22/0x27) */
+#define DA732X_HP_DAC_OFF_CNTL_CONT_MASK (7 << 0)
+#define DA732X_HP_DAC_OFF_CNTL_COMPO (1 << 3)
+#define DA732X_HP_DAC_OFF_CALIBRATION (1 << 0)
+#define DA732X_HP_DAC_OFF_SCALE_STEPS (1 << 1)
+#define DA732X_HP_DAC_OFF_MASK 0x7F
+#define DA732X_HP_DAC_COMPO_SHIFT 3
+
+/* DA732X_REG_HPL_OUT_OFFSET (addr=0x23/0x28) */
+#define DA732X_HP_OUT_OFFSET_MASK (0xFF << 0)
+#define DA732X_HP_DAC_OFFSET_TRIM_VAL 0x7F
+
+/* DA732X_REG_HPL/R (addr=0x24/0x29) */
+#define DA732X_HP_OUT_SIGN (1 << 0)
+#define DA732X_HP_OUT_COMP (1 << 1)
+#define DA732X_HP_OUT_RESERVED (1 << 2)
+#define DA732X_HP_OUT_COMPO (1 << 3)
+#define DA732X_HP_OUT_DAC_EN (1 << 4)
+#define DA732X_HP_OUT_HIZ_EN (1 << 5)
+#define DA732X_HP_OUT_HIZ_DIS (0 << 5)
+#define DA732X_HP_OUT_MUTE (1 << 6)
+#define DA732X_HP_OUT_EN (1 << 7)
+#define DA732X_HP_OUT_COMPO_SHIFT 3
+#define DA732X_HP_OUT_DAC_EN_SHIFT 4
+#define DA732X_HP_HIZ_SHIFT 5
+#define DA732X_HP_MUTE_SHIFT 6
+#define DA732X_HP_OUT_EN_SHIFT 7
+
+#define DA732X_OUT_HIZ_EN (1 << 5)
+#define DA732X_OUT_HIZ_DIS (0 << 5)
+
+/* DA732X_REG_HPL/R_VOL (addr=0x25/0x2A) */
+#define DA732X_HP_VOL_VAL_MASK 0xF
+#define DA732X_HP_VOL_SHIFT 0
+#define DA732X_HP_VOL_VAL_MAX DA732X_HP_VOL_VAL_MASK
+
+/* DA732X_REG_LIN2/3/4 (addr=0x2B/0x2C/0x2D) */
+#define DA732X_LOUT_VOL_SHIFT 0
+#define DA732X_LOUT_VOL_MASK 0x0F
+#define DA732X_LOUT_DAC_OFF (0 << 4)
+#define DA732X_LOUT_DAC_EN (1 << 4)
+#define DA732X_LOUT_HIZ_N_DIS (0 << 5)
+#define DA732X_LOUT_HIZ_N_EN (1 << 5)
+#define DA732X_LOUT_UNMUTED (0 << 6)
+#define DA732X_LOUT_MUTED (1 << 6)
+#define DA732X_LOUT_EN (0 << 7)
+#define DA732X_LOUT_DIS (1 << 7)
+#define DA732X_LOUT_DAC_EN_SHIFT 4
+#define DA732X_LOUT_MUTE_SHIFT 6
+#define DA732X_LIN_OUT_EN_SHIFT 7
+#define DA732X_LOUT_VOL_VAL_MAX DA732X_LOUT_VOL_MASK
+
+/* DA732X_REG_OUT_ZC_EN (addr=0x2E) */
+#define DA732X_HPL_ZC_EN_SHIFT 0
+#define DA732X_HPR_ZC_EN_SHIFT 1
+#define DA732X_HPL_ZC_EN (1 << 0)
+#define DA732X_HPL_ZC_DIS (0 << 0)
+#define DA732X_HPR_ZC_EN (1 << 1)
+#define DA732X_HPR_ZC_DIS (0 << 1)
+#define DA732X_LIN2_ZC_EN (1 << 2)
+#define DA732X_LIN2_ZC_DIS (0 << 2)
+#define DA732X_LIN3_ZC_EN (1 << 3)
+#define DA732X_LIN3_ZC_DIS (0 << 3)
+#define DA732X_LIN4_ZC_EN (1 << 4)
+#define DA732X_LIN4_ZC_DIS (0 << 4)
+
+/* DA732X_REG_HP_LIN1_GNDSEL (addr=0x37) */
+#define DA732X_HP_OUT_GNDSEL (1 << 0)
+
+/* DA732X_REG_CP_HP2 (addr=0x3a) */
+#define DA732X_HP_CP_PULSESKIP (1 << 0)
+#define DA732X_HP_CP_REG (1 << 1)
+#define DA732X_HP_CP_EN (1 << 3)
+#define DA732X_HP_CP_DIS (0 << 3)
+
+/* DA732X_REG_CP_CTRL1 (addr=0x40) */
+#define DA732X_CP_MODE_MASK (7 << 1)
+#define DA732X_CP_CTRL_STANDBY (0 << 1)
+#define DA732X_CP_CTRL_CPVDD6 (2 << 1)
+#define DA732X_CP_CTRL_CPVDD5 (3 << 1)
+#define DA732X_CP_CTRL_CPVDD4 (4 << 1)
+#define DA732X_CP_CTRL_CPVDD3 (5 << 1)
+#define DA732X_CP_CTRL_CPVDD2 (6 << 1)
+#define DA732X_CP_CTRL_CPVDD1 (7 << 1)
+#define DA723X_CP_DIS (0 << 7)
+#define DA732X_CP_EN (1 << 7)
+
+/* DA732X_REG_CP_CTRL2 (addr=0x41) */
+#define DA732X_CP_BOOST (1 << 0)
+#define DA732X_CP_MANAGE_MAGNITUDE (2 << 2)
+
+/* DA732X_REG_CP_CTRL3 (addr=0x42) */
+#define DA732X_CP_1MHZ (0 << 0)
+#define DA732X_CP_500KHZ (1 << 0)
+#define DA732X_CP_250KHZ (2 << 0)
+#define DA732X_CP_125KHZ (3 << 0)
+#define DA732X_CP_63KHZ (4 << 0)
+#define DA732X_CP_0KHZ (5 << 0)
+
+/* DA732X_REG_PLL_CTRL (addr=0x53) */
+#define DA732X_PLL_INDIV_MASK (3 << 0)
+#define DA732X_PLL_SRM_EN (1 << 2)
+#define DA732X_PLL_EN (1 << 7)
+#define DA732X_PLL_BYPASS (0 << 0)
+
+/* DA732X_REG_CLK_CTRL (addr=0x54) */
+#define DA732X_SR1_MASK (0xF)
+#define DA732X_SR2_MASK (0xF0)
+
+/* DA732X_REG_CLK_DSP (addr=0x5A) */
+#define DA732X_DSP_FREQ_MASK (7 << 0)
+#define DA732X_DSP_FREQ_12MHZ (0 << 0)
+#define DA732X_DSP_FREQ_24MHZ (1 << 0)
+#define DA732X_DSP_FREQ_36MHZ (2 << 0)
+#define DA732X_DSP_FREQ_48MHZ (3 << 0)
+#define DA732X_DSP_FREQ_60MHZ (4 << 0)
+#define DA732X_DSP_FREQ_72MHZ (5 << 0)
+#define DA732X_DSP_FREQ_84MHZ (6 << 0)
+#define DA732X_DSP_FREQ_96MHZ (7 << 0)
+
+/* DA732X_REG_CLK_EN1 (addr=0x5B) */
+#define DA732X_DSP_CLK_EN (1 << 0)
+#define DA732X_SYS3_CLK_EN (1 << 1)
+#define DA732X_DSP12_CLK_EN (1 << 2)
+#define DA732X_PC_CLK_EN (1 << 3)
+#define DA732X_MCLK_SQR_EN (1 << 7)
+
+/* DA732X_REG_CLK_EN2 (addr=0x5C) */
+#define DA732X_UART_CLK_EN (1 << 1)
+#define DA732X_CP_CLK_EN (1 << 2)
+#define DA732X_CP_CLK_DIS (0 << 2)
+
+/* DA732X_REG_CLK_EN3 (addr=0x5D) */
+#define DA732X_ADCA_BB_CLK_EN (1 << 0)
+#define DA732X_ADCC_BB_CLK_EN (1 << 4)
+
+/* DA732X_REG_CLK_EN4 (addr=0x5E) */
+#define DA732X_DACA_BB_CLK_EN (1 << 0)
+#define DA732X_DACC_BB_CLK_EN (1 << 4)
+#define DA732X_DACA_BB_CLK_SHIFT 0
+#define DA732X_DACC_BB_CLK_SHIFT 4
+
+/* DA732X_REG_CLK_EN5 (addr=0x5F) */
+#define DA732X_DACE_BB_CLK_EN (1 << 0)
+#define DA732X_DACE_BB_CLK_SHIFT 0
+
+/* DA732X_REG_AIF_MCLK (addr=0x60) */
+#define DA732X_AIFM_FRAME_64 (1 << 2)
+#define DA732X_AIFM_SRC_SEL_AIFA (1 << 6)
+#define DA732X_CLK_GENERATION_AIF_A (1 << 4)
+#define DA732X_NO_CLK_GENERATION 0x0
+
+/* DA732X_REG_AIFA1 (addr=0x61) */
+#define DA732X_AIF_WORD_MASK (0x3 << 0)
+#define DA732X_AIF_WORD_16 (0 << 0)
+#define DA732X_AIF_WORD_20 (1 << 0)
+#define DA732X_AIF_WORD_24 (2 << 0)
+#define DA732X_AIF_WORD_32 (3 << 0)
+#define DA732X_AIF_TDM_MONO_SHIFT (1 << 6)
+#define DA732X_AIF1_CLK_MASK (1 << 7)
+#define DA732X_AIF_SLAVE (0 << 7)
+#define DA732X_AIF_CLK_FROM_SRC (1 << 7)
+
+/* DA732X_REG_AIFA3 (addr=0x63) */
+#define DA732X_AIF_MODE_SHIFT 0
+#define DA732X_AIF_MODE_MASK 0x3
+#define DA732X_AIF_I2S_MODE (0 << 0)
+#define DA732X_AIF_LEFT_J_MODE (1 << 0)
+#define DA732X_AIF_RIGHT_J_MODE (2 << 0)
+#define DA732X_AIF_DSP_MODE (3 << 0)
+#define DA732X_AIF_WCLK_INV (1 << 4)
+#define DA732X_AIF_BCLK_INV (1 << 5)
+#define DA732X_AIF_EN (1 << 7)
+#define DA732X_AIF_EN_SHIFT 7
+
+/* DA732X_REG_PC_CTRL (addr=0x6a) */
+#define DA732X_PC_PULSE_AIFA (0 << 0)
+#define DA732X_PC_PULSE_AIFB (1 << 0)
+#define DA732X_PC_RESYNC_AUT (1 << 6)
+#define DA732X_PC_RESYNC_NOT_AUT (0 << 6)
+#define DA732X_PC_SAME (1 << 7)
+
+/* DA732X_REG_DATA_ROUTE (addr=0x70) */
+#define DA732X_ADC1_TO_AIFA (0 << 0)
+#define DA732X_DSP_TO_AIFA (1 << 0)
+#define DA732X_ADC2_TO_AIFB (0 << 1)
+#define DA732X_DSP_TO_AIFB (1 << 1)
+#define DA732X_AIFA_TO_DAC1L (0 << 2)
+#define DA732X_DSP_TO_DAC1L (1 << 2)
+#define DA732X_AIFA_TO_DAC1R (0 << 3)
+#define DA732X_DSP_TO_DAC1R (1 << 3)
+#define DA732X_AIFB_TO_DAC2L (0 << 4)
+#define DA732X_DSP_TO_DAC2L (1 << 4)
+#define DA732X_AIFB_TO_DAC2R (0 << 5)
+#define DA732X_DSP_TO_DAC2R (1 << 5)
+#define DA732X_AIFB_TO_DAC3 (0 << 6)
+#define DA732X_DSP_TO_DAC3 (1 << 6)
+#define DA732X_BYPASS_DSP (0 << 0)
+#define DA732X_ALL_TO_DSP (0x7F << 0)
+
+/* DA732X_REG_DSP_CTRL (addr=0x71) */
+#define DA732X_DIGITAL_EN (1 << 0)
+#define DA732X_DIGITAL_RESET (0 << 0)
+#define DA732X_DSP_CORE_EN (1 << 1)
+#define DA732X_DSP_CORE_RESET (0 << 1)
+
+/* DA732X_REG_SPARE1_OUT (addr=0x7D)*/
+#define DA732X_HP_DRIVER_EN (1 << 0)
+#define DA732X_HP_GATE_LOW (1 << 2)
+#define DA732X_HP_LOOP_GAIN_CTRL (1 << 3)
+
+/* DA732X_REG_ID (addr=0x81)*/
+#define DA732X_ID_MINOR_MASK (0xF << 0)
+#define DA732X_ID_MAJOR_MASK (0xF << 4)
+
+/* DA732X_REG_ADC1/2_PD (addr=0x90/0x98) */
+#define DA732X_ADC_RST_MASK (0x3 << 0)
+#define DA732X_ADC_PD_MASK (0x3 << 2)
+#define DA732X_ADC_SET_ACT (0x3 << 0)
+#define DA732X_ADC_SET_RST (0x0 << 0)
+#define DA732X_ADC_ON (0x3 << 2)
+#define DA732X_ADC_OFF (0x0 << 2)
+
+/* DA732X_REG_ADC1/2_SEL (addr=0x94/0x9C) */
+#define DA732X_ADC_VOL_VAL_MASK 0x7
+#define DA732X_ADCL_VOL_SHIFT 0
+#define DA732X_ADCR_VOL_SHIFT 4
+#define DA732X_ADCL_EN_SHIFT 2
+#define DA732X_ADCR_EN_SHIFT 3
+#define DA732X_ADCL_EN (1 << 2)
+#define DA732X_ADCR_EN (1 << 3)
+#define DA732X_ADC_VOL_VAL_MAX DA732X_ADC_VOL_VAL_MASK
+
+/*
+ * DA732X_REG_ADC1/2_HPF (addr=0x93/0x9b)
+ * DA732x_REG_DAC1/2/3_HPG (addr=0xA5/0xB5/0xC5)
+ */
+#define DA732X_HPF_MUSIC_EN (1 << 3)
+#define DA732X_HPF_VOICE_EN ((1 << 3) | (1 << 7))
+#define DA732X_HPF_MASK ((1 << 3) | (1 << 7))
+#define DA732X_HPF_DIS ((0 << 3) | (0 << 7))
+
+/* DA732X_REG_DAC1/2/3_VOL */
+#define DA732X_DAC_VOL_VAL_MASK 0x7F
+#define DA732X_DAC_VOL_SHIFT 0
+#define DA732X_DAC_VOL_VAL_MAX DA732X_DAC_VOL_VAL_MASK
+
+/* DA732X_REG_DAC1/2/3_SEL (addr=0xA3/0xB3/0xC3) */
+#define DA732X_DACL_EN_SHIFT 3
+#define DA732X_DACR_EN_SHIFT 7
+#define DA732X_DACL_MUTE_SHIFT 2
+#define DA732X_DACR_MUTE_SHIFT 6
+#define DA732X_DACL_EN (1 << 3)
+#define DA732X_DACR_EN (1 << 7)
+#define DA732X_DACL_SDM (1 << 0)
+#define DA732X_DACR_SDM (1 << 4)
+#define DA732X_DACL_MUTE (1 << 2)
+#define DA732X_DACR_MUTE (1 << 6)
+
+/* DA732X_REG_DAC_SOFTMUTE (addr=0xA4/0xB4/0xC4) */
+#define DA732X_SOFTMUTE_EN (1 << 7)
+#define DA732X_GAIN_RAMPED (1 << 6)
+#define DA732X_16_SAMPLES (4 << 0)
+#define DA732X_SOFTMUTE_MASK (1 << 7)
+#define DA732X_SOFTMUTE_SHIFT 7
+
+/*
+ * DA732x_REG_ADC1/2_EQ12 (addr=0x95/0x9D)
+ * DA732x_REG_ADC1/2_EQ34 (addr=0x96/0x9E)
+ * DA732x_REG_ADC1/2_EQ5 (addr=0x97/0x9F)
+ * DA732x_REG_DAC1/2/3_EQ12 (addr=0xA5/0xB5/0xC5)
+ * DA732x_REG_DAC1/2/3_EQ34 (addr=0xA6/0xB6/0xC6)
+ * DA732x_REG_DAC1/2/3_EQ5 (addr=0xA7/0xB7/0xB7)
+ */
+#define DA732X_EQ_VOL_VAL_MASK 0xF
+#define DA732X_EQ_BAND1_SHIFT 0
+#define DA732X_EQ_BAND2_SHIFT 4
+#define DA732X_EQ_BAND3_SHIFT 0
+#define DA732X_EQ_BAND4_SHIFT 4
+#define DA732X_EQ_BAND5_SHIFT 0
+#define DA732X_EQ_OVERALL_SHIFT 4
+#define DA732X_EQ_OVERALL_VOL_VAL_MASK 0x3
+#define DA732X_EQ_DIS (0 << 7)
+#define DA732X_EQ_EN (1 << 7)
+#define DA732X_EQ_EN_SHIFT 7
+#define DA732X_EQ_VOL_VAL_MAX DA732X_EQ_VOL_VAL_MASK
+#define DA732X_EQ_OVERALL_VOL_VAL_MAX DA732X_EQ_OVERALL_VOL_VAL_MASK
+
+/* DA732X_REG_DMA_CMD (addr=0xD3) */
+#define DA732X_SEL_DSP_DMA_MASK (3 << 0)
+#define DA732X_SEL_DSP_DMA_DIS (0 << 0)
+#define DA732X_SEL_DSP_DMA_PMEM (1 << 0)
+#define DA732X_SEL_DSP_DMA_XMEM (2 << 0)
+#define DA732X_SEL_DSP_DMA_YMEM (3 << 0)
+#define DA732X_DSP_RW_MASK (1 << 4)
+#define DA732X_DSP_DMA_WRITE (0 << 4)
+#define DA732X_DSP_DMA_READ (1 << 4)
+
+/* DA732X_REG_DMA_STATUS (addr=0xDA) */
+#define DA732X_DSP_DMA_FREE (0 << 0)
+#define DA732X_DSP_DMA_BUSY (1 << 0)
+
+#endif /* __DA732X_REG_H_ */
diff --git a/kernel/sound/soc/codecs/da9055.c b/kernel/sound/soc/codecs/da9055.c
new file mode 100644
index 000000000..ad19cc567
--- /dev/null
+++ b/kernel/sound/soc/codecs/da9055.c
@@ -0,0 +1,1554 @@
+/*
+ * DA9055 ALSA Soc codec driver
+ *
+ * Copyright (c) 2012 Dialog Semiconductor
+ *
+ * Tested on (Samsung SMDK6410 board + DA9055 EVB) using I2S and I2C
+ * Written by David Chen <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>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#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>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/da9055.h>
+
+/* DA9055 register space */
+
+/* Status Registers */
+#define DA9055_STATUS1 0x02
+#define DA9055_PLL_STATUS 0x03
+#define DA9055_AUX_L_GAIN_STATUS 0x04
+#define DA9055_AUX_R_GAIN_STATUS 0x05
+#define DA9055_MIC_L_GAIN_STATUS 0x06
+#define DA9055_MIC_R_GAIN_STATUS 0x07
+#define DA9055_MIXIN_L_GAIN_STATUS 0x08
+#define DA9055_MIXIN_R_GAIN_STATUS 0x09
+#define DA9055_ADC_L_GAIN_STATUS 0x0A
+#define DA9055_ADC_R_GAIN_STATUS 0x0B
+#define DA9055_DAC_L_GAIN_STATUS 0x0C
+#define DA9055_DAC_R_GAIN_STATUS 0x0D
+#define DA9055_HP_L_GAIN_STATUS 0x0E
+#define DA9055_HP_R_GAIN_STATUS 0x0F
+#define DA9055_LINE_GAIN_STATUS 0x10
+
+/* System Initialisation Registers */
+#define DA9055_CIF_CTRL 0x20
+#define DA9055_DIG_ROUTING_AIF 0X21
+#define DA9055_SR 0x22
+#define DA9055_REFERENCES 0x23
+#define DA9055_PLL_FRAC_TOP 0x24
+#define DA9055_PLL_FRAC_BOT 0x25
+#define DA9055_PLL_INTEGER 0x26
+#define DA9055_PLL_CTRL 0x27
+#define DA9055_AIF_CLK_MODE 0x28
+#define DA9055_AIF_CTRL 0x29
+#define DA9055_DIG_ROUTING_DAC 0x2A
+#define DA9055_ALC_CTRL1 0x2B
+
+/* Input - Gain, Select and Filter Registers */
+#define DA9055_AUX_L_GAIN 0x30
+#define DA9055_AUX_R_GAIN 0x31
+#define DA9055_MIXIN_L_SELECT 0x32
+#define DA9055_MIXIN_R_SELECT 0x33
+#define DA9055_MIXIN_L_GAIN 0x34
+#define DA9055_MIXIN_R_GAIN 0x35
+#define DA9055_ADC_L_GAIN 0x36
+#define DA9055_ADC_R_GAIN 0x37
+#define DA9055_ADC_FILTERS1 0x38
+#define DA9055_MIC_L_GAIN 0x39
+#define DA9055_MIC_R_GAIN 0x3A
+
+/* Output - Gain, Select and Filter Registers */
+#define DA9055_DAC_FILTERS5 0x40
+#define DA9055_DAC_FILTERS2 0x41
+#define DA9055_DAC_FILTERS3 0x42
+#define DA9055_DAC_FILTERS4 0x43
+#define DA9055_DAC_FILTERS1 0x44
+#define DA9055_DAC_L_GAIN 0x45
+#define DA9055_DAC_R_GAIN 0x46
+#define DA9055_CP_CTRL 0x47
+#define DA9055_HP_L_GAIN 0x48
+#define DA9055_HP_R_GAIN 0x49
+#define DA9055_LINE_GAIN 0x4A
+#define DA9055_MIXOUT_L_SELECT 0x4B
+#define DA9055_MIXOUT_R_SELECT 0x4C
+
+/* System Controller Registers */
+#define DA9055_SYSTEM_MODES_INPUT 0x50
+#define DA9055_SYSTEM_MODES_OUTPUT 0x51
+
+/* Control Registers */
+#define DA9055_AUX_L_CTRL 0x60
+#define DA9055_AUX_R_CTRL 0x61
+#define DA9055_MIC_BIAS_CTRL 0x62
+#define DA9055_MIC_L_CTRL 0x63
+#define DA9055_MIC_R_CTRL 0x64
+#define DA9055_MIXIN_L_CTRL 0x65
+#define DA9055_MIXIN_R_CTRL 0x66
+#define DA9055_ADC_L_CTRL 0x67
+#define DA9055_ADC_R_CTRL 0x68
+#define DA9055_DAC_L_CTRL 0x69
+#define DA9055_DAC_R_CTRL 0x6A
+#define DA9055_HP_L_CTRL 0x6B
+#define DA9055_HP_R_CTRL 0x6C
+#define DA9055_LINE_CTRL 0x6D
+#define DA9055_MIXOUT_L_CTRL 0x6E
+#define DA9055_MIXOUT_R_CTRL 0x6F
+
+/* Configuration Registers */
+#define DA9055_LDO_CTRL 0x90
+#define DA9055_IO_CTRL 0x91
+#define DA9055_GAIN_RAMP_CTRL 0x92
+#define DA9055_MIC_CONFIG 0x93
+#define DA9055_PC_COUNT 0x94
+#define DA9055_CP_VOL_THRESHOLD1 0x95
+#define DA9055_CP_DELAY 0x96
+#define DA9055_CP_DETECTOR 0x97
+#define DA9055_AIF_OFFSET 0x98
+#define DA9055_DIG_CTRL 0x99
+#define DA9055_ALC_CTRL2 0x9A
+#define DA9055_ALC_CTRL3 0x9B
+#define DA9055_ALC_NOISE 0x9C
+#define DA9055_ALC_TARGET_MIN 0x9D
+#define DA9055_ALC_TARGET_MAX 0x9E
+#define DA9055_ALC_GAIN_LIMITS 0x9F
+#define DA9055_ALC_ANA_GAIN_LIMITS 0xA0
+#define DA9055_ALC_ANTICLIP_CTRL 0xA1
+#define DA9055_ALC_ANTICLIP_LEVEL 0xA2
+#define DA9055_ALC_OFFSET_OP2M_L 0xA6
+#define DA9055_ALC_OFFSET_OP2U_L 0xA7
+#define DA9055_ALC_OFFSET_OP2M_R 0xAB
+#define DA9055_ALC_OFFSET_OP2U_R 0xAC
+#define DA9055_ALC_CIC_OP_LVL_CTRL 0xAD
+#define DA9055_ALC_CIC_OP_LVL_DATA 0xAE
+#define DA9055_DAC_NG_SETUP_TIME 0xAF
+#define DA9055_DAC_NG_OFF_THRESHOLD 0xB0
+#define DA9055_DAC_NG_ON_THRESHOLD 0xB1
+#define DA9055_DAC_NG_CTRL 0xB2
+
+/* SR bit fields */
+#define DA9055_SR_8000 (0x1 << 0)
+#define DA9055_SR_11025 (0x2 << 0)
+#define DA9055_SR_12000 (0x3 << 0)
+#define DA9055_SR_16000 (0x5 << 0)
+#define DA9055_SR_22050 (0x6 << 0)
+#define DA9055_SR_24000 (0x7 << 0)
+#define DA9055_SR_32000 (0x9 << 0)
+#define DA9055_SR_44100 (0xA << 0)
+#define DA9055_SR_48000 (0xB << 0)
+#define DA9055_SR_88200 (0xE << 0)
+#define DA9055_SR_96000 (0xF << 0)
+
+/* REFERENCES bit fields */
+#define DA9055_BIAS_EN (1 << 3)
+#define DA9055_VMID_EN (1 << 7)
+
+/* PLL_CTRL bit fields */
+#define DA9055_PLL_INDIV_10_20_MHZ (1 << 2)
+#define DA9055_PLL_SRM_EN (1 << 6)
+#define DA9055_PLL_EN (1 << 7)
+
+/* AIF_CLK_MODE bit fields */
+#define DA9055_AIF_BCLKS_PER_WCLK_32 (0 << 0)
+#define DA9055_AIF_BCLKS_PER_WCLK_64 (1 << 0)
+#define DA9055_AIF_BCLKS_PER_WCLK_128 (2 << 0)
+#define DA9055_AIF_BCLKS_PER_WCLK_256 (3 << 0)
+#define DA9055_AIF_CLK_EN_SLAVE_MODE (0 << 7)
+#define DA9055_AIF_CLK_EN_MASTER_MODE (1 << 7)
+
+/* AIF_CTRL bit fields */
+#define DA9055_AIF_FORMAT_I2S_MODE (0 << 0)
+#define DA9055_AIF_FORMAT_LEFT_J (1 << 0)
+#define DA9055_AIF_FORMAT_RIGHT_J (2 << 0)
+#define DA9055_AIF_FORMAT_DSP (3 << 0)
+#define DA9055_AIF_WORD_S16_LE (0 << 2)
+#define DA9055_AIF_WORD_S20_3LE (1 << 2)
+#define DA9055_AIF_WORD_S24_LE (2 << 2)
+#define DA9055_AIF_WORD_S32_LE (3 << 2)
+
+/* MIC_L_CTRL bit fields */
+#define DA9055_MIC_L_MUTE_EN (1 << 6)
+
+/* MIC_R_CTRL bit fields */
+#define DA9055_MIC_R_MUTE_EN (1 << 6)
+
+/* MIXIN_L_CTRL bit fields */
+#define DA9055_MIXIN_L_MIX_EN (1 << 3)
+
+/* MIXIN_R_CTRL bit fields */
+#define DA9055_MIXIN_R_MIX_EN (1 << 3)
+
+/* ADC_L_CTRL bit fields */
+#define DA9055_ADC_L_EN (1 << 7)
+
+/* ADC_R_CTRL bit fields */
+#define DA9055_ADC_R_EN (1 << 7)
+
+/* DAC_L_CTRL bit fields */
+#define DA9055_DAC_L_MUTE_EN (1 << 6)
+
+/* DAC_R_CTRL bit fields */
+#define DA9055_DAC_R_MUTE_EN (1 << 6)
+
+/* HP_L_CTRL bit fields */
+#define DA9055_HP_L_AMP_OE (1 << 3)
+
+/* HP_R_CTRL bit fields */
+#define DA9055_HP_R_AMP_OE (1 << 3)
+
+/* LINE_CTRL bit fields */
+#define DA9055_LINE_AMP_OE (1 << 3)
+
+/* MIXOUT_L_CTRL bit fields */
+#define DA9055_MIXOUT_L_MIX_EN (1 << 3)
+
+/* MIXOUT_R_CTRL bit fields */
+#define DA9055_MIXOUT_R_MIX_EN (1 << 3)
+
+/* MIC bias select bit fields */
+#define DA9055_MICBIAS2_EN (1 << 6)
+
+/* ALC_CIC_OP_LEVEL_CTRL bit fields */
+#define DA9055_ALC_DATA_MIDDLE (2 << 0)
+#define DA9055_ALC_DATA_TOP (3 << 0)
+#define DA9055_ALC_CIC_OP_CHANNEL_LEFT (0 << 7)
+#define DA9055_ALC_CIC_OP_CHANNEL_RIGHT (1 << 7)
+
+#define DA9055_AIF_BCLK_MASK (3 << 0)
+#define DA9055_AIF_CLK_MODE_MASK (1 << 7)
+#define DA9055_AIF_FORMAT_MASK (3 << 0)
+#define DA9055_AIF_WORD_LENGTH_MASK (3 << 2)
+#define DA9055_GAIN_RAMPING_EN (1 << 5)
+#define DA9055_MICBIAS_LEVEL_MASK (3 << 4)
+
+#define DA9055_ALC_OFFSET_15_8 0x00FF00
+#define DA9055_ALC_OFFSET_17_16 0x030000
+#define DA9055_ALC_AVG_ITERATIONS 5
+
+struct pll_div {
+ int fref;
+ int fout;
+ u8 frac_top;
+ u8 frac_bot;
+ u8 integer;
+ u8 mode; /* 0 = slave, 1 = master */
+};
+
+/* PLL divisor table */
+static const struct pll_div da9055_pll_div[] = {
+ /* for MASTER mode, fs = 44.1Khz and its harmonics */
+ {11289600, 2822400, 0x00, 0x00, 0x20, 1}, /* MCLK=11.2896Mhz */
+ {12000000, 2822400, 0x03, 0x61, 0x1E, 1}, /* MCLK=12Mhz */
+ {12288000, 2822400, 0x0C, 0xCC, 0x1D, 1}, /* MCLK=12.288Mhz */
+ {13000000, 2822400, 0x19, 0x45, 0x1B, 1}, /* MCLK=13Mhz */
+ {13500000, 2822400, 0x18, 0x56, 0x1A, 1}, /* MCLK=13.5Mhz */
+ {14400000, 2822400, 0x02, 0xD0, 0x19, 1}, /* MCLK=14.4Mhz */
+ {19200000, 2822400, 0x1A, 0x1C, 0x12, 1}, /* MCLK=19.2Mhz */
+ {19680000, 2822400, 0x0B, 0x6D, 0x12, 1}, /* MCLK=19.68Mhz */
+ {19800000, 2822400, 0x07, 0xDD, 0x12, 1}, /* MCLK=19.8Mhz */
+ /* for MASTER mode, fs = 48Khz and its harmonics */
+ {11289600, 3072000, 0x1A, 0x8E, 0x22, 1}, /* MCLK=11.2896Mhz */
+ {12000000, 3072000, 0x18, 0x93, 0x20, 1}, /* MCLK=12Mhz */
+ {12288000, 3072000, 0x00, 0x00, 0x20, 1}, /* MCLK=12.288Mhz */
+ {13000000, 3072000, 0x07, 0xEA, 0x1E, 1}, /* MCLK=13Mhz */
+ {13500000, 3072000, 0x04, 0x11, 0x1D, 1}, /* MCLK=13.5Mhz */
+ {14400000, 3072000, 0x09, 0xD0, 0x1B, 1}, /* MCLK=14.4Mhz */
+ {19200000, 3072000, 0x0F, 0x5C, 0x14, 1}, /* MCLK=19.2Mhz */
+ {19680000, 3072000, 0x1F, 0x60, 0x13, 1}, /* MCLK=19.68Mhz */
+ {19800000, 3072000, 0x1B, 0x80, 0x13, 1}, /* MCLK=19.8Mhz */
+ /* for SLAVE mode with SRM */
+ {11289600, 2822400, 0x0D, 0x47, 0x21, 0}, /* MCLK=11.2896Mhz */
+ {12000000, 2822400, 0x0D, 0xFA, 0x1F, 0}, /* MCLK=12Mhz */
+ {12288000, 2822400, 0x16, 0x66, 0x1E, 0}, /* MCLK=12.288Mhz */
+ {13000000, 2822400, 0x00, 0x98, 0x1D, 0}, /* MCLK=13Mhz */
+ {13500000, 2822400, 0x1E, 0x33, 0x1B, 0}, /* MCLK=13.5Mhz */
+ {14400000, 2822400, 0x06, 0x50, 0x1A, 0}, /* MCLK=14.4Mhz */
+ {19200000, 2822400, 0x14, 0xBC, 0x13, 0}, /* MCLK=19.2Mhz */
+ {19680000, 2822400, 0x05, 0x66, 0x13, 0}, /* MCLK=19.68Mhz */
+ {19800000, 2822400, 0x01, 0xAE, 0x13, 0}, /* MCLK=19.8Mhz */
+};
+
+enum clk_src {
+ DA9055_CLKSRC_MCLK
+};
+
+/* Gain and Volume */
+
+static const unsigned int aux_vol_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 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),
+ 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),
+ 0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* 0dB to 36dB */
+ 0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0)
+};
+
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(lineout_vol_tlv, -4800, 100, 0);
+static const DECLARE_TLV_DB_SCALE(alc_threshold_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(alc_gain_tlv, 0, 600, 0);
+
+/* ADC and DAC high pass filter cutoff value */
+static const char * const da9055_hpf_cutoff_txt[] = {
+ "Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_hpf_cutoff,
+ DA9055_DAC_FILTERS1, 4, da9055_hpf_cutoff_txt);
+
+static SOC_ENUM_SINGLE_DECL(da9055_adc_hpf_cutoff,
+ DA9055_ADC_FILTERS1, 4, da9055_hpf_cutoff_txt);
+
+/* ADC and DAC voice mode (8kHz) high pass cutoff value */
+static const char * const da9055_vf_cutoff_txt[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_vf_cutoff,
+ DA9055_DAC_FILTERS1, 0, da9055_vf_cutoff_txt);
+
+static SOC_ENUM_SINGLE_DECL(da9055_adc_vf_cutoff,
+ DA9055_ADC_FILTERS1, 0, da9055_vf_cutoff_txt);
+
+/* Gain ramping rate value */
+static const char * const da9055_gain_ramping_txt[] = {
+ "nominal rate", "nominal rate * 4", "nominal rate * 8",
+ "nominal rate / 8"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_gain_ramping_rate,
+ DA9055_GAIN_RAMP_CTRL, 0, da9055_gain_ramping_txt);
+
+/* DAC noise gate setup time value */
+static const char * const da9055_dac_ng_setup_time_txt[] = {
+ "256 samples", "512 samples", "1024 samples", "2048 samples"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_setup_time,
+ DA9055_DAC_NG_SETUP_TIME, 0,
+ da9055_dac_ng_setup_time_txt);
+
+/* DAC noise gate rampup rate value */
+static const char * const da9055_dac_ng_rampup_txt[] = {
+ "0.02 ms/dB", "0.16 ms/dB"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_rampup_rate,
+ DA9055_DAC_NG_SETUP_TIME, 2,
+ da9055_dac_ng_rampup_txt);
+
+/* DAC noise gate rampdown rate value */
+static const char * const da9055_dac_ng_rampdown_txt[] = {
+ "0.64 ms/dB", "20.48 ms/dB"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_rampdown_rate,
+ DA9055_DAC_NG_SETUP_TIME, 3,
+ da9055_dac_ng_rampdown_txt);
+
+/* DAC soft mute rate value */
+static const char * const da9055_dac_soft_mute_rate_txt[] = {
+ "1", "2", "4", "8", "16", "32", "64"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_soft_mute_rate,
+ DA9055_DAC_FILTERS5, 4,
+ da9055_dac_soft_mute_rate_txt);
+
+/* DAC routing select */
+static const char * const da9055_dac_src_txt[] = {
+ "ADC output left", "ADC output right", "AIF input left",
+ "AIF input right"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_l_src,
+ DA9055_DIG_ROUTING_DAC, 0, da9055_dac_src_txt);
+
+static SOC_ENUM_SINGLE_DECL(da9055_dac_r_src,
+ DA9055_DIG_ROUTING_DAC, 4, da9055_dac_src_txt);
+
+/* MIC PGA Left source select */
+static const char * const da9055_mic_l_src_txt[] = {
+ "MIC1_P_N", "MIC1_P", "MIC1_N", "MIC2_L"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_mic_l_src,
+ DA9055_MIXIN_L_SELECT, 4, da9055_mic_l_src_txt);
+
+/* MIC PGA Right source select */
+static const char * const da9055_mic_r_src_txt[] = {
+ "MIC2_R_L", "MIC2_R", "MIC2_L"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_mic_r_src,
+ DA9055_MIXIN_R_SELECT, 4, da9055_mic_r_src_txt);
+
+/* ALC Input Signal Tracking rate select */
+static const char * const da9055_signal_tracking_rate_txt[] = {
+ "1/4", "1/16", "1/256", "1/65536"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_integ_attack_rate,
+ DA9055_ALC_CTRL3, 4,
+ da9055_signal_tracking_rate_txt);
+
+static SOC_ENUM_SINGLE_DECL(da9055_integ_release_rate,
+ DA9055_ALC_CTRL3, 6,
+ da9055_signal_tracking_rate_txt);
+
+/* ALC Attack Rate select */
+static const char * const da9055_attack_rate_txt[] = {
+ "44/fs", "88/fs", "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs",
+ "5632/fs", "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_attack_rate,
+ DA9055_ALC_CTRL2, 0, da9055_attack_rate_txt);
+
+/* ALC Release Rate select */
+static const char * const da9055_release_rate_txt[] = {
+ "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs", "5632/fs",
+ "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_release_rate,
+ DA9055_ALC_CTRL2, 4, da9055_release_rate_txt);
+
+/* ALC Hold Time select */
+static const char * const da9055_hold_time_txt[] = {
+ "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs",
+ "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs",
+ "253952/fs", "507904/fs", "1015808/fs", "2031616/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(da9055_hold_time,
+ DA9055_ALC_CTRL3, 0, da9055_hold_time_txt);
+
+static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
+{
+ int mid_data, top_data;
+ int sum = 0;
+ u8 iteration;
+
+ for (iteration = 0; iteration < DA9055_ALC_AVG_ITERATIONS;
+ iteration++) {
+ /* Select the left or right channel and capture data */
+ snd_soc_write(codec, 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,
+ reg_val | DA9055_ALC_DATA_MIDDLE);
+ mid_data = snd_soc_read(codec, 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,
+ reg_val | DA9055_ALC_DATA_TOP);
+ top_data = snd_soc_read(codec, DA9055_ALC_CIC_OP_LVL_DATA);
+
+ sum += ((mid_data << 8) | (top_data << 16));
+ }
+
+ return sum / DA9055_ALC_AVG_ITERATIONS;
+}
+
+static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ u8 reg_val, adc_left, adc_right, mic_left, mic_right;
+ int avg_left_data, avg_right_data, offset_l, offset_r;
+
+ if (ucontrol->value.integer.value[0]) {
+ /*
+ * While enabling ALC (or ALC sync mode), calibration of the DC
+ * offsets must be done first
+ */
+
+ /* Save current values from Mic control registers */
+ mic_left = snd_soc_read(codec, DA9055_MIC_L_CTRL);
+ mic_right = snd_soc_read(codec, DA9055_MIC_R_CTRL);
+
+ /* Mute Mic PGA Left and Right */
+ snd_soc_update_bits(codec, DA9055_MIC_L_CTRL,
+ DA9055_MIC_L_MUTE_EN, DA9055_MIC_L_MUTE_EN);
+ snd_soc_update_bits(codec, 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);
+
+ /* Enable ADC Left and Right */
+ snd_soc_update_bits(codec, DA9055_ADC_L_CTRL,
+ DA9055_ADC_L_EN, DA9055_ADC_L_EN);
+ snd_soc_update_bits(codec, 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,
+ DA9055_ALC_CIC_OP_CHANNEL_LEFT);
+ /* Right Data */
+ avg_right_data = da9055_get_alc_data(codec,
+ DA9055_ALC_CIC_OP_CHANNEL_RIGHT);
+
+ /* Calculate DC offset */
+ offset_l = -avg_left_data;
+ offset_r = -avg_right_data;
+
+ reg_val = (offset_l & DA9055_ALC_OFFSET_15_8) >> 8;
+ snd_soc_write(codec, 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);
+
+ reg_val = (offset_r & DA9055_ALC_OFFSET_15_8) >> 8;
+ snd_soc_write(codec, 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);
+
+ /* 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);
+
+ /* 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);
+ }
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+static const struct snd_kcontrol_new da9055_snd_controls[] = {
+
+ /* Volume controls */
+ SOC_DOUBLE_R_TLV("Mic Volume",
+ DA9055_MIC_L_GAIN, DA9055_MIC_R_GAIN,
+ 0, 0x7, 0, mic_vol_tlv),
+ SOC_DOUBLE_R_TLV("Aux Volume",
+ DA9055_AUX_L_GAIN, DA9055_AUX_R_GAIN,
+ 0, 0x3f, 0, aux_vol_tlv),
+ SOC_DOUBLE_R_TLV("Mixin PGA Volume",
+ DA9055_MIXIN_L_GAIN, DA9055_MIXIN_R_GAIN,
+ 0, 0xf, 0, mixin_gain_tlv),
+ SOC_DOUBLE_R_TLV("ADC Volume",
+ DA9055_ADC_L_GAIN, DA9055_ADC_R_GAIN,
+ 0, 0x7f, 0, digital_gain_tlv),
+
+ SOC_DOUBLE_R_TLV("DAC Volume",
+ DA9055_DAC_L_GAIN, DA9055_DAC_R_GAIN,
+ 0, 0x7f, 0, digital_gain_tlv),
+ SOC_DOUBLE_R_TLV("Headphone Volume",
+ DA9055_HP_L_GAIN, DA9055_HP_R_GAIN,
+ 0, 0x3f, 0, hp_vol_tlv),
+ SOC_SINGLE_TLV("Lineout Volume", DA9055_LINE_GAIN, 0, 0x3f, 0,
+ lineout_vol_tlv),
+
+ /* DAC Equalizer controls */
+ SOC_SINGLE("DAC EQ Switch", DA9055_DAC_FILTERS4, 7, 1, 0),
+ SOC_SINGLE_TLV("DAC EQ1 Volume", DA9055_DAC_FILTERS2, 0, 0xf, 0,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ2 Volume", DA9055_DAC_FILTERS2, 4, 0xf, 0,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ3 Volume", DA9055_DAC_FILTERS3, 0, 0xf, 0,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ4 Volume", DA9055_DAC_FILTERS3, 4, 0xf, 0,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ5 Volume", DA9055_DAC_FILTERS4, 0, 0xf, 0,
+ eq_gain_tlv),
+
+ /* High Pass Filter and Voice Mode controls */
+ SOC_SINGLE("ADC HPF Switch", DA9055_ADC_FILTERS1, 7, 1, 0),
+ SOC_ENUM("ADC HPF Cutoff", da9055_adc_hpf_cutoff),
+ SOC_SINGLE("ADC Voice Mode Switch", DA9055_ADC_FILTERS1, 3, 1, 0),
+ SOC_ENUM("ADC Voice Cutoff", da9055_adc_vf_cutoff),
+
+ SOC_SINGLE("DAC HPF Switch", DA9055_DAC_FILTERS1, 7, 1, 0),
+ SOC_ENUM("DAC HPF Cutoff", da9055_dac_hpf_cutoff),
+ SOC_SINGLE("DAC Voice Mode Switch", DA9055_DAC_FILTERS1, 3, 1, 0),
+ SOC_ENUM("DAC Voice Cutoff", da9055_dac_vf_cutoff),
+
+ /* Mute controls */
+ SOC_DOUBLE_R("Mic Switch", DA9055_MIC_L_CTRL,
+ DA9055_MIC_R_CTRL, 6, 1, 0),
+ SOC_DOUBLE_R("Aux Switch", DA9055_AUX_L_CTRL,
+ DA9055_AUX_R_CTRL, 6, 1, 0),
+ SOC_DOUBLE_R("Mixin PGA Switch", DA9055_MIXIN_L_CTRL,
+ DA9055_MIXIN_R_CTRL, 6, 1, 0),
+ SOC_DOUBLE_R("ADC Switch", DA9055_ADC_L_CTRL,
+ DA9055_ADC_R_CTRL, 6, 1, 0),
+ SOC_DOUBLE_R("Headphone Switch", DA9055_HP_L_CTRL,
+ DA9055_HP_R_CTRL, 6, 1, 0),
+ SOC_SINGLE("Lineout Switch", DA9055_LINE_CTRL, 6, 1, 0),
+ SOC_SINGLE("DAC Soft Mute Switch", DA9055_DAC_FILTERS5, 7, 1, 0),
+ SOC_ENUM("DAC Soft Mute Rate", da9055_dac_soft_mute_rate),
+
+ /* Zero Cross controls */
+ SOC_DOUBLE_R("Aux ZC Switch", DA9055_AUX_L_CTRL,
+ DA9055_AUX_R_CTRL, 4, 1, 0),
+ SOC_DOUBLE_R("Mixin PGA ZC Switch", DA9055_MIXIN_L_CTRL,
+ DA9055_MIXIN_R_CTRL, 4, 1, 0),
+ SOC_DOUBLE_R("Headphone ZC Switch", DA9055_HP_L_CTRL,
+ DA9055_HP_R_CTRL, 4, 1, 0),
+ SOC_SINGLE("Lineout ZC Switch", DA9055_LINE_CTRL, 4, 1, 0),
+
+ /* Gain Ramping controls */
+ SOC_DOUBLE_R("Aux Gain Ramping Switch", DA9055_AUX_L_CTRL,
+ DA9055_AUX_R_CTRL, 5, 1, 0),
+ SOC_DOUBLE_R("Mixin Gain Ramping Switch", DA9055_MIXIN_L_CTRL,
+ DA9055_MIXIN_R_CTRL, 5, 1, 0),
+ SOC_DOUBLE_R("ADC Gain Ramping Switch", DA9055_ADC_L_CTRL,
+ DA9055_ADC_R_CTRL, 5, 1, 0),
+ SOC_DOUBLE_R("DAC Gain Ramping Switch", DA9055_DAC_L_CTRL,
+ DA9055_DAC_R_CTRL, 5, 1, 0),
+ SOC_DOUBLE_R("Headphone Gain Ramping Switch", DA9055_HP_L_CTRL,
+ DA9055_HP_R_CTRL, 5, 1, 0),
+ SOC_SINGLE("Lineout Gain Ramping Switch", DA9055_LINE_CTRL, 5, 1, 0),
+ SOC_ENUM("Gain Ramping Rate", da9055_gain_ramping_rate),
+
+ /* DAC Noise Gate controls */
+ SOC_SINGLE("DAC NG Switch", DA9055_DAC_NG_CTRL, 7, 1, 0),
+ SOC_SINGLE("DAC NG ON Threshold", DA9055_DAC_NG_ON_THRESHOLD,
+ 0, 0x7, 0),
+ SOC_SINGLE("DAC NG OFF Threshold", DA9055_DAC_NG_OFF_THRESHOLD,
+ 0, 0x7, 0),
+ SOC_ENUM("DAC NG Setup Time", da9055_dac_ng_setup_time),
+ SOC_ENUM("DAC NG Rampup Rate", da9055_dac_ng_rampup_rate),
+ SOC_ENUM("DAC NG Rampdown Rate", da9055_dac_ng_rampdown_rate),
+
+ /* DAC Invertion control */
+ SOC_SINGLE("DAC Left Invert", DA9055_DIG_CTRL, 3, 1, 0),
+ SOC_SINGLE("DAC Right Invert", DA9055_DIG_CTRL, 7, 1, 0),
+
+ /* DMIC controls */
+ SOC_DOUBLE_R("DMIC Switch", DA9055_MIXIN_L_SELECT,
+ DA9055_MIXIN_R_SELECT, 7, 1, 0),
+
+ /* ALC Controls */
+ SOC_DOUBLE_EXT("ALC Switch", DA9055_ALC_CTRL1, 3, 7, 1, 0,
+ snd_soc_get_volsw, da9055_put_alc_sw),
+ SOC_SINGLE_EXT("ALC Sync Mode Switch", DA9055_ALC_CTRL1, 1, 1, 0,
+ snd_soc_get_volsw, da9055_put_alc_sw),
+ SOC_SINGLE("ALC Offset Switch", DA9055_ALC_CTRL1, 0, 1, 0),
+ SOC_SINGLE("ALC Anticlip Mode Switch", DA9055_ALC_ANTICLIP_CTRL,
+ 7, 1, 0),
+ SOC_SINGLE("ALC Anticlip Level", DA9055_ALC_ANTICLIP_LEVEL,
+ 0, 0x7f, 0),
+ SOC_SINGLE_TLV("ALC Min Threshold Volume", DA9055_ALC_TARGET_MIN,
+ 0, 0x3f, 1, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold Volume", DA9055_ALC_TARGET_MAX,
+ 0, 0x3f, 1, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Noise Threshold Volume", DA9055_ALC_NOISE,
+ 0, 0x3f, 1, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Gain Volume", DA9055_ALC_GAIN_LIMITS,
+ 4, 0xf, 0, alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Attenuation Volume", DA9055_ALC_GAIN_LIMITS,
+ 0, 0xf, 0, alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Min Analog Gain Volume",
+ DA9055_ALC_ANA_GAIN_LIMITS,
+ 0, 0x7, 0, alc_analog_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Analog Gain Volume",
+ DA9055_ALC_ANA_GAIN_LIMITS,
+ 4, 0x7, 0, alc_analog_gain_tlv),
+ SOC_ENUM("ALC Attack Rate", da9055_attack_rate),
+ SOC_ENUM("ALC Release Rate", da9055_release_rate),
+ SOC_ENUM("ALC Hold Time", da9055_hold_time),
+ /*
+ * Rate at which input signal envelope is tracked as the signal gets
+ * larger
+ */
+ SOC_ENUM("ALC Integ Attack Rate", da9055_integ_attack_rate),
+ /*
+ * Rate at which input signal envelope is tracked as the signal gets
+ * smaller
+ */
+ SOC_ENUM("ALC Integ Release Rate", da9055_integ_release_rate),
+};
+
+/* DAPM Controls */
+
+/* Mic PGA Left Source */
+static const struct snd_kcontrol_new da9055_mic_l_mux_controls =
+SOC_DAPM_ENUM("Route", da9055_mic_l_src);
+
+/* Mic PGA Right Source */
+static const struct snd_kcontrol_new da9055_mic_r_mux_controls =
+SOC_DAPM_ENUM("Route", da9055_mic_r_src);
+
+/* In Mixer Left */
+static const struct snd_kcontrol_new da9055_dapm_mixinl_controls[] = {
+ SOC_DAPM_SINGLE("Aux Left Switch", DA9055_MIXIN_L_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mic Left Switch", DA9055_MIXIN_L_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mic Right Switch", DA9055_MIXIN_L_SELECT, 2, 1, 0),
+};
+
+/* In Mixer Right */
+static const struct snd_kcontrol_new da9055_dapm_mixinr_controls[] = {
+ SOC_DAPM_SINGLE("Aux Right Switch", DA9055_MIXIN_R_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mic Right Switch", DA9055_MIXIN_R_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mic Left Switch", DA9055_MIXIN_R_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXIN_R_SELECT, 3, 1, 0),
+};
+
+/* DAC Left Source */
+static const struct snd_kcontrol_new da9055_dac_l_mux_controls =
+SOC_DAPM_ENUM("Route", da9055_dac_l_src);
+
+/* DAC Right Source */
+static const struct snd_kcontrol_new da9055_dac_r_mux_controls =
+SOC_DAPM_ENUM("Route", da9055_dac_r_src);
+
+/* Out Mixer Left */
+static const struct snd_kcontrol_new da9055_dapm_mixoutl_controls[] = {
+ SOC_DAPM_SINGLE("Aux Left Switch", DA9055_MIXOUT_L_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXOUT_L_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Right Switch", DA9055_MIXOUT_L_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("DAC Left Switch", DA9055_MIXOUT_L_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Aux Left Invert Switch", DA9055_MIXOUT_L_SELECT,
+ 4, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA9055_MIXOUT_L_SELECT,
+ 5, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA9055_MIXOUT_L_SELECT,
+ 6, 1, 0),
+};
+
+/* Out Mixer Right */
+static const struct snd_kcontrol_new da9055_dapm_mixoutr_controls[] = {
+ SOC_DAPM_SINGLE("Aux Right Switch", DA9055_MIXOUT_R_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Right Switch", DA9055_MIXOUT_R_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXOUT_R_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("DAC Right Switch", DA9055_MIXOUT_R_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Aux Right Invert Switch", DA9055_MIXOUT_R_SELECT,
+ 4, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA9055_MIXOUT_R_SELECT,
+ 5, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA9055_MIXOUT_R_SELECT,
+ 6, 1, 0),
+};
+
+/* Headphone Output Enable */
+static const struct snd_kcontrol_new da9055_dapm_hp_l_control =
+SOC_DAPM_SINGLE("Switch", DA9055_HP_L_CTRL, 3, 1, 0);
+
+static const struct snd_kcontrol_new da9055_dapm_hp_r_control =
+SOC_DAPM_SINGLE("Switch", DA9055_HP_R_CTRL, 3, 1, 0);
+
+/* Lineout Output Enable */
+static const struct snd_kcontrol_new da9055_dapm_lineout_control =
+SOC_DAPM_SINGLE("Switch", DA9055_LINE_CTRL, 3, 1, 0);
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget da9055_dapm_widgets[] = {
+ /* Input Side */
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+
+ /* MUXs for Mic PGA source selection */
+ SND_SOC_DAPM_MUX("Mic Left Source", SND_SOC_NOPM, 0, 0,
+ &da9055_mic_l_mux_controls),
+ SND_SOC_DAPM_MUX("Mic Right Source", SND_SOC_NOPM, 0, 0,
+ &da9055_mic_r_mux_controls),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_PGA("Mic Left", DA9055_MIC_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic Right", DA9055_MIC_R_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux Left", DA9055_AUX_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux Right", DA9055_AUX_R_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIXIN Left", DA9055_MIXIN_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIXIN Right", DA9055_MIXIN_R_CTRL, 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", DA9055_MIC_BIAS_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF", DA9055_AIF_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Charge Pump", DA9055_CP_CTRL, 7, 0, NULL, 0),
+
+ /* Input Mixers */
+ SND_SOC_DAPM_MIXER("In Mixer Left", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_mixinl_controls[0],
+ ARRAY_SIZE(da9055_dapm_mixinl_controls)),
+ SND_SOC_DAPM_MIXER("In Mixer Right", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_mixinr_controls[0],
+ ARRAY_SIZE(da9055_dapm_mixinr_controls)),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC Left", "Capture", DA9055_ADC_L_CTRL, 7, 0),
+ SND_SOC_DAPM_ADC("ADC Right", "Capture", DA9055_ADC_R_CTRL, 7, 0),
+
+ /* Output Side */
+
+ /* MUXs for DAC source selection */
+ SND_SOC_DAPM_MUX("DAC Left Source", SND_SOC_NOPM, 0, 0,
+ &da9055_dac_l_mux_controls),
+ SND_SOC_DAPM_MUX("DAC Right Source", SND_SOC_NOPM, 0, 0,
+ &da9055_dac_r_mux_controls),
+
+ /* AIF input */
+ SND_SOC_DAPM_AIF_IN("AIFIN Left", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFIN Right", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC Left", "Playback", DA9055_DAC_L_CTRL, 7, 0),
+ SND_SOC_DAPM_DAC("DAC Right", "Playback", DA9055_DAC_R_CTRL, 7, 0),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Out Mixer Left", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_mixoutl_controls[0],
+ ARRAY_SIZE(da9055_dapm_mixoutl_controls)),
+ SND_SOC_DAPM_MIXER("Out Mixer Right", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_mixoutr_controls[0],
+ ARRAY_SIZE(da9055_dapm_mixoutr_controls)),
+
+ /* Output Enable Switches */
+ SND_SOC_DAPM_SWITCH("Headphone Left Enable", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_hp_l_control),
+ SND_SOC_DAPM_SWITCH("Headphone Right Enable", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_hp_r_control),
+ SND_SOC_DAPM_SWITCH("Lineout Enable", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_lineout_control),
+
+ /* Output PGAs */
+ SND_SOC_DAPM_PGA("MIXOUT Left", DA9055_MIXOUT_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIXOUT Right", DA9055_MIXOUT_R_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Lineout", DA9055_LINE_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Left", DA9055_HP_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Right", DA9055_HP_R_CTRL, 7, 0, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("LINE"),
+};
+
+/* DAPM audio route definition */
+static const struct snd_soc_dapm_route da9055_audio_map[] = {
+ /* Dest Connecting Widget source */
+
+ /* Input path */
+ {"Mic Left Source", "MIC1_P_N", "MIC1"},
+ {"Mic Left Source", "MIC1_P", "MIC1"},
+ {"Mic Left Source", "MIC1_N", "MIC1"},
+ {"Mic Left Source", "MIC2_L", "MIC2"},
+
+ {"Mic Right Source", "MIC2_R_L", "MIC2"},
+ {"Mic Right Source", "MIC2_R", "MIC2"},
+ {"Mic Right Source", "MIC2_L", "MIC2"},
+
+ {"Mic Left", NULL, "Mic Left Source"},
+ {"Mic Right", NULL, "Mic Right Source"},
+
+ {"Aux Left", NULL, "AUXL"},
+ {"Aux Right", NULL, "AUXR"},
+
+ {"In Mixer Left", "Mic Left Switch", "Mic Left"},
+ {"In Mixer Left", "Mic Right Switch", "Mic Right"},
+ {"In Mixer Left", "Aux Left Switch", "Aux Left"},
+
+ {"In Mixer Right", "Mic Right Switch", "Mic Right"},
+ {"In Mixer Right", "Mic Left Switch", "Mic Left"},
+ {"In Mixer Right", "Aux Right Switch", "Aux Right"},
+ {"In Mixer Right", "Mixin Left Switch", "MIXIN Left"},
+
+ {"MIXIN Left", NULL, "In Mixer Left"},
+ {"ADC Left", NULL, "MIXIN Left"},
+
+ {"MIXIN Right", NULL, "In Mixer Right"},
+ {"ADC Right", NULL, "MIXIN Right"},
+
+ {"ADC Left", NULL, "AIF"},
+ {"ADC Right", NULL, "AIF"},
+
+ /* Output path */
+ {"AIFIN Left", NULL, "AIF"},
+ {"AIFIN Right", NULL, "AIF"},
+
+ {"DAC Left Source", "ADC output left", "ADC Left"},
+ {"DAC Left Source", "ADC output right", "ADC Right"},
+ {"DAC Left Source", "AIF input left", "AIFIN Left"},
+ {"DAC Left Source", "AIF input right", "AIFIN Right"},
+
+ {"DAC Right Source", "ADC output left", "ADC Left"},
+ {"DAC Right Source", "ADC output right", "ADC Right"},
+ {"DAC Right Source", "AIF input left", "AIFIN Left"},
+ {"DAC Right Source", "AIF input right", "AIFIN Right"},
+
+ {"DAC Left", NULL, "DAC Left Source"},
+ {"DAC Right", NULL, "DAC Right Source"},
+
+ {"Out Mixer Left", "Aux Left Switch", "Aux Left"},
+ {"Out Mixer Left", "Mixin Left Switch", "MIXIN Left"},
+ {"Out Mixer Left", "Mixin Right Switch", "MIXIN Right"},
+ {"Out Mixer Left", "Aux Left Invert Switch", "Aux Left"},
+ {"Out Mixer Left", "Mixin Left Invert Switch", "MIXIN Left"},
+ {"Out Mixer Left", "Mixin Right Invert Switch", "MIXIN Right"},
+ {"Out Mixer Left", "DAC Left Switch", "DAC Left"},
+
+ {"Out Mixer Right", "Aux Right Switch", "Aux Right"},
+ {"Out Mixer Right", "Mixin Right Switch", "MIXIN Right"},
+ {"Out Mixer Right", "Mixin Left Switch", "MIXIN Left"},
+ {"Out Mixer Right", "Aux Right Invert Switch", "Aux Right"},
+ {"Out Mixer Right", "Mixin Right Invert Switch", "MIXIN Right"},
+ {"Out Mixer Right", "Mixin Left Invert Switch", "MIXIN Left"},
+ {"Out Mixer Right", "DAC Right Switch", "DAC Right"},
+
+ {"MIXOUT Left", NULL, "Out Mixer Left"},
+ {"Headphone Left Enable", "Switch", "MIXOUT Left"},
+ {"Headphone Left", NULL, "Headphone Left Enable"},
+ {"Headphone Left", NULL, "Charge Pump"},
+ {"HPL", NULL, "Headphone Left"},
+
+ {"MIXOUT Right", NULL, "Out Mixer Right"},
+ {"Headphone Right Enable", "Switch", "MIXOUT Right"},
+ {"Headphone Right", NULL, "Headphone Right Enable"},
+ {"Headphone Right", NULL, "Charge Pump"},
+ {"HPR", NULL, "Headphone Right"},
+
+ {"MIXOUT Right", NULL, "Out Mixer Right"},
+ {"Lineout Enable", "Switch", "MIXOUT Right"},
+ {"Lineout", NULL, "Lineout Enable"},
+ {"LINE", NULL, "Lineout"},
+};
+
+/* Codec private data */
+struct da9055_priv {
+ struct regmap *regmap;
+ unsigned int mclk_rate;
+ int master;
+ struct da9055_platform_data *pdata;
+};
+
+static struct reg_default da9055_reg_defaults[] = {
+ { 0x21, 0x10 },
+ { 0x22, 0x0A },
+ { 0x23, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x27, 0x0C },
+ { 0x28, 0x01 },
+ { 0x29, 0x08 },
+ { 0x2A, 0x32 },
+ { 0x2B, 0x00 },
+ { 0x30, 0x35 },
+ { 0x31, 0x35 },
+ { 0x32, 0x00 },
+ { 0x33, 0x00 },
+ { 0x34, 0x03 },
+ { 0x35, 0x03 },
+ { 0x36, 0x6F },
+ { 0x37, 0x6F },
+ { 0x38, 0x80 },
+ { 0x39, 0x01 },
+ { 0x3A, 0x01 },
+ { 0x40, 0x00 },
+ { 0x41, 0x88 },
+ { 0x42, 0x88 },
+ { 0x43, 0x08 },
+ { 0x44, 0x80 },
+ { 0x45, 0x6F },
+ { 0x46, 0x6F },
+ { 0x47, 0x61 },
+ { 0x48, 0x35 },
+ { 0x49, 0x35 },
+ { 0x4A, 0x35 },
+ { 0x4B, 0x00 },
+ { 0x4C, 0x00 },
+ { 0x60, 0x44 },
+ { 0x61, 0x44 },
+ { 0x62, 0x00 },
+ { 0x63, 0x40 },
+ { 0x64, 0x40 },
+ { 0x65, 0x40 },
+ { 0x66, 0x40 },
+ { 0x67, 0x40 },
+ { 0x68, 0x40 },
+ { 0x69, 0x48 },
+ { 0x6A, 0x40 },
+ { 0x6B, 0x41 },
+ { 0x6C, 0x40 },
+ { 0x6D, 0x40 },
+ { 0x6E, 0x10 },
+ { 0x6F, 0x10 },
+ { 0x90, 0x80 },
+ { 0x92, 0x02 },
+ { 0x93, 0x00 },
+ { 0x99, 0x00 },
+ { 0x9A, 0x00 },
+ { 0x9B, 0x00 },
+ { 0x9C, 0x3F },
+ { 0x9D, 0x00 },
+ { 0x9E, 0x3F },
+ { 0x9F, 0xFF },
+ { 0xA0, 0x71 },
+ { 0xA1, 0x00 },
+ { 0xA2, 0x00 },
+ { 0xA6, 0x00 },
+ { 0xA7, 0x00 },
+ { 0xAB, 0x00 },
+ { 0xAC, 0x00 },
+ { 0xAD, 0x00 },
+ { 0xAF, 0x08 },
+ { 0xB0, 0x00 },
+ { 0xB1, 0x00 },
+ { 0xB2, 0x00 },
+};
+
+static bool da9055_volatile_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case DA9055_STATUS1:
+ case DA9055_PLL_STATUS:
+ case DA9055_AUX_L_GAIN_STATUS:
+ case DA9055_AUX_R_GAIN_STATUS:
+ case DA9055_MIC_L_GAIN_STATUS:
+ case DA9055_MIC_R_GAIN_STATUS:
+ case DA9055_MIXIN_L_GAIN_STATUS:
+ case DA9055_MIXIN_R_GAIN_STATUS:
+ case DA9055_ADC_L_GAIN_STATUS:
+ case DA9055_ADC_R_GAIN_STATUS:
+ case DA9055_DAC_L_GAIN_STATUS:
+ case DA9055_DAC_R_GAIN_STATUS:
+ case DA9055_HP_L_GAIN_STATUS:
+ case DA9055_HP_R_GAIN_STATUS:
+ case DA9055_LINE_GAIN_STATUS:
+ case DA9055_ALC_CIC_OP_LVL_DATA:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Set DAI word length */
+static int da9055_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ u8 aif_ctrl, fs;
+ u32 sysclk;
+
+ switch (params_width(params)) {
+ case 16:
+ aif_ctrl = DA9055_AIF_WORD_S16_LE;
+ break;
+ case 20:
+ aif_ctrl = DA9055_AIF_WORD_S20_3LE;
+ break;
+ case 24:
+ aif_ctrl = DA9055_AIF_WORD_S24_LE;
+ break;
+ case 32:
+ aif_ctrl = DA9055_AIF_WORD_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set AIF format */
+ snd_soc_update_bits(codec, DA9055_AIF_CTRL, DA9055_AIF_WORD_LENGTH_MASK,
+ aif_ctrl);
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA9055_SR_8000;
+ sysclk = 3072000;
+ break;
+ case 11025:
+ fs = DA9055_SR_11025;
+ sysclk = 2822400;
+ break;
+ case 12000:
+ fs = DA9055_SR_12000;
+ sysclk = 3072000;
+ break;
+ case 16000:
+ fs = DA9055_SR_16000;
+ sysclk = 3072000;
+ break;
+ case 22050:
+ fs = DA9055_SR_22050;
+ sysclk = 2822400;
+ break;
+ case 32000:
+ fs = DA9055_SR_32000;
+ sysclk = 3072000;
+ break;
+ case 44100:
+ fs = DA9055_SR_44100;
+ sysclk = 2822400;
+ break;
+ case 48000:
+ fs = DA9055_SR_48000;
+ sysclk = 3072000;
+ break;
+ case 88200:
+ fs = DA9055_SR_88200;
+ sysclk = 2822400;
+ break;
+ case 96000:
+ fs = DA9055_SR_96000;
+ sysclk = 3072000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (da9055->mclk_rate) {
+ /* PLL Mode, Write actual FS */
+ snd_soc_write(codec, DA9055_SR, fs);
+ } else {
+ /*
+ * Non-PLL Mode
+ * When PLL is bypassed, chip assumes constant MCLK of
+ * 12.288MHz and uses sample rate value to divide this MCLK
+ * to derive its sys clk. As sys clk has to be 256 * Fs, we
+ * need to write constant sample rate i.e. 48KHz.
+ */
+ snd_soc_write(codec, 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,
+ 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,
+ DA9055_PLL_EN, DA9055_PLL_EN);
+ }
+ } else {
+ /* Non PLL Mode, disable PLL */
+ snd_soc_update_bits(codec, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
+ }
+
+ return 0;
+}
+
+/* Set DAI mode and Format */
+static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ u8 aif_clk_mode, aif_ctrl, mode;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* DA9055 in I2S Master Mode */
+ mode = 1;
+ aif_clk_mode = DA9055_AIF_CLK_EN_MASTER_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* DA9055 in I2S Slave Mode */
+ mode = 0;
+ aif_clk_mode = DA9055_AIF_CLK_EN_SLAVE_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Don't allow change of mode if PLL is enabled */
+ if ((snd_soc_read(codec, DA9055_PLL_CTRL) & DA9055_PLL_EN) &&
+ (da9055->master != mode))
+ return -EINVAL;
+
+ da9055->master = mode;
+
+ /* Only I2S is supported */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ aif_ctrl = DA9055_AIF_FORMAT_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif_ctrl = DA9055_AIF_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ aif_ctrl = DA9055_AIF_FORMAT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif_ctrl = DA9055_AIF_FORMAT_DSP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* By default only 32 BCLK per WCLK is supported */
+ aif_clk_mode |= DA9055_AIF_BCLKS_PER_WCLK_32;
+
+ snd_soc_update_bits(codec, 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,
+ aif_ctrl);
+ return 0;
+}
+
+static int da9055_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute) {
+ snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ DA9055_DAC_L_MUTE_EN, DA9055_DAC_L_MUTE_EN);
+ snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ DA9055_DAC_R_MUTE_EN, DA9055_DAC_R_MUTE_EN);
+ } else {
+ snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ DA9055_DAC_L_MUTE_EN, 0);
+ snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ DA9055_DAC_R_MUTE_EN, 0);
+ }
+
+ return 0;
+}
+
+#define DA9055_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int da9055_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case DA9055_CLKSRC_MCLK:
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 13000000:
+ case 13500000:
+ case 14400000:
+ case 19200000:
+ case 19680000:
+ case 19800000:
+ da9055->mclk_rate = freq;
+ return 0;
+ default:
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+}
+
+/*
+ * da9055_set_dai_pll : Configure the codec PLL
+ * @param codec_dai : Pointer to codec DAI
+ * @param pll_id : da9055 has only one pll, so pll_id is always zero
+ * @param fref : Input MCLK frequency
+ * @param fout : FsDM value
+ * @return int : Zero for success, negative error code for error
+ *
+ * Note: Supported PLL input frequencies are 11.2896MHz, 12MHz, 12.288MHz,
+ * 13MHz, 13.5MHz, 14.4MHz, 19.2MHz, 19.6MHz and 19.8MHz
+ */
+static int da9055_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+
+ 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);
+
+ /* In slave mode, there is only one set of divisors */
+ if (!da9055->master && (fout != 2822400))
+ goto pll_err;
+
+ /* Search pll div array for correct divisors */
+ for (cnt = 0; cnt < ARRAY_SIZE(da9055_pll_div); cnt++) {
+ /* Check fref, mode and fout */
+ if ((fref == da9055_pll_div[cnt].fref) &&
+ (da9055->master == da9055_pll_div[cnt].mode) &&
+ (fout == da9055_pll_div[cnt].fout)) {
+ /* All match, pick up divisors */
+ pll_frac_top = da9055_pll_div[cnt].frac_top;
+ pll_frac_bot = da9055_pll_div[cnt].frac_bot;
+ pll_integer = da9055_pll_div[cnt].integer;
+ break;
+ }
+ }
+ if (cnt >= ARRAY_SIZE(da9055_pll_div))
+ goto pll_err;
+
+ /* Write PLL dividers */
+ snd_soc_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);
+
+ return 0;
+pll_err:
+ dev_err(codec_dai->dev, "Error in setting up PLL\n");
+ return -EINVAL;
+}
+
+/* DAI operations */
+static const struct snd_soc_dai_ops da9055_dai_ops = {
+ .hw_params = da9055_hw_params,
+ .set_fmt = da9055_set_dai_fmt,
+ .set_sysclk = da9055_set_dai_sysclk,
+ .set_pll = da9055_set_dai_pll,
+ .digital_mute = da9055_mute,
+};
+
+static struct snd_soc_dai_driver da9055_dai = {
+ .name = "da9055-hifi",
+ /* Playback Capabilities */
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA9055_FORMATS,
+ },
+ /* Capture Capabilities */
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA9055_FORMATS,
+ },
+ .ops = &da9055_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int da9055_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ /* Enable VMID reference & master bias */
+ snd_soc_update_bits(codec, 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,
+ DA9055_VMID_EN | DA9055_BIAS_EN, 0);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int da9055_probe(struct snd_soc_codec *codec)
+{
+ struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+
+ /* Enable all Gain Ramps */
+ snd_soc_update_bits(codec, DA9055_AUX_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_AUX_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_MIXIN_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_MIXIN_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_ADC_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_ADC_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_HP_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_HP_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_LINE_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+
+ /*
+ * There are two separate control bits for input and output mixers.
+ * One to enable corresponding amplifier and other to enable its
+ * output. As amplifier bits are related to power control, they are
+ * being managed by DAPM while other (non power related) bits are
+ * enabled here
+ */
+ snd_soc_update_bits(codec, DA9055_MIXIN_L_CTRL,
+ DA9055_MIXIN_L_MIX_EN, DA9055_MIXIN_L_MIX_EN);
+ snd_soc_update_bits(codec, DA9055_MIXIN_R_CTRL,
+ DA9055_MIXIN_R_MIX_EN, DA9055_MIXIN_R_MIX_EN);
+
+ snd_soc_update_bits(codec, DA9055_MIXOUT_L_CTRL,
+ DA9055_MIXOUT_L_MIX_EN, DA9055_MIXOUT_L_MIX_EN);
+ snd_soc_update_bits(codec, 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);
+
+ /* 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,
+ DA9055_MICBIAS2_EN,
+ DA9055_MICBIAS2_EN);
+ } else {
+ snd_soc_update_bits(codec, DA9055_MIXIN_R_SELECT,
+ DA9055_MICBIAS2_EN, 0);
+ }
+ /* set mic bias voltage */
+ switch (da9055->pdata->micbias) {
+ case DA9055_MICBIAS_2_2V:
+ case DA9055_MICBIAS_2_1V:
+ case DA9055_MICBIAS_1_8V:
+ case DA9055_MICBIAS_1_6V:
+ snd_soc_update_bits(codec, DA9055_MIC_CONFIG,
+ DA9055_MICBIAS_LEVEL_MASK,
+ (da9055->pdata->micbias) << 4);
+ break;
+ }
+ }
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_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),
+};
+
+static const struct regmap_config da9055_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = da9055_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da9055_reg_defaults),
+ .volatile_reg = da9055_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int da9055_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da9055_priv *da9055;
+ struct da9055_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ int ret;
+
+ da9055 = devm_kzalloc(&i2c->dev, sizeof(struct da9055_priv),
+ GFP_KERNEL);
+ if (!da9055)
+ return -ENOMEM;
+
+ if (pdata)
+ da9055->pdata = pdata;
+
+ i2c_set_clientdata(i2c, da9055);
+
+ da9055->regmap = devm_regmap_init_i2c(i2c, &da9055_regmap_config);
+ if (IS_ERR(da9055->regmap)) {
+ ret = PTR_ERR(da9055->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_da9055, &da9055_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register da9055 codec: %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
+ * devices (both have configurable I2C addresses, and are to all intents and
+ * purposes separate). As a result there are specific DA9055 Ids for CODEC
+ * and PMIC, which must be different to operate together.
+ */
+static const struct i2c_device_id da9055_i2c_id[] = {
+ { "da9055-codec", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da9055_i2c_id);
+
+static const struct of_device_id da9055_of_match[] = {
+ { .compatible = "dlg,da9055-codec", },
+ { }
+};
+
+/* 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,
+};
+
+module_i2c_driver(da9055_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC DA9055 Codec driver");
+MODULE_AUTHOR("David Chen, Ashish Chavan");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/dmic.c b/kernel/sound/soc/codecs/dmic.c
new file mode 100644
index 000000000..fde53251c
--- /dev/null
+++ b/kernel/sound/soc/codecs/dmic.c
@@ -0,0 +1,86 @@
+/*
+ * 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/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+static struct snd_soc_dai_driver dmic_dai = {
+ .name = "dmic-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+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_INPUT("DMic"),
+};
+
+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 int dmic_dev_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_dmic, &dmic_dai, 1);
+}
+
+static int dmic_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+MODULE_ALIAS("platform:dmic-codec");
+
+static struct platform_driver dmic_driver = {
+ .driver = {
+ .name = "dmic-codec",
+ },
+ .probe = dmic_dev_probe,
+ .remove = dmic_dev_remove,
+};
+
+module_platform_driver(dmic_driver);
+
+MODULE_DESCRIPTION("Generic DMIC driver");
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/es8328-i2c.c b/kernel/sound/soc/codecs/es8328-i2c.c
new file mode 100644
index 000000000..2d05b5d3a
--- /dev/null
+++ b/kernel/sound/soc/codecs/es8328-i2c.c
@@ -0,0 +1,60 @@
+/*
+ * 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>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "es8328.h"
+
+static const struct i2c_device_id es8328_id[] = {
+ { "es8328", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8328_id);
+
+static const struct of_device_id es8328_of_match[] = {
+ { .compatible = "everest,es8328", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es8328_of_match);
+
+static int es8328_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ 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,
+ .id_table = es8328_id,
+};
+
+module_i2c_driver(es8328_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ES8328 audio CODEC I2C driver");
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/es8328-spi.c b/kernel/sound/soc/codecs/es8328-spi.c
new file mode 100644
index 000000000..8fbd935e1
--- /dev/null
+++ b/kernel/sound/soc/codecs/es8328-spi.c
@@ -0,0 +1,49 @@
+/*
+ * 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>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+#include "es8328.h"
+
+static const struct of_device_id es8328_of_match[] = {
+ { .compatible = "everest,es8328", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es8328_of_match);
+
+static int es8328_spi_probe(struct spi_device *spi)
+{
+ return es8328_probe(&spi->dev,
+ devm_regmap_init_spi(spi, &es8328_regmap_config));
+}
+
+static 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);
+MODULE_DESCRIPTION("ASoC ES8328 audio CODEC SPI driver");
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/es8328.c b/kernel/sound/soc/codecs/es8328.c
new file mode 100644
index 000000000..c5f35a07e
--- /dev/null
+++ b/kernel/sound/soc/codecs/es8328.c
@@ -0,0 +1,756 @@
+/*
+ * 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>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.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 "es8328.h"
+
+#define ES8328_SYSCLK_RATE_1X 11289600
+#define ES8328_SYSCLK_RATE_2X 22579200
+
+/* 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 },
+};
+
+/* regulator supplies for sgtl5000, VDDD is an optional external supply */
+enum sgtl5000_regulator_supplies {
+ DVDD,
+ AVDD,
+ PVDD,
+ HPVDD,
+ ES8328_SUPPLY_NUM
+};
+
+/* vddd is optional supply */
+static const char * const supply_names[ES8328_SUPPLY_NUM] = {
+ "DVDD",
+ "AVDD",
+ "PVDD",
+ "HPVDD",
+};
+
+#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_11025)
+#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+struct es8328_priv {
+ struct regmap *regmap;
+ struct clk *clk;
+ int playback_fs;
+ bool deemph;
+ struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
+};
+
+/*
+ * ES8328 Controls
+ */
+
+static const char * const adcpol_txt[] = {"Normal", "L Invert", "R Invert",
+ "L + R Invert"};
+static SOC_ENUM_SINGLE_DECL(adcpol,
+ ES8328_ADCCONTROL6, 6, adcpol_txt);
+
+static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0);
+static const DECLARE_TLV_DB_SCALE(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 int es8328_set_deemph(struct snd_soc_codec *codec)
+{
+ struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+ int val, i, best;
+
+ /*
+ * If we're using deemphasis select the nearest available sample
+ * 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 = i;
+ }
+
+ val = best << 1;
+ } else {
+ val = 0;
+ }
+
+ dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+
+ return snd_soc_update_bits(codec, ES8328_DACCONTROL6, 0x6, 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);
+
+ 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.integer.value[0];
+ int ret;
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ ret = es8328_set_deemph(codec);
+ if (ret < 0)
+ return ret;
+
+ es8328->deemph = deemph;
+
+ return 0;
+}
+
+
+
+static const struct snd_kcontrol_new es8328_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Capture Digital Volume",
+ ES8328_ADCCONTROL8, ES8328_ADCCONTROL9,
+ 0, 0xc0, 1, dac_adc_tlv),
+ SOC_SINGLE("Capture ZC Switch", ES8328_ADCCONTROL7, 6, 1, 0),
+
+ SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
+ es8328_get_deemph, es8328_put_deemph),
+
+ SOC_ENUM("Capture Polarity", adcpol),
+
+ SOC_SINGLE_TLV("Left Mixer Left Bypass Volume",
+ ES8328_DACCONTROL17, 3, 7, 1, bypass_tlv),
+ SOC_SINGLE_TLV("Left Mixer Right Bypass Volume",
+ ES8328_DACCONTROL19, 3, 7, 1, bypass_tlv),
+ SOC_SINGLE_TLV("Right Mixer Left Bypass Volume",
+ ES8328_DACCONTROL18, 3, 7, 1, bypass_tlv),
+ SOC_SINGLE_TLV("Right Mixer Right Bypass Volume",
+ ES8328_DACCONTROL20, 3, 7, 1, bypass_tlv),
+
+ SOC_DOUBLE_R_TLV("PCM Volume",
+ ES8328_LDACVOL, ES8328_RDACVOL,
+ 0, ES8328_DACVOL_MAX, 1, dac_adc_tlv),
+
+ SOC_DOUBLE_R_TLV("Output 1 Playback Volume",
+ ES8328_LOUT1VOL, ES8328_ROUT1VOL,
+ 0, ES8328_OUT1VOL_MAX, 0, play_tlv),
+
+ SOC_DOUBLE_R_TLV("Output 2 Playback Volume",
+ ES8328_LOUT2VOL, ES8328_ROUT2VOL,
+ 0, ES8328_OUT2VOL_MAX, 0, play_tlv),
+
+ SOC_DOUBLE_TLV("Mic PGA Volume", ES8328_ADCCONTROL1,
+ 4, 0, 8, 0, mic_tlv),
+};
+
+/*
+ * DAPM Controls
+ */
+
+static const char * const es8328_line_texts[] = {
+ "Line 1", "Line 2", "PGA", "Differential"};
+
+static const struct soc_enum es8328_lline_enum =
+ SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 3,
+ ARRAY_SIZE(es8328_line_texts),
+ es8328_line_texts);
+static const struct snd_kcontrol_new es8328_left_line_controls =
+ SOC_DAPM_ENUM("Route", es8328_lline_enum);
+
+static const struct soc_enum es8328_rline_enum =
+ SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 0,
+ ARRAY_SIZE(es8328_line_texts),
+ es8328_line_texts);
+static const struct snd_kcontrol_new es8328_right_line_controls =
+ SOC_DAPM_ENUM("Route", es8328_lline_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),
+};
+
+/* 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),
+};
+
+static const char * const es8328_pga_sel[] = {
+ "Line 1", "Line 2", "Line 3", "Differential"};
+
+/* Left PGA Mux */
+static const struct soc_enum es8328_lpga_enum =
+ SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 6,
+ ARRAY_SIZE(es8328_pga_sel),
+ es8328_pga_sel);
+static const struct snd_kcontrol_new es8328_left_pga_controls =
+ SOC_DAPM_ENUM("Route", es8328_lpga_enum);
+
+/* Right PGA Mux */
+static const struct soc_enum es8328_rpga_enum =
+ SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 4,
+ ARRAY_SIZE(es8328_pga_sel),
+ es8328_pga_sel);
+static const struct snd_kcontrol_new es8328_right_pga_controls =
+ SOC_DAPM_ENUM("Route", es8328_rpga_enum);
+
+/* Differential Mux */
+static const char * const es8328_diff_sel[] = {"Line 1", "Line 2"};
+static SOC_ENUM_SINGLE_DECL(diffmux,
+ ES8328_ADCCONTROL3, 7, es8328_diff_sel);
+static const struct snd_kcontrol_new es8328_diffmux_controls =
+ SOC_DAPM_ENUM("Route", diffmux);
+
+/* Mono ADC Mux */
+static const char * const es8328_mono_mux[] = {"Stereo", "Mono (Left)",
+ "Mono (Right)", "Digital Mono"};
+static SOC_ENUM_SINGLE_DECL(monomux,
+ ES8328_ADCCONTROL3, 3, es8328_mono_mux);
+static const struct snd_kcontrol_new es8328_monomux_controls =
+ SOC_DAPM_ENUM("Route", monomux);
+
+static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_diffmux_controls),
+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_monomux_controls),
+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_monomux_controls),
+
+ SND_SOC_DAPM_MUX("Left PGA Mux", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_AINL_OFF, 1,
+ &es8328_left_pga_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_AINR_OFF, 1,
+ &es8328_right_pga_controls),
+
+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_left_line_controls),
+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
+ &es8328_right_line_controls),
+
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_ADCR_OFF, 1),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_ADCL_OFF, 1),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_MIC_BIAS_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8328_ADCPOWER,
+ ES8328_ADCPOWER_ADC_BIAS_GEN_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC STM", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_DACSTM_RESET, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC STM", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC DIG", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_DACDIG_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC DIG", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC DLL", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_DACDLL_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC DLL", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC Vref", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Vref", ES8328_CHIPPOWER,
+ ES8328_CHIPPOWER_DACVREF_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8328_DACPOWER,
+ ES8328_DACPOWER_RDAC_OFF, 1),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8328_DACPOWER,
+ ES8328_DACPOWER_LDAC_OFF, 1),
+
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ &es8328_left_mixer_controls[0],
+ ARRAY_SIZE(es8328_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ &es8328_right_mixer_controls[0],
+ ARRAY_SIZE(es8328_right_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("Right Out 2", ES8328_DACPOWER,
+ ES8328_DACPOWER_ROUT2_ON, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", ES8328_DACPOWER,
+ ES8328_DACPOWER_LOUT2_ON, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", ES8328_DACPOWER,
+ ES8328_DACPOWER_ROUT1_ON, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", ES8328_DACPOWER,
+ ES8328_DACPOWER_LOUT1_ON, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+
+ SND_SOC_DAPM_INPUT("LINPUT1"),
+ SND_SOC_DAPM_INPUT("LINPUT2"),
+ SND_SOC_DAPM_INPUT("RINPUT1"),
+ SND_SOC_DAPM_INPUT("RINPUT2"),
+};
+
+static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
+
+ { "Left Line Mux", "Line 1", "LINPUT1" },
+ { "Left Line Mux", "Line 2", "LINPUT2" },
+ { "Left Line Mux", "PGA", "Left PGA Mux" },
+ { "Left Line Mux", "Differential", "Differential Mux" },
+
+ { "Right Line Mux", "Line 1", "RINPUT1" },
+ { "Right Line Mux", "Line 2", "RINPUT2" },
+ { "Right Line Mux", "PGA", "Right PGA Mux" },
+ { "Right Line Mux", "Differential", "Differential Mux" },
+
+ { "Left PGA Mux", "Line 1", "LINPUT1" },
+ { "Left PGA Mux", "Line 2", "LINPUT2" },
+ { "Left PGA Mux", "Differential", "Differential Mux" },
+
+ { "Right PGA Mux", "Line 1", "RINPUT1" },
+ { "Right PGA Mux", "Line 2", "RINPUT2" },
+ { "Right PGA Mux", "Differential", "Differential Mux" },
+
+ { "Differential Mux", "Line 1", "LINPUT1" },
+ { "Differential Mux", "Line 1", "RINPUT1" },
+ { "Differential Mux", "Line 2", "LINPUT2" },
+ { "Differential Mux", "Line 2", "RINPUT2" },
+
+ { "Left ADC Mux", "Stereo", "Left PGA Mux" },
+ { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" },
+ { "Left ADC Mux", "Digital Mono", "Left PGA Mux" },
+
+ { "Right ADC Mux", "Stereo", "Right PGA Mux" },
+ { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" },
+ { "Right ADC Mux", "Digital Mono", "Right PGA Mux" },
+
+ { "Left ADC", NULL, "Left ADC Mux" },
+ { "Right ADC", NULL, "Right ADC Mux" },
+
+ { "ADC DIG", NULL, "ADC STM" },
+ { "ADC DIG", NULL, "ADC Vref" },
+ { "ADC DIG", NULL, "ADC DLL" },
+
+ { "Left ADC", NULL, "ADC DIG" },
+ { "Right ADC", NULL, "ADC DIG" },
+
+ { "Mic Bias", NULL, "Mic Bias Gen" },
+
+ { "Left Line Mux", "Line 1", "LINPUT1" },
+ { "Left Line Mux", "Line 2", "LINPUT2" },
+ { "Left Line Mux", "PGA", "Left PGA Mux" },
+ { "Left Line Mux", "Differential", "Differential Mux" },
+
+ { "Right Line Mux", "Line 1", "RINPUT1" },
+ { "Right Line Mux", "Line 2", "RINPUT2" },
+ { "Right Line Mux", "PGA", "Right PGA Mux" },
+ { "Right Line Mux", "Differential", "Differential Mux" },
+
+ { "Left Out 1", NULL, "Left DAC" },
+ { "Right Out 1", NULL, "Right DAC" },
+ { "Left Out 2", NULL, "Left DAC" },
+ { "Right Out 2", NULL, "Right DAC" },
+
+ { "Left Mixer", "Playback Switch", "Left DAC" },
+ { "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
+ { "Left Mixer", "Right Playback Switch", "Right DAC" },
+ { "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
+
+ { "Right Mixer", "Left Playback Switch", "Left DAC" },
+ { "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
+ { "Right Mixer", "Playback Switch", "Right DAC" },
+ { "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
+
+ { "DAC DIG", NULL, "DAC STM" },
+ { "DAC DIG", NULL, "DAC Vref" },
+ { "DAC DIG", NULL, "DAC DLL" },
+
+ { "Left DAC", NULL, "DAC DIG" },
+ { "Right DAC", NULL, "DAC DIG" },
+
+ { "Left Out 1", NULL, "Left Mixer" },
+ { "LOUT1", NULL, "Left Out 1" },
+ { "Right Out 1", NULL, "Right Mixer" },
+ { "ROUT1", NULL, "Right Out 1" },
+
+ { "Left Out 2", NULL, "Left Mixer" },
+ { "LOUT2", NULL, "Left Out 2" },
+ { "Right Out 2", NULL, "Right Mixer" },
+ { "ROUT2", NULL, "Right Out 2" },
+};
+
+static int es8328_mute(struct snd_soc_dai *dai, int mute)
+{
+ return snd_soc_update_bits(dai->codec, ES8328_DACCONTROL3,
+ ES8328_DACCONTROL3_DACMUTE,
+ mute ? ES8328_DACCONTROL3_DACMUTE : 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;
+ int i;
+ int reg;
+ u8 ratio;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = ES8328_DACCONTROL2;
+ else
+ reg = ES8328_ADCCONTROL5;
+
+ clk_rate = clk_get_rate(es8328->clk);
+
+ 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;
+ }
+
+ /* 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;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ es8328->playback_fs = params_rate(params);
+ es8328_set_deemph(codec);
+ }
+
+ return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio);
+}
+
+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)
+ return -EINVAL;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ 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);
+
+ return 0;
+}
+
+static int es8328_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VREF, VMID=2x50k, digital enabled */
+ snd_soc_write(codec, ES8328_CHIPPOWER, 0);
+ snd_soc_update_bits(codec, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_50k |
+ ES8328_CONTROL1_ENREF);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ snd_soc_update_bits(codec, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_5k |
+ ES8328_CONTROL1_ENREF);
+
+ /* Charge caps */
+ msleep(100);
+ }
+
+ snd_soc_write(codec, ES8328_CONTROL2,
+ ES8328_CONTROL2_OVERCURRENT_ON |
+ ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
+
+ /* VREF, VMID=2*500k, digital stopped */
+ snd_soc_update_bits(codec, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_500k |
+ ES8328_CONTROL1_ENREF);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, 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 = {
+ .hw_params = es8328_hw_params,
+ .digital_mute = es8328_mute,
+ .set_fmt = es8328_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver es8328_dai = {
+ .name = "es8328-hifi-analog",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ES8328_RATES,
+ .formats = ES8328_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ES8328_RATES,
+ .formats = ES8328_FORMATS,
+ },
+ .ops = &es8328_dai_ops,
+};
+
+static int es8328_suspend(struct snd_soc_codec *codec)
+{
+ struct es8328_priv *es8328;
+ int ret;
+
+ es8328 = snd_soc_codec_get_drvdata(codec);
+
+ 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");
+ return ret;
+ }
+ return 0;
+}
+
+static int es8328_resume(struct snd_soc_codec *codec)
+{
+ struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ struct es8328_priv *es8328;
+ int ret;
+
+ es8328 = snd_soc_codec_get_drvdata(codec);
+
+ ret = clk_prepare_enable(es8328->clk);
+ if (ret) {
+ dev_err(codec->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");
+ return ret;
+ }
+
+ regcache_mark_dirty(regmap);
+ ret = regcache_sync(regmap);
+ if (ret) {
+ dev_err(codec->dev, "unable to sync regcache\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int es8328_codec_probe(struct snd_soc_codec *codec)
+{
+ struct es8328_priv *es8328;
+ int ret;
+
+ es8328 = snd_soc_codec_get_drvdata(codec);
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
+ es8328->supplies);
+ if (ret) {
+ dev_err(codec->dev, "unable to enable regulators\n");
+ return ret;
+ }
+
+ /* Setup clocks */
+ es8328->clk = devm_clk_get(codec->dev, NULL);
+ if (IS_ERR(es8328->clk)) {
+ dev_err(codec->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");
+ goto clk_fail;
+ }
+
+ return 0;
+
+clk_fail:
+ regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
+ es8328->supplies);
+ return ret;
+}
+
+static int es8328_remove(struct snd_soc_codec *codec)
+{
+ struct es8328_priv *es8328;
+
+ es8328 = snd_soc_codec_get_drvdata(codec);
+
+ if (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,
+};
+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),
+};
+
+int es8328_probe(struct device *dev, struct regmap *regmap)
+{
+ struct es8328_priv *es8328;
+ int ret;
+ int i;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ es8328 = devm_kzalloc(dev, sizeof(*es8328), GFP_KERNEL);
+ if (es8328 == NULL)
+ return -ENOMEM;
+
+ es8328->regmap = regmap;
+
+ for (i = 0; i < ARRAY_SIZE(es8328->supplies); i++)
+ es8328->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8328->supplies),
+ es8328->supplies);
+ if (ret) {
+ dev_err(dev, "unable to get regulators\n");
+ return ret;
+ }
+
+ dev_set_drvdata(dev, es8328);
+
+ return snd_soc_register_codec(dev,
+ &es8328_codec_driver, &es8328_dai, 1);
+}
+EXPORT_SYMBOL_GPL(es8328_probe);
+
+MODULE_DESCRIPTION("ASoC ES8328 driver");
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/es8328.h b/kernel/sound/soc/codecs/es8328.h
new file mode 100644
index 000000000..cb36afe10
--- /dev/null
+++ b/kernel/sound/soc/codecs/es8328.h
@@ -0,0 +1,314 @@
+/*
+ * es8328.h -- ES8328 ALSA SoC Audio driver
+ */
+
+#ifndef _ES8328_H
+#define _ES8328_H
+
+#include <linux/regmap.h>
+
+struct device;
+
+extern const struct regmap_config es8328_regmap_config;
+int es8328_probe(struct device *dev, struct regmap *regmap);
+
+#define ES8328_DACLVOL 46
+#define ES8328_DACRVOL 47
+#define ES8328_DACCTL 28
+#define ES8328_RATEMASK (0x1f << 0)
+
+#define ES8328_CONTROL1 0x00
+#define ES8328_CONTROL1_VMIDSEL_OFF (0 << 0)
+#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
+#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
+#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
+#define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0)
+#define ES8328_CONTROL1_ENREF (1 << 2)
+#define ES8328_CONTROL1_SEQEN (1 << 3)
+#define ES8328_CONTROL1_SAMEFS (1 << 4)
+#define ES8328_CONTROL1_DACMCLK_ADC (0 << 5)
+#define ES8328_CONTROL1_DACMCLK_DAC (1 << 5)
+#define ES8328_CONTROL1_LRCM (1 << 6)
+#define ES8328_CONTROL1_SCP_RESET (1 << 7)
+
+#define ES8328_CONTROL2 0x01
+#define ES8328_CONTROL2_VREF_BUF_OFF (1 << 0)
+#define ES8328_CONTROL2_VREF_LOWPOWER (1 << 1)
+#define ES8328_CONTROL2_IBIASGEN_OFF (1 << 2)
+#define ES8328_CONTROL2_ANALOG_OFF (1 << 3)
+#define ES8328_CONTROL2_VREF_BUF_LOWPOWER (1 << 4)
+#define ES8328_CONTROL2_VCM_MOD_LOWPOWER (1 << 5)
+#define ES8328_CONTROL2_OVERCURRENT_ON (1 << 6)
+#define ES8328_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7)
+
+#define ES8328_CHIPPOWER 0x02
+#define ES8328_CHIPPOWER_DACVREF_OFF 0
+#define ES8328_CHIPPOWER_ADCVREF_OFF 1
+#define ES8328_CHIPPOWER_DACDLL_OFF 2
+#define ES8328_CHIPPOWER_ADCDLL_OFF 3
+#define ES8328_CHIPPOWER_DACSTM_RESET 4
+#define ES8328_CHIPPOWER_ADCSTM_RESET 5
+#define ES8328_CHIPPOWER_DACDIG_OFF 6
+#define ES8328_CHIPPOWER_ADCDIG_OFF 7
+
+#define ES8328_ADCPOWER 0x03
+#define ES8328_ADCPOWER_INT1_LOWPOWER 0
+#define ES8328_ADCPOWER_FLASH_ADC_LOWPOWER 1
+#define ES8328_ADCPOWER_ADC_BIAS_GEN_OFF 2
+#define ES8328_ADCPOWER_MIC_BIAS_OFF 3
+#define ES8328_ADCPOWER_ADCR_OFF 4
+#define ES8328_ADCPOWER_ADCL_OFF 5
+#define ES8328_ADCPOWER_AINR_OFF 6
+#define ES8328_ADCPOWER_AINL_OFF 7
+
+#define ES8328_DACPOWER 0x04
+#define ES8328_DACPOWER_OUT3_ON 0
+#define ES8328_DACPOWER_MONO_ON 1
+#define ES8328_DACPOWER_ROUT2_ON 2
+#define ES8328_DACPOWER_LOUT2_ON 3
+#define ES8328_DACPOWER_ROUT1_ON 4
+#define ES8328_DACPOWER_LOUT1_ON 5
+#define ES8328_DACPOWER_RDAC_OFF 6
+#define ES8328_DACPOWER_LDAC_OFF 7
+
+#define ES8328_CHIPLOPOW1 0x05
+#define ES8328_CHIPLOPOW2 0x06
+#define ES8328_ANAVOLMANAG 0x07
+
+#define ES8328_MASTERMODE 0x08
+#define ES8328_MASTERMODE_BCLKDIV (0 << 0)
+#define ES8328_MASTERMODE_BCLK_INV (1 << 5)
+#define ES8328_MASTERMODE_MCLKDIV2 (1 << 6)
+#define ES8328_MASTERMODE_MSC (1 << 7)
+
+#define ES8328_ADCCONTROL1 0x09
+#define ES8328_ADCCONTROL2 0x0a
+#define ES8328_ADCCONTROL3 0x0b
+#define ES8328_ADCCONTROL4 0x0c
+#define ES8328_ADCCONTROL5 0x0d
+#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
+
+#define ES8328_ADCCONTROL6 0x0e
+
+#define ES8328_ADCCONTROL7 0x0f
+#define ES8328_ADCCONTROL7_ADC_MUTE (1 << 2)
+#define ES8328_ADCCONTROL7_ADC_LER (1 << 3)
+#define ES8328_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4)
+#define ES8328_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6)
+
+#define ES8328_ADCCONTROL8 0x10
+#define ES8328_ADCCONTROL9 0x11
+#define ES8328_ADCCONTROL10 0x12
+#define ES8328_ADCCONTROL11 0x13
+#define ES8328_ADCCONTROL12 0x14
+#define ES8328_ADCCONTROL13 0x15
+#define ES8328_ADCCONTROL14 0x16
+
+#define ES8328_DACCONTROL1 0x17
+#define ES8328_DACCONTROL1_DACFORMAT_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_DACLRP_I2S_POL_NORMAL (0 << 6)
+#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
+#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
+#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (1 << 6)
+#define ES8328_DACCONTROL1_LRSWAP (1 << 7)
+
+#define ES8328_DACCONTROL2 0x18
+#define ES8328_DACCONTROL2_RATEMASK (0x1f << 0)
+#define ES8328_DACCONTROL2_DOUBLESPEED (1 << 5)
+
+#define ES8328_DACCONTROL3 0x19
+#define ES8328_DACCONTROL3_AUTOMUTE (1 << 2)
+#define ES8328_DACCONTROL3_DACMUTE (1 << 2)
+#define ES8328_DACCONTROL3_LEFTGAINVOL (1 << 3)
+#define ES8328_DACCONTROL3_DACZEROCROSS (1 << 4)
+#define ES8328_DACCONTROL3_DACSOFTRAMP (1 << 5)
+#define ES8328_DACCONTROL3_DACRAMPRATE (3 << 6)
+
+#define ES8328_LDACVOL 0x1a
+#define ES8328_LDACVOL_MASK (0 << 0)
+#define ES8328_LDACVOL_MAX (0xc0)
+
+#define ES8328_RDACVOL 0x1b
+#define ES8328_RDACVOL_MASK (0 << 0)
+#define ES8328_RDACVOL_MAX (0xc0)
+
+#define ES8328_DACVOL_MAX (0xc0)
+
+#define ES8328_DACCONTROL4 0x1a
+#define ES8328_DACCONTROL5 0x1b
+
+#define ES8328_DACCONTROL6 0x1c
+#define ES8328_DACCONTROL6_CLICKFREE (1 << 3)
+#define ES8328_DACCONTROL6_DAC_INVR (1 << 4)
+#define ES8328_DACCONTROL6_DAC_INVL (1 << 5)
+#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_48k (3 << 6)
+
+#define ES8328_DACCONTROL7 0x1d
+#define ES8328_DACCONTROL7_VPP_SCALE_3p5 (0 << 0)
+#define ES8328_DACCONTROL7_VPP_SCALE_4p0 (1 << 0)
+#define ES8328_DACCONTROL7_VPP_SCALE_3p0 (2 << 0)
+#define ES8328_DACCONTROL7_VPP_SCALE_2p5 (3 << 0)
+#define ES8328_DACCONTROL7_SHELVING_STRENGTH (1 << 2) /* In eights */
+#define ES8328_DACCONTROL7_MONO (1 << 5)
+#define ES8328_DACCONTROL7_ZEROR (1 << 6)
+#define ES8328_DACCONTROL7_ZEROL (1 << 7)
+
+/* Shelving filter */
+#define ES8328_DACCONTROL8 0x1e
+#define ES8328_DACCONTROL9 0x1f
+#define ES8328_DACCONTROL10 0x20
+#define ES8328_DACCONTROL11 0x21
+#define ES8328_DACCONTROL12 0x22
+#define ES8328_DACCONTROL13 0x23
+#define ES8328_DACCONTROL14 0x24
+#define ES8328_DACCONTROL15 0x25
+
+#define ES8328_DACCONTROL16 0x26
+#define ES8328_DACCONTROL16_RMIXSEL_RIN1 (0 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RIN2 (1 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RIN3 (2 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RADC (3 << 0)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN1 (0 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN2 (1 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN3 (2 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LADC (3 << 3)
+
+#define ES8328_DACCONTROL17 0x27
+#define ES8328_DACCONTROL17_LI2LOVOL (7 << 3)
+#define ES8328_DACCONTROL17_LI2LO (1 << 6)
+#define ES8328_DACCONTROL17_LD2LO (1 << 7)
+
+#define ES8328_DACCONTROL18 0x28
+#define ES8328_DACCONTROL18_RI2LOVOL (7 << 3)
+#define ES8328_DACCONTROL18_RI2LO (1 << 6)
+#define ES8328_DACCONTROL18_RD2LO (1 << 7)
+
+#define ES8328_DACCONTROL19 0x29
+#define ES8328_DACCONTROL19_LI2ROVOL (7 << 3)
+#define ES8328_DACCONTROL19_LI2RO (1 << 6)
+#define ES8328_DACCONTROL19_LD2RO (1 << 7)
+
+#define ES8328_DACCONTROL20 0x2a
+#define ES8328_DACCONTROL20_RI2ROVOL (7 << 3)
+#define ES8328_DACCONTROL20_RI2RO (1 << 6)
+#define ES8328_DACCONTROL20_RD2RO (1 << 7)
+
+#define ES8328_DACCONTROL21 0x2b
+#define ES8328_DACCONTROL21_LI2MOVOL (7 << 3)
+#define ES8328_DACCONTROL21_LI2MO (1 << 6)
+#define ES8328_DACCONTROL21_LD2MO (1 << 7)
+
+#define ES8328_DACCONTROL22 0x2c
+#define ES8328_DACCONTROL22_RI2MOVOL (7 << 3)
+#define ES8328_DACCONTROL22_RI2MO (1 << 6)
+#define ES8328_DACCONTROL22_RD2MO (1 << 7)
+
+#define ES8328_DACCONTROL23 0x2d
+#define ES8328_DACCONTROL23_MOUTINV (1 << 1)
+#define ES8328_DACCONTROL23_HPSWPOL (1 << 2)
+#define ES8328_DACCONTROL23_HPSWEN (1 << 3)
+#define ES8328_DACCONTROL23_VROI_1p5k (0 << 4)
+#define ES8328_DACCONTROL23_VROI_40k (1 << 4)
+#define ES8328_DACCONTROL23_OUT3_VREF (0 << 5)
+#define ES8328_DACCONTROL23_OUT3_ROUT1 (1 << 5)
+#define ES8328_DACCONTROL23_OUT3_MONOOUT (2 << 5)
+#define ES8328_DACCONTROL23_OUT3_RIGHT_MIXER (3 << 5)
+#define ES8328_DACCONTROL23_ROUT2INV (1 << 7)
+
+/* LOUT1 Amplifier */
+#define ES8328_LOUT1VOL 0x2e
+#define ES8328_LOUT1VOL_MASK (0 << 5)
+#define ES8328_LOUT1VOL_MAX (0x24)
+
+/* ROUT1 Amplifier */
+#define ES8328_ROUT1VOL 0x2f
+#define ES8328_ROUT1VOL_MASK (0 << 5)
+#define ES8328_ROUT1VOL_MAX (0x24)
+
+#define ES8328_OUT1VOL_MAX (0x24)
+
+/* LOUT2 Amplifier */
+#define ES8328_LOUT2VOL 0x30
+#define ES8328_LOUT2VOL_MASK (0 << 5)
+#define ES8328_LOUT2VOL_MAX (0x24)
+
+/* ROUT2 Amplifier */
+#define ES8328_ROUT2VOL 0x31
+#define ES8328_ROUT2VOL_MASK (0 << 5)
+#define ES8328_ROUT2VOL_MAX (0x24)
+
+#define ES8328_OUT2VOL_MAX (0x24)
+
+/* Mono Out Amplifier */
+#define ES8328_MONOOUTVOL 0x32
+#define ES8328_MONOOUTVOL_MASK (0 << 5)
+#define ES8328_MONOOUTVOL_MAX (0x24)
+
+#define ES8328_DACCONTROL29 0x33
+#define ES8328_DACCONTROL30 0x34
+
+#define ES8328_SYSCLK 0
+
+#define ES8328_REG_MAX 0x35
+
+#define ES8328_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
+#define ES8328_512FS 512
+#define ES8328_384FS 384
+#define ES8328_256FS 256
+#define ES8328_128FS 128
+
+#endif
diff --git a/kernel/sound/soc/codecs/hdmi.c b/kernel/sound/soc/codecs/hdmi.c
new file mode 100644
index 000000000..bd42ad34e
--- /dev/null
+++ b/kernel/sound/soc/codecs/hdmi.c
@@ -0,0 +1,109 @@
+/*
+ * 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/kernel/sound/soc/codecs/isabelle.c b/kernel/sound/soc/codecs/isabelle.c
new file mode 100644
index 000000000..3a89ce66d
--- /dev/null
+++ b/kernel/sound/soc/codecs/isabelle.c
@@ -0,0 +1,1166 @@
+/*
+ * 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>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.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/tlv.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <asm/div64.h>
+#include "isabelle.h"
+
+
+/* Register default values for ISABELLE driver. */
+static struct reg_default isabelle_reg_defs[] = {
+ { 0, 0x00 },
+ { 1, 0x00 },
+ { 2, 0x00 },
+ { 3, 0x00 },
+ { 4, 0x00 },
+ { 5, 0x00 },
+ { 6, 0x00 },
+ { 7, 0x00 },
+ { 8, 0x00 },
+ { 9, 0x00 },
+ { 10, 0x00 },
+ { 11, 0x00 },
+ { 12, 0x00 },
+ { 13, 0x00 },
+ { 14, 0x00 },
+ { 15, 0x00 },
+ { 16, 0x00 },
+ { 17, 0x00 },
+ { 18, 0x00 },
+ { 19, 0x00 },
+ { 20, 0x00 },
+ { 21, 0x02 },
+ { 22, 0x02 },
+ { 23, 0x02 },
+ { 24, 0x02 },
+ { 25, 0x0F },
+ { 26, 0x8F },
+ { 27, 0x0F },
+ { 28, 0x8F },
+ { 29, 0x00 },
+ { 30, 0x00 },
+ { 31, 0x00 },
+ { 32, 0x00 },
+ { 33, 0x00 },
+ { 34, 0x00 },
+ { 35, 0x00 },
+ { 36, 0x00 },
+ { 37, 0x00 },
+ { 38, 0x00 },
+ { 39, 0x00 },
+ { 40, 0x00 },
+ { 41, 0x00 },
+ { 42, 0x00 },
+ { 43, 0x00 },
+ { 44, 0x00 },
+ { 45, 0x00 },
+ { 46, 0x00 },
+ { 47, 0x00 },
+ { 48, 0x00 },
+ { 49, 0x00 },
+ { 50, 0x00 },
+ { 51, 0x00 },
+ { 52, 0x00 },
+ { 53, 0x00 },
+ { 54, 0x00 },
+ { 55, 0x00 },
+ { 56, 0x00 },
+ { 57, 0x00 },
+ { 58, 0x00 },
+ { 59, 0x00 },
+ { 60, 0x00 },
+ { 61, 0x00 },
+ { 62, 0x00 },
+ { 63, 0x00 },
+ { 64, 0x00 },
+ { 65, 0x00 },
+ { 66, 0x00 },
+ { 67, 0x00 },
+ { 68, 0x00 },
+ { 69, 0x90 },
+ { 70, 0x90 },
+ { 71, 0x90 },
+ { 72, 0x00 },
+ { 73, 0x00 },
+ { 74, 0x00 },
+ { 75, 0x00 },
+ { 76, 0x00 },
+ { 77, 0x00 },
+ { 78, 0x00 },
+ { 79, 0x00 },
+ { 80, 0x00 },
+ { 81, 0x00 },
+ { 82, 0x00 },
+ { 83, 0x00 },
+ { 84, 0x00 },
+ { 85, 0x07 },
+ { 86, 0x00 },
+ { 87, 0x00 },
+ { 88, 0x00 },
+ { 89, 0x07 },
+ { 90, 0x80 },
+ { 91, 0x07 },
+ { 92, 0x07 },
+ { 93, 0x00 },
+ { 94, 0x00 },
+ { 95, 0x00 },
+ { 96, 0x00 },
+ { 97, 0x00 },
+ { 98, 0x00 },
+ { 99, 0x00 },
+};
+
+static const char *isabelle_rx1_texts[] = {"VRX1", "ARX1"};
+static const char *isabelle_rx2_texts[] = {"VRX2", "ARX2"};
+
+static const struct soc_enum isabelle_rx1_enum[] = {
+ SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 3,
+ ARRAY_SIZE(isabelle_rx1_texts), isabelle_rx1_texts),
+ SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 5,
+ ARRAY_SIZE(isabelle_rx1_texts), isabelle_rx1_texts),
+};
+
+static const struct soc_enum isabelle_rx2_enum[] = {
+ SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 2,
+ ARRAY_SIZE(isabelle_rx2_texts), isabelle_rx2_texts),
+ SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 4,
+ ARRAY_SIZE(isabelle_rx2_texts), isabelle_rx2_texts),
+};
+
+/* Headset DAC playback switches */
+static const struct snd_kcontrol_new rx1_mux_controls =
+ SOC_DAPM_ENUM("Route", isabelle_rx1_enum);
+
+static const struct snd_kcontrol_new rx2_mux_controls =
+ SOC_DAPM_ENUM("Route", isabelle_rx2_enum);
+
+/* TX input selection */
+static const char *isabelle_atx_texts[] = {"AMIC1", "DMIC"};
+static const char *isabelle_vtx_texts[] = {"AMIC2", "DMIC"};
+
+static const struct soc_enum isabelle_atx_enum[] = {
+ SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 7,
+ ARRAY_SIZE(isabelle_atx_texts), isabelle_atx_texts),
+ SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0,
+ ARRAY_SIZE(isabelle_atx_texts), isabelle_atx_texts),
+};
+
+static const struct soc_enum isabelle_vtx_enum[] = {
+ SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 6,
+ ARRAY_SIZE(isabelle_vtx_texts), isabelle_vtx_texts),
+ SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0,
+ ARRAY_SIZE(isabelle_vtx_texts), isabelle_vtx_texts),
+};
+
+static const struct snd_kcontrol_new atx_mux_controls =
+ SOC_DAPM_ENUM("Route", isabelle_atx_enum);
+
+static const struct snd_kcontrol_new vtx_mux_controls =
+ SOC_DAPM_ENUM("Route", isabelle_vtx_enum);
+
+/* Left analog microphone selection */
+static const char *isabelle_amic1_texts[] = {
+ "Main Mic", "Headset Mic", "Aux/FM Left"};
+
+/* Left analog microphone selection */
+static const char *isabelle_amic2_texts[] = {"Sub Mic", "Aux/FM Right"};
+
+static SOC_ENUM_SINGLE_DECL(isabelle_amic1_enum,
+ ISABELLE_AMIC_CFG_REG, 5,
+ isabelle_amic1_texts);
+
+static SOC_ENUM_SINGLE_DECL(isabelle_amic2_enum,
+ ISABELLE_AMIC_CFG_REG, 4,
+ isabelle_amic2_texts);
+
+static const struct snd_kcontrol_new amic1_control =
+ SOC_DAPM_ENUM("Route", isabelle_amic1_enum);
+
+static const struct snd_kcontrol_new amic2_control =
+ SOC_DAPM_ENUM("Route", isabelle_amic2_enum);
+
+static const char *isabelle_st_audio_texts[] = {"ATX1", "ATX2"};
+
+static const char *isabelle_st_voice_texts[] = {"VTX1", "VTX2"};
+
+static const struct soc_enum isabelle_st_audio_enum[] = {
+ SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA1_CFG_REG, 7,
+ ARRAY_SIZE(isabelle_st_audio_texts),
+ isabelle_st_audio_texts),
+ SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA2_CFG_REG, 7,
+ ARRAY_SIZE(isabelle_st_audio_texts),
+ isabelle_st_audio_texts),
+};
+
+static const struct soc_enum isabelle_st_voice_enum[] = {
+ SOC_ENUM_SINGLE(ISABELLE_VTX_STPGA1_CFG_REG, 7,
+ ARRAY_SIZE(isabelle_st_voice_texts),
+ isabelle_st_voice_texts),
+ SOC_ENUM_SINGLE(ISABELLE_VTX2_STPGA2_CFG_REG, 7,
+ ARRAY_SIZE(isabelle_st_voice_texts),
+ isabelle_st_voice_texts),
+};
+
+static const struct snd_kcontrol_new st_audio_control =
+ SOC_DAPM_ENUM("Route", isabelle_st_audio_enum);
+
+static const struct snd_kcontrol_new st_voice_control =
+ SOC_DAPM_ENUM("Route", isabelle_st_voice_enum);
+
+/* Mixer controls */
+static const struct snd_kcontrol_new isabelle_hs_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC1L Playback Switch", ISABELLE_HSDRV_CFG1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_HSDRV_CFG1_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_hs_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC1R Playback Switch", ISABELLE_HSDRV_CFG1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_HSDRV_CFG1_REG, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_hf_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC2L Playback Switch", ISABELLE_HFLPGA_CFG_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_HFLPGA_CFG_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_hf_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC2R Playback Switch", ISABELLE_HFRPGA_CFG_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_HFRPGA_CFG_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_ep_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC2L Playback Switch", ISABELLE_EARDRV_CFG1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_EARDRV_CFG1_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_aux_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC3L Playback Switch", ISABELLE_LINEAMP_CFG_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_LINEAMP_CFG_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_aux_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC3R Playback Switch", ISABELLE_LINEAMP_CFG_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_LINEAMP_CFG_REG, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga1_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga1_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga2_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga2_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("USNC Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga3_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga3_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx1_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DL1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx2_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("DL2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx3_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("DL3 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx4_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DL4 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx5_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DL5 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx6_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("DL6 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new ep_path_enable_control =
+ SOC_DAPM_SINGLE("Switch", ISABELLE_EARDRV_CFG2_REG, 0, 1, 0);
+
+/* TLV Declarations */
+static const DECLARE_TLV_DB_SCALE(mic_amp_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(afm_amp_tlv, -3300, 300, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -1200, 200, 0);
+static const DECLARE_TLV_DB_SCALE(hf_tlv, -5000, 200, 0);
+
+/* from -63 to 0 dB in 1 dB steps */
+static const DECLARE_TLV_DB_SCALE(dpga_tlv, -6300, 100, 1);
+
+/* from -63 to 9 dB in 1 dB steps */
+static const DECLARE_TLV_DB_SCALE(rx_tlv, -6300, 100, 1);
+
+static const DECLARE_TLV_DB_SCALE(st_tlv, -2700, 300, 1);
+static const DECLARE_TLV_DB_SCALE(tx_tlv, -600, 100, 0);
+
+static const struct snd_kcontrol_new isabelle_snd_controls[] = {
+ SOC_DOUBLE_TLV("Headset Playback Volume", ISABELLE_HSDRV_GAIN_REG,
+ 4, 0, 0xF, 0, dac_tlv),
+ SOC_DOUBLE_R_TLV("Handsfree Playback Volume",
+ ISABELLE_HFLPGA_CFG_REG, ISABELLE_HFRPGA_CFG_REG,
+ 0, 0x1F, 0, hf_tlv),
+ SOC_DOUBLE_TLV("Aux Playback Volume", ISABELLE_LINEAMP_GAIN_REG,
+ 4, 0, 0xF, 0, dac_tlv),
+ SOC_SINGLE_TLV("Earpiece Playback Volume", ISABELLE_EARDRV_CFG1_REG,
+ 0, 0xF, 0, dac_tlv),
+
+ SOC_DOUBLE_TLV("Aux FM Volume", ISABELLE_APGA_GAIN_REG, 4, 0, 0xF, 0,
+ afm_amp_tlv),
+ SOC_SINGLE_TLV("Mic1 Capture Volume", ISABELLE_MIC1_GAIN_REG, 3, 0x1F,
+ 0, mic_amp_tlv),
+ SOC_SINGLE_TLV("Mic2 Capture Volume", ISABELLE_MIC2_GAIN_REG, 3, 0x1F,
+ 0, mic_amp_tlv),
+
+ SOC_DOUBLE_R_TLV("DPGA1 Volume", ISABELLE_DPGA1L_GAIN_REG,
+ ISABELLE_DPGA1R_GAIN_REG, 0, 0x3F, 0, dpga_tlv),
+ SOC_DOUBLE_R_TLV("DPGA2 Volume", ISABELLE_DPGA2L_GAIN_REG,
+ ISABELLE_DPGA2R_GAIN_REG, 0, 0x3F, 0, dpga_tlv),
+ SOC_DOUBLE_R_TLV("DPGA3 Volume", ISABELLE_DPGA3L_GAIN_REG,
+ ISABELLE_DPGA3R_GAIN_REG, 0, 0x3F, 0, dpga_tlv),
+
+ SOC_SINGLE_TLV("Sidetone Audio TX1 Volume",
+ ISABELLE_ATX_STPGA1_CFG_REG, 0, 0xF, 0, st_tlv),
+ SOC_SINGLE_TLV("Sidetone Audio TX2 Volume",
+ ISABELLE_ATX_STPGA2_CFG_REG, 0, 0xF, 0, st_tlv),
+ SOC_SINGLE_TLV("Sidetone Voice TX1 Volume",
+ ISABELLE_VTX_STPGA1_CFG_REG, 0, 0xF, 0, st_tlv),
+ SOC_SINGLE_TLV("Sidetone Voice TX2 Volume",
+ ISABELLE_VTX2_STPGA2_CFG_REG, 0, 0xF, 0, st_tlv),
+
+ SOC_SINGLE_TLV("Audio TX1 Volume", ISABELLE_ATX1_DPGA_REG, 4, 0xF, 0,
+ tx_tlv),
+ SOC_SINGLE_TLV("Audio TX2 Volume", ISABELLE_ATX2_DPGA_REG, 4, 0xF, 0,
+ tx_tlv),
+ SOC_SINGLE_TLV("Voice TX1 Volume", ISABELLE_VTX1_DPGA_REG, 4, 0xF, 0,
+ tx_tlv),
+ SOC_SINGLE_TLV("Voice TX2 Volume", ISABELLE_VTX2_DPGA_REG, 4, 0xF, 0,
+ tx_tlv),
+
+ SOC_SINGLE_TLV("RX1 DPGA Volume", ISABELLE_RX1_DPGA_REG, 0, 0x3F, 0,
+ rx_tlv),
+ SOC_SINGLE_TLV("RX2 DPGA Volume", ISABELLE_RX2_DPGA_REG, 0, 0x3F, 0,
+ rx_tlv),
+ SOC_SINGLE_TLV("RX3 DPGA Volume", ISABELLE_RX3_DPGA_REG, 0, 0x3F, 0,
+ rx_tlv),
+ SOC_SINGLE_TLV("RX4 DPGA Volume", ISABELLE_RX4_DPGA_REG, 0, 0x3F, 0,
+ rx_tlv),
+ SOC_SINGLE_TLV("RX5 DPGA Volume", ISABELLE_RX5_DPGA_REG, 0, 0x3F, 0,
+ rx_tlv),
+ SOC_SINGLE_TLV("RX6 DPGA Volume", ISABELLE_RX6_DPGA_REG, 0, 0x3F, 0,
+ rx_tlv),
+
+ SOC_SINGLE("Headset Noise Gate", ISABELLE_HS_NG_CFG1_REG, 7, 1, 0),
+ SOC_SINGLE("Handsfree Noise Gate", ISABELLE_HF_NG_CFG1_REG, 7, 1, 0),
+
+ SOC_SINGLE("ATX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 7, 1, 0),
+ SOC_SINGLE("ATX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 6, 1, 0),
+ SOC_SINGLE("ARX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 5, 1, 0),
+ SOC_SINGLE("ARX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 4, 1, 0),
+ SOC_SINGLE("ARX3 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 3, 1, 0),
+ SOC_SINGLE("ARX4 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 2, 1, 0),
+ SOC_SINGLE("ARX5 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 1, 1, 0),
+ SOC_SINGLE("ARX6 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 0, 1, 0),
+ SOC_SINGLE("VRX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 3, 1, 0),
+ SOC_SINGLE("VRX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+ 2, 1, 0),
+
+ SOC_SINGLE("ATX1 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG,
+ 7, 1, 0),
+ SOC_SINGLE("ATX2 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG,
+ 6, 1, 0),
+ SOC_SINGLE("VTX1 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG,
+ 5, 1, 0),
+ SOC_SINGLE("VTX2 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG,
+ 4, 1, 0),
+ SOC_SINGLE("RX1 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+ 5, 1, 0),
+ SOC_SINGLE("RX2 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+ 4, 1, 0),
+ SOC_SINGLE("RX3 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+ 3, 1, 0),
+ SOC_SINGLE("RX4 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+ 2, 1, 0),
+ SOC_SINGLE("RX5 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+ 1, 1, 0),
+ SOC_SINGLE("RX6 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+ 0, 1, 0),
+
+ SOC_SINGLE("ULATX12 Capture Switch", ISABELLE_ULATX12_INTF_CFG_REG,
+ 7, 1, 0),
+
+ SOC_SINGLE("DL12 Playback Switch", ISABELLE_DL12_INTF_CFG_REG,
+ 7, 1, 0),
+ SOC_SINGLE("DL34 Playback Switch", ISABELLE_DL34_INTF_CFG_REG,
+ 7, 1, 0),
+ SOC_SINGLE("DL56 Playback Switch", ISABELLE_DL56_INTF_CFG_REG,
+ 7, 1, 0),
+
+ /* DMIC Switch */
+ SOC_SINGLE("DMIC Switch", ISABELLE_DMIC_CFG_REG, 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget isabelle_dapm_widgets[] = {
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MAINMIC"),
+ SND_SOC_DAPM_INPUT("HSMIC"),
+ SND_SOC_DAPM_INPUT("SUBMIC"),
+ SND_SOC_DAPM_INPUT("LINEIN1"),
+ SND_SOC_DAPM_INPUT("LINEIN2"),
+ SND_SOC_DAPM_INPUT("DMICDAT"),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HSOL"),
+ SND_SOC_DAPM_OUTPUT("HSOR"),
+ SND_SOC_DAPM_OUTPUT("HFL"),
+ SND_SOC_DAPM_OUTPUT("HFR"),
+ SND_SOC_DAPM_OUTPUT("EP"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT1"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT2"),
+
+ SND_SOC_DAPM_PGA("DL1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DL2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DL3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DL4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DL5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DL6", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Analog input muxes for the capture amplifiers */
+ SND_SOC_DAPM_MUX("Analog Left Capture Route",
+ SND_SOC_NOPM, 0, 0, &amic1_control),
+ SND_SOC_DAPM_MUX("Analog Right Capture Route",
+ SND_SOC_NOPM, 0, 0, &amic2_control),
+
+ SND_SOC_DAPM_MUX("Sidetone Audio Playback", SND_SOC_NOPM, 0, 0,
+ &st_audio_control),
+ SND_SOC_DAPM_MUX("Sidetone Voice Playback", SND_SOC_NOPM, 0, 0,
+ &st_voice_control),
+
+ /* AIF */
+ SND_SOC_DAPM_AIF_IN("INTF1_SDI", NULL, 0, ISABELLE_INTF_EN_REG, 7, 0),
+ SND_SOC_DAPM_AIF_IN("INTF2_SDI", NULL, 0, ISABELLE_INTF_EN_REG, 6, 0),
+
+ SND_SOC_DAPM_AIF_OUT("INTF1_SDO", NULL, 0, ISABELLE_INTF_EN_REG, 5, 0),
+ SND_SOC_DAPM_AIF_OUT("INTF2_SDO", NULL, 0, ISABELLE_INTF_EN_REG, 4, 0),
+
+ SND_SOC_DAPM_OUT_DRV("ULATX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("ULATX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("ULVTX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("ULVTX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Analog Capture PGAs */
+ SND_SOC_DAPM_PGA("MicAmp1", ISABELLE_AMIC_CFG_REG, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MicAmp2", ISABELLE_AMIC_CFG_REG, 4, 0, NULL, 0),
+
+ /* Auxiliary FM PGAs */
+ SND_SOC_DAPM_PGA("APGA1", ISABELLE_APGA_CFG_REG, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("APGA2", ISABELLE_APGA_CFG_REG, 6, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1", "Left Front Capture",
+ ISABELLE_AMIC_CFG_REG, 7, 0),
+ SND_SOC_DAPM_ADC("ADC2", "Right Front Capture",
+ ISABELLE_AMIC_CFG_REG, 6, 0),
+
+ /* Microphone Bias */
+ SND_SOC_DAPM_SUPPLY("Headset Mic Bias", ISABELLE_ABIAS_CFG_REG,
+ 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Main Mic Bias", ISABELLE_ABIAS_CFG_REG,
+ 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Digital Mic1 Bias",
+ ISABELLE_DBIAS_CFG_REG, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Digital Mic2 Bias",
+ ISABELLE_DBIAS_CFG_REG, 2, 0, NULL, 0),
+
+ /* Mixers */
+ SND_SOC_DAPM_MIXER("Headset Left Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_hs_left_mixer_controls,
+ ARRAY_SIZE(isabelle_hs_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Headset Right Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_hs_right_mixer_controls,
+ ARRAY_SIZE(isabelle_hs_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Handsfree Left Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_hf_left_mixer_controls,
+ ARRAY_SIZE(isabelle_hf_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Handsfree Right Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_hf_right_mixer_controls,
+ ARRAY_SIZE(isabelle_hf_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("LINEOUT1 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_aux_left_mixer_controls,
+ ARRAY_SIZE(isabelle_aux_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("LINEOUT2 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_aux_right_mixer_controls,
+ ARRAY_SIZE(isabelle_aux_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Earphone Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_ep_mixer_controls,
+ ARRAY_SIZE(isabelle_ep_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("DPGA1L Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_dpga1_left_mixer_controls,
+ ARRAY_SIZE(isabelle_dpga1_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DPGA1R Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_dpga1_right_mixer_controls,
+ ARRAY_SIZE(isabelle_dpga1_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DPGA2L Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_dpga2_left_mixer_controls,
+ ARRAY_SIZE(isabelle_dpga2_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DPGA2R Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_dpga2_right_mixer_controls,
+ ARRAY_SIZE(isabelle_dpga2_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DPGA3L Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_dpga3_left_mixer_controls,
+ ARRAY_SIZE(isabelle_dpga3_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DPGA3R Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_dpga3_right_mixer_controls,
+ ARRAY_SIZE(isabelle_dpga3_right_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("RX1 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_rx1_mixer_controls,
+ ARRAY_SIZE(isabelle_rx1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX2 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_rx2_mixer_controls,
+ ARRAY_SIZE(isabelle_rx2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX3 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_rx3_mixer_controls,
+ ARRAY_SIZE(isabelle_rx3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX4 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_rx4_mixer_controls,
+ ARRAY_SIZE(isabelle_rx4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX5 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_rx5_mixer_controls,
+ ARRAY_SIZE(isabelle_rx5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RX6 Mixer", SND_SOC_NOPM, 0, 0,
+ isabelle_rx6_mixer_controls,
+ ARRAY_SIZE(isabelle_rx6_mixer_controls)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC1L", "Headset Playback", ISABELLE_DAC_CFG_REG,
+ 5, 0),
+ SND_SOC_DAPM_DAC("DAC1R", "Headset Playback", ISABELLE_DAC_CFG_REG,
+ 4, 0),
+ SND_SOC_DAPM_DAC("DAC2L", "Handsfree Playback", ISABELLE_DAC_CFG_REG,
+ 3, 0),
+ SND_SOC_DAPM_DAC("DAC2R", "Handsfree Playback", ISABELLE_DAC_CFG_REG,
+ 2, 0),
+ SND_SOC_DAPM_DAC("DAC3L", "Lineout Playback", ISABELLE_DAC_CFG_REG,
+ 1, 0),
+ SND_SOC_DAPM_DAC("DAC3R", "Lineout Playback", ISABELLE_DAC_CFG_REG,
+ 0, 0),
+
+ /* Analog Playback PGAs */
+ SND_SOC_DAPM_PGA("Sidetone Audio PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Sidetone Voice PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HF Left PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HF Right PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DPGA1L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DPGA1R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DPGA2L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DPGA2R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DPGA3L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DPGA3R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Analog Playback Mux */
+ SND_SOC_DAPM_MUX("RX1 Playback", ISABELLE_ALU_RX_EN_REG, 5, 0,
+ &rx1_mux_controls),
+ SND_SOC_DAPM_MUX("RX2 Playback", ISABELLE_ALU_RX_EN_REG, 4, 0,
+ &rx2_mux_controls),
+
+ /* TX Select */
+ SND_SOC_DAPM_MUX("ATX Select", ISABELLE_TX_INPUT_CFG_REG,
+ 7, 0, &atx_mux_controls),
+ SND_SOC_DAPM_MUX("VTX Select", ISABELLE_TX_INPUT_CFG_REG,
+ 6, 0, &vtx_mux_controls),
+
+ SND_SOC_DAPM_SWITCH("Earphone Playback", SND_SOC_NOPM, 0, 0,
+ &ep_path_enable_control),
+
+ /* Output Drivers */
+ SND_SOC_DAPM_OUT_DRV("HS Left Driver", ISABELLE_HSDRV_CFG2_REG,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("HS Right Driver", ISABELLE_HSDRV_CFG2_REG,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("LINEOUT1 Left Driver", ISABELLE_LINEAMP_CFG_REG,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("LINEOUT2 Right Driver", ISABELLE_LINEAMP_CFG_REG,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Earphone Driver", ISABELLE_EARDRV_CFG2_REG,
+ 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("HF Left Driver", ISABELLE_HFDRV_CFG_REG,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("HF Right Driver", ISABELLE_HFDRV_CFG_REG,
+ 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route isabelle_intercon[] = {
+ /* Interface mapping */
+ { "DL1", "DL12 Playback Switch", "INTF1_SDI" },
+ { "DL2", "DL12 Playback Switch", "INTF1_SDI" },
+ { "DL3", "DL34 Playback Switch", "INTF1_SDI" },
+ { "DL4", "DL34 Playback Switch", "INTF1_SDI" },
+ { "DL5", "DL56 Playback Switch", "INTF1_SDI" },
+ { "DL6", "DL56 Playback Switch", "INTF1_SDI" },
+
+ { "DL1", "DL12 Playback Switch", "INTF2_SDI" },
+ { "DL2", "DL12 Playback Switch", "INTF2_SDI" },
+ { "DL3", "DL34 Playback Switch", "INTF2_SDI" },
+ { "DL4", "DL34 Playback Switch", "INTF2_SDI" },
+ { "DL5", "DL56 Playback Switch", "INTF2_SDI" },
+ { "DL6", "DL56 Playback Switch", "INTF2_SDI" },
+
+ /* Input side mapping */
+ { "Sidetone Audio PGA", NULL, "Sidetone Audio Playback" },
+ { "Sidetone Voice PGA", NULL, "Sidetone Voice Playback" },
+
+ { "RX1 Mixer", "ST1 Playback Switch", "Sidetone Audio PGA" },
+
+ { "RX1 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" },
+ { "RX1 Mixer", "DL1 Playback Switch", "DL1" },
+
+ { "RX2 Mixer", "ST2 Playback Switch", "Sidetone Audio PGA" },
+
+ { "RX2 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" },
+ { "RX2 Mixer", "DL2 Playback Switch", "DL2" },
+
+ { "RX3 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" },
+ { "RX3 Mixer", "DL3 Playback Switch", "DL3" },
+
+ { "RX4 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" },
+ { "RX4 Mixer", "DL4 Playback Switch", "DL4" },
+
+ { "RX5 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" },
+ { "RX5 Mixer", "DL5 Playback Switch", "DL5" },
+
+ { "RX6 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" },
+ { "RX6 Mixer", "DL6 Playback Switch", "DL6" },
+
+ /* Capture path */
+ { "Analog Left Capture Route", "Headset Mic", "HSMIC" },
+ { "Analog Left Capture Route", "Main Mic", "MAINMIC" },
+ { "Analog Left Capture Route", "Aux/FM Left", "LINEIN1" },
+
+ { "Analog Right Capture Route", "Sub Mic", "SUBMIC" },
+ { "Analog Right Capture Route", "Aux/FM Right", "LINEIN2" },
+
+ { "MicAmp1", NULL, "Analog Left Capture Route" },
+ { "MicAmp2", NULL, "Analog Right Capture Route" },
+
+ { "ADC1", NULL, "MicAmp1" },
+ { "ADC2", NULL, "MicAmp2" },
+
+ { "ATX Select", "AMIC1", "ADC1" },
+ { "ATX Select", "DMIC", "DMICDAT" },
+ { "ATX Select", "AMIC2", "ADC2" },
+
+ { "VTX Select", "AMIC1", "ADC1" },
+ { "VTX Select", "DMIC", "DMICDAT" },
+ { "VTX Select", "AMIC2", "ADC2" },
+
+ { "ULATX1", "ATX1 Filter Enable Switch", "ATX Select" },
+ { "ULATX1", "ATX1 Filter Bypass Switch", "ATX Select" },
+ { "ULATX2", "ATX2 Filter Enable Switch", "ATX Select" },
+ { "ULATX2", "ATX2 Filter Bypass Switch", "ATX Select" },
+
+ { "ULVTX1", "VTX1 Filter Enable Switch", "VTX Select" },
+ { "ULVTX1", "VTX1 Filter Bypass Switch", "VTX Select" },
+ { "ULVTX2", "VTX2 Filter Enable Switch", "VTX Select" },
+ { "ULVTX2", "VTX2 Filter Bypass Switch", "VTX Select" },
+
+ { "INTF1_SDO", "ULATX12 Capture Switch", "ULATX1" },
+ { "INTF1_SDO", "ULATX12 Capture Switch", "ULATX2" },
+ { "INTF2_SDO", "ULATX12 Capture Switch", "ULATX1" },
+ { "INTF2_SDO", "ULATX12 Capture Switch", "ULATX2" },
+
+ { "INTF1_SDO", NULL, "ULVTX1" },
+ { "INTF1_SDO", NULL, "ULVTX2" },
+ { "INTF2_SDO", NULL, "ULVTX1" },
+ { "INTF2_SDO", NULL, "ULVTX2" },
+
+ /* AFM Path */
+ { "APGA1", NULL, "LINEIN1" },
+ { "APGA2", NULL, "LINEIN2" },
+
+ { "RX1 Playback", "VRX1 Filter Bypass Switch", "RX1 Mixer" },
+ { "RX1 Playback", "ARX1 Filter Bypass Switch", "RX1 Mixer" },
+ { "RX1 Playback", "RX1 Filter Enable Switch", "RX1 Mixer" },
+
+ { "RX2 Playback", "VRX2 Filter Bypass Switch", "RX2 Mixer" },
+ { "RX2 Playback", "ARX2 Filter Bypass Switch", "RX2 Mixer" },
+ { "RX2 Playback", "RX2 Filter Enable Switch", "RX2 Mixer" },
+
+ { "RX3 Playback", "ARX3 Filter Bypass Switch", "RX3 Mixer" },
+ { "RX3 Playback", "RX3 Filter Enable Switch", "RX3 Mixer" },
+
+ { "RX4 Playback", "ARX4 Filter Bypass Switch", "RX4 Mixer" },
+ { "RX4 Playback", "RX4 Filter Enable Switch", "RX4 Mixer" },
+
+ { "RX5 Playback", "ARX5 Filter Bypass Switch", "RX5 Mixer" },
+ { "RX5 Playback", "RX5 Filter Enable Switch", "RX5 Mixer" },
+
+ { "RX6 Playback", "ARX6 Filter Bypass Switch", "RX6 Mixer" },
+ { "RX6 Playback", "RX6 Filter Enable Switch", "RX6 Mixer" },
+
+ { "DPGA1L Mixer", "RX1 Playback Switch", "RX1 Playback" },
+ { "DPGA1L Mixer", "RX3 Playback Switch", "RX3 Playback" },
+ { "DPGA1L Mixer", "RX5 Playback Switch", "RX5 Playback" },
+
+ { "DPGA1R Mixer", "RX2 Playback Switch", "RX2 Playback" },
+ { "DPGA1R Mixer", "RX4 Playback Switch", "RX4 Playback" },
+ { "DPGA1R Mixer", "RX6 Playback Switch", "RX6 Playback" },
+
+ { "DPGA1L", NULL, "DPGA1L Mixer" },
+ { "DPGA1R", NULL, "DPGA1R Mixer" },
+
+ { "DAC1L", NULL, "DPGA1L" },
+ { "DAC1R", NULL, "DPGA1R" },
+
+ { "DPGA2L Mixer", "RX1 Playback Switch", "RX1 Playback" },
+ { "DPGA2L Mixer", "RX2 Playback Switch", "RX2 Playback" },
+ { "DPGA2L Mixer", "RX3 Playback Switch", "RX3 Playback" },
+ { "DPGA2L Mixer", "RX4 Playback Switch", "RX4 Playback" },
+ { "DPGA2L Mixer", "RX5 Playback Switch", "RX5 Playback" },
+ { "DPGA2L Mixer", "RX6 Playback Switch", "RX6 Playback" },
+
+ { "DPGA2R Mixer", "RX2 Playback Switch", "RX2 Playback" },
+ { "DPGA2R Mixer", "RX4 Playback Switch", "RX4 Playback" },
+ { "DPGA2R Mixer", "RX6 Playback Switch", "RX6 Playback" },
+
+ { "DPGA2L", NULL, "DPGA2L Mixer" },
+ { "DPGA2R", NULL, "DPGA2R Mixer" },
+
+ { "DAC2L", NULL, "DPGA2L" },
+ { "DAC2R", NULL, "DPGA2R" },
+
+ { "DPGA3L Mixer", "RX1 Playback Switch", "RX1 Playback" },
+ { "DPGA3L Mixer", "RX3 Playback Switch", "RX3 Playback" },
+ { "DPGA3L Mixer", "RX5 Playback Switch", "RX5 Playback" },
+
+ { "DPGA3R Mixer", "RX2 Playback Switch", "RX2 Playback" },
+ { "DPGA3R Mixer", "RX4 Playback Switch", "RX4 Playback" },
+ { "DPGA3R Mixer", "RX6 Playback Switch", "RX6 Playback" },
+
+ { "DPGA3L", NULL, "DPGA3L Mixer" },
+ { "DPGA3R", NULL, "DPGA3R Mixer" },
+
+ { "DAC3L", NULL, "DPGA3L" },
+ { "DAC3R", NULL, "DPGA3R" },
+
+ { "Headset Left Mixer", "DAC1L Playback Switch", "DAC1L" },
+ { "Headset Left Mixer", "APGA1 Playback Switch", "APGA1" },
+
+ { "Headset Right Mixer", "DAC1R Playback Switch", "DAC1R" },
+ { "Headset Right Mixer", "APGA2 Playback Switch", "APGA2" },
+
+ { "HS Left Driver", NULL, "Headset Left Mixer" },
+ { "HS Right Driver", NULL, "Headset Right Mixer" },
+
+ { "HSOL", NULL, "HS Left Driver" },
+ { "HSOR", NULL, "HS Right Driver" },
+
+ /* Earphone playback path */
+ { "Earphone Mixer", "DAC2L Playback Switch", "DAC2L" },
+ { "Earphone Mixer", "APGA1 Playback Switch", "APGA1" },
+
+ { "Earphone Playback", "Switch", "Earphone Mixer" },
+ { "Earphone Driver", NULL, "Earphone Playback" },
+ { "EP", NULL, "Earphone Driver" },
+
+ { "Handsfree Left Mixer", "DAC2L Playback Switch", "DAC2L" },
+ { "Handsfree Left Mixer", "APGA1 Playback Switch", "APGA1" },
+
+ { "Handsfree Right Mixer", "DAC2R Playback Switch", "DAC2R" },
+ { "Handsfree Right Mixer", "APGA2 Playback Switch", "APGA2" },
+
+ { "HF Left PGA", NULL, "Handsfree Left Mixer" },
+ { "HF Right PGA", NULL, "Handsfree Right Mixer" },
+
+ { "HF Left Driver", NULL, "HF Left PGA" },
+ { "HF Right Driver", NULL, "HF Right PGA" },
+
+ { "HFL", NULL, "HF Left Driver" },
+ { "HFR", NULL, "HF Right Driver" },
+
+ { "LINEOUT1 Mixer", "DAC3L Playback Switch", "DAC3L" },
+ { "LINEOUT1 Mixer", "APGA1 Playback Switch", "APGA1" },
+
+ { "LINEOUT2 Mixer", "DAC3R Playback Switch", "DAC3R" },
+ { "LINEOUT2 Mixer", "APGA2 Playback Switch", "APGA2" },
+
+ { "LINEOUT1 Driver", NULL, "LINEOUT1 Mixer" },
+ { "LINEOUT2 Driver", NULL, "LINEOUT2 Mixer" },
+
+ { "LINEOUT1", NULL, "LINEOUT1 Driver" },
+ { "LINEOUT2", NULL, "LINEOUT2 Driver" },
+};
+
+static int isabelle_hs_mute(struct snd_soc_dai *dai, int mute)
+{
+ snd_soc_update_bits(dai->codec, ISABELLE_DAC1_SOFTRAMP_REG,
+ BIT(4), (mute ? BIT(4) : 0));
+
+ return 0;
+}
+
+static int isabelle_hf_mute(struct snd_soc_dai *dai, int mute)
+{
+ snd_soc_update_bits(dai->codec, ISABELLE_DAC2_SOFTRAMP_REG,
+ BIT(4), (mute ? BIT(4) : 0));
+
+ return 0;
+}
+
+static int isabelle_line_mute(struct snd_soc_dai *dai, int mute)
+{
+ snd_soc_update_bits(dai->codec, ISABELLE_DAC3_SOFTRAMP_REG,
+ BIT(4), (mute ? BIT(4) : 0));
+
+ return 0;
+}
+
+static int isabelle_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_update_bits(codec, ISABELLE_PWR_EN_REG,
+ ISABELLE_CHIP_EN, BIT(0));
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, ISABELLE_PWR_EN_REG,
+ ISABELLE_CHIP_EN, 0);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int isabelle_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 aif = 0;
+ unsigned int fs_val = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs_val = ISABELLE_FS_RATE_8;
+ break;
+ case 11025:
+ fs_val = ISABELLE_FS_RATE_11;
+ break;
+ case 12000:
+ fs_val = ISABELLE_FS_RATE_12;
+ break;
+ case 16000:
+ fs_val = ISABELLE_FS_RATE_16;
+ break;
+ case 22050:
+ fs_val = ISABELLE_FS_RATE_22;
+ break;
+ case 24000:
+ fs_val = ISABELLE_FS_RATE_24;
+ break;
+ case 32000:
+ fs_val = ISABELLE_FS_RATE_32;
+ break;
+ case 44100:
+ fs_val = ISABELLE_FS_RATE_44;
+ break;
+ case 48000:
+ fs_val = ISABELLE_FS_RATE_48;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, ISABELLE_FS_RATE_CFG_REG,
+ ISABELLE_FS_RATE_MASK, fs_val);
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 20:
+ aif |= ISABELLE_AIF_LENGTH_20;
+ break;
+ case 32:
+ aif |= ISABELLE_AIF_LENGTH_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, ISABELLE_INTF_CFG_REG,
+ ISABELLE_AIF_LENGTH_MASK, aif);
+
+ return 0;
+}
+
+static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int aif_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ aif_val &= ~ISABELLE_AIF_MS;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif_val |= ISABELLE_AIF_MS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ aif_val |= ISABELLE_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif_val |= ISABELLE_LEFT_J_MODE;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ aif_val |= ISABELLE_PDM_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, ISABELLE_INTF_CFG_REG,
+ (ISABELLE_AIF_MS | ISABELLE_AIF_FMT_MASK), aif_val);
+
+ return 0;
+}
+
+/* Rates supported by Isabelle driver */
+#define ISABELLE_RATES SNDRV_PCM_RATE_8000_48000
+
+/* Formates supported by Isabelle driver. */
+#define ISABELLE_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static 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,
+};
+
+static 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,
+};
+
+static 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,
+};
+
+static struct snd_soc_dai_ops isabelle_ul_dai_ops = {
+ .hw_params = isabelle_hw_params,
+ .set_fmt = isabelle_set_dai_fmt,
+};
+
+/* ISABELLE dai structure */
+static struct snd_soc_dai_driver isabelle_dai[] = {
+ {
+ .name = "isabelle-dl1",
+ .playback = {
+ .stream_name = "Headset Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ISABELLE_RATES,
+ .formats = ISABELLE_FORMATS,
+ },
+ .ops = &isabelle_hs_dai_ops,
+ },
+ {
+ .name = "isabelle-dl2",
+ .playback = {
+ .stream_name = "Handsfree Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ISABELLE_RATES,
+ .formats = ISABELLE_FORMATS,
+ },
+ .ops = &isabelle_hf_dai_ops,
+ },
+ {
+ .name = "isabelle-lineout",
+ .playback = {
+ .stream_name = "Lineout Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ISABELLE_RATES,
+ .formats = ISABELLE_FORMATS,
+ },
+ .ops = &isabelle_line_dai_ops,
+ },
+ {
+ .name = "isabelle-ul",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ISABELLE_RATES,
+ .formats = ISABELLE_FORMATS,
+ },
+ .ops = &isabelle_ul_dai_ops,
+ },
+};
+
+static 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 regmap_config isabelle_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = ISABELLE_MAX_REGISTER,
+ .reg_defaults = isabelle_reg_defs,
+ .num_reg_defaults = ARRAY_SIZE(isabelle_reg_defs),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int isabelle_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regmap *isabelle_regmap;
+ int ret = 0;
+
+ isabelle_regmap = devm_regmap_init_i2c(i2c, &isabelle_regmap_config);
+ if (IS_ERR(isabelle_regmap)) {
+ ret = PTR_ERR(isabelle_regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+ i2c_set_clientdata(i2c, isabelle_regmap);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_isabelle, isabelle_dai,
+ ARRAY_SIZE(isabelle_dai));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register codec: %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 },
+ { }
+};
+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,
+};
+
+module_i2c_driver(isabelle_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ISABELLE driver");
+MODULE_AUTHOR("Vishwas A Deshpande <vishwas.a.deshpande@ti.com>");
+MODULE_AUTHOR("M R Swami Reddy <MR.Swami.Reddy@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/isabelle.h b/kernel/sound/soc/codecs/isabelle.h
new file mode 100644
index 000000000..96d839a8c
--- /dev/null
+++ b/kernel/sound/soc/codecs/isabelle.h
@@ -0,0 +1,143 @@
+/*
+ * 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
+#define _ISABELLE_H
+
+#include <linux/bitops.h>
+
+/* ISABELLE REGISTERS */
+
+#define ISABELLE_PWR_CFG_REG 0x01
+#define ISABELLE_PWR_EN_REG 0x02
+#define ISABELLE_PS_EN1_REG 0x03
+#define ISABELLE_INT1_STATUS_REG 0x04
+#define ISABELLE_INT1_MASK_REG 0x05
+#define ISABELLE_INT2_STATUS_REG 0x06
+#define ISABELLE_INT2_MASK_REG 0x07
+#define ISABELLE_HKCTL1_REG 0x08
+#define ISABELLE_HKCTL2_REG 0x09
+#define ISABELLE_HKCTL3_REG 0x0A
+#define ISABELLE_ACCDET_STATUS_REG 0x0B
+#define ISABELLE_BUTTON_ID_REG 0x0C
+#define ISABELLE_PLL_CFG_REG 0x10
+#define ISABELLE_PLL_EN_REG 0x11
+#define ISABELLE_FS_RATE_CFG_REG 0x12
+#define ISABELLE_INTF_CFG_REG 0x13
+#define ISABELLE_INTF_EN_REG 0x14
+#define ISABELLE_ULATX12_INTF_CFG_REG 0x15
+#define ISABELLE_DL12_INTF_CFG_REG 0x16
+#define ISABELLE_DL34_INTF_CFG_REG 0x17
+#define ISABELLE_DL56_INTF_CFG_REG 0x18
+#define ISABELLE_ATX_STPGA1_CFG_REG 0x19
+#define ISABELLE_ATX_STPGA2_CFG_REG 0x1A
+#define ISABELLE_VTX_STPGA1_CFG_REG 0x1B
+#define ISABELLE_VTX2_STPGA2_CFG_REG 0x1C
+#define ISABELLE_ATX1_DPGA_REG 0x1D
+#define ISABELLE_ATX2_DPGA_REG 0x1E
+#define ISABELLE_VTX1_DPGA_REG 0x1F
+#define ISABELLE_VTX2_DPGA_REG 0x20
+#define ISABELLE_TX_INPUT_CFG_REG 0x21
+#define ISABELLE_RX_INPUT_CFG_REG 0x22
+#define ISABELLE_RX_INPUT_CFG2_REG 0x23
+#define ISABELLE_VOICE_HPF_CFG_REG 0x24
+#define ISABELLE_AUDIO_HPF_CFG_REG 0x25
+#define ISABELLE_RX1_DPGA_REG 0x26
+#define ISABELLE_RX2_DPGA_REG 0x27
+#define ISABELLE_RX3_DPGA_REG 0x28
+#define ISABELLE_RX4_DPGA_REG 0x29
+#define ISABELLE_RX5_DPGA_REG 0x2A
+#define ISABELLE_RX6_DPGA_REG 0x2B
+#define ISABELLE_ALU_TX_EN_REG 0x2C
+#define ISABELLE_ALU_RX_EN_REG 0x2D
+#define ISABELLE_IIR_RESYNC_REG 0x2E
+#define ISABELLE_ABIAS_CFG_REG 0x30
+#define ISABELLE_DBIAS_CFG_REG 0x31
+#define ISABELLE_MIC1_GAIN_REG 0x32
+#define ISABELLE_MIC2_GAIN_REG 0x33
+#define ISABELLE_AMIC_CFG_REG 0x34
+#define ISABELLE_DMIC_CFG_REG 0x35
+#define ISABELLE_APGA_GAIN_REG 0x36
+#define ISABELLE_APGA_CFG_REG 0x37
+#define ISABELLE_TX_GAIN_DLY_REG 0x38
+#define ISABELLE_RX_GAIN_DLY_REG 0x39
+#define ISABELLE_RX_PWR_CTRL_REG 0x3A
+#define ISABELLE_DPGA1LR_IN_SEL_REG 0x3B
+#define ISABELLE_DPGA1L_GAIN_REG 0x3C
+#define ISABELLE_DPGA1R_GAIN_REG 0x3D
+#define ISABELLE_DPGA2L_IN_SEL_REG 0x3E
+#define ISABELLE_DPGA2R_IN_SEL_REG 0x3F
+#define ISABELLE_DPGA2L_GAIN_REG 0x40
+#define ISABELLE_DPGA2R_GAIN_REG 0x41
+#define ISABELLE_DPGA3LR_IN_SEL_REG 0x42
+#define ISABELLE_DPGA3L_GAIN_REG 0x43
+#define ISABELLE_DPGA3R_GAIN_REG 0x44
+#define ISABELLE_DAC1_SOFTRAMP_REG 0x45
+#define ISABELLE_DAC2_SOFTRAMP_REG 0x46
+#define ISABELLE_DAC3_SOFTRAMP_REG 0x47
+#define ISABELLE_DAC_CFG_REG 0x48
+#define ISABELLE_EARDRV_CFG1_REG 0x49
+#define ISABELLE_EARDRV_CFG2_REG 0x4A
+#define ISABELLE_HSDRV_GAIN_REG 0x4B
+#define ISABELLE_HSDRV_CFG1_REG 0x4C
+#define ISABELLE_HSDRV_CFG2_REG 0x4D
+#define ISABELLE_HS_NG_CFG1_REG 0x4E
+#define ISABELLE_HS_NG_CFG2_REG 0x4F
+#define ISABELLE_LINEAMP_GAIN_REG 0x50
+#define ISABELLE_LINEAMP_CFG_REG 0x51
+#define ISABELLE_HFL_VOL_CTRL_REG 0x52
+#define ISABELLE_HFL_SFTVOL_CTRL_REG 0x53
+#define ISABELLE_HFL_LIM_CTRL_1_REG 0x54
+#define ISABELLE_HFL_LIM_CTRL_2_REG 0x55
+#define ISABELLE_HFR_VOL_CTRL_REG 0x56
+#define ISABELLE_HFR_SFTVOL_CTRL_REG 0x57
+#define ISABELLE_HFR_LIM_CTRL_1_REG 0x58
+#define ISABELLE_HFR_LIM_CTRL_2_REG 0x59
+#define ISABELLE_HF_MODE_REG 0x5A
+#define ISABELLE_HFLPGA_CFG_REG 0x5B
+#define ISABELLE_HFRPGA_CFG_REG 0x5C
+#define ISABELLE_HFDRV_CFG_REG 0x5D
+#define ISABELLE_PDMOUT_CFG1_REG 0x5E
+#define ISABELLE_PDMOUT_CFG2_REG 0x5F
+#define ISABELLE_PDMOUT_L_WM_REG 0x60
+#define ISABELLE_PDMOUT_R_WM_REG 0x61
+#define ISABELLE_HF_NG_CFG1_REG 0x62
+#define ISABELLE_HF_NG_CFG2_REG 0x63
+
+/* ISABELLE_PWR_EN_REG (0x02h) */
+#define ISABELLE_CHIP_EN BIT(0)
+
+/* ISABELLE DAI FORMATS */
+#define ISABELLE_AIF_FMT_MASK 0x70
+#define ISABELLE_I2S_MODE 0x0
+#define ISABELLE_LEFT_J_MODE 0x1
+#define ISABELLE_PDM_MODE 0x2
+
+#define ISABELLE_AIF_LENGTH_MASK 0x30
+#define ISABELLE_AIF_LENGTH_20 0x00
+#define ISABELLE_AIF_LENGTH_32 0x10
+
+#define ISABELLE_AIF_MS 0x80
+
+#define ISABELLE_FS_RATE_MASK 0xF
+#define ISABELLE_FS_RATE_8 0x0
+#define ISABELLE_FS_RATE_11 0x1
+#define ISABELLE_FS_RATE_12 0x2
+#define ISABELLE_FS_RATE_16 0x4
+#define ISABELLE_FS_RATE_22 0x5
+#define ISABELLE_FS_RATE_24 0x6
+#define ISABELLE_FS_RATE_32 0x8
+#define ISABELLE_FS_RATE_44 0x9
+#define ISABELLE_FS_RATE_48 0xA
+
+#define ISABELLE_MAX_REGISTER 0xFF
+
+#endif
diff --git a/kernel/sound/soc/codecs/jz4740.c b/kernel/sound/soc/codecs/jz4740.c
new file mode 100644
index 000000000..933f4476d
--- /dev/null
+++ b/kernel/sound/soc/codecs/jz4740.c
@@ -0,0 +1,375 @@
+/*
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/regmap.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 JZ4740_REG_CODEC_1 0x0
+#define JZ4740_REG_CODEC_2 0x4
+
+#define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
+#define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
+#define JZ4740_CODEC_1_SW1_ENABLE BIT(27)
+#define JZ4740_CODEC_1_ADC_ENABLE BIT(26)
+#define JZ4740_CODEC_1_SW2_ENABLE BIT(25)
+#define JZ4740_CODEC_1_DAC_ENABLE BIT(24)
+#define JZ4740_CODEC_1_VREF_DISABLE BIT(20)
+#define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19)
+#define JZ4740_CODEC_1_VREF_PULLDOWN BIT(18)
+#define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17)
+#define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16)
+#define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14)
+#define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13)
+#define JZ4740_CODEC_1_HEADPHONE_CHARGE BIT(12)
+#define JZ4740_CODEC_1_HEADPHONE_PULLDOWN (BIT(11) | BIT(10))
+#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M BIT(9)
+#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN BIT(8)
+#define JZ4740_CODEC_1_SUSPEND BIT(1)
+#define JZ4740_CODEC_1_RESET BIT(0)
+
+#define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29
+#define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28
+#define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27
+#define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26
+#define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25
+#define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24
+#define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14
+#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET 8
+
+#define JZ4740_CODEC_2_INPUT_VOLUME_MASK 0x1f0000
+#define JZ4740_CODEC_2_SAMPLE_RATE_MASK 0x000f00
+#define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK 0x000030
+#define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK 0x000003
+
+#define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET 16
+#define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET 8
+#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4
+#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0
+
+static const struct reg_default jz4740_codec_reg_defaults[] = {
+ { JZ4740_REG_CODEC_1, 0x021b2302 },
+ { JZ4740_REG_CODEC_2, 0x00170803 },
+};
+
+struct jz4740_codec {
+ struct regmap *regmap;
+};
+
+static const unsigned int jz4740_mic_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 2, TLV_DB_SCALE_ITEM(0, 600, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0),
+};
+
+static const DECLARE_TLV_DB_SCALE(jz4740_out_tlv, 0, 200, 0);
+static const DECLARE_TLV_DB_SCALE(jz4740_in_tlv, -3450, 150, 0);
+
+static const struct snd_kcontrol_new jz4740_codec_controls[] = {
+ SOC_SINGLE_TLV("Master Playback Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0,
+ jz4740_out_tlv),
+ SOC_SINGLE_TLV("Master Capture Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0,
+ jz4740_in_tlv),
+ SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
+ SOC_SINGLE_TLV("Mic Capture Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0,
+ jz4740_mic_tlv),
+};
+
+static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
+ SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
+ SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
+};
+
+static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
+ SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
+ SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
+ SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
+ SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),
+
+ SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET, 1,
+ jz4740_codec_output_controls,
+ ARRAY_SIZE(jz4740_codec_output_controls)),
+
+ SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
+ jz4740_codec_input_controls,
+ ARRAY_SIZE(jz4740_codec_input_controls)),
+ SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_INPUT("LIN"),
+ SND_SOC_DAPM_INPUT("RIN"),
+};
+
+static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
+ {"Line Input", NULL, "LIN"},
+ {"Line Input", NULL, "RIN"},
+
+ {"Input Mixer", "Line Capture Switch", "Line Input"},
+ {"Input Mixer", "Mic Capture Switch", "MIC"},
+
+ {"ADC", NULL, "Input Mixer"},
+
+ {"Output Mixer", "Bypass Switch", "Input Mixer"},
+ {"Output Mixer", "DAC Switch", "DAC"},
+
+ {"LOUT", NULL, "Output Mixer"},
+ {"ROUT", NULL, "Output Mixer"},
+};
+
+static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(dai->codec);
+ uint32_t val;
+
+ switch (params_rate(params)) {
+ case 8000:
+ val = 0;
+ break;
+ case 11025:
+ val = 1;
+ break;
+ case 12000:
+ val = 2;
+ break;
+ case 16000:
+ val = 3;
+ break;
+ case 22050:
+ val = 4;
+ break;
+ case 24000:
+ val = 5;
+ break;
+ case 32000:
+ val = 6;
+ break;
+ case 44100:
+ val = 7;
+ break;
+ case 48000:
+ val = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
+
+ regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops jz4740_codec_dai_ops = {
+ .hw_params = jz4740_codec_hw_params,
+};
+
+static struct snd_soc_dai_driver jz4740_codec_dai = {
+ .name = "jz4740-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+ },
+ .ops = &jz4740_codec_dai_ops,
+ .symmetric_rates = 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);
+ udelay(2);
+
+ regmap_update_bits(regmap, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
+
+ regcache_sync(regmap);
+}
+
+static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
+ struct regmap *regmap = jz4740_codec->regmap;
+ unsigned int mask;
+ unsigned int value;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ mask = JZ4740_CODEC_1_VREF_DISABLE |
+ JZ4740_CODEC_1_VREF_AMP_DISABLE |
+ JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
+ value = 0;
+
+ regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
+ 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)
+ 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);
+ 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);
+ 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)
+{
+ struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
+
+ regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
+
+ 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 regmap_config jz4740_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = JZ4740_REG_CODEC_2,
+
+ .reg_defaults = jz4740_codec_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+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),
+ GFP_KERNEL);
+ if (!jz4740_codec)
+ return -ENOMEM;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ jz4740_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &jz4740_codec_regmap_config);
+ if (IS_ERR(jz4740_codec->regmap))
+ return PTR_ERR(jz4740_codec->regmap);
+
+ platform_set_drvdata(pdev, jz4740_codec);
+
+ ret = snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_jz4740_codec, &jz4740_codec_dai, 1);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to register codec\n");
+
+ return ret;
+}
+
+static int jz4740_codec_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver jz4740_codec_driver = {
+ .probe = jz4740_codec_probe,
+ .remove = jz4740_codec_remove,
+ .driver = {
+ .name = "jz4740-codec",
+ },
+};
+
+module_platform_driver(jz4740_codec_driver);
+
+MODULE_DESCRIPTION("JZ4740 SoC internal codec driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:jz4740-codec");
diff --git a/kernel/sound/soc/codecs/l3.c b/kernel/sound/soc/codecs/l3.c
new file mode 100644
index 000000000..5353af588
--- /dev/null
+++ b/kernel/sound/soc/codecs/l3.c
@@ -0,0 +1,91 @@
+/*
+ * 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/kernel/sound/soc/codecs/lm4857.c b/kernel/sound/soc/codecs/lm4857.c
new file mode 100644
index 000000000..a924bb9d7
--- /dev/null
+++ b/kernel/sound/soc/codecs/lm4857.c
@@ -0,0 +1,211 @@
+/*
+ * LM4857 AMP driver
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * 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>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#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 },
+ { 0x2, 0x00 },
+ { 0x3, 0x00 },
+};
+
+/* The register offsets in the cache array */
+#define LM4857_MVOL 0
+#define LM4857_LVOL 1
+#define LM4857_RVOL 2
+#define LM4857_CTRL 3
+
+/* the shifts required to set these bits */
+#define LM4857_3D 5
+#define LM4857_WAKEUP 5
+#define LM4857_EPGAIN 4
+
+static 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 char *lm4857_mode[] = {
+ "Earpiece",
+ "Loudspeaker",
+ "Loudspeaker + Headphone",
+ "Headphone",
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(lm4857_mode_enum, lm4857_mode);
+
+static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+
+ SND_SOC_DAPM_OUTPUT("LS"),
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_OUTPUT("EP"),
+};
+
+static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0);
+
+static const struct snd_kcontrol_new lm4857_controls[] = {
+ SOC_SINGLE_TLV("Left Playback Volume", LM4857_LVOL, 0, 31, 0,
+ stereo_tlv),
+ SOC_SINGLE_TLV("Right Playback Volume", LM4857_RVOL, 0, 31, 0,
+ stereo_tlv),
+ SOC_SINGLE_TLV("Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
+ mono_tlv),
+ SOC_SINGLE("Spk 3D Playback Switch", LM4857_LVOL, LM4857_3D, 1, 0),
+ SOC_SINGLE("HP 3D Playback Switch", LM4857_RVOL, LM4857_3D, 1, 0),
+ SOC_SINGLE("Fast Wakeup Playback Switch", LM4857_CTRL,
+ LM4857_WAKEUP, 1, 0),
+ SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL,
+ LM4857_EPGAIN, 1, 0),
+
+ 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"},
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_lm4857 = {
+ .set_bias_level = lm4857_set_bias_level,
+
+ .controls = lm4857_controls,
+ .num_controls = ARRAY_SIZE(lm4857_controls),
+ .dapm_widgets = lm4857_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(lm4857_dapm_widgets),
+ .dapm_routes = lm4857_routes,
+ .num_dapm_routes = ARRAY_SIZE(lm4857_routes),
+};
+
+static const struct regmap_config lm4857_regmap_config = {
+ .val_bits = 6,
+ .reg_bits = 2,
+
+ .max_register = LM4857_CTRL,
+
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = lm4857_default_regs,
+ .num_reg_defaults = ARRAY_SIZE(lm4857_default_regs),
+};
+
+static int lm4857_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ 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);
+
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0);
+}
+
+static int lm4857_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+ return 0;
+}
+
+static const struct i2c_device_id lm4857_i2c_id[] = {
+ { "lm4857", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id);
+
+static struct i2c_driver lm4857_i2c_driver = {
+ .driver = {
+ .name = "lm4857",
+ .owner = THIS_MODULE,
+ },
+ .probe = lm4857_i2c_probe,
+ .remove = lm4857_i2c_remove,
+ .id_table = lm4857_i2c_id,
+};
+
+module_i2c_driver(lm4857_i2c_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("LM4857 amplifier driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/lm49453.c b/kernel/sound/soc/codecs/lm49453.c
new file mode 100644
index 000000000..c4dfde9bd
--- /dev/null
+++ b/kernel/sound/soc/codecs/lm49453.c
@@ -0,0 +1,1476 @@
+/*
+ * 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
+ */
+
+#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/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <asm/div64.h>
+#include "lm49453.h"
+
+static struct reg_default lm49453_reg_defs[] = {
+ { 0, 0x00 },
+ { 1, 0x00 },
+ { 2, 0x00 },
+ { 3, 0x00 },
+ { 4, 0x00 },
+ { 5, 0x00 },
+ { 6, 0x00 },
+ { 7, 0x00 },
+ { 8, 0x00 },
+ { 9, 0x00 },
+ { 10, 0x00 },
+ { 11, 0x00 },
+ { 12, 0x00 },
+ { 13, 0x00 },
+ { 14, 0x00 },
+ { 15, 0x00 },
+ { 16, 0x00 },
+ { 17, 0x00 },
+ { 18, 0x00 },
+ { 19, 0x00 },
+ { 20, 0x00 },
+ { 21, 0x00 },
+ { 22, 0x00 },
+ { 23, 0x00 },
+ { 32, 0x00 },
+ { 33, 0x00 },
+ { 35, 0x00 },
+ { 36, 0x00 },
+ { 37, 0x00 },
+ { 46, 0x00 },
+ { 48, 0x00 },
+ { 49, 0x00 },
+ { 51, 0x00 },
+ { 56, 0x00 },
+ { 58, 0x00 },
+ { 59, 0x00 },
+ { 60, 0x00 },
+ { 61, 0x00 },
+ { 62, 0x00 },
+ { 63, 0x00 },
+ { 64, 0x00 },
+ { 65, 0x00 },
+ { 66, 0x00 },
+ { 67, 0x00 },
+ { 68, 0x00 },
+ { 69, 0x00 },
+ { 70, 0x00 },
+ { 71, 0x00 },
+ { 72, 0x00 },
+ { 73, 0x00 },
+ { 74, 0x00 },
+ { 75, 0x00 },
+ { 76, 0x00 },
+ { 77, 0x00 },
+ { 78, 0x00 },
+ { 79, 0x00 },
+ { 80, 0x00 },
+ { 81, 0x00 },
+ { 82, 0x00 },
+ { 83, 0x00 },
+ { 85, 0x00 },
+ { 85, 0x00 },
+ { 86, 0x00 },
+ { 87, 0x00 },
+ { 88, 0x00 },
+ { 89, 0x00 },
+ { 90, 0x00 },
+ { 91, 0x00 },
+ { 92, 0x00 },
+ { 93, 0x00 },
+ { 94, 0x00 },
+ { 95, 0x00 },
+ { 96, 0x01 },
+ { 97, 0x00 },
+ { 98, 0x00 },
+ { 99, 0x00 },
+ { 100, 0x00 },
+ { 101, 0x00 },
+ { 102, 0x00 },
+ { 103, 0x01 },
+ { 104, 0x01 },
+ { 105, 0x00 },
+ { 106, 0x01 },
+ { 107, 0x00 },
+ { 108, 0x00 },
+ { 109, 0x00 },
+ { 110, 0x00 },
+ { 111, 0x02 },
+ { 112, 0x02 },
+ { 113, 0x00 },
+ { 121, 0x80 },
+ { 122, 0xBB },
+ { 123, 0x80 },
+ { 124, 0xBB },
+ { 128, 0x00 },
+ { 130, 0x00 },
+ { 131, 0x00 },
+ { 132, 0x00 },
+ { 133, 0x0A },
+ { 134, 0x0A },
+ { 135, 0x0A },
+ { 136, 0x0F },
+ { 137, 0x00 },
+ { 138, 0x73 },
+ { 139, 0x33 },
+ { 140, 0x73 },
+ { 141, 0x33 },
+ { 142, 0x73 },
+ { 143, 0x33 },
+ { 144, 0x73 },
+ { 145, 0x33 },
+ { 146, 0x73 },
+ { 147, 0x33 },
+ { 148, 0x73 },
+ { 149, 0x33 },
+ { 150, 0x73 },
+ { 151, 0x33 },
+ { 152, 0x00 },
+ { 153, 0x00 },
+ { 154, 0x00 },
+ { 155, 0x00 },
+ { 176, 0x00 },
+ { 177, 0x00 },
+ { 178, 0x00 },
+ { 179, 0x00 },
+ { 180, 0x00 },
+ { 181, 0x00 },
+ { 182, 0x00 },
+ { 183, 0x00 },
+ { 184, 0x00 },
+ { 185, 0x00 },
+ { 186, 0x00 },
+ { 187, 0x00 },
+ { 188, 0x00 },
+ { 189, 0x00 },
+ { 208, 0x06 },
+ { 209, 0x00 },
+ { 210, 0x08 },
+ { 211, 0x54 },
+ { 212, 0x14 },
+ { 213, 0x0d },
+ { 214, 0x0d },
+ { 215, 0x14 },
+ { 216, 0x60 },
+ { 221, 0x00 },
+ { 222, 0x00 },
+ { 223, 0x00 },
+ { 224, 0x00 },
+ { 248, 0x00 },
+ { 249, 0x00 },
+ { 250, 0x00 },
+ { 255, 0x00 },
+};
+
+/* codec private data */
+struct lm49453_priv {
+ struct regmap *regmap;
+ int fs_rate;
+};
+
+/* capture path controls */
+
+static const char *lm49453_mic2mode_text[] = {"Single Ended", "Differential"};
+
+static SOC_ENUM_SINGLE_DECL(lm49453_mic2mode_enum, LM49453_P0_MICR_REG, 5,
+ lm49453_mic2mode_text);
+
+static const char *lm49453_dmic_cfg_text[] = {"DMICDAT1", "DMICDAT2"};
+
+static SOC_ENUM_SINGLE_DECL(lm49453_dmic12_cfg_enum,
+ LM49453_P0_DIGITAL_MIC1_CONFIG_REG, 7,
+ lm49453_dmic_cfg_text);
+
+static SOC_ENUM_SINGLE_DECL(lm49453_dmic34_cfg_enum,
+ LM49453_P0_DIGITAL_MIC2_CONFIG_REG, 7,
+ lm49453_dmic_cfg_text);
+
+/* MUX Controls */
+static const char *lm49453_adcl_mux_text[] = { "MIC1", "Aux_L" };
+
+static const char *lm49453_adcr_mux_text[] = { "MIC2", "Aux_R" };
+
+static SOC_ENUM_SINGLE_DECL(lm49453_adcl_enum,
+ LM49453_P0_ANALOG_MIXER_ADC_REG, 0,
+ lm49453_adcl_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(lm49453_adcr_enum,
+ LM49453_P0_ANALOG_MIXER_ADC_REG, 1,
+ lm49453_adcr_mux_text);
+
+static const struct snd_kcontrol_new lm49453_adcl_mux_control =
+ SOC_DAPM_ENUM("ADC Left Mux", lm49453_adcl_enum);
+
+static const struct snd_kcontrol_new lm49453_adcr_mux_control =
+ SOC_DAPM_ENUM("ADC Right Mux", lm49453_adcr_enum);
+
+static const struct snd_kcontrol_new lm49453_headset_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHPL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHPL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHPL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHPL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHPL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHPL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHPL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHPL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHPL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHPL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHPL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHPL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHPL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHPL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHPL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHPL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 0, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_headset_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHPR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHPR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHPR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHPR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHPR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHPR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHPR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHPR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHPR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHPR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHPR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHPR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHPR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHPR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHPR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHPR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 1, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_speaker_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLSL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLSL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLSL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLSL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLSL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLSL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLSL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLSL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLSL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLSL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLSL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLSL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLSL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLSL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLSL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLSL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 2, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_speaker_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLSR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLSR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLSR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLSR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLSR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLSR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLSR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLSR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLSR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLSR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLSR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLSR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLSR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLSR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLSR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLSR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 3, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_haptic_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHAL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHAL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHAL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHAL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHAL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHAL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHAL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHAL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHAL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHAL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHAL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHAL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHAL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHAL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHAL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHAL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 4, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_haptic_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHAR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHAR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHAR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHAR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHAR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHAR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHAR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHAR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHAR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHAR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHAR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHAR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHAR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHAR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHAR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHAR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 5, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_lineout_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLOL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLOL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLOL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLOL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLOL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLOL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLOL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLOL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLOL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLOL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLOL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLOL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLOL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLOL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLOL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLOL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 6, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_lineout_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLOR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLOR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLOR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLOR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLOR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLOR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLOR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLOR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLOR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLOR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLOR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLOR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLOR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLOR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLOR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLOR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 7, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx1_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_PORT1_TX1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_PORT1_TX1_REG, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx2_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_PORT1_TX2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT1_TX2_REG, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx3_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX3_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX3_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX3_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX3_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX3_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX3_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_PORT1_TX3_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx4_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX4_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX4_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX4_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX4_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX4_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX4_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_PORT1_TX4_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx5_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX5_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX5_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX5_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX5_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX5_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX5_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_PORT1_TX5_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx6_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX6_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX6_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX6_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX6_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX6_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX6_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_PORT1_TX6_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx7_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX7_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX7_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX7_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX7_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX7_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX7_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_PORT1_TX7_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx8_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX8_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX8_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX8_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX8_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX8_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX8_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_PORT1_TX8_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port2_tx1_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT2_TX1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT2_TX1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT2_TX1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT2_TX1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT2_TX1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT2_TX1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_PORT2_TX1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_PORT2_TX1_REG, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port2_tx2_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT2_TX2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT2_TX2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT2_TX2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT2_TX2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT2_TX2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT2_TX2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_PORT2_TX2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT2_TX2_REG, 7, 1, 0),
+};
+
+/* TLV Declarations */
+static const DECLARE_TLV_DB_SCALE(adc_dac_tlv, -7650, 150, 1);
+static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 200, 1);
+static const DECLARE_TLV_DB_SCALE(port_tlv, -1800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(stn_tlv, -7200, 150, 0);
+
+static const struct snd_kcontrol_new lm49453_sidetone_mixer_controls[] = {
+/* Sidetone supports mono only */
+SOC_DAPM_SINGLE_TLV("Sidetone ADCL Volume", LM49453_P0_STN_VOL_ADCL_REG,
+ 0, 0x3F, 0, stn_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone ADCR Volume", LM49453_P0_STN_VOL_ADCR_REG,
+ 0, 0x3F, 0, stn_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC1L Volume", LM49453_P0_STN_VOL_DMIC1L_REG,
+ 0, 0x3F, 0, stn_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC1R Volume", LM49453_P0_STN_VOL_DMIC1R_REG,
+ 0, 0x3F, 0, stn_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC2L Volume", LM49453_P0_STN_VOL_DMIC2L_REG,
+ 0, 0x3F, 0, stn_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC2R Volume", LM49453_P0_STN_VOL_DMIC2R_REG,
+ 0, 0x3F, 0, stn_tlv),
+};
+
+static const struct snd_kcontrol_new lm49453_snd_controls[] = {
+ /* mic1 and mic2 supports mono only */
+ SOC_SINGLE_TLV("Mic1 Volume", LM49453_P0_MICL_REG, 0, 15, 0, mic_tlv),
+ SOC_SINGLE_TLV("Mic2 Volume", LM49453_P0_MICR_REG, 0, 15, 0, mic_tlv),
+
+ SOC_SINGLE_TLV("ADCL Volume", LM49453_P0_ADC_LEVELL_REG, 0, 63,
+ 0, adc_dac_tlv),
+ SOC_SINGLE_TLV("ADCR Volume", LM49453_P0_ADC_LEVELR_REG, 0, 63,
+ 0, adc_dac_tlv),
+
+ SOC_DOUBLE_R_TLV("DMIC1 Volume", LM49453_P0_DMIC1_LEVELL_REG,
+ LM49453_P0_DMIC1_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
+ SOC_DOUBLE_R_TLV("DMIC2 Volume", LM49453_P0_DMIC2_LEVELL_REG,
+ LM49453_P0_DMIC2_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
+
+ SOC_DAPM_ENUM("Mic2Mode", lm49453_mic2mode_enum),
+ SOC_DAPM_ENUM("DMIC12 SRC", lm49453_dmic12_cfg_enum),
+ SOC_DAPM_ENUM("DMIC34 SRC", lm49453_dmic34_cfg_enum),
+
+ /* Capture path filter enable */
+ SOC_SINGLE("DMIC1 HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG,
+ 0, 1, 0),
+ SOC_SINGLE("DMIC2 HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG,
+ 1, 1, 0),
+ SOC_SINGLE("ADC HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG,
+ 2, 1, 0),
+
+ SOC_DOUBLE_R_TLV("DAC HP Volume", LM49453_P0_DAC_HP_LEVELL_REG,
+ LM49453_P0_DAC_HP_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC LO Volume", LM49453_P0_DAC_LO_LEVELL_REG,
+ LM49453_P0_DAC_LO_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC LS Volume", LM49453_P0_DAC_LS_LEVELL_REG,
+ LM49453_P0_DAC_LS_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC HA Volume", LM49453_P0_DAC_HA_LEVELL_REG,
+ LM49453_P0_DAC_HA_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
+
+ SOC_SINGLE_TLV("EP Volume", LM49453_P0_DAC_LS_LEVELL_REG,
+ 0, 63, 0, adc_dac_tlv),
+
+ SOC_SINGLE_TLV("PORT1_1_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+ 0, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_2_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+ 2, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_3_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+ 4, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_4_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+ 6, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_5_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+ 0, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_6_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+ 2, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_7_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+ 4, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT1_8_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+ 6, 3, 0, port_tlv),
+
+ SOC_SINGLE_TLV("PORT2_1_RX_LVL Volume", LM49453_P0_PORT2_RX_LVL_REG,
+ 0, 3, 0, port_tlv),
+ SOC_SINGLE_TLV("PORT2_2_RX_LVL Volume", LM49453_P0_PORT2_RX_LVL_REG,
+ 2, 3, 0, port_tlv),
+
+ SOC_SINGLE("Port1 Playback Switch", LM49453_P0_AUDIO_PORT1_BASIC_REG,
+ 1, 1, 0),
+ SOC_SINGLE("Port2 Playback Switch", LM49453_P0_AUDIO_PORT2_BASIC_REG,
+ 1, 1, 0),
+ SOC_SINGLE("Port1 Capture Switch", LM49453_P0_AUDIO_PORT1_BASIC_REG,
+ 2, 1, 0),
+ SOC_SINGLE("Port2 Capture Switch", LM49453_P0_AUDIO_PORT2_BASIC_REG,
+ 2, 1, 0)
+
+};
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget lm49453_dapm_widgets[] = {
+
+ /* All end points HP,EP, LS, Lineout and Haptic */
+ SND_SOC_DAPM_OUTPUT("HPOUTL"),
+ SND_SOC_DAPM_OUTPUT("HPOUTR"),
+ SND_SOC_DAPM_OUTPUT("EPOUT"),
+ SND_SOC_DAPM_OUTPUT("LSOUTL"),
+ SND_SOC_DAPM_OUTPUT("LSOUTR"),
+ SND_SOC_DAPM_OUTPUT("LOOUTR"),
+ SND_SOC_DAPM_OUTPUT("LOOUTL"),
+ SND_SOC_DAPM_OUTPUT("HAOUTL"),
+ SND_SOC_DAPM_OUTPUT("HAOUTR"),
+
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("DMIC1DAT"),
+ SND_SOC_DAPM_INPUT("DMIC2DAT"),
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+
+ SND_SOC_DAPM_PGA("PORT1_1_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_2_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_3_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_4_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_5_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_6_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_7_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT1_8_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT2_1_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PORT2_2_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("AMIC1Bias", LM49453_P0_MICL_REG, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AMIC2Bias", LM49453_P0_MICR_REG, 6, 0, NULL, 0),
+
+ /* playback path driver enables */
+ SND_SOC_DAPM_OUT_DRV("Headset Switch",
+ LM49453_P0_PMC_SETUP_REG, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Earpiece Switch",
+ LM49453_P0_EP_REG, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Speaker Left Switch",
+ LM49453_P0_DIS_PKVL_FB_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Speaker Right Switch",
+ LM49453_P0_DIS_PKVL_FB_REG, 1, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Haptic Left Switch",
+ LM49453_P0_DIS_PKVL_FB_REG, 2, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Haptic Right Switch",
+ LM49453_P0_DIS_PKVL_FB_REG, 3, 1, NULL, 0),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("HPL DAC", "Headset", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("HPR DAC", "Headset", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("LSL DAC", "Speaker", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("LSR DAC", "Speaker", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("HAL DAC", "Haptic", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("HAR DAC", "Haptic", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("LOL DAC", "Lineout", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("LOR DAC", "Lineout", SND_SOC_NOPM, 0, 0),
+
+
+ SND_SOC_DAPM_PGA("AUXL Input",
+ LM49453_P0_ANALOG_MIXER_ADC_REG, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUXR Input",
+ LM49453_P0_ANALOG_MIXER_ADC_REG, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Sidetone", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("DMIC1 Left", "Capture", SND_SOC_NOPM, 1, 0),
+ SND_SOC_DAPM_ADC("DMIC1 Right", "Capture", SND_SOC_NOPM, 1, 0),
+ SND_SOC_DAPM_ADC("DMIC2 Left", "Capture", SND_SOC_NOPM, 1, 0),
+ SND_SOC_DAPM_ADC("DMIC2 Right", "Capture", SND_SOC_NOPM, 1, 0),
+
+ SND_SOC_DAPM_ADC("ADC Left", "Capture", SND_SOC_NOPM, 1, 0),
+ SND_SOC_DAPM_ADC("ADC Right", "Capture", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("ADCL Mux", SND_SOC_NOPM, 0, 0,
+ &lm49453_adcl_mux_control),
+ SND_SOC_DAPM_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0,
+ &lm49453_adcr_mux_control),
+
+ SND_SOC_DAPM_MUX("Mic1 Input",
+ SND_SOC_NOPM, 0, 0, &lm49453_adcl_mux_control),
+
+ SND_SOC_DAPM_MUX("Mic2 Input",
+ SND_SOC_NOPM, 0, 0, &lm49453_adcr_mux_control),
+
+ /* AIF */
+ SND_SOC_DAPM_AIF_IN("PORT1_SDI", NULL, 0,
+ LM49453_P0_PULL_CONFIG1_REG, 2, 0),
+ SND_SOC_DAPM_AIF_IN("PORT2_SDI", NULL, 0,
+ LM49453_P0_PULL_CONFIG1_REG, 6, 0),
+
+ SND_SOC_DAPM_AIF_OUT("PORT1_SDO", NULL, 0,
+ LM49453_P0_PULL_CONFIG1_REG, 3, 0),
+ SND_SOC_DAPM_AIF_OUT("PORT2_SDO", NULL, 0,
+ LM49453_P0_PULL_CONFIG1_REG, 7, 0),
+
+ /* Port1 TX controls */
+ SND_SOC_DAPM_OUT_DRV("P1_1_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_2_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_3_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_4_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_5_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_6_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_7_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P1_8_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Port2 TX controls */
+ SND_SOC_DAPM_OUT_DRV("P2_1_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("P2_2_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Sidetone Mixer */
+ SND_SOC_DAPM_MIXER("Sidetone Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_sidetone_mixer_controls,
+ ARRAY_SIZE(lm49453_sidetone_mixer_controls)),
+
+ /* DAC MIXERS */
+ SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_headset_left_mixer,
+ ARRAY_SIZE(lm49453_headset_left_mixer)),
+ SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_headset_right_mixer,
+ ARRAY_SIZE(lm49453_headset_right_mixer)),
+ SND_SOC_DAPM_MIXER("LOL Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_lineout_left_mixer,
+ ARRAY_SIZE(lm49453_lineout_left_mixer)),
+ SND_SOC_DAPM_MIXER("LOR Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_lineout_right_mixer,
+ ARRAY_SIZE(lm49453_lineout_right_mixer)),
+ SND_SOC_DAPM_MIXER("LSL Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_speaker_left_mixer,
+ ARRAY_SIZE(lm49453_speaker_left_mixer)),
+ SND_SOC_DAPM_MIXER("LSR Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_speaker_right_mixer,
+ ARRAY_SIZE(lm49453_speaker_right_mixer)),
+ SND_SOC_DAPM_MIXER("HAL Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_haptic_left_mixer,
+ ARRAY_SIZE(lm49453_haptic_left_mixer)),
+ SND_SOC_DAPM_MIXER("HAR Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_haptic_right_mixer,
+ ARRAY_SIZE(lm49453_haptic_right_mixer)),
+
+ /* Capture Mixer */
+ SND_SOC_DAPM_MIXER("Port1_1 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx1_mixer,
+ ARRAY_SIZE(lm49453_port1_tx1_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_2 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx2_mixer,
+ ARRAY_SIZE(lm49453_port1_tx2_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_3 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx3_mixer,
+ ARRAY_SIZE(lm49453_port1_tx3_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_4 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx4_mixer,
+ ARRAY_SIZE(lm49453_port1_tx4_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_5 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx5_mixer,
+ ARRAY_SIZE(lm49453_port1_tx5_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_6 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx6_mixer,
+ ARRAY_SIZE(lm49453_port1_tx6_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_7 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx7_mixer,
+ ARRAY_SIZE(lm49453_port1_tx7_mixer)),
+ SND_SOC_DAPM_MIXER("Port1_8 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port1_tx8_mixer,
+ ARRAY_SIZE(lm49453_port1_tx8_mixer)),
+
+ SND_SOC_DAPM_MIXER("Port2_1 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port2_tx1_mixer,
+ ARRAY_SIZE(lm49453_port2_tx1_mixer)),
+ SND_SOC_DAPM_MIXER("Port2_2 Mixer", SND_SOC_NOPM, 0, 0,
+ lm49453_port2_tx2_mixer,
+ ARRAY_SIZE(lm49453_port2_tx2_mixer)),
+};
+
+static const struct snd_soc_dapm_route lm49453_audio_map[] = {
+ /* Port SDI mapping */
+ { "PORT1_1_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_2_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_3_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_4_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_5_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_6_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_7_RX", "Port1 Playback Switch", "PORT1_SDI" },
+ { "PORT1_8_RX", "Port1 Playback Switch", "PORT1_SDI" },
+
+ { "PORT2_1_RX", "Port2 Playback Switch", "PORT2_SDI" },
+ { "PORT2_2_RX", "Port2 Playback Switch", "PORT2_SDI" },
+
+ /* HP mapping */
+ { "HPL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "HPL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "HPL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "HPL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "HPL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "HPL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "HPL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "HPL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ { "HPL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "HPL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "HPL Mixer", "ADCL Switch", "ADC Left" },
+ { "HPL Mixer", "ADCR Switch", "ADC Right" },
+ { "HPL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "HPL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "HPL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "HPL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "HPL Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "HPL DAC", NULL, "HPL Mixer" },
+
+ { "HPR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "HPR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "HPR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "HPR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "HPR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "HPR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "HPR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "HPR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "HPR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "HPR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "HPR Mixer", "ADCL Switch", "ADC Left" },
+ { "HPR Mixer", "ADCR Switch", "ADC Right" },
+ { "HPR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "HPR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "HPR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "HPR Mixer", "DMIC2L Switch", "DMIC2 Right" },
+ { "HPR Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "HPR DAC", NULL, "HPR Mixer" },
+
+ { "HPOUTL", "Headset Switch", "HPL DAC"},
+ { "HPOUTR", "Headset Switch", "HPR DAC"},
+
+ /* EP map */
+ { "EPOUT", "Earpiece Switch", "HPL DAC" },
+
+ /* Speaker map */
+ { "LSL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "LSL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "LSL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "LSL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "LSL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "LSL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "LSL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "LSL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "LSL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "LSL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "LSL Mixer", "ADCL Switch", "ADC Left" },
+ { "LSL Mixer", "ADCR Switch", "ADC Right" },
+ { "LSL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "LSL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "LSL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "LSL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "LSL Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "LSL DAC", NULL, "LSL Mixer" },
+
+ { "LSR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "LSR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "LSR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "LSR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "LSR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "LSR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "LSR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "LSR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "LSR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "LSR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "LSR Mixer", "ADCL Switch", "ADC Left" },
+ { "LSR Mixer", "ADCR Switch", "ADC Right" },
+ { "LSR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "LSR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "LSR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "LSR Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "LSR Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "LSR DAC", NULL, "LSR Mixer" },
+
+ { "LSOUTL", "Speaker Left Switch", "LSL DAC"},
+ { "LSOUTR", "Speaker Left Switch", "LSR DAC"},
+
+ /* Haptic map */
+ { "HAL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "HAL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "HAL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "HAL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "HAL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "HAL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "HAL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "HAL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "HAL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "HAL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "HAL Mixer", "ADCL Switch", "ADC Left" },
+ { "HAL Mixer", "ADCR Switch", "ADC Right" },
+ { "HAL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "HAL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "HAL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "HAL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "HAL Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "HAL DAC", NULL, "HAL Mixer" },
+
+ { "HAR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "HAR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "HAR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "HAR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "HAR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "HAR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "HAR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "HAR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "HAR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "HAR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "HAR Mixer", "ADCL Switch", "ADC Left" },
+ { "HAR Mixer", "ADCR Switch", "ADC Right" },
+ { "HAR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "HAR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "HAR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "HAR Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "HAR Mixer", "Sideton Switch", "Sidetone" },
+
+ { "HAR DAC", NULL, "HAR Mixer" },
+
+ { "HAOUTL", "Haptic Left Switch", "HAL DAC" },
+ { "HAOUTR", "Haptic Right Switch", "HAR DAC" },
+
+ /* Lineout map */
+ { "LOL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "LOL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "LOL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "LOL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "LOL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "LOL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "LOL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "LOL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "LOL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "LOL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "LOL Mixer", "ADCL Switch", "ADC Left" },
+ { "LOL Mixer", "ADCR Switch", "ADC Right" },
+ { "LOL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "LOL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "LOL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "LOL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "LOL Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "LOL DAC", NULL, "LOL Mixer" },
+
+ { "LOR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+ { "LOR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+ { "LOR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+ { "LOR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+ { "LOR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+ { "LOR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+ { "LOR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+ { "LOR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+ /* Port 2 */
+ { "LOR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+ { "LOR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+ { "LOR Mixer", "ADCL Switch", "ADC Left" },
+ { "LOR Mixer", "ADCR Switch", "ADC Right" },
+ { "LOR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "LOR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "LOR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "LOR Mixer", "DMIC2R Switch", "DMIC2 Right" },
+ { "LOR Mixer", "Sidetone Switch", "Sidetone" },
+
+ { "LOR DAC", NULL, "LOR Mixer" },
+
+ { "LOOUTL", NULL, "LOL DAC" },
+ { "LOOUTR", NULL, "LOR DAC" },
+
+ /* TX map */
+ /* Port1 mappings */
+ { "Port1_1 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_1 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_1 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_1 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_1 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_1 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_2 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_2 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_2 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_2 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_2 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_2 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_3 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_3 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_3 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_3 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_3 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_3 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_4 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_4 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_4 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_4 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_4 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_4 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_5 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_5 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_5 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_5 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_5 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_5 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_6 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_6 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_6 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_6 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_6 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_6 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_7 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_7 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_7 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_7 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_7 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_7 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port1_8 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port1_8 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port1_8 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port1_8 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port1_8 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port1_8 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port2_1 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port2_1 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port2_1 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port2_1 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port2_1 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port2_1 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "Port2_2 Mixer", "ADCL Switch", "ADC Left" },
+ { "Port2_2 Mixer", "ADCR Switch", "ADC Right" },
+ { "Port2_2 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+ { "Port2_2 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+ { "Port2_2 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+ { "Port2_2 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+ { "P1_1_TX", NULL, "Port1_1 Mixer" },
+ { "P1_2_TX", NULL, "Port1_2 Mixer" },
+ { "P1_3_TX", NULL, "Port1_3 Mixer" },
+ { "P1_4_TX", NULL, "Port1_4 Mixer" },
+ { "P1_5_TX", NULL, "Port1_5 Mixer" },
+ { "P1_6_TX", NULL, "Port1_6 Mixer" },
+ { "P1_7_TX", NULL, "Port1_7 Mixer" },
+ { "P1_8_TX", NULL, "Port1_8 Mixer" },
+
+ { "P2_1_TX", NULL, "Port2_1 Mixer" },
+ { "P2_2_TX", NULL, "Port2_2 Mixer" },
+
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_1_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_2_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_3_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_4_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_5_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_6_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_7_TX"},
+ { "PORT1_SDO", "Port1 Capture Switch", "P1_8_TX"},
+
+ { "PORT2_SDO", "Port2 Capture Switch", "P2_1_TX"},
+ { "PORT2_SDO", "Port2 Capture Switch", "P2_2_TX"},
+
+ { "Mic1 Input", NULL, "AMIC1" },
+ { "Mic2 Input", NULL, "AMIC2" },
+
+ { "AUXL Input", NULL, "AUXL" },
+ { "AUXR Input", NULL, "AUXR" },
+
+ /* AUX connections */
+ { "ADCL Mux", "Aux_L", "AUXL Input" },
+ { "ADCL Mux", "MIC1", "Mic1 Input" },
+
+ { "ADCR Mux", "Aux_R", "AUXR Input" },
+ { "ADCR Mux", "MIC2", "Mic2 Input" },
+
+ /* ADC connection */
+ { "ADC Left", NULL, "ADCL Mux"},
+ { "ADC Right", NULL, "ADCR Mux"},
+
+ { "DMIC1 Left", NULL, "DMIC1DAT"},
+ { "DMIC1 Right", NULL, "DMIC1DAT"},
+ { "DMIC2 Left", NULL, "DMIC2DAT"},
+ { "DMIC2 Right", NULL, "DMIC2DAT"},
+
+ /* Sidetone map */
+ { "Sidetone Mixer", NULL, "ADC Left" },
+ { "Sidetone Mixer", NULL, "ADC Right" },
+ { "Sidetone Mixer", NULL, "DMIC1 Left" },
+ { "Sidetone Mixer", NULL, "DMIC1 Right" },
+ { "Sidetone Mixer", NULL, "DMIC2 Left" },
+ { "Sidetone Mixer", NULL, "DMIC2 Right" },
+
+ { "Sidetone", "Sidetone Switch", "Sidetone Mixer" },
+};
+
+static int lm49453_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec);
+ u16 clk_div = 0;
+
+ lm49453->fs_rate = params_rate(params);
+
+ /* Setting DAC clock dividers based on substream sample rate. */
+ switch (lm49453->fs_rate) {
+ case 8000:
+ case 16000:
+ case 32000:
+ case 24000:
+ case 48000:
+ clk_div = 256;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ clk_div = 216;
+ break;
+ case 96000:
+ clk_div = 127;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, LM49453_P0_ADC_CLK_DIV_REG, clk_div);
+ snd_soc_write(codec, 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;
+
+ 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:
+ aif_val = 0;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ aif_val = LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS |
+ LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ mode = 1;
+ clk_phase = (1 << 5);
+ clk_shift = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ mode = 1;
+ clk_phase = (1 << 5);
+ clk_shift = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, 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);
+
+ return 0;
+}
+
+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;
+ u16 pll_clk = 0;
+
+ switch (freq) {
+ case 12288000:
+ case 26000000:
+ case 19200000:
+ /* pll clk slection */
+ pll_clk = 0;
+ 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);
+
+ return 0;
+}
+
+static int lm49453_hp_mute(struct snd_soc_dai *dai, int mute)
+{
+ snd_soc_update_bits(dai->codec, 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)
+{
+ snd_soc_update_bits(dai->codec, 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)
+{
+ snd_soc_update_bits(dai->codec, 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)
+{
+ snd_soc_update_bits(dai->codec, 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)
+{
+ snd_soc_update_bits(dai->codec, 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,
+ enum snd_soc_bias_level level)
+{
+ struct lm49453_priv *lm49453 = 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)
+ regcache_sync(lm49453->regmap);
+
+ snd_soc_update_bits(codec, 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,
+ LM49453_PMC_SETUP_CHIP_EN, 0);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+/* Formates supported by LM49453 driver. */
+#define LM49453_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static 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,
+};
+
+static 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,
+};
+
+static 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,
+};
+
+static 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,
+};
+
+static 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,
+};
+
+/* LM49453 dai structure. */
+static struct snd_soc_dai_driver lm49453_dai[] = {
+ {
+ .name = "LM49453 Headset",
+ .playback = {
+ .stream_name = "Headset",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 5,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_headset_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "LM49453 Speaker",
+ .playback = {
+ .stream_name = "Speaker",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_speaker_dai_ops,
+ },
+ {
+ .name = "LM49453 Haptic",
+ .playback = {
+ .stream_name = "Haptic",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_haptic_dai_ops,
+ },
+ {
+ .name = "LM49453 Earpiece",
+ .playback = {
+ .stream_name = "Earpiece",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_ep_dai_ops,
+ },
+ {
+ .name = "LM49453 line out",
+ .playback = {
+ .stream_name = "Lineout",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = LM49453_FORMATS,
+ },
+ .ops = &lm49453_lineout_dai_ops,
+ },
+};
+
+static 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 regmap_config lm49453_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = LM49453_MAX_REGISTER,
+ .reg_defaults = lm49453_reg_defs,
+ .num_reg_defaults = ARRAY_SIZE(lm49453_reg_defs),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int lm49453_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct lm49453_priv *lm49453;
+ int ret = 0;
+
+ lm49453 = devm_kzalloc(&i2c->dev, sizeof(struct lm49453_priv),
+ GFP_KERNEL);
+
+ if (lm49453 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, lm49453);
+
+ lm49453->regmap = devm_regmap_init_i2c(i2c, &lm49453_regmap_config);
+ if (IS_ERR(lm49453->regmap)) {
+ ret = PTR_ERR(lm49453->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_lm49453,
+ lm49453_dai, ARRAY_SIZE(lm49453_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %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 },
+ { }
+};
+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,
+};
+
+module_i2c_driver(lm49453_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC LM49453 driver");
+MODULE_AUTHOR("M R Swami Reddy <MR.Swami.Reddy@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/lm49453.h b/kernel/sound/soc/codecs/lm49453.h
new file mode 100644
index 000000000..a63cfa5c0
--- /dev/null
+++ b/kernel/sound/soc/codecs/lm49453.h
@@ -0,0 +1,380 @@
+/*
+ * 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
+#define _LM49453_H
+
+#include <linux/bitops.h>
+
+/* LM49453_P0 register space for page0 */
+#define LM49453_P0_PMC_SETUP_REG 0x00
+#define LM49453_P0_PLL_CLK_SEL1_REG 0x01
+#define LM49453_P0_PLL_CLK_SEL2_REG 0x02
+#define LM49453_P0_PMC_CLK_DIV_REG 0x03
+#define LM49453_P0_HSDET_CLK_DIV_REG 0x04
+#define LM49453_P0_DMIC_CLK_DIV_REG 0x05
+#define LM49453_P0_ADC_CLK_DIV_REG 0x06
+#define LM49453_P0_DAC_OT_CLK_DIV_REG 0x07
+#define LM49453_P0_PLL_HF_M_REG 0x08
+#define LM49453_P0_PLL_LF_M_REG 0x09
+#define LM49453_P0_PLL_NL_REG 0x0A
+#define LM49453_P0_PLL_N_MODL_REG 0x0B
+#define LM49453_P0_PLL_N_MODH_REG 0x0C
+#define LM49453_P0_PLL_P1_REG 0x0D
+#define LM49453_P0_PLL_P2_REG 0x0E
+#define LM49453_P0_FLL_REF_FREQL_REG 0x0F
+#define LM49453_P0_FLL_REF_FREQH_REG 0x10
+#define LM49453_P0_VCO_TARGETLL_REG 0x11
+#define LM49453_P0_VCO_TARGETLH_REG 0x12
+#define LM49453_P0_VCO_TARGETHL_REG 0x13
+#define LM49453_P0_VCO_TARGETHH_REG 0x14
+#define LM49453_P0_PLL_CONFIG_REG 0x15
+#define LM49453_P0_DAC_CLK_SEL_REG 0x16
+#define LM49453_P0_DAC_HP_CLK_DIV_REG 0x17
+
+/* Analog Mixer Input Stages */
+#define LM49453_P0_MICL_REG 0x20
+#define LM49453_P0_MICR_REG 0x21
+#define LM49453_P0_EP_REG 0x24
+#define LM49453_P0_DIS_PKVL_FB_REG 0x25
+
+/* Analog Mixer Output Stages */
+#define LM49453_P0_ANALOG_MIXER_ADC_REG 0x2E
+
+/*ADC or DAC */
+#define LM49453_P0_ADC_DSP_REG 0x30
+#define LM49453_P0_DAC_DSP_REG 0x31
+
+/* EFFECTS ENABLES */
+#define LM49453_P0_ADC_FX_ENABLES_REG 0x33
+
+/* GPIO */
+#define LM49453_P0_GPIO1_REG 0x38
+#define LM49453_P0_GPIO2_REG 0x39
+#define LM49453_P0_GPIO3_REG 0x3A
+#define LM49453_P0_HAP_CTL_REG 0x3B
+#define LM49453_P0_HAP_FREQ_PROG_LEFTL_REG 0x3C
+#define LM49453_P0_HAP_FREQ_PROG_LEFTH_REG 0x3D
+#define LM49453_P0_HAP_FREQ_PROG_RIGHTL_REG 0x3E
+#define LM49453_P0_HAP_FREQ_PROG_RIGHTH_REG 0x3F
+
+/* DIGITAL MIXER */
+#define LM49453_P0_DMIX_CLK_SEL_REG 0x40
+#define LM49453_P0_PORT1_RX_LVL1_REG 0x41
+#define LM49453_P0_PORT1_RX_LVL2_REG 0x42
+#define LM49453_P0_PORT2_RX_LVL_REG 0x43
+#define LM49453_P0_PORT1_TX1_REG 0x44
+#define LM49453_P0_PORT1_TX2_REG 0x45
+#define LM49453_P0_PORT1_TX3_REG 0x46
+#define LM49453_P0_PORT1_TX4_REG 0x47
+#define LM49453_P0_PORT1_TX5_REG 0x48
+#define LM49453_P0_PORT1_TX6_REG 0x49
+#define LM49453_P0_PORT1_TX7_REG 0x4A
+#define LM49453_P0_PORT1_TX8_REG 0x4B
+#define LM49453_P0_PORT2_TX1_REG 0x4C
+#define LM49453_P0_PORT2_TX2_REG 0x4D
+#define LM49453_P0_STN_SEL_REG 0x4F
+#define LM49453_P0_DACHPL1_REG 0x50
+#define LM49453_P0_DACHPL2_REG 0x51
+#define LM49453_P0_DACHPR1_REG 0x52
+#define LM49453_P0_DACHPR2_REG 0x53
+#define LM49453_P0_DACLOL1_REG 0x54
+#define LM49453_P0_DACLOL2_REG 0x55
+#define LM49453_P0_DACLOR1_REG 0x56
+#define LM49453_P0_DACLOR2_REG 0x57
+#define LM49453_P0_DACLSL1_REG 0x58
+#define LM49453_P0_DACLSL2_REG 0x59
+#define LM49453_P0_DACLSR1_REG 0x5A
+#define LM49453_P0_DACLSR2_REG 0x5B
+#define LM49453_P0_DACHAL1_REG 0x5C
+#define LM49453_P0_DACHAL2_REG 0x5D
+#define LM49453_P0_DACHAR1_REG 0x5E
+#define LM49453_P0_DACHAR2_REG 0x5F
+
+/* AUDIO PORT 1 (TDM) */
+#define LM49453_P0_AUDIO_PORT1_BASIC_REG 0x60
+#define LM49453_P0_AUDIO_PORT1_CLK_GEN1_REG 0x61
+#define LM49453_P0_AUDIO_PORT1_CLK_GEN2_REG 0x62
+#define LM49453_P0_AUDIO_PORT1_CLK_GEN3_REG 0x63
+#define LM49453_P0_AUDIO_PORT1_SYNC_RATE_REG 0x64
+#define LM49453_P0_AUDIO_PORT1_SYNC_SDO_SETUP_REG 0x65
+#define LM49453_P0_AUDIO_PORT1_DATA_WIDTH_REG 0x66
+#define LM49453_P0_AUDIO_PORT1_RX_MSB_REG 0x67
+#define LM49453_P0_AUDIO_PORT1_TX_MSB_REG 0x68
+#define LM49453_P0_AUDIO_PORT1_TDM_CHANNELS_REG 0x69
+
+/* AUDIO PORT 2 */
+#define LM49453_P0_AUDIO_PORT2_BASIC_REG 0x6A
+#define LM49453_P0_AUDIO_PORT2_CLK_GEN1_REG 0x6B
+#define LM49453_P0_AUDIO_PORT2_CLK_GEN2_REG 0x6C
+#define LM49453_P0_AUDIO_PORT2_SYNC_GEN_REG 0x6D
+#define LM49453_P0_AUDIO_PORT2_DATA_WIDTH_REG 0x6E
+#define LM49453_P0_AUDIO_PORT2_RX_MODE_REG 0x6F
+#define LM49453_P0_AUDIO_PORT2_TX_MODE_REG 0x70
+
+/* SAMPLE RATE */
+#define LM49453_P0_PORT1_SR_LSB_REG 0x79
+#define LM49453_P0_PORT1_SR_MSB_REG 0x7A
+#define LM49453_P0_PORT2_SR_LSB_REG 0x7B
+#define LM49453_P0_PORT2_SR_MSB_REG 0x7C
+
+/* EFFECTS - HPFs */
+#define LM49453_P0_HPF_REG 0x80
+
+/* EFFECTS ADC ALC */
+#define LM49453_P0_ADC_ALC1_REG 0x82
+#define LM49453_P0_ADC_ALC2_REG 0x83
+#define LM49453_P0_ADC_ALC3_REG 0x84
+#define LM49453_P0_ADC_ALC4_REG 0x85
+#define LM49453_P0_ADC_ALC5_REG 0x86
+#define LM49453_P0_ADC_ALC6_REG 0x87
+#define LM49453_P0_ADC_ALC7_REG 0x88
+#define LM49453_P0_ADC_ALC8_REG 0x89
+#define LM49453_P0_DMIC1_LEVELL_REG 0x8A
+#define LM49453_P0_DMIC1_LEVELR_REG 0x8B
+#define LM49453_P0_DMIC2_LEVELL_REG 0x8C
+#define LM49453_P0_DMIC2_LEVELR_REG 0x8D
+#define LM49453_P0_ADC_LEVELL_REG 0x8E
+#define LM49453_P0_ADC_LEVELR_REG 0x8F
+#define LM49453_P0_DAC_HP_LEVELL_REG 0x90
+#define LM49453_P0_DAC_HP_LEVELR_REG 0x91
+#define LM49453_P0_DAC_LO_LEVELL_REG 0x92
+#define LM49453_P0_DAC_LO_LEVELR_REG 0x93
+#define LM49453_P0_DAC_LS_LEVELL_REG 0x94
+#define LM49453_P0_DAC_LS_LEVELR_REG 0x95
+#define LM49453_P0_DAC_HA_LEVELL_REG 0x96
+#define LM49453_P0_DAC_HA_LEVELR_REG 0x97
+#define LM49453_P0_SOFT_MUTE_REG 0x98
+#define LM49453_P0_DMIC_MUTE_CFG_REG 0x99
+#define LM49453_P0_ADC_MUTE_CFG_REG 0x9A
+#define LM49453_P0_DAC_MUTE_CFG_REG 0x9B
+
+/*DIGITAL MIC1 */
+#define LM49453_P0_DIGITAL_MIC1_CONFIG_REG 0xB0
+#define LM49453_P0_DIGITAL_MIC1_DATA_DELAYL_REG 0xB1
+#define LM49453_P0_DIGITAL_MIC1_DATA_DELAYR_REG 0xB2
+
+/*DIGITAL MIC2 */
+#define LM49453_P0_DIGITAL_MIC2_CONFIG_REG 0xB3
+#define LM49453_P0_DIGITAL_MIC2_DATA_DELAYL_REG 0xB4
+#define LM49453_P0_DIGITAL_MIC2_DATA_DELAYR_REG 0xB5
+
+/* ADC DECIMATOR */
+#define LM49453_P0_ADC_DECIMATOR_REG 0xB6
+
+/* DAC CONFIGURE */
+#define LM49453_P0_DAC_CONFIG_REG 0xB7
+
+/* SIDETONE */
+#define LM49453_P0_STN_VOL_ADCL_REG 0xB8
+#define LM49453_P0_STN_VOL_ADCR_REG 0xB9
+#define LM49453_P0_STN_VOL_DMIC1L_REG 0xBA
+#define LM49453_P0_STN_VOL_DMIC1R_REG 0xBB
+#define LM49453_P0_STN_VOL_DMIC2L_REG 0xBC
+#define LM49453_P0_STN_VOL_DMIC2R_REG 0xBD
+
+/* ADC/DAC CLIPPING MONITORS (Read Only/Write to Clear) */
+#define LM49453_P0_ADC_DEC_CLIP_REG 0xC2
+#define LM49453_P0_ADC_HPF_CLIP_REG 0xC3
+#define LM49453_P0_ADC_LVL_CLIP_REG 0xC4
+#define LM49453_P0_DAC_LVL_CLIP_REG 0xC5
+
+/* ADC ALC EFFECT MONITORS (Read Only) */
+#define LM49453_P0_ADC_LVLMONL_REG 0xC8
+#define LM49453_P0_ADC_LVLMONR_REG 0xC9
+#define LM49453_P0_ADC_ALCMONL_REG 0xCA
+#define LM49453_P0_ADC_ALCMONR_REG 0xCB
+#define LM49453_P0_ADC_MUTED_REG 0xCC
+#define LM49453_P0_DAC_MUTED_REG 0xCD
+
+/* HEADSET DETECT */
+#define LM49453_P0_HSD_PPB_LONG_CNT_LIMITL_REG 0xD0
+#define LM49453_P0_HSD_PPB_LONG_CNT_LIMITR_REG 0xD1
+#define LM49453_P0_HSD_PIN3_4_EX_LOOP_CNT_LIMITL_REG 0xD2
+#define LM49453_P0_HSD_PIN3_4_EX_LOOP_CNT_LIMITH_REG 0xD3
+#define LM49453_P0_HSD_TIMEOUT1_REG 0xD4
+#define LM49453_P0_HSD_TIMEOUT2_REG 0xD5
+#define LM49453_P0_HSD_TIMEOUT3_REG 0xD6
+#define LM49453_P0_HSD_PIN3_4_CFG_REG 0xD7
+#define LM49453_P0_HSD_IRQ1_REG 0xD8
+#define LM49453_P0_HSD_IRQ2_REG 0xD9
+#define LM49453_P0_HSD_IRQ3_REG 0xDA
+#define LM49453_P0_HSD_IRQ4_REG 0xDB
+#define LM49453_P0_HSD_IRQ_MASK1_REG 0xDC
+#define LM49453_P0_HSD_IRQ_MASK2_REG 0xDD
+#define LM49453_P0_HSD_IRQ_MASK3_REG 0xDE
+#define LM49453_P0_HSD_R_HPLL_REG 0xE0
+#define LM49453_P0_HSD_R_HPLH_REG 0xE1
+#define LM49453_P0_HSD_R_HPLU_REG 0xE2
+#define LM49453_P0_HSD_R_HPRL_REG 0xE3
+#define LM49453_P0_HSD_R_HPRH_REG 0xE4
+#define LM49453_P0_HSD_R_HPRU_REG 0xE5
+#define LM49453_P0_HSD_VEL_L_FINALL_REG 0xE6
+#define LM49453_P0_HSD_VEL_L_FINALH_REG 0xE7
+#define LM49453_P0_HSD_VEL_L_FINALU_REG 0xE8
+#define LM49453_P0_HSD_RO_FINALL_REG 0xE9
+#define LM49453_P0_HSD_RO_FINALH_REG 0xEA
+#define LM49453_P0_HSD_RO_FINALU_REG 0xEB
+#define LM49453_P0_HSD_VMIC_BIAS_FINALL_REG 0xEC
+#define LM49453_P0_HSD_VMIC_BIAS_FINALH_REG 0xED
+#define LM49453_P0_HSD_VMIC_BIAS_FINALU_REG 0xEE
+#define LM49453_P0_HSD_PIN_CONFIG_REG 0xEF
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS1_REG 0xF1
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS2_REG 0xF2
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS3_REG 0xF3
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATEL_REG 0xF4
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATEH_REG 0xF5
+
+/* I/O PULLDOWN CONFIG */
+#define LM49453_P0_PULL_CONFIG1_REG 0xF8
+#define LM49453_P0_PULL_CONFIG2_REG 0xF9
+#define LM49453_P0_PULL_CONFIG3_REG 0xFA
+
+/* RESET */
+#define LM49453_P0_RESET_REG 0xFE
+
+/* PAGE */
+#define LM49453_PAGE_REG 0xFF
+
+#define LM49453_MAX_REGISTER (0xFF+1)
+
+/* LM49453_P0_PMC_SETUP_REG (0x00h) */
+#define LM49453_PMC_SETUP_CHIP_EN (BIT(1)|BIT(0))
+#define LM49453_PMC_SETUP_PLL_EN BIT(2)
+#define LM49453_PMC_SETUP_PLL_P2_EN BIT(3)
+#define LM49453_PMC_SETUP_PLL_FLL BIT(4)
+#define LM49453_PMC_SETUP_MCLK_OVER BIT(5)
+#define LM49453_PMC_SETUP_RTC_CLK_OVER BIT(6)
+#define LM49453_PMC_SETUP_CHIP_ACTIVE BIT(7)
+
+/* Chip Enable bits */
+#define LM49453_CHIP_EN_SHUTDOWN 0x00
+#define LM49453_CHIP_EN 0x01
+#define LM49453_CHIP_EN_HSD_DETECT 0x02
+#define LM49453_CHIP_EN_INVALID_HSD 0x03
+
+/* LM49453_P0_PLL_CLK_SEL1_REG (0x01h) */
+#define LM49453_CLK_SEL1_MCLK_SEL 0x11
+#define LM49453_CLK_SEL1_RTC_SEL 0x11
+#define LM49453_CLK_SEL1_PORT1_SEL 0x10
+#define LM49453_CLK_SEL1_PORT2_SEL 0x11
+
+/* LM49453_P0_PLL_CLK_SEL2_REG (0x02h) */
+#define LM49453_CLK_SEL2_ADC_CLK_SEL 0x38
+
+/* LM49453_P0_FLL_REF_FREQL_REG (0x0F) */
+#define LM49453_FLL_REF_FREQ_VAL 0x8ca0001
+
+/* LM49453_P0_VCO_TARGETLL_REG (0x11) */
+#define LM49453_VCO_TARGET_VAL 0x8ca0001
+
+/* LM49453_P0_ADC_DSP_REG (0x30h) */
+#define LM49453_ADC_DSP_ADC_MUTEL BIT(0)
+#define LM49453_ADC_DSP_ADC_MUTER BIT(1)
+#define LM49453_ADC_DSP_DMIC1_MUTEL BIT(2)
+#define LM49453_ADC_DSP_DMIC1_MUTER BIT(3)
+#define LM49453_ADC_DSP_DMIC2_MUTEL BIT(4)
+#define LM49453_ADC_DSP_DMIC2_MUTER BIT(5)
+#define LM49453_ADC_DSP_MUTE_ALL 0x3F
+
+/* LM49453_P0_DAC_DSP_REG (0x31h) */
+#define LM49453_DAC_DSP_MUTE_ALL 0xFF
+
+/* LM49453_P0_AUDIO_PORT1_BASIC_REG (0x60h) */
+#define LM49453_AUDIO_PORT1_BASIC_FMT_MASK (BIT(4)|BIT(3))
+#define LM49453_AUDIO_PORT1_BASIC_CLK_MS BIT(3)
+#define LM49453_AUDIO_PORT1_BASIC_SYNC_MS BIT(4)
+
+/* LM49453_P0_RESET_REG (0xFEh) */
+#define LM49453_RESET_REG_RST BIT(0)
+
+/* Page select register bits (0xFF) */
+#define LM49453_PAGE0_SELECT 0x0
+#define LM49453_PAGE1_SELECT 0x1
+
+/* LM49453_P0_HSD_PIN3_4_CFG_REG (Jack Pin config - 0xD7) */
+#define LM49453_JACK_DISABLE 0x00
+#define LM49453_JACK_CONFIG1 0x01
+#define LM49453_JACK_CONFIG2 0x02
+#define LM49453_JACK_CONFIG3 0x03
+#define LM49453_JACK_CONFIG4 0x04
+#define LM49453_JACK_CONFIG5 0x05
+
+/* Page 1 REGISTERS */
+
+/* SIDETONE */
+#define LM49453_P1_SIDETONE_SA0L_REG 0x80
+#define LM49453_P1_SIDETONE_SA0H_REG 0x81
+#define LM49453_P1_SIDETONE_SAB0U_REG 0x82
+#define LM49453_P1_SIDETONE_SB0L_REG 0x83
+#define LM49453_P1_SIDETONE_SB0H_REG 0x84
+#define LM49453_P1_SIDETONE_SH0L_REG 0x85
+#define LM49453_P1_SIDETONE_SH0H_REG 0x86
+#define LM49453_P1_SIDETONE_SH0U_REG 0x87
+#define LM49453_P1_SIDETONE_SA1L_REG 0x88
+#define LM49453_P1_SIDETONE_SA1H_REG 0x89
+#define LM49453_P1_SIDETONE_SAB1U_REG 0x8A
+#define LM49453_P1_SIDETONE_SB1L_REG 0x8B
+#define LM49453_P1_SIDETONE_SB1H_REG 0x8C
+#define LM49453_P1_SIDETONE_SH1L_REG 0x8D
+#define LM49453_P1_SIDETONE_SH1H_REG 0x8E
+#define LM49453_P1_SIDETONE_SH1U_REG 0x8F
+#define LM49453_P1_SIDETONE_SA2L_REG 0x90
+#define LM49453_P1_SIDETONE_SA2H_REG 0x91
+#define LM49453_P1_SIDETONE_SAB2U_REG 0x92
+#define LM49453_P1_SIDETONE_SB2L_REG 0x93
+#define LM49453_P1_SIDETONE_SB2H_REG 0x94
+#define LM49453_P1_SIDETONE_SH2L_REG 0x95
+#define LM49453_P1_SIDETONE_SH2H_REG 0x96
+#define LM49453_P1_SIDETONE_SH2U_REG 0x97
+#define LM49453_P1_SIDETONE_SA3L_REG 0x98
+#define LM49453_P1_SIDETONE_SA3H_REG 0x99
+#define LM49453_P1_SIDETONE_SAB3U_REG 0x9A
+#define LM49453_P1_SIDETONE_SB3L_REG 0x9B
+#define LM49453_P1_SIDETONE_SB3H_REG 0x9C
+#define LM49453_P1_SIDETONE_SH3L_REG 0x9D
+#define LM49453_P1_SIDETONE_SH3H_REG 0x9E
+#define LM49453_P1_SIDETONE_SH3U_REG 0x9F
+#define LM49453_P1_SIDETONE_SA4L_REG 0xA0
+#define LM49453_P1_SIDETONE_SA4H_REG 0xA1
+#define LM49453_P1_SIDETONE_SAB4U_REG 0xA2
+#define LM49453_P1_SIDETONE_SB4L_REG 0xA3
+#define LM49453_P1_SIDETONE_SB4H_REG 0xA4
+#define LM49453_P1_SIDETONE_SH4L_REG 0xA5
+#define LM49453_P1_SIDETONE_SH4H_REG 0xA6
+#define LM49453_P1_SIDETONE_SH4U_REG 0xA7
+#define LM49453_P1_SIDETONE_SA5L_REG 0xA8
+#define LM49453_P1_SIDETONE_SA5H_REG 0xA9
+#define LM49453_P1_SIDETONE_SAB5U_REG 0xAA
+#define LM49453_P1_SIDETONE_SB5L_REG 0xAB
+#define LM49453_P1_SIDETONE_SB5H_REG 0xAC
+#define LM49453_P1_SIDETONE_SH5L_REG 0xAD
+#define LM49453_P1_SIDETONE_SH5H_REG 0xAE
+#define LM49453_P1_SIDETONE_SH5U_REG 0xAF
+
+/* CHARGE PUMP CONFIG */
+#define LM49453_P1_CP_CONFIG1_REG 0xB0
+#define LM49453_P1_CP_CONFIG2_REG 0xB1
+#define LM49453_P1_CP_CONFIG3_REG 0xB2
+#define LM49453_P1_CP_CONFIG4_REG 0xB3
+#define LM49453_P1_CP_LA_VTH1L_REG 0xB4
+#define LM49453_P1_CP_LA_VTH1M_REG 0xB5
+#define LM49453_P1_CP_LA_VTH2L_REG 0xB6
+#define LM49453_P1_CP_LA_VTH2M_REG 0xB7
+#define LM49453_P1_CP_LA_VTH3L_REG 0xB8
+#define LM49453_P1_CP_LA_VTH3H_REG 0xB9
+#define LM49453_P1_CP_CLK_DIV_REG 0xBA
+
+/* DAC */
+#define LM49453_P1_DAC_CHOP_REG 0xC0
+
+#define LM49453_CLK_SRC_MCLK 1
+#endif
diff --git a/kernel/sound/soc/codecs/max9768.c b/kernel/sound/soc/codecs/max9768.c
new file mode 100644
index 000000000..e1c196a41
--- /dev/null
+++ b/kernel/sound/soc/codecs/max9768.c
@@ -0,0 +1,255 @@
+/*
+ * 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/regmap.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/max9768.h>
+
+/* "Registers" */
+#define MAX9768_VOL 0
+#define MAX9768_CTRL 3
+
+/* Commands */
+#define MAX9768_CTRL_PWM 0x15
+#define MAX9768_CTRL_FILTERLESS 0x16
+
+struct max9768 {
+ struct regmap *regmap;
+ int mute_gpio;
+ int shdn_gpio;
+ u32 flags;
+};
+
+static struct reg_default max9768_default_regs[] = {
+ { 0, 0 },
+ { 3, MAX9768_CTRL_FILTERLESS},
+};
+
+static int max9768_get_gpio(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_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);
+
+ ucontrol->value.integer.value[0] = !val;
+
+ return 0;
+}
+
+static int max9768_set_gpio(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec);
+
+ gpio_set_value_cansleep(max9768->mute_gpio, !ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static const unsigned int volume_tlv[] = {
+ TLV_DB_RANGE_HEAD(43),
+ 0, 0, TLV_DB_SCALE_ITEM(-16150, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(-9280, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(-9030, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(-8680, 0, 0),
+ 4, 4, TLV_DB_SCALE_ITEM(-8430, 0, 0),
+ 5, 5, TLV_DB_SCALE_ITEM(-8080, 0, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(-7830, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(-7470, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(-7220, 0, 0),
+ 9, 9, TLV_DB_SCALE_ITEM(-6870, 0, 0),
+ 10, 10, TLV_DB_SCALE_ITEM(-6620, 0, 0),
+ 11, 11, TLV_DB_SCALE_ITEM(-6270, 0, 0),
+ 12, 12, TLV_DB_SCALE_ITEM(-6020, 0, 0),
+ 13, 13, TLV_DB_SCALE_ITEM(-5670, 0, 0),
+ 14, 14, TLV_DB_SCALE_ITEM(-5420, 0, 0),
+ 15, 17, TLV_DB_SCALE_ITEM(-5060, 250, 0),
+ 18, 18, TLV_DB_SCALE_ITEM(-4370, 0, 0),
+ 19, 19, TLV_DB_SCALE_ITEM(-4210, 0, 0),
+ 20, 20, TLV_DB_SCALE_ITEM(-3960, 0, 0),
+ 21, 21, TLV_DB_SCALE_ITEM(-3760, 0, 0),
+ 22, 22, TLV_DB_SCALE_ITEM(-3600, 0, 0),
+ 23, 23, TLV_DB_SCALE_ITEM(-3340, 0, 0),
+ 24, 24, TLV_DB_SCALE_ITEM(-3150, 0, 0),
+ 25, 25, TLV_DB_SCALE_ITEM(-2980, 0, 0),
+ 26, 26, TLV_DB_SCALE_ITEM(-2720, 0, 0),
+ 27, 27, TLV_DB_SCALE_ITEM(-2520, 0, 0),
+ 28, 30, TLV_DB_SCALE_ITEM(-2350, 190, 0),
+ 31, 31, TLV_DB_SCALE_ITEM(-1750, 0, 0),
+ 32, 34, TLV_DB_SCALE_ITEM(-1640, 100, 0),
+ 35, 37, TLV_DB_SCALE_ITEM(-1310, 110, 0),
+ 38, 39, TLV_DB_SCALE_ITEM(-990, 100, 0),
+ 40, 40, TLV_DB_SCALE_ITEM(-710, 0, 0),
+ 41, 41, TLV_DB_SCALE_ITEM(-600, 0, 0),
+ 42, 42, TLV_DB_SCALE_ITEM(-500, 0, 0),
+ 43, 43, TLV_DB_SCALE_ITEM(-340, 0, 0),
+ 44, 44, TLV_DB_SCALE_ITEM(-190, 0, 0),
+ 45, 45, TLV_DB_SCALE_ITEM(-50, 0, 0),
+ 46, 46, TLV_DB_SCALE_ITEM(50, 0, 0),
+ 47, 50, TLV_DB_SCALE_ITEM(120, 40, 0),
+ 51, 57, TLV_DB_SCALE_ITEM(290, 50, 0),
+ 58, 58, TLV_DB_SCALE_ITEM(650, 0, 0),
+ 59, 62, TLV_DB_SCALE_ITEM(700, 60, 0),
+ 63, 63, TLV_DB_SCALE_ITEM(950, 0, 0),
+};
+
+static const struct snd_kcontrol_new max9768_volume[] = {
+ SOC_SINGLE_TLV("Playback Volume", MAX9768_VOL, 0, 63, 0, volume_tlv),
+};
+
+static const struct snd_kcontrol_new max9768_mute[] = {
+ SOC_SINGLE_BOOL_EXT("Playback Switch", 0, max9768_get_gpio, max9768_set_gpio),
+};
+
+static const struct snd_soc_dapm_widget max9768_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN"),
+
+SND_SOC_DAPM_OUTPUT("OUT+"),
+SND_SOC_DAPM_OUTPUT("OUT-"),
+};
+
+static const struct snd_soc_dapm_route max9768_dapm_routes[] = {
+ { "OUT+", NULL, "IN" },
+ { "OUT-", NULL, "IN" },
+};
+
+static int max9768_probe(struct snd_soc_codec *codec)
+{
+ struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ if (max9768->flags & MAX9768_FLAG_CLASSIC_PWM) {
+ ret = snd_soc_write(codec, 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,
+ ARRAY_SIZE(max9768_mute));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver max9768_codec_driver = {
+ .probe = max9768_probe,
+ .controls = max9768_volume,
+ .num_controls = ARRAY_SIZE(max9768_volume),
+ .dapm_widgets = max9768_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9768_dapm_widgets),
+ .dapm_routes = max9768_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9768_dapm_routes),
+};
+
+static const struct regmap_config max9768_i2c_regmap_config = {
+ .reg_bits = 2,
+ .val_bits = 6,
+ .max_register = 3,
+ .reg_defaults = max9768_default_regs,
+ .num_reg_defaults = ARRAY_SIZE(max9768_default_regs),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max9768_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ 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;
+
+ 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;
+
+ 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;
+}
+
+static const struct i2c_device_id max9768_i2c_id[] = {
+ { "max9768", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max9768_i2c_id);
+
+static struct i2c_driver max9768_i2c_driver = {
+ .driver = {
+ .name = "max9768",
+ .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_DESCRIPTION("ASoC MAX9768 amplifier driver");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/max98088.c b/kernel/sound/soc/codecs/max98088.c
new file mode 100644
index 000000000..805b3f8cd
--- /dev/null
+++ b/kernel/sound/soc/codecs/max98088.c
@@ -0,0 +1,2026 @@
+/*
+ * 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>
+#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 <sound/max98088.h>
+#include "max98088.h"
+
+enum max98088_type {
+ MAX98088,
+ MAX98089,
+};
+
+struct max98088_cdata {
+ unsigned int rate;
+ unsigned int fmt;
+ int eq_sel;
+};
+
+struct max98088_priv {
+ struct regmap *regmap;
+ enum max98088_type devtype;
+ struct max98088_pdata *pdata;
+ unsigned int sysclk;
+ struct max98088_cdata dai[2];
+ int eq_textcnt;
+ const char **eq_texts;
+ struct soc_enum eq_enum;
+ u8 ina_state;
+ u8 inb_state;
+ unsigned int ex_mode;
+ unsigned int digmic;
+ unsigned int mic1pre;
+ unsigned int mic2pre;
+ unsigned int extmic_mode;
+};
+
+static const struct reg_default max98088_reg[] = {
+ { 0xf, 0x00 }, /* 0F interrupt enable */
+
+ { 0x10, 0x00 }, /* 10 master clock */
+ { 0x11, 0x00 }, /* 11 DAI1 clock mode */
+ { 0x12, 0x00 }, /* 12 DAI1 clock control */
+ { 0x13, 0x00 }, /* 13 DAI1 clock control */
+ { 0x14, 0x00 }, /* 14 DAI1 format */
+ { 0x15, 0x00 }, /* 15 DAI1 clock */
+ { 0x16, 0x00 }, /* 16 DAI1 config */
+ { 0x17, 0x00 }, /* 17 DAI1 TDM */
+ { 0x18, 0x00 }, /* 18 DAI1 filters */
+ { 0x19, 0x00 }, /* 19 DAI2 clock mode */
+ { 0x1a, 0x00 }, /* 1A DAI2 clock control */
+ { 0x1b, 0x00 }, /* 1B DAI2 clock control */
+ { 0x1c, 0x00 }, /* 1C DAI2 format */
+ { 0x1d, 0x00 }, /* 1D DAI2 clock */
+ { 0x1e, 0x00 }, /* 1E DAI2 config */
+ { 0x1f, 0x00 }, /* 1F DAI2 TDM */
+
+ { 0x20, 0x00 }, /* 20 DAI2 filters */
+ { 0x21, 0x00 }, /* 21 data config */
+ { 0x22, 0x00 }, /* 22 DAC mixer */
+ { 0x23, 0x00 }, /* 23 left ADC mixer */
+ { 0x24, 0x00 }, /* 24 right ADC mixer */
+ { 0x25, 0x00 }, /* 25 left HP mixer */
+ { 0x26, 0x00 }, /* 26 right HP mixer */
+ { 0x27, 0x00 }, /* 27 HP control */
+ { 0x28, 0x00 }, /* 28 left REC mixer */
+ { 0x29, 0x00 }, /* 29 right REC mixer */
+ { 0x2a, 0x00 }, /* 2A REC control */
+ { 0x2b, 0x00 }, /* 2B left SPK mixer */
+ { 0x2c, 0x00 }, /* 2C right SPK mixer */
+ { 0x2d, 0x00 }, /* 2D SPK control */
+ { 0x2e, 0x00 }, /* 2E sidetone */
+ { 0x2f, 0x00 }, /* 2F DAI1 playback level */
+
+ { 0x30, 0x00 }, /* 30 DAI1 playback level */
+ { 0x31, 0x00 }, /* 31 DAI2 playback level */
+ { 0x32, 0x00 }, /* 32 DAI2 playbakc level */
+ { 0x33, 0x00 }, /* 33 left ADC level */
+ { 0x34, 0x00 }, /* 34 right ADC level */
+ { 0x35, 0x00 }, /* 35 MIC1 level */
+ { 0x36, 0x00 }, /* 36 MIC2 level */
+ { 0x37, 0x00 }, /* 37 INA level */
+ { 0x38, 0x00 }, /* 38 INB level */
+ { 0x39, 0x00 }, /* 39 left HP volume */
+ { 0x3a, 0x00 }, /* 3A right HP volume */
+ { 0x3b, 0x00 }, /* 3B left REC volume */
+ { 0x3c, 0x00 }, /* 3C right REC volume */
+ { 0x3d, 0x00 }, /* 3D left SPK volume */
+ { 0x3e, 0x00 }, /* 3E right SPK volume */
+ { 0x3f, 0x00 }, /* 3F MIC config */
+
+ { 0x40, 0x00 }, /* 40 MIC threshold */
+ { 0x41, 0x00 }, /* 41 excursion limiter filter */
+ { 0x42, 0x00 }, /* 42 excursion limiter threshold */
+ { 0x43, 0x00 }, /* 43 ALC */
+ { 0x44, 0x00 }, /* 44 power limiter threshold */
+ { 0x45, 0x00 }, /* 45 power limiter config */
+ { 0x46, 0x00 }, /* 46 distortion limiter config */
+ { 0x47, 0x00 }, /* 47 audio input */
+ { 0x48, 0x00 }, /* 48 microphone */
+ { 0x49, 0x00 }, /* 49 level control */
+ { 0x4a, 0x00 }, /* 4A bypass switches */
+ { 0x4b, 0x00 }, /* 4B jack detect */
+ { 0x4c, 0x00 }, /* 4C input enable */
+ { 0x4d, 0x00 }, /* 4D output enable */
+ { 0x4e, 0xF0 }, /* 4E bias control */
+ { 0x4f, 0x00 }, /* 4F DAC power */
+
+ { 0x50, 0x0F }, /* 50 DAC power */
+ { 0x51, 0x00 }, /* 51 system */
+ { 0x52, 0x00 }, /* 52 DAI1 EQ1 */
+ { 0x53, 0x00 }, /* 53 DAI1 EQ1 */
+ { 0x54, 0x00 }, /* 54 DAI1 EQ1 */
+ { 0x55, 0x00 }, /* 55 DAI1 EQ1 */
+ { 0x56, 0x00 }, /* 56 DAI1 EQ1 */
+ { 0x57, 0x00 }, /* 57 DAI1 EQ1 */
+ { 0x58, 0x00 }, /* 58 DAI1 EQ1 */
+ { 0x59, 0x00 }, /* 59 DAI1 EQ1 */
+ { 0x5a, 0x00 }, /* 5A DAI1 EQ1 */
+ { 0x5b, 0x00 }, /* 5B DAI1 EQ1 */
+ { 0x5c, 0x00 }, /* 5C DAI1 EQ2 */
+ { 0x5d, 0x00 }, /* 5D DAI1 EQ2 */
+ { 0x5e, 0x00 }, /* 5E DAI1 EQ2 */
+ { 0x5f, 0x00 }, /* 5F DAI1 EQ2 */
+
+ { 0x60, 0x00 }, /* 60 DAI1 EQ2 */
+ { 0x61, 0x00 }, /* 61 DAI1 EQ2 */
+ { 0x62, 0x00 }, /* 62 DAI1 EQ2 */
+ { 0x63, 0x00 }, /* 63 DAI1 EQ2 */
+ { 0x64, 0x00 }, /* 64 DAI1 EQ2 */
+ { 0x65, 0x00 }, /* 65 DAI1 EQ2 */
+ { 0x66, 0x00 }, /* 66 DAI1 EQ3 */
+ { 0x67, 0x00 }, /* 67 DAI1 EQ3 */
+ { 0x68, 0x00 }, /* 68 DAI1 EQ3 */
+ { 0x69, 0x00 }, /* 69 DAI1 EQ3 */
+ { 0x6a, 0x00 }, /* 6A DAI1 EQ3 */
+ { 0x6b, 0x00 }, /* 6B DAI1 EQ3 */
+ { 0x6c, 0x00 }, /* 6C DAI1 EQ3 */
+ { 0x6d, 0x00 }, /* 6D DAI1 EQ3 */
+ { 0x6e, 0x00 }, /* 6E DAI1 EQ3 */
+ { 0x6f, 0x00 }, /* 6F DAI1 EQ3 */
+
+ { 0x70, 0x00 }, /* 70 DAI1 EQ4 */
+ { 0x71, 0x00 }, /* 71 DAI1 EQ4 */
+ { 0x72, 0x00 }, /* 72 DAI1 EQ4 */
+ { 0x73, 0x00 }, /* 73 DAI1 EQ4 */
+ { 0x74, 0x00 }, /* 74 DAI1 EQ4 */
+ { 0x75, 0x00 }, /* 75 DAI1 EQ4 */
+ { 0x76, 0x00 }, /* 76 DAI1 EQ4 */
+ { 0x77, 0x00 }, /* 77 DAI1 EQ4 */
+ { 0x78, 0x00 }, /* 78 DAI1 EQ4 */
+ { 0x79, 0x00 }, /* 79 DAI1 EQ4 */
+ { 0x7a, 0x00 }, /* 7A DAI1 EQ5 */
+ { 0x7b, 0x00 }, /* 7B DAI1 EQ5 */
+ { 0x7c, 0x00 }, /* 7C DAI1 EQ5 */
+ { 0x7d, 0x00 }, /* 7D DAI1 EQ5 */
+ { 0x7e, 0x00 }, /* 7E DAI1 EQ5 */
+ { 0x7f, 0x00 }, /* 7F DAI1 EQ5 */
+
+ { 0x80, 0x00 }, /* 80 DAI1 EQ5 */
+ { 0x81, 0x00 }, /* 81 DAI1 EQ5 */
+ { 0x82, 0x00 }, /* 82 DAI1 EQ5 */
+ { 0x83, 0x00 }, /* 83 DAI1 EQ5 */
+ { 0x84, 0x00 }, /* 84 DAI2 EQ1 */
+ { 0x85, 0x00 }, /* 85 DAI2 EQ1 */
+ { 0x86, 0x00 }, /* 86 DAI2 EQ1 */
+ { 0x87, 0x00 }, /* 87 DAI2 EQ1 */
+ { 0x88, 0x00 }, /* 88 DAI2 EQ1 */
+ { 0x89, 0x00 }, /* 89 DAI2 EQ1 */
+ { 0x8a, 0x00 }, /* 8A DAI2 EQ1 */
+ { 0x8b, 0x00 }, /* 8B DAI2 EQ1 */
+ { 0x8c, 0x00 }, /* 8C DAI2 EQ1 */
+ { 0x8d, 0x00 }, /* 8D DAI2 EQ1 */
+ { 0x8e, 0x00 }, /* 8E DAI2 EQ2 */
+ { 0x8f, 0x00 }, /* 8F DAI2 EQ2 */
+
+ { 0x90, 0x00 }, /* 90 DAI2 EQ2 */
+ { 0x91, 0x00 }, /* 91 DAI2 EQ2 */
+ { 0x92, 0x00 }, /* 92 DAI2 EQ2 */
+ { 0x93, 0x00 }, /* 93 DAI2 EQ2 */
+ { 0x94, 0x00 }, /* 94 DAI2 EQ2 */
+ { 0x95, 0x00 }, /* 95 DAI2 EQ2 */
+ { 0x96, 0x00 }, /* 96 DAI2 EQ2 */
+ { 0x97, 0x00 }, /* 97 DAI2 EQ2 */
+ { 0x98, 0x00 }, /* 98 DAI2 EQ3 */
+ { 0x99, 0x00 }, /* 99 DAI2 EQ3 */
+ { 0x9a, 0x00 }, /* 9A DAI2 EQ3 */
+ { 0x9b, 0x00 }, /* 9B DAI2 EQ3 */
+ { 0x9c, 0x00 }, /* 9C DAI2 EQ3 */
+ { 0x9d, 0x00 }, /* 9D DAI2 EQ3 */
+ { 0x9e, 0x00 }, /* 9E DAI2 EQ3 */
+ { 0x9f, 0x00 }, /* 9F DAI2 EQ3 */
+
+ { 0xa0, 0x00 }, /* A0 DAI2 EQ3 */
+ { 0xa1, 0x00 }, /* A1 DAI2 EQ3 */
+ { 0xa2, 0x00 }, /* A2 DAI2 EQ4 */
+ { 0xa3, 0x00 }, /* A3 DAI2 EQ4 */
+ { 0xa4, 0x00 }, /* A4 DAI2 EQ4 */
+ { 0xa5, 0x00 }, /* A5 DAI2 EQ4 */
+ { 0xa6, 0x00 }, /* A6 DAI2 EQ4 */
+ { 0xa7, 0x00 }, /* A7 DAI2 EQ4 */
+ { 0xa8, 0x00 }, /* A8 DAI2 EQ4 */
+ { 0xa9, 0x00 }, /* A9 DAI2 EQ4 */
+ { 0xaa, 0x00 }, /* AA DAI2 EQ4 */
+ { 0xab, 0x00 }, /* AB DAI2 EQ4 */
+ { 0xac, 0x00 }, /* AC DAI2 EQ5 */
+ { 0xad, 0x00 }, /* AD DAI2 EQ5 */
+ { 0xae, 0x00 }, /* AE DAI2 EQ5 */
+ { 0xaf, 0x00 }, /* AF DAI2 EQ5 */
+
+ { 0xb0, 0x00 }, /* B0 DAI2 EQ5 */
+ { 0xb1, 0x00 }, /* B1 DAI2 EQ5 */
+ { 0xb2, 0x00 }, /* B2 DAI2 EQ5 */
+ { 0xb3, 0x00 }, /* B3 DAI2 EQ5 */
+ { 0xb4, 0x00 }, /* B4 DAI2 EQ5 */
+ { 0xb5, 0x00 }, /* B5 DAI2 EQ5 */
+ { 0xb6, 0x00 }, /* B6 DAI1 biquad */
+ { 0xb7, 0x00 }, /* B7 DAI1 biquad */
+ { 0xb8 ,0x00 }, /* B8 DAI1 biquad */
+ { 0xb9, 0x00 }, /* B9 DAI1 biquad */
+ { 0xba, 0x00 }, /* BA DAI1 biquad */
+ { 0xbb, 0x00 }, /* BB DAI1 biquad */
+ { 0xbc, 0x00 }, /* BC DAI1 biquad */
+ { 0xbd, 0x00 }, /* BD DAI1 biquad */
+ { 0xbe, 0x00 }, /* BE DAI1 biquad */
+ { 0xbf, 0x00 }, /* BF DAI1 biquad */
+
+ { 0xc0, 0x00 }, /* C0 DAI2 biquad */
+ { 0xc1, 0x00 }, /* C1 DAI2 biquad */
+ { 0xc2, 0x00 }, /* C2 DAI2 biquad */
+ { 0xc3, 0x00 }, /* C3 DAI2 biquad */
+ { 0xc4, 0x00 }, /* C4 DAI2 biquad */
+ { 0xc5, 0x00 }, /* C5 DAI2 biquad */
+ { 0xc6, 0x00 }, /* C6 DAI2 biquad */
+ { 0xc7, 0x00 }, /* C7 DAI2 biquad */
+ { 0xc8, 0x00 }, /* C8 DAI2 biquad */
+ { 0xc9, 0x00 }, /* C9 DAI2 biquad */
+};
+
+static 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;
+}
+
+static bool max98088_volatile_register(struct device *dev, unsigned int reg)
+{
+ return max98088_access[reg].vol;
+}
+
+static const struct regmap_config max98088_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = max98088_readable_register,
+ .volatile_reg = max98088_volatile_register,
+ .max_register = 0xff,
+
+ .reg_defaults = max98088_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98088_reg),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+static void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+ unsigned int band, u16 *coefs)
+{
+ unsigned int eq_reg;
+ unsigned int i;
+
+ if (WARN_ON(band > 4) ||
+ WARN_ON(dai > 1))
+ return;
+
+ /* Load the base register address */
+ eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
+
+ /* Add the band address offset, note adjustment for word address */
+ eq_reg += band * (M98088_COEFS_PER_BAND << 1);
+
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
+ snd_soc_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
+ snd_soc_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
+ }
+}
+
+/*
+ * Excursion limiter modes
+ */
+static const char *max98088_exmode_texts[] = {
+ "Off", "100Hz", "400Hz", "600Hz", "800Hz", "1000Hz", "200-400Hz",
+ "400-600Hz", "400-800Hz",
+};
+
+static const unsigned int max98088_exmode_values[] = {
+ 0x00, 0x43, 0x10, 0x20, 0x30, 0x40, 0x11, 0x22, 0x32
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(max98088_exmode_enum,
+ M98088_REG_41_SPKDHP, 0, 127,
+ max98088_exmode_texts,
+ max98088_exmode_values);
+
+static const char *max98088_ex_thresh[] = { /* volts PP */
+ "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
+static SOC_ENUM_SINGLE_DECL(max98088_ex_thresh_enum,
+ M98088_REG_42_SPKDHP_THRESH, 0,
+ max98088_ex_thresh);
+
+static const char *max98088_fltr_mode[] = {"Voice", "Music" };
+static SOC_ENUM_SINGLE_DECL(max98088_filter_mode_enum,
+ M98088_REG_18_DAI1_FILTERS, 7,
+ max98088_fltr_mode);
+
+static const char *max98088_extmic_text[] = { "None", "MIC1", "MIC2" };
+
+static SOC_ENUM_SINGLE_DECL(max98088_extmic_enum,
+ M98088_REG_48_CFG_MIC, 0,
+ max98088_extmic_text);
+
+static const struct snd_kcontrol_new max98088_extmic_mux =
+ SOC_DAPM_ENUM("External MIC Mux", max98088_extmic_enum);
+
+static const char *max98088_dai1_fltr[] = {
+ "Off", "fc=258/fs=16k", "fc=500/fs=16k",
+ "fc=258/fs=8k", "fc=500/fs=8k", "fc=200"};
+static SOC_ENUM_SINGLE_DECL(max98088_dai1_dac_filter_enum,
+ M98088_REG_18_DAI1_FILTERS, 0,
+ max98088_dai1_fltr);
+static SOC_ENUM_SINGLE_DECL(max98088_dai1_adc_filter_enum,
+ M98088_REG_18_DAI1_FILTERS, 4,
+ max98088_dai1_fltr);
+
+static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98088->mic1pre = sel;
+ snd_soc_update_bits(codec, M98088_REG_35_LVL_MIC1, M98088_MICPRE_MASK,
+ (1+sel)<<M98088_MICPRE_SHIFT);
+
+ return 0;
+}
+
+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);
+
+ ucontrol->value.integer.value[0] = max98088->mic1pre;
+ return 0;
+}
+
+static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98088->mic2pre = sel;
+ snd_soc_update_bits(codec, M98088_REG_36_LVL_MIC2, M98088_MICPRE_MASK,
+ (1+sel)<<M98088_MICPRE_SHIFT);
+
+ return 0;
+}
+
+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);
+
+ 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 unsigned int max98088_hp_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0),
+};
+
+static const unsigned int max98088_spk_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0),
+};
+
+static const struct snd_kcontrol_new max98088_snd_controls[] = {
+
+ SOC_DOUBLE_R_TLV("Headphone Volume", M98088_REG_39_LVL_HP_L,
+ M98088_REG_3A_LVL_HP_R, 0, 31, 0, max98088_hp_tlv),
+ SOC_DOUBLE_R_TLV("Speaker Volume", M98088_REG_3D_LVL_SPK_L,
+ M98088_REG_3E_LVL_SPK_R, 0, 31, 0, max98088_spk_tlv),
+ SOC_DOUBLE_R_TLV("Receiver Volume", M98088_REG_3B_LVL_REC_L,
+ M98088_REG_3C_LVL_REC_R, 0, 31, 0, max98088_spk_tlv),
+
+ SOC_DOUBLE_R("Headphone Switch", M98088_REG_39_LVL_HP_L,
+ M98088_REG_3A_LVL_HP_R, 7, 1, 1),
+ SOC_DOUBLE_R("Speaker Switch", M98088_REG_3D_LVL_SPK_L,
+ M98088_REG_3E_LVL_SPK_R, 7, 1, 1),
+ SOC_DOUBLE_R("Receiver Switch", M98088_REG_3B_LVL_REC_L,
+ M98088_REG_3C_LVL_REC_R, 7, 1, 1),
+
+ SOC_SINGLE("MIC1 Volume", M98088_REG_35_LVL_MIC1, 0, 31, 1),
+ SOC_SINGLE("MIC2 Volume", M98088_REG_36_LVL_MIC2, 0, 31, 1),
+
+ SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
+ M98088_REG_35_LVL_MIC1, 5, 2, 0,
+ max98088_mic1pre_get, max98088_mic1pre_set,
+ max98088_micboost_tlv),
+ SOC_SINGLE_EXT_TLV("MIC2 Boost Volume",
+ M98088_REG_36_LVL_MIC2, 5, 2, 0,
+ max98088_mic2pre_get, max98088_mic2pre_set,
+ max98088_micboost_tlv),
+
+ SOC_SINGLE("INA Volume", M98088_REG_37_LVL_INA, 0, 7, 1),
+ SOC_SINGLE("INB Volume", M98088_REG_38_LVL_INB, 0, 7, 1),
+
+ SOC_SINGLE("ADCL Volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0),
+ SOC_SINGLE("ADCR Volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0),
+
+ SOC_SINGLE("ADCL Boost Volume", M98088_REG_33_LVL_ADC_L, 4, 3, 0),
+ SOC_SINGLE("ADCR Boost Volume", M98088_REG_34_LVL_ADC_R, 4, 3, 0),
+
+ SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
+ SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+
+ SOC_ENUM("EX Limiter Mode", max98088_exmode_enum),
+ SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
+
+ SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
+ SOC_ENUM("DAI1 DAC Filter", max98088_dai1_dac_filter_enum),
+ SOC_ENUM("DAI1 ADC Filter", max98088_dai1_adc_filter_enum),
+ SOC_SINGLE("DAI2 DC Block Switch", M98088_REG_20_DAI2_FILTERS,
+ 0, 1, 0),
+
+ SOC_SINGLE("ALC Switch", M98088_REG_43_SPKALC_COMP, 7, 1, 0),
+ SOC_SINGLE("ALC Threshold", M98088_REG_43_SPKALC_COMP, 0, 7, 0),
+ SOC_SINGLE("ALC Multiband", M98088_REG_43_SPKALC_COMP, 3, 1, 0),
+ SOC_SINGLE("ALC Release Time", M98088_REG_43_SPKALC_COMP, 4, 7, 0),
+
+ SOC_SINGLE("PWR Limiter Threshold", M98088_REG_44_PWRLMT_CFG,
+ 4, 15, 0),
+ SOC_SINGLE("PWR Limiter Weight", M98088_REG_44_PWRLMT_CFG, 0, 7, 0),
+ SOC_SINGLE("PWR Limiter Time1", M98088_REG_45_PWRLMT_TIME, 0, 15, 0),
+ SOC_SINGLE("PWR Limiter Time2", M98088_REG_45_PWRLMT_TIME, 4, 15, 0),
+
+ SOC_SINGLE("THD Limiter Threshold", M98088_REG_46_THDLMT_CFG, 4, 15, 0),
+ SOC_SINGLE("THD Limiter Time", M98088_REG_46_THDLMT_CFG, 0, 7, 0),
+};
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 4, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 4, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_25_MIX_HP_LEFT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_25_MIX_HP_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_25_MIX_HP_LEFT, 4, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_26_MIX_HP_RIGHT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_26_MIX_HP_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_26_MIX_HP_RIGHT, 4, 1, 0),
+};
+
+/* Left earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_28_MIX_REC_LEFT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_28_MIX_REC_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_28_MIX_REC_LEFT, 4, 1, 0),
+};
+
+/* Right earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_29_MIX_REC_RIGHT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_29_MIX_REC_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_29_MIX_REC_RIGHT, 4, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98088_left_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_23_MIX_ADC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_23_MIX_ADC_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_23_MIX_ADC_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_23_MIX_ADC_LEFT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_23_MIX_ADC_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_23_MIX_ADC_LEFT, 0, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 0, 1, 0),
+};
+
+static int max98088_mic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+ 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,
+ (1+max98088->mic1pre)<<M98088_MICPRE_SHIFT);
+ } else {
+ snd_soc_update_bits(codec, 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);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * The line inputs are 2-channel stereo inputs with the left
+ * and right channels sharing a common PGA power control signal.
+ */
+static int max98088_line_pga(struct snd_soc_dapm_widget *w,
+ int event, int line, u8 channel)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ u8 *state;
+
+ if (WARN_ON(!(channel == 1 || channel == 2)))
+ return -EINVAL;
+
+ switch (line) {
+ case LINE_INA:
+ state = &max98088->ina_state;
+ break;
+ case LINE_INB:
+ state = &max98088->inb_state;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ *state |= channel;
+ snd_soc_update_bits(codec, 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,
+ (1 << w->shift), 0);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int max98088_pga_ina1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98088_line_pga(w, event, LINE_INA, 1);
+}
+
+static int max98088_pga_ina2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98088_line_pga(w, event, LINE_INA, 2);
+}
+
+static int max98088_pga_inb1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98088_line_pga(w, event, LINE_INB, 1);
+}
+
+static int max98088_pga_inb2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98088_line_pga(w, event, LINE_INB, 2);
+}
+
+static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
+
+ SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0),
+ SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0),
+
+ SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+ M98088_REG_4D_PWR_EN_OUT, 1, 0),
+ SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+ M98088_REG_4D_PWR_EN_OUT, 0, 0),
+ SND_SOC_DAPM_DAC("DACL2", "Aux Playback",
+ M98088_REG_4D_PWR_EN_OUT, 1, 0),
+ SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+ M98088_REG_4D_PWR_EN_OUT, 0, 0),
+
+ SND_SOC_DAPM_PGA("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
+ 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP Right Out", M98088_REG_4D_PWR_EN_OUT,
+ 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("SPK Left Out", M98088_REG_4D_PWR_EN_OUT,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPK Right Out", M98088_REG_4D_PWR_EN_OUT,
+ 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("REC Left Out", M98088_REG_4D_PWR_EN_OUT,
+ 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("REC Right Out", M98088_REG_4D_PWR_EN_OUT,
+ 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("External MIC", SND_SOC_NOPM, 0, 0,
+ &max98088_extmic_mux),
+
+ SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_left_hp_mixer_controls[0],
+ ARRAY_SIZE(max98088_left_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_right_hp_mixer_controls[0],
+ ARRAY_SIZE(max98088_right_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left SPK Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_left_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98088_left_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right SPK Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_right_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98088_right_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left REC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_left_rec_mixer_controls[0],
+ ARRAY_SIZE(max98088_left_rec_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right REC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_right_rec_mixer_controls[0],
+ ARRAY_SIZE(max98088_right_rec_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_left_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98088_left_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_right_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98088_right_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_PGA_E("MIC1 Input", M98088_REG_35_LVL_MIC1,
+ 5, 0, NULL, 0, max98088_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("MIC2 Input", M98088_REG_36_LVL_MIC2,
+ 5, 0, NULL, 0, max98088_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("INA1 Input", M98088_REG_4C_PWR_EN_IN,
+ 7, 0, NULL, 0, max98088_pga_ina1_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("INA2 Input", M98088_REG_4C_PWR_EN_IN,
+ 7, 0, NULL, 0, max98088_pga_ina2_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("INB1 Input", M98088_REG_4C_PWR_EN_IN,
+ 6, 0, NULL, 0, max98088_pga_inb1_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("INB2 Input", M98088_REG_4C_PWR_EN_IN,
+ 6, 0, NULL, 0, max98088_pga_inb2_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("RECL"),
+ SND_SOC_DAPM_OUTPUT("RECR"),
+
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("INA1"),
+ SND_SOC_DAPM_INPUT("INA2"),
+ SND_SOC_DAPM_INPUT("INB1"),
+ SND_SOC_DAPM_INPUT("INB2"),
+};
+
+static const struct snd_soc_dapm_route max98088_audio_map[] = {
+ /* Left headphone output mixer */
+ {"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
+ {"Left HP Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left HP Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Left HP Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left HP Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left HP Mixer", "INA1 Switch", "INA1 Input"},
+ {"Left HP Mixer", "INA2 Switch", "INA2 Input"},
+ {"Left HP Mixer", "INB1 Switch", "INB1 Input"},
+ {"Left HP Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Right headphone output mixer */
+ {"Right HP Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right HP Mixer", "Left DAC2 Switch", "DACL2" },
+ {"Right HP Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right HP Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Right HP Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right HP Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right HP Mixer", "INA1 Switch", "INA1 Input"},
+ {"Right HP Mixer", "INA2 Switch", "INA2 Input"},
+ {"Right HP Mixer", "INB1 Switch", "INB1 Input"},
+ {"Right HP Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Left speaker output mixer */
+ {"Left SPK Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left SPK Mixer", "Left DAC2 Switch", "DACL2"},
+ {"Left SPK Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left SPK Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Left SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left SPK Mixer", "INA1 Switch", "INA1 Input"},
+ {"Left SPK Mixer", "INA2 Switch", "INA2 Input"},
+ {"Left SPK Mixer", "INB1 Switch", "INB1 Input"},
+ {"Left SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Right speaker output mixer */
+ {"Right SPK Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right SPK Mixer", "Left DAC2 Switch", "DACL2"},
+ {"Right SPK Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right SPK Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Right SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right SPK Mixer", "INA1 Switch", "INA1 Input"},
+ {"Right SPK Mixer", "INA2 Switch", "INA2 Input"},
+ {"Right SPK Mixer", "INB1 Switch", "INB1 Input"},
+ {"Right SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Earpiece/Receiver output mixer */
+ {"Left REC Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left REC Mixer", "Left DAC2 Switch", "DACL2"},
+ {"Left REC Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left REC Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Left REC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left REC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left REC Mixer", "INA1 Switch", "INA1 Input"},
+ {"Left REC Mixer", "INA2 Switch", "INA2 Input"},
+ {"Left REC Mixer", "INB1 Switch", "INB1 Input"},
+ {"Left REC Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Earpiece/Receiver output mixer */
+ {"Right REC Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right REC Mixer", "Left DAC2 Switch", "DACL2"},
+ {"Right REC Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right REC Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Right REC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right REC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right REC Mixer", "INA1 Switch", "INA1 Input"},
+ {"Right REC Mixer", "INA2 Switch", "INA2 Input"},
+ {"Right REC Mixer", "INB1 Switch", "INB1 Input"},
+ {"Right REC Mixer", "INB2 Switch", "INB2 Input"},
+
+ {"HP Left Out", NULL, "Left HP Mixer"},
+ {"HP Right Out", NULL, "Right HP Mixer"},
+ {"SPK Left Out", NULL, "Left SPK Mixer"},
+ {"SPK Right Out", NULL, "Right SPK Mixer"},
+ {"REC Left Out", NULL, "Left REC Mixer"},
+ {"REC Right Out", NULL, "Right REC Mixer"},
+
+ {"HPL", NULL, "HP Left Out"},
+ {"HPR", NULL, "HP Right Out"},
+ {"SPKL", NULL, "SPK Left Out"},
+ {"SPKR", NULL, "SPK Right Out"},
+ {"RECL", NULL, "REC Left Out"},
+ {"RECR", NULL, "REC Right Out"},
+
+ /* Left ADC input mixer */
+ {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left ADC Mixer", "INA1 Switch", "INA1 Input"},
+ {"Left ADC Mixer", "INA2 Switch", "INA2 Input"},
+ {"Left ADC Mixer", "INB1 Switch", "INB1 Input"},
+ {"Left ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Right ADC input mixer */
+ {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right ADC Mixer", "INA1 Switch", "INA1 Input"},
+ {"Right ADC Mixer", "INA2 Switch", "INA2 Input"},
+ {"Right ADC Mixer", "INB1 Switch", "INB1 Input"},
+ {"Right ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Inputs */
+ {"ADCL", NULL, "Left ADC Mixer"},
+ {"ADCR", NULL, "Right ADC Mixer"},
+ {"INA1 Input", NULL, "INA1"},
+ {"INA2 Input", NULL, "INA2"},
+ {"INB1 Input", NULL, "INB1"},
+ {"INB2 Input", NULL, "INB2"},
+ {"MIC1 Input", NULL, "MIC1"},
+ {"MIC2 Input", NULL, "MIC2"},
+};
+
+/* codec mclk clock divider coefficients */
+static const struct {
+ u32 rate;
+ u8 sr;
+} rate_table[] = {
+ {8000, 0x10},
+ {11025, 0x20},
+ {16000, 0x30},
+ {22050, 0x40},
+ {24000, 0x50},
+ {32000, 0x60},
+ {44100, 0x70},
+ {48000, 0x80},
+ {88200, 0x90},
+ {96000, 0xA0},
+};
+
+static inline int rate_value(int rate, u8 *value)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= rate) {
+ *value = rate_table[i].sr;
+ return 0;
+ }
+ }
+ *value = rate_table[0].sr;
+ return -EINVAL;
+}
+
+static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98088->dai[0];
+
+ rate = params_rate(params);
+
+ switch (params_width(params)) {
+ case 16:
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ M98088_DAI_WS, 0);
+ break;
+ case 24:
+ snd_soc_update_bits(codec, 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);
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, 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)
+ & M98088_DAI_MAS) {
+ if (max98088->sysclk == 0) {
+ dev_err(codec->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,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, 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,
+ M98088_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+ M98088_DAI_DHF, M98088_DAI_DHF);
+
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+ M98088_SHDNRUN);
+
+ return 0;
+}
+
+static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98088->dai[1];
+
+ rate = params_rate(params);
+
+ switch (params_width(params)) {
+ case 16:
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ M98088_DAI_WS, 0);
+ break;
+ case 24:
+ snd_soc_update_bits(codec, 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);
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, 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)
+ & M98088_DAI_MAS) {
+ if (max98088->sysclk == 0) {
+ dev_err(codec->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,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, 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,
+ M98088_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+ M98088_DAI_DHF, M98088_DAI_DHF);
+
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+ M98088_SHDNRUN);
+
+ return 0;
+}
+
+static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+ /* Requested clock frequency is already setup */
+ if (freq == max98088->sysclk)
+ return 0;
+
+ /* 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);
+ } else if ((freq >= 20000000) && (freq < 30000000)) {
+ snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+ } else {
+ dev_err(codec->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,
+ M98088_SHDNRUN, 0);
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+ M98088_SHDNRUN, M98088_SHDNRUN);
+ }
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+ max98088->sysclk = freq;
+ return 0;
+}
+
+static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_cdata *cdata;
+ u8 reg15val;
+ u8 reg14val = 0;
+
+ cdata = &max98088->dai[0];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Slave mode PLL */
+ snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+ 0x80);
+ snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master 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");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ reg14val |= M98088_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ reg14val |= M98088_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg14val |= M98088_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ reg14val |= M98088_DAI_BCI|M98088_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, 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);
+ }
+
+ return 0;
+}
+
+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 max98088_cdata *cdata;
+ u8 reg1Cval = 0;
+
+ cdata = &max98088->dai[1];
+
+ 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,
+ 0x80);
+ snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master 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");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ reg1Cval |= M98088_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ reg1Cval |= M98088_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg1Cval |= M98088_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ reg1Cval |= M98088_DAI_BCI|M98088_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, 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,
+ M98088_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98088_dai1_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int reg;
+
+ if (mute)
+ reg = M98088_DAI_MUTE;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, 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)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int reg;
+
+ if (mute)
+ reg = M98088_DAI_MUTE;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, M98088_REG_31_LVL_DAI2_PLAY,
+ M98088_DAI_MUTE_MASK, reg);
+ return 0;
+}
+
+static int max98088_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ regcache_sync(max98088->regmap);
+
+ snd_soc_update_bits(codec, 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,
+ M98088_MBEN, 0);
+ regcache_mark_dirty(max98088->regmap);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops max98088_dai1_ops = {
+ .set_sysclk = max98088_dai_set_sysclk,
+ .set_fmt = max98088_dai1_set_fmt,
+ .hw_params = max98088_dai1_hw_params,
+ .digital_mute = max98088_dai1_digital_mute,
+};
+
+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,
+};
+
+static struct snd_soc_dai_driver max98088_dai[] = {
+{
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98088_RATES,
+ .formats = MAX98088_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98088_RATES,
+ .formats = MAX98088_FORMATS,
+ },
+ .ops = &max98088_dai1_ops,
+},
+{
+ .name = "Aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98088_RATES,
+ .formats = MAX98088_FORMATS,
+ },
+ .ops = &max98088_dai2_ops,
+}
+};
+
+static const char *eq_mode_name[] = {"EQ1 Mode", "EQ2 Mode"};
+
+static int max98088_get_channel(struct snd_soc_codec *codec, 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;
+
+ /* Shouldn't happen */
+ dev_err(codec->dev, "Bad EQ channel name '%s'\n", name);
+ return -EINVAL;
+}
+
+static void max98088_setup_eq1(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_eq_cfg *coef_set;
+ int best, best_val, save, i, sel, fs;
+ struct max98088_cdata *cdata;
+
+ cdata = &max98088->dai[0];
+
+ if (!pdata || !max98088->eq_textcnt)
+ return;
+
+ /* Find the selected configuration with nearest sample rate */
+ fs = cdata->rate;
+ sel = cdata->eq_sel;
+
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->eq_cfgcnt; i++) {
+ if (strcmp(pdata->eq_cfg[i].name, max98088->eq_texts[sel]) == 0 &&
+ abs(pdata->eq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->eq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->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);
+
+ 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);
+
+ /* Restore the original on/off state */
+ snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+}
+
+static void max98088_setup_eq2(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_eq_cfg *coef_set;
+ int best, best_val, save, i, sel, fs;
+ struct max98088_cdata *cdata;
+
+ cdata = &max98088->dai[1];
+
+ if (!pdata || !max98088->eq_textcnt)
+ return;
+
+ /* Find the selected configuration with nearest sample rate */
+ fs = cdata->rate;
+
+ sel = cdata->eq_sel;
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->eq_cfgcnt; i++) {
+ if (strcmp(pdata->eq_cfg[i].name, max98088->eq_texts[sel]) == 0 &&
+ abs(pdata->eq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->eq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->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);
+
+ 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);
+
+ /* Restore the original on/off state */
+ snd_soc_update_bits(codec, 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 max98088_pdata *pdata = max98088->pdata;
+ int channel = max98088_get_channel(codec, kcontrol->id.name);
+ struct max98088_cdata *cdata;
+ int sel = ucontrol->value.integer.value[0];
+
+ if (channel < 0)
+ return channel;
+
+ cdata = &max98088->dai[channel];
+
+ if (sel >= pdata->eq_cfgcnt)
+ return -EINVAL;
+
+ cdata->eq_sel = sel;
+
+ switch (channel) {
+ case 0:
+ max98088_setup_eq1(codec);
+ break;
+ case 1:
+ max98088_setup_eq2(codec);
+ break;
+ }
+
+ return 0;
+}
+
+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 max98088_cdata *cdata;
+
+ if (channel < 0)
+ return channel;
+
+ cdata = &max98088->dai[channel];
+ ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+ return 0;
+}
+
+static void max98088_handle_eq_pdata(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_eq_cfg *cfg;
+ unsigned int cfgcnt;
+ int i, j;
+ const char **t;
+ int ret;
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT((char *)eq_mode_name[0],
+ max98088->eq_enum,
+ max98088_get_eq_enum,
+ max98088_put_eq_enum),
+ SOC_ENUM_EXT((char *)eq_mode_name[1],
+ max98088->eq_enum,
+ max98088_get_eq_enum,
+ max98088_put_eq_enum),
+ };
+ BUILD_BUG_ON(ARRAY_SIZE(controls) != ARRAY_SIZE(eq_mode_name));
+
+ cfg = pdata->eq_cfg;
+ cfgcnt = pdata->eq_cfgcnt;
+
+ /* Setup an array of texts for the equalizer enum.
+ * This is based on Mark Brown's equalizer driver code.
+ */
+ max98088->eq_textcnt = 0;
+ max98088->eq_texts = NULL;
+ for (i = 0; i < cfgcnt; i++) {
+ for (j = 0; j < max98088->eq_textcnt; j++) {
+ if (strcmp(cfg[i].name, max98088->eq_texts[j]) == 0)
+ break;
+ }
+
+ if (j != max98088->eq_textcnt)
+ continue;
+
+ /* Expand the array */
+ t = krealloc(max98088->eq_texts,
+ sizeof(char *) * (max98088->eq_textcnt + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* Store the new entry */
+ t[max98088->eq_textcnt] = cfg[i].name;
+ max98088->eq_textcnt++;
+ max98088->eq_texts = t;
+ }
+
+ /* Now point the soc_enum to .texts array items */
+ max98088->eq_enum.texts = max98088->eq_texts;
+ max98088->eq_enum.items = max98088->eq_textcnt;
+
+ ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_pdata *pdata = max98088->pdata;
+ u8 regval = 0;
+
+ if (!pdata) {
+ dev_dbg(codec->dev, "No platform data\n");
+ return;
+ }
+
+ /* Configure mic for analog/digital mic mode */
+ if (pdata->digmic_left_mode)
+ regval |= M98088_DIGMIC_L;
+
+ if (pdata->digmic_right_mode)
+ regval |= M98088_DIGMIC_R;
+
+ max98088->digmic = (regval ? 1 : 0);
+
+ snd_soc_write(codec, 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,
+ M98088_REC_LINEMODE_MASK, regval);
+
+ /* Configure equalizers */
+ if (pdata->eq_cfgcnt)
+ max98088_handle_eq_pdata(codec);
+}
+
+static int max98088_probe(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_cdata *cdata;
+ int ret = 0;
+
+ regcache_mark_dirty(max98088->regmap);
+
+ /* initialize private data */
+
+ max98088->sysclk = (unsigned)-1;
+ max98088->eq_textcnt = 0;
+
+ cdata = &max98088->dai[0];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+
+ cdata = &max98088->dai[1];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+
+ max98088->ina_state = 0;
+ max98088->inb_state = 0;
+ max98088->ex_mode = 0;
+ max98088->digmic = 0;
+ max98088->mic1pre = 0;
+ max98088->mic2pre = 0;
+
+ ret = snd_soc_read(codec, M98088_REG_FF_REV_ID);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_access;
+ }
+ dev_info(codec->dev, "revision %c\n", ret - 0x40 + 'A');
+
+ snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+
+ snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+
+ snd_soc_write(codec, 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_write(codec, M98088_REG_16_DAI1_IOCFG,
+ M98088_S1NORMAL|M98088_SDATA);
+
+ snd_soc_write(codec, M98088_REG_1E_DAI2_IOCFG,
+ M98088_S2NORMAL|M98088_SDATA);
+
+ max98088_handle_pdata(codec);
+
+err_access:
+ return ret;
+}
+
+static int max98088_remove(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+ 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 int max98088_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max98088_priv *max98088;
+ int ret;
+
+ 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->devtype = id->driver_data;
+
+ i2c_set_clientdata(i2c, max98088);
+ max98088->pdata = i2c->dev.platform_data;
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_max98088, &max98088_dai[0], 2);
+ return ret;
+}
+
+static int max98088_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id max98088_i2c_id[] = {
+ { "max98088", MAX98088 },
+ { "max98089", MAX98089 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+static struct i2c_driver max98088_i2c_driver = {
+ .driver = {
+ .name = "max98088",
+ .owner = THIS_MODULE,
+ },
+ .probe = max98088_i2c_probe,
+ .remove = max98088_i2c_remove,
+ .id_table = max98088_i2c_id,
+};
+
+module_i2c_driver(max98088_i2c_driver);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98088 driver");
+MODULE_AUTHOR("Peter Hsiang, Jesse Marroquin");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/max98088.h b/kernel/sound/soc/codecs/max98088.h
new file mode 100644
index 000000000..be89a4f4a
--- /dev/null
+++ b/kernel/sound/soc/codecs/max98088.h
@@ -0,0 +1,206 @@
+/*
+ * 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
+#define _MAX98088_H
+
+/*
+ * MAX98088 Registers Definition
+ */
+#define M98088_REG_00_IRQ_STATUS 0x00
+#define M98088_REG_01_MIC_STATUS 0x01
+#define M98088_REG_02_JACK_STAUS 0x02
+#define M98088_REG_03_BATTERY_VOLTAGE 0x03
+#define M98088_REG_0F_IRQ_ENABLE 0x0F
+#define M98088_REG_10_SYS_CLK 0x10
+#define M98088_REG_11_DAI1_CLKMODE 0x11
+#define M98088_REG_12_DAI1_CLKCFG_HI 0x12
+#define M98088_REG_13_DAI1_CLKCFG_LO 0x13
+#define M98088_REG_14_DAI1_FORMAT 0x14
+#define M98088_REG_15_DAI1_CLOCK 0x15
+#define M98088_REG_16_DAI1_IOCFG 0x16
+#define M98088_REG_17_DAI1_TDM 0x17
+#define M98088_REG_18_DAI1_FILTERS 0x18
+#define M98088_REG_19_DAI2_CLKMODE 0x19
+#define M98088_REG_1A_DAI2_CLKCFG_HI 0x1A
+#define M98088_REG_1B_DAI2_CLKCFG_LO 0x1B
+#define M98088_REG_1C_DAI2_FORMAT 0x1C
+#define M98088_REG_1D_DAI2_CLOCK 0x1D
+#define M98088_REG_1E_DAI2_IOCFG 0x1E
+#define M98088_REG_1F_DAI2_TDM 0x1F
+#define M98088_REG_20_DAI2_FILTERS 0x20
+#define M98088_REG_21_SRC 0x21
+#define M98088_REG_22_MIX_DAC 0x22
+#define M98088_REG_23_MIX_ADC_LEFT 0x23
+#define M98088_REG_24_MIX_ADC_RIGHT 0x24
+#define M98088_REG_25_MIX_HP_LEFT 0x25
+#define M98088_REG_26_MIX_HP_RIGHT 0x26
+#define M98088_REG_27_MIX_HP_CNTL 0x27
+#define M98088_REG_28_MIX_REC_LEFT 0x28
+#define M98088_REG_29_MIX_REC_RIGHT 0x29
+#define M98088_REG_2A_MIC_REC_CNTL 0x2A
+#define M98088_REG_2B_MIX_SPK_LEFT 0x2B
+#define M98088_REG_2C_MIX_SPK_RIGHT 0x2C
+#define M98088_REG_2D_MIX_SPK_CNTL 0x2D
+#define M98088_REG_2E_LVL_SIDETONE 0x2E
+#define M98088_REG_2F_LVL_DAI1_PLAY 0x2F
+#define M98088_REG_30_LVL_DAI1_PLAY_EQ 0x30
+#define M98088_REG_31_LVL_DAI2_PLAY 0x31
+#define M98088_REG_32_LVL_DAI2_PLAY_EQ 0x32
+#define M98088_REG_33_LVL_ADC_L 0x33
+#define M98088_REG_34_LVL_ADC_R 0x34
+#define M98088_REG_35_LVL_MIC1 0x35
+#define M98088_REG_36_LVL_MIC2 0x36
+#define M98088_REG_37_LVL_INA 0x37
+#define M98088_REG_38_LVL_INB 0x38
+#define M98088_REG_39_LVL_HP_L 0x39
+#define M98088_REG_3A_LVL_HP_R 0x3A
+#define M98088_REG_3B_LVL_REC_L 0x3B
+#define M98088_REG_3C_LVL_REC_R 0x3C
+#define M98088_REG_3D_LVL_SPK_L 0x3D
+#define M98088_REG_3E_LVL_SPK_R 0x3E
+#define M98088_REG_3F_MICAGC_CFG 0x3F
+#define M98088_REG_40_MICAGC_THRESH 0x40
+#define M98088_REG_41_SPKDHP 0x41
+#define M98088_REG_42_SPKDHP_THRESH 0x42
+#define M98088_REG_43_SPKALC_COMP 0x43
+#define M98088_REG_44_PWRLMT_CFG 0x44
+#define M98088_REG_45_PWRLMT_TIME 0x45
+#define M98088_REG_46_THDLMT_CFG 0x46
+#define M98088_REG_47_CFG_AUDIO_IN 0x47
+#define M98088_REG_48_CFG_MIC 0x48
+#define M98088_REG_49_CFG_LEVEL 0x49
+#define M98088_REG_4A_CFG_BYPASS 0x4A
+#define M98088_REG_4B_CFG_JACKDET 0x4B
+#define M98088_REG_4C_PWR_EN_IN 0x4C
+#define M98088_REG_4D_PWR_EN_OUT 0x4D
+#define M98088_REG_4E_BIAS_CNTL 0x4E
+#define M98088_REG_4F_DAC_BIAS1 0x4F
+#define M98088_REG_50_DAC_BIAS2 0x50
+#define M98088_REG_51_PWR_SYS 0x51
+#define M98088_REG_52_DAI1_EQ_BASE 0x52
+#define M98088_REG_84_DAI2_EQ_BASE 0x84
+#define M98088_REG_B6_DAI1_BIQUAD_BASE 0xB6
+#define M98088_REG_C0_DAI2_BIQUAD_BASE 0xC0
+#define M98088_REG_FF_REV_ID 0xFF
+
+#define M98088_REG_CNT (0xFF+1)
+
+/* MAX98088 Registers Bit Fields */
+
+/* M98088_REG_11_DAI1_CLKMODE, M98088_REG_19_DAI2_CLKMODE */
+ #define M98088_CLKMODE_MASK 0xFF
+
+/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */
+ #define M98088_DAI_MAS (1<<7)
+ #define M98088_DAI_WCI (1<<6)
+ #define M98088_DAI_BCI (1<<5)
+ #define M98088_DAI_DLY (1<<4)
+ #define M98088_DAI_TDM (1<<2)
+ #define M98088_DAI_FSW (1<<1)
+ #define M98088_DAI_WS (1<<0)
+
+/* M98088_REG_15_DAI1_CLOCK, M98088_REG_1D_DAI2_CLOCK */
+ #define M98088_DAI_BSEL64 (1<<0)
+ #define M98088_DAI_OSR64 (1<<6)
+
+/* M98088_REG_16_DAI1_IOCFG, M98088_REG_1E_DAI2_IOCFG */
+ #define M98088_S1NORMAL (1<<6)
+ #define M98088_S2NORMAL (2<<6)
+ #define M98088_SDATA (3<<0)
+
+/* M98088_REG_18_DAI1_FILTERS, M98088_REG_20_DAI2_FILTERS */
+ #define M98088_DAI_DHF (1<<3)
+
+/* M98088_REG_22_MIX_DAC */
+ #define M98088_DAI1L_TO_DACL (1<<7)
+ #define M98088_DAI1R_TO_DACL (1<<6)
+ #define M98088_DAI2L_TO_DACL (1<<5)
+ #define M98088_DAI2R_TO_DACL (1<<4)
+ #define M98088_DAI1L_TO_DACR (1<<3)
+ #define M98088_DAI1R_TO_DACR (1<<2)
+ #define M98088_DAI2L_TO_DACR (1<<1)
+ #define M98088_DAI2R_TO_DACR (1<<0)
+
+/* M98088_REG_2A_MIC_REC_CNTL */
+ #define M98088_REC_LINEMODE (1<<7)
+ #define M98088_REC_LINEMODE_MASK (1<<7)
+
+/* M98088_REG_2D_MIX_SPK_CNTL */
+ #define M98088_MIX_SPKR_GAIN_MASK (3<<2)
+ #define M98088_MIX_SPKR_GAIN_SHIFT 2
+ #define M98088_MIX_SPKL_GAIN_MASK (3<<0)
+ #define M98088_MIX_SPKL_GAIN_SHIFT 0
+
+/* M98088_REG_2F_LVL_DAI1_PLAY, M98088_REG_31_LVL_DAI2_PLAY */
+ #define M98088_DAI_MUTE (1<<7)
+ #define M98088_DAI_MUTE_MASK (1<<7)
+ #define M98088_DAI_VOICE_GAIN_MASK (3<<4)
+ #define M98088_DAI_ATTENUATION_MASK (0xF<<0)
+ #define M98088_DAI_ATTENUATION_SHIFT 0
+
+/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
+ #define M98088_MICPRE_MASK (3<<5)
+ #define M98088_MICPRE_SHIFT 5
+
+/* M98088_REG_3A_LVL_HP_R */
+ #define M98088_HP_MUTE (1<<7)
+
+/* M98088_REG_3C_LVL_REC_R */
+ #define M98088_REC_MUTE (1<<7)
+
+/* M98088_REG_3E_LVL_SPK_R */
+ #define M98088_SP_MUTE (1<<7)
+
+/* M98088_REG_48_CFG_MIC */
+ #define M98088_EXTMIC_MASK (3<<0)
+ #define M98088_DIGMIC_L (1<<5)
+ #define M98088_DIGMIC_R (1<<4)
+
+/* M98088_REG_49_CFG_LEVEL */
+ #define M98088_VSEN (1<<6)
+ #define M98088_ZDEN (1<<5)
+ #define M98088_EQ2EN (1<<1)
+ #define M98088_EQ1EN (1<<0)
+
+/* M98088_REG_4C_PWR_EN_IN */
+ #define M98088_INAEN (1<<7)
+ #define M98088_INBEN (1<<6)
+ #define M98088_MBEN (1<<3)
+ #define M98088_ADLEN (1<<1)
+ #define M98088_ADREN (1<<0)
+
+/* M98088_REG_4D_PWR_EN_OUT */
+ #define M98088_HPLEN (1<<7)
+ #define M98088_HPREN (1<<6)
+ #define M98088_HPEN ((1<<7)|(1<<6))
+ #define M98088_SPLEN (1<<5)
+ #define M98088_SPREN (1<<4)
+ #define M98088_RECEN (1<<3)
+ #define M98088_DALEN (1<<1)
+ #define M98088_DAREN (1<<0)
+
+/* M98088_REG_51_PWR_SYS */
+ #define M98088_SHDNRUN (1<<7)
+ #define M98088_PERFMODE (1<<3)
+ #define M98088_HPPLYBACK (1<<2)
+ #define M98088_PWRSV8K (1<<1)
+ #define M98088_PWRSV (1<<0)
+
+/* Line inputs */
+#define LINE_INA 0
+#define LINE_INB 1
+
+#define M98088_COEFS_PER_BAND 5
+
+#define M98088_BYTE1(w) ((w >> 8) & 0xff)
+#define M98088_BYTE0(w) (w & 0xff)
+
+#endif
diff --git a/kernel/sound/soc/codecs/max98090.c b/kernel/sound/soc/codecs/max98090.c
new file mode 100644
index 000000000..3e33ef2ac
--- /dev/null
+++ b/kernel/sound/soc/codecs/max98090.c
@@ -0,0 +1,2724 @@
+/*
+ * 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>
+#include <linux/i2c.h>
+#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>
+#include <linux/clk.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/max98090.h>
+#include "max98090.h"
+
+/* Allows for sparsely populated register maps */
+static struct reg_default max98090_reg[] = {
+ { 0x00, 0x00 }, /* 00 Software Reset */
+ { 0x03, 0x04 }, /* 03 Interrupt Masks */
+ { 0x04, 0x00 }, /* 04 System Clock Quick */
+ { 0x05, 0x00 }, /* 05 Sample Rate Quick */
+ { 0x06, 0x00 }, /* 06 DAI Interface Quick */
+ { 0x07, 0x00 }, /* 07 DAC Path Quick */
+ { 0x08, 0x00 }, /* 08 Mic/Direct to ADC Quick */
+ { 0x09, 0x00 }, /* 09 Line to ADC Quick */
+ { 0x0A, 0x00 }, /* 0A Analog Mic Loop Quick */
+ { 0x0B, 0x00 }, /* 0B Analog Line Loop Quick */
+ { 0x0C, 0x00 }, /* 0C Reserved */
+ { 0x0D, 0x00 }, /* 0D Input Config */
+ { 0x0E, 0x1B }, /* 0E Line Input Level */
+ { 0x0F, 0x00 }, /* 0F Line Config */
+
+ { 0x10, 0x14 }, /* 10 Mic1 Input Level */
+ { 0x11, 0x14 }, /* 11 Mic2 Input Level */
+ { 0x12, 0x00 }, /* 12 Mic Bias Voltage */
+ { 0x13, 0x00 }, /* 13 Digital Mic Config */
+ { 0x14, 0x00 }, /* 14 Digital Mic Mode */
+ { 0x15, 0x00 }, /* 15 Left ADC Mixer */
+ { 0x16, 0x00 }, /* 16 Right ADC Mixer */
+ { 0x17, 0x03 }, /* 17 Left ADC Level */
+ { 0x18, 0x03 }, /* 18 Right ADC Level */
+ { 0x19, 0x00 }, /* 19 ADC Biquad Level */
+ { 0x1A, 0x00 }, /* 1A ADC Sidetone */
+ { 0x1B, 0x00 }, /* 1B System Clock */
+ { 0x1C, 0x00 }, /* 1C Clock Mode */
+ { 0x1D, 0x00 }, /* 1D Any Clock 1 */
+ { 0x1E, 0x00 }, /* 1E Any Clock 2 */
+ { 0x1F, 0x00 }, /* 1F Any Clock 3 */
+
+ { 0x20, 0x00 }, /* 20 Any Clock 4 */
+ { 0x21, 0x00 }, /* 21 Master Mode */
+ { 0x22, 0x00 }, /* 22 Interface Format */
+ { 0x23, 0x00 }, /* 23 TDM Format 1*/
+ { 0x24, 0x00 }, /* 24 TDM Format 2*/
+ { 0x25, 0x00 }, /* 25 I/O Configuration */
+ { 0x26, 0x80 }, /* 26 Filter Config */
+ { 0x27, 0x00 }, /* 27 DAI Playback Level */
+ { 0x28, 0x00 }, /* 28 EQ Playback Level */
+ { 0x29, 0x00 }, /* 29 Left HP Mixer */
+ { 0x2A, 0x00 }, /* 2A Right HP Mixer */
+ { 0x2B, 0x00 }, /* 2B HP Control */
+ { 0x2C, 0x1A }, /* 2C Left HP Volume */
+ { 0x2D, 0x1A }, /* 2D Right HP Volume */
+ { 0x2E, 0x00 }, /* 2E Left Spk Mixer */
+ { 0x2F, 0x00 }, /* 2F Right Spk Mixer */
+
+ { 0x30, 0x00 }, /* 30 Spk Control */
+ { 0x31, 0x2C }, /* 31 Left Spk Volume */
+ { 0x32, 0x2C }, /* 32 Right Spk Volume */
+ { 0x33, 0x00 }, /* 33 ALC Timing */
+ { 0x34, 0x00 }, /* 34 ALC Compressor */
+ { 0x35, 0x00 }, /* 35 ALC Expander */
+ { 0x36, 0x00 }, /* 36 ALC Gain */
+ { 0x37, 0x00 }, /* 37 Rcv/Line OutL Mixer */
+ { 0x38, 0x00 }, /* 38 Rcv/Line OutL Control */
+ { 0x39, 0x15 }, /* 39 Rcv/Line OutL Volume */
+ { 0x3A, 0x00 }, /* 3A Line OutR Mixer */
+ { 0x3B, 0x00 }, /* 3B Line OutR Control */
+ { 0x3C, 0x15 }, /* 3C Line OutR Volume */
+ { 0x3D, 0x00 }, /* 3D Jack Detect */
+ { 0x3E, 0x00 }, /* 3E Input Enable */
+ { 0x3F, 0x00 }, /* 3F Output Enable */
+
+ { 0x40, 0x00 }, /* 40 Level Control */
+ { 0x41, 0x00 }, /* 41 DSP Filter Enable */
+ { 0x42, 0x00 }, /* 42 Bias Control */
+ { 0x43, 0x00 }, /* 43 DAC Control */
+ { 0x44, 0x06 }, /* 44 ADC Control */
+ { 0x45, 0x00 }, /* 45 Device Shutdown */
+ { 0x46, 0x00 }, /* 46 Equalizer Band 1 Coefficient B0 */
+ { 0x47, 0x00 }, /* 47 Equalizer Band 1 Coefficient B0 */
+ { 0x48, 0x00 }, /* 48 Equalizer Band 1 Coefficient B0 */
+ { 0x49, 0x00 }, /* 49 Equalizer Band 1 Coefficient B1 */
+ { 0x4A, 0x00 }, /* 4A Equalizer Band 1 Coefficient B1 */
+ { 0x4B, 0x00 }, /* 4B Equalizer Band 1 Coefficient B1 */
+ { 0x4C, 0x00 }, /* 4C Equalizer Band 1 Coefficient B2 */
+ { 0x4D, 0x00 }, /* 4D Equalizer Band 1 Coefficient B2 */
+ { 0x4E, 0x00 }, /* 4E Equalizer Band 1 Coefficient B2 */
+ { 0x4F, 0x00 }, /* 4F Equalizer Band 1 Coefficient A1 */
+
+ { 0x50, 0x00 }, /* 50 Equalizer Band 1 Coefficient A1 */
+ { 0x51, 0x00 }, /* 51 Equalizer Band 1 Coefficient A1 */
+ { 0x52, 0x00 }, /* 52 Equalizer Band 1 Coefficient A2 */
+ { 0x53, 0x00 }, /* 53 Equalizer Band 1 Coefficient A2 */
+ { 0x54, 0x00 }, /* 54 Equalizer Band 1 Coefficient A2 */
+ { 0x55, 0x00 }, /* 55 Equalizer Band 2 Coefficient B0 */
+ { 0x56, 0x00 }, /* 56 Equalizer Band 2 Coefficient B0 */
+ { 0x57, 0x00 }, /* 57 Equalizer Band 2 Coefficient B0 */
+ { 0x58, 0x00 }, /* 58 Equalizer Band 2 Coefficient B1 */
+ { 0x59, 0x00 }, /* 59 Equalizer Band 2 Coefficient B1 */
+ { 0x5A, 0x00 }, /* 5A Equalizer Band 2 Coefficient B1 */
+ { 0x5B, 0x00 }, /* 5B Equalizer Band 2 Coefficient B2 */
+ { 0x5C, 0x00 }, /* 5C Equalizer Band 2 Coefficient B2 */
+ { 0x5D, 0x00 }, /* 5D Equalizer Band 2 Coefficient B2 */
+ { 0x5E, 0x00 }, /* 5E Equalizer Band 2 Coefficient A1 */
+ { 0x5F, 0x00 }, /* 5F Equalizer Band 2 Coefficient A1 */
+
+ { 0x60, 0x00 }, /* 60 Equalizer Band 2 Coefficient A1 */
+ { 0x61, 0x00 }, /* 61 Equalizer Band 2 Coefficient A2 */
+ { 0x62, 0x00 }, /* 62 Equalizer Band 2 Coefficient A2 */
+ { 0x63, 0x00 }, /* 63 Equalizer Band 2 Coefficient A2 */
+ { 0x64, 0x00 }, /* 64 Equalizer Band 3 Coefficient B0 */
+ { 0x65, 0x00 }, /* 65 Equalizer Band 3 Coefficient B0 */
+ { 0x66, 0x00 }, /* 66 Equalizer Band 3 Coefficient B0 */
+ { 0x67, 0x00 }, /* 67 Equalizer Band 3 Coefficient B1 */
+ { 0x68, 0x00 }, /* 68 Equalizer Band 3 Coefficient B1 */
+ { 0x69, 0x00 }, /* 69 Equalizer Band 3 Coefficient B1 */
+ { 0x6A, 0x00 }, /* 6A Equalizer Band 3 Coefficient B2 */
+ { 0x6B, 0x00 }, /* 6B Equalizer Band 3 Coefficient B2 */
+ { 0x6C, 0x00 }, /* 6C Equalizer Band 3 Coefficient B2 */
+ { 0x6D, 0x00 }, /* 6D Equalizer Band 3 Coefficient A1 */
+ { 0x6E, 0x00 }, /* 6E Equalizer Band 3 Coefficient A1 */
+ { 0x6F, 0x00 }, /* 6F Equalizer Band 3 Coefficient A1 */
+
+ { 0x70, 0x00 }, /* 70 Equalizer Band 3 Coefficient A2 */
+ { 0x71, 0x00 }, /* 71 Equalizer Band 3 Coefficient A2 */
+ { 0x72, 0x00 }, /* 72 Equalizer Band 3 Coefficient A2 */
+ { 0x73, 0x00 }, /* 73 Equalizer Band 4 Coefficient B0 */
+ { 0x74, 0x00 }, /* 74 Equalizer Band 4 Coefficient B0 */
+ { 0x75, 0x00 }, /* 75 Equalizer Band 4 Coefficient B0 */
+ { 0x76, 0x00 }, /* 76 Equalizer Band 4 Coefficient B1 */
+ { 0x77, 0x00 }, /* 77 Equalizer Band 4 Coefficient B1 */
+ { 0x78, 0x00 }, /* 78 Equalizer Band 4 Coefficient B1 */
+ { 0x79, 0x00 }, /* 79 Equalizer Band 4 Coefficient B2 */
+ { 0x7A, 0x00 }, /* 7A Equalizer Band 4 Coefficient B2 */
+ { 0x7B, 0x00 }, /* 7B Equalizer Band 4 Coefficient B2 */
+ { 0x7C, 0x00 }, /* 7C Equalizer Band 4 Coefficient A1 */
+ { 0x7D, 0x00 }, /* 7D Equalizer Band 4 Coefficient A1 */
+ { 0x7E, 0x00 }, /* 7E Equalizer Band 4 Coefficient A1 */
+ { 0x7F, 0x00 }, /* 7F Equalizer Band 4 Coefficient A2 */
+
+ { 0x80, 0x00 }, /* 80 Equalizer Band 4 Coefficient A2 */
+ { 0x81, 0x00 }, /* 81 Equalizer Band 4 Coefficient A2 */
+ { 0x82, 0x00 }, /* 82 Equalizer Band 5 Coefficient B0 */
+ { 0x83, 0x00 }, /* 83 Equalizer Band 5 Coefficient B0 */
+ { 0x84, 0x00 }, /* 84 Equalizer Band 5 Coefficient B0 */
+ { 0x85, 0x00 }, /* 85 Equalizer Band 5 Coefficient B1 */
+ { 0x86, 0x00 }, /* 86 Equalizer Band 5 Coefficient B1 */
+ { 0x87, 0x00 }, /* 87 Equalizer Band 5 Coefficient B1 */
+ { 0x88, 0x00 }, /* 88 Equalizer Band 5 Coefficient B2 */
+ { 0x89, 0x00 }, /* 89 Equalizer Band 5 Coefficient B2 */
+ { 0x8A, 0x00 }, /* 8A Equalizer Band 5 Coefficient B2 */
+ { 0x8B, 0x00 }, /* 8B Equalizer Band 5 Coefficient A1 */
+ { 0x8C, 0x00 }, /* 8C Equalizer Band 5 Coefficient A1 */
+ { 0x8D, 0x00 }, /* 8D Equalizer Band 5 Coefficient A1 */
+ { 0x8E, 0x00 }, /* 8E Equalizer Band 5 Coefficient A2 */
+ { 0x8F, 0x00 }, /* 8F Equalizer Band 5 Coefficient A2 */
+
+ { 0x90, 0x00 }, /* 90 Equalizer Band 5 Coefficient A2 */
+ { 0x91, 0x00 }, /* 91 Equalizer Band 6 Coefficient B0 */
+ { 0x92, 0x00 }, /* 92 Equalizer Band 6 Coefficient B0 */
+ { 0x93, 0x00 }, /* 93 Equalizer Band 6 Coefficient B0 */
+ { 0x94, 0x00 }, /* 94 Equalizer Band 6 Coefficient B1 */
+ { 0x95, 0x00 }, /* 95 Equalizer Band 6 Coefficient B1 */
+ { 0x96, 0x00 }, /* 96 Equalizer Band 6 Coefficient B1 */
+ { 0x97, 0x00 }, /* 97 Equalizer Band 6 Coefficient B2 */
+ { 0x98, 0x00 }, /* 98 Equalizer Band 6 Coefficient B2 */
+ { 0x99, 0x00 }, /* 99 Equalizer Band 6 Coefficient B2 */
+ { 0x9A, 0x00 }, /* 9A Equalizer Band 6 Coefficient A1 */
+ { 0x9B, 0x00 }, /* 9B Equalizer Band 6 Coefficient A1 */
+ { 0x9C, 0x00 }, /* 9C Equalizer Band 6 Coefficient A1 */
+ { 0x9D, 0x00 }, /* 9D Equalizer Band 6 Coefficient A2 */
+ { 0x9E, 0x00 }, /* 9E Equalizer Band 6 Coefficient A2 */
+ { 0x9F, 0x00 }, /* 9F Equalizer Band 6 Coefficient A2 */
+
+ { 0xA0, 0x00 }, /* A0 Equalizer Band 7 Coefficient B0 */
+ { 0xA1, 0x00 }, /* A1 Equalizer Band 7 Coefficient B0 */
+ { 0xA2, 0x00 }, /* A2 Equalizer Band 7 Coefficient B0 */
+ { 0xA3, 0x00 }, /* A3 Equalizer Band 7 Coefficient B1 */
+ { 0xA4, 0x00 }, /* A4 Equalizer Band 7 Coefficient B1 */
+ { 0xA5, 0x00 }, /* A5 Equalizer Band 7 Coefficient B1 */
+ { 0xA6, 0x00 }, /* A6 Equalizer Band 7 Coefficient B2 */
+ { 0xA7, 0x00 }, /* A7 Equalizer Band 7 Coefficient B2 */
+ { 0xA8, 0x00 }, /* A8 Equalizer Band 7 Coefficient B2 */
+ { 0xA9, 0x00 }, /* A9 Equalizer Band 7 Coefficient A1 */
+ { 0xAA, 0x00 }, /* AA Equalizer Band 7 Coefficient A1 */
+ { 0xAB, 0x00 }, /* AB Equalizer Band 7 Coefficient A1 */
+ { 0xAC, 0x00 }, /* AC Equalizer Band 7 Coefficient A2 */
+ { 0xAD, 0x00 }, /* AD Equalizer Band 7 Coefficient A2 */
+ { 0xAE, 0x00 }, /* AE Equalizer Band 7 Coefficient A2 */
+ { 0xAF, 0x00 }, /* AF ADC Biquad Coefficient B0 */
+
+ { 0xB0, 0x00 }, /* B0 ADC Biquad Coefficient B0 */
+ { 0xB1, 0x00 }, /* B1 ADC Biquad Coefficient B0 */
+ { 0xB2, 0x00 }, /* B2 ADC Biquad Coefficient B1 */
+ { 0xB3, 0x00 }, /* B3 ADC Biquad Coefficient B1 */
+ { 0xB4, 0x00 }, /* B4 ADC Biquad Coefficient B1 */
+ { 0xB5, 0x00 }, /* B5 ADC Biquad Coefficient B2 */
+ { 0xB6, 0x00 }, /* B6 ADC Biquad Coefficient B2 */
+ { 0xB7, 0x00 }, /* B7 ADC Biquad Coefficient B2 */
+ { 0xB8, 0x00 }, /* B8 ADC Biquad Coefficient A1 */
+ { 0xB9, 0x00 }, /* B9 ADC Biquad Coefficient A1 */
+ { 0xBA, 0x00 }, /* BA ADC Biquad Coefficient A1 */
+ { 0xBB, 0x00 }, /* BB ADC Biquad Coefficient A2 */
+ { 0xBC, 0x00 }, /* BC ADC Biquad Coefficient A2 */
+ { 0xBD, 0x00 }, /* BD ADC Biquad Coefficient A2 */
+ { 0xBE, 0x00 }, /* BE Digital Mic 3 Volume */
+ { 0xBF, 0x00 }, /* BF Digital Mic 4 Volume */
+
+ { 0xC0, 0x00 }, /* C0 Digital Mic 34 Biquad Pre Atten */
+ { 0xC1, 0x00 }, /* C1 Record TDM Slot */
+ { 0xC2, 0x00 }, /* C2 Sample Rate */
+ { 0xC3, 0x00 }, /* C3 Digital Mic 34 Biquad Coefficient C3 */
+ { 0xC4, 0x00 }, /* C4 Digital Mic 34 Biquad Coefficient C4 */
+ { 0xC5, 0x00 }, /* C5 Digital Mic 34 Biquad Coefficient C5 */
+ { 0xC6, 0x00 }, /* C6 Digital Mic 34 Biquad Coefficient C6 */
+ { 0xC7, 0x00 }, /* C7 Digital Mic 34 Biquad Coefficient C7 */
+ { 0xC8, 0x00 }, /* C8 Digital Mic 34 Biquad Coefficient C8 */
+ { 0xC9, 0x00 }, /* C9 Digital Mic 34 Biquad Coefficient C9 */
+ { 0xCA, 0x00 }, /* CA Digital Mic 34 Biquad Coefficient CA */
+ { 0xCB, 0x00 }, /* CB Digital Mic 34 Biquad Coefficient CB */
+ { 0xCC, 0x00 }, /* CC Digital Mic 34 Biquad Coefficient CC */
+ { 0xCD, 0x00 }, /* CD Digital Mic 34 Biquad Coefficient CD */
+ { 0xCE, 0x00 }, /* CE Digital Mic 34 Biquad Coefficient CE */
+ { 0xCF, 0x00 }, /* CF Digital Mic 34 Biquad Coefficient CF */
+
+ { 0xD0, 0x00 }, /* D0 Digital Mic 34 Biquad Coefficient D0 */
+ { 0xD1, 0x00 }, /* D1 Digital Mic 34 Biquad Coefficient D1 */
+};
+
+static bool max98090_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case M98090_REG_SOFTWARE_RESET:
+ case M98090_REG_DEVICE_STATUS:
+ case M98090_REG_JACK_STATUS:
+ case M98090_REG_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98090_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case M98090_REG_DEVICE_STATUS:
+ 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_REVISION_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int max98090_reset(struct max98090_priv *max98090)
+{
+ int ret;
+
+ /* Reset the codec by writing to this write-only reset register */
+ ret = regmap_write(max98090->regmap, M98090_REG_SOFTWARE_RESET,
+ M98090_SWRESET_MASK);
+ if (ret < 0) {
+ dev_err(max98090->codec->dev,
+ "Failed to reset codec: %d\n", ret);
+ return ret;
+ }
+
+ msleep(20);
+ return ret;
+}
+
+static const unsigned int max98090_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_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),
+ 0, 3, TLV_DB_SCALE_ITEM(-600, 300, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0),
+};
+
+static const DECLARE_TLV_DB_SCALE(max98090_avg_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(max98090_av_tlv, -1200, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(max98090_dvg_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(max98090_dv_tlv, -1500, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(max98090_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),
+ 0, 1, TLV_DB_SCALE_ITEM(-1200, 250, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(-600, 600, 0),
+};
+
+static const unsigned int max98090_hp_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0),
+};
+
+static const unsigned int max98090_spk_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 4, TLV_DB_SCALE_ITEM(-4800, 400, 0),
+ 5, 10, TLV_DB_SCALE_ITEM(-2900, 300, 0),
+ 11, 14, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 15, 29, TLV_DB_SCALE_ITEM(-500, 100, 0),
+ 30, 39, TLV_DB_SCALE_ITEM(950, 50, 0),
+};
+
+static const unsigned int max98090_rcv_lout_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0),
+};
+
+static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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 *select;
+
+ switch (mc->reg) {
+ case M98090_REG_MIC1_INPUT_LEVEL:
+ select = &(max98090->pa1en);
+ break;
+ case M98090_REG_MIC2_INPUT_LEVEL:
+ select = &(max98090->pa2en);
+ break;
+ case M98090_REG_ADC_SIDETONE:
+ select = &(max98090->sidetone);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = (val >> mc->shift) & mask;
+
+ if (val >= 1) {
+ /* If on, return the volume */
+ val = val - 1;
+ *select = val;
+ } else {
+ /* If off, return last stored value */
+ val = *select;
+ }
+
+ ucontrol->value.integer.value[0] = val;
+ return 0;
+}
+
+static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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);
+ unsigned int *select;
+
+ switch (mc->reg) {
+ case M98090_REG_MIC1_INPUT_LEVEL:
+ select = &(max98090->pa1en);
+ break;
+ case M98090_REG_MIC2_INPUT_LEVEL:
+ select = &(max98090->pa2en);
+ break;
+ case M98090_REG_ADC_SIDETONE:
+ select = &(max98090->sidetone);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = (val >> mc->shift) & mask;
+
+ *select = sel;
+
+ /* Setting a volume is only valid if it is already On */
+ if (val >= 1) {
+ sel = sel + 1;
+ } else {
+ /* Write what was already there */
+ sel = val;
+ }
+
+ snd_soc_update_bits(codec, mc->reg,
+ mask << mc->shift,
+ sel << mc->shift);
+
+ return 0;
+}
+
+static const char *max98090_perf_pwr_text[] =
+ { "High Performance", "Low Power" };
+static const char *max98090_pwr_perf_text[] =
+ { "Low Power", "High Performance" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_vcmbandgap_enum,
+ M98090_REG_BIAS_CONTROL,
+ M98090_VCM_MODE_SHIFT,
+ max98090_pwr_perf_text);
+
+static const char *max98090_osr128_text[] = { "64*fs", "128*fs" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_osr128_enum,
+ M98090_REG_ADC_CONTROL,
+ M98090_OSR128_SHIFT,
+ max98090_osr128_text);
+
+static const char *max98090_mode_text[] = { "Voice", "Music" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_mode_enum,
+ M98090_REG_FILTER_CONFIG,
+ M98090_MODE_SHIFT,
+ max98090_mode_text);
+
+static SOC_ENUM_SINGLE_DECL(max98090_filter_dmic34mode_enum,
+ M98090_REG_FILTER_CONFIG,
+ M98090_FLT_DMIC34MODE_SHIFT,
+ max98090_mode_text);
+
+static const char *max98090_drcatk_text[] =
+ { "0.5ms", "1ms", "5ms", "10ms", "25ms", "50ms", "100ms", "200ms" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_drcatk_enum,
+ M98090_REG_DRC_TIMING,
+ M98090_DRCATK_SHIFT,
+ max98090_drcatk_text);
+
+static const char *max98090_drcrls_text[] =
+ { "8s", "4s", "2s", "1s", "0.5s", "0.25s", "0.125s", "0.0625s" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_drcrls_enum,
+ M98090_REG_DRC_TIMING,
+ M98090_DRCRLS_SHIFT,
+ max98090_drcrls_text);
+
+static const char *max98090_alccmp_text[] =
+ { "1:1", "1:1.5", "1:2", "1:4", "1:INF" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_alccmp_enum,
+ M98090_REG_DRC_COMPRESSOR,
+ M98090_DRCCMP_SHIFT,
+ max98090_alccmp_text);
+
+static const char *max98090_drcexp_text[] = { "1:1", "2:1", "3:1" };
+
+static SOC_ENUM_SINGLE_DECL(max98090_drcexp_enum,
+ M98090_REG_DRC_EXPANDER,
+ M98090_DRCEXP_SHIFT,
+ max98090_drcexp_text);
+
+static SOC_ENUM_SINGLE_DECL(max98090_dac_perfmode_enum,
+ M98090_REG_DAC_CONTROL,
+ M98090_PERFMODE_SHIFT,
+ max98090_perf_pwr_text);
+
+static SOC_ENUM_SINGLE_DECL(max98090_dachp_enum,
+ M98090_REG_DAC_CONTROL,
+ M98090_DACHP_SHIFT,
+ max98090_pwr_perf_text);
+
+static SOC_ENUM_SINGLE_DECL(max98090_adchp_enum,
+ M98090_REG_ADC_CONTROL,
+ M98090_ADCHP_SHIFT,
+ max98090_pwr_perf_text);
+
+static const struct snd_kcontrol_new max98090_snd_controls[] = {
+ SOC_ENUM("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum),
+
+ SOC_SINGLE("DMIC MIC Comp Filter Config", M98090_REG_DIGITAL_MIC_CONFIG,
+ M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0),
+
+ SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
+ M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT,
+ M98090_MIC_PA1EN_NUM - 1, 0, max98090_get_enab_tlv,
+ max98090_put_enab_tlv, max98090_micboost_tlv),
+
+ SOC_SINGLE_EXT_TLV("MIC2 Boost Volume",
+ M98090_REG_MIC2_INPUT_LEVEL, M98090_MIC_PA2EN_SHIFT,
+ M98090_MIC_PA2EN_NUM - 1, 0, max98090_get_enab_tlv,
+ max98090_put_enab_tlv, max98090_micboost_tlv),
+
+ SOC_SINGLE_TLV("MIC1 Volume", M98090_REG_MIC1_INPUT_LEVEL,
+ M98090_MIC_PGAM1_SHIFT, M98090_MIC_PGAM1_NUM - 1, 1,
+ max98090_mic_tlv),
+
+ SOC_SINGLE_TLV("MIC2 Volume", M98090_REG_MIC2_INPUT_LEVEL,
+ M98090_MIC_PGAM2_SHIFT, M98090_MIC_PGAM2_NUM - 1, 1,
+ max98090_mic_tlv),
+
+ SOC_SINGLE_RANGE_TLV("LINEA Single Ended Volume",
+ M98090_REG_LINE_INPUT_LEVEL, M98090_MIXG135_SHIFT, 0,
+ M98090_MIXG135_NUM - 1, 1, max98090_line_single_ended_tlv),
+
+ SOC_SINGLE_RANGE_TLV("LINEB Single Ended Volume",
+ M98090_REG_LINE_INPUT_LEVEL, M98090_MIXG246_SHIFT, 0,
+ M98090_MIXG246_NUM - 1, 1, max98090_line_single_ended_tlv),
+
+ SOC_SINGLE_RANGE_TLV("LINEA Volume", M98090_REG_LINE_INPUT_LEVEL,
+ M98090_LINAPGA_SHIFT, 0, M98090_LINAPGA_NUM - 1, 1,
+ max98090_line_tlv),
+
+ SOC_SINGLE_RANGE_TLV("LINEB Volume", M98090_REG_LINE_INPUT_LEVEL,
+ M98090_LINBPGA_SHIFT, 0, M98090_LINBPGA_NUM - 1, 1,
+ max98090_line_tlv),
+
+ SOC_SINGLE("LINEA Ext Resistor Gain Mode", M98090_REG_INPUT_MODE,
+ M98090_EXTBUFA_SHIFT, M98090_EXTBUFA_NUM - 1, 0),
+ SOC_SINGLE("LINEB Ext Resistor Gain Mode", M98090_REG_INPUT_MODE,
+ M98090_EXTBUFB_SHIFT, M98090_EXTBUFB_NUM - 1, 0),
+
+ SOC_SINGLE_TLV("ADCL Boost Volume", M98090_REG_LEFT_ADC_LEVEL,
+ M98090_AVLG_SHIFT, M98090_AVLG_NUM - 1, 0,
+ max98090_avg_tlv),
+ SOC_SINGLE_TLV("ADCR Boost Volume", M98090_REG_RIGHT_ADC_LEVEL,
+ M98090_AVRG_SHIFT, M98090_AVLG_NUM - 1, 0,
+ max98090_avg_tlv),
+
+ SOC_SINGLE_TLV("ADCL Volume", M98090_REG_LEFT_ADC_LEVEL,
+ M98090_AVL_SHIFT, M98090_AVL_NUM - 1, 1,
+ max98090_av_tlv),
+ SOC_SINGLE_TLV("ADCR Volume", M98090_REG_RIGHT_ADC_LEVEL,
+ M98090_AVR_SHIFT, M98090_AVR_NUM - 1, 1,
+ max98090_av_tlv),
+
+ SOC_ENUM("ADC Oversampling Rate", max98090_osr128_enum),
+ SOC_SINGLE("ADC Quantizer Dither", M98090_REG_ADC_CONTROL,
+ M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0),
+ SOC_ENUM("ADC High Performance Mode", max98090_adchp_enum),
+
+ SOC_SINGLE("DAC Mono Mode", M98090_REG_IO_CONFIGURATION,
+ M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0),
+ SOC_SINGLE("SDIN Mode", M98090_REG_IO_CONFIGURATION,
+ M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0),
+ SOC_SINGLE("SDOUT Mode", M98090_REG_IO_CONFIGURATION,
+ M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0),
+ SOC_SINGLE("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION,
+ M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1),
+ SOC_ENUM("Filter Mode", max98090_mode_enum),
+ SOC_SINGLE("Record Path DC Blocking", M98090_REG_FILTER_CONFIG,
+ M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0),
+ SOC_SINGLE("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG,
+ M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0),
+ SOC_SINGLE_TLV("Digital BQ Volume", M98090_REG_ADC_BIQUAD_LEVEL,
+ M98090_AVBQ_SHIFT, M98090_AVBQ_NUM - 1, 1, max98090_dv_tlv),
+ SOC_SINGLE_EXT_TLV("Digital Sidetone Volume",
+ M98090_REG_ADC_SIDETONE, M98090_DVST_SHIFT,
+ M98090_DVST_NUM - 1, 1, max98090_get_enab_tlv,
+ max98090_put_enab_tlv, max98090_sdg_tlv),
+ SOC_SINGLE_TLV("Digital Coarse Volume", M98090_REG_DAI_PLAYBACK_LEVEL,
+ M98090_DVG_SHIFT, M98090_DVG_NUM - 1, 0,
+ max98090_dvg_tlv),
+ SOC_SINGLE_TLV("Digital Volume", M98090_REG_DAI_PLAYBACK_LEVEL,
+ M98090_DV_SHIFT, M98090_DV_NUM - 1, 1,
+ max98090_dv_tlv),
+ SND_SOC_BYTES("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105),
+ SOC_SINGLE("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
+ M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0),
+ SOC_SINGLE("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
+ M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0),
+ SOC_SINGLE("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
+ M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0),
+ SOC_SINGLE("Digital EQ Clipping Detection", M98090_REG_DAI_PLAYBACK_LEVEL_EQ,
+ M98090_EQCLPN_SHIFT, M98090_EQCLPN_NUM - 1,
+ 1),
+ SOC_SINGLE_TLV("Digital EQ Volume", M98090_REG_DAI_PLAYBACK_LEVEL_EQ,
+ M98090_DVEQ_SHIFT, M98090_DVEQ_NUM - 1, 1,
+ max98090_dv_tlv),
+
+ SOC_SINGLE("ALC Enable", M98090_REG_DRC_TIMING,
+ M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0),
+ SOC_ENUM("ALC Attack Time", max98090_drcatk_enum),
+ SOC_ENUM("ALC Release Time", max98090_drcrls_enum),
+ SOC_SINGLE_TLV("ALC Make Up Volume", M98090_REG_DRC_GAIN,
+ M98090_DRCG_SHIFT, M98090_DRCG_NUM - 1, 0,
+ max98090_alcmakeup_tlv),
+ SOC_ENUM("ALC Compression Ratio", max98090_alccmp_enum),
+ SOC_ENUM("ALC Expansion Ratio", max98090_drcexp_enum),
+ SOC_SINGLE_TLV("ALC Compression Threshold Volume",
+ M98090_REG_DRC_COMPRESSOR, M98090_DRCTHC_SHIFT,
+ M98090_DRCTHC_NUM - 1, 1, max98090_alccomp_tlv),
+ SOC_SINGLE_TLV("ALC Expansion Threshold Volume",
+ M98090_REG_DRC_EXPANDER, M98090_DRCTHE_SHIFT,
+ M98090_DRCTHE_NUM - 1, 1, max98090_drcexp_tlv),
+
+ SOC_ENUM("DAC HP Playback Performance Mode",
+ max98090_dac_perfmode_enum),
+ SOC_ENUM("DAC High Performance Mode", max98090_dachp_enum),
+
+ SOC_SINGLE_TLV("Headphone Left Mixer Volume",
+ M98090_REG_HP_CONTROL, M98090_MIXHPLG_SHIFT,
+ M98090_MIXHPLG_NUM - 1, 1, max98090_mixout_tlv),
+ SOC_SINGLE_TLV("Headphone Right Mixer Volume",
+ M98090_REG_HP_CONTROL, M98090_MIXHPRG_SHIFT,
+ M98090_MIXHPRG_NUM - 1, 1, max98090_mixout_tlv),
+
+ SOC_SINGLE_TLV("Speaker Left Mixer Volume",
+ M98090_REG_SPK_CONTROL, M98090_MIXSPLG_SHIFT,
+ M98090_MIXSPLG_NUM - 1, 1, max98090_mixout_tlv),
+ SOC_SINGLE_TLV("Speaker Right Mixer Volume",
+ M98090_REG_SPK_CONTROL, M98090_MIXSPRG_SHIFT,
+ M98090_MIXSPRG_NUM - 1, 1, max98090_mixout_tlv),
+
+ SOC_SINGLE_TLV("Receiver Left Mixer Volume",
+ M98090_REG_RCV_LOUTL_CONTROL, M98090_MIXRCVLG_SHIFT,
+ M98090_MIXRCVLG_NUM - 1, 1, max98090_mixout_tlv),
+ SOC_SINGLE_TLV("Receiver Right Mixer Volume",
+ M98090_REG_LOUTR_CONTROL, M98090_MIXRCVRG_SHIFT,
+ M98090_MIXRCVRG_NUM - 1, 1, max98090_mixout_tlv),
+
+ SOC_DOUBLE_R_TLV("Headphone Volume", M98090_REG_LEFT_HP_VOLUME,
+ M98090_REG_RIGHT_HP_VOLUME, M98090_HPVOLL_SHIFT,
+ M98090_HPVOLL_NUM - 1, 0, max98090_hp_tlv),
+
+ SOC_DOUBLE_R_RANGE_TLV("Speaker Volume",
+ M98090_REG_LEFT_SPK_VOLUME, M98090_REG_RIGHT_SPK_VOLUME,
+ M98090_SPVOLL_SHIFT, 24, M98090_SPVOLL_NUM - 1 + 24,
+ 0, max98090_spk_tlv),
+
+ SOC_DOUBLE_R_TLV("Receiver Volume", M98090_REG_RCV_LOUTL_VOLUME,
+ M98090_REG_LOUTR_VOLUME, M98090_RCVLVOL_SHIFT,
+ M98090_RCVLVOL_NUM - 1, 0, max98090_rcv_lout_tlv),
+
+ SOC_SINGLE("Headphone Left Switch", M98090_REG_LEFT_HP_VOLUME,
+ M98090_HPLM_SHIFT, 1, 1),
+ SOC_SINGLE("Headphone Right Switch", M98090_REG_RIGHT_HP_VOLUME,
+ M98090_HPRM_SHIFT, 1, 1),
+
+ SOC_SINGLE("Speaker Left Switch", M98090_REG_LEFT_SPK_VOLUME,
+ M98090_SPLM_SHIFT, 1, 1),
+ SOC_SINGLE("Speaker Right Switch", M98090_REG_RIGHT_SPK_VOLUME,
+ M98090_SPRM_SHIFT, 1, 1),
+
+ SOC_SINGLE("Receiver Left Switch", M98090_REG_RCV_LOUTL_VOLUME,
+ M98090_RCVLM_SHIFT, 1, 1),
+ SOC_SINGLE("Receiver Right Switch", M98090_REG_LOUTR_VOLUME,
+ M98090_RCVRM_SHIFT, 1, 1),
+
+ SOC_SINGLE("Zero-Crossing Detection", M98090_REG_LEVEL_CONTROL,
+ M98090_ZDENN_SHIFT, M98090_ZDENN_NUM - 1, 1),
+ SOC_SINGLE("Enhanced Vol Smoothing", M98090_REG_LEVEL_CONTROL,
+ M98090_VS2ENN_SHIFT, M98090_VS2ENN_NUM - 1, 1),
+ SOC_SINGLE("Volume Adjustment Smoothing", M98090_REG_LEVEL_CONTROL,
+ M98090_VSENN_SHIFT, M98090_VSENN_NUM - 1, 1),
+
+ SND_SOC_BYTES("Biquad Coefficients", M98090_REG_RECORD_BIQUAD_BASE, 15),
+ SOC_SINGLE("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
+ M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0),
+};
+
+static const struct snd_kcontrol_new max98091_snd_controls[] = {
+
+ SOC_SINGLE("DMIC34 Zeropad", M98090_REG_SAMPLE_RATE,
+ M98090_DMIC34_ZEROPAD_SHIFT,
+ M98090_DMIC34_ZEROPAD_NUM - 1, 0),
+
+ SOC_ENUM("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum),
+ SOC_SINGLE("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG,
+ M98090_FLT_DMIC34HPF_SHIFT,
+ M98090_FLT_DMIC34HPF_NUM - 1, 0),
+
+ SOC_SINGLE_TLV("DMIC3 Boost Volume", M98090_REG_DMIC3_VOLUME,
+ M98090_DMIC_AV3G_SHIFT, M98090_DMIC_AV3G_NUM - 1, 0,
+ max98090_avg_tlv),
+ SOC_SINGLE_TLV("DMIC4 Boost Volume", M98090_REG_DMIC4_VOLUME,
+ M98090_DMIC_AV4G_SHIFT, M98090_DMIC_AV4G_NUM - 1, 0,
+ max98090_avg_tlv),
+
+ SOC_SINGLE_TLV("DMIC3 Volume", M98090_REG_DMIC3_VOLUME,
+ M98090_DMIC_AV3_SHIFT, M98090_DMIC_AV3_NUM - 1, 1,
+ max98090_av_tlv),
+ SOC_SINGLE_TLV("DMIC4 Volume", M98090_REG_DMIC4_VOLUME,
+ M98090_DMIC_AV4_SHIFT, M98090_DMIC_AV4_NUM - 1, 1,
+ max98090_av_tlv),
+
+ SND_SOC_BYTES("DMIC34 Biquad Coefficients",
+ M98090_REG_DMIC34_BIQUAD_BASE, 15),
+ SOC_SINGLE("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
+ M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0),
+
+ SOC_SINGLE_TLV("DMIC34 BQ PreAttenuation Volume",
+ M98090_REG_DMIC34_BQ_PREATTEN, M98090_AV34BQ_SHIFT,
+ M98090_AV34BQ_NUM - 1, 1, max98090_dv_tlv),
+};
+
+static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+
+ unsigned int val = snd_soc_read(codec, w->reg);
+
+ if (w->reg == M98090_REG_MIC1_INPUT_LEVEL)
+ val = (val & M98090_MIC_PA1EN_MASK) >> M98090_MIC_PA1EN_SHIFT;
+ else
+ val = (val & M98090_MIC_PA2EN_MASK) >> M98090_MIC_PA2EN_SHIFT;
+
+ if (val >= 1) {
+ if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) {
+ max98090->pa1en = val - 1; /* Update for volatile */
+ } else {
+ max98090->pa2en = val - 1; /* Update for volatile */
+ }
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* If turning on, set to most recently selected volume */
+ if (w->reg == M98090_REG_MIC1_INPUT_LEVEL)
+ val = max98090->pa1en + 1;
+ else
+ val = max98090->pa2en + 1;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* If turning off, turn off */
+ val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (w->reg == M98090_REG_MIC1_INPUT_LEVEL)
+ snd_soc_update_bits(codec, w->reg, M98090_MIC_PA1EN_MASK,
+ val << M98090_MIC_PA1EN_SHIFT);
+ else
+ snd_soc_update_bits(codec, w->reg, M98090_MIC_PA2EN_MASK,
+ val << M98090_MIC_PA2EN_SHIFT);
+
+ return 0;
+}
+
+static const char *mic1_mux_text[] = { "IN12", "IN56" };
+
+static SOC_ENUM_SINGLE_DECL(mic1_mux_enum,
+ M98090_REG_INPUT_MODE,
+ M98090_EXTMIC1_SHIFT,
+ mic1_mux_text);
+
+static const struct snd_kcontrol_new max98090_mic1_mux =
+ SOC_DAPM_ENUM("MIC1 Mux", mic1_mux_enum);
+
+static const char *mic2_mux_text[] = { "IN34", "IN56" };
+
+static SOC_ENUM_SINGLE_DECL(mic2_mux_enum,
+ M98090_REG_INPUT_MODE,
+ M98090_EXTMIC2_SHIFT,
+ mic2_mux_text);
+
+static const struct snd_kcontrol_new max98090_mic2_mux =
+ SOC_DAPM_ENUM("MIC2 Mux", mic2_mux_enum);
+
+static const char *dmic_mux_text[] = { "ADC", "DMIC" };
+
+static SOC_ENUM_SINGLE_VIRT_DECL(dmic_mux_enum, dmic_mux_text);
+
+static const struct snd_kcontrol_new max98090_dmic_mux =
+ SOC_DAPM_ENUM("DMIC Mux", dmic_mux_enum);
+
+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,
+ M98090_IN1SEEN_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN3 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN3SEEN_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN5 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN5SEEN_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN34DIFF_SHIFT, 1, 0),
+};
+
+/* LINEB mixer switch */
+static const struct snd_kcontrol_new max98090_lineb_mixer_controls[] = {
+ SOC_DAPM_SINGLE("IN2 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN2SEEN_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN4 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN4SEEN_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN6 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN6SEEN_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_LINE_INPUT_CONFIG,
+ M98090_IN56DIFF_SHIFT, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98090_left_adc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("IN12 Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_IN12DIFF_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_IN34DIFF_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_IN65DIFF_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_ADC_MIXER,
+ M98090_MIXADL_MIC2_SHIFT, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98090_right_adc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("IN12 Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_IN12DIFF_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_IN34DIFF_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_IN65DIFF_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_ADC_MIXER,
+ M98090_MIXADR_MIC2_SHIFT, 1, 0),
+};
+
+static const char *lten_mux_text[] = { "Normal", "Loopthrough" };
+
+static SOC_ENUM_SINGLE_DECL(ltenl_mux_enum,
+ M98090_REG_IO_CONFIGURATION,
+ M98090_LTEN_SHIFT,
+ lten_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(ltenr_mux_enum,
+ M98090_REG_IO_CONFIGURATION,
+ M98090_LTEN_SHIFT,
+ lten_mux_text);
+
+static const struct snd_kcontrol_new max98090_ltenl_mux =
+ SOC_DAPM_ENUM("LTENL Mux", ltenl_mux_enum);
+
+static const struct snd_kcontrol_new max98090_ltenr_mux =
+ SOC_DAPM_ENUM("LTENR Mux", ltenr_mux_enum);
+
+static const char *lben_mux_text[] = { "Normal", "Loopback" };
+
+static SOC_ENUM_SINGLE_DECL(lbenl_mux_enum,
+ M98090_REG_IO_CONFIGURATION,
+ M98090_LBEN_SHIFT,
+ lben_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(lbenr_mux_enum,
+ M98090_REG_IO_CONFIGURATION,
+ M98090_LBEN_SHIFT,
+ lben_mux_text);
+
+static const struct snd_kcontrol_new max98090_lbenl_mux =
+ SOC_DAPM_ENUM("LBENL Mux", lbenl_mux_enum);
+
+static const struct snd_kcontrol_new max98090_lbenr_mux =
+ SOC_DAPM_ENUM("LBENR Mux", lbenr_mux_enum);
+
+static const char *stenl_mux_text[] = { "Normal", "Sidetone Left" };
+
+static const char *stenr_mux_text[] = { "Normal", "Sidetone Right" };
+
+static SOC_ENUM_SINGLE_DECL(stenl_mux_enum,
+ M98090_REG_ADC_SIDETONE,
+ M98090_DSTSL_SHIFT,
+ stenl_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(stenr_mux_enum,
+ M98090_REG_ADC_SIDETONE,
+ M98090_DSTSR_SHIFT,
+ stenr_mux_text);
+
+static const struct snd_kcontrol_new max98090_stenl_mux =
+ SOC_DAPM_ENUM("STENL Mux", stenl_mux_enum);
+
+static const struct snd_kcontrol_new max98090_stenr_mux =
+ SOC_DAPM_ENUM("STENR Mux", stenr_mux_enum);
+
+/* Left speaker mixer switch */
+static const struct
+ snd_kcontrol_new max98090_left_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LEFT_SPK_MIXER,
+ M98090_MIXSPL_DACL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LEFT_SPK_MIXER,
+ M98090_MIXSPL_DACR_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_SPK_MIXER,
+ M98090_MIXSPL_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_SPK_MIXER,
+ M98090_MIXSPL_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_SPK_MIXER,
+ M98090_MIXSPL_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_SPK_MIXER,
+ M98090_MIXSPL_MIC2_SHIFT, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct
+ snd_kcontrol_new max98090_right_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RIGHT_SPK_MIXER,
+ M98090_MIXSPR_DACL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RIGHT_SPK_MIXER,
+ M98090_MIXSPR_DACR_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_SPK_MIXER,
+ M98090_MIXSPR_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_SPK_MIXER,
+ M98090_MIXSPR_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_SPK_MIXER,
+ M98090_MIXSPR_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_SPK_MIXER,
+ M98090_MIXSPR_MIC2_SHIFT, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98090_left_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LEFT_HP_MIXER,
+ M98090_MIXHPL_DACL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LEFT_HP_MIXER,
+ M98090_MIXHPL_DACR_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_HP_MIXER,
+ M98090_MIXHPL_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_HP_MIXER,
+ M98090_MIXHPL_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_HP_MIXER,
+ M98090_MIXHPL_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_HP_MIXER,
+ M98090_MIXHPL_MIC2_SHIFT, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98090_right_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RIGHT_HP_MIXER,
+ M98090_MIXHPR_DACL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RIGHT_HP_MIXER,
+ M98090_MIXHPR_DACR_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_HP_MIXER,
+ M98090_MIXHPR_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_HP_MIXER,
+ M98090_MIXHPR_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_HP_MIXER,
+ M98090_MIXHPR_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_HP_MIXER,
+ M98090_MIXHPR_MIC2_SHIFT, 1, 0),
+};
+
+/* Left receiver mixer switch */
+static const struct snd_kcontrol_new max98090_left_rcv_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RCV_LOUTL_MIXER,
+ M98090_MIXRCVL_DACL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RCV_LOUTL_MIXER,
+ M98090_MIXRCVL_DACR_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RCV_LOUTL_MIXER,
+ M98090_MIXRCVL_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RCV_LOUTL_MIXER,
+ M98090_MIXRCVL_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RCV_LOUTL_MIXER,
+ M98090_MIXRCVL_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RCV_LOUTL_MIXER,
+ M98090_MIXRCVL_MIC2_SHIFT, 1, 0),
+};
+
+/* Right receiver mixer switch */
+static const struct snd_kcontrol_new max98090_right_rcv_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LOUTR_MIXER,
+ M98090_MIXRCVR_DACL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LOUTR_MIXER,
+ M98090_MIXRCVR_DACR_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LOUTR_MIXER,
+ M98090_MIXRCVR_LINEA_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LOUTR_MIXER,
+ M98090_MIXRCVR_LINEB_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LOUTR_MIXER,
+ M98090_MIXRCVR_MIC1_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LOUTR_MIXER,
+ M98090_MIXRCVR_MIC2_SHIFT, 1, 0),
+};
+
+static const char *linmod_mux_text[] = { "Left Only", "Left and Right" };
+
+static SOC_ENUM_SINGLE_DECL(linmod_mux_enum,
+ M98090_REG_LOUTR_MIXER,
+ M98090_LINMOD_SHIFT,
+ linmod_mux_text);
+
+static const struct snd_kcontrol_new max98090_linmod_mux =
+ SOC_DAPM_ENUM("LINMOD Mux", linmod_mux_enum);
+
+static const char *mixhpsel_mux_text[] = { "DAC Only", "HP Mixer" };
+
+/*
+ * This is a mux as it selects the HP output, but to DAPM it is a Mixer enable
+ */
+static SOC_ENUM_SINGLE_DECL(mixhplsel_mux_enum,
+ M98090_REG_HP_CONTROL,
+ M98090_MIXHPLSEL_SHIFT,
+ mixhpsel_mux_text);
+
+static const struct snd_kcontrol_new max98090_mixhplsel_mux =
+ SOC_DAPM_ENUM("MIXHPLSEL Mux", mixhplsel_mux_enum);
+
+static SOC_ENUM_SINGLE_DECL(mixhprsel_mux_enum,
+ M98090_REG_HP_CONTROL,
+ M98090_MIXHPRSEL_SHIFT,
+ mixhpsel_mux_text);
+
+static const struct snd_kcontrol_new max98090_mixhprsel_mux =
+ SOC_DAPM_ENUM("MIXHPRSEL Mux", mixhprsel_mux_enum);
+
+static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("DMICL"),
+ SND_SOC_DAPM_INPUT("DMICR"),
+ SND_SOC_DAPM_INPUT("IN1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_INPUT("IN3"),
+ SND_SOC_DAPM_INPUT("IN4"),
+ SND_SOC_DAPM_INPUT("IN5"),
+ SND_SOC_DAPM_INPUT("IN6"),
+ SND_SOC_DAPM_INPUT("IN12"),
+ SND_SOC_DAPM_INPUT("IN34"),
+ SND_SOC_DAPM_INPUT("IN56"),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS", M98090_REG_INPUT_ENABLE,
+ M98090_MBEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SHDN", M98090_REG_DEVICE_SHUTDOWN,
+ M98090_SHDNN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SDIEN", M98090_REG_IO_CONFIGURATION,
+ M98090_SDIEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SDOEN", M98090_REG_IO_CONFIGURATION,
+ M98090_SDOEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMICL_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
+ M98090_DIGMICL_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMICR_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
+ M98090_DIGMICR_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AHPF", M98090_REG_FILTER_CONFIG,
+ M98090_AHPF_SHIFT, 0, NULL, 0),
+
+/*
+ * Note: Sysclk and misc power supplies are taken care of by SHDN
+ */
+
+ SND_SOC_DAPM_MUX("MIC1 Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_mic1_mux),
+
+ SND_SOC_DAPM_MUX("MIC2 Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_mic2_mux),
+
+ SND_SOC_DAPM_MUX("DMIC Mux", SND_SOC_NOPM, 0, 0, &max98090_dmic_mux),
+
+ SND_SOC_DAPM_PGA_E("MIC1 Input", M98090_REG_MIC1_INPUT_LEVEL,
+ M98090_MIC_PA1EN_SHIFT, 0, NULL, 0, max98090_micinput_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("MIC2 Input", M98090_REG_MIC2_INPUT_LEVEL,
+ M98090_MIC_PA2EN_SHIFT, 0, NULL, 0, max98090_micinput_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("LINEA Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_linea_mixer_controls[0],
+ ARRAY_SIZE(max98090_linea_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("LINEB Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_lineb_mixer_controls[0],
+ ARRAY_SIZE(max98090_lineb_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("LINEA Input", M98090_REG_INPUT_ENABLE,
+ M98090_LINEAEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LINEB Input", M98090_REG_INPUT_ENABLE,
+ M98090_LINEBEN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_left_adc_mixer_controls[0],
+ ARRAY_SIZE(max98090_left_adc_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_right_adc_mixer_controls[0],
+ ARRAY_SIZE(max98090_right_adc_mixer_controls)),
+
+ SND_SOC_DAPM_ADC("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_AIF_OUT("AIFOUTL", "HiFi Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIFOUTR", "HiFi Capture", 1,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("LBENL Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_lbenl_mux),
+
+ SND_SOC_DAPM_MUX("LBENR Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_lbenr_mux),
+
+ SND_SOC_DAPM_MUX("LTENL Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_ltenl_mux),
+
+ SND_SOC_DAPM_MUX("LTENR Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_ltenr_mux),
+
+ SND_SOC_DAPM_MUX("STENL Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_stenl_mux),
+
+ SND_SOC_DAPM_MUX("STENR Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_stenr_mux),
+
+ SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DACL", NULL, M98090_REG_OUTPUT_ENABLE,
+ M98090_DALEN_SHIFT, 0),
+ SND_SOC_DAPM_DAC("DACR", NULL, M98090_REG_OUTPUT_ENABLE,
+ M98090_DAREN_SHIFT, 0),
+
+ SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_left_hp_mixer_controls[0],
+ ARRAY_SIZE(max98090_left_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_right_hp_mixer_controls[0],
+ ARRAY_SIZE(max98090_right_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_left_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98090_left_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_right_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98090_right_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Receiver Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_left_rcv_mixer_controls[0],
+ ARRAY_SIZE(max98090_left_rcv_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Receiver Mixer", SND_SOC_NOPM, 0, 0,
+ &max98090_right_rcv_mixer_controls[0],
+ ARRAY_SIZE(max98090_right_rcv_mixer_controls)),
+
+ SND_SOC_DAPM_MUX("LINMOD Mux", M98090_REG_LOUTR_MIXER,
+ M98090_LINMOD_SHIFT, 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("MIXHPRSEL Mux", M98090_REG_HP_CONTROL,
+ M98090_MIXHPRSEL_SHIFT, 0, &max98090_mixhprsel_mux),
+
+ SND_SOC_DAPM_PGA("HP Left Out", M98090_REG_OUTPUT_ENABLE,
+ M98090_HPLEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP Right Out", M98090_REG_OUTPUT_ENABLE,
+ M98090_HPREN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("SPK Left Out", M98090_REG_OUTPUT_ENABLE,
+ M98090_SPLEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPK Right Out", M98090_REG_OUTPUT_ENABLE,
+ M98090_SPREN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("RCV Left Out", M98090_REG_OUTPUT_ENABLE,
+ M98090_RCVLEN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RCV Right Out", M98090_REG_OUTPUT_ENABLE,
+ M98090_RCVREN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("RCVL"),
+ SND_SOC_DAPM_OUTPUT("RCVR"),
+};
+
+static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC3"),
+ SND_SOC_DAPM_INPUT("DMIC4"),
+
+ SND_SOC_DAPM_SUPPLY("DMIC3_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
+ M98090_DIGMIC3_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC4_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
+ M98090_DIGMIC4_SHIFT, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
+ {"MIC1 Input", NULL, "MIC1"},
+ {"MIC2 Input", NULL, "MIC2"},
+
+ {"DMICL", NULL, "DMICL_ENA"},
+ {"DMICL", NULL, "DMICR_ENA"},
+ {"DMICR", NULL, "DMICL_ENA"},
+ {"DMICR", NULL, "DMICR_ENA"},
+ {"DMICL", NULL, "AHPF"},
+ {"DMICR", NULL, "AHPF"},
+
+ /* MIC1 input mux */
+ {"MIC1 Mux", "IN12", "IN12"},
+ {"MIC1 Mux", "IN56", "IN56"},
+
+ /* MIC2 input mux */
+ {"MIC2 Mux", "IN34", "IN34"},
+ {"MIC2 Mux", "IN56", "IN56"},
+
+ {"MIC1 Input", NULL, "MIC1 Mux"},
+ {"MIC2 Input", NULL, "MIC2 Mux"},
+
+ /* Left ADC input mixer */
+ {"Left ADC Mixer", "IN12 Switch", "IN12"},
+ {"Left ADC Mixer", "IN34 Switch", "IN34"},
+ {"Left ADC Mixer", "IN56 Switch", "IN56"},
+ {"Left ADC Mixer", "LINEA Switch", "LINEA Input"},
+ {"Left ADC Mixer", "LINEB Switch", "LINEB Input"},
+ {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+
+ /* Right ADC input mixer */
+ {"Right ADC Mixer", "IN12 Switch", "IN12"},
+ {"Right ADC Mixer", "IN34 Switch", "IN34"},
+ {"Right ADC Mixer", "IN56 Switch", "IN56"},
+ {"Right ADC Mixer", "LINEA Switch", "LINEA Input"},
+ {"Right ADC Mixer", "LINEB Switch", "LINEB Input"},
+ {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+
+ /* Line A input mixer */
+ {"LINEA Mixer", "IN1 Switch", "IN1"},
+ {"LINEA Mixer", "IN3 Switch", "IN3"},
+ {"LINEA Mixer", "IN5 Switch", "IN5"},
+ {"LINEA Mixer", "IN34 Switch", "IN34"},
+
+ /* Line B input mixer */
+ {"LINEB Mixer", "IN2 Switch", "IN2"},
+ {"LINEB Mixer", "IN4 Switch", "IN4"},
+ {"LINEB Mixer", "IN6 Switch", "IN6"},
+ {"LINEB Mixer", "IN56 Switch", "IN56"},
+
+ {"LINEA Input", NULL, "LINEA Mixer"},
+ {"LINEB Input", NULL, "LINEB Mixer"},
+
+ /* Inputs */
+ {"ADCL", NULL, "Left ADC Mixer"},
+ {"ADCR", NULL, "Right ADC Mixer"},
+ {"ADCL", NULL, "SHDN"},
+ {"ADCR", NULL, "SHDN"},
+
+ {"DMIC Mux", "ADC", "ADCL"},
+ {"DMIC Mux", "ADC", "ADCR"},
+ {"DMIC Mux", "DMIC", "DMICL"},
+ {"DMIC Mux", "DMIC", "DMICR"},
+
+ {"LBENL Mux", "Normal", "DMIC Mux"},
+ {"LBENL Mux", "Loopback", "LTENL Mux"},
+ {"LBENR Mux", "Normal", "DMIC Mux"},
+ {"LBENR Mux", "Loopback", "LTENR Mux"},
+
+ {"AIFOUTL", NULL, "LBENL Mux"},
+ {"AIFOUTR", NULL, "LBENR Mux"},
+ {"AIFOUTL", NULL, "SHDN"},
+ {"AIFOUTR", NULL, "SHDN"},
+ {"AIFOUTL", NULL, "SDOEN"},
+ {"AIFOUTR", NULL, "SDOEN"},
+
+ {"LTENL Mux", "Normal", "AIFINL"},
+ {"LTENL Mux", "Loopthrough", "LBENL Mux"},
+ {"LTENR Mux", "Normal", "AIFINR"},
+ {"LTENR Mux", "Loopthrough", "LBENR Mux"},
+
+ {"DACL", NULL, "LTENL Mux"},
+ {"DACR", NULL, "LTENR Mux"},
+
+ {"STENL Mux", "Sidetone Left", "ADCL"},
+ {"STENL Mux", "Sidetone Left", "DMICL"},
+ {"STENR Mux", "Sidetone Right", "ADCR"},
+ {"STENR Mux", "Sidetone Right", "DMICR"},
+ {"DACL", NULL, "STENL Mux"},
+ {"DACR", NULL, "STENR Mux"},
+
+ {"AIFINL", NULL, "SHDN"},
+ {"AIFINR", NULL, "SHDN"},
+ {"AIFINL", NULL, "SDIEN"},
+ {"AIFINR", NULL, "SDIEN"},
+ {"DACL", NULL, "SHDN"},
+ {"DACR", NULL, "SHDN"},
+
+ /* Left headphone output mixer */
+ {"Left Headphone Mixer", "Left DAC Switch", "DACL"},
+ {"Left Headphone Mixer", "Right DAC Switch", "DACR"},
+ {"Left Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Headphone Mixer", "LINEA Switch", "LINEA Input"},
+ {"Left Headphone Mixer", "LINEB Switch", "LINEB Input"},
+
+ /* Right headphone output mixer */
+ {"Right Headphone Mixer", "Left DAC Switch", "DACL"},
+ {"Right Headphone Mixer", "Right DAC Switch", "DACR"},
+ {"Right Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Headphone Mixer", "LINEA Switch", "LINEA Input"},
+ {"Right Headphone Mixer", "LINEB Switch", "LINEB Input"},
+
+ /* Left speaker output mixer */
+ {"Left Speaker Mixer", "Left DAC Switch", "DACL"},
+ {"Left Speaker Mixer", "Right DAC Switch", "DACR"},
+ {"Left Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Speaker Mixer", "LINEA Switch", "LINEA Input"},
+ {"Left Speaker Mixer", "LINEB Switch", "LINEB Input"},
+
+ /* Right speaker output mixer */
+ {"Right Speaker Mixer", "Left DAC Switch", "DACL"},
+ {"Right Speaker Mixer", "Right DAC Switch", "DACR"},
+ {"Right Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Speaker Mixer", "LINEA Switch", "LINEA Input"},
+ {"Right Speaker Mixer", "LINEB Switch", "LINEB Input"},
+
+ /* Left Receiver output mixer */
+ {"Left Receiver Mixer", "Left DAC Switch", "DACL"},
+ {"Left Receiver Mixer", "Right DAC Switch", "DACR"},
+ {"Left Receiver Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Receiver Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Receiver Mixer", "LINEA Switch", "LINEA Input"},
+ {"Left Receiver Mixer", "LINEB Switch", "LINEB Input"},
+
+ /* Right Receiver output mixer */
+ {"Right Receiver Mixer", "Left DAC Switch", "DACL"},
+ {"Right Receiver Mixer", "Right DAC Switch", "DACR"},
+ {"Right Receiver Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Receiver Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Receiver Mixer", "LINEA Switch", "LINEA Input"},
+ {"Right Receiver Mixer", "LINEB Switch", "LINEB Input"},
+
+ {"MIXHPLSEL Mux", "HP Mixer", "Left Headphone Mixer"},
+
+ /*
+ * Disable this for lowest power if bypassing
+ * the DAC with an analog signal
+ */
+ {"HP Left Out", NULL, "DACL"},
+ {"HP Left Out", NULL, "MIXHPLSEL Mux"},
+
+ {"MIXHPRSEL Mux", "HP Mixer", "Right Headphone Mixer"},
+
+ /*
+ * Disable this for lowest power if bypassing
+ * the DAC with an analog signal
+ */
+ {"HP Right Out", NULL, "DACR"},
+ {"HP Right Out", NULL, "MIXHPRSEL Mux"},
+
+ {"SPK Left Out", NULL, "Left Speaker Mixer"},
+ {"SPK Right Out", NULL, "Right Speaker Mixer"},
+ {"RCV Left Out", NULL, "Left Receiver Mixer"},
+
+ {"LINMOD Mux", "Left and Right", "Right Receiver Mixer"},
+ {"LINMOD Mux", "Left Only", "Left Receiver Mixer"},
+ {"RCV Right Out", NULL, "LINMOD Mux"},
+
+ {"HPL", NULL, "HP Left Out"},
+ {"HPR", NULL, "HP Right Out"},
+ {"SPKL", NULL, "SPK Left Out"},
+ {"SPKR", NULL, "SPK Right Out"},
+ {"RCVL", NULL, "RCV Left Out"},
+ {"RCVR", NULL, "RCV Right Out"},
+};
+
+static const struct snd_soc_dapm_route max98091_dapm_routes[] = {
+ /* DMIC inputs */
+ {"DMIC3", NULL, "DMIC3_ENA"},
+ {"DMIC4", NULL, "DMIC4_ENA"},
+ {"DMIC3", NULL, "AHPF"},
+ {"DMIC4", NULL, "AHPF"},
+};
+
+static int max98090_add_widgets(struct snd_soc_codec *codec)
+{
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ snd_soc_add_codec_controls(codec, max98090_snd_controls,
+ ARRAY_SIZE(max98090_snd_controls));
+
+ if (max98090->devtype == MAX98091) {
+ snd_soc_add_codec_controls(codec, max98091_snd_controls,
+ ARRAY_SIZE(max98091_snd_controls));
+ }
+
+ snd_soc_dapm_new_controls(dapm, max98090_dapm_widgets,
+ ARRAY_SIZE(max98090_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, max98090_dapm_routes,
+ ARRAY_SIZE(max98090_dapm_routes));
+
+ if (max98090->devtype == MAX98091) {
+ snd_soc_dapm_new_controls(dapm, max98091_dapm_widgets,
+ ARRAY_SIZE(max98091_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, max98091_dapm_routes,
+ ARRAY_SIZE(max98091_dapm_routes));
+ }
+
+ return 0;
+}
+
+static const int pclk_rates[] = {
+ 12000000, 12000000, 13000000, 13000000,
+ 16000000, 16000000, 19200000, 19200000
+};
+
+static const int lrclk_rates[] = {
+ 8000, 16000, 8000, 16000,
+ 8000, 16000, 8000, 16000
+};
+
+static const int user_pclk_rates[] = {
+ 13000000, 13000000, 19200000, 19200000,
+};
+
+static const int user_lrclk_rates[] = {
+ 44100, 48000, 44100, 48000,
+};
+
+static const unsigned long long ni_value[] = {
+ 3528, 768, 441, 8
+};
+
+static const unsigned long long mi_value[] = {
+ 8125, 1625, 1500, 25
+};
+
+static void max98090_configure_bclk(struct snd_soc_codec *codec)
+{
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ unsigned long long ni;
+ int i;
+
+ if (!max98090->sysclk) {
+ dev_err(codec->dev, "No SYSCLK configured\n");
+ return;
+ }
+
+ if (!max98090->bclk || !max98090->lrclk) {
+ dev_err(codec->dev, "No audio clocks configured\n");
+ return;
+ }
+
+ /* Skip configuration when operating as slave */
+ if (!(snd_soc_read(codec, M98090_REG_MASTER_MODE) &
+ M98090_MAS_MASK)) {
+ return;
+ }
+
+ /* Check for supported PCLK to LRCLK ratios */
+ for (i = 0; i < ARRAY_SIZE(pclk_rates); i++) {
+ if ((pclk_rates[i] == max98090->sysclk) &&
+ (lrclk_rates[i] == max98090->lrclk)) {
+ dev_dbg(codec->dev,
+ "Found supported PCLK to LRCLK rates 0x%x\n",
+ i + 0x8);
+
+ snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ M98090_FREQ_MASK,
+ (i + 0x8) << M98090_FREQ_SHIFT);
+ snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ M98090_USE_M1_MASK, 0);
+ return;
+ }
+ }
+
+ /* Check for user calculated MI and NI ratios */
+ for (i = 0; i < ARRAY_SIZE(user_pclk_rates); i++) {
+ if ((user_pclk_rates[i] == max98090->sysclk) &&
+ (user_lrclk_rates[i] == max98090->lrclk)) {
+ dev_dbg(codec->dev,
+ "Found user supported PCLK to LRCLK rates\n");
+ dev_dbg(codec->dev, "i %d ni %lld mi %lld\n",
+ i, ni_value[i], mi_value[i]);
+
+ snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ M98090_FREQ_MASK, 0);
+ snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ M98090_USE_M1_MASK,
+ 1 << M98090_USE_M1_SHIFT);
+
+ snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB,
+ (ni_value[i] >> 8) & 0x7F);
+ snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB,
+ ni_value[i] & 0xFF);
+ snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_MSB,
+ (mi_value[i] >> 8) & 0x7F);
+ snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_LSB,
+ mi_value[i] & 0xFF);
+
+ return;
+ }
+ }
+
+ /*
+ * Calculate based on MI = 65536 (not as good as either method above)
+ */
+ snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ M98090_FREQ_MASK, 0);
+ snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ M98090_USE_M1_MASK, 0);
+
+ /*
+ * Configure NI when operating as master
+ * Note: There is a small, but significant audio quality improvement
+ * by calculating ni and mi.
+ */
+ ni = 65536ULL * (max98090->lrclk < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)max98090->lrclk;
+ do_div(ni, (unsigned long long int)max98090->sysclk);
+ dev_info(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,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, 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 max98090_cdata *cdata;
+ u8 regval;
+
+ max98090->dai_fmt = fmt;
+ cdata = &max98090->dai[0];
+
+ if (fmt != cdata->fmt) {
+ 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,
+ M98090_REG_CLOCK_RATIO_NI_MSB, 0x00);
+ snd_soc_write(codec,
+ M98090_REG_CLOCK_RATIO_NI_LSB, 0x00);
+ snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ M98090_USE_M1_MASK, 0);
+ max98090->master = false;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master mode */
+ if (max98090->tdm_slots == 4) {
+ /* TDM */
+ regval |= M98090_MAS_MASK |
+ M98090_BSEL_64;
+ } else if (max98090->tdm_slots == 3) {
+ /* TDM */
+ regval |= M98090_MAS_MASK |
+ M98090_BSEL_48;
+ } else {
+ /* Few TDM slots, or No TDM */
+ regval |= M98090_MAS_MASK |
+ M98090_BSEL_32;
+ }
+ max98090->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "DAI clock mode unsupported");
+ return -EINVAL;
+ }
+ snd_soc_write(codec, M98090_REG_MASTER_MODE, regval);
+
+ regval = 0;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98090_DLY_MASK;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ regval |= M98090_RJ_MASK;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /* Not supported mode */
+ default:
+ dev_err(codec->dev, "DAI format unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98090_WCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98090_BCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98090_BCI_MASK|M98090_WCI_MASK;
+ break;
+ default:
+ dev_err(codec->dev, "DAI invert mode unsupported");
+ return -EINVAL;
+ }
+
+ /*
+ * This accommodates an inverted logic in the MAX98090 chip
+ * for Bit Clock Invert (BCI). The inverted logic is only
+ * seen for the case of TDM mode. The remaining cases have
+ * normal logic.
+ */
+ if (max98090->tdm_slots > 1)
+ regval ^= M98090_BCI_MASK;
+
+ snd_soc_write(codec,
+ M98090_REG_INTERFACE_FORMAT, regval);
+ }
+
+ return 0;
+}
+
+static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_cdata *cdata;
+ cdata = &max98090->dai[0];
+
+ 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);
+ }
+
+ /*
+ * Normally advisable to set TDM first, but this permits either order
+ */
+ cdata->fmt = 0;
+ max98090_dai_set_fmt(codec_dai, max98090->dai_fmt);
+
+ return 0;
+}
+
+static int max98090_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /*
+ * SND_SOC_BIAS_PREPARE is called while preparing for a
+ * transition to ON or away from ON. If current bias_level
+ * is SND_SOC_BIAS_ON, then it is preparing for a transition
+ * away from ON. Disable the clock in that case, otherwise
+ * enable it.
+ */
+ if (!IS_ERR(max98090->mclk)) {
+ if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
+ clk_disable_unprepare(max98090->mclk);
+ else
+ clk_prepare_enable(max98090->mclk);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regcache_sync(max98090->regmap);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Set internal pull-up to lowest power mode */
+ snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+ M98090_JDWK_MASK, M98090_JDWK_MASK);
+ regcache_mark_dirty(max98090->regmap);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static const int dmic_divisors[] = { 2, 3, 4, 5, 6, 8 };
+
+static const int comp_lrclk_rates[] = {
+ 8000, 16000, 32000, 44100, 48000, 96000
+};
+
+struct dmic_table {
+ int pclk;
+ struct {
+ int freq;
+ int comp[6]; /* One each for 8, 16, 32, 44.1, 48, and 96 kHz */
+ } settings[6]; /* One for each dmic divisor. */
+};
+
+static const struct dmic_table dmic_table[] = { /* One for each pclk freq. */
+ {
+ .pclk = 11289600,
+ .settings = {
+ { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ },
+ },
+ {
+ .pclk = 12000000,
+ .settings = {
+ { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ }
+ },
+ {
+ .pclk = 12288000,
+ .settings = {
+ { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ }
+ },
+ {
+ .pclk = 13000000,
+ .settings = {
+ { .freq = 2, .comp = { 7, 8, 1, 1, 1, 1 } },
+ { .freq = 1, .comp = { 7, 8, 0, 0, 0, 0 } },
+ { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+ { .freq = 0, .comp = { 7, 8, 4, 4, 5, 5 } },
+ { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+ { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+ }
+ },
+ {
+ .pclk = 19200000,
+ .settings = {
+ { .freq = 2, .comp = { 0, 0, 0, 0, 0, 0 } },
+ { .freq = 1, .comp = { 7, 8, 1, 1, 1, 1 } },
+ { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+ { .freq = 0, .comp = { 7, 8, 2, 2, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 1, 1, 2, 2 } },
+ { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+ }
+ },
+};
+
+static int max98090_find_divisor(int target_freq, int pclk)
+{
+ int current_diff = INT_MAX;
+ int test_diff = INT_MAX;
+ int divisor_index = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dmic_divisors); i++) {
+ test_diff = abs(target_freq - (pclk / dmic_divisors[i]));
+ if (test_diff < current_diff) {
+ current_diff = test_diff;
+ divisor_index = i;
+ }
+ }
+
+ return divisor_index;
+}
+
+static int max98090_find_closest_pclk(int pclk)
+{
+ int m1;
+ int m2;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dmic_table); i++) {
+ if (pclk == dmic_table[i].pclk)
+ return i;
+ if (pclk < dmic_table[i].pclk) {
+ if (i == 0)
+ return i;
+ m1 = pclk - dmic_table[i-1].pclk;
+ m2 = dmic_table[i].pclk - pclk;
+ if (m1 < m2)
+ return i - 1;
+ else
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int max98090_configure_dmic(struct max98090_priv *max98090,
+ int target_dmic_clk, int pclk, int fs)
+{
+ int micclk_index;
+ int pclk_index;
+ int dmic_freq;
+ int dmic_comp;
+ int i;
+
+ pclk_index = max98090_find_closest_pclk(pclk);
+ if (pclk_index < 0)
+ return pclk_index;
+
+ micclk_index = max98090_find_divisor(target_dmic_clk, pclk);
+
+ for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) {
+ if (fs <= (comp_lrclk_rates[i] + comp_lrclk_rates[i+1]) / 2)
+ break;
+ }
+
+ dmic_freq = dmic_table[pclk_index].settings[micclk_index].freq;
+ dmic_comp = dmic_table[pclk_index].settings[micclk_index].comp[i];
+
+ regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_ENABLE,
+ M98090_MICCLK_MASK,
+ micclk_index << M98090_MICCLK_SHIFT);
+
+ regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_CONFIG,
+ M98090_DMIC_COMP_MASK | M98090_DMIC_FREQ_MASK,
+ dmic_comp << M98090_DMIC_COMP_SHIFT |
+ dmic_freq << M98090_DMIC_FREQ_SHIFT);
+
+ return 0;
+}
+
+static int max98090_dai_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 max98090_cdata *cdata;
+
+ cdata = &max98090->dai[0];
+ max98090->bclk = snd_soc_params_to_bclk(params);
+ if (params_channels(params) == 1)
+ max98090->bclk *= 2;
+
+ max98090->lrclk = params_rate(params);
+
+ switch (params_width(params)) {
+ case 16:
+ snd_soc_update_bits(codec, M98090_REG_INTERFACE_FORMAT,
+ M98090_WS_MASK, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (max98090->master)
+ max98090_configure_bclk(codec);
+
+ cdata->rate = max98090->lrclk;
+
+ /* Update filter mode */
+ if (max98090->lrclk < 24000)
+ snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ M98090_MODE_MASK, 0);
+ else
+ snd_soc_update_bits(codec, 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,
+ M98090_DHF_MASK, 0);
+ else
+ snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ M98090_DHF_MASK, M98090_DHF_MASK);
+
+ max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk,
+ max98090->lrclk);
+
+ return 0;
+}
+
+/*
+ * PLL / Sysclk
+ */
+static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+
+ /* Requested clock frequency is already setup */
+ if (freq == max98090->sysclk)
+ return 0;
+
+ if (!IS_ERR(max98090->mclk)) {
+ freq = clk_round_rate(max98090->mclk, freq);
+ clk_set_rate(max98090->mclk, freq);
+ }
+
+ /* Setup clocks for slave mode, and using the PLL
+ * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+ * 0x02 (when master clk is 20MHz to 40MHz)..
+ * 0x03 (when master clk is 40MHz to 60MHz)..
+ */
+ if ((freq >= 10000000) && (freq <= 20000000)) {
+ snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
+ M98090_PSCLK_DIV1);
+ max98090->pclk = freq;
+ } else if ((freq > 20000000) && (freq <= 40000000)) {
+ snd_soc_write(codec, 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,
+ M98090_PSCLK_DIV4);
+ max98090->pclk = freq >> 2;
+ } else {
+ dev_err(codec->dev, "Invalid master clock frequency\n");
+ return -EINVAL;
+ }
+
+ max98090->sysclk = freq;
+
+ return 0;
+}
+
+static int max98090_dai_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int regval;
+
+ regval = mute ? M98090_DVM_MASK : 0;
+ snd_soc_update_bits(codec, M98090_REG_DAI_PLAYBACK_LEVEL,
+ M98090_DVM_MASK, regval);
+
+ return 0;
+}
+
+static int max98090_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (!max98090->master && dai->active == 1)
+ queue_delayed_work(system_power_efficient_wq,
+ &max98090->pll_det_enable_work,
+ msecs_to_jiffies(10));
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (!max98090->master && dai->active == 1)
+ schedule_work(&max98090->pll_det_disable_work);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void max98090_pll_det_enable_work(struct work_struct *work)
+{
+ struct max98090_priv *max98090 =
+ container_of(work, struct max98090_priv,
+ pll_det_enable_work.work);
+ struct snd_soc_codec *codec = max98090->codec;
+ unsigned int status, mask;
+
+ /*
+ * Clear status register in order to clear possibly already occurred
+ * PLL unlock. If PLL hasn't still locked, the status will be set
+ * again and PLL unlock interrupt will occur.
+ * Note this will clear all status bits
+ */
+ regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &status);
+
+ /*
+ * Queue jack work in case jack state has just changed but handler
+ * hasn't run yet
+ */
+ regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask);
+ status &= mask;
+ if (status & M98090_JDET_MASK)
+ queue_delayed_work(system_power_efficient_wq,
+ &max98090->jack_work,
+ msecs_to_jiffies(100));
+
+ /* Enable PLL unlock interrupt */
+ snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ M98090_IULK_MASK,
+ 1 << M98090_IULK_SHIFT);
+}
+
+static void max98090_pll_det_disable_work(struct work_struct *work)
+{
+ struct max98090_priv *max98090 =
+ container_of(work, struct max98090_priv, pll_det_disable_work);
+ struct snd_soc_codec *codec = max98090->codec;
+
+ cancel_delayed_work_sync(&max98090->pll_det_enable_work);
+
+ /* Disable PLL unlock interrupt */
+ snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ M98090_IULK_MASK, 0);
+}
+
+static void max98090_pll_work(struct work_struct *work)
+{
+ struct max98090_priv *max98090 =
+ container_of(work, struct max98090_priv, pll_work);
+ struct snd_soc_codec *codec = max98090->codec;
+
+ if (!snd_soc_codec_is_active(codec))
+ return;
+
+ dev_info(codec->dev, "PLL unlocked\n");
+
+ /* Toggle shutdown OFF then ON */
+ snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ M98090_SHDNN_MASK, 0);
+ msleep(10);
+ snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ M98090_SHDNN_MASK, M98090_SHDNN_MASK);
+
+ /* Give PLL time to lock */
+ msleep(10);
+}
+
+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;
+ int status = 0;
+ int reg;
+
+ /* Read a second time */
+ if (max98090->jack_state == M98090_JACK_STATE_NO_HEADSET) {
+
+ /* Strong pull up allows mic detection */
+ snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+ M98090_JDWK_MASK, 0);
+
+ msleep(50);
+
+ reg = snd_soc_read(codec, M98090_REG_JACK_STATUS);
+
+ /* Weak pull up allows only insertion detection */
+ snd_soc_update_bits(codec, 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);
+
+ switch (reg & (M98090_LSNS_MASK | M98090_JKSNS_MASK)) {
+ case M98090_LSNS_MASK | M98090_JKSNS_MASK:
+ dev_dbg(codec->dev, "No Headset Detected\n");
+
+ max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
+
+ status |= 0;
+
+ break;
+
+ case 0:
+ if (max98090->jack_state ==
+ M98090_JACK_STATE_HEADSET) {
+
+ dev_dbg(codec->dev,
+ "Headset Button Down Detected\n");
+
+ /*
+ * max98090_headset_button_event(codec)
+ * could be defined, then called here.
+ */
+
+ status |= SND_JACK_HEADSET;
+ status |= SND_JACK_BTN_0;
+
+ break;
+ }
+
+ /* Line is reported as Headphone */
+ /* Nokia Headset is reported as Headphone */
+ /* Mono Headphone is reported as Headphone */
+ dev_dbg(codec->dev, "Headphone Detected\n");
+
+ max98090->jack_state = M98090_JACK_STATE_HEADPHONE;
+
+ status |= SND_JACK_HEADPHONE;
+
+ break;
+
+ case M98090_JKSNS_MASK:
+ dev_dbg(codec->dev, "Headset Detected\n");
+
+ max98090->jack_state = M98090_JACK_STATE_HEADSET;
+
+ status |= SND_JACK_HEADSET;
+
+ break;
+
+ default:
+ dev_dbg(codec->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;
+ int ret;
+ unsigned int mask;
+ unsigned int active;
+
+ /* Treat interrupt before codec is initialized as spurious */
+ if (codec == NULL)
+ return IRQ_NONE;
+
+ dev_dbg(codec->dev, "***** max98090_interrupt *****\n");
+
+ ret = regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask);
+
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "failed to read M98090_REG_INTERRUPT_S: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ ret = regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &active);
+
+ if (ret != 0) {
+ dev_err(codec->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",
+ active, mask, active & mask);
+
+ active &= mask;
+
+ if (!active)
+ return IRQ_NONE;
+
+ if (active & M98090_CLD_MASK)
+ dev_err(codec->dev, "M98090_CLD_MASK\n");
+
+ if (active & M98090_SLD_MASK)
+ dev_dbg(codec->dev, "M98090_SLD_MASK\n");
+
+ if (active & M98090_ULK_MASK) {
+ dev_dbg(codec->dev, "M98090_ULK_MASK\n");
+ schedule_work(&max98090->pll_work);
+ }
+
+ if (active & M98090_JDET_MASK) {
+ dev_dbg(codec->dev, "M98090_JDET_MASK\n");
+
+ pm_wakeup_event(codec->dev, 100);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &max98090->jack_work,
+ msecs_to_jiffies(100));
+ }
+
+ if (active & M98090_DRCACT_MASK)
+ dev_dbg(codec->dev, "M98090_DRCACT_MASK\n");
+
+ if (active & M98090_DRCCLP_MASK)
+ dev_err(codec->dev, "M98090_DRCCLP_MASK\n");
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * max98090_mic_detect - Enable microphone detection via the MAX98090 IRQ
+ *
+ * @codec: MAX98090 codec
+ * @jack: jack to report detection events on
+ *
+ * Enable microphone detection via IRQ on the MAX98090. If GPIOs are
+ * being used to bring out signals to the processor then only platform
+ * data configuration is needed for MAX98090 and processor GPIOs should
+ * be configured using snd_soc_jack_add_gpios() instead.
+ *
+ * If no jack is supplied detection will be disabled.
+ */
+int max98090_mic_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack)
+{
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "max98090_mic_detect\n");
+
+ max98090->jack = jack;
+ if (jack) {
+ snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ M98090_IJDET_MASK,
+ 1 << M98090_IJDET_SHIFT);
+ } else {
+ snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ M98090_IJDET_MASK,
+ 0);
+ }
+
+ /* Send an initial empty report */
+ snd_soc_jack_report(max98090->jack, 0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &max98090->jack_work,
+ msecs_to_jiffies(100));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max98090_mic_detect);
+
+#define MAX98090_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98090_dai_ops = {
+ .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,
+ .trigger = max98090_dai_trigger,
+};
+
+static struct snd_soc_dai_driver max98090_dai[] = {
+{
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = MAX98090_RATES,
+ .formats = MAX98090_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98090_RATES,
+ .formats = MAX98090_FORMATS,
+ },
+ .ops = &max98090_dai_ops,
+}
+};
+
+static int max98090_probe(struct snd_soc_codec *codec)
+{
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_cdata *cdata;
+ enum max98090_type devtype;
+ int ret = 0;
+
+ dev_dbg(codec->dev, "max98090_probe\n");
+
+ max98090->mclk = devm_clk_get(codec->dev, "mclk");
+ if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ max98090->codec = codec;
+
+ /* Reset the codec, the DSP core, and disable all interrupts */
+ max98090_reset(max98090);
+
+ /* Initialize private data */
+
+ max98090->sysclk = (unsigned)-1;
+ max98090->pclk = (unsigned)-1;
+ max98090->master = false;
+
+ cdata = &max98090->dai[0];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+
+ max98090->lin_state = 0;
+ max98090->pa1en = 0;
+ max98090->pa2en = 0;
+
+ ret = snd_soc_read(codec, M98090_REG_REVISION_ID);
+ if (ret < 0) {
+ dev_err(codec->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);
+ } else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) {
+ devtype = MAX98091;
+ dev_info(codec->dev, "MAX98091 REVID=0x%02x\n", ret);
+ } else {
+ devtype = MAX98090;
+ dev_err(codec->dev, "Unrecognized revision 0x%02x\n", ret);
+ }
+
+ if (max98090->devtype != devtype) {
+ dev_warn(codec->dev, "Mismatch in DT specified CODEC type.\n");
+ max98090->devtype = devtype;
+ }
+
+ max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
+
+ INIT_DELAYED_WORK(&max98090->jack_work, max98090_jack_work);
+ INIT_DELAYED_WORK(&max98090->pll_det_enable_work,
+ max98090_pll_det_enable_work);
+ INIT_WORK(&max98090->pll_det_disable_work,
+ max98090_pll_det_disable_work);
+ INIT_WORK(&max98090->pll_work, max98090_pll_work);
+
+ /* Enable jack detection */
+ snd_soc_write(codec, M98090_REG_JACK_DETECT,
+ M98090_JDETEN_MASK | M98090_JDEB_25MS);
+
+ /*
+ * Clear any old interrupts.
+ * An old interrupt ocurring prior to installing the ISR
+ * can keep a new interrupt from generating a trigger.
+ */
+ snd_soc_read(codec, M98090_REG_DEVICE_STATUS);
+
+ /* High Performance is default */
+ snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL,
+ M98090_DACHP_MASK,
+ 1 << M98090_DACHP_SHIFT);
+ snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL,
+ M98090_PERFMODE_MASK,
+ 0 << M98090_PERFMODE_SHIFT);
+ snd_soc_update_bits(codec, M98090_REG_ADC_CONTROL,
+ M98090_ADCHP_MASK,
+ 1 << M98090_ADCHP_SHIFT);
+
+ /* Turn on VCM bandgap reference */
+ snd_soc_write(codec, M98090_REG_BIAS_CONTROL,
+ M98090_VCM_MODE_MASK);
+
+ snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
+ M98090_MBVSEL_MASK, M98090_MBVSEL_2V8);
+
+ max98090_add_widgets(codec);
+
+err_access:
+ return ret;
+}
+
+static int max98090_remove(struct snd_soc_codec *codec)
+{
+ struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+
+ 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;
+
+ return 0;
+}
+
+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 regmap_config max98090_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MAX98090_MAX_REGISTER,
+ .reg_defaults = max98090_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98090_reg),
+ .volatile_reg = max98090_volatile_register,
+ .readable_reg = max98090_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98090_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *i2c_id)
+{
+ struct max98090_priv *max98090;
+ const struct acpi_device_id *acpi_id;
+ kernel_ulong_t driver_data = 0;
+ int ret;
+
+ pr_debug("max98090_i2c_probe\n");
+
+ max98090 = devm_kzalloc(&i2c->dev, sizeof(struct max98090_priv),
+ GFP_KERNEL);
+ if (max98090 == NULL)
+ return -ENOMEM;
+
+ if (ACPI_HANDLE(&i2c->dev)) {
+ acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table,
+ &i2c->dev);
+ if (!acpi_id) {
+ dev_err(&i2c->dev, "No driver data\n");
+ return -EINVAL;
+ }
+ driver_data = acpi_id->driver_data;
+ } else if (i2c_id) {
+ driver_data = i2c_id->driver_data;
+ }
+
+ max98090->devtype = driver_data;
+ i2c_set_clientdata(i2c, max98090);
+ max98090->pdata = i2c->dev.platform_data;
+
+ ret = of_property_read_u32(i2c->dev.of_node, "maxim,dmic-freq",
+ &max98090->dmic_freq);
+ if (ret < 0)
+ max98090->dmic_freq = MAX98090_DEFAULT_DMIC_FREQ;
+
+ max98090->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap);
+ if (IS_ERR(max98090->regmap)) {
+ ret = PTR_ERR(max98090->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ goto err_enable;
+ }
+
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ max98090_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "max98090_interrupt", max98090);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "request_irq failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_max98090, max98090_dai,
+ ARRAY_SIZE(max98090_dai));
+err_enable:
+ return ret;
+}
+
+static void max98090_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct max98090_priv *max98090 = dev_get_drvdata(&i2c->dev);
+
+ /*
+ * Enable volume smoothing, disable zero cross. This will cause
+ * a quick 40ms ramp to mute on shutdown.
+ */
+ regmap_write(max98090->regmap,
+ M98090_REG_LEVEL_CONTROL, M98090_VSENN_MASK);
+ regmap_write(max98090->regmap,
+ M98090_REG_DEVICE_SHUTDOWN, 0x00);
+ msleep(40);
+}
+
+static int max98090_i2c_remove(struct i2c_client *client)
+{
+ max98090_i2c_shutdown(client);
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int max98090_runtime_resume(struct device *dev)
+{
+ struct max98090_priv *max98090 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98090->regmap, false);
+
+ max98090_reset(max98090);
+
+ regcache_sync(max98090->regmap);
+
+ return 0;
+}
+
+static int max98090_runtime_suspend(struct device *dev)
+{
+ struct max98090_priv *max98090 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98090->regmap, true);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int max98090_resume(struct device *dev)
+{
+ struct max98090_priv *max98090 = dev_get_drvdata(dev);
+ unsigned int status;
+
+ regcache_mark_dirty(max98090->regmap);
+
+ max98090_reset(max98090);
+
+ /* clear IRQ status */
+ regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &status);
+
+ regcache_sync(max98090->regmap);
+
+ return 0;
+}
+
+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)
+};
+
+static const struct i2c_device_id max98090_i2c_id[] = {
+ { "max98090", MAX98090 },
+ { "max98091", MAX98091 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
+
+static const struct of_device_id max98090_of_match[] = {
+ { .compatible = "maxim,max98090", },
+ { .compatible = "maxim,max98091", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98090_of_match);
+
+#ifdef CONFIG_ACPI
+static struct acpi_device_id max98090_acpi_match[] = {
+ { "193C9890", MAX98090 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, max98090_acpi_match);
+#endif
+
+static struct i2c_driver max98090_i2c_driver = {
+ .driver = {
+ .name = "max98090",
+ .owner = THIS_MODULE,
+ .pm = &max98090_pm,
+ .of_match_table = of_match_ptr(max98090_of_match),
+ .acpi_match_table = ACPI_PTR(max98090_acpi_match),
+ },
+ .probe = max98090_i2c_probe,
+ .shutdown = max98090_i2c_shutdown,
+ .remove = max98090_i2c_remove,
+ .id_table = max98090_i2c_id,
+};
+
+module_i2c_driver(max98090_i2c_driver);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98090 driver");
+MODULE_AUTHOR("Peter Hsiang, Jesse Marroqin, Jerry Wong");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/max98090.h b/kernel/sound/soc/codecs/max98090.h
new file mode 100644
index 000000000..21ff743f5
--- /dev/null
+++ b/kernel/sound/soc/codecs/max98090.h
@@ -0,0 +1,1551 @@
+/*
+ * 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
+#define _MAX98090_H
+
+/*
+ * The default operating frequency for a DMIC attached to the codec.
+ * This can be overridden by a device tree property.
+ */
+#define MAX98090_DEFAULT_DMIC_FREQ 2500000
+
+/*
+ * MAX98090 Register Definitions
+ */
+
+#define M98090_REG_SOFTWARE_RESET 0x00
+#define M98090_REG_DEVICE_STATUS 0x01
+#define M98090_REG_JACK_STATUS 0x02
+#define M98090_REG_INTERRUPT_S 0x03
+#define M98090_REG_QUICK_SYSTEM_CLOCK 0x04
+#define M98090_REG_QUICK_SAMPLE_RATE 0x05
+#define M98090_REG_DAI_INTERFACE 0x06
+#define M98090_REG_DAC_PATH 0x07
+#define M98090_REG_MIC_DIRECT_TO_ADC 0x08
+#define M98090_REG_LINE_TO_ADC 0x09
+#define M98090_REG_ANALOG_MIC_LOOP 0x0A
+#define M98090_REG_ANALOG_LINE_LOOP 0x0B
+#define M98090_REG_RESERVED 0x0C
+#define M98090_REG_LINE_INPUT_CONFIG 0x0D
+#define M98090_REG_LINE_INPUT_LEVEL 0x0E
+#define M98090_REG_INPUT_MODE 0x0F
+#define M98090_REG_MIC1_INPUT_LEVEL 0x10
+#define M98090_REG_MIC2_INPUT_LEVEL 0x11
+#define M98090_REG_MIC_BIAS_VOLTAGE 0x12
+#define M98090_REG_DIGITAL_MIC_ENABLE 0x13
+#define M98090_REG_DIGITAL_MIC_CONFIG 0x14
+#define M98090_REG_LEFT_ADC_MIXER 0x15
+#define M98090_REG_RIGHT_ADC_MIXER 0x16
+#define M98090_REG_LEFT_ADC_LEVEL 0x17
+#define M98090_REG_RIGHT_ADC_LEVEL 0x18
+#define M98090_REG_ADC_BIQUAD_LEVEL 0x19
+#define M98090_REG_ADC_SIDETONE 0x1A
+#define M98090_REG_SYSTEM_CLOCK 0x1B
+#define M98090_REG_CLOCK_MODE 0x1C
+#define M98090_REG_CLOCK_RATIO_NI_MSB 0x1D
+#define M98090_REG_CLOCK_RATIO_NI_LSB 0x1E
+#define M98090_REG_CLOCK_RATIO_MI_MSB 0x1F
+#define M98090_REG_CLOCK_RATIO_MI_LSB 0x20
+#define M98090_REG_MASTER_MODE 0x21
+#define M98090_REG_INTERFACE_FORMAT 0x22
+#define M98090_REG_TDM_CONTROL 0x23
+#define M98090_REG_TDM_FORMAT 0x24
+#define M98090_REG_IO_CONFIGURATION 0x25
+#define M98090_REG_FILTER_CONFIG 0x26
+#define M98090_REG_DAI_PLAYBACK_LEVEL 0x27
+#define M98090_REG_DAI_PLAYBACK_LEVEL_EQ 0x28
+#define M98090_REG_LEFT_HP_MIXER 0x29
+#define M98090_REG_RIGHT_HP_MIXER 0x2A
+#define M98090_REG_HP_CONTROL 0x2B
+#define M98090_REG_LEFT_HP_VOLUME 0x2C
+#define M98090_REG_RIGHT_HP_VOLUME 0x2D
+#define M98090_REG_LEFT_SPK_MIXER 0x2E
+#define M98090_REG_RIGHT_SPK_MIXER 0x2F
+#define M98090_REG_SPK_CONTROL 0x30
+#define M98090_REG_LEFT_SPK_VOLUME 0x31
+#define M98090_REG_RIGHT_SPK_VOLUME 0x32
+#define M98090_REG_DRC_TIMING 0x33
+#define M98090_REG_DRC_COMPRESSOR 0x34
+#define M98090_REG_DRC_EXPANDER 0x35
+#define M98090_REG_DRC_GAIN 0x36
+#define M98090_REG_RCV_LOUTL_MIXER 0x37
+#define M98090_REG_RCV_LOUTL_CONTROL 0x38
+#define M98090_REG_RCV_LOUTL_VOLUME 0x39
+#define M98090_REG_LOUTR_MIXER 0x3A
+#define M98090_REG_LOUTR_CONTROL 0x3B
+#define M98090_REG_LOUTR_VOLUME 0x3C
+#define M98090_REG_JACK_DETECT 0x3D
+#define M98090_REG_INPUT_ENABLE 0x3E
+#define M98090_REG_OUTPUT_ENABLE 0x3F
+#define M98090_REG_LEVEL_CONTROL 0x40
+#define M98090_REG_DSP_FILTER_ENABLE 0x41
+#define M98090_REG_BIAS_CONTROL 0x42
+#define M98090_REG_DAC_CONTROL 0x43
+#define M98090_REG_ADC_CONTROL 0x44
+#define M98090_REG_DEVICE_SHUTDOWN 0x45
+#define M98090_REG_EQUALIZER_BASE 0x46
+#define M98090_REG_RECORD_BIQUAD_BASE 0xAF
+#define M98090_REG_DMIC3_VOLUME 0xBE
+#define M98090_REG_DMIC4_VOLUME 0xBF
+#define M98090_REG_DMIC34_BQ_PREATTEN 0xC0
+#define M98090_REG_RECORD_TDM_SLOT 0xC1
+#define M98090_REG_SAMPLE_RATE 0xC2
+#define M98090_REG_DMIC34_BIQUAD_BASE 0xC3
+#define M98090_REG_REVISION_ID 0xFF
+
+#define M98090_REG_CNT (0xFF+1)
+#define MAX98090_MAX_REGISTER 0xFF
+
+/* MAX98090 Register Bit Fields */
+
+/*
+ * M98090_REG_SOFTWARE_RESET
+ */
+#define M98090_SWRESET_MASK (1<<7)
+#define M98090_SWRESET_SHIFT 7
+#define M98090_SWRESET_WIDTH 1
+
+/*
+ * M98090_REG_DEVICE_STATUS
+ */
+#define M98090_CLD_MASK (1<<7)
+#define M98090_CLD_SHIFT 7
+#define M98090_CLD_WIDTH 1
+#define M98090_SLD_MASK (1<<6)
+#define M98090_SLD_SHIFT 6
+#define M98090_SLD_WIDTH 1
+#define M98090_ULK_MASK (1<<5)
+#define M98090_ULK_SHIFT 5
+#define M98090_ULK_WIDTH 1
+#define M98090_JDET_MASK (1<<2)
+#define M98090_JDET_SHIFT 2
+#define M98090_JDET_WIDTH 1
+#define M98090_DRCACT_MASK (1<<1)
+#define M98090_DRCACT_SHIFT 1
+#define M98090_DRCACT_WIDTH 1
+#define M98090_DRCCLP_MASK (1<<0)
+#define M98090_DRCCLP_SHIFT 0
+#define M98090_DRCCLP_WIDTH 1
+
+/*
+ * M98090_REG_JACK_STATUS
+ */
+#define M98090_LSNS_MASK (1<<2)
+#define M98090_LSNS_SHIFT 2
+#define M98090_LSNS_WIDTH 1
+#define M98090_JKSNS_MASK (1<<1)
+#define M98090_JKSNS_SHIFT 1
+#define M98090_JKSNS_WIDTH 1
+
+/*
+ * M98090_REG_INTERRUPT_S
+ */
+#define M98090_ICLD_MASK (1<<7)
+#define M98090_ICLD_SHIFT 7
+#define M98090_ICLD_WIDTH 1
+#define M98090_ISLD_MASK (1<<6)
+#define M98090_ISLD_SHIFT 6
+#define M98090_ISLD_WIDTH 1
+#define M98090_IULK_MASK (1<<5)
+#define M98090_IULK_SHIFT 5
+#define M98090_IULK_WIDTH 1
+#define M98090_IJDET_MASK (1<<2)
+#define M98090_IJDET_SHIFT 2
+#define M98090_IJDET_WIDTH 1
+#define M98090_IDRCACT_MASK (1<<1)
+#define M98090_IDRCACT_SHIFT 1
+#define M98090_IDRCACT_WIDTH 1
+#define M98090_IDRCCLP_MASK (1<<0)
+#define M98090_IDRCCLP_SHIFT 0
+#define M98090_IDRCCLP_WIDTH 1
+
+/*
+ * M98090_REG_QUICK_SYSTEM_CLOCK
+ */
+#define M98090_26M_MASK (1<<7)
+#define M98090_26M_SHIFT 7
+#define M98090_26M_WIDTH 1
+#define M98090_19P2M_MASK (1<<6)
+#define M98090_19P2M_SHIFT 6
+#define M98090_19P2M_WIDTH 1
+#define M98090_13M_MASK (1<<5)
+#define M98090_13M_SHIFT 5
+#define M98090_13M_WIDTH 1
+#define M98090_12P288M_MASK (1<<4)
+#define M98090_12P288M_SHIFT 4
+#define M98090_12P288M_WIDTH 1
+#define M98090_12M_MASK (1<<3)
+#define M98090_12M_SHIFT 3
+#define M98090_12M_WIDTH 1
+#define M98090_11P2896M_MASK (1<<2)
+#define M98090_11P2896M_SHIFT 2
+#define M98090_11P2896M_WIDTH 1
+#define M98090_256FS_MASK (1<<0)
+#define M98090_256FS_SHIFT 0
+#define M98090_256FS_WIDTH 1
+#define M98090_CLK_ALL_SHIFT 0
+#define M98090_CLK_ALL_WIDTH 8
+#define M98090_CLK_ALL_NUM (1<<M98090_CLK_ALL_WIDTH)
+
+/*
+ * M98090_REG_QUICK_SAMPLE_RATE
+ */
+#define M98090_SR_96K_MASK (1<<5)
+#define M98090_SR_96K_SHIFT 5
+#define M98090_SR_96K_WIDTH 1
+#define M98090_SR_32K_MASK (1<<4)
+#define M98090_SR_32K_SHIFT 4
+#define M98090_SR_32K_WIDTH 1
+#define M98090_SR_48K_MASK (1<<3)
+#define M98090_SR_48K_SHIFT 3
+#define M98090_SR_48K_WIDTH 1
+#define M98090_SR_44K1_MASK (1<<2)
+#define M98090_SR_44K1_SHIFT 2
+#define M98090_SR_44K1_WIDTH 1
+#define M98090_SR_16K_MASK (1<<1)
+#define M98090_SR_16K_SHIFT 1
+#define M98090_SR_16K_WIDTH 1
+#define M98090_SR_8K_MASK (1<<0)
+#define M98090_SR_8K_SHIFT 0
+#define M98090_SR_8K_WIDTH 1
+#define M98090_SR_MASK 0x3F
+#define M98090_SR_ALL_SHIFT 0
+#define M98090_SR_ALL_WIDTH 8
+#define M98090_SR_ALL_NUM (1<<M98090_SR_ALL_WIDTH)
+
+/*
+ * M98090_REG_DAI_INTERFACE
+ */
+#define M98090_RJ_M_MASK (1<<5)
+#define M98090_RJ_M_SHIFT 5
+#define M98090_RJ_M_WIDTH 1
+#define M98090_RJ_S_MASK (1<<4)
+#define M98090_RJ_S_SHIFT 4
+#define M98090_RJ_S_WIDTH 1
+#define M98090_LJ_M_MASK (1<<3)
+#define M98090_LJ_M_SHIFT 3
+#define M98090_LJ_M_WIDTH 1
+#define M98090_LJ_S_MASK (1<<2)
+#define M98090_LJ_S_SHIFT 2
+#define M98090_LJ_S_WIDTH 1
+#define M98090_I2S_M_MASK (1<<1)
+#define M98090_I2S_M_SHIFT 1
+#define M98090_I2S_M_WIDTH 1
+#define M98090_I2S_S_MASK (1<<0)
+#define M98090_I2S_S_SHIFT 0
+#define M98090_I2S_S_WIDTH 1
+#define M98090_DAI_ALL_SHIFT 0
+#define M98090_DAI_ALL_WIDTH 8
+#define M98090_DAI_ALL_NUM (1<<M98090_DAI_ALL_WIDTH)
+
+/*
+ * M98090_REG_DAC_PATH
+ */
+#define M98090_DIG2_HP_MASK (1<<7)
+#define M98090_DIG2_HP_SHIFT 7
+#define M98090_DIG2_HP_WIDTH 1
+#define M98090_DIG2_EAR_MASK (1<<6)
+#define M98090_DIG2_EAR_SHIFT 6
+#define M98090_DIG2_EAR_WIDTH 1
+#define M98090_DIG2_SPK_MASK (1<<5)
+#define M98090_DIG2_SPK_SHIFT 5
+#define M98090_DIG2_SPK_WIDTH 1
+#define M98090_DIG2_LOUT_MASK (1<<4)
+#define M98090_DIG2_LOUT_SHIFT 4
+#define M98090_DIG2_LOUT_WIDTH 1
+#define M98090_DIG2_ALL_SHIFT 0
+#define M98090_DIG2_ALL_WIDTH 8
+#define M98090_DIG2_ALL_NUM (1<<M98090_DIG2_ALL_WIDTH)
+
+/*
+ * M98090_REG_MIC_DIRECT_TO_ADC
+ */
+#define M98090_IN12_MIC1_MASK (1<<7)
+#define M98090_IN12_MIC1_SHIFT 7
+#define M98090_IN12_MIC1_WIDTH 1
+#define M98090_IN34_MIC2_MASK (1<<6)
+#define M98090_IN34_MIC2_SHIFT 6
+#define M98090_IN34_MIC2_WIDTH 1
+#define M98090_IN56_MIC1_MASK (1<<5)
+#define M98090_IN56_MIC1_SHIFT 5
+#define M98090_IN56_MIC1_WIDTH 1
+#define M98090_IN56_MIC2_MASK (1<<4)
+#define M98090_IN56_MIC2_SHIFT 4
+#define M98090_IN56_MIC2_WIDTH 1
+#define M98090_IN12_DADC_MASK (1<<3)
+#define M98090_IN12_DADC_SHIFT 3
+#define M98090_IN12_DADC_WIDTH 1
+#define M98090_IN34_DADC_MASK (1<<2)
+#define M98090_IN34_DADC_SHIFT 2
+#define M98090_IN34_DADC_WIDTH 1
+#define M98090_IN56_DADC_MASK (1<<1)
+#define M98090_IN56_DADC_SHIFT 1
+#define M98090_IN56_DADC_WIDTH 1
+#define M98090_MIC_ALL_SHIFT 0
+#define M98090_MIC_ALL_WIDTH 8
+#define M98090_MIC_ALL_NUM (1<<M98090_MIC_ALL_WIDTH)
+
+/*
+ * M98090_REG_LINE_TO_ADC
+ */
+#define M98090_IN12S_AB_MASK (1<<7)
+#define M98090_IN12S_AB_SHIFT 7
+#define M98090_IN12S_AB_WIDTH 1
+#define M98090_IN34S_AB_MASK (1<<6)
+#define M98090_IN34S_AB_SHIFT 6
+#define M98090_IN34S_AB_WIDTH 1
+#define M98090_IN56S_AB_MASK (1<<5)
+#define M98090_IN56S_AB_SHIFT 5
+#define M98090_IN56S_AB_WIDTH 1
+#define M98090_IN34D_A_MASK (1<<4)
+#define M98090_IN34D_A_SHIFT 4
+#define M98090_IN34D_A_WIDTH 1
+#define M98090_IN56D_B_MASK (1<<3)
+#define M98090_IN56D_B_SHIFT 3
+#define M98090_IN56D_B_WIDTH 1
+#define M98090_LINE_ALL_SHIFT 0
+#define M98090_LINE_ALL_WIDTH 8
+#define M98090_LINE_ALL_NUM (1<<M98090_LINE_ALL_WIDTH)
+
+/*
+ * M98090_REG_ANALOG_MIC_LOOP
+ */
+#define M98090_IN12_M1HPL_MASK (1<<7)
+#define M98090_IN12_M1HPL_SHIFT 7
+#define M98090_IN12_M1HPL_WIDTH 1
+#define M98090_IN12_M1SPKL_MASK (1<<6)
+#define M98090_IN12_M1SPKL_SHIFT 6
+#define M98090_IN12_M1SPKL_WIDTH 1
+#define M98090_IN12_M1EAR_MASK (1<<5)
+#define M98090_IN12_M1EAR_SHIFT 5
+#define M98090_IN12_M1EAR_WIDTH 1
+#define M98090_IN12_M1LOUTL_MASK (1<<4)
+#define M98090_IN12_M1LOUTL_SHIFT 4
+#define M98090_IN12_M1LOUTL_WIDTH 1
+#define M98090_IN34_M2HPR_MASK (1<<3)
+#define M98090_IN34_M2HPR_SHIFT 3
+#define M98090_IN34_M2HPR_WIDTH 1
+#define M98090_IN34_M2SPKR_MASK (1<<2)
+#define M98090_IN34_M2SPKR_SHIFT 2
+#define M98090_IN34_M2SPKR_WIDTH 1
+#define M98090_IN34_M2EAR_MASK (1<<1)
+#define M98090_IN34_M2EAR_SHIFT 1
+#define M98090_IN34_M2EAR_WIDTH 1
+#define M98090_IN34_M2LOUTR_MASK (1<<0)
+#define M98090_IN34_M2LOUTR_SHIFT 0
+#define M98090_IN34_M2LOUTR_WIDTH 1
+#define M98090_AMIC_ALL_SHIFT 0
+#define M98090_AMIC_ALL_WIDTH 8
+#define M98090_AMIC_ALL_NUM (1<<M98090_AMIC_ALL_WIDTH)
+
+/*
+ * M98090_REG_ANALOG_LINE_LOOP
+ */
+#define M98090_IN12S_ABHP_MASK (1<<7)
+#define M98090_IN12S_ABHP_SHIFT 7
+#define M98090_IN12S_ABHP_WIDTH 1
+#define M98090_IN34D_ASPKL_MASK (1<<6)
+#define M98090_IN34D_ASPKL_SHIFT 6
+#define M98090_IN34D_ASPKL_WIDTH 1
+#define M98090_IN34D_AEAR_MASK (1<<5)
+#define M98090_IN34D_AEAR_SHIFT 5
+#define M98090_IN34D_AEAR_WIDTH 1
+#define M98090_IN12S_ABLOUT_MASK (1<<4)
+#define M98090_IN12S_ABLOUT_SHIFT 4
+#define M98090_IN12S_ABLOUT_WIDTH 1
+#define M98090_IN34S_ABHP_MASK (1<<3)
+#define M98090_IN34S_ABHP_SHIFT 3
+#define M98090_IN34S_ABHP_WIDTH 1
+#define M98090_IN56D_BSPKR_MASK (1<<2)
+#define M98090_IN56D_BSPKR_SHIFT 2
+#define M98090_IN56D_BSPKR_WIDTH 1
+#define M98090_IN56D_BEAR_MASK (1<<1)
+#define M98090_IN56D_BEAR_SHIFT 1
+#define M98090_IN56D_BEAR_WIDTH 1
+#define M98090_IN34S_ABLOUT_MASK (1<<0)
+#define M98090_IN34S_ABLOUT_SHIFT 0
+#define M98090_IN34S_ABLOUT_WIDTH 1
+#define M98090_ALIN_ALL_SHIFT 0
+#define M98090_ALIN_ALL_WIDTH 8
+#define M98090_ALIN_ALL_NUM (1<<M98090_ALIN_ALL_WIDTH)
+
+/*
+ * M98090_REG_RESERVED
+ */
+
+/*
+ * M98090_REG_LINE_INPUT_CONFIG
+ */
+#define M98090_IN34DIFF_MASK (1<<7)
+#define M98090_IN34DIFF_SHIFT 7
+#define M98090_IN34DIFF_WIDTH 1
+#define M98090_IN56DIFF_MASK (1<<6)
+#define M98090_IN56DIFF_SHIFT 6
+#define M98090_IN56DIFF_WIDTH 1
+#define M98090_IN1SEEN_MASK (1<<5)
+#define M98090_IN1SEEN_SHIFT 5
+#define M98090_IN1SEEN_WIDTH 1
+#define M98090_IN2SEEN_MASK (1<<4)
+#define M98090_IN2SEEN_SHIFT 4
+#define M98090_IN2SEEN_WIDTH 1
+#define M98090_IN3SEEN_MASK (1<<3)
+#define M98090_IN3SEEN_SHIFT 3
+#define M98090_IN3SEEN_WIDTH 1
+#define M98090_IN4SEEN_MASK (1<<2)
+#define M98090_IN4SEEN_SHIFT 2
+#define M98090_IN4SEEN_WIDTH 1
+#define M98090_IN5SEEN_MASK (1<<1)
+#define M98090_IN5SEEN_SHIFT 1
+#define M98090_IN5SEEN_WIDTH 1
+#define M98090_IN6SEEN_MASK (1<<0)
+#define M98090_IN6SEEN_SHIFT 0
+#define M98090_IN6SEEN_WIDTH 1
+
+/*
+ * M98090_REG_LINE_INPUT_LEVEL
+ */
+#define M98090_MIXG135_MASK (1<<7)
+#define M98090_MIXG135_SHIFT 7
+#define M98090_MIXG135_WIDTH 1
+#define M98090_MIXG135_NUM (1<<M98090_MIXG135_WIDTH)
+#define M98090_MIXG246_MASK (1<<6)
+#define M98090_MIXG246_SHIFT 6
+#define M98090_MIXG246_WIDTH 1
+#define M98090_MIXG246_NUM (1<<M98090_MIXG246_WIDTH)
+#define M98090_LINAPGA_MASK (7<<3)
+#define M98090_LINAPGA_SHIFT 3
+#define M98090_LINAPGA_WIDTH 3
+#define M98090_LINAPGA_NUM 6
+#define M98090_LINBPGA_MASK (7<<0)
+#define M98090_LINBPGA_SHIFT 0
+#define M98090_LINBPGA_WIDTH 3
+#define M98090_LINBPGA_NUM 6
+
+/*
+ * M98090_REG_INPUT_MODE
+ */
+#define M98090_EXTBUFA_MASK (1<<7)
+#define M98090_EXTBUFA_SHIFT 7
+#define M98090_EXTBUFA_WIDTH 1
+#define M98090_EXTBUFA_NUM (1<<M98090_EXTBUFA_WIDTH)
+#define M98090_EXTBUFB_MASK (1<<6)
+#define M98090_EXTBUFB_SHIFT 6
+#define M98090_EXTBUFB_WIDTH 1
+#define M98090_EXTBUFB_NUM (1<<M98090_EXTBUFB_WIDTH)
+#define M98090_EXTMIC_MASK (3<<0)
+#define M98090_EXTMIC_SHIFT 0
+#define M98090_EXTMIC1_SHIFT 0
+#define M98090_EXTMIC2_SHIFT 1
+#define M98090_EXTMIC_WIDTH 2
+#define M98090_EXTMIC_NONE (0<<0)
+#define M98090_EXTMIC_MIC1 (1<<0)
+#define M98090_EXTMIC_MIC2 (2<<0)
+
+/*
+ * M98090_REG_MIC1_INPUT_LEVEL
+ */
+#define M98090_MIC_PA1EN_MASK (3<<5)
+#define M98090_MIC_PA1EN_SHIFT 5
+#define M98090_MIC_PA1EN_WIDTH 2
+#define M98090_MIC_PA1EN_NUM 3
+#define M98090_MIC_PGAM1_MASK (31<<0)
+#define M98090_MIC_PGAM1_SHIFT 0
+#define M98090_MIC_PGAM1_WIDTH 5
+#define M98090_MIC_PGAM1_NUM 21
+
+/*
+ * M98090_REG_MIC2_INPUT_LEVEL
+ */
+#define M98090_MIC_PA2EN_MASK (3<<5)
+#define M98090_MIC_PA2EN_SHIFT 5
+#define M98090_MIC_PA2EN_WIDTH 2
+#define M98090_MIC_PA2EN_NUM 3
+#define M98090_MIC_PGAM2_MASK (31<<0)
+#define M98090_MIC_PGAM2_SHIFT 0
+#define M98090_MIC_PGAM2_WIDTH 5
+#define M98090_MIC_PGAM2_NUM 21
+
+/*
+ * M98090_REG_MIC_BIAS_VOLTAGE
+ */
+#define M98090_MBVSEL_MASK (3<<0)
+#define M98090_MBVSEL_SHIFT 0
+#define M98090_MBVSEL_WIDTH 2
+#define M98090_MBVSEL_2V8 (3<<0)
+#define M98090_MBVSEL_2V55 (2<<0)
+#define M98090_MBVSEL_2V4 (1<<0)
+#define M98090_MBVSEL_2V2 (0<<0)
+
+/*
+ * M98090_REG_DIGITAL_MIC_ENABLE
+ */
+#define M98090_MICCLK_MASK (7<<4)
+#define M98090_MICCLK_SHIFT 4
+#define M98090_MICCLK_WIDTH 3
+#define M98090_DIGMIC4_MASK (1<<3)
+#define M98090_DIGMIC4_SHIFT 3
+#define M98090_DIGMIC4_WIDTH 1
+#define M98090_DIGMIC4_NUM (1<<M98090_DIGMIC4_WIDTH)
+#define M98090_DIGMIC3_MASK (1<<2)
+#define M98090_DIGMIC3_SHIFT 2
+#define M98090_DIGMIC3_WIDTH 1
+#define M98090_DIGMIC3_NUM (1<<M98090_DIGMIC3_WIDTH)
+#define M98090_DIGMICR_MASK (1<<1)
+#define M98090_DIGMICR_SHIFT 1
+#define M98090_DIGMICR_WIDTH 1
+#define M98090_DIGMICR_NUM (1<<M98090_DIGMICR_WIDTH)
+#define M98090_DIGMICL_MASK (1<<0)
+#define M98090_DIGMICL_SHIFT 0
+#define M98090_DIGMICL_WIDTH 1
+#define M98090_DIGMICL_NUM (1<<M98090_DIGMICL_WIDTH)
+
+/*
+ * M98090_REG_DIGITAL_MIC_CONFIG
+ */
+#define M98090_DMIC_COMP_MASK (15<<4)
+#define M98090_DMIC_COMP_SHIFT 4
+#define M98090_DMIC_COMP_WIDTH 4
+#define M98090_DMIC_COMP_NUM (1<<M98090_DMIC_COMP_WIDTH)
+#define M98090_DMIC_FREQ_MASK (3<<0)
+#define M98090_DMIC_FREQ_SHIFT 0
+#define M98090_DMIC_FREQ_WIDTH 2
+
+/*
+ * M98090_REG_LEFT_ADC_MIXER
+ */
+#define M98090_MIXADL_MIC2_MASK (1<<6)
+#define M98090_MIXADL_MIC2_SHIFT 6
+#define M98090_MIXADL_MIC2_WIDTH 1
+#define M98090_MIXADL_MIC1_MASK (1<<5)
+#define M98090_MIXADL_MIC1_SHIFT 5
+#define M98090_MIXADL_MIC1_WIDTH 1
+#define M98090_MIXADL_LINEB_MASK (1<<4)
+#define M98090_MIXADL_LINEB_SHIFT 4
+#define M98090_MIXADL_LINEB_WIDTH 1
+#define M98090_MIXADL_LINEA_MASK (1<<3)
+#define M98090_MIXADL_LINEA_SHIFT 3
+#define M98090_MIXADL_LINEA_WIDTH 1
+#define M98090_MIXADL_IN65DIFF_MASK (1<<2)
+#define M98090_MIXADL_IN65DIFF_SHIFT 2
+#define M98090_MIXADL_IN65DIFF_WIDTH 1
+#define M98090_MIXADL_IN34DIFF_MASK (1<<1)
+#define M98090_MIXADL_IN34DIFF_SHIFT 1
+#define M98090_MIXADL_IN34DIFF_WIDTH 1
+#define M98090_MIXADL_IN12DIFF_MASK (1<<0)
+#define M98090_MIXADL_IN12DIFF_SHIFT 0
+#define M98090_MIXADL_IN12DIFF_WIDTH 1
+#define M98090_MIXADL_MASK (255<<0)
+#define M98090_MIXADL_SHIFT 0
+#define M98090_MIXADL_WIDTH 8
+
+/*
+ * M98090_REG_RIGHT_ADC_MIXER
+ */
+#define M98090_MIXADR_MIC2_MASK (1<<6)
+#define M98090_MIXADR_MIC2_SHIFT 6
+#define M98090_MIXADR_MIC2_WIDTH 1
+#define M98090_MIXADR_MIC1_MASK (1<<5)
+#define M98090_MIXADR_MIC1_SHIFT 5
+#define M98090_MIXADR_MIC1_WIDTH 1
+#define M98090_MIXADR_LINEB_MASK (1<<4)
+#define M98090_MIXADR_LINEB_SHIFT 4
+#define M98090_MIXADR_LINEB_WIDTH 1
+#define M98090_MIXADR_LINEA_MASK (1<<3)
+#define M98090_MIXADR_LINEA_SHIFT 3
+#define M98090_MIXADR_LINEA_WIDTH 1
+#define M98090_MIXADR_IN65DIFF_MASK (1<<2)
+#define M98090_MIXADR_IN65DIFF_SHIFT 2
+#define M98090_MIXADR_IN65DIFF_WIDTH 1
+#define M98090_MIXADR_IN34DIFF_MASK (1<<1)
+#define M98090_MIXADR_IN34DIFF_SHIFT 1
+#define M98090_MIXADR_IN34DIFF_WIDTH 1
+#define M98090_MIXADR_IN12DIFF_MASK (1<<0)
+#define M98090_MIXADR_IN12DIFF_SHIFT 0
+#define M98090_MIXADR_IN12DIFF_WIDTH 1
+#define M98090_MIXADR_MASK (255<<0)
+#define M98090_MIXADR_SHIFT 0
+#define M98090_MIXADR_WIDTH 8
+
+/*
+ * M98090_REG_LEFT_ADC_LEVEL
+ */
+#define M98090_AVLG_MASK (7<<4)
+#define M98090_AVLG_SHIFT 4
+#define M98090_AVLG_WIDTH 3
+#define M98090_AVLG_NUM (1<<M98090_AVLG_WIDTH)
+#define M98090_AVL_MASK (15<<0)
+#define M98090_AVL_SHIFT 0
+#define M98090_AVL_WIDTH 4
+#define M98090_AVL_NUM (1<<M98090_AVL_WIDTH)
+
+/*
+ * M98090_REG_RIGHT_ADC_LEVEL
+ */
+#define M98090_AVRG_MASK (7<<4)
+#define M98090_AVRG_SHIFT 4
+#define M98090_AVRG_WIDTH 3
+#define M98090_AVRG_NUM (1<<M98090_AVRG_WIDTH)
+#define M98090_AVR_MASK (15<<0)
+#define M98090_AVR_SHIFT 0
+#define M98090_AVR_WIDTH 4
+#define M98090_AVR_NUM (1<<M98090_AVR_WIDTH)
+
+/*
+ * M98090_REG_ADC_BIQUAD_LEVEL
+ */
+#define M98090_AVBQ_MASK (15<<0)
+#define M98090_AVBQ_SHIFT 0
+#define M98090_AVBQ_WIDTH 4
+#define M98090_AVBQ_NUM (1<<M98090_AVBQ_WIDTH)
+
+/*
+ * M98090_REG_ADC_SIDETONE
+ */
+#define M98090_DSTSR_MASK (1<<7)
+#define M98090_DSTSR_SHIFT 7
+#define M98090_DSTSR_WIDTH 1
+#define M98090_DSTSL_MASK (1<<6)
+#define M98090_DSTSL_SHIFT 6
+#define M98090_DSTSL_WIDTH 1
+#define M98090_DVST_MASK (31<<0)
+#define M98090_DVST_SHIFT 0
+#define M98090_DVST_WIDTH 5
+#define M98090_DVST_NUM 31
+
+/*
+ * M98090_REG_SYSTEM_CLOCK
+ */
+#define M98090_PSCLK_MASK (3<<4)
+#define M98090_PSCLK_SHIFT 4
+#define M98090_PSCLK_WIDTH 2
+#define M98090_PSCLK_DISABLED (0<<4)
+#define M98090_PSCLK_DIV1 (1<<4)
+#define M98090_PSCLK_DIV2 (2<<4)
+#define M98090_PSCLK_DIV4 (3<<4)
+
+/*
+ * M98090_REG_CLOCK_MODE
+ */
+#define M98090_FREQ_MASK (15<<4)
+#define M98090_FREQ_SHIFT 4
+#define M98090_FREQ_WIDTH 4
+#define M98090_USE_M1_MASK (1<<0)
+#define M98090_USE_M1_SHIFT 0
+#define M98090_USE_M1_WIDTH 1
+#define M98090_USE_M1_NUM (1<<M98090_USE_M1_WIDTH)
+
+/*
+ * M98090_REG_CLOCK_RATIO_NI_MSB
+ */
+#define M98090_NI_HI_MASK (127<<0)
+#define M98090_NI_HI_SHIFT 0
+#define M98090_NI_HI_WIDTH 7
+#define M98090_NI_HI_NUM (1<<M98090_NI_HI_WIDTH)
+
+/*
+ * M98090_REG_CLOCK_RATIO_NI_LSB
+ */
+#define M98090_NI_LO_MASK (255<<0)
+#define M98090_NI_LO_SHIFT 0
+#define M98090_NI_LO_WIDTH 8
+#define M98090_NI_LO_NUM (1<<M98090_NI_LO_WIDTH)
+
+/*
+ * M98090_REG_CLOCK_RATIO_MI_MSB
+ */
+#define M98090_MI_HI_MASK (255<<0)
+#define M98090_MI_HI_SHIFT 0
+#define M98090_MI_HI_WIDTH 8
+#define M98090_MI_HI_NUM (1<<M98090_MI_HI_WIDTH)
+
+/*
+ * M98090_REG_CLOCK_RATIO_MI_LSB
+ */
+#define M98090_MI_LO_MASK (255<<0)
+#define M98090_MI_LO_SHIFT 0
+#define M98090_MI_LO_WIDTH 8
+#define M98090_MI_LO_NUM (1<<M98090_MI_LO_WIDTH)
+
+/*
+ * M98090_REG_MASTER_MODE
+ */
+#define M98090_MAS_MASK (1<<7)
+#define M98090_MAS_SHIFT 7
+#define M98090_MAS_WIDTH 1
+#define M98090_BSEL_MASK (1<<0)
+#define M98090_BSEL_SHIFT 0
+#define M98090_BSEL_WIDTH 1
+#define M98090_BSEL_32 (1<<0)
+#define M98090_BSEL_48 (2<<0)
+#define M98090_BSEL_64 (3<<0)
+
+/*
+ * M98090_REG_INTERFACE_FORMAT
+ */
+#define M98090_RJ_MASK (1<<5)
+#define M98090_RJ_SHIFT 5
+#define M98090_RJ_WIDTH 1
+#define M98090_WCI_MASK (1<<4)
+#define M98090_WCI_SHIFT 4
+#define M98090_WCI_WIDTH 1
+#define M98090_BCI_MASK (1<<3)
+#define M98090_BCI_SHIFT 3
+#define M98090_BCI_WIDTH 1
+#define M98090_DLY_MASK (1<<2)
+#define M98090_DLY_SHIFT 2
+#define M98090_DLY_WIDTH 1
+#define M98090_WS_MASK (3<<0)
+#define M98090_WS_SHIFT 0
+#define M98090_WS_WIDTH 2
+#define M98090_WS_NUM (1<<M98090_WS_WIDTH)
+
+/*
+ * M98090_REG_TDM_CONTROL
+ */
+#define M98090_FSW_MASK (1<<1)
+#define M98090_FSW_SHIFT 1
+#define M98090_FSW_WIDTH 1
+#define M98090_TDM_MASK (1<<0)
+#define M98090_TDM_SHIFT 0
+#define M98090_TDM_WIDTH 1
+#define M98090_TDM_NUM (1<<M98090_TDM_WIDTH)
+
+/*
+ * M98090_REG_TDM_FORMAT
+ */
+#define M98090_TDM_SLOTL_MASK (3<<6)
+#define M98090_TDM_SLOTL_SHIFT 6
+#define M98090_TDM_SLOTL_WIDTH 2
+#define M98090_TDM_SLOTL_NUM (1<<M98090_TDM_SLOTL_WIDTH)
+#define M98090_TDM_SLOTR_MASK (3<<4)
+#define M98090_TDM_SLOTR_SHIFT 4
+#define M98090_TDM_SLOTR_WIDTH 2
+#define M98090_TDM_SLOTR_NUM (1<<M98090_TDM_SLOTR_WIDTH)
+#define M98090_TDM_SLOTDLY_MASK (15<<0)
+#define M98090_TDM_SLOTDLY_SHIFT 0
+#define M98090_TDM_SLOTDLY_WIDTH 4
+#define M98090_TDM_SLOTDLY_NUM (1<<M98090_TDM_SLOTDLY_WIDTH)
+
+/*
+ * M98090_REG_IO_CONFIGURATION
+ */
+#define M98090_LTEN_MASK (1<<5)
+#define M98090_LTEN_SHIFT 5
+#define M98090_LTEN_WIDTH 1
+#define M98090_LTEN_NUM (1<<M98090_LTEN_WIDTH)
+#define M98090_LBEN_MASK (1<<4)
+#define M98090_LBEN_SHIFT 4
+#define M98090_LBEN_WIDTH 1
+#define M98090_LBEN_NUM (1<<M98090_LBEN_WIDTH)
+#define M98090_DMONO_MASK (1<<3)
+#define M98090_DMONO_SHIFT 3
+#define M98090_DMONO_WIDTH 1
+#define M98090_DMONO_NUM (1<<M98090_DMONO_WIDTH)
+#define M98090_HIZOFF_MASK (1<<2)
+#define M98090_HIZOFF_SHIFT 2
+#define M98090_HIZOFF_WIDTH 1
+#define M98090_HIZOFF_NUM (1<<M98090_HIZOFF_WIDTH)
+#define M98090_SDOEN_MASK (1<<1)
+#define M98090_SDOEN_SHIFT 1
+#define M98090_SDOEN_WIDTH 1
+#define M98090_SDOEN_NUM (1<<M98090_SDOEN_WIDTH)
+#define M98090_SDIEN_MASK (1<<0)
+#define M98090_SDIEN_SHIFT 0
+#define M98090_SDIEN_WIDTH 1
+#define M98090_SDIEN_NUM (1<<M98090_SDIEN_WIDTH)
+
+/*
+ * M98090_REG_FILTER_CONFIG
+ */
+#define M98090_MODE_MASK (1<<7)
+#define M98090_MODE_SHIFT 7
+#define M98090_MODE_WIDTH 1
+#define M98090_AHPF_MASK (1<<6)
+#define M98090_AHPF_SHIFT 6
+#define M98090_AHPF_WIDTH 1
+#define M98090_AHPF_NUM (1<<M98090_AHPF_WIDTH)
+#define M98090_DHPF_MASK (1<<5)
+#define M98090_DHPF_SHIFT 5
+#define M98090_DHPF_WIDTH 1
+#define M98090_DHPF_NUM (1<<M98090_DHPF_WIDTH)
+#define M98090_DHF_MASK (1<<4)
+#define M98090_DHF_SHIFT 4
+#define M98090_DHF_WIDTH 1
+#define M98090_FLT_DMIC34MODE_MASK (1<<3)
+#define M98090_FLT_DMIC34MODE_SHIFT 3
+#define M98090_FLT_DMIC34MODE_WIDTH 1
+#define M98090_FLT_DMIC34HPF_MASK (1<<2)
+#define M98090_FLT_DMIC34HPF_SHIFT 2
+#define M98090_FLT_DMIC34HPF_WIDTH 1
+#define M98090_FLT_DMIC34HPF_NUM (1<<M98090_FLT_DMIC34HPF_WIDTH)
+
+/*
+ * M98090_REG_DAI_PLAYBACK_LEVEL
+ */
+#define M98090_DVM_MASK (1<<7)
+#define M98090_DVM_SHIFT 7
+#define M98090_DVM_WIDTH 1
+#define M98090_DVG_MASK (3<<4)
+#define M98090_DVG_SHIFT 4
+#define M98090_DVG_WIDTH 2
+#define M98090_DVG_NUM (1<<M98090_DVG_WIDTH)
+#define M98090_DV_MASK (15<<0)
+#define M98090_DV_SHIFT 0
+#define M98090_DV_WIDTH 4
+#define M98090_DV_NUM (1<<M98090_DV_WIDTH)
+
+/*
+ * M98090_REG_DAI_PLAYBACK_LEVEL_EQ
+ */
+#define M98090_EQCLPN_MASK (1<<4)
+#define M98090_EQCLPN_SHIFT 4
+#define M98090_EQCLPN_WIDTH 1
+#define M98090_EQCLPN_NUM (1<<M98090_EQCLPN_WIDTH)
+#define M98090_DVEQ_MASK (15<<0)
+#define M98090_DVEQ_SHIFT 0
+#define M98090_DVEQ_WIDTH 4
+#define M98090_DVEQ_NUM (1<<M98090_DVEQ_WIDTH)
+
+/*
+ * M98090_REG_LEFT_HP_MIXER
+ */
+#define M98090_MIXHPL_MIC2_MASK (1<<5)
+#define M98090_MIXHPL_MIC2_SHIFT 5
+#define M98090_MIXHPL_MIC2_WIDTH 1
+#define M98090_MIXHPL_MIC1_MASK (1<<4)
+#define M98090_MIXHPL_MIC1_SHIFT 4
+#define M98090_MIXHPL_MIC1_WIDTH 1
+#define M98090_MIXHPL_LINEB_MASK (1<<3)
+#define M98090_MIXHPL_LINEB_SHIFT 3
+#define M98090_MIXHPL_LINEB_WIDTH 1
+#define M98090_MIXHPL_LINEA_MASK (1<<2)
+#define M98090_MIXHPL_LINEA_SHIFT 2
+#define M98090_MIXHPL_LINEA_WIDTH 1
+#define M98090_MIXHPL_DACR_MASK (1<<1)
+#define M98090_MIXHPL_DACR_SHIFT 1
+#define M98090_MIXHPL_DACR_WIDTH 1
+#define M98090_MIXHPL_DACL_MASK (1<<0)
+#define M98090_MIXHPL_DACL_SHIFT 0
+#define M98090_MIXHPL_DACL_WIDTH 1
+#define M98090_MIXHPL_MASK (63<<0)
+#define M98090_MIXHPL_SHIFT 0
+#define M98090_MIXHPL_WIDTH 6
+
+/*
+ * M98090_REG_RIGHT_HP_MIXER
+ */
+#define M98090_MIXHPR_MIC2_MASK (1<<5)
+#define M98090_MIXHPR_MIC2_SHIFT 5
+#define M98090_MIXHPR_MIC2_WIDTH 1
+#define M98090_MIXHPR_MIC1_MASK (1<<4)
+#define M98090_MIXHPR_MIC1_SHIFT 4
+#define M98090_MIXHPR_MIC1_WIDTH 1
+#define M98090_MIXHPR_LINEB_MASK (1<<3)
+#define M98090_MIXHPR_LINEB_SHIFT 3
+#define M98090_MIXHPR_LINEB_WIDTH 1
+#define M98090_MIXHPR_LINEA_MASK (1<<2)
+#define M98090_MIXHPR_LINEA_SHIFT 2
+#define M98090_MIXHPR_LINEA_WIDTH 1
+#define M98090_MIXHPR_DACR_MASK (1<<1)
+#define M98090_MIXHPR_DACR_SHIFT 1
+#define M98090_MIXHPR_DACR_WIDTH 1
+#define M98090_MIXHPR_DACL_MASK (1<<0)
+#define M98090_MIXHPR_DACL_SHIFT 0
+#define M98090_MIXHPR_DACL_WIDTH 1
+#define M98090_MIXHPR_MASK (63<<0)
+#define M98090_MIXHPR_SHIFT 0
+#define M98090_MIXHPR_WIDTH 6
+
+/*
+ * M98090_REG_HP_CONTROL
+ */
+#define M98090_MIXHPRSEL_MASK (1<<5)
+#define M98090_MIXHPRSEL_SHIFT 5
+#define M98090_MIXHPRSEL_WIDTH 1
+#define M98090_MIXHPLSEL_MASK (1<<4)
+#define M98090_MIXHPLSEL_SHIFT 4
+#define M98090_MIXHPLSEL_WIDTH 1
+#define M98090_MIXHPRG_MASK (3<<2)
+#define M98090_MIXHPRG_SHIFT 2
+#define M98090_MIXHPRG_WIDTH 2
+#define M98090_MIXHPRG_NUM (1<<M98090_MIXHPRG_WIDTH)
+#define M98090_MIXHPLG_MASK (3<<0)
+#define M98090_MIXHPLG_SHIFT 0
+#define M98090_MIXHPLG_WIDTH 2
+#define M98090_MIXHPLG_NUM (1<<M98090_MIXHPLG_WIDTH)
+
+/*
+ * M98090_REG_LEFT_HP_VOLUME
+ */
+#define M98090_HPLM_MASK (1<<7)
+#define M98090_HPLM_SHIFT 7
+#define M98090_HPLM_WIDTH 1
+#define M98090_HPVOLL_MASK (31<<0)
+#define M98090_HPVOLL_SHIFT 0
+#define M98090_HPVOLL_WIDTH 5
+#define M98090_HPVOLL_NUM (1<<M98090_HPVOLL_WIDTH)
+
+/*
+ * M98090_REG_RIGHT_HP_VOLUME
+ */
+#define M98090_HPRM_MASK (1<<7)
+#define M98090_HPRM_SHIFT 7
+#define M98090_HPRM_WIDTH 1
+#define M98090_HPVOLR_MASK (31<<0)
+#define M98090_HPVOLR_SHIFT 0
+#define M98090_HPVOLR_WIDTH 5
+#define M98090_HPVOLR_NUM (1<<M98090_HPVOLR_WIDTH)
+
+/*
+ * M98090_REG_LEFT_SPK_MIXER
+ */
+#define M98090_MIXSPL_MIC2_MASK (1<<5)
+#define M98090_MIXSPL_MIC2_SHIFT 5
+#define M98090_MIXSPL_MIC2_WIDTH 1
+#define M98090_MIXSPL_MIC1_MASK (1<<4)
+#define M98090_MIXSPL_MIC1_SHIFT 4
+#define M98090_MIXSPL_MIC1_WIDTH 1
+#define M98090_MIXSPL_LINEB_MASK (1<<3)
+#define M98090_MIXSPL_LINEB_SHIFT 3
+#define M98090_MIXSPL_LINEB_WIDTH 1
+#define M98090_MIXSPL_LINEA_MASK (1<<2)
+#define M98090_MIXSPL_LINEA_SHIFT 2
+#define M98090_MIXSPL_LINEA_WIDTH 1
+#define M98090_MIXSPL_DACR_MASK (1<<1)
+#define M98090_MIXSPL_DACR_SHIFT 1
+#define M98090_MIXSPL_DACR_WIDTH 1
+#define M98090_MIXSPL_DACL_MASK (1<<0)
+#define M98090_MIXSPL_DACL_SHIFT 0
+#define M98090_MIXSPL_DACL_WIDTH 1
+#define M98090_MIXSPL_MASK (63<<0)
+#define M98090_MIXSPL_SHIFT 0
+#define M98090_MIXSPL_WIDTH 6
+#define M98090_MIXSPR_DACR_MASK (1<<1)
+#define M98090_MIXSPR_DACR_SHIFT 1
+#define M98090_MIXSPR_DACR_WIDTH 1
+
+
+/*
+ * M98090_REG_RIGHT_SPK_MIXER
+ */
+#define M98090_SPK_SLAVE_MASK (1<<6)
+#define M98090_SPK_SLAVE_SHIFT 6
+#define M98090_SPK_SLAVE_WIDTH 1
+#define M98090_MIXSPR_MIC2_MASK (1<<5)
+#define M98090_MIXSPR_MIC2_SHIFT 5
+#define M98090_MIXSPR_MIC2_WIDTH 1
+#define M98090_MIXSPR_MIC1_MASK (1<<4)
+#define M98090_MIXSPR_MIC1_SHIFT 4
+#define M98090_MIXSPR_MIC1_WIDTH 1
+#define M98090_MIXSPR_LINEB_MASK (1<<3)
+#define M98090_MIXSPR_LINEB_SHIFT 3
+#define M98090_MIXSPR_LINEB_WIDTH 1
+#define M98090_MIXSPR_LINEA_MASK (1<<2)
+#define M98090_MIXSPR_LINEA_SHIFT 2
+#define M98090_MIXSPR_LINEA_WIDTH 1
+#define M98090_MIXSPR_DACR_MASK (1<<1)
+#define M98090_MIXSPR_DACR_SHIFT 1
+#define M98090_MIXSPR_DACR_WIDTH 1
+#define M98090_MIXSPR_DACL_MASK (1<<0)
+#define M98090_MIXSPR_DACL_SHIFT 0
+#define M98090_MIXSPR_DACL_WIDTH 1
+#define M98090_MIXSPR_MASK (63<<0)
+#define M98090_MIXSPR_SHIFT 0
+#define M98090_MIXSPR_WIDTH 6
+
+/*
+ * M98090_REG_SPK_CONTROL
+ */
+#define M98090_MIXSPRG_MASK (3<<2)
+#define M98090_MIXSPRG_SHIFT 2
+#define M98090_MIXSPRG_WIDTH 2
+#define M98090_MIXSPRG_NUM (1<<M98090_MIXSPRG_WIDTH)
+#define M98090_MIXSPLG_MASK (3<<0)
+#define M98090_MIXSPLG_SHIFT 0
+#define M98090_MIXSPLG_WIDTH 2
+#define M98090_MIXSPLG_NUM (1<<M98090_MIXSPLG_WIDTH)
+
+/*
+ * M98090_REG_LEFT_SPK_VOLUME
+ */
+#define M98090_SPLM_MASK (1<<7)
+#define M98090_SPLM_SHIFT 7
+#define M98090_SPLM_WIDTH 1
+#define M98090_SPVOLL_MASK (63<<0)
+#define M98090_SPVOLL_SHIFT 0
+#define M98090_SPVOLL_WIDTH 6
+#define M98090_SPVOLL_NUM 40
+
+/*
+ * M98090_REG_RIGHT_SPK_VOLUME
+ */
+#define M98090_SPRM_MASK (1<<7)
+#define M98090_SPRM_SHIFT 7
+#define M98090_SPRM_WIDTH 1
+#define M98090_SPVOLR_MASK (63<<0)
+#define M98090_SPVOLR_SHIFT 0
+#define M98090_SPVOLR_WIDTH 6
+#define M98090_SPVOLR_NUM 40
+
+/*
+ * M98090_REG_DRC_TIMING
+ */
+#define M98090_DRCEN_MASK (1<<7)
+#define M98090_DRCEN_SHIFT 7
+#define M98090_DRCEN_WIDTH 1
+#define M98090_DRCEN_NUM (1<<M98090_DRCEN_WIDTH)
+#define M98090_DRCRLS_MASK (7<<4)
+#define M98090_DRCRLS_SHIFT 4
+#define M98090_DRCRLS_WIDTH 3
+#define M98090_DRCATK_MASK (7<<0)
+#define M98090_DRCATK_SHIFT 0
+#define M98090_DRCATK_WIDTH 3
+
+/*
+ * M98090_REG_DRC_COMPRESSOR
+ */
+#define M98090_DRCCMP_MASK (7<<5)
+#define M98090_DRCCMP_SHIFT 5
+#define M98090_DRCCMP_WIDTH 3
+#define M98090_DRCTHC_MASK (31<<0)
+#define M98090_DRCTHC_SHIFT 0
+#define M98090_DRCTHC_WIDTH 5
+#define M98090_DRCTHC_NUM (1<<M98090_DRCTHC_WIDTH)
+
+/*
+ * M98090_REG_DRC_EXPANDER
+ */
+#define M98090_DRCEXP_MASK (7<<5)
+#define M98090_DRCEXP_SHIFT 5
+#define M98090_DRCEXP_WIDTH 3
+#define M98090_DRCTHE_MASK (31<<0)
+#define M98090_DRCTHE_SHIFT 0
+#define M98090_DRCTHE_WIDTH 5
+#define M98090_DRCTHE_NUM (1<<M98090_DRCTHE_WIDTH)
+
+/*
+ * M98090_REG_DRC_GAIN
+ */
+#define M98090_DRCG_MASK (31<<0)
+#define M98090_DRCG_SHIFT 0
+#define M98090_DRCG_WIDTH 5
+#define M98090_DRCG_NUM 13
+
+/*
+ * M98090_REG_RCV_LOUTL_MIXER
+ */
+#define M98090_MIXRCVL_MIC2_MASK (1<<5)
+#define M98090_MIXRCVL_MIC2_SHIFT 5
+#define M98090_MIXRCVL_MIC2_WIDTH 1
+#define M98090_MIXRCVL_MIC1_MASK (1<<4)
+#define M98090_MIXRCVL_MIC1_SHIFT 4
+#define M98090_MIXRCVL_MIC1_WIDTH 1
+#define M98090_MIXRCVL_LINEB_MASK (1<<3)
+#define M98090_MIXRCVL_LINEB_SHIFT 3
+#define M98090_MIXRCVL_LINEB_WIDTH 1
+#define M98090_MIXRCVL_LINEA_MASK (1<<2)
+#define M98090_MIXRCVL_LINEA_SHIFT 2
+#define M98090_MIXRCVL_LINEA_WIDTH 1
+#define M98090_MIXRCVL_DACR_MASK (1<<1)
+#define M98090_MIXRCVL_DACR_SHIFT 1
+#define M98090_MIXRCVL_DACR_WIDTH 1
+#define M98090_MIXRCVL_DACL_MASK (1<<0)
+#define M98090_MIXRCVL_DACL_SHIFT 0
+#define M98090_MIXRCVL_DACL_WIDTH 1
+#define M98090_MIXRCVL_MASK (63<<0)
+#define M98090_MIXRCVL_SHIFT 0
+#define M98090_MIXRCVL_WIDTH 6
+
+/*
+ * M98090_REG_RCV_LOUTL_CONTROL
+ */
+#define M98090_MIXRCVLG_MASK (3<<0)
+#define M98090_MIXRCVLG_SHIFT 0
+#define M98090_MIXRCVLG_WIDTH 2
+#define M98090_MIXRCVLG_NUM (1<<M98090_MIXRCVLG_WIDTH)
+
+/*
+ * M98090_REG_RCV_LOUTL_VOLUME
+ */
+#define M98090_RCVLM_MASK (1<<7)
+#define M98090_RCVLM_SHIFT 7
+#define M98090_RCVLM_WIDTH 1
+#define M98090_RCVLVOL_MASK (31<<0)
+#define M98090_RCVLVOL_SHIFT 0
+#define M98090_RCVLVOL_WIDTH 5
+#define M98090_RCVLVOL_NUM (1<<M98090_RCVLVOL_WIDTH)
+
+/*
+ * M98090_REG_LOUTR_MIXER
+ */
+#define M98090_LINMOD_MASK (1<<7)
+#define M98090_LINMOD_SHIFT 7
+#define M98090_LINMOD_WIDTH 1
+#define M98090_MIXRCVR_MIC2_MASK (1<<5)
+#define M98090_MIXRCVR_MIC2_SHIFT 5
+#define M98090_MIXRCVR_MIC2_WIDTH 1
+#define M98090_MIXRCVR_MIC1_MASK (1<<4)
+#define M98090_MIXRCVR_MIC1_SHIFT 4
+#define M98090_MIXRCVR_MIC1_WIDTH 1
+#define M98090_MIXRCVR_LINEB_MASK (1<<3)
+#define M98090_MIXRCVR_LINEB_SHIFT 3
+#define M98090_MIXRCVR_LINEB_WIDTH 1
+#define M98090_MIXRCVR_LINEA_MASK (1<<2)
+#define M98090_MIXRCVR_LINEA_SHIFT 2
+#define M98090_MIXRCVR_LINEA_WIDTH 1
+#define M98090_MIXRCVR_DACR_MASK (1<<1)
+#define M98090_MIXRCVR_DACR_SHIFT 1
+#define M98090_MIXRCVR_DACR_WIDTH 1
+#define M98090_MIXRCVR_DACL_MASK (1<<0)
+#define M98090_MIXRCVR_DACL_SHIFT 0
+#define M98090_MIXRCVR_DACL_WIDTH 1
+#define M98090_MIXRCVR_MASK (63<<0)
+#define M98090_MIXRCVR_SHIFT 0
+#define M98090_MIXRCVR_WIDTH 6
+
+/*
+ * M98090_REG_LOUTR_CONTROL
+ */
+#define M98090_MIXRCVRG_MASK (3<<0)
+#define M98090_MIXRCVRG_SHIFT 0
+#define M98090_MIXRCVRG_WIDTH 2
+#define M98090_MIXRCVRG_NUM (1<<M98090_MIXRCVRG_WIDTH)
+
+/*
+ * M98090_REG_LOUTR_VOLUME
+ */
+#define M98090_RCVRM_MASK (1<<7)
+#define M98090_RCVRM_SHIFT 7
+#define M98090_RCVRM_WIDTH 1
+#define M98090_RCVRVOL_MASK (31<<0)
+#define M98090_RCVRVOL_SHIFT 0
+#define M98090_RCVRVOL_WIDTH 5
+#define M98090_RCVRVOL_NUM (1<<M98090_RCVRVOL_WIDTH)
+
+/*
+ * M98090_REG_JACK_DETECT
+ */
+#define M98090_JDETEN_MASK (1<<7)
+#define M98090_JDETEN_SHIFT 7
+#define M98090_JDETEN_WIDTH 1
+#define M98090_JDWK_MASK (1<<6)
+#define M98090_JDWK_SHIFT 6
+#define M98090_JDWK_WIDTH 1
+#define M98090_JDEB_MASK (3<<0)
+#define M98090_JDEB_SHIFT 0
+#define M98090_JDEB_WIDTH 2
+#define M98090_JDEB_25MS (0<<0)
+#define M98090_JDEB_50MS (1<<0)
+#define M98090_JDEB_100MS (2<<0)
+#define M98090_JDEB_200MS (3<<0)
+
+/*
+ * M98090_REG_INPUT_ENABLE
+ */
+#define M98090_MBEN_MASK (1<<4)
+#define M98090_MBEN_SHIFT 4
+#define M98090_MBEN_WIDTH 1
+#define M98090_LINEAEN_MASK (1<<3)
+#define M98090_LINEAEN_SHIFT 3
+#define M98090_LINEAEN_WIDTH 1
+#define M98090_LINEBEN_MASK (1<<2)
+#define M98090_LINEBEN_SHIFT 2
+#define M98090_LINEBEN_WIDTH 1
+#define M98090_ADREN_MASK (1<<1)
+#define M98090_ADREN_SHIFT 1
+#define M98090_ADREN_WIDTH 1
+#define M98090_ADLEN_MASK (1<<0)
+#define M98090_ADLEN_SHIFT 0
+#define M98090_ADLEN_WIDTH 1
+
+/*
+ * M98090_REG_OUTPUT_ENABLE
+ */
+#define M98090_HPREN_MASK (1<<7)
+#define M98090_HPREN_SHIFT 7
+#define M98090_HPREN_WIDTH 1
+#define M98090_HPLEN_MASK (1<<6)
+#define M98090_HPLEN_SHIFT 6
+#define M98090_HPLEN_WIDTH 1
+#define M98090_SPREN_MASK (1<<5)
+#define M98090_SPREN_SHIFT 5
+#define M98090_SPREN_WIDTH 1
+#define M98090_SPLEN_MASK (1<<4)
+#define M98090_SPLEN_SHIFT 4
+#define M98090_SPLEN_WIDTH 1
+#define M98090_RCVLEN_MASK (1<<3)
+#define M98090_RCVLEN_SHIFT 3
+#define M98090_RCVLEN_WIDTH 1
+#define M98090_RCVREN_MASK (1<<2)
+#define M98090_RCVREN_SHIFT 2
+#define M98090_RCVREN_WIDTH 1
+#define M98090_DAREN_MASK (1<<1)
+#define M98090_DAREN_SHIFT 1
+#define M98090_DAREN_WIDTH 1
+#define M98090_DALEN_MASK (1<<0)
+#define M98090_DALEN_SHIFT 0
+#define M98090_DALEN_WIDTH 1
+
+/*
+ * M98090_REG_LEVEL_CONTROL
+ */
+#define M98090_ZDENN_MASK (1<<2)
+#define M98090_ZDENN_SHIFT 2
+#define M98090_ZDENN_WIDTH 1
+#define M98090_ZDENN_NUM (1<<M98090_ZDENN_WIDTH)
+#define M98090_VS2ENN_MASK (1<<1)
+#define M98090_VS2ENN_SHIFT 1
+#define M98090_VS2ENN_WIDTH 1
+#define M98090_VS2ENN_NUM (1<<M98090_VS2ENN_WIDTH)
+#define M98090_VSENN_MASK (1<<0)
+#define M98090_VSENN_SHIFT 0
+#define M98090_VSENN_WIDTH 1
+#define M98090_VSENN_NUM (1<<M98090_VSENN_WIDTH)
+
+/*
+ * M98090_REG_DSP_FILTER_ENABLE
+ */
+#define M98090_DMIC34BQEN_MASK (1<<4)
+#define M98090_DMIC34BQEN_SHIFT 4
+#define M98090_DMIC34BQEN_WIDTH 1
+#define M98090_DMIC34BQEN_NUM (1<<M98090_DMIC34BQEN_WIDTH)
+#define M98090_ADCBQEN_MASK (1<<3)
+#define M98090_ADCBQEN_SHIFT 3
+#define M98090_ADCBQEN_WIDTH 1
+#define M98090_ADCBQEN_NUM (1<<M98090_ADCBQEN_WIDTH)
+#define M98090_EQ3BANDEN_MASK (1<<2)
+#define M98090_EQ3BANDEN_SHIFT 2
+#define M98090_EQ3BANDEN_WIDTH 1
+#define M98090_EQ3BANDEN_NUM (1<<M98090_EQ3BANDEN_WIDTH)
+#define M98090_EQ5BANDEN_MASK (1<<1)
+#define M98090_EQ5BANDEN_SHIFT 1
+#define M98090_EQ5BANDEN_WIDTH 1
+#define M98090_EQ5BANDEN_NUM (1<<M98090_EQ5BANDEN_WIDTH)
+#define M98090_EQ7BANDEN_MASK (1<<0)
+#define M98090_EQ7BANDEN_SHIFT 0
+#define M98090_EQ7BANDEN_WIDTH 1
+#define M98090_EQ7BANDEN_NUM (1<<M98090_EQ7BANDEN_WIDTH)
+
+/*
+ * M98090_REG_BIAS_CONTROL
+ */
+#define M98090_VCM_MODE_MASK (1<<0)
+#define M98090_VCM_MODE_SHIFT 0
+#define M98090_VCM_MODE_WIDTH 1
+#define M98090_VCM_MODE_NUM (1<<M98090_VCM_MODE_WIDTH)
+
+/*
+ * M98090_REG_DAC_CONTROL
+ */
+#define M98090_PERFMODE_MASK (1<<1)
+#define M98090_PERFMODE_SHIFT 1
+#define M98090_PERFMODE_WIDTH 1
+#define M98090_PERFMODE_NUM (1<<M98090_PERFMODE_WIDTH)
+#define M98090_DACHP_MASK (1<<0)
+#define M98090_DACHP_SHIFT 0
+#define M98090_DACHP_WIDTH 1
+#define M98090_DACHP_NUM (1<<M98090_DACHP_WIDTH)
+
+/*
+ * M98090_REG_ADC_CONTROL
+ */
+#define M98090_OSR128_MASK (1<<2)
+#define M98090_OSR128_SHIFT 2
+#define M98090_OSR128_WIDTH 1
+#define M98090_ADCDITHER_MASK (1<<1)
+#define M98090_ADCDITHER_SHIFT 1
+#define M98090_ADCDITHER_WIDTH 1
+#define M98090_ADCDITHER_NUM (1<<M98090_ADCDITHER_WIDTH)
+#define M98090_ADCHP_MASK (1<<0)
+#define M98090_ADCHP_SHIFT 0
+#define M98090_ADCHP_WIDTH 1
+#define M98090_ADCHP_NUM (1<<M98090_ADCHP_WIDTH)
+
+/*
+ * M98090_REG_DEVICE_SHUTDOWN
+ */
+#define M98090_SHDNN_MASK (1<<7)
+#define M98090_SHDNN_SHIFT 7
+#define M98090_SHDNN_WIDTH 1
+
+/*
+ * M98090_REG_EQUALIZER_BASE
+ */
+#define M98090_B0_1_HI_MASK (255<<0)
+#define M98090_B0_1_HI_SHIFT 0
+#define M98090_B0_1_HI_WIDTH 8
+#define M98090_B0_1_MID_MASK (255<<0)
+#define M98090_B0_1_MID_SHIFT 0
+#define M98090_B0_1_MID_WIDTH 8
+#define M98090_B0_1_LO_MASK (255<<0)
+#define M98090_B0_1_LO_SHIFT 0
+#define M98090_B0_1_LO_WIDTH 8
+#define M98090_B1_1_HI_MASK (255<<0)
+#define M98090_B1_1_HI_SHIFT 0
+#define M98090_B1_1_HI_WIDTH 8
+#define M98090_B1_1_MID_MASK (255<<0)
+#define M98090_B1_1_MID_SHIFT 0
+#define M98090_B1_1_MID_WIDTH 8
+#define M98090_B1_1_LO_MASK (255<<0)
+#define M98090_B1_1_LO_SHIFT 0
+#define M98090_B1_1_LO_WIDTH 8
+#define M98090_B2_1_HI_MASK (255<<0)
+#define M98090_B2_1_HI_SHIFT 0
+#define M98090_B2_1_HI_WIDTH 8
+#define M98090_B2_1_MID_MASK (255<<0)
+#define M98090_B2_1_MID_SHIFT 0
+#define M98090_B2_1_MID_WIDTH 8
+#define M98090_B2_1_LO_MASK (255<<0)
+#define M98090_B2_1_LO_SHIFT 0
+#define M98090_B2_1_LO_WIDTH 8
+#define M98090_A1_1_HI_MASK (255<<0)
+#define M98090_A1_1_HI_SHIFT 0
+#define M98090_A1_1_HI_WIDTH 8
+#define M98090_A1_1_MID_MASK (255<<0)
+#define M98090_A1_1_MID_SHIFT 0
+#define M98090_A1_1_MID_WIDTH 8
+#define M98090_A1_1_LO_MASK (255<<0)
+#define M98090_A1_1_LO_SHIFT 0
+#define M98090_A1_1_LO_WIDTH 8
+#define M98090_A2_1_HI_MASK (255<<0)
+#define M98090_A2_1_HI_SHIFT 0
+#define M98090_A2_1_HI_WIDTH 8
+#define M98090_A2_1_MID_MASK (255<<0)
+#define M98090_A2_1_MID_SHIFT 0
+#define M98090_A2_1_MID_WIDTH 8
+#define M98090_A2_1_LO_MASK (255<<0)
+#define M98090_A2_1_LO_SHIFT 0
+#define M98090_A2_1_LO_WIDTH 8
+
+#define M98090_COEFS_PER_BAND 5
+#define M98090_COEFS_BLK_SZ (M98090_COEFS_PER_BAND * 3)
+#define M98090_COEFS_MAX_SZ (M98090_COEFS_BLK_SZ * 7)
+
+/*
+ * M98090_REG_RECORD_BIQUAD_BASE
+ */
+#define M98090_REC_B0_HI_MASK (255<<0)
+#define M98090_REC_B0_HI_SHIFT 0
+#define M98090_REC_B0_HI_WIDTH 8
+#define M98090_REC_B0_MID_MASK (255<<0)
+#define M98090_REC_B0_MID_SHIFT 0
+#define M98090_REC_B0_MID_WIDTH 8
+#define M98090_REC_B0_LO_MASK (255<<0)
+#define M98090_REC_B0_LO_SHIFT 0
+#define M98090_REC_B0_LO_WIDTH 8
+#define M98090_REC_B1_HI_MASK (255<<0)
+#define M98090_REC_B1_HI_SHIFT 0
+#define M98090_REC_B1_HI_WIDTH 8
+#define M98090_REC_B1_MID_MASK (255<<0)
+#define M98090_REC_B1_MID_SHIFT 0
+#define M98090_REC_B1_MID_WIDTH 8
+#define M98090_REC_B1_LO_MASK (255<<0)
+#define M98090_REC_B1_LO_SHIFT 0
+#define M98090_REC_B1_LO_WIDTH 8
+#define M98090_REC_B2_HI_MASK (255<<0)
+#define M98090_REC_B2_HI_SHIFT 0
+#define M98090_REC_B2_HI_WIDTH 8
+#define M98090_REC_B2_MID_MASK (255<<0)
+#define M98090_REC_B2_MID_SHIFT 0
+#define M98090_REC_B2_MID_WIDTH 8
+#define M98090_REC_B2_LO_MASK (255<<0)
+#define M98090_REC_B2_LO_SHIFT 0
+#define M98090_REC_B2_LO_WIDTH 8
+#define M98090_REC_A1_HI_MASK (255<<0)
+#define M98090_REC_A1_HI_SHIFT 0
+#define M98090_REC_A1_HI_WIDTH 8
+#define M98090_REC_A1_MID_MASK (255<<0)
+#define M98090_REC_A1_MID_SHIFT 0
+#define M98090_REC_A1_MID_WIDTH 8
+#define M98090_REC_A1_LO_MASK (255<<0)
+#define M98090_REC_A1_LO_SHIFT 0
+#define M98090_REC_A1_LO_WIDTH 8
+#define M98090_REC_A2_HI_MASK (255<<0)
+#define M98090_REC_A2_HI_SHIFT 0
+#define M98090_REC_A2_HI_WIDTH 8
+#define M98090_REC_A2_MID_MASK (255<<0)
+#define M98090_REC_A2_MID_SHIFT 0
+#define M98090_REC_A2_MID_WIDTH 8
+#define M98090_REC_A2_LO_MASK (255<<0)
+#define M98090_REC_A2_LO_SHIFT 0
+#define M98090_REC_A2_LO_WIDTH 8
+
+/*
+ * M98090_REG_DMIC3_VOLUME
+ */
+#define M98090_DMIC_AV3G_MASK (7<<4)
+#define M98090_DMIC_AV3G_SHIFT 4
+#define M98090_DMIC_AV3G_WIDTH 3
+#define M98090_DMIC_AV3G_NUM (1<<M98090_DMIC_AV3G_WIDTH)
+#define M98090_DMIC_AV3_MASK (15<<0)
+#define M98090_DMIC_AV3_SHIFT 0
+#define M98090_DMIC_AV3_WIDTH 4
+#define M98090_DMIC_AV3_NUM (1<<M98090_DMIC_AV3_WIDTH)
+
+/*
+ * M98090_REG_DMIC4_VOLUME
+ */
+#define M98090_DMIC_AV4G_MASK (7<<4)
+#define M98090_DMIC_AV4G_SHIFT 4
+#define M98090_DMIC_AV4G_WIDTH 3
+#define M98090_DMIC_AV4G_NUM (1<<M98090_DMIC_AV4G_WIDTH)
+#define M98090_DMIC_AV4_MASK (15<<0)
+#define M98090_DMIC_AV4_SHIFT 0
+#define M98090_DMIC_AV4_WIDTH 4
+#define M98090_DMIC_AV4_NUM (1<<M98090_DMIC_AV4_WIDTH)
+
+/*
+ * M98090_REG_DMIC34_BQ_PREATTEN
+ */
+#define M98090_AV34BQ_MASK (15<<0)
+#define M98090_AV34BQ_SHIFT 0
+#define M98090_AV34BQ_WIDTH 4
+#define M98090_AV34BQ_NUM (1<<M98090_AV34BQ_WIDTH)
+
+/*
+ * M98090_REG_RECORD_TDM_SLOT
+ */
+#define M98090_TDM_SLOTADCL_MASK (3<<6)
+#define M98090_TDM_SLOTADCL_SHIFT 6
+#define M98090_TDM_SLOTADCL_WIDTH 2
+#define M98090_TDM_SLOTADCL_NUM (1<<M98090_TDM_SLOTADCL_WIDTH)
+#define M98090_TDM_SLOTADCR_MASK (3<<4)
+#define M98090_TDM_SLOTADCR_SHIFT 4
+#define M98090_TDM_SLOTADCR_WIDTH 2
+#define M98090_TDM_SLOTADCR_NUM (1<<M98090_TDM_SLOTADCR_WIDTH)
+#define M98090_TDM_SLOTDMIC3_MASK (3<<2)
+#define M98090_TDM_SLOTDMIC3_SHIFT 2
+#define M98090_TDM_SLOTDMIC3_WIDTH 2
+#define M98090_TDM_SLOTDMIC3_NUM (1<<M98090_TDM_SLOTDMIC3_WIDTH)
+#define M98090_TDM_SLOTDMIC4_MASK (3<<0)
+#define M98090_TDM_SLOTDMIC4_SHIFT 0
+#define M98090_TDM_SLOTDMIC4_WIDTH 2
+#define M98090_TDM_SLOTDMIC4_NUM (1<<M98090_TDM_SLOTDMIC4_WIDTH)
+
+/*
+ * M98090_REG_SAMPLE_RATE
+ */
+#define M98090_DMIC34_ZEROPAD_MASK (1<<4)
+#define M98090_DMIC34_ZEROPAD_SHIFT 4
+#define M98090_DMIC34_ZEROPAD_WIDTH 1
+#define M98090_DMIC34_ZEROPAD_NUM (1<<M98090_DIGMIC4_WIDTH)
+#define M98090_DMIC34_SRDIV_MASK (7<<0)
+#define M98090_DMIC34_SRDIV_SHIFT 0
+#define M98090_DMIC34_SRDIV_WIDTH 3
+
+/*
+ * M98090_REG_DMIC34_BIQUAD_BASE
+ */
+#define M98090_DMIC34_B0_HI_MASK (255<<0)
+#define M98090_DMIC34_B0_HI_SHIFT 0
+#define M98090_DMIC34_B0_HI_WIDTH 8
+#define M98090_DMIC34_B0_MID_MASK (255<<0)
+#define M98090_DMIC34_B0_MID_SHIFT 0
+#define M98090_DMIC34_B0_MID_WIDTH 8
+#define M98090_DMIC34_B0_LO_MASK (255<<0)
+#define M98090_DMIC34_B0_LO_SHIFT 0
+#define M98090_DMIC34_B0_LO_WIDTH 8
+#define M98090_DMIC34_B1_HI_MASK (255<<0)
+#define M98090_DMIC34_B1_HI_SHIFT 0
+#define M98090_DMIC34_B1_HI_WIDTH 8
+#define M98090_DMIC34_B1_MID_MASK (255<<0)
+#define M98090_DMIC34_B1_MID_SHIFT 0
+#define M98090_DMIC34_B1_MID_WIDTH 8
+#define M98090_DMIC34_B1_LO_MASK (255<<0)
+#define M98090_DMIC34_B1_LO_SHIFT 0
+#define M98090_DMIC34_B1_LO_WIDTH 8
+#define M98090_DMIC34_B2_HI_MASK (255<<0)
+#define M98090_DMIC34_B2_HI_SHIFT 0
+#define M98090_DMIC34_B2_HI_WIDTH 8
+#define M98090_DMIC34_B2_MID_MASK (255<<0)
+#define M98090_DMIC34_B2_MID_SHIFT 0
+#define M98090_DMIC34_B2_MID_WIDTH 8
+#define M98090_DMIC34_B2_LO_MASK (255<<0)
+#define M98090_DMIC34_B2_LO_SHIFT 0
+#define M98090_DMIC34_B2_LO_WIDTH 8
+#define M98090_DMIC34_A1_HI_MASK (255<<0)
+#define M98090_DMIC34_A1_HI_SHIFT 0
+#define M98090_DMIC34_A1_HI_WIDTH 8
+#define M98090_DMIC34_A1_MID_MASK (255<<0)
+#define M98090_DMIC34_A1_MID_SHIFT 0
+#define M98090_DMIC34_A1_MID_WIDTH 8
+#define M98090_DMIC34_A1_LO_MASK (255<<0)
+#define M98090_DMIC34_A1_LO_SHIFT 0
+#define M98090_DMIC34_A1_LO_WIDTH 8
+#define M98090_DMIC34_A2_HI_MASK (255<<0)
+#define M98090_DMIC34_A2_HI_SHIFT 0
+#define M98090_DMIC34_A2_HI_WIDTH 8
+#define M98090_DMIC34_A2_MID_MASK (255<<0)
+#define M98090_DMIC34_A2_MID_SHIFT 0
+#define M98090_DMIC34_A2_MID_WIDTH 8
+#define M98090_DMIC34_A2_LO_MASK (255<<0)
+#define M98090_DMIC34_A2_LO_SHIFT 0
+#define M98090_DMIC34_A2_LO_WIDTH 8
+
+#define M98090_JACK_STATE_NO_HEADSET 0
+#define M98090_JACK_STATE_NO_HEADSET_2 1
+#define M98090_JACK_STATE_HEADPHONE 2
+#define M98090_JACK_STATE_HEADSET 3
+
+/*
+ * M98090_REG_REVISION_ID
+ */
+#define M98090_REVID_MASK (255<<0)
+#define M98090_REVID_SHIFT 0
+#define M98090_REVID_WIDTH 8
+#define M98090_REVID_NUM (1<<M98090_REVID_WIDTH)
+
+/* Silicon revision number */
+#define M98090_REVA 0x40
+#define M98091_REVA 0x50
+
+enum max98090_type {
+ MAX98090,
+ MAX98091,
+};
+
+struct max98090_cdata {
+ unsigned int rate;
+ unsigned int fmt;
+};
+
+struct max98090_priv {
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ enum max98090_type devtype;
+ struct max98090_pdata *pdata;
+ struct clk *mclk;
+ unsigned int sysclk;
+ unsigned int pclk;
+ unsigned int bclk;
+ unsigned int lrclk;
+ u32 dmic_freq;
+ struct max98090_cdata dai[1];
+ int jack_state;
+ 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;
+ u8 lin_state;
+ unsigned int pa1en;
+ unsigned int pa2en;
+ unsigned int sidetone;
+ bool master;
+};
+
+int max98090_mic_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack);
+
+#endif
diff --git a/kernel/sound/soc/codecs/max98095.c b/kernel/sound/soc/codecs/max98095.c
new file mode 100644
index 000000000..8fba0c3db
--- /dev/null
+++ b/kernel/sound/soc/codecs/max98095.c
@@ -0,0 +1,2446 @@
+/*
+ * 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>
+#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/clk.h>
+#include <linux/mutex.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 <sound/max98095.h>
+#include <sound/jack.h>
+#include "max98095.h"
+
+enum max98095_type {
+ MAX98095,
+};
+
+struct max98095_cdata {
+ unsigned int rate;
+ unsigned int fmt;
+ int eq_sel;
+ int bq_sel;
+};
+
+struct max98095_priv {
+ struct regmap *regmap;
+ enum max98095_type devtype;
+ struct max98095_pdata *pdata;
+ struct clk *mclk;
+ unsigned int sysclk;
+ struct max98095_cdata dai[3];
+ const char **eq_texts;
+ const char **bq_texts;
+ struct soc_enum eq_enum;
+ struct soc_enum bq_enum;
+ int eq_textcnt;
+ int bq_textcnt;
+ u8 lin_state;
+ unsigned int mic1pre;
+ unsigned int mic2pre;
+ struct snd_soc_jack *headphone_jack;
+ struct snd_soc_jack *mic_jack;
+ struct mutex lock;
+};
+
+static const struct reg_default max98095_reg_def[] = {
+ { 0xf, 0x00 }, /* 0F */
+ { 0x10, 0x00 }, /* 10 */
+ { 0x11, 0x00 }, /* 11 */
+ { 0x12, 0x00 }, /* 12 */
+ { 0x13, 0x00 }, /* 13 */
+ { 0x14, 0x00 }, /* 14 */
+ { 0x15, 0x00 }, /* 15 */
+ { 0x16, 0x00 }, /* 16 */
+ { 0x17, 0x00 }, /* 17 */
+ { 0x18, 0x00 }, /* 18 */
+ { 0x19, 0x00 }, /* 19 */
+ { 0x1a, 0x00 }, /* 1A */
+ { 0x1b, 0x00 }, /* 1B */
+ { 0x1c, 0x00 }, /* 1C */
+ { 0x1d, 0x00 }, /* 1D */
+ { 0x1e, 0x00 }, /* 1E */
+ { 0x1f, 0x00 }, /* 1F */
+ { 0x20, 0x00 }, /* 20 */
+ { 0x21, 0x00 }, /* 21 */
+ { 0x22, 0x00 }, /* 22 */
+ { 0x23, 0x00 }, /* 23 */
+ { 0x24, 0x00 }, /* 24 */
+ { 0x25, 0x00 }, /* 25 */
+ { 0x26, 0x00 }, /* 26 */
+ { 0x27, 0x00 }, /* 27 */
+ { 0x28, 0x00 }, /* 28 */
+ { 0x29, 0x00 }, /* 29 */
+ { 0x2a, 0x00 }, /* 2A */
+ { 0x2b, 0x00 }, /* 2B */
+ { 0x2c, 0x00 }, /* 2C */
+ { 0x2d, 0x00 }, /* 2D */
+ { 0x2e, 0x00 }, /* 2E */
+ { 0x2f, 0x00 }, /* 2F */
+ { 0x30, 0x00 }, /* 30 */
+ { 0x31, 0x00 }, /* 31 */
+ { 0x32, 0x00 }, /* 32 */
+ { 0x33, 0x00 }, /* 33 */
+ { 0x34, 0x00 }, /* 34 */
+ { 0x35, 0x00 }, /* 35 */
+ { 0x36, 0x00 }, /* 36 */
+ { 0x37, 0x00 }, /* 37 */
+ { 0x38, 0x00 }, /* 38 */
+ { 0x39, 0x00 }, /* 39 */
+ { 0x3a, 0x00 }, /* 3A */
+ { 0x3b, 0x00 }, /* 3B */
+ { 0x3c, 0x00 }, /* 3C */
+ { 0x3d, 0x00 }, /* 3D */
+ { 0x3e, 0x00 }, /* 3E */
+ { 0x3f, 0x00 }, /* 3F */
+ { 0x40, 0x00 }, /* 40 */
+ { 0x41, 0x00 }, /* 41 */
+ { 0x42, 0x00 }, /* 42 */
+ { 0x43, 0x00 }, /* 43 */
+ { 0x44, 0x00 }, /* 44 */
+ { 0x45, 0x00 }, /* 45 */
+ { 0x46, 0x00 }, /* 46 */
+ { 0x47, 0x00 }, /* 47 */
+ { 0x48, 0x00 }, /* 48 */
+ { 0x49, 0x00 }, /* 49 */
+ { 0x4a, 0x00 }, /* 4A */
+ { 0x4b, 0x00 }, /* 4B */
+ { 0x4c, 0x00 }, /* 4C */
+ { 0x4d, 0x00 }, /* 4D */
+ { 0x4e, 0x00 }, /* 4E */
+ { 0x4f, 0x00 }, /* 4F */
+ { 0x50, 0x00 }, /* 50 */
+ { 0x51, 0x00 }, /* 51 */
+ { 0x52, 0x00 }, /* 52 */
+ { 0x53, 0x00 }, /* 53 */
+ { 0x54, 0x00 }, /* 54 */
+ { 0x55, 0x00 }, /* 55 */
+ { 0x56, 0x00 }, /* 56 */
+ { 0x57, 0x00 }, /* 57 */
+ { 0x58, 0x00 }, /* 58 */
+ { 0x59, 0x00 }, /* 59 */
+ { 0x5a, 0x00 }, /* 5A */
+ { 0x5b, 0x00 }, /* 5B */
+ { 0x5c, 0x00 }, /* 5C */
+ { 0x5d, 0x00 }, /* 5D */
+ { 0x5e, 0x00 }, /* 5E */
+ { 0x5f, 0x00 }, /* 5F */
+ { 0x60, 0x00 }, /* 60 */
+ { 0x61, 0x00 }, /* 61 */
+ { 0x62, 0x00 }, /* 62 */
+ { 0x63, 0x00 }, /* 63 */
+ { 0x64, 0x00 }, /* 64 */
+ { 0x65, 0x00 }, /* 65 */
+ { 0x66, 0x00 }, /* 66 */
+ { 0x67, 0x00 }, /* 67 */
+ { 0x68, 0x00 }, /* 68 */
+ { 0x69, 0x00 }, /* 69 */
+ { 0x6a, 0x00 }, /* 6A */
+ { 0x6b, 0x00 }, /* 6B */
+ { 0x6c, 0x00 }, /* 6C */
+ { 0x6d, 0x00 }, /* 6D */
+ { 0x6e, 0x00 }, /* 6E */
+ { 0x6f, 0x00 }, /* 6F */
+ { 0x70, 0x00 }, /* 70 */
+ { 0x71, 0x00 }, /* 71 */
+ { 0x72, 0x00 }, /* 72 */
+ { 0x73, 0x00 }, /* 73 */
+ { 0x74, 0x00 }, /* 74 */
+ { 0x75, 0x00 }, /* 75 */
+ { 0x76, 0x00 }, /* 76 */
+ { 0x77, 0x00 }, /* 77 */
+ { 0x78, 0x00 }, /* 78 */
+ { 0x79, 0x00 }, /* 79 */
+ { 0x7a, 0x00 }, /* 7A */
+ { 0x7b, 0x00 }, /* 7B */
+ { 0x7c, 0x00 }, /* 7C */
+ { 0x7d, 0x00 }, /* 7D */
+ { 0x7e, 0x00 }, /* 7E */
+ { 0x7f, 0x00 }, /* 7F */
+ { 0x80, 0x00 }, /* 80 */
+ { 0x81, 0x00 }, /* 81 */
+ { 0x82, 0x00 }, /* 82 */
+ { 0x83, 0x00 }, /* 83 */
+ { 0x84, 0x00 }, /* 84 */
+ { 0x85, 0x00 }, /* 85 */
+ { 0x86, 0x00 }, /* 86 */
+ { 0x87, 0x00 }, /* 87 */
+ { 0x88, 0x00 }, /* 88 */
+ { 0x89, 0x00 }, /* 89 */
+ { 0x8a, 0x00 }, /* 8A */
+ { 0x8b, 0x00 }, /* 8B */
+ { 0x8c, 0x00 }, /* 8C */
+ { 0x8d, 0x00 }, /* 8D */
+ { 0x8e, 0x00 }, /* 8E */
+ { 0x8f, 0x00 }, /* 8F */
+ { 0x90, 0x00 }, /* 90 */
+ { 0x91, 0x00 }, /* 91 */
+ { 0x92, 0x30 }, /* 92 */
+ { 0x93, 0xF0 }, /* 93 */
+ { 0x94, 0x00 }, /* 94 */
+ { 0x95, 0x00 }, /* 95 */
+ { 0x96, 0x3F }, /* 96 */
+ { 0x97, 0x00 }, /* 97 */
+ { 0xff, 0x00 }, /* FF */
+};
+
+static 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;
+}
+
+static bool max98095_volatile(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;
+ }
+
+ return 0;
+}
+
+static const struct regmap_config max98095_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = max98095_reg_def,
+ .num_reg_defaults = ARRAY_SIZE(max98095_reg_def),
+ .max_register = M98095_0FF_REV_ID,
+ .cache_type = REGCACHE_RBTREE,
+
+ .readable_reg = max98095_readable,
+ .volatile_reg = max98095_volatile,
+};
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+static void m98095_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+ unsigned int band, u16 *coefs)
+{
+ unsigned int eq_reg;
+ unsigned int i;
+
+ if (WARN_ON(band > 4) ||
+ WARN_ON(dai > 1))
+ return;
+
+ /* Load the base register address */
+ eq_reg = dai ? M98095_142_DAI2_EQ_BASE : M98095_110_DAI1_EQ_BASE;
+
+ /* Add the band address offset, note adjustment for word address */
+ eq_reg += band * (M98095_COEFS_PER_BAND << 1);
+
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
+ snd_soc_write(codec, eq_reg++, M98095_BYTE1(coefs[i]));
+ snd_soc_write(codec, 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,
+ unsigned int band, u16 *coefs)
+{
+ unsigned int bq_reg;
+ unsigned int i;
+
+ if (WARN_ON(band > 1) ||
+ WARN_ON(dai > 1))
+ return;
+
+ /* Load the base register address */
+ bq_reg = dai ? M98095_17E_DAI2_BQ_BASE : M98095_174_DAI1_BQ_BASE;
+
+ /* Add the band address offset, note adjustment for word address */
+ bq_reg += band * (M98095_COEFS_PER_BAND << 1);
+
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
+ snd_soc_write(codec, bq_reg++, M98095_BYTE1(coefs[i]));
+ snd_soc_write(codec, bq_reg++, M98095_BYTE0(coefs[i]));
+ }
+}
+
+static const char * const max98095_fltr_mode[] = { "Voice", "Music" };
+static SOC_ENUM_SINGLE_DECL(max98095_dai1_filter_mode_enum,
+ M98095_02E_DAI1_FILTERS, 7,
+ max98095_fltr_mode);
+static SOC_ENUM_SINGLE_DECL(max98095_dai2_filter_mode_enum,
+ M98095_038_DAI2_FILTERS, 7,
+ max98095_fltr_mode);
+
+static const char * const max98095_extmic_text[] = { "None", "MIC1", "MIC2" };
+
+static SOC_ENUM_SINGLE_DECL(max98095_extmic_enum,
+ M98095_087_CFG_MIC, 0,
+ max98095_extmic_text);
+
+static const struct snd_kcontrol_new max98095_extmic_mux =
+ SOC_DAPM_ENUM("External MIC Mux", max98095_extmic_enum);
+
+static const char * const max98095_linein_text[] = { "INA", "INB" };
+
+static SOC_ENUM_SINGLE_DECL(max98095_linein_enum,
+ M98095_086_CFG_LINE, 6,
+ max98095_linein_text);
+
+static const struct snd_kcontrol_new max98095_linein_mux =
+ SOC_DAPM_ENUM("Linein Input Mux", max98095_linein_enum);
+
+static const char * const max98095_line_mode_text[] = {
+ "Stereo", "Differential"};
+
+static SOC_ENUM_SINGLE_DECL(max98095_linein_mode_enum,
+ M98095_086_CFG_LINE, 7,
+ max98095_line_mode_text);
+
+static SOC_ENUM_SINGLE_DECL(max98095_lineout_mode_enum,
+ M98095_086_CFG_LINE, 4,
+ max98095_line_mode_text);
+
+static const char * const max98095_dai_fltr[] = {
+ "Off", "Elliptical-HPF-16k", "Butterworth-HPF-16k",
+ "Elliptical-HPF-8k", "Butterworth-HPF-8k", "Butterworth-HPF-Fs/240"};
+static SOC_ENUM_SINGLE_DECL(max98095_dai1_dac_filter_enum,
+ M98095_02E_DAI1_FILTERS, 0,
+ max98095_dai_fltr);
+static SOC_ENUM_SINGLE_DECL(max98095_dai2_dac_filter_enum,
+ M98095_038_DAI2_FILTERS, 0,
+ max98095_dai_fltr);
+static SOC_ENUM_SINGLE_DECL(max98095_dai3_dac_filter_enum,
+ M98095_042_DAI3_FILTERS, 0,
+ max98095_dai_fltr);
+
+static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98095->mic1pre = sel;
+ snd_soc_update_bits(codec, M98095_05F_LVL_MIC1, M98095_MICPRE_MASK,
+ (1+sel)<<M98095_MICPRE_SHIFT);
+
+ return 0;
+}
+
+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);
+
+ ucontrol->value.integer.value[0] = max98095->mic1pre;
+ return 0;
+}
+
+static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98095->mic2pre = sel;
+ snd_soc_update_bits(codec, M98095_060_LVL_MIC2, M98095_MICPRE_MASK,
+ (1+sel)<<M98095_MICPRE_SHIFT);
+
+ return 0;
+}
+
+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);
+
+ ucontrol->value.integer.value[0] = max98095->mic2pre;
+ return 0;
+}
+
+static const unsigned int max98095_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_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),
+ 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0),
+};
+
+static const unsigned int max98095_spk_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 10, TLV_DB_SCALE_ITEM(-5900, 400, 0),
+ 11, 18, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 19, 27, TLV_DB_SCALE_ITEM(-200, 100, 0),
+ 28, 39, TLV_DB_SCALE_ITEM(650, 50, 0),
+};
+
+static const unsigned int max98095_rcv_lout_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0),
+};
+
+static const unsigned int max98095_lin_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 2, TLV_DB_SCALE_ITEM(-600, 300, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(300, 1100, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0),
+};
+
+static const struct snd_kcontrol_new max98095_snd_controls[] = {
+
+ SOC_DOUBLE_R_TLV("Headphone Volume", M98095_064_LVL_HP_L,
+ M98095_065_LVL_HP_R, 0, 31, 0, max98095_hp_tlv),
+
+ SOC_DOUBLE_R_TLV("Speaker Volume", M98095_067_LVL_SPK_L,
+ M98095_068_LVL_SPK_R, 0, 39, 0, max98095_spk_tlv),
+
+ SOC_SINGLE_TLV("Receiver Volume", M98095_066_LVL_RCV,
+ 0, 31, 0, max98095_rcv_lout_tlv),
+
+ SOC_DOUBLE_R_TLV("Lineout Volume", M98095_062_LVL_LINEOUT1,
+ M98095_063_LVL_LINEOUT2, 0, 31, 0, max98095_rcv_lout_tlv),
+
+ SOC_DOUBLE_R("Headphone Switch", M98095_064_LVL_HP_L,
+ M98095_065_LVL_HP_R, 7, 1, 1),
+
+ SOC_DOUBLE_R("Speaker Switch", M98095_067_LVL_SPK_L,
+ M98095_068_LVL_SPK_R, 7, 1, 1),
+
+ SOC_SINGLE("Receiver Switch", M98095_066_LVL_RCV, 7, 1, 1),
+
+ SOC_DOUBLE_R("Lineout Switch", M98095_062_LVL_LINEOUT1,
+ M98095_063_LVL_LINEOUT2, 7, 1, 1),
+
+ SOC_SINGLE_TLV("MIC1 Volume", M98095_05F_LVL_MIC1, 0, 20, 1,
+ max98095_mic_tlv),
+
+ SOC_SINGLE_TLV("MIC2 Volume", M98095_060_LVL_MIC2, 0, 20, 1,
+ max98095_mic_tlv),
+
+ SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
+ M98095_05F_LVL_MIC1, 5, 2, 0,
+ max98095_mic1pre_get, max98095_mic1pre_set,
+ max98095_micboost_tlv),
+ SOC_SINGLE_EXT_TLV("MIC2 Boost Volume",
+ M98095_060_LVL_MIC2, 5, 2, 0,
+ max98095_mic2pre_get, max98095_mic2pre_set,
+ max98095_micboost_tlv),
+
+ SOC_SINGLE_TLV("Linein Volume", M98095_061_LVL_LINEIN, 0, 5, 1,
+ max98095_lin_tlv),
+
+ SOC_SINGLE_TLV("ADCL Volume", M98095_05D_LVL_ADC_L, 0, 15, 1,
+ max98095_adc_tlv),
+ SOC_SINGLE_TLV("ADCR Volume", M98095_05E_LVL_ADC_R, 0, 15, 1,
+ max98095_adc_tlv),
+
+ SOC_SINGLE_TLV("ADCL Boost Volume", M98095_05D_LVL_ADC_L, 4, 3, 0,
+ max98095_adcboost_tlv),
+ SOC_SINGLE_TLV("ADCR Boost Volume", M98095_05E_LVL_ADC_R, 4, 3, 0,
+ max98095_adcboost_tlv),
+
+ SOC_SINGLE("EQ1 Switch", M98095_088_CFG_LEVEL, 0, 1, 0),
+ SOC_SINGLE("EQ2 Switch", M98095_088_CFG_LEVEL, 1, 1, 0),
+
+ SOC_SINGLE("Biquad1 Switch", M98095_088_CFG_LEVEL, 2, 1, 0),
+ SOC_SINGLE("Biquad2 Switch", M98095_088_CFG_LEVEL, 3, 1, 0),
+
+ SOC_ENUM("DAI1 Filter Mode", max98095_dai1_filter_mode_enum),
+ SOC_ENUM("DAI2 Filter Mode", max98095_dai2_filter_mode_enum),
+ SOC_ENUM("DAI1 DAC Filter", max98095_dai1_dac_filter_enum),
+ SOC_ENUM("DAI2 DAC Filter", max98095_dai2_dac_filter_enum),
+ SOC_ENUM("DAI3 DAC Filter", max98095_dai3_dac_filter_enum),
+
+ SOC_ENUM("Linein Mode", max98095_linein_mode_enum),
+ SOC_ENUM("Lineout Mode", max98095_lineout_mode_enum),
+};
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98095_left_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_050_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_050_MIX_SPK_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_050_MIX_SPK_LEFT, 4, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_050_MIX_SPK_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_050_MIX_SPK_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_050_MIX_SPK_LEFT, 2, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98095_right_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_051_MIX_SPK_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_051_MIX_SPK_RIGHT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_051_MIX_SPK_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_051_MIX_SPK_RIGHT, 2, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98095_left_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04C_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04C_MIX_HP_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04C_MIX_HP_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04C_MIX_HP_LEFT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04C_MIX_HP_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04C_MIX_HP_LEFT, 2, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98095_right_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04D_MIX_HP_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04D_MIX_HP_RIGHT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04D_MIX_HP_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04D_MIX_HP_RIGHT, 2, 1, 0),
+};
+
+/* Receiver earpiece mixer switch */
+static const struct snd_kcontrol_new max98095_mono_rcv_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04F_MIX_RCV, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04F_MIX_RCV, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04F_MIX_RCV, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04F_MIX_RCV, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04F_MIX_RCV, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04F_MIX_RCV, 2, 1, 0),
+};
+
+/* Left lineout mixer switch */
+static const struct snd_kcontrol_new max98095_left_lineout_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_053_MIX_LINEOUT1, 5, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_053_MIX_LINEOUT1, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_053_MIX_LINEOUT1, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_053_MIX_LINEOUT1, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_053_MIX_LINEOUT1, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_053_MIX_LINEOUT1, 2, 1, 0),
+};
+
+/* Right lineout mixer switch */
+static const struct snd_kcontrol_new max98095_right_lineout_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_054_MIX_LINEOUT2, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_054_MIX_LINEOUT2, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_054_MIX_LINEOUT2, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_054_MIX_LINEOUT2, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_054_MIX_LINEOUT2, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_054_MIX_LINEOUT2, 2, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98095_left_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04A_MIX_ADC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04A_MIX_ADC_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04A_MIX_ADC_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04A_MIX_ADC_LEFT, 2, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04B_MIX_ADC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04B_MIX_ADC_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04B_MIX_ADC_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04B_MIX_ADC_RIGHT, 2, 1, 0),
+};
+
+static int max98095_mic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ 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,
+ (1+max98095->mic1pre)<<M98095_MICPRE_SHIFT);
+ } else {
+ snd_soc_update_bits(codec, 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);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * The line inputs are stereo inputs with the left and right
+ * channels sharing a common PGA power control signal.
+ */
+static int max98095_line_pga(struct snd_soc_dapm_widget *w,
+ int event, u8 channel)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ u8 *state;
+
+ if (WARN_ON(!(channel == 1 || channel == 2)))
+ return -EINVAL;
+
+ state = &max98095->lin_state;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ *state |= channel;
+ snd_soc_update_bits(codec, 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,
+ (1 << w->shift), 0);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int max98095_pga_in1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98095_line_pga(w, event, 1);
+}
+
+static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98095_line_pga(w, event, 2);
+}
+
+/*
+ * The stereo line out mixer outputs to two stereo line outs.
+ * The 2nd pair has a separate set of enables.
+ */
+static int max98095_lineout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, w->reg,
+ (1 << (w->shift+2)), (1 << (w->shift+2)));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, w->reg,
+ (1 << (w->shift+2)), 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget max98095_dapm_widgets[] = {
+
+ SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98095_090_PWR_EN_IN, 0, 0),
+ SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98095_090_PWR_EN_IN, 1, 0),
+
+ SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+ M98095_091_PWR_EN_OUT, 0, 0),
+ SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+ M98095_091_PWR_EN_OUT, 1, 0),
+ SND_SOC_DAPM_DAC("DACM2", "Aux Playback",
+ M98095_091_PWR_EN_OUT, 2, 0),
+ SND_SOC_DAPM_DAC("DACM3", "Voice Playback",
+ M98095_091_PWR_EN_OUT, 2, 0),
+
+ SND_SOC_DAPM_PGA("HP Left Out", M98095_091_PWR_EN_OUT,
+ 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP Right Out", M98095_091_PWR_EN_OUT,
+ 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("SPK Left Out", M98095_091_PWR_EN_OUT,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPK Right Out", M98095_091_PWR_EN_OUT,
+ 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("RCV Mono Out", M98095_091_PWR_EN_OUT,
+ 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_E("LINE Left Out", M98095_092_PWR_EN_OUT,
+ 0, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("LINE Right Out", M98095_092_PWR_EN_OUT,
+ 1, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX("External MIC", SND_SOC_NOPM, 0, 0,
+ &max98095_extmic_mux),
+
+ SND_SOC_DAPM_MUX("Linein Mux", SND_SOC_NOPM, 0, 0,
+ &max98095_linein_mux),
+
+ SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_hp_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_hp_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Receiver Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_mono_rcv_mixer_controls[0],
+ ARRAY_SIZE(max98095_mono_rcv_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_lineout_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_lineout_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_lineout_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_lineout_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_PGA_E("MIC1 Input", M98095_05F_LVL_MIC1,
+ 5, 0, NULL, 0, max98095_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("MIC2 Input", M98095_060_LVL_MIC2,
+ 5, 0, NULL, 0, max98095_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("IN1 Input", M98095_090_PWR_EN_IN,
+ 7, 0, NULL, 0, max98095_pga_in1_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("IN2 Input", M98095_090_PWR_EN_IN,
+ 7, 0, NULL, 0, max98095_pga_in2_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MICBIAS("MICBIAS1", M98095_090_PWR_EN_IN, 2, 0),
+ SND_SOC_DAPM_MICBIAS("MICBIAS2", M98095_090_PWR_EN_IN, 3, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("RCV"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("OUT4"),
+
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("INA1"),
+ SND_SOC_DAPM_INPUT("INA2"),
+ SND_SOC_DAPM_INPUT("INB1"),
+ SND_SOC_DAPM_INPUT("INB2"),
+};
+
+static const struct snd_soc_dapm_route max98095_audio_map[] = {
+ /* Left headphone output mixer */
+ {"Left Headphone Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Headphone Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Headphone Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Headphone Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right headphone output mixer */
+ {"Right Headphone Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Headphone Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Headphone Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Headphone Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Left speaker output mixer */
+ {"Left Speaker Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Speaker Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Speaker Mixer", "Mono DAC2 Switch", "DACM2"},
+ {"Left Speaker Mixer", "Mono DAC3 Switch", "DACM3"},
+ {"Left Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Speaker Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Speaker Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right speaker output mixer */
+ {"Right Speaker Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Speaker Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Speaker Mixer", "Mono DAC2 Switch", "DACM2"},
+ {"Right Speaker Mixer", "Mono DAC3 Switch", "DACM3"},
+ {"Right Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Speaker Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Speaker Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Earpiece/Receiver output mixer */
+ {"Receiver Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Receiver Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Receiver Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Receiver Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Receiver Mixer", "IN1 Switch", "IN1 Input"},
+ {"Receiver Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Left Lineout output mixer */
+ {"Left Lineout Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Lineout Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Lineout Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Lineout Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Lineout Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Lineout Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right lineout output mixer */
+ {"Right Lineout Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Lineout Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Lineout Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Lineout Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Lineout Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Lineout Mixer", "IN2 Switch", "IN2 Input"},
+
+ {"HP Left Out", NULL, "Left Headphone Mixer"},
+ {"HP Right Out", NULL, "Right Headphone Mixer"},
+ {"SPK Left Out", NULL, "Left Speaker Mixer"},
+ {"SPK Right Out", NULL, "Right Speaker Mixer"},
+ {"RCV Mono Out", NULL, "Receiver Mixer"},
+ {"LINE Left Out", NULL, "Left Lineout Mixer"},
+ {"LINE Right Out", NULL, "Right Lineout Mixer"},
+
+ {"HPL", NULL, "HP Left Out"},
+ {"HPR", NULL, "HP Right Out"},
+ {"SPKL", NULL, "SPK Left Out"},
+ {"SPKR", NULL, "SPK Right Out"},
+ {"RCV", NULL, "RCV Mono Out"},
+ {"OUT1", NULL, "LINE Left Out"},
+ {"OUT2", NULL, "LINE Right Out"},
+ {"OUT3", NULL, "LINE Left Out"},
+ {"OUT4", NULL, "LINE Right Out"},
+
+ /* Left ADC input mixer */
+ {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left ADC Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left ADC Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right ADC input mixer */
+ {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right ADC Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right ADC Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Inputs */
+ {"ADCL", NULL, "Left ADC Mixer"},
+ {"ADCR", NULL, "Right ADC Mixer"},
+
+ {"IN1 Input", NULL, "INA1"},
+ {"IN2 Input", NULL, "INA2"},
+
+ {"MIC1 Input", NULL, "MIC1"},
+ {"MIC2 Input", NULL, "MIC2"},
+};
+
+/* codec mclk clock divider coefficients */
+static const struct {
+ u32 rate;
+ u8 sr;
+} rate_table[] = {
+ {8000, 0x01},
+ {11025, 0x02},
+ {16000, 0x03},
+ {22050, 0x04},
+ {24000, 0x05},
+ {32000, 0x06},
+ {44100, 0x07},
+ {48000, 0x08},
+ {88200, 0x09},
+ {96000, 0x0A},
+};
+
+static int rate_value(int rate, u8 *value)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= rate) {
+ *value = rate_table[i].sr;
+ return 0;
+ }
+ }
+ *value = rate_table[0].sr;
+ return -EINVAL;
+}
+
+static int max98095_dai1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[0];
+
+ rate = params_rate(params);
+
+ switch (params_width(params)) {
+ case 16:
+ snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case 24:
+ snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, 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 (max98095->sysclk == 0) {
+ dev_err(codec->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,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[1];
+
+ rate = params_rate(params);
+
+ switch (params_width(params)) {
+ case 16:
+ snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case 24:
+ snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, 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 (max98095->sysclk == 0) {
+ dev_err(codec->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,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai3_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[2];
+
+ rate = params_rate(params);
+
+ switch (params_width(params)) {
+ case 16:
+ snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case 24:
+ snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, 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 (max98095->sysclk == 0) {
+ dev_err(codec->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,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ /* Requested clock frequency is already setup */
+ if (freq == max98095->sysclk)
+ return 0;
+
+ if (!IS_ERR(max98095->mclk)) {
+ freq = clk_round_rate(max98095->mclk, freq);
+ clk_set_rate(max98095->mclk, freq);
+ }
+
+ /* Setup clocks for slave mode, and using the PLL
+ * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+ * 0x02 (when master clk is 20MHz to 40MHz)..
+ * 0x03 (when master clk is 40MHz to 60MHz)..
+ */
+ if ((freq >= 10000000) && (freq < 20000000)) {
+ snd_soc_write(codec, M98095_026_SYS_CLK, 0x10);
+ } else if ((freq >= 20000000) && (freq < 40000000)) {
+ snd_soc_write(codec, M98095_026_SYS_CLK, 0x20);
+ } else if ((freq >= 40000000) && (freq < 60000000)) {
+ snd_soc_write(codec, M98095_026_SYS_CLK, 0x30);
+ } else {
+ dev_err(codec->dev, "Invalid master clock frequency\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+ max98095->sysclk = freq;
+ return 0;
+}
+
+static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[0];
+
+ 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,
+ 0x80);
+ snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master 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");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, 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);
+ }
+
+ return 0;
+}
+
+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 max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[1];
+
+ 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,
+ 0x80);
+ snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master 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");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, 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,
+ M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+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 max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[2];
+
+ 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,
+ 0x80);
+ snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master 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");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, 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,
+ M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98095_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /*
+ * SND_SOC_BIAS_PREPARE is called while preparing for a
+ * transition to ON or away from ON. If current bias_level
+ * is SND_SOC_BIAS_ON, then it is preparing for a transition
+ * away from ON. Disable the clock in that case, otherwise
+ * enable it.
+ */
+ if (!IS_ERR(max98095->mclk)) {
+ if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
+ clk_disable_unprepare(max98095->mclk);
+ else
+ clk_prepare_enable(max98095->mclk);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regcache_sync(max98095->regmap);
+
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+
+ snd_soc_update_bits(codec, 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,
+ M98095_MBEN, 0);
+ regcache_mark_dirty(max98095->regmap);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define MAX98095_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98095_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops max98095_dai1_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai1_set_fmt,
+ .hw_params = max98095_dai1_hw_params,
+};
+
+static const struct snd_soc_dai_ops max98095_dai2_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai2_set_fmt,
+ .hw_params = max98095_dai2_hw_params,
+};
+
+static const struct snd_soc_dai_ops max98095_dai3_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai3_set_fmt,
+ .hw_params = max98095_dai3_hw_params,
+};
+
+static struct snd_soc_dai_driver max98095_dai[] = {
+{
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai1_ops,
+},
+{
+ .name = "Aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai2_ops,
+},
+{
+ .name = "Voice",
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai3_ops,
+}
+
+};
+
+static int max98095_get_eq_channel(const char *name)
+{
+ if (strcmp(name, "EQ1 Mode") == 0)
+ return 0;
+ if (strcmp(name, "EQ2 Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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];
+ struct max98095_eq_cfg *coef_set;
+ int fs, best, best_val, i;
+ int regmask, regsave;
+
+ if (WARN_ON(channel > 1))
+ return -EINVAL;
+
+ if (!pdata || !max98095->eq_textcnt)
+ return 0;
+
+ if (sel >= pdata->eq_cfgcnt)
+ return -EINVAL;
+
+ cdata = &max98095->dai[channel];
+ cdata->eq_sel = sel;
+ fs = cdata->rate;
+
+ /* Find the selected configuration with nearest sample rate */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->eq_cfgcnt; i++) {
+ if (strcmp(pdata->eq_cfg[i].name, max98095->eq_texts[sel]) == 0 &&
+ abs(pdata->eq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->eq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->eq_cfg[best].name,
+ pdata->eq_cfg[best].rate, fs);
+
+ coef_set = &pdata->eq_cfg[best];
+
+ regmask = (channel == 0) ? M98095_EQ1EN : M98095_EQ2EN;
+
+ /* Disable filter while configuring, and save current on/off state */
+ regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
+ snd_soc_update_bits(codec, 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);
+ mutex_unlock(&max98095->lock);
+
+ /* Restore the original on/off state */
+ snd_soc_update_bits(codec, 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);
+ int channel = max98095_get_eq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+
+ cdata = &max98095->dai[channel];
+ ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+
+ return 0;
+}
+
+static void max98095_handle_eq_pdata(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ struct max98095_eq_cfg *cfg;
+ unsigned int cfgcnt;
+ int i, j;
+ const char **t;
+ int ret;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("EQ1 Mode",
+ max98095->eq_enum,
+ max98095_get_eq_enum,
+ max98095_put_eq_enum),
+ SOC_ENUM_EXT("EQ2 Mode",
+ max98095->eq_enum,
+ max98095_get_eq_enum,
+ max98095_put_eq_enum),
+ };
+
+ cfg = pdata->eq_cfg;
+ cfgcnt = pdata->eq_cfgcnt;
+
+ /* Setup an array of texts for the equalizer enum.
+ * This is based on Mark Brown's equalizer driver code.
+ */
+ max98095->eq_textcnt = 0;
+ max98095->eq_texts = NULL;
+ for (i = 0; i < cfgcnt; i++) {
+ for (j = 0; j < max98095->eq_textcnt; j++) {
+ if (strcmp(cfg[i].name, max98095->eq_texts[j]) == 0)
+ break;
+ }
+
+ if (j != max98095->eq_textcnt)
+ continue;
+
+ /* Expand the array */
+ t = krealloc(max98095->eq_texts,
+ sizeof(char *) * (max98095->eq_textcnt + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* Store the new entry */
+ t[max98095->eq_textcnt] = cfg[i].name;
+ max98095->eq_textcnt++;
+ max98095->eq_texts = t;
+ }
+
+ /* Now point the soc_enum to .texts array items */
+ max98095->eq_enum.texts = max98095->eq_texts;
+ max98095->eq_enum.items = max98095->eq_textcnt;
+
+ ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->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,
+ 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;
+
+ /* Shouldn't happen */
+ dev_err(codec->dev, "Bad biquad channel name '%s'\n", name);
+ return -EINVAL;
+}
+
+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 max98095_pdata *pdata = max98095->pdata;
+ int channel = max98095_get_bq_channel(codec, kcontrol->id.name);
+ struct max98095_cdata *cdata;
+ unsigned int sel = ucontrol->value.integer.value[0];
+ struct max98095_biquad_cfg *coef_set;
+ int fs, best, best_val, i;
+ int regmask, regsave;
+
+ if (channel < 0)
+ return channel;
+
+ if (!pdata || !max98095->bq_textcnt)
+ return 0;
+
+ if (sel >= pdata->bq_cfgcnt)
+ return -EINVAL;
+
+ cdata = &max98095->dai[channel];
+ cdata->bq_sel = sel;
+ fs = cdata->rate;
+
+ /* Find the selected configuration with nearest sample rate */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->bq_cfgcnt; i++) {
+ if (strcmp(pdata->bq_cfg[i].name, max98095->bq_texts[sel]) == 0 &&
+ abs(pdata->bq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->bq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->bq_cfg[best].name,
+ pdata->bq_cfg[best].rate, fs);
+
+ coef_set = &pdata->bq_cfg[best];
+
+ regmask = (channel == 0) ? M98095_BQ1EN : M98095_BQ2EN;
+
+ /* Disable filter while configuring, and save current on/off state */
+ regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
+ snd_soc_update_bits(codec, 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);
+ mutex_unlock(&max98095->lock);
+
+ /* Restore the original on/off state */
+ snd_soc_update_bits(codec, 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 max98095_cdata *cdata;
+
+ if (channel < 0)
+ return channel;
+
+ cdata = &max98095->dai[channel];
+ ucontrol->value.enumerated.item[0] = cdata->bq_sel;
+
+ return 0;
+}
+
+static void max98095_handle_bq_pdata(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ struct max98095_biquad_cfg *cfg;
+ unsigned int cfgcnt;
+ int i, j;
+ const char **t;
+ int ret;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT((char *)bq_mode_name[0],
+ max98095->bq_enum,
+ max98095_get_bq_enum,
+ max98095_put_bq_enum),
+ SOC_ENUM_EXT((char *)bq_mode_name[1],
+ max98095->bq_enum,
+ max98095_get_bq_enum,
+ max98095_put_bq_enum),
+ };
+ BUILD_BUG_ON(ARRAY_SIZE(controls) != ARRAY_SIZE(bq_mode_name));
+
+ cfg = pdata->bq_cfg;
+ cfgcnt = pdata->bq_cfgcnt;
+
+ /* Setup an array of texts for the biquad enum.
+ * This is based on Mark Brown's equalizer driver code.
+ */
+ max98095->bq_textcnt = 0;
+ max98095->bq_texts = NULL;
+ for (i = 0; i < cfgcnt; i++) {
+ for (j = 0; j < max98095->bq_textcnt; j++) {
+ if (strcmp(cfg[i].name, max98095->bq_texts[j]) == 0)
+ break;
+ }
+
+ if (j != max98095->bq_textcnt)
+ continue;
+
+ /* Expand the array */
+ t = krealloc(max98095->bq_texts,
+ sizeof(char *) * (max98095->bq_textcnt + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* Store the new entry */
+ t[max98095->bq_textcnt] = cfg[i].name;
+ max98095->bq_textcnt++;
+ max98095->bq_texts = t;
+ }
+
+ /* Now point the soc_enum to .texts array items */
+ max98095->bq_enum.texts = max98095->bq_texts;
+ max98095->bq_enum.items = max98095->bq_textcnt;
+
+ ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add Biquad control: %d\n", ret);
+}
+
+static void max98095_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ u8 regval = 0;
+
+ if (!pdata) {
+ dev_dbg(codec->dev, "No platform data\n");
+ return;
+ }
+
+ /* Configure mic for analog/digital mic mode */
+ if (pdata->digmic_left_mode)
+ regval |= M98095_DIGMIC_L;
+
+ if (pdata->digmic_right_mode)
+ regval |= M98095_DIGMIC_R;
+
+ snd_soc_write(codec, M98095_087_CFG_MIC, regval);
+
+ /* Configure equalizers */
+ if (pdata->eq_cfgcnt)
+ max98095_handle_eq_pdata(codec);
+
+ /* Configure bi-quad filters */
+ if (pdata->bq_cfgcnt)
+ max98095_handle_bq_pdata(codec);
+}
+
+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);
+ 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);
+
+ /* If ddone is not set, then detection isn't finished yet */
+ if ((value & M98095_DDONE) == 0)
+ return IRQ_NONE;
+
+ /* if hp, check its bit, and if set, clear it */
+ if ((value & M98095_HP_IN || value & M98095_LO_IN) &&
+ max98095->headphone_jack)
+ hp_report |= SND_JACK_HEADPHONE;
+
+ /* if mic, check its bit, and if set, clear it */
+ if ((value & M98095_MIC_IN) && max98095->mic_jack)
+ mic_report |= SND_JACK_MICROPHONE;
+
+ if (max98095->headphone_jack == max98095->mic_jack) {
+ snd_soc_jack_report(max98095->headphone_jack,
+ hp_report | mic_report,
+ SND_JACK_HEADSET);
+ } else {
+ if (max98095->headphone_jack)
+ snd_soc_jack_report(max98095->headphone_jack,
+ hp_report, SND_JACK_HEADPHONE);
+ if (max98095->mic_jack)
+ snd_soc_jack_report(max98095->mic_jack,
+ mic_report, SND_JACK_MICROPHONE);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int max98095_jack_detect_enable(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ int detect_enable = M98095_JDEN;
+ unsigned int slew = M98095_DEFAULT_SLEW_DELAY;
+
+ if (max98095->pdata->jack_detect_pin5en)
+ detect_enable |= M98095_PIN5EN;
+
+ if (max98095->pdata->jack_detect_delay)
+ slew = max98095->pdata->jack_detect_delay;
+
+ ret = snd_soc_write(codec, M98095_08E_JACK_DC_SLEW, slew);
+ if (ret < 0) {
+ dev_err(codec->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);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int max98095_jack_detect_disable(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+
+ /* configure auto detection to be disabled */
+ ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, 0x0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+int max98095_jack_detect(struct snd_soc_codec *codec,
+ 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);
+ int ret = 0;
+
+ max98095->headphone_jack = hp_jack;
+ max98095->mic_jack = mic_jack;
+
+ /* only progress if we have at least 1 jack pointer */
+ if (!hp_jack && !mic_jack)
+ return -EINVAL;
+
+ max98095_jack_detect_enable(codec);
+
+ /* enable interrupts for headphone jack detection */
+ ret = snd_soc_update_bits(codec, M98095_013_JACK_INT_EN,
+ M98095_IDDONE, M98095_IDDONE);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to cfg jack irqs %d\n", ret);
+ return ret;
+ }
+
+ max98095_report_jack(client->irq, codec);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max98095_jack_detect);
+
+#ifdef CONFIG_PM
+static int max98095_suspend(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ if (max98095->headphone_jack || max98095->mic_jack)
+ max98095_jack_detect_disable(codec);
+
+ max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int max98095_resume(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *client = to_i2c_client(codec->dev);
+
+ max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ if (max98095->headphone_jack || max98095->mic_jack) {
+ max98095_jack_detect_enable(codec);
+ max98095_report_jack(client->irq, codec);
+ }
+
+ return 0;
+}
+#else
+#define max98095_suspend NULL
+#define max98095_resume NULL
+#endif
+
+static int max98095_reset(struct snd_soc_codec *codec)
+{
+ 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);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to reset DSP: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_write(codec, M98095_097_PWR_SYS, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to reset codec: %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));
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to reset: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int max98095_probe(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ struct i2c_client *client;
+ int ret = 0;
+
+ max98095->mclk = devm_clk_get(codec->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);
+
+ client = to_i2c_client(codec->dev);
+
+ /* initialize private data */
+
+ max98095->sysclk = (unsigned)-1;
+ max98095->eq_textcnt = 0;
+ max98095->bq_textcnt = 0;
+
+ cdata = &max98095->dai[0];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ cdata = &max98095->dai[1];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ cdata = &max98095->dai[2];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ max98095->lin_state = 0;
+ max98095->mic1pre = 0;
+ max98095->mic2pre = 0;
+
+ if (client->irq) {
+ /* register an audio interrupt */
+ ret = request_threaded_irq(client->irq, NULL,
+ max98095_report_jack,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "max98095", codec);
+ if (ret) {
+ dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+ goto err_access;
+ }
+ }
+
+ ret = snd_soc_read(codec, M98095_0FF_REV_ID);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failure reading hardware revision: %d\n",
+ ret);
+ goto err_irq;
+ }
+ dev_info(codec->dev, "Hardware revision: %c\n", ret - 0x40 + 'A');
+
+ snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
+
+ snd_soc_write(codec, M98095_048_MIX_DAC_LR,
+ M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR);
+
+ snd_soc_write(codec, 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_write(codec, M98095_02C_DAI1_IOCFG,
+ M98095_S1NORMAL|M98095_SDATA);
+
+ snd_soc_write(codec, M98095_036_DAI2_IOCFG,
+ M98095_S2NORMAL|M98095_SDATA);
+
+ snd_soc_write(codec, M98095_040_DAI3_IOCFG,
+ M98095_S3NORMAL|M98095_SDATA);
+
+ max98095_handle_pdata(codec);
+
+ /* take the codec out of the shut down */
+ snd_soc_update_bits(codec, M98095_097_PWR_SYS, M98095_SHDNRUN,
+ M98095_SHDNRUN);
+
+ return 0;
+
+err_irq:
+ if (client->irq)
+ free_irq(client->irq, codec);
+err_access:
+ return ret;
+}
+
+static int max98095_remove(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *client = to_i2c_client(codec->dev);
+
+ if (max98095->headphone_jack || max98095->mic_jack)
+ max98095_jack_detect_disable(codec);
+
+ if (client->irq)
+ free_irq(client->irq, codec);
+
+ return 0;
+}
+
+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 int max98095_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max98095_priv *max98095;
+ int ret;
+
+ max98095 = devm_kzalloc(&i2c->dev, sizeof(struct max98095_priv),
+ GFP_KERNEL);
+ if (max98095 == NULL)
+ return -ENOMEM;
+
+ mutex_init(&max98095->lock);
+
+ max98095->regmap = devm_regmap_init_i2c(i2c, &max98095_regmap);
+ if (IS_ERR(max98095->regmap)) {
+ ret = PTR_ERR(max98095->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ max98095->devtype = id->driver_data;
+ i2c_set_clientdata(i2c, max98095);
+ max98095->pdata = i2c->dev.platform_data;
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_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);
+
+static const struct of_device_id max98095_of_match[] = {
+ { .compatible = "maxim,max98095", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98095_of_match);
+
+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,
+ .id_table = max98095_i2c_id,
+};
+
+module_i2c_driver(max98095_i2c_driver);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98095 driver");
+MODULE_AUTHOR("Peter Hsiang");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/max98095.h b/kernel/sound/soc/codecs/max98095.h
new file mode 100644
index 000000000..2ebbe4e89
--- /dev/null
+++ b/kernel/sound/soc/codecs/max98095.h
@@ -0,0 +1,321 @@
+/*
+ * 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
+#define _MAX98095_H
+
+/*
+ * MAX98095 Registers Definition
+ */
+
+#define M98095_000_HOST_DATA 0x00
+#define M98095_001_HOST_INT_STS 0x01
+#define M98095_002_HOST_RSP_STS 0x02
+#define M98095_003_HOST_CMD_STS 0x03
+#define M98095_004_CODEC_STS 0x04
+#define M98095_005_DAI1_ALC_STS 0x05
+#define M98095_006_DAI2_ALC_STS 0x06
+#define M98095_007_JACK_AUTO_STS 0x07
+#define M98095_008_JACK_MANUAL_STS 0x08
+#define M98095_009_JACK_VBAT_STS 0x09
+#define M98095_00A_ACC_ADC_STS 0x0A
+#define M98095_00B_MIC_NG_AGC_STS 0x0B
+#define M98095_00C_SPK_L_VOLT_STS 0x0C
+#define M98095_00D_SPK_R_VOLT_STS 0x0D
+#define M98095_00E_TEMP_SENSOR_STS 0x0E
+#define M98095_00F_HOST_CFG 0x0F
+#define M98095_010_HOST_INT_CFG 0x10
+#define M98095_011_HOST_INT_EN 0x11
+#define M98095_012_CODEC_INT_EN 0x12
+#define M98095_013_JACK_INT_EN 0x13
+#define M98095_014_JACK_INT_EN 0x14
+#define M98095_015_DEC 0x15
+#define M98095_016_RESERVED 0x16
+#define M98095_017_RESERVED 0x17
+#define M98095_018_KEYCODE3 0x18
+#define M98095_019_KEYCODE2 0x19
+#define M98095_01A_KEYCODE1 0x1A
+#define M98095_01B_KEYCODE0 0x1B
+#define M98095_01C_OEMCODE1 0x1C
+#define M98095_01D_OEMCODE0 0x1D
+#define M98095_01E_XCFG1 0x1E
+#define M98095_01F_XCFG2 0x1F
+#define M98095_020_XCFG3 0x20
+#define M98095_021_XCFG4 0x21
+#define M98095_022_XCFG5 0x22
+#define M98095_023_XCFG6 0x23
+#define M98095_024_XGPIO 0x24
+#define M98095_025_XCLKCFG 0x25
+#define M98095_026_SYS_CLK 0x26
+#define M98095_027_DAI1_CLKMODE 0x27
+#define M98095_028_DAI1_CLKCFG_HI 0x28
+#define M98095_029_DAI1_CLKCFG_LO 0x29
+#define M98095_02A_DAI1_FORMAT 0x2A
+#define M98095_02B_DAI1_CLOCK 0x2B
+#define M98095_02C_DAI1_IOCFG 0x2C
+#define M98095_02D_DAI1_TDM 0x2D
+#define M98095_02E_DAI1_FILTERS 0x2E
+#define M98095_02F_DAI1_LVL1 0x2F
+#define M98095_030_DAI1_LVL2 0x30
+#define M98095_031_DAI2_CLKMODE 0x31
+#define M98095_032_DAI2_CLKCFG_HI 0x32
+#define M98095_033_DAI2_CLKCFG_LO 0x33
+#define M98095_034_DAI2_FORMAT 0x34
+#define M98095_035_DAI2_CLOCK 0x35
+#define M98095_036_DAI2_IOCFG 0x36
+#define M98095_037_DAI2_TDM 0x37
+#define M98095_038_DAI2_FILTERS 0x38
+#define M98095_039_DAI2_LVL1 0x39
+#define M98095_03A_DAI2_LVL2 0x3A
+#define M98095_03B_DAI3_CLKMODE 0x3B
+#define M98095_03C_DAI3_CLKCFG_HI 0x3C
+#define M98095_03D_DAI3_CLKCFG_LO 0x3D
+#define M98095_03E_DAI3_FORMAT 0x3E
+#define M98095_03F_DAI3_CLOCK 0x3F
+#define M98095_040_DAI3_IOCFG 0x40
+#define M98095_041_DAI3_TDM 0x41
+#define M98095_042_DAI3_FILTERS 0x42
+#define M98095_043_DAI3_LVL1 0x43
+#define M98095_044_DAI3_LVL2 0x44
+#define M98095_045_CFG_DSP 0x45
+#define M98095_046_DAC_CTRL1 0x46
+#define M98095_047_DAC_CTRL2 0x47
+#define M98095_048_MIX_DAC_LR 0x48
+#define M98095_049_MIX_DAC_M 0x49
+#define M98095_04A_MIX_ADC_LEFT 0x4A
+#define M98095_04B_MIX_ADC_RIGHT 0x4B
+#define M98095_04C_MIX_HP_LEFT 0x4C
+#define M98095_04D_MIX_HP_RIGHT 0x4D
+#define M98095_04E_CFG_HP 0x4E
+#define M98095_04F_MIX_RCV 0x4F
+#define M98095_050_MIX_SPK_LEFT 0x50
+#define M98095_051_MIX_SPK_RIGHT 0x51
+#define M98095_052_MIX_SPK_CFG 0x52
+#define M98095_053_MIX_LINEOUT1 0x53
+#define M98095_054_MIX_LINEOUT2 0x54
+#define M98095_055_MIX_LINEOUT_CFG 0x55
+#define M98095_056_LVL_SIDETONE_DAI12 0x56
+#define M98095_057_LVL_SIDETONE_DAI3 0x57
+#define M98095_058_LVL_DAI1_PLAY 0x58
+#define M98095_059_LVL_DAI1_EQ 0x59
+#define M98095_05A_LVL_DAI2_PLAY 0x5A
+#define M98095_05B_LVL_DAI2_EQ 0x5B
+#define M98095_05C_LVL_DAI3_PLAY 0x5C
+#define M98095_05D_LVL_ADC_L 0x5D
+#define M98095_05E_LVL_ADC_R 0x5E
+#define M98095_05F_LVL_MIC1 0x5F
+#define M98095_060_LVL_MIC2 0x60
+#define M98095_061_LVL_LINEIN 0x61
+#define M98095_062_LVL_LINEOUT1 0x62
+#define M98095_063_LVL_LINEOUT2 0x63
+#define M98095_064_LVL_HP_L 0x64
+#define M98095_065_LVL_HP_R 0x65
+#define M98095_066_LVL_RCV 0x66
+#define M98095_067_LVL_SPK_L 0x67
+#define M98095_068_LVL_SPK_R 0x68
+#define M98095_069_MICAGC_CFG 0x69
+#define M98095_06A_MICAGC_THRESH 0x6A
+#define M98095_06B_SPK_NOISEGATE 0x6B
+#define M98095_06C_DAI1_ALC1_TIME 0x6C
+#define M98095_06D_DAI1_ALC1_COMP 0x6D
+#define M98095_06E_DAI1_ALC1_EXPN 0x6E
+#define M98095_06F_DAI1_ALC1_GAIN 0x6F
+#define M98095_070_DAI1_ALC2_TIME 0x70
+#define M98095_071_DAI1_ALC2_COMP 0x71
+#define M98095_072_DAI1_ALC2_EXPN 0x72
+#define M98095_073_DAI1_ALC2_GAIN 0x73
+#define M98095_074_DAI1_ALC3_TIME 0x74
+#define M98095_075_DAI1_ALC3_COMP 0x75
+#define M98095_076_DAI1_ALC3_EXPN 0x76
+#define M98095_077_DAI1_ALC3_GAIN 0x77
+#define M98095_078_DAI2_ALC1_TIME 0x78
+#define M98095_079_DAI2_ALC1_COMP 0x79
+#define M98095_07A_DAI2_ALC1_EXPN 0x7A
+#define M98095_07B_DAI2_ALC1_GAIN 0x7B
+#define M98095_07C_DAI2_ALC2_TIME 0x7C
+#define M98095_07D_DAI2_ALC2_COMP 0x7D
+#define M98095_07E_DAI2_ALC2_EXPN 0x7E
+#define M98095_07F_DAI2_ALC2_GAIN 0x7F
+#define M98095_080_DAI2_ALC3_TIME 0x80
+#define M98095_081_DAI2_ALC3_COMP 0x81
+#define M98095_082_DAI2_ALC3_EXPN 0x82
+#define M98095_083_DAI2_ALC3_GAIN 0x83
+#define M98095_084_HP_NOISE_GATE 0x84
+#define M98095_085_AUX_ADC 0x85
+#define M98095_086_CFG_LINE 0x86
+#define M98095_087_CFG_MIC 0x87
+#define M98095_088_CFG_LEVEL 0x88
+#define M98095_089_JACK_DET_AUTO 0x89
+#define M98095_08A_JACK_DET_MANUAL 0x8A
+#define M98095_08B_JACK_KEYSCAN_DBC 0x8B
+#define M98095_08C_JACK_KEYSCAN_DLY 0x8C
+#define M98095_08D_JACK_KEY_THRESH 0x8D
+#define M98095_08E_JACK_DC_SLEW 0x8E
+#define M98095_08F_JACK_TEST_CFG 0x8F
+#define M98095_090_PWR_EN_IN 0x90
+#define M98095_091_PWR_EN_OUT 0x91
+#define M98095_092_PWR_EN_OUT 0x92
+#define M98095_093_BIAS_CTRL 0x93
+#define M98095_094_PWR_DAC_21 0x94
+#define M98095_095_PWR_DAC_03 0x95
+#define M98095_096_PWR_DAC_CK 0x96
+#define M98095_097_PWR_SYS 0x97
+
+#define M98095_0FF_REV_ID 0xFF
+
+#define M98095_REG_CNT (0xFF+1)
+#define M98095_REG_MAX_CACHED 0X97
+
+/* MAX98095 Registers Bit Fields */
+
+/* M98095_007_JACK_AUTO_STS */
+ #define M98095_MIC_IN (1<<3)
+ #define M98095_LO_IN (1<<5)
+ #define M98095_HP_IN (1<<6)
+ #define M98095_DDONE (1<<7)
+
+/* M98095_00F_HOST_CFG */
+ #define M98095_SEG (1<<0)
+ #define M98095_XTEN (1<<1)
+ #define M98095_MDLLEN (1<<2)
+
+/* M98095_013_JACK_INT_EN */
+ #define M98095_IMIC_IN (1<<3)
+ #define M98095_ILO_IN (1<<5)
+ #define M98095_IHP_IN (1<<6)
+ #define M98095_IDDONE (1<<7)
+
+/* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */
+ #define M98095_CLKMODE_MASK 0xFF
+
+/* M98095_02A_DAI1_FORMAT, M98095_034_DAI2_FORMAT, M98095_03E_DAI3_FORMAT */
+ #define M98095_DAI_MAS (1<<7)
+ #define M98095_DAI_WCI (1<<6)
+ #define M98095_DAI_BCI (1<<5)
+ #define M98095_DAI_DLY (1<<4)
+ #define M98095_DAI_TDM (1<<2)
+ #define M98095_DAI_FSW (1<<1)
+ #define M98095_DAI_WS (1<<0)
+
+/* M98095_02B_DAI1_CLOCK, M98095_035_DAI2_CLOCK, M98095_03F_DAI3_CLOCK */
+ #define M98095_DAI_BSEL64 (1<<0)
+ #define M98095_DAI_DOSR_DIV2 (0<<5)
+ #define M98095_DAI_DOSR_DIV4 (1<<5)
+
+/* M98095_02C_DAI1_IOCFG, M98095_036_DAI2_IOCFG, M98095_040_DAI3_IOCFG */
+ #define M98095_S1NORMAL (1<<6)
+ #define M98095_S2NORMAL (2<<6)
+ #define M98095_S3NORMAL (3<<6)
+ #define M98095_SDATA (3<<0)
+
+/* M98095_02E_DAI1_FILTERS, M98095_038_DAI2_FILTERS, M98095_042_DAI3_FILTERS */
+ #define M98095_DAI_DHF (1<<3)
+
+/* M98095_045_DSP_CFG */
+ #define M98095_DSPNORMAL (5<<4)
+
+/* M98095_048_MIX_DAC_LR */
+ #define M98095_DAI1L_TO_DACR (1<<7)
+ #define M98095_DAI1R_TO_DACR (1<<6)
+ #define M98095_DAI2M_TO_DACR (1<<5)
+ #define M98095_DAI1L_TO_DACL (1<<3)
+ #define M98095_DAI1R_TO_DACL (1<<2)
+ #define M98095_DAI2M_TO_DACL (1<<1)
+ #define M98095_DAI3M_TO_DACL (1<<0)
+
+/* M98095_049_MIX_DAC_M */
+ #define M98095_DAI1L_TO_DACM (1<<3)
+ #define M98095_DAI1R_TO_DACM (1<<2)
+ #define M98095_DAI2M_TO_DACM (1<<1)
+ #define M98095_DAI3M_TO_DACM (1<<0)
+
+/* M98095_04E_MIX_HP_CFG */
+ #define M98095_HPNORMAL (3<<4)
+
+/* M98095_05F_LVL_MIC1, M98095_060_LVL_MIC2 */
+ #define M98095_MICPRE_MASK (3<<5)
+ #define M98095_MICPRE_SHIFT 5
+
+/* M98095_064_LVL_HP_L, M98095_065_LVL_HP_R */
+ #define M98095_HP_MUTE (1<<7)
+
+/* M98095_066_LVL_RCV */
+ #define M98095_REC_MUTE (1<<7)
+
+/* M98095_067_LVL_SPK_L, M98095_068_LVL_SPK_R */
+ #define M98095_SP_MUTE (1<<7)
+
+/* M98095_087_CFG_MIC */
+ #define M98095_MICSEL_MASK (3<<0)
+ #define M98095_DIGMIC_L (1<<2)
+ #define M98095_DIGMIC_R (1<<3)
+ #define M98095_DIGMIC2L (1<<4)
+ #define M98095_DIGMIC2R (1<<5)
+
+/* M98095_088_CFG_LEVEL */
+ #define M98095_VSEN (1<<6)
+ #define M98095_ZDEN (1<<5)
+ #define M98095_BQ2EN (1<<3)
+ #define M98095_BQ1EN (1<<2)
+ #define M98095_EQ2EN (1<<1)
+ #define M98095_EQ1EN (1<<0)
+
+/* M98095_089_JACK_DET_AUTO */
+ #define M98095_PIN5EN (1<<2)
+ #define M98095_JDEN (1<<7)
+
+/* M98095_090_PWR_EN_IN */
+ #define M98095_INEN (1<<7)
+ #define M98095_MB2EN (1<<3)
+ #define M98095_MB1EN (1<<2)
+ #define M98095_MBEN (3<<2)
+ #define M98095_ADREN (1<<1)
+ #define M98095_ADLEN (1<<0)
+
+/* M98095_091_PWR_EN_OUT */
+ #define M98095_HPLEN (1<<7)
+ #define M98095_HPREN (1<<6)
+ #define M98095_SPLEN (1<<5)
+ #define M98095_SPREN (1<<4)
+ #define M98095_RECEN (1<<3)
+ #define M98095_DALEN (1<<1)
+ #define M98095_DAREN (1<<0)
+
+/* M98095_092_PWR_EN_OUT */
+ #define M98095_SPK_FIXEDSPECTRUM (0<<4)
+ #define M98095_SPK_SPREADSPECTRUM (1<<4)
+
+/* M98095_097_PWR_SYS */
+ #define M98095_SHDNRUN (1<<7)
+ #define M98095_PERFMODE (1<<3)
+ #define M98095_HPPLYBACK (1<<2)
+ #define M98095_PWRSV8K (1<<1)
+ #define M98095_PWRSV (1<<0)
+
+#define M98095_COEFS_PER_BAND 5
+
+#define M98095_BYTE1(w) ((w >> 8) & 0xff)
+#define M98095_BYTE0(w) (w & 0xff)
+
+/* Equalizer filter coefficients */
+#define M98095_110_DAI1_EQ_BASE 0x10
+#define M98095_142_DAI2_EQ_BASE 0x42
+
+/* Biquad filter coefficients */
+#define M98095_174_DAI1_BQ_BASE 0x74
+#define M98095_17E_DAI2_BQ_BASE 0x7E
+
+/* Default Delay used in Slew Rate Calculation for Jack detection */
+#define M98095_DEFAULT_SLEW_DELAY 0x18
+
+extern int max98095_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack);
+
+#endif
diff --git a/kernel/sound/soc/codecs/max98357a.c b/kernel/sound/soc/codecs/max98357a.c
new file mode 100644
index 000000000..bf3e933ee
--- /dev/null
+++ b/kernel/sound/soc/codecs/max98357a.c
@@ -0,0 +1,145 @@
+/* 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/device.h>
+#include <linux/err.h>
+#include <linux/gpio.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 <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+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);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ gpiod_set_value(sdmode, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ gpiod_set_value(sdmode, 0);
+ break;
+ }
+
+ 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"),
+};
+
+static const struct snd_soc_dapm_route max98357a_dapm_routes[] = {
+ {"Speaker", NULL, "SDMode"},
+};
+
+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,
+ .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),
+};
+
+static struct snd_soc_dai_ops max98357a_dai_ops = {
+ .trigger = max98357a_daiops_trigger,
+};
+
+static struct snd_soc_dai_driver max98357a_dai_driver = {
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &max98357a_dai_ops,
+};
+
+static int max98357a_platform_probe(struct platform_device *pdev)
+{
+ 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;
+}
+
+static int max98357a_platform_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id max98357a_device_id[] = {
+ { .compatible = "maxim,max98357a" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, max98357a_device_id);
+#endif
+
+static struct platform_driver max98357a_platform_driver = {
+ .driver = {
+ .name = "max98357a",
+ .of_match_table = of_match_ptr(max98357a_device_id),
+ },
+ .probe = max98357a_platform_probe,
+ .remove = max98357a_platform_remove,
+};
+module_platform_driver(max98357a_platform_driver);
+
+MODULE_DESCRIPTION("Maxim MAX98357A Codec Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/max9850.c b/kernel/sound/soc/codecs/max9850.c
new file mode 100644
index 000000000..10f8e47ce
--- /dev/null
+++ b/kernel/sound/soc/codecs/max9850.c
@@ -0,0 +1,367 @@
+/*
+ * max9850.c -- codec driver for max9850
+ *
+ * Copyright (C) 2011 taskit GmbH
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.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 "max9850.h"
+
+struct max9850_priv {
+ struct regmap *regmap;
+ 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)
+{
+ switch (reg) {
+ case MAX9850_STATUSA:
+ case MAX9850_STATUSB:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static const struct regmap_config max9850_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MAX9850_DIGITAL_AUDIO,
+ .volatile_reg = max9850_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const unsigned int max9850_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0),
+ 0x20, 0x33, TLV_DB_SCALE_ITEM(-4150, 200, 0),
+ 0x34, 0x37, TLV_DB_SCALE_ITEM(-150, 100, 0),
+ 0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0),
+};
+
+static const struct snd_kcontrol_new max9850_controls[] = {
+SOC_SINGLE_TLV("Headphone Volume", MAX9850_VOLUME, 0, 0x3f, 1, max9850_tlv),
+SOC_SINGLE("Headphone Switch", MAX9850_VOLUME, 7, 1, 1),
+SOC_SINGLE("Mono Switch", MAX9850_GENERAL_PURPOSE, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new max9850_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Line In Switch", MAX9850_ENABLE, 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget max9850_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("Charge Pump 1", MAX9850_ENABLE, 4, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("Charge Pump 2", MAX9850_ENABLE, 5, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MCLK", MAX9850_ENABLE, 6, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("SHDN", MAX9850_ENABLE, 7, 0, NULL, 0),
+SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", MAX9850_ENABLE, 2, 0,
+ &max9850_mixer_controls[0],
+ ARRAY_SIZE(max9850_mixer_controls)),
+SND_SOC_DAPM_PGA("Headphone Output", MAX9850_ENABLE, 3, 0, NULL, 0),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", MAX9850_ENABLE, 0, 0),
+SND_SOC_DAPM_OUTPUT("OUTL"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("OUTR"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_INPUT("INL"),
+SND_SOC_DAPM_INPUT("INR"),
+};
+
+static const struct snd_soc_dapm_route max9850_dapm_routes[] = {
+ /* output mixer */
+ {"Output Mixer", NULL, "DAC"},
+ {"Output Mixer", "Line In Switch", "Line Input"},
+
+ /* outputs */
+ {"Headphone Output", NULL, "Output Mixer"},
+ {"HPL", NULL, "Headphone Output"},
+ {"HPR", NULL, "Headphone Output"},
+ {"OUTL", NULL, "Output Mixer"},
+ {"OUTR", NULL, "Output Mixer"},
+
+ /* inputs */
+ {"Line Input", NULL, "INL"},
+ {"Line Input", NULL, "INR"},
+
+ /* supplies */
+ {"Output Mixer", NULL, "Charge Pump 1"},
+ {"Output Mixer", NULL, "Charge Pump 2"},
+ {"Output Mixer", NULL, "SHDN"},
+ {"DAC", NULL, "MCLK"},
+};
+
+static int max9850_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+ u64 lrclk_div;
+ u8 sf, da;
+
+ if (!max9850->sysclk)
+ return -EINVAL;
+
+ /* lrclk_div = 2^22 * rate / iclk with iclk = mclk / sf */
+ sf = (snd_soc_read(codec, 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);
+
+ switch (params_width(params)) {
+ case 16:
+ da = 0;
+ break;
+ case 20:
+ da = 0x2;
+ break;
+ case 24:
+ da = 0x3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, MAX9850_DIGITAL_AUDIO, 0x3, da);
+
+ return 0;
+}
+
+static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+
+ /* calculate mclk -> iclk divider */
+ if (freq <= 13000000)
+ snd_soc_write(codec, MAX9850_CLOCK, 0x0);
+ else if (freq <= 26000000)
+ snd_soc_write(codec, MAX9850_CLOCK, 0x4);
+ else if (freq <= 40000000)
+ snd_soc_write(codec, MAX9850_CLOCK, 0x8);
+ else
+ return -EINVAL;
+
+ max9850->sysclk = freq;
+ return 0;
+}
+
+static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 da = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ da |= MAX9850_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ da |= MAX9850_DLY;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ da |= MAX9850_RTJ;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ da |= MAX9850_BCINV | MAX9850_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ da |= MAX9850_BCINV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ da |= MAX9850_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set da */
+ snd_soc_write(codec, MAX9850_DIGITAL_AUDIO, da);
+
+ return 0;
+}
+
+static int max9850_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regcache_sync(max9850->regmap);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define MAX9850_RATES SNDRV_PCM_RATE_8000_48000
+
+#define MAX9850_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops max9850_dai_ops = {
+ .hw_params = max9850_hw_params,
+ .set_sysclk = max9850_set_dai_sysclk,
+ .set_fmt = max9850_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver max9850_dai = {
+ .name = "max9850-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX9850_RATES,
+ .formats = MAX9850_FORMATS
+ },
+ .ops = &max9850_dai_ops,
+};
+
+static int max9850_probe(struct snd_soc_codec *codec)
+{
+ /* enable zero-detect */
+ snd_soc_update_bits(codec, MAX9850_GENERAL_PURPOSE, 1, 1);
+ /* enable slew-rate control */
+ snd_soc_update_bits(codec, MAX9850_VOLUME, 0x40, 0x40);
+ /* set slew-rate 125ms */
+ snd_soc_update_bits(codec, 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 int max9850_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max9850_priv *max9850;
+ int ret;
+
+ max9850 = devm_kzalloc(&i2c->dev, sizeof(struct max9850_priv),
+ GFP_KERNEL);
+ if (max9850 == NULL)
+ return -ENOMEM;
+
+ max9850->regmap = devm_regmap_init_i2c(i2c, &max9850_regmap);
+ if (IS_ERR(max9850->regmap))
+ return PTR_ERR(max9850->regmap);
+
+ i2c_set_clientdata(i2c, max9850);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_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 },
+ { }
+};
+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,
+};
+
+module_i2c_driver(max9850_i2c_driver);
+
+MODULE_AUTHOR("Christian Glindkamp <christian.glindkamp@taskit.de>");
+MODULE_DESCRIPTION("ASoC MAX9850 codec driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/max9850.h b/kernel/sound/soc/codecs/max9850.h
new file mode 100644
index 000000000..72b1ddb04
--- /dev/null
+++ b/kernel/sound/soc/codecs/max9850.h
@@ -0,0 +1,38 @@
+/*
+ * 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
+#define _MAX9850_H
+
+#define MAX9850_STATUSA 0x00
+#define MAX9850_STATUSB 0x01
+#define MAX9850_VOLUME 0x02
+#define MAX9850_GENERAL_PURPOSE 0x03
+#define MAX9850_INTERRUPT 0x04
+#define MAX9850_ENABLE 0x05
+#define MAX9850_CLOCK 0x06
+#define MAX9850_CHARGE_PUMP 0x07
+#define MAX9850_LRCLK_MSB 0x08
+#define MAX9850_LRCLK_LSB 0x09
+#define MAX9850_DIGITAL_AUDIO 0x0a
+
+#define MAX9850_CACHEREGNUM 11
+
+/* MAX9850_DIGITAL_AUDIO */
+#define MAX9850_MASTER (1<<7)
+#define MAX9850_INV (1<<6)
+#define MAX9850_BCINV (1<<5)
+#define MAX9850_DLY (1<<3)
+#define MAX9850_RTJ (1<<2)
+
+#endif
diff --git a/kernel/sound/soc/codecs/max9877.c b/kernel/sound/soc/codecs/max9877.c
new file mode 100644
index 000000000..29549cdbf
--- /dev/null
+++ b/kernel/sound/soc/codecs/max9877.c
@@ -0,0 +1,188 @@
+/*
+ * 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>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max9877.h"
+
+static struct regmap *regmap;
+
+static struct reg_default max9877_regs[] = {
+ { 0, 0x40 },
+ { 1, 0x00 },
+ { 2, 0x00 },
+ { 3, 0x00 },
+ { 4, 0x49 },
+};
+
+static const unsigned int max9877_pgain_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 1, TLV_DB_SCALE_ITEM(0, 900, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0),
+};
+
+static const unsigned int max9877_output_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
+ 16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
+ 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0),
+};
+
+static const char *max9877_out_mode[] = {
+ "INA -> SPK",
+ "INA -> HP",
+ "INA -> SPK and HP",
+ "INB -> SPK",
+ "INB -> HP",
+ "INB -> SPK and HP",
+ "INA + INB -> SPK",
+ "INA + INB -> HP",
+ "INA + INB -> SPK and HP",
+};
+
+static const char *max9877_osc_mode[] = {
+ "1176KHz",
+ "1100KHz",
+ "700KHz",
+};
+
+static const struct soc_enum max9877_enum[] = {
+ SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, 0, ARRAY_SIZE(max9877_out_mode),
+ max9877_out_mode),
+ SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, MAX9877_OSC_OFFSET,
+ ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode),
+};
+
+static const struct snd_kcontrol_new max9877_controls[] = {
+ SOC_SINGLE_TLV("MAX9877 PGAINA Playback Volume",
+ MAX9877_INPUT_MODE, 0, 2, 0, max9877_pgain_tlv),
+ SOC_SINGLE_TLV("MAX9877 PGAINB Playback Volume",
+ MAX9877_INPUT_MODE, 2, 2, 0, max9877_pgain_tlv),
+ SOC_SINGLE_TLV("MAX9877 Amp Speaker Playback Volume",
+ MAX9877_SPK_VOLUME, 0, 31, 0, max9877_output_tlv),
+ SOC_DOUBLE_R_TLV("MAX9877 Amp HP Playback Volume",
+ MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0,
+ max9877_output_tlv),
+ SOC_SINGLE("MAX9877 INB Stereo Switch",
+ MAX9877_INPUT_MODE, 4, 1, 1),
+ SOC_SINGLE("MAX9877 INA Stereo Switch",
+ MAX9877_INPUT_MODE, 5, 1, 1),
+ SOC_SINGLE("MAX9877 Zero-crossing detection Switch",
+ MAX9877_INPUT_MODE, 6, 1, 0),
+ SOC_SINGLE("MAX9877 Bypass Mode Switch",
+ MAX9877_OUTPUT_MODE, 6, 1, 0),
+ SOC_ENUM("MAX9877 Output Mode", max9877_enum[0]),
+ SOC_ENUM("MAX9877 Oscillator Mode", max9877_enum[1]),
+};
+
+static const struct snd_soc_dapm_widget max9877_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("INA1"),
+SND_SOC_DAPM_INPUT("INA2"),
+SND_SOC_DAPM_INPUT("INB1"),
+SND_SOC_DAPM_INPUT("INB2"),
+SND_SOC_DAPM_INPUT("RXIN+"),
+SND_SOC_DAPM_INPUT("RXIN-"),
+
+SND_SOC_DAPM_PGA("SHDN", MAX9877_OUTPUT_MODE, 7, 1, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("OUT+"),
+SND_SOC_DAPM_OUTPUT("OUT-"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+static const struct snd_soc_dapm_route max9877_dapm_routes[] = {
+ { "SHDN", NULL, "INA1" },
+ { "SHDN", NULL, "INA2" },
+ { "SHDN", NULL, "INB1" },
+ { "SHDN", NULL, "INB2" },
+
+ { "OUT+", NULL, "RXIN+" },
+ { "OUT+", NULL, "SHDN" },
+
+ { "OUT-", NULL, "SHDN" },
+ { "OUT-", NULL, "RXIN-" },
+
+ { "HPL", NULL, "SHDN" },
+ { "HPR", NULL, "SHDN" },
+};
+
+static const struct snd_soc_codec_driver max9877_codec = {
+ .controls = max9877_controls,
+ .num_controls = ARRAY_SIZE(max9877_controls),
+
+ .dapm_widgets = max9877_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9877_dapm_widgets),
+ .dapm_routes = max9877_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9877_dapm_routes),
+};
+
+static const struct regmap_config max9877_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = max9877_regs,
+ .num_reg_defaults = ARRAY_SIZE(max9877_regs),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max9877_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int i;
+
+ regmap = devm_regmap_init_i2c(client, &max9877_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* Ensure the device is in reset state */
+ for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
+ regmap_write(regmap, max9877_regs[i].reg, max9877_regs[i].def);
+
+ return 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;
+}
+
+static const struct i2c_device_id max9877_i2c_id[] = {
+ { "max9877", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max9877_i2c_id);
+
+static struct i2c_driver max9877_i2c_driver = {
+ .driver = {
+ .name = "max9877",
+ .owner = THIS_MODULE,
+ },
+ .probe = max9877_i2c_probe,
+ .remove = max9877_i2c_remove,
+ .id_table = max9877_i2c_id,
+};
+
+module_i2c_driver(max9877_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC MAX9877 amp driver");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/max9877.h b/kernel/sound/soc/codecs/max9877.h
new file mode 100644
index 000000000..6da72290a
--- /dev/null
+++ b/kernel/sound/soc/codecs/max9877.h
@@ -0,0 +1,37 @@
+/*
+ * 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
+#define _MAX9877_H
+
+#define MAX9877_INPUT_MODE 0x00
+#define MAX9877_SPK_VOLUME 0x01
+#define MAX9877_HPL_VOLUME 0x02
+#define MAX9877_HPR_VOLUME 0x03
+#define MAX9877_OUTPUT_MODE 0x04
+
+/* MAX9877_INPUT_MODE */
+#define MAX9877_INB (1 << 4)
+#define MAX9877_INA (1 << 5)
+#define MAX9877_ZCD (1 << 6)
+
+/* MAX9877_OUTPUT_MODE */
+#define MAX9877_OUTMODE_MASK (15 << 0)
+#define MAX9877_OSC_MASK (3 << 4)
+#define MAX9877_OSC_OFFSET 4
+#define MAX9877_BYPASS (1 << 6)
+#define MAX9877_SHDN (1 << 7)
+
+extern int max9877_add_controls(struct snd_soc_codec *codec);
+
+#endif
diff --git a/kernel/sound/soc/codecs/max98925.c b/kernel/sound/soc/codecs/max98925.c
new file mode 100644
index 000000000..9b5a17de4
--- /dev/null
+++ b/kernel/sound/soc/codecs/max98925.c
@@ -0,0 +1,655 @@
+/*
+ * max98925.c -- ALSA SoC Stereo MAX98925 driver
+ * Copyright 2013-15 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>
+#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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+
+ 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_codec *codec,
+ 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;
+ }
+ }
+ dev_dbg(codec->dev, "%s: sample rate is %d, returning %d\n",
+ __func__, rate_table[i].rate, *value);
+ 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_codec *codec = codec_dai->codec;
+ struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+ unsigned int invert = 0;
+
+ dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* set DAI to slave mode */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_MAS_MASK, 0);
+ max98925_set_sense_data(max98925);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /*
+ * set left channel DAI to master mode,
+ * right channel always slave
+ */
+ regmap_update_bits(max98925->regmap,
+ MAX98925_DAI_CLK_MODE2,
+ M98925_DAI_MAS_MASK, M98925_DAI_MAS_MASK);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->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(codec->dev, "DAI invert mode unsupported");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98925->regmap, MAX98925_FORMAT,
+ M98925_DAI_BCI_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_codec *codec = max98925->codec;
+ 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->codec->dev, "unsupported sysclk %d\n",
+ max98925->sysclk);
+ return -EINVAL;
+ }
+
+ if (max98925_rate_value(codec, 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_codec *codec = dai->codec;
+ struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+
+ switch (snd_pcm_format_width(params_format(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(codec->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_codec *codec = dai->codec;
+ struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+
+ 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_codec *codec)
+{
+ struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec);
+
+ max98925->codec = codec;
+ codec->control_data = max98925->regmap;
+ 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_codec_driver soc_codec_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),
+};
+
+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,
+ const struct i2c_device_id *id)
+{
+ 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);
+ goto err_out;
+ }
+
+ 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) ||
+ ((reg != MAX98925_VERSION) &&
+ (reg != MAX98925_VERSION1))) {
+ dev_err(&i2c->dev,
+ "device initialization error (%d 0x%02X)\n",
+ ret, reg);
+ goto err_out;
+ }
+ dev_info(&i2c->dev, "device version 0x%02X\n", reg);
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98925,
+ max98925_dai, ARRAY_SIZE(max98925_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev,
+ "Failed to register codec: %d\n", ret);
+err_out:
+ return ret;
+}
+
+static int max98925_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id max98925_i2c_id[] = {
+ { "max98925", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98925_i2c_id);
+
+static const struct of_device_id max98925_of_match[] = {
+ { .compatible = "maxim,max98925", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98925_of_match);
+
+static struct i2c_driver max98925_i2c_driver = {
+ .driver = {
+ .name = "max98925",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(max98925_of_match),
+ .pm = NULL,
+ },
+ .probe = max98925_i2c_probe,
+ .remove = max98925_i2c_remove,
+ .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/kernel/sound/soc/codecs/max98925.h b/kernel/sound/soc/codecs/max98925.h
new file mode 100644
index 000000000..3783248f2
--- /dev/null
+++ b/kernel/sound/soc/codecs/max98925.h
@@ -0,0 +1,832 @@
+/*
+ * max98925.h -- MAX98925 ALSA SoC Audio driver
+ *
+ * Copyright 2013-2015 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 _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_codec *codec;
+ 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/kernel/sound/soc/codecs/mc13783.c b/kernel/sound/soc/codecs/mc13783.c
new file mode 100644
index 000000000..3d44fc50e
--- /dev/null
+++ b/kernel/sound/soc/codecs/mc13783.c
@@ -0,0 +1,813 @@
+/*
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/mfd/mc13xxx.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/soc-dapm.h>
+#include <linux/regmap.h>
+
+#include "mc13783.h"
+
+#define AUDIO_RX0_ALSPEN (1 << 5)
+#define AUDIO_RX0_ALSPSEL (1 << 7)
+#define AUDIO_RX0_ADDCDC (1 << 21)
+#define AUDIO_RX0_ADDSTDC (1 << 22)
+#define AUDIO_RX0_ADDRXIN (1 << 23)
+
+#define AUDIO_RX1_PGARXEN (1 << 0);
+#define AUDIO_RX1_PGASTEN (1 << 5)
+#define AUDIO_RX1_ARXINEN (1 << 10)
+
+#define AUDIO_TX_AMC1REN (1 << 5)
+#define AUDIO_TX_AMC1LEN (1 << 7)
+#define AUDIO_TX_AMC2EN (1 << 9)
+#define AUDIO_TX_ATXINEN (1 << 11)
+#define AUDIO_TX_RXINREC (1 << 13)
+
+#define SSI_NETWORK_CDCTXRXSLOT(x) (((x) & 0x3) << 2)
+#define SSI_NETWORK_CDCTXSECSLOT(x) (((x) & 0x3) << 4)
+#define SSI_NETWORK_CDCRXSECSLOT(x) (((x) & 0x3) << 6)
+#define SSI_NETWORK_CDCRXSECGAIN(x) (((x) & 0x3) << 8)
+#define SSI_NETWORK_CDCSUMGAIN(x) (1 << 10)
+#define SSI_NETWORK_CDCFSDLY(x) (1 << 11)
+#define SSI_NETWORK_DAC_SLOTS_8 (1 << 12)
+#define SSI_NETWORK_DAC_SLOTS_4 (2 << 12)
+#define SSI_NETWORK_DAC_SLOTS_2 (3 << 12)
+#define SSI_NETWORK_DAC_SLOT_MASK (3 << 12)
+#define SSI_NETWORK_DAC_RXSLOT_0_1 (0 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_2_3 (1 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_4_5 (2 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_6_7 (3 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_MASK (3 << 14)
+#define SSI_NETWORK_STDCRXSECSLOT(x) (((x) & 0x3) << 16)
+#define SSI_NETWORK_STDCRXSECGAIN(x) (((x) & 0x3) << 18)
+#define SSI_NETWORK_STDCSUMGAIN (1 << 20)
+
+/*
+ * MC13783_AUDIO_CODEC and MC13783_AUDIO_DAC mostly share the same
+ * register layout
+ */
+#define AUDIO_SSI_SEL (1 << 0)
+#define AUDIO_CLK_SEL (1 << 1)
+#define AUDIO_CSM (1 << 2)
+#define AUDIO_BCL_INV (1 << 3)
+#define AUDIO_CFS_INV (1 << 4)
+#define AUDIO_CFS(x) (((x) & 0x3) << 5)
+#define AUDIO_CLK(x) (((x) & 0x7) << 7)
+#define AUDIO_C_EN (1 << 11)
+#define AUDIO_C_CLK_EN (1 << 12)
+#define AUDIO_C_RESET (1 << 15)
+
+#define AUDIO_CODEC_CDCFS8K16K (1 << 10)
+#define AUDIO_DAC_CFS_DLY_B (1 << 10)
+
+struct mc13783_priv {
+ struct mc13xxx *mc13xxx;
+ struct regmap *regmap;
+
+ enum mc13783_ssi_port adc_ssi_port;
+ enum mc13783_ssi_port dac_ssi_port;
+};
+
+/* Mapping between sample rates and register value */
+static unsigned int mc13783_rates[] = {
+ 8000, 11025, 12000, 16000,
+ 22050, 24000, 32000, 44100,
+ 48000, 64000, 96000
+};
+
+static int mc13783_pcm_hw_params_dac(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ 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,
+ 0xf << 17, i << 17);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int mc13783_pcm_hw_params_codec(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int rate = params_rate(params);
+ unsigned int val;
+
+ switch (rate) {
+ case 8000:
+ val = 0;
+ break;
+ case 16000:
+ val = AUDIO_CODEC_CDCFS8K16K;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K,
+ val);
+
+ return 0;
+}
+
+static int mc13783_pcm_hw_params_sync(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return mc13783_pcm_hw_params_dac(substream, params, dai);
+ else
+ return mc13783_pcm_hw_params_codec(substream, params, dai);
+}
+
+static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt,
+ unsigned int reg)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int val = 0;
+ unsigned int mask = AUDIO_CFS(3) | AUDIO_BCL_INV | AUDIO_CFS_INV |
+ AUDIO_CSM | AUDIO_C_CLK_EN | AUDIO_C_RESET;
+
+
+ /* DAI mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val |= AUDIO_CFS(2);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ val |= AUDIO_CFS(1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* DAI clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ val |= AUDIO_BCL_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val |= AUDIO_BCL_INV | AUDIO_CFS_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= AUDIO_CFS_INV;
+ break;
+ }
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ val |= AUDIO_C_CLK_EN;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ val |= AUDIO_CSM;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+ return -EINVAL;
+ }
+
+ val |= AUDIO_C_RESET;
+
+ snd_soc_update_bits(codec, reg, mask, val);
+
+ return 0;
+}
+
+static int mc13783_set_fmt_async(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ if (dai->id == MC13783_ID_STEREO_DAC)
+ return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC);
+ else
+ return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC);
+}
+
+static int mc13783_set_fmt_sync(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ int ret;
+
+ ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC);
+ if (ret)
+ return ret;
+
+ /*
+ * In synchronous mode force the voice codec into slave mode
+ * so that the clock / framesync from the stereo DAC is used
+ */
+ fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+ fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC);
+
+ return ret;
+}
+
+static int mc13783_sysclk[] = {
+ 13000000,
+ 15360000,
+ 16800000,
+ -1,
+ 26000000,
+ -1, /* 12000000, invalid for voice codec */
+ -1, /* 3686400, invalid for voice codec */
+ 33600000,
+};
+
+static int mc13783_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir,
+ unsigned int reg)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int clk;
+ unsigned int val = 0;
+ unsigned int mask = AUDIO_CLK(0x7) | AUDIO_CLK_SEL;
+
+ for (clk = 0; clk < ARRAY_SIZE(mc13783_sysclk); clk++) {
+ if (mc13783_sysclk[clk] < 0)
+ continue;
+ if (mc13783_sysclk[clk] == freq)
+ break;
+ }
+
+ if (clk == ARRAY_SIZE(mc13783_sysclk))
+ return -EINVAL;
+
+ if (clk_id == MC13783_CLK_CLIB)
+ val |= AUDIO_CLK_SEL;
+
+ val |= AUDIO_CLK(clk);
+
+ snd_soc_update_bits(codec, reg, mask, val);
+
+ return 0;
+}
+
+static int mc13783_set_sysclk_dac(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC);
+}
+
+static int mc13783_set_sysclk_codec(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC);
+}
+
+static int mc13783_set_sysclk_sync(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ int ret;
+
+ ret = mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC);
+ if (ret)
+ return ret;
+
+ return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC);
+}
+
+static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int val = 0;
+ unsigned int mask = SSI_NETWORK_DAC_SLOT_MASK |
+ SSI_NETWORK_DAC_RXSLOT_MASK;
+
+ switch (slots) {
+ case 2:
+ val |= SSI_NETWORK_DAC_SLOTS_2;
+ break;
+ case 4:
+ val |= SSI_NETWORK_DAC_SLOTS_4;
+ break;
+ case 8:
+ val |= SSI_NETWORK_DAC_SLOTS_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (rx_mask) {
+ case 0x03:
+ val |= SSI_NETWORK_DAC_RXSLOT_0_1;
+ break;
+ case 0x0c:
+ val |= SSI_NETWORK_DAC_RXSLOT_2_3;
+ break;
+ case 0x30:
+ val |= SSI_NETWORK_DAC_RXSLOT_4_5;
+ break;
+ case 0xc0:
+ val |= SSI_NETWORK_DAC_RXSLOT_6_7;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val);
+
+ return 0;
+}
+
+static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int val = 0;
+ unsigned int mask = 0x3f;
+
+ if (slots != 4)
+ return -EINVAL;
+
+ if (tx_mask != 0x3)
+ return -EINVAL;
+
+ val |= (0x00 << 2); /* primary timeslot RX/TX(?) is 0 */
+ val |= (0x01 << 4); /* secondary timeslot TX is 1 */
+
+ snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val);
+
+ return 0;
+}
+
+static int mc13783_set_tdm_slot_sync(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ int ret;
+
+ ret = mc13783_set_tdm_slot_dac(dai, tx_mask, rx_mask, slots,
+ slot_width);
+ if (ret)
+ return ret;
+
+ ret = mc13783_set_tdm_slot_codec(dai, tx_mask, rx_mask, slots,
+ slot_width);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new mc1l_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 7, 1, 0);
+
+static const struct snd_kcontrol_new mc1r_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 5, 1, 0);
+
+static const struct snd_kcontrol_new mc2_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 9, 1, 0);
+
+static const struct snd_kcontrol_new atx_amp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 11, 1, 0);
+
+
+/* Virtual mux. The chip does the input selection automatically
+ * as soon as we enable one input. */
+static const char * const adcl_enum_text[] = {
+ "MC1L", "RXINL",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(adcl_enum, adcl_enum_text);
+
+static const struct snd_kcontrol_new left_input_mux =
+ SOC_DAPM_ENUM("Route", adcl_enum);
+
+static const char * const adcr_enum_text[] = {
+ "MC1R", "MC2", "RXINR", "TXIN",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(adcr_enum, adcr_enum_text);
+
+static const struct snd_kcontrol_new right_input_mux =
+ SOC_DAPM_ENUM("Route", adcr_enum);
+
+static const struct snd_kcontrol_new samp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 3, 1, 0);
+
+static const char * const speaker_amp_source_text[] = {
+ "CODEC", "Right"
+};
+static SOC_ENUM_SINGLE_DECL(speaker_amp_source, MC13783_AUDIO_RX0, 4,
+ speaker_amp_source_text);
+static const struct snd_kcontrol_new speaker_amp_source_mux =
+ SOC_DAPM_ENUM("Speaker Amp Source MUX", speaker_amp_source);
+
+static const char * const headset_amp_source_text[] = {
+ "CODEC", "Mixer"
+};
+
+static SOC_ENUM_SINGLE_DECL(headset_amp_source, MC13783_AUDIO_RX0, 11,
+ headset_amp_source_text);
+static const struct snd_kcontrol_new headset_amp_source_mux =
+ SOC_DAPM_ENUM("Headset Amp Source MUX", headset_amp_source);
+
+static const struct snd_kcontrol_new cdcout_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 18, 1, 0);
+
+static const struct snd_kcontrol_new adc_bypass_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_CODEC, 16, 1, 0);
+
+static const struct snd_kcontrol_new lamp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 5, 1, 0);
+
+static const struct snd_kcontrol_new hlamp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 10, 1, 0);
+
+static const struct snd_kcontrol_new hramp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 9, 1, 0);
+
+static const struct snd_kcontrol_new llamp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 16, 1, 0);
+
+static const struct snd_kcontrol_new lramp_ctl =
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 15, 1, 0);
+
+static const struct snd_soc_dapm_widget mc13783_dapm_widgets[] = {
+/* Input */
+ SND_SOC_DAPM_INPUT("MC1LIN"),
+ SND_SOC_DAPM_INPUT("MC1RIN"),
+ SND_SOC_DAPM_INPUT("MC2IN"),
+ SND_SOC_DAPM_INPUT("RXINR"),
+ SND_SOC_DAPM_INPUT("RXINL"),
+ SND_SOC_DAPM_INPUT("TXIN"),
+
+ SND_SOC_DAPM_SUPPLY("MC1 Bias", MC13783_AUDIO_TX, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MC2 Bias", MC13783_AUDIO_TX, 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("MC1L Amp", MC13783_AUDIO_TX, 7, 0, &mc1l_amp_ctl),
+ SND_SOC_DAPM_SWITCH("MC1R Amp", MC13783_AUDIO_TX, 5, 0, &mc1r_amp_ctl),
+ SND_SOC_DAPM_SWITCH("MC2 Amp", MC13783_AUDIO_TX, 9, 0, &mc2_amp_ctl),
+ SND_SOC_DAPM_SWITCH("TXIN Amp", MC13783_AUDIO_TX, 11, 0, &atx_amp_ctl),
+
+ SND_SOC_DAPM_MUX("PGA Left Input Mux", SND_SOC_NOPM, 0, 0,
+ &left_input_mux),
+ SND_SOC_DAPM_MUX("PGA Right Input Mux", SND_SOC_NOPM, 0, 0,
+ &right_input_mux),
+
+ SND_SOC_DAPM_MUX("Speaker Amp Source MUX", SND_SOC_NOPM, 0, 0,
+ &speaker_amp_source_mux),
+
+ SND_SOC_DAPM_MUX("Headset Amp Source MUX", SND_SOC_NOPM, 0, 0,
+ &headset_amp_source_mux),
+
+ SND_SOC_DAPM_PGA("PGA Left Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA Right Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("ADC", "Capture", MC13783_AUDIO_CODEC, 11, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_Reset", MC13783_AUDIO_CODEC, 15, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Voice CODEC PGA", MC13783_AUDIO_RX1, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("Voice CODEC Bypass", MC13783_AUDIO_CODEC, 16, 0,
+ &adc_bypass_ctl),
+
+/* Output */
+ SND_SOC_DAPM_SUPPLY("DAC_E", MC13783_AUDIO_DAC, 11, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_Reset", MC13783_AUDIO_DAC, 15, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("RXOUTL"),
+ SND_SOC_DAPM_OUTPUT("RXOUTR"),
+ SND_SOC_DAPM_OUTPUT("HSL"),
+ SND_SOC_DAPM_OUTPUT("HSR"),
+ SND_SOC_DAPM_OUTPUT("LSPL"),
+ SND_SOC_DAPM_OUTPUT("LSP"),
+ SND_SOC_DAPM_OUTPUT("SP"),
+ SND_SOC_DAPM_OUTPUT("CDCOUT"),
+
+ SND_SOC_DAPM_SWITCH("CDCOUT Switch", MC13783_AUDIO_RX0, 18, 0,
+ &cdcout_ctl),
+ SND_SOC_DAPM_SWITCH("Speaker Amp Switch", MC13783_AUDIO_RX0, 3, 0,
+ &samp_ctl),
+ SND_SOC_DAPM_SWITCH("Loudspeaker Amp", SND_SOC_NOPM, 0, 0, &lamp_ctl),
+ SND_SOC_DAPM_SWITCH("Headset Amp Left", MC13783_AUDIO_RX0, 10, 0,
+ &hlamp_ctl),
+ SND_SOC_DAPM_SWITCH("Headset Amp Right", MC13783_AUDIO_RX0, 9, 0,
+ &hramp_ctl),
+ SND_SOC_DAPM_SWITCH("Line out Amp Left", MC13783_AUDIO_RX0, 16, 0,
+ &llamp_ctl),
+ SND_SOC_DAPM_SWITCH("Line out Amp Right", MC13783_AUDIO_RX0, 15, 0,
+ &lramp_ctl),
+ SND_SOC_DAPM_DAC("DAC", "Playback", MC13783_AUDIO_RX0, 22, 0),
+ SND_SOC_DAPM_PGA("DAC PGA", MC13783_AUDIO_RX1, 5, 0, NULL, 0),
+};
+
+static struct snd_soc_dapm_route mc13783_routes[] = {
+/* Input */
+ { "MC1L Amp", NULL, "MC1LIN"},
+ { "MC1R Amp", NULL, "MC1RIN" },
+ { "MC2 Amp", NULL, "MC2IN" },
+ { "TXIN Amp", NULL, "TXIN"},
+
+ { "PGA Left Input Mux", "MC1L", "MC1L Amp" },
+ { "PGA Left Input Mux", "RXINL", "RXINL"},
+ { "PGA Right Input Mux", "MC1R", "MC1R Amp" },
+ { "PGA Right Input Mux", "MC2", "MC2 Amp"},
+ { "PGA Right Input Mux", "TXIN", "TXIN Amp"},
+ { "PGA Right Input Mux", "RXINR", "RXINR"},
+
+ { "PGA Left Input", NULL, "PGA Left Input Mux"},
+ { "PGA Right Input", NULL, "PGA Right Input Mux"},
+
+ { "ADC", NULL, "PGA Left Input"},
+ { "ADC", NULL, "PGA Right Input"},
+ { "ADC", NULL, "ADC_Reset"},
+
+ { "Voice CODEC PGA", "Voice CODEC Bypass", "ADC" },
+
+ { "Speaker Amp Source MUX", "CODEC", "Voice CODEC PGA"},
+ { "Speaker Amp Source MUX", "Right", "DAC PGA"},
+
+ { "Headset Amp Source MUX", "CODEC", "Voice CODEC PGA"},
+ { "Headset Amp Source MUX", "Mixer", "DAC PGA"},
+
+/* Output */
+ { "HSL", NULL, "Headset Amp Left" },
+ { "HSR", NULL, "Headset Amp Right"},
+ { "RXOUTL", NULL, "Line out Amp Left"},
+ { "RXOUTR", NULL, "Line out Amp Right"},
+ { "SP", "Speaker Amp Switch", "Speaker Amp Source MUX"},
+ { "LSP", "Loudspeaker Amp", "Speaker Amp Source MUX"},
+ { "HSL", "Headset Amp Left", "Headset Amp Source MUX"},
+ { "HSR", "Headset Amp Right", "Headset Amp Source MUX"},
+ { "Line out Amp Left", NULL, "DAC PGA"},
+ { "Line out Amp Right", NULL, "DAC PGA"},
+ { "DAC PGA", NULL, "DAC"},
+ { "DAC", NULL, "DAC_E"},
+ { "CDCOUT", "CDCOUT Switch", "Voice CODEC PGA"},
+};
+
+static const char * const mc13783_3d_mixer[] = {"Stereo", "Phase Mix",
+ "Mono", "Mono Mix"};
+
+static SOC_ENUM_SINGLE_DECL(mc13783_enum_3d_mixer,
+ MC13783_AUDIO_RX1, 16,
+ mc13783_3d_mixer);
+
+static struct snd_kcontrol_new mc13783_control_list[] = {
+ SOC_SINGLE("Loudspeaker enable", MC13783_AUDIO_RX0, 5, 1, 0),
+ SOC_SINGLE("PCM Playback Volume", MC13783_AUDIO_RX1, 6, 15, 0),
+ SOC_SINGLE("PCM Playback Switch", MC13783_AUDIO_RX1, 5, 1, 0),
+ SOC_DOUBLE("PCM Capture Volume", MC13783_AUDIO_TX, 19, 14, 31, 0),
+ SOC_ENUM("3D Control", mc13783_enum_3d_mixer),
+
+ SOC_SINGLE("CDCOUT Switch", MC13783_AUDIO_RX0, 18, 1, 0),
+ SOC_SINGLE("Earpiece Amp Switch", MC13783_AUDIO_RX0, 3, 1, 0),
+ SOC_DOUBLE("Headset Amp Switch", MC13783_AUDIO_RX0, 10, 9, 1, 0),
+ SOC_DOUBLE("Line out Amp Switch", MC13783_AUDIO_RX0, 16, 15, 1, 0),
+
+ SOC_SINGLE("PCM Capture Mixin Switch", MC13783_AUDIO_RX0, 22, 1, 0),
+ SOC_SINGLE("Line in Capture Mixin Switch", MC13783_AUDIO_RX0, 23, 1, 0),
+
+ SOC_SINGLE("CODEC Capture Volume", MC13783_AUDIO_RX1, 1, 15, 0),
+ SOC_SINGLE("CODEC Capture Mixin Switch", MC13783_AUDIO_RX0, 21, 1, 0),
+
+ SOC_SINGLE("Line in Capture Volume", MC13783_AUDIO_RX1, 12, 15, 0),
+ SOC_SINGLE("Line in Capture Switch", MC13783_AUDIO_RX1, 10, 1, 0),
+
+ SOC_SINGLE("MC1 Capture Bias Switch", MC13783_AUDIO_TX, 0, 1, 0),
+ SOC_SINGLE("MC2 Capture Bias Switch", MC13783_AUDIO_TX, 1, 1, 0),
+};
+
+static int mc13783_probe(struct snd_soc_codec *codec)
+{
+ struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ /* these are the reset values */
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX0, 0x25893);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX1, 0x00d35A);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_TX, 0x420000);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_SSI_NETWORK, 0x013060);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_CODEC, 0x180027);
+ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_DAC, 0x0e0004);
+
+ if (priv->adc_ssi_port == MC13783_SSI1_PORT)
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC,
+ AUDIO_SSI_SEL, 0);
+ else
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC,
+ AUDIO_SSI_SEL, AUDIO_SSI_SEL);
+
+ if (priv->dac_ssi_port == MC13783_SSI1_PORT)
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC,
+ AUDIO_SSI_SEL, 0);
+ else
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC,
+ AUDIO_SSI_SEL, AUDIO_SSI_SEL);
+
+ return 0;
+}
+
+static int mc13783_remove(struct snd_soc_codec *codec)
+{
+ struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ /* 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)
+
+#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 = {
+ .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 = {
+ .hw_params = mc13783_pcm_hw_params_codec,
+ .set_fmt = mc13783_set_fmt_async,
+ .set_sysclk = mc13783_set_sysclk_codec,
+ .set_tdm_slot = mc13783_set_tdm_slot_codec,
+};
+
+/*
+ * The mc13783 has two SSI ports, both of them can be routed either
+ * to the voice codec or the stereo DAC. When two different SSI ports
+ * are used for the voice codec and the stereo DAC we can do different
+ * formats and sysclock settings for playback and capture
+ * (mc13783-hifi-playback and mc13783-hifi-capture). Using the same port
+ * forces us to use symmetric rates (mc13783-hifi).
+ */
+static struct snd_soc_dai_driver mc13783_dai_async[] = {
+ {
+ .name = "mc13783-hifi-playback",
+ .id = MC13783_ID_STEREO_DAC,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = MC13783_FORMATS,
+ },
+ .ops = &mc13783_ops_dac,
+ }, {
+ .name = "mc13783-hifi-capture",
+ .id = MC13783_ID_STEREO_CODEC,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = MC13783_RATES_RECORD,
+ .formats = MC13783_FORMATS,
+ },
+ .ops = &mc13783_ops_codec,
+ },
+};
+
+static struct snd_soc_dai_ops mc13783_ops_sync = {
+ .hw_params = mc13783_pcm_hw_params_sync,
+ .set_fmt = mc13783_set_fmt_sync,
+ .set_sysclk = mc13783_set_sysclk_sync,
+ .set_tdm_slot = mc13783_set_tdm_slot_sync,
+};
+
+static struct snd_soc_dai_driver mc13783_dai_sync[] = {
+ {
+ .name = "mc13783-hifi",
+ .id = MC13783_ID_SYNC,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = MC13783_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = MC13783_RATES_RECORD,
+ .formats = MC13783_FORMATS,
+ },
+ .ops = &mc13783_ops_sync,
+ .symmetric_rates = 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 int __init mc13783_codec_probe(struct platform_device *pdev)
+{
+ struct mc13783_priv *priv;
+ struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *np;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (pdata) {
+ priv->adc_ssi_port = pdata->adc_ssi_port;
+ priv->dac_ssi_port = pdata->dac_ssi_port;
+ } else {
+ np = of_get_child_by_name(pdev->dev.parent->of_node, "codec");
+ if (!np)
+ return -ENOSYS;
+
+ ret = of_property_read_u32(np, "adc-port", &priv->adc_ssi_port);
+ if (ret) {
+ of_node_put(np);
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port);
+ if (ret) {
+ of_node_put(np);
+ return ret;
+ }
+
+ of_node_put(np);
+ }
+
+ dev_set_drvdata(&pdev->dev, priv);
+ priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
+
+ if (priv->adc_ssi_port == priv->dac_ssi_port)
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
+ mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync));
+ else
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_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);
+
+MODULE_DESCRIPTION("ASoC MC13783 driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/mc13783.h b/kernel/sound/soc/codecs/mc13783.h
new file mode 100644
index 000000000..3a6d1993a
--- /dev/null
+++ b/kernel/sound/soc/codecs/mc13783.h
@@ -0,0 +1,28 @@
+/*
+ * 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
+#define MC13783_MIXER_H
+
+#define MC13783_CLK_CLIA 1
+#define MC13783_CLK_CLIB 2
+
+#define MC13783_ID_STEREO_DAC 1
+#define MC13783_ID_STEREO_CODEC 2
+#define MC13783_ID_SYNC 3
+
+#endif /* MC13783_MIXER_H */
diff --git a/kernel/sound/soc/codecs/ml26124.c b/kernel/sound/soc/codecs/ml26124.c
new file mode 100644
index 000000000..711f55039
--- /dev/null
+++ b/kernel/sound/soc/codecs/ml26124.c
@@ -0,0 +1,648 @@
+/*
+ * 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>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/platform_device.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 "ml26124.h"
+
+#define DVOL_CTL_DVMUTE_ON BIT(4) /* Digital volume MUTE On */
+#define DVOL_CTL_DVMUTE_OFF 0 /* Digital volume MUTE Off */
+#define ML26124_SAI_NO_DELAY BIT(1)
+#define ML26124_SAI_FRAME_SYNC (BIT(5) | BIT(0)) /* For mono (Telecodec) */
+#define ML26134_CACHESIZE 212
+#define ML26124_VMID BIT(1)
+#define ML26124_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+#define ML26124_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define ML26124_NUM_REGISTER ML26134_CACHESIZE
+
+struct ml26124_priv {
+ u32 mclk;
+ u32 rate;
+ struct regmap *regmap;
+ int clk_in;
+ struct snd_pcm_substream *substream;
+};
+
+struct clk_coeff {
+ u32 mclk;
+ u32 rate;
+ u8 pllnl;
+ u8 pllnh;
+ u8 pllml;
+ u8 pllmh;
+ u8 plldiv;
+};
+
+/* ML26124 configuration */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7150, 50, 0);
+
+static const DECLARE_TLV_DB_SCALE(alclvl, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mingain, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(maxgain, -675, 600, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0);
+
+static const char * const ml26124_companding[] = {"16bit PCM", "u-law",
+ "A-law"};
+
+static SOC_ENUM_SINGLE_DECL(ml26124_adc_companding_enum,
+ ML26124_SAI_TRANS_CTL, 6, ml26124_companding);
+
+static SOC_ENUM_SINGLE_DECL(ml26124_dac_companding_enum,
+ ML26124_SAI_RCV_CTL, 6, ml26124_companding);
+
+static const struct snd_kcontrol_new ml26124_snd_controls[] = {
+ SOC_SINGLE_TLV("Capture Digital Volume", ML26124_RECORD_DIG_VOL, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("Playback Digital Volume", ML26124_PLBAK_DIG_VOL, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("Digital Boost Volume", ML26124_DIGI_BOOST_VOL, 0,
+ 0x3f, 0, boost_vol),
+ SOC_SINGLE_TLV("EQ Band0 Volume", ML26124_EQ_GAIN_BRAND0, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("EQ Band1 Volume", ML26124_EQ_GAIN_BRAND1, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("EQ Band2 Volume", ML26124_EQ_GAIN_BRAND2, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("EQ Band3 Volume", ML26124_EQ_GAIN_BRAND3, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("EQ Band4 Volume", ML26124_EQ_GAIN_BRAND4, 0,
+ 0xff, 1, digital_tlv),
+ SOC_SINGLE_TLV("ALC Target Level", ML26124_ALC_TARGET_LEV, 0,
+ 0xf, 1, alclvl),
+ SOC_SINGLE_TLV("ALC Min Input Volume", ML26124_ALC_MAXMIN_GAIN, 0,
+ 7, 0, mingain),
+ SOC_SINGLE_TLV("ALC Max Input Volume", ML26124_ALC_MAXMIN_GAIN, 4,
+ 7, 1, maxgain),
+ SOC_SINGLE_TLV("Playback Limiter Min Input Volume",
+ ML26124_PL_MAXMIN_GAIN, 0, 7, 0, mingain),
+ SOC_SINGLE_TLV("Playback Limiter Max Input Volume",
+ ML26124_PL_MAXMIN_GAIN, 4, 7, 1, maxgain),
+ SOC_SINGLE_TLV("Playback Boost Volume", ML26124_PLYBAK_BOST_VOL, 0,
+ 0x3f, 0, boost_vol),
+ SOC_SINGLE("DC High Pass Filter Switch", ML26124_FILTER_EN, 0, 1, 0),
+ SOC_SINGLE("Noise High Pass Filter Switch", ML26124_FILTER_EN, 1, 1, 0),
+ SOC_SINGLE("ZC Switch", ML26124_PW_ZCCMP_PW_MNG, 1,
+ 1, 0),
+ SOC_SINGLE("EQ Band0 Switch", ML26124_FILTER_EN, 2, 1, 0),
+ SOC_SINGLE("EQ Band1 Switch", ML26124_FILTER_EN, 3, 1, 0),
+ SOC_SINGLE("EQ Band2 Switch", ML26124_FILTER_EN, 4, 1, 0),
+ SOC_SINGLE("EQ Band3 Switch", ML26124_FILTER_EN, 5, 1, 0),
+ SOC_SINGLE("EQ Band4 Switch", ML26124_FILTER_EN, 6, 1, 0),
+ SOC_SINGLE("Play Limiter", ML26124_DVOL_CTL, 0, 1, 0),
+ SOC_SINGLE("Capture Limiter", ML26124_DVOL_CTL, 1, 1, 0),
+ SOC_SINGLE("Digital Volume Fade Switch", ML26124_DVOL_CTL, 3, 1, 0),
+ SOC_SINGLE("Digital Switch", ML26124_DVOL_CTL, 4, 1, 0),
+ SOC_ENUM("DAC Companding", ml26124_dac_companding_enum),
+ SOC_ENUM("ADC Companding", ml26124_adc_companding_enum),
+};
+
+static const struct snd_kcontrol_new ml26124_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DAC Switch", ML26124_SPK_AMP_OUT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Line in loopback Switch", ML26124_SPK_AMP_OUT, 3, 1,
+ 0),
+ SOC_DAPM_SINGLE("PGA Switch", ML26124_SPK_AMP_OUT, 5, 1, 0),
+};
+
+/* Input mux */
+static const char * const ml26124_input_select[] = {"Analog MIC SingleEnded in",
+ "Digital MIC in", "Analog MIC Differential in"};
+
+static SOC_ENUM_SINGLE_DECL(ml26124_insel_enum,
+ ML26124_MIC_IF_CTL, 0, ml26124_input_select);
+
+static const struct snd_kcontrol_new ml26124_input_mux_controls =
+ SOC_DAPM_ENUM("Input Select", ml26124_insel_enum);
+
+static const struct snd_kcontrol_new ml26124_line_control =
+ SOC_DAPM_SINGLE("Switch", ML26124_PW_LOUT_PW_MNG, 1, 1, 0);
+
+static const struct snd_soc_dapm_widget ml26124_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("MCLKEN", ML26124_CLK_EN, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLLEN", ML26124_CLK_EN, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLLOE", ML26124_CLK_EN, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS", ML26124_PW_REF_PW_MNG, 2, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
+ &ml26124_output_mixer_controls[0],
+ ARRAY_SIZE(ml26124_output_mixer_controls)),
+ SND_SOC_DAPM_DAC("DAC", "Playback", ML26124_PW_DAC_PW_MNG, 1, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", ML26124_PW_IN_PW_MNG, 1, 0),
+ SND_SOC_DAPM_PGA("PGA", ML26124_PW_IN_PW_MNG, 3, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &ml26124_input_mux_controls),
+ SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
+ &ml26124_line_control),
+ SND_SOC_DAPM_INPUT("MDIN"),
+ SND_SOC_DAPM_INPUT("MIN"),
+ SND_SOC_DAPM_INPUT("LIN"),
+ SND_SOC_DAPM_OUTPUT("SPOUT"),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+};
+
+static const struct snd_soc_dapm_route ml26124_intercon[] = {
+ /* Supply */
+ {"DAC", NULL, "MCLKEN"},
+ {"ADC", NULL, "MCLKEN"},
+ {"DAC", NULL, "PLLEN"},
+ {"ADC", NULL, "PLLEN"},
+ {"DAC", NULL, "PLLOE"},
+ {"ADC", NULL, "PLLOE"},
+
+ /* output mixer */
+ {"Output Mixer", "DAC Switch", "DAC"},
+ {"Output Mixer", "Line in loopback Switch", "LIN"},
+
+ /* outputs */
+ {"LOUT", NULL, "Output Mixer"},
+ {"SPOUT", NULL, "Output Mixer"},
+ {"Line Out Enable", NULL, "LOUT"},
+
+ /* input */
+ {"ADC", NULL, "Input Mux"},
+ {"Input Mux", "Analog MIC SingleEnded in", "PGA"},
+ {"Input Mux", "Analog MIC Differential in", "PGA"},
+ {"PGA", NULL, "MIN"},
+};
+
+/* PLLOutputFreq(Hz) = InputMclkFreq(Hz) * PLLM / (PLLN * PLLDIV) */
+static const struct clk_coeff coeff_div[] = {
+ {12288000, 16000, 0xc, 0x0, 0x20, 0x0, 0x4},
+ {12288000, 32000, 0xc, 0x0, 0x20, 0x0, 0x4},
+ {12288000, 48000, 0xc, 0x0, 0x30, 0x0, 0x4},
+};
+
+static struct reg_default ml26124_reg[] = {
+ /* CLOCK control Register */
+ {0x00, 0x00 }, /* Sampling Rate */
+ {0x02, 0x00}, /* PLL NL */
+ {0x04, 0x00}, /* PLLNH */
+ {0x06, 0x00}, /* PLLML */
+ {0x08, 0x00}, /* MLLMH */
+ {0x0a, 0x00}, /* PLLDIV */
+ {0x0c, 0x00}, /* Clock Enable */
+ {0x0e, 0x00}, /* CLK Input/Output Control */
+
+ /* System Control Register */
+ {0x10, 0x00}, /* Software RESET */
+ {0x12, 0x00}, /* Record/Playback Run */
+ {0x14, 0x00}, /* Mic Input/Output control */
+
+ /* Power Management Register */
+ {0x20, 0x00}, /* Reference Power Management */
+ {0x22, 0x00}, /* Input Power Management */
+ {0x24, 0x00}, /* DAC Power Management */
+ {0x26, 0x00}, /* SP-AMP Power Management */
+ {0x28, 0x00}, /* LINEOUT Power Management */
+ {0x2a, 0x00}, /* VIDEO Power Management */
+ {0x2e, 0x00}, /* AC-CMP Power Management */
+
+ /* Analog reference Control Register */
+ {0x30, 0x04}, /* MICBIAS Voltage Control */
+
+ /* Input/Output Amplifier Control Register */
+ {0x32, 0x10}, /* MIC Input Volume */
+ {0x38, 0x00}, /* Mic Boost Volume */
+ {0x3a, 0x33}, /* Speaker AMP Volume */
+ {0x48, 0x00}, /* AMP Volume Control Function Enable */
+ {0x4a, 0x00}, /* Amplifier Volume Fader Control */
+
+ /* Analog Path Control Register */
+ {0x54, 0x00}, /* Speaker AMP Output Control */
+ {0x5a, 0x00}, /* Mic IF Control */
+ {0xe8, 0x01}, /* Mic Select Control */
+
+ /* Audio Interface Control Register */
+ {0x60, 0x00}, /* SAI-Trans Control */
+ {0x62, 0x00}, /* SAI-Receive Control */
+ {0x64, 0x00}, /* SAI Mode select */
+
+ /* DSP Control Register */
+ {0x66, 0x01}, /* Filter Func Enable */
+ {0x68, 0x00}, /* Volume Control Func Enable */
+ {0x6A, 0x00}, /* Mixer & Volume Control*/
+ {0x6C, 0xff}, /* Record Digital Volume */
+ {0x70, 0xff}, /* Playback Digital Volume */
+ {0x72, 0x10}, /* Digital Boost Volume */
+ {0x74, 0xe7}, /* EQ gain Band0 */
+ {0x76, 0xe7}, /* EQ gain Band1 */
+ {0x78, 0xe7}, /* EQ gain Band2 */
+ {0x7A, 0xe7}, /* EQ gain Band3 */
+ {0x7C, 0xe7}, /* EQ gain Band4 */
+ {0x7E, 0x00}, /* HPF2 CutOff*/
+ {0x80, 0x00}, /* EQ Band0 Coef0L */
+ {0x82, 0x00}, /* EQ Band0 Coef0H */
+ {0x84, 0x00}, /* EQ Band0 Coef0L */
+ {0x86, 0x00}, /* EQ Band0 Coef0H */
+ {0x88, 0x00}, /* EQ Band1 Coef0L */
+ {0x8A, 0x00}, /* EQ Band1 Coef0H */
+ {0x8C, 0x00}, /* EQ Band1 Coef0L */
+ {0x8E, 0x00}, /* EQ Band1 Coef0H */
+ {0x90, 0x00}, /* EQ Band2 Coef0L */
+ {0x92, 0x00}, /* EQ Band2 Coef0H */
+ {0x94, 0x00}, /* EQ Band2 Coef0L */
+ {0x96, 0x00}, /* EQ Band2 Coef0H */
+ {0x98, 0x00}, /* EQ Band3 Coef0L */
+ {0x9A, 0x00}, /* EQ Band3 Coef0H */
+ {0x9C, 0x00}, /* EQ Band3 Coef0L */
+ {0x9E, 0x00}, /* EQ Band3 Coef0H */
+ {0xA0, 0x00}, /* EQ Band4 Coef0L */
+ {0xA2, 0x00}, /* EQ Band4 Coef0H */
+ {0xA4, 0x00}, /* EQ Band4 Coef0L */
+ {0xA6, 0x00}, /* EQ Band4 Coef0H */
+
+ /* ALC Control Register */
+ {0xb0, 0x00}, /* ALC Mode */
+ {0xb2, 0x02}, /* ALC Attack Time */
+ {0xb4, 0x03}, /* ALC Decay Time */
+ {0xb6, 0x00}, /* ALC Hold Time */
+ {0xb8, 0x0b}, /* ALC Target Level */
+ {0xba, 0x70}, /* ALC Max/Min Gain */
+ {0xbc, 0x00}, /* Noise Gate Threshold */
+ {0xbe, 0x00}, /* ALC ZeroCross TimeOut */
+
+ /* Playback Limiter Control Register */
+ {0xc0, 0x04}, /* PL Attack Time */
+ {0xc2, 0x05}, /* PL Decay Time */
+ {0xc4, 0x0d}, /* PL Target Level */
+ {0xc6, 0x70}, /* PL Max/Min Gain */
+ {0xc8, 0x10}, /* Playback Boost Volume */
+ {0xca, 0x00}, /* PL ZeroCross TimeOut */
+
+ /* Video Amplifier Control Register */
+ {0xd0, 0x01}, /* VIDEO AMP Gain Control */
+ {0xd2, 0x01}, /* VIDEO AMP Setup 1 */
+ {0xd4, 0x01}, /* VIDEO AMP Control2 */
+};
+
+/* Get sampling rate value of sampling rate setting register (0x0) */
+static inline int get_srate(int rate)
+{
+ int srate;
+
+ switch (rate) {
+ case 16000:
+ srate = 3;
+ break;
+ case 32000:
+ srate = 6;
+ break;
+ case 48000:
+ srate = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return srate;
+}
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int ml26124_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int i = get_coeff(priv->mclk, params_rate(hw_params));
+
+ if (i < 0)
+ return i;
+ priv->substream = substream;
+ priv->rate = params_rate(hw_params);
+
+ if (priv->clk_in) {
+ switch (priv->mclk / params_rate(hw_params)) {
+ case 256:
+ snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ BIT(0) | BIT(1), 1);
+ break;
+ case 512:
+ snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ BIT(0) | BIT(1), 2);
+ break;
+ case 1024:
+ snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ BIT(0) | BIT(1), 3);
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported MCLKI\n");
+ break;
+ }
+ } else {
+ snd_soc_update_bits(codec, 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;
+ }
+
+ return 0;
+}
+
+static int ml26124_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (priv->substream->stream) {
+ case SNDRV_PCM_STREAM_CAPTURE:
+ snd_soc_update_bits(codec, 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);
+ break;
+ }
+
+ if (mute)
+ snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+ DVOL_CTL_DVMUTE_ON);
+ else
+ snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+ DVOL_CTL_DVMUTE_OFF);
+
+ return 0;
+}
+
+static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ unsigned char mode;
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ mode = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ mode = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, ML26124_SAI_MODE_SEL, BIT(0), mode);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case ML26124_USE_PLLOUT:
+ priv->clk_in = ML26124_USE_PLLOUT;
+ break;
+ case ML26124_USE_MCLKI:
+ priv->clk_in = ML26124_USE_MCLKI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ priv->mclk = freq;
+
+ return 0;
+}
+
+static int ml26124_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+ ML26124_R26_MASK, ML26124_BLT_PREAMP_ON);
+ msleep(100);
+ snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+ ML26124_R26_MASK,
+ ML26124_MICBEN_ON | ML26124_BLT_ALL_ON);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* VMID ON */
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
+ ML26124_VMID, ML26124_VMID);
+ msleep(500);
+ regcache_sync(priv->regmap);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* VMID OFF */
+ snd_soc_update_bits(codec, 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,
+ .set_fmt = ml26124_set_dai_fmt,
+ .set_sysclk = ml26124_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver ml26124_dai = {
+ .name = "ml26124-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ML26124_RATES,
+ .formats = ML26124_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ML26124_RATES,
+ .formats = ML26124_FORMATS,},
+ .ops = &ml26124_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int ml26124_probe(struct snd_soc_codec *codec)
+{
+ /* Software Reset */
+ snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 1);
+ snd_soc_update_bits(codec, 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 regmap_config ml26124_i2c_regmap = {
+ .val_bits = 8,
+ .reg_bits = 8,
+ .max_register = ML26124_NUM_REGISTER,
+ .reg_defaults = ml26124_reg,
+ .num_reg_defaults = ARRAY_SIZE(ml26124_reg),
+ .cache_type = REGCACHE_RBTREE,
+ .write_flag_mask = 0x01,
+};
+
+static int ml26124_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ml26124_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, priv);
+
+ priv->regmap = devm_regmap_init_i2c(i2c, &ml26124_i2c_regmap);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ dev_err(&i2c->dev, "regmap_init_i2c() failed: %d\n", ret);
+ return ret;
+ }
+
+ return 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;
+}
+
+static const struct i2c_device_id ml26124_i2c_id[] = {
+ { "ml26124", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id);
+
+static struct i2c_driver ml26124_i2c_driver = {
+ .driver = {
+ .name = "ml26124",
+ .owner = THIS_MODULE,
+ },
+ .probe = ml26124_i2c_probe,
+ .remove = ml26124_i2c_remove,
+ .id_table = ml26124_i2c_id,
+};
+
+module_i2c_driver(ml26124_i2c_driver);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya.rohm@gmail.com>");
+MODULE_DESCRIPTION("LAPIS Semiconductor ML26124 ALSA SoC codec driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ml26124.h b/kernel/sound/soc/codecs/ml26124.h
new file mode 100644
index 000000000..5ea0cbb8c
--- /dev/null
+++ b/kernel/sound/soc/codecs/ml26124.h
@@ -0,0 +1,184 @@
+/*
+ * 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
+#define ML26124_H
+
+/* Clock Control Register */
+#define ML26124_SMPLING_RATE 0x00
+#define ML26124_PLLNL 0x02
+#define ML26124_PLLNH 0x04
+#define ML26124_PLLML 0x06
+#define ML26124_PLLMH 0x08
+#define ML26124_PLLDIV 0x0a
+#define ML26124_CLK_EN 0x0c
+#define ML26124_CLK_CTL 0x0e
+
+/* System Control Register */
+#define ML26124_SW_RST 0x10
+#define ML26124_REC_PLYBAK_RUN 0x12
+#define ML26124_MIC_TIM 0x14
+
+/* Power Mnagement Register */
+#define ML26124_PW_REF_PW_MNG 0x20
+#define ML26124_PW_IN_PW_MNG 0x22
+#define ML26124_PW_DAC_PW_MNG 0x24
+#define ML26124_PW_SPAMP_PW_MNG 0x26
+#define ML26124_PW_LOUT_PW_MNG 0x28
+#define ML26124_PW_VOUT_PW_MNG 0x2a
+#define ML26124_PW_ZCCMP_PW_MNG 0x2e
+
+/* Analog Reference Control Register */
+#define ML26124_PW_MICBIAS_VOL 0x30
+
+/* Input/Output Amplifier Control Register */
+#define ML26124_PW_MIC_IN_VOL 0x32
+#define ML26124_PW_MIC_BOST_VOL 0x38
+#define ML26124_PW_SPK_AMP_VOL 0x3a
+#define ML26124_PW_AMP_VOL_FUNC 0x48
+#define ML26124_PW_AMP_VOL_FADE 0x4a
+
+/* Analog Path Control Register */
+#define ML26124_SPK_AMP_OUT 0x54
+#define ML26124_MIC_IF_CTL 0x5a
+#define ML26124_MIC_SELECT 0xe8
+
+/* Audio Interface Control Register */
+#define ML26124_SAI_TRANS_CTL 0x60
+#define ML26124_SAI_RCV_CTL 0x62
+#define ML26124_SAI_MODE_SEL 0x64
+
+/* DSP Control Register */
+#define ML26124_FILTER_EN 0x66
+#define ML26124_DVOL_CTL 0x68
+#define ML26124_MIXER_VOL_CTL 0x6a
+#define ML26124_RECORD_DIG_VOL 0x6c
+#define ML26124_PLBAK_DIG_VOL 0x70
+#define ML26124_DIGI_BOOST_VOL 0x72
+#define ML26124_EQ_GAIN_BRAND0 0x74
+#define ML26124_EQ_GAIN_BRAND1 0x76
+#define ML26124_EQ_GAIN_BRAND2 0x78
+#define ML26124_EQ_GAIN_BRAND3 0x7a
+#define ML26124_EQ_GAIN_BRAND4 0x7c
+#define ML26124_HPF2_CUTOFF 0x7e
+#define ML26124_EQBRAND0_F0L 0x80
+#define ML26124_EQBRAND0_F0H 0x82
+#define ML26124_EQBRAND0_F1L 0x84
+#define ML26124_EQBRAND0_F1H 0x86
+#define ML26124_EQBRAND1_F0L 0x88
+#define ML26124_EQBRAND1_F0H 0x8a
+#define ML26124_EQBRAND1_F1L 0x8c
+#define ML26124_EQBRAND1_F1H 0x8e
+#define ML26124_EQBRAND2_F0L 0x90
+#define ML26124_EQBRAND2_F0H 0x92
+#define ML26124_EQBRAND2_F1L 0x94
+#define ML26124_EQBRAND2_F1H 0x96
+#define ML26124_EQBRAND3_F0L 0x98
+#define ML26124_EQBRAND3_F0H 0x9a
+#define ML26124_EQBRAND3_F1L 0x9c
+#define ML26124_EQBRAND3_F1H 0x9e
+#define ML26124_EQBRAND4_F0L 0xa0
+#define ML26124_EQBRAND4_F0H 0xa2
+#define ML26124_EQBRAND4_F1L 0xa4
+#define ML26124_EQBRAND4_F1H 0xa6
+
+/* ALC Control Register */
+#define ML26124_ALC_MODE 0xb0
+#define ML26124_ALC_ATTACK_TIM 0xb2
+#define ML26124_ALC_DECAY_TIM 0xb4
+#define ML26124_ALC_HOLD_TIM 0xb6
+#define ML26124_ALC_TARGET_LEV 0xb8
+#define ML26124_ALC_MAXMIN_GAIN 0xba
+#define ML26124_NOIS_GATE_THRSH 0xbc
+#define ML26124_ALC_ZERO_TIMOUT 0xbe
+
+/* Playback Limiter Control Register */
+#define ML26124_PL_ATTACKTIME 0xc0
+#define ML26124_PL_DECAYTIME 0xc2
+#define ML26124_PL_TARGETTIME 0xc4
+#define ML26124_PL_MAXMIN_GAIN 0xc6
+#define ML26124_PLYBAK_BOST_VOL 0xc8
+#define ML26124_PL_0CROSS_TIMOUT 0xca
+
+/* Video Amplifer Control Register */
+#define ML26124_VIDEO_AMP_GAIN_CTL 0xd0
+#define ML26124_VIDEO_AMP_SETUP1 0xd2
+#define ML26124_VIDEO_AMP_CTL2 0xd4
+
+/* Clock select for machine driver */
+#define ML26124_USE_PLL 0
+#define ML26124_USE_MCLKI_256FS 1
+#define ML26124_USE_MCLKI_512FS 2
+#define ML26124_USE_MCLKI_1024FS 3
+
+/* Register Mask */
+#define ML26124_R0_MASK 0xf
+#define ML26124_R2_MASK 0xff
+#define ML26124_R4_MASK 0x1
+#define ML26124_R6_MASK 0xf
+#define ML26124_R8_MASK 0x3f
+#define ML26124_Ra_MASK 0x1f
+#define ML26124_Rc_MASK 0x1f
+#define ML26124_Re_MASK 0x7
+#define ML26124_R10_MASK 0x1
+#define ML26124_R12_MASK 0x17
+#define ML26124_R14_MASK 0x3f
+#define ML26124_R20_MASK 0x47
+#define ML26124_R22_MASK 0xa
+#define ML26124_R24_MASK 0x2
+#define ML26124_R26_MASK 0x1f
+#define ML26124_R28_MASK 0x2
+#define ML26124_R2a_MASK 0x2
+#define ML26124_R2e_MASK 0x2
+#define ML26124_R30_MASK 0x7
+#define ML26124_R32_MASK 0x3f
+#define ML26124_R38_MASK 0x38
+#define ML26124_R3a_MASK 0x3f
+#define ML26124_R48_MASK 0x3
+#define ML26124_R4a_MASK 0x7
+#define ML26124_R54_MASK 0x2a
+#define ML26124_R5a_MASK 0x3
+#define ML26124_Re8_MASK 0x3
+#define ML26124_R60_MASK 0xff
+#define ML26124_R62_MASK 0xff
+#define ML26124_R64_MASK 0x1
+#define ML26124_R66_MASK 0xff
+#define ML26124_R68_MASK 0x3b
+#define ML26124_R6a_MASK 0xf3
+#define ML26124_R6c_MASK 0xff
+#define ML26124_R70_MASK 0xff
+
+#define ML26124_MCLKEN BIT(0)
+#define ML26124_PLLEN BIT(1)
+#define ML26124_PLLOE BIT(2)
+#define ML26124_MCLKOE BIT(3)
+
+#define ML26124_BLT_ALL_ON 0x1f
+#define ML26124_BLT_PREAMP_ON 0x13
+
+#define ML26124_MICBEN_ON BIT(2)
+
+enum ml26124_regs {
+ ML26124_MCLK = 0,
+};
+
+enum ml26124_clk_in {
+ ML26124_USE_PLLOUT = 0,
+ ML26124_USE_MCLKI,
+};
+
+#endif
diff --git a/kernel/sound/soc/codecs/pcm1681.c b/kernel/sound/soc/codecs/pcm1681.c
new file mode 100644
index 000000000..477e13d30
--- /dev/null
+++ b/kernel/sound/soc/codecs/pcm1681.c
@@ -0,0 +1,345 @@
+/*
+ * 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>
+#include <sound/tlv.h>
+
+#define PCM1681_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define PCM1681_PCM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+
+#define PCM1681_SOFT_MUTE_ALL 0xff
+#define PCM1681_DEEMPH_RATE_MASK 0x18
+#define PCM1681_DEEMPH_MASK 0x01
+
+#define PCM1681_ATT_CONTROL(X) (X <= 6 ? X : X + 9) /* Attenuation level */
+#define PCM1681_SOFT_MUTE 0x07 /* Soft mute control register */
+#define PCM1681_DAC_CONTROL 0x08 /* DAC operation control */
+#define PCM1681_FMT_CONTROL 0x09 /* Audio interface data format */
+#define PCM1681_DEEMPH_CONTROL 0x0a /* De-emphasis control */
+#define PCM1681_ZERO_DETECT_STATUS 0x0e /* Zero detect status reg */
+
+static const struct reg_default pcm1681_reg_defaults[] = {
+ { 0x01, 0xff },
+ { 0x02, 0xff },
+ { 0x03, 0xff },
+ { 0x04, 0xff },
+ { 0x05, 0xff },
+ { 0x06, 0xff },
+ { 0x07, 0x00 },
+ { 0x08, 0x00 },
+ { 0x09, 0x06 },
+ { 0x0A, 0x00 },
+ { 0x0B, 0xff },
+ { 0x0C, 0x0f },
+ { 0x0D, 0x00 },
+ { 0x10, 0xff },
+ { 0x11, 0xff },
+ { 0x12, 0x00 },
+ { 0x13, 0x00 },
+};
+
+static bool pcm1681_accessible_reg(struct device *dev, unsigned int reg)
+{
+ return !((reg == 0x00) || (reg == 0x0f));
+}
+
+static bool pcm1681_writeable_reg(struct device *dev, unsigned register reg)
+{
+ return pcm1681_accessible_reg(dev, reg) &&
+ (reg != PCM1681_ZERO_DETECT_STATUS);
+}
+
+struct pcm1681_private {
+ struct regmap *regmap;
+ unsigned int format;
+ /* Current deemphasis status */
+ unsigned int deemph;
+ /* Current rate for deemphasis control */
+ unsigned int rate;
+};
+
+static const int pcm1681_deemph[] = { 44100, 48000, 32000 };
+
+static int pcm1681_set_deemph(struct snd_soc_codec *codec)
+{
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ int i = 0, val = -1, enable = 0;
+
+ if (priv->deemph)
+ for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++)
+ if (pcm1681_deemph[i] == priv->rate)
+ val = i;
+
+ if (val != -1) {
+ regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
+ PCM1681_DEEMPH_RATE_MASK, val);
+ enable = 1;
+ } else
+ enable = 0;
+
+ /* enable/disable deemphasis functionality */
+ return regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
+ PCM1681_DEEMPH_MASK, enable);
+}
+
+static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = priv->deemph;
+
+ return 0;
+}
+
+static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->deemph = ucontrol->value.integer.value[0];
+
+ return pcm1681_set_deemph(codec);
+}
+
+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);
+
+ /* 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");
+ return -EINVAL;
+ }
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm1681_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ int val;
+
+ if (mute)
+ val = PCM1681_SOFT_MUTE_ALL;
+ else
+ val = 0;
+
+ return regmap_write(priv->regmap, PCM1681_SOFT_MUTE, val);
+}
+
+static int pcm1681_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm1681_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:
+ val = 0;
+ break;
+ case 16:
+ val = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = 0x04;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = 0x05;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(priv->regmap, PCM1681_FMT_CONTROL, 0x0f, val);
+ if (ret < 0)
+ return ret;
+
+ return pcm1681_set_deemph(codec);
+}
+
+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,
+};
+
+static const struct snd_soc_dapm_widget pcm1681_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("VOUT1"),
+SND_SOC_DAPM_OUTPUT("VOUT2"),
+SND_SOC_DAPM_OUTPUT("VOUT3"),
+SND_SOC_DAPM_OUTPUT("VOUT4"),
+SND_SOC_DAPM_OUTPUT("VOUT5"),
+SND_SOC_DAPM_OUTPUT("VOUT6"),
+SND_SOC_DAPM_OUTPUT("VOUT7"),
+SND_SOC_DAPM_OUTPUT("VOUT8"),
+};
+
+static const struct snd_soc_dapm_route pcm1681_dapm_routes[] = {
+ { "VOUT1", NULL, "Playback" },
+ { "VOUT2", NULL, "Playback" },
+ { "VOUT3", NULL, "Playback" },
+ { "VOUT4", NULL, "Playback" },
+ { "VOUT5", NULL, "Playback" },
+ { "VOUT6", NULL, "Playback" },
+ { "VOUT7", NULL, "Playback" },
+ { "VOUT8", NULL, "Playback" },
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm1681_dac_tlv, -6350, 50, 1);
+
+static const struct snd_kcontrol_new pcm1681_controls[] = {
+ SOC_DOUBLE_R_TLV("Channel 1/2 Playback Volume",
+ PCM1681_ATT_CONTROL(1), PCM1681_ATT_CONTROL(2), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 3/4 Playback Volume",
+ PCM1681_ATT_CONTROL(3), PCM1681_ATT_CONTROL(4), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 5/6 Playback Volume",
+ PCM1681_ATT_CONTROL(5), PCM1681_ATT_CONTROL(6), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 7/8 Playback Volume",
+ PCM1681_ATT_CONTROL(7), PCM1681_ATT_CONTROL(8), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
+ pcm1681_get_deemph, pcm1681_put_deemph),
+};
+
+static struct snd_soc_dai_driver pcm1681_dai = {
+ .name = "pcm1681-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = PCM1681_PCM_RATES,
+ .formats = PCM1681_PCM_FORMATS,
+ },
+ .ops = &pcm1681_dai_ops,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm1681_dt_ids[] = {
+ { .compatible = "ti,pcm1681", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm1681_dt_ids);
+#endif
+
+static const struct regmap_config pcm1681_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x13,
+ .reg_defaults = pcm1681_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm1681_reg_defaults),
+ .writeable_reg = pcm1681_writeable_reg,
+ .readable_reg = pcm1681_accessible_reg,
+};
+
+static struct snd_soc_codec_driver soc_codec_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),
+};
+
+static const struct i2c_device_id pcm1681_i2c_id[] = {
+ {"pcm1681", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id);
+
+static int pcm1681_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct pcm1681_private *priv;
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = devm_regmap_init_i2c(client, &pcm1681_regmap);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ dev_err(&client->dev, "Failed to create regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, priv);
+
+ return snd_soc_register_codec(&client->dev, &soc_codec_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);
+
+MODULE_DESCRIPTION("Texas Instruments PCM1681 ALSA SoC Codec Driver");
+MODULE_AUTHOR("Marek Belisko <marek.belisko@streamunlimited.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/pcm1792a.c b/kernel/sound/soc/codecs/pcm1792a.c
new file mode 100644
index 000000000..57b0c94a7
--- /dev/null
+++ b/kernel/sound/soc/codecs/pcm1792a.c
@@ -0,0 +1,272 @@
+/*
+ * 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/kernel/sound/soc/codecs/pcm1792a.h b/kernel/sound/soc/codecs/pcm1792a.h
new file mode 100644
index 000000000..51d5470fe
--- /dev/null
+++ b/kernel/sound/soc/codecs/pcm1792a.h
@@ -0,0 +1,27 @@
+/*
+ * 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/kernel/sound/soc/codecs/pcm3008.c b/kernel/sound/soc/codecs/pcm3008.c
new file mode 100644
index 000000000..8fb445f33
--- /dev/null
+++ b/kernel/sound/soc/codecs/pcm3008.c
@@ -0,0 +1,172 @@
+/*
+ * ALSA Soc PCM3008 codec support
+ *
+ * Author: Hugo Villeneuve
+ * Copyright (C) 2008 Lyrtech inc
+ *
+ * Based on AC97 Soc codec, original copyright follow:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ *
+ * 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/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "pcm3008.h"
+
+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;
+
+ gpio_set_value_cansleep(setup->pdda_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct pcm3008_setup_data *setup = codec->dev->platform_data;
+
+ gpio_set_value_cansleep(setup->pdad_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget pcm3008_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("VINL"),
+SND_SOC_DAPM_INPUT("VINR"),
+
+SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_dac_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_adc_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm3008_dapm_routes[] = {
+ { "PCM3008 Capture", NULL, "ADC" },
+ { "ADC", NULL, "VINL" },
+ { "ADC", NULL, "VINR" },
+
+ { "DAC", NULL, "PCM3008 Playback" },
+ { "VOUTL", NULL, "DAC" },
+ { "VOUTR", NULL, "DAC" },
+};
+
+#define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+static struct snd_soc_dai_driver pcm3008_dai = {
+ .name = "pcm3008-hifi",
+ .playback = {
+ .stream_name = "PCM3008 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PCM3008_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "PCM3008 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PCM3008_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static 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 int pcm3008_codec_probe(struct platform_device *pdev)
+{
+ struct pcm3008_setup_data *setup = pdev->dev.platform_data;
+ int ret;
+
+ if (!setup)
+ return -EINVAL;
+
+ /* DEM1 DEM0 DE-EMPHASIS_MODE
+ * Low Low De-emphasis 44.1 kHz ON
+ * Low High De-emphasis OFF
+ * High Low De-emphasis 48 kHz ON
+ * High High De-emphasis 32 kHz ON
+ */
+
+ /* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */
+ ret = devm_gpio_request_one(&pdev->dev, setup->dem0_pin,
+ GPIOF_OUT_INIT_HIGH, "codec_dem0");
+ if (ret != 0)
+ return ret;
+
+ /* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */
+ ret = devm_gpio_request_one(&pdev->dev, setup->dem1_pin,
+ GPIOF_OUT_INIT_LOW, "codec_dem1");
+ if (ret != 0)
+ return ret;
+
+ /* Configure PDAD GPIO. */
+ ret = devm_gpio_request_one(&pdev->dev, setup->pdad_pin,
+ GPIOF_OUT_INIT_LOW, "codec_pdad");
+ if (ret != 0)
+ return ret;
+
+ /* Configure PDDA GPIO. */
+ ret = devm_gpio_request_one(&pdev->dev, setup->pdda_pin,
+ GPIOF_OUT_INIT_LOW, "codec_pdda");
+ if (ret != 0)
+ return ret;
+
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_pcm3008, &pcm3008_dai, 1);
+}
+
+static int pcm3008_codec_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:pcm3008-codec");
+
+static struct platform_driver pcm3008_codec_driver = {
+ .probe = pcm3008_codec_probe,
+ .remove = pcm3008_codec_remove,
+ .driver = {
+ .name = "pcm3008-codec",
+ },
+};
+
+module_platform_driver(pcm3008_codec_driver);
+
+MODULE_DESCRIPTION("Soc PCM3008 driver");
+MODULE_AUTHOR("Hugo Villeneuve");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/pcm3008.h b/kernel/sound/soc/codecs/pcm3008.h
new file mode 100644
index 000000000..7e5489ab4
--- /dev/null
+++ b/kernel/sound/soc/codecs/pcm3008.h
@@ -0,0 +1,22 @@
+/*
+ * 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/kernel/sound/soc/codecs/pcm512x-i2c.c b/kernel/sound/soc/codecs/pcm512x-i2c.c
new file mode 100644
index 000000000..dcdfac0ff
--- /dev/null
+++ b/kernel/sound/soc/codecs/pcm512x-i2c.c
@@ -0,0 +1,80 @@
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author: Mark Brown <broonie@linaro.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 "pcm512x.h"
+
+static int pcm512x_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+ struct regmap_config config = pcm512x_regmap;
+
+ /* msb needs to be set to enable auto-increment of addresses */
+ config.read_flag_mask = 0x80;
+ config.write_flag_mask = 0x80;
+
+ regmap = devm_regmap_init_i2c(i2c, &config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcm512x_probe(&i2c->dev, regmap);
+}
+
+static int pcm512x_i2c_remove(struct i2c_client *i2c)
+{
+ pcm512x_remove(&i2c->dev);
+ return 0;
+}
+
+static const struct i2c_device_id pcm512x_i2c_id[] = {
+ { "pcm5121", },
+ { "pcm5122", },
+ { "pcm5141", },
+ { "pcm5142", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id);
+
+static const struct of_device_id pcm512x_of_match[] = {
+ { .compatible = "ti,pcm5121", },
+ { .compatible = "ti,pcm5122", },
+ { .compatible = "ti,pcm5141", },
+ { .compatible = "ti,pcm5142", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm512x_of_match);
+
+static struct i2c_driver pcm512x_i2c_driver = {
+ .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,
+ },
+};
+
+module_i2c_driver(pcm512x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC PCM512x codec driver - I2C");
+MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/pcm512x-spi.c b/kernel/sound/soc/codecs/pcm512x-spi.c
new file mode 100644
index 000000000..7b64a9cef
--- /dev/null
+++ b/kernel/sound/soc/codecs/pcm512x-spi.c
@@ -0,0 +1,73 @@
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author: Mark Brown <broonie@linaro.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/spi/spi.h>
+
+#include "pcm512x.h"
+
+static int pcm512x_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_spi(spi, &pcm512x_regmap);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ return ret;
+ }
+
+ return pcm512x_probe(&spi->dev, regmap);
+}
+
+static int pcm512x_spi_remove(struct spi_device *spi)
+{
+ pcm512x_remove(&spi->dev);
+ return 0;
+}
+
+static const struct spi_device_id pcm512x_spi_id[] = {
+ { "pcm5121", },
+ { "pcm5122", },
+ { "pcm5141", },
+ { "pcm5142", },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, pcm512x_spi_id);
+
+static const struct of_device_id pcm512x_of_match[] = {
+ { .compatible = "ti,pcm5121", },
+ { .compatible = "ti,pcm5122", },
+ { .compatible = "ti,pcm5141", },
+ { .compatible = "ti,pcm5142", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm512x_of_match);
+
+static struct spi_driver pcm512x_spi_driver = {
+ .probe = pcm512x_spi_probe,
+ .remove = pcm512x_spi_remove,
+ .id_table = pcm512x_spi_id,
+ .driver = {
+ .name = "pcm512x",
+ .owner = THIS_MODULE,
+ .of_match_table = pcm512x_of_match,
+ .pm = &pcm512x_pm_ops,
+ },
+};
+
+module_spi_driver(pcm512x_spi_driver);
diff --git a/kernel/sound/soc/codecs/pcm512x.c b/kernel/sound/soc/codecs/pcm512x.c
new file mode 100644
index 000000000..e12764d15
--- /dev/null
+++ b/kernel/sound/soc/codecs/pcm512x.c
@@ -0,0 +1,1609 @@
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author: Mark Brown <broonie@linaro.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>
+#include <linux/gcd.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include "pcm512x.h"
+
+#define DIV_ROUND_DOWN_ULL(ll, d) \
+ ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; })
+
+#define PCM512x_NUM_SUPPLIES 3
+static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = {
+ "AVDD",
+ "DVDD",
+ "CPVDD",
+};
+
+struct pcm512x_priv {
+ struct regmap *regmap;
+ struct clk *sclk;
+ struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES];
+ struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES];
+ int fmt;
+ int pll_in;
+ int pll_out;
+ int pll_r;
+ int pll_j;
+ int pll_d;
+ int pll_p;
+ unsigned long real_pll;
+ unsigned long overclock_pll;
+ unsigned long overclock_dac;
+ unsigned long overclock_dsp;
+};
+
+/*
+ * We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define PCM512x_REGULATOR_EVENT(n) \
+static int pcm512x_regulator_event_##n(struct notifier_block *nb, \
+ unsigned long event, void *data) \
+{ \
+ struct pcm512x_priv *pcm512x = container_of(nb, struct pcm512x_priv, \
+ supply_nb[n]); \
+ if (event & REGULATOR_EVENT_DISABLE) { \
+ regcache_mark_dirty(pcm512x->regmap); \
+ regcache_cache_only(pcm512x->regmap, true); \
+ } \
+ return 0; \
+}
+
+PCM512x_REGULATOR_EVENT(0)
+PCM512x_REGULATOR_EVENT(1)
+PCM512x_REGULATOR_EVENT(2)
+
+static const struct reg_default pcm512x_reg_defaults[] = {
+ { PCM512x_RESET, 0x00 },
+ { PCM512x_POWER, 0x00 },
+ { PCM512x_MUTE, 0x00 },
+ { PCM512x_DSP, 0x00 },
+ { PCM512x_PLL_REF, 0x00 },
+ { PCM512x_DAC_REF, 0x00 },
+ { PCM512x_DAC_ROUTING, 0x11 },
+ { PCM512x_DSP_PROGRAM, 0x01 },
+ { PCM512x_CLKDET, 0x00 },
+ { PCM512x_AUTO_MUTE, 0x00 },
+ { PCM512x_ERROR_DETECT, 0x00 },
+ { PCM512x_DIGITAL_VOLUME_1, 0x00 },
+ { PCM512x_DIGITAL_VOLUME_2, 0x30 },
+ { PCM512x_DIGITAL_VOLUME_3, 0x30 },
+ { PCM512x_DIGITAL_MUTE_1, 0x22 },
+ { PCM512x_DIGITAL_MUTE_2, 0x00 },
+ { PCM512x_DIGITAL_MUTE_3, 0x07 },
+ { PCM512x_OUTPUT_AMPLITUDE, 0x00 },
+ { PCM512x_ANALOG_GAIN_CTRL, 0x00 },
+ { PCM512x_UNDERVOLTAGE_PROT, 0x00 },
+ { PCM512x_ANALOG_MUTE_CTRL, 0x00 },
+ { PCM512x_ANALOG_GAIN_BOOST, 0x00 },
+ { PCM512x_VCOM_CTRL_1, 0x00 },
+ { PCM512x_VCOM_CTRL_2, 0x01 },
+ { PCM512x_BCLK_LRCLK_CFG, 0x00 },
+ { PCM512x_MASTER_MODE, 0x7c },
+ { PCM512x_GPIO_DACIN, 0x00 },
+ { PCM512x_GPIO_PLLIN, 0x00 },
+ { PCM512x_SYNCHRONIZE, 0x10 },
+ { PCM512x_PLL_COEFF_0, 0x00 },
+ { PCM512x_PLL_COEFF_1, 0x00 },
+ { PCM512x_PLL_COEFF_2, 0x00 },
+ { PCM512x_PLL_COEFF_3, 0x00 },
+ { PCM512x_PLL_COEFF_4, 0x00 },
+ { PCM512x_DSP_CLKDIV, 0x00 },
+ { PCM512x_DAC_CLKDIV, 0x00 },
+ { PCM512x_NCP_CLKDIV, 0x00 },
+ { PCM512x_OSR_CLKDIV, 0x00 },
+ { PCM512x_MASTER_CLKDIV_1, 0x00 },
+ { PCM512x_MASTER_CLKDIV_2, 0x00 },
+ { PCM512x_FS_SPEED_MODE, 0x00 },
+ { PCM512x_IDAC_1, 0x01 },
+ { PCM512x_IDAC_2, 0x00 },
+};
+
+static bool pcm512x_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PCM512x_RESET:
+ case PCM512x_POWER:
+ case PCM512x_MUTE:
+ case PCM512x_PLL_EN:
+ case PCM512x_SPI_MISO_FUNCTION:
+ case PCM512x_DSP:
+ case PCM512x_GPIO_EN:
+ case PCM512x_BCLK_LRCLK_CFG:
+ case PCM512x_DSP_GPIO_INPUT:
+ case PCM512x_MASTER_MODE:
+ case PCM512x_PLL_REF:
+ case PCM512x_DAC_REF:
+ case PCM512x_GPIO_DACIN:
+ case PCM512x_GPIO_PLLIN:
+ case PCM512x_SYNCHRONIZE:
+ case PCM512x_PLL_COEFF_0:
+ case PCM512x_PLL_COEFF_1:
+ case PCM512x_PLL_COEFF_2:
+ case PCM512x_PLL_COEFF_3:
+ case PCM512x_PLL_COEFF_4:
+ case PCM512x_DSP_CLKDIV:
+ case PCM512x_DAC_CLKDIV:
+ case PCM512x_NCP_CLKDIV:
+ case PCM512x_OSR_CLKDIV:
+ case PCM512x_MASTER_CLKDIV_1:
+ case PCM512x_MASTER_CLKDIV_2:
+ case PCM512x_FS_SPEED_MODE:
+ case PCM512x_IDAC_1:
+ case PCM512x_IDAC_2:
+ case PCM512x_ERROR_DETECT:
+ case PCM512x_I2S_1:
+ case PCM512x_I2S_2:
+ case PCM512x_DAC_ROUTING:
+ case PCM512x_DSP_PROGRAM:
+ case PCM512x_CLKDET:
+ case PCM512x_AUTO_MUTE:
+ case PCM512x_DIGITAL_VOLUME_1:
+ case PCM512x_DIGITAL_VOLUME_2:
+ case PCM512x_DIGITAL_VOLUME_3:
+ case PCM512x_DIGITAL_MUTE_1:
+ case PCM512x_DIGITAL_MUTE_2:
+ case PCM512x_DIGITAL_MUTE_3:
+ case PCM512x_GPIO_OUTPUT_1:
+ case PCM512x_GPIO_OUTPUT_2:
+ case PCM512x_GPIO_OUTPUT_3:
+ case PCM512x_GPIO_OUTPUT_4:
+ case PCM512x_GPIO_OUTPUT_5:
+ case PCM512x_GPIO_OUTPUT_6:
+ case PCM512x_GPIO_CONTROL_1:
+ case PCM512x_GPIO_CONTROL_2:
+ case PCM512x_OVERFLOW:
+ case PCM512x_RATE_DET_1:
+ case PCM512x_RATE_DET_2:
+ case PCM512x_RATE_DET_3:
+ case PCM512x_RATE_DET_4:
+ case PCM512x_CLOCK_STATUS:
+ case PCM512x_ANALOG_MUTE_DET:
+ case PCM512x_GPIN:
+ case PCM512x_DIGITAL_MUTE_DET:
+ case PCM512x_OUTPUT_AMPLITUDE:
+ case PCM512x_ANALOG_GAIN_CTRL:
+ case PCM512x_UNDERVOLTAGE_PROT:
+ case PCM512x_ANALOG_MUTE_CTRL:
+ case PCM512x_ANALOG_GAIN_BOOST:
+ case PCM512x_VCOM_CTRL_1:
+ case PCM512x_VCOM_CTRL_2:
+ case PCM512x_CRAM_CTRL:
+ case PCM512x_FLEX_A:
+ case PCM512x_FLEX_B:
+ return true;
+ default:
+ /* There are 256 raw register addresses */
+ return reg < 0xff;
+ }
+}
+
+static bool pcm512x_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PCM512x_PLL_EN:
+ case PCM512x_OVERFLOW:
+ case PCM512x_RATE_DET_1:
+ case PCM512x_RATE_DET_2:
+ case PCM512x_RATE_DET_3:
+ case PCM512x_RATE_DET_4:
+ case PCM512x_CLOCK_STATUS:
+ case PCM512x_ANALOG_MUTE_DET:
+ case PCM512x_GPIN:
+ case PCM512x_DIGITAL_MUTE_DET:
+ case PCM512x_CRAM_CTRL:
+ return true;
+ default:
+ /* There are 256 raw register addresses */
+ return reg < 0xff;
+ }
+}
+
+static int pcm512x_overclock_pll_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+
+ switch (codec->dapm.bias_level) {
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+
+ switch (codec->dapm.bias_level) {
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+
+ switch (codec->dapm.bias_level) {
+ case SND_SOC_BIAS_OFF:
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ default:
+ return -EBUSY;
+ }
+
+ pcm512x->overclock_dac = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(analog_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0);
+
+static const char * const pcm512x_dsp_program_texts[] = {
+ "FIR interpolation with de-emphasis",
+ "Low latency IIR with de-emphasis",
+ "High attenuation with de-emphasis",
+ "Fixed process flow",
+ "Ringing-less low latency FIR",
+};
+
+static const unsigned int pcm512x_dsp_program_values[] = {
+ 1,
+ 2,
+ 3,
+ 5,
+ 7,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pcm512x_dsp_program,
+ PCM512x_DSP_PROGRAM, 0, 0x1f,
+ pcm512x_dsp_program_texts,
+ pcm512x_dsp_program_values);
+
+static const char * const pcm512x_clk_missing_text[] = {
+ "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s"
+};
+
+static const struct soc_enum pcm512x_clk_missing =
+ SOC_ENUM_SINGLE(PCM512x_CLKDET, 0, 8, pcm512x_clk_missing_text);
+
+static const char * const pcm512x_autom_text[] = {
+ "21ms", "106ms", "213ms", "533ms", "1.07s", "2.13s", "5.33s", "10.66s"
+};
+
+static const struct soc_enum pcm512x_autom_l =
+ SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATML_SHIFT, 8,
+ pcm512x_autom_text);
+
+static const struct soc_enum pcm512x_autom_r =
+ SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATMR_SHIFT, 8,
+ pcm512x_autom_text);
+
+static const char * const pcm512x_ramp_rate_text[] = {
+ "1 sample/update", "2 samples/update", "4 samples/update",
+ "Immediate"
+};
+
+static const struct soc_enum pcm512x_vndf =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDF_SHIFT, 4,
+ pcm512x_ramp_rate_text);
+
+static const struct soc_enum pcm512x_vnuf =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUF_SHIFT, 4,
+ pcm512x_ramp_rate_text);
+
+static const struct soc_enum pcm512x_vedf =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDF_SHIFT, 4,
+ pcm512x_ramp_rate_text);
+
+static const char * const pcm512x_ramp_step_text[] = {
+ "4dB/step", "2dB/step", "1dB/step", "0.5dB/step"
+};
+
+static const struct soc_enum pcm512x_vnds =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDS_SHIFT, 4,
+ pcm512x_ramp_step_text);
+
+static const struct soc_enum pcm512x_vnus =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUS_SHIFT, 4,
+ pcm512x_ramp_step_text);
+
+static const struct soc_enum pcm512x_veds =
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4,
+ pcm512x_ramp_step_text);
+
+static const struct snd_kcontrol_new pcm512x_controls[] = {
+SOC_DOUBLE_R_TLV("Digital Playback Volume", PCM512x_DIGITAL_VOLUME_2,
+ PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv),
+SOC_DOUBLE_TLV("Analogue Playback Volume", PCM512x_ANALOG_GAIN_CTRL,
+ PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv),
+SOC_DOUBLE_TLV("Analogue Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST,
+ PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv),
+SOC_DOUBLE("Digital Playback Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
+ PCM512x_RQMR_SHIFT, 1, 1),
+
+SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1),
+SOC_ENUM("DSP Program", pcm512x_dsp_program),
+
+SOC_ENUM("Clock Missing Period", pcm512x_clk_missing),
+SOC_ENUM("Auto Mute Time Left", pcm512x_autom_l),
+SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r),
+SOC_SINGLE("Auto Mute Mono Switch", PCM512x_DIGITAL_MUTE_3,
+ PCM512x_ACTL_SHIFT, 1, 0),
+SOC_DOUBLE("Auto Mute Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT,
+ PCM512x_AMRE_SHIFT, 1, 0),
+
+SOC_ENUM("Volume Ramp Down Rate", pcm512x_vndf),
+SOC_ENUM("Volume Ramp Down Step", pcm512x_vnds),
+SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf),
+SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus),
+SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf),
+SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds),
+
+SOC_SINGLE_EXT("Max Overclock PLL", SND_SOC_NOPM, 0, 20, 0,
+ pcm512x_overclock_pll_get, pcm512x_overclock_pll_put),
+SOC_SINGLE_EXT("Max Overclock DSP", SND_SOC_NOPM, 0, 40, 0,
+ pcm512x_overclock_dsp_get, pcm512x_overclock_dsp_put),
+SOC_SINGLE_EXT("Max Overclock DAC", SND_SOC_NOPM, 0, 40, 0,
+ pcm512x_overclock_dac_get, pcm512x_overclock_dac_put),
+};
+
+static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_OUTPUT("OUTL"),
+SND_SOC_DAPM_OUTPUT("OUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = {
+ { "DACL", NULL, "Playback" },
+ { "DACR", NULL, "Playback" },
+
+ { "OUTL", NULL, "DACL" },
+ { "OUTR", NULL, "DACR" },
+};
+
+static unsigned long pcm512x_pll_max(struct pcm512x_priv *pcm512x)
+{
+ return 25000000 + 25000000 * pcm512x->overclock_pll / 100;
+}
+
+static unsigned long pcm512x_dsp_max(struct pcm512x_priv *pcm512x)
+{
+ return 50000000 + 50000000 * pcm512x->overclock_dsp / 100;
+}
+
+static unsigned long pcm512x_dac_max(struct pcm512x_priv *pcm512x,
+ unsigned long rate)
+{
+ return rate + rate * pcm512x->overclock_dac / 100;
+}
+
+static unsigned long pcm512x_sck_max(struct pcm512x_priv *pcm512x)
+{
+ if (!pcm512x->pll_out)
+ return 25000000;
+ return pcm512x_pll_max(pcm512x);
+}
+
+static unsigned long pcm512x_ncp_target(struct pcm512x_priv *pcm512x,
+ unsigned long dac_rate)
+{
+ /*
+ * If the DAC is not actually overclocked, use the good old
+ * NCP target rate...
+ */
+ if (dac_rate <= 6144000)
+ return 1536000;
+ /*
+ * ...but if the DAC is in fact overclocked, bump the NCP target
+ * rate to get the recommended dividers even when overclocking.
+ */
+ return pcm512x_dac_max(pcm512x, 1536000);
+}
+
+static const u32 pcm512x_dai_rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000,
+ 88200, 96000, 176400, 192000, 384000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_slave = {
+ .count = ARRAY_SIZE(pcm512x_dai_rates),
+ .list = pcm512x_dai_rates,
+};
+
+static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct pcm512x_priv *pcm512x = rule->private;
+ struct snd_interval ranges[2];
+ int frame_size;
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0)
+ return frame_size;
+
+ switch (frame_size) {
+ case 32:
+ /* No hole when the frame size is 32. */
+ return 0;
+ case 48:
+ case 64:
+ /* There is only one hole in the range of supported
+ * rates, but it moves with the frame size.
+ */
+ memset(ranges, 0, sizeof(ranges));
+ ranges[0].min = 8000;
+ ranges[0].max = pcm512x_sck_max(pcm512x) / frame_size / 2;
+ ranges[1].min = DIV_ROUND_UP(16000000, frame_size);
+ ranges[1].max = 384000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_interval_ranges(hw_param_interval(params, rule->var),
+ ARRAY_SIZE(ranges), ranges, 0);
+}
+
+static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct device *dev = dai->dev;
+ struct snd_pcm_hw_constraint_ratnums *constraints_no_pll;
+ struct snd_ratnum *rats_no_pll;
+
+ if (IS_ERR(pcm512x->sclk)) {
+ dev_err(dev, "Need SCLK for master mode: %ld\n",
+ PTR_ERR(pcm512x->sclk));
+ return PTR_ERR(pcm512x->sclk);
+ }
+
+ if (pcm512x->pll_out)
+ return snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ pcm512x_hw_rule_rate,
+ pcm512x,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+ constraints_no_pll = devm_kzalloc(dev, sizeof(*constraints_no_pll),
+ GFP_KERNEL);
+ if (!constraints_no_pll)
+ return -ENOMEM;
+ constraints_no_pll->nrats = 1;
+ rats_no_pll = devm_kzalloc(dev, sizeof(*rats_no_pll), GFP_KERNEL);
+ if (!rats_no_pll)
+ return -ENOMEM;
+ constraints_no_pll->rats = rats_no_pll;
+ rats_no_pll->num = clk_get_rate(pcm512x->sclk) / 64;
+ rats_no_pll->den_min = 1;
+ rats_no_pll->den_max = 128;
+ rats_no_pll->den_step = 1;
+
+ return snd_pcm_hw_constraint_ratnums(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ constraints_no_pll);
+}
+
+static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct device *dev = dai->dev;
+ struct regmap *regmap = pcm512x->regmap;
+
+ if (IS_ERR(pcm512x->sclk)) {
+ dev_info(dev, "No SCLK, using BCLK: %ld\n",
+ PTR_ERR(pcm512x->sclk));
+
+ /* Disable reporting of missing SCLK as an error */
+ regmap_update_bits(regmap, PCM512x_ERROR_DETECT,
+ PCM512x_IDCH, PCM512x_IDCH);
+
+ /* Switch PLL input to BCLK */
+ regmap_update_bits(regmap, PCM512x_PLL_REF,
+ PCM512x_SREF, PCM512x_SREF_BCK);
+ }
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_slave);
+}
+
+static int pcm512x_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+
+ switch (pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ return pcm512x_dai_startup_master(substream, dai);
+
+ case SND_SOC_DAIFMT_CBS_CFS:
+ return pcm512x_dai_startup_slave(substream, dai);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(codec->dev);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQST, 0);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to remove standby: %d\n",
+ ret);
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQST, PCM512x_RQST);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request standby: %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static unsigned long pcm512x_find_sck(struct snd_soc_dai *dai,
+ unsigned long bclk_rate)
+{
+ struct device *dev = dai->dev;
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ unsigned long sck_rate;
+ int pow2;
+
+ /* 64 MHz <= pll_rate <= 100 MHz, VREF mode */
+ /* 16 MHz <= sck_rate <= 25 MHz, VREF mode */
+
+ /* select sck_rate as a multiple of bclk_rate but still with
+ * as many factors of 2 as possible, as that makes it easier
+ * to find a fast DAC rate
+ */
+ pow2 = 1 << fls((pcm512x_pll_max(pcm512x) - 16000000) / bclk_rate);
+ for (; pow2; pow2 >>= 1) {
+ sck_rate = rounddown(pcm512x_pll_max(pcm512x),
+ bclk_rate * pow2);
+ if (sck_rate >= 16000000)
+ break;
+ }
+ if (!pow2) {
+ dev_err(dev, "Impossible to generate a suitable SCK\n");
+ return 0;
+ }
+
+ dev_dbg(dev, "sck_rate %lu\n", sck_rate);
+ return sck_rate;
+}
+
+/* pll_rate = pllin_rate * R * J.D / P
+ * 1 <= R <= 16
+ * 1 <= J <= 63
+ * 0 <= D <= 9999
+ * 1 <= P <= 15
+ * 64 MHz <= pll_rate <= 100 MHz
+ * if D == 0
+ * 1 MHz <= pllin_rate / P <= 20 MHz
+ * else if D > 0
+ * 6.667 MHz <= pllin_rate / P <= 20 MHz
+ * 4 <= J <= 11
+ * R = 1
+ */
+static int pcm512x_find_pll_coeff(struct snd_soc_dai *dai,
+ unsigned long pllin_rate,
+ unsigned long pll_rate)
+{
+ struct device *dev = dai->dev;
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ unsigned long common;
+ int R, J, D, P;
+ unsigned long K; /* 10000 * J.D */
+ unsigned long num;
+ unsigned long den;
+
+ common = gcd(pll_rate, pllin_rate);
+ dev_dbg(dev, "pll %lu pllin %lu common %lu\n",
+ pll_rate, pllin_rate, common);
+ num = pll_rate / common;
+ den = pllin_rate / common;
+
+ /* pllin_rate / P (or here, den) cannot be greater than 20 MHz */
+ if (pllin_rate / den > 20000000 && num < 8) {
+ num *= DIV_ROUND_UP(pllin_rate / den, 20000000);
+ den *= DIV_ROUND_UP(pllin_rate / den, 20000000);
+ }
+ dev_dbg(dev, "num / den = %lu / %lu\n", num, den);
+
+ P = den;
+ if (den <= 15 && num <= 16 * 63
+ && 1000000 <= pllin_rate / P && pllin_rate / P <= 20000000) {
+ /* Try the case with D = 0 */
+ D = 0;
+ /* factor 'num' into J and R, such that R <= 16 and J <= 63 */
+ for (R = 16; R; R--) {
+ if (num % R)
+ continue;
+ J = num / R;
+ if (J == 0 || J > 63)
+ continue;
+
+ dev_dbg(dev, "R * J / P = %d * %d / %d\n", R, J, P);
+ pcm512x->real_pll = pll_rate;
+ goto done;
+ }
+ /* no luck */
+ }
+
+ R = 1;
+
+ if (num > 0xffffffffUL / 10000)
+ goto fallback;
+
+ /* Try to find an exact pll_rate using the D > 0 case */
+ common = gcd(10000 * num, den);
+ num = 10000 * num / common;
+ den /= common;
+ dev_dbg(dev, "num %lu den %lu common %lu\n", num, den, common);
+
+ for (P = den; P <= 15; P++) {
+ if (pllin_rate / P < 6667000 || 200000000 < pllin_rate / P)
+ continue;
+ if (num * P % den)
+ continue;
+ K = num * P / den;
+ /* J == 12 is ok if D == 0 */
+ if (K < 40000 || K > 120000)
+ continue;
+
+ J = K / 10000;
+ D = K % 10000;
+ dev_dbg(dev, "J.D / P = %d.%04d / %d\n", J, D, P);
+ pcm512x->real_pll = pll_rate;
+ goto done;
+ }
+
+ /* Fall back to an approximate pll_rate */
+
+fallback:
+ /* find smallest possible P */
+ P = DIV_ROUND_UP(pllin_rate, 20000000);
+ if (!P)
+ P = 1;
+ else if (P > 15) {
+ dev_err(dev, "Need a slower clock as pll-input\n");
+ return -EINVAL;
+ }
+ if (pllin_rate / P < 6667000) {
+ dev_err(dev, "Need a faster clock as pll-input\n");
+ return -EINVAL;
+ }
+ K = DIV_ROUND_CLOSEST_ULL(10000ULL * pll_rate * P, pllin_rate);
+ if (K < 40000)
+ K = 40000;
+ /* J == 12 is ok if D == 0 */
+ if (K > 120000)
+ K = 120000;
+ J = K / 10000;
+ D = K % 10000;
+ dev_dbg(dev, "J.D / P ~ %d.%04d / %d\n", J, D, P);
+ pcm512x->real_pll = DIV_ROUND_DOWN_ULL((u64)K * pllin_rate, 10000 * P);
+
+done:
+ pcm512x->pll_r = R;
+ pcm512x->pll_j = J;
+ pcm512x->pll_d = D;
+ pcm512x->pll_p = P;
+ return 0;
+}
+
+static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai,
+ unsigned long osr_rate,
+ unsigned long pllin_rate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ unsigned long dac_rate;
+
+ if (!pcm512x->pll_out)
+ return 0; /* no PLL to bypass, force SCK as DAC input */
+
+ if (pllin_rate % osr_rate)
+ return 0; /* futile, quit early */
+
+ /* run DAC no faster than 6144000 Hz */
+ for (dac_rate = rounddown(pcm512x_dac_max(pcm512x, 6144000), osr_rate);
+ dac_rate;
+ dac_rate -= osr_rate) {
+
+ if (pllin_rate / dac_rate > 128)
+ return 0; /* DAC divider would be too big */
+
+ if (!(pllin_rate % dac_rate))
+ return dac_rate;
+
+ dac_rate -= osr_rate;
+ }
+
+ return 0;
+}
+
+static int pcm512x_set_dividers(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params)
+{
+ struct device *dev = dai->dev;
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ unsigned long pllin_rate = 0;
+ unsigned long pll_rate;
+ unsigned long sck_rate;
+ unsigned long mck_rate;
+ unsigned long bclk_rate;
+ unsigned long sample_rate;
+ unsigned long osr_rate;
+ unsigned long dacsrc_rate;
+ int bclk_div;
+ int lrclk_div;
+ int dsp_div;
+ int dac_div;
+ unsigned long dac_rate;
+ int ncp_div;
+ int osr_div;
+ int ret;
+ int idac;
+ int fssp;
+ int gpio;
+
+ 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);
+
+ mck_rate = sck_rate;
+ } else {
+ ret = snd_soc_params_to_bclk(params);
+ if (ret < 0) {
+ dev_err(dev, "Failed to find suitable BCLK: %d\n", ret);
+ return ret;
+ }
+ if (ret == 0) {
+ dev_err(dev, "No BCLK?\n");
+ return -EINVAL;
+ }
+ bclk_rate = ret;
+
+ pllin_rate = clk_get_rate(pcm512x->sclk);
+
+ sck_rate = pcm512x_find_sck(dai, bclk_rate);
+ if (!sck_rate)
+ return -EINVAL;
+ pll_rate = 4 * sck_rate;
+
+ ret = pcm512x_find_pll_coeff(dai, pllin_rate, pll_rate);
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_0, pcm512x->pll_p - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write PLL P: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_1, pcm512x->pll_j);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write PLL J: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_2, pcm512x->pll_d >> 8);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write PLL D msb: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_3, pcm512x->pll_d & 0xff);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write PLL D lsb: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_4, pcm512x->pll_r - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write PLL R: %d\n", ret);
+ return ret;
+ }
+
+ mck_rate = pcm512x->real_pll;
+
+ bclk_div = DIV_ROUND_CLOSEST(sck_rate, bclk_rate);
+ }
+
+ if (bclk_div > 128) {
+ dev_err(dev, "Failed to find BCLK divider\n");
+ return -EINVAL;
+ }
+
+ /* the actual rate */
+ sample_rate = sck_rate / bclk_div / lrclk_div;
+ osr_rate = 16 * sample_rate;
+
+ /* run DSP no faster than 50 MHz */
+ dsp_div = mck_rate > pcm512x_dsp_max(pcm512x) ? 2 : 1;
+
+ dac_rate = pcm512x_pllin_dac_rate(dai, osr_rate, pllin_rate);
+ if (dac_rate) {
+ /* the desired clock rate is "compatible" with the pll input
+ * clock, so use that clock as dac input instead of the pll
+ * output clock since the pll will introduce jitter and thus
+ * noise.
+ */
+ dev_dbg(dev, "using pll input as dac input\n");
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF,
+ PCM512x_SDAC, PCM512x_SDAC_GPIO);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to set gpio as dacref: %d\n", ret);
+ return ret;
+ }
+
+ gpio = PCM512x_GREF_GPIO1 + pcm512x->pll_in - 1;
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_DACIN,
+ PCM512x_GREF, gpio);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to set gpio %d as dacin: %d\n",
+ pcm512x->pll_in, ret);
+ return ret;
+ }
+
+ dacsrc_rate = pllin_rate;
+ } else {
+ /* run DAC no faster than 6144000 Hz */
+ unsigned long dac_mul = pcm512x_dac_max(pcm512x, 6144000)
+ / osr_rate;
+ unsigned long sck_mul = sck_rate / osr_rate;
+
+ for (; dac_mul; dac_mul--) {
+ if (!(sck_mul % dac_mul))
+ break;
+ }
+ if (!dac_mul) {
+ dev_err(dev, "Failed to find DAC rate\n");
+ return -EINVAL;
+ }
+
+ dac_rate = dac_mul * osr_rate;
+ dev_dbg(dev, "dac_rate %lu sample_rate %lu\n",
+ dac_rate, sample_rate);
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF,
+ PCM512x_SDAC, PCM512x_SDAC_SCK);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to set sck as dacref: %d\n", ret);
+ return ret;
+ }
+
+ dacsrc_rate = sck_rate;
+ }
+
+ osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate);
+ if (osr_div > 128) {
+ dev_err(dev, "Failed to find OSR divider\n");
+ return -EINVAL;
+ }
+
+ dac_div = DIV_ROUND_CLOSEST(dacsrc_rate, dac_rate);
+ if (dac_div > 128) {
+ dev_err(dev, "Failed to find DAC divider\n");
+ return -EINVAL;
+ }
+ dac_rate = dacsrc_rate / dac_div;
+
+ ncp_div = DIV_ROUND_CLOSEST(dac_rate,
+ pcm512x_ncp_target(pcm512x, dac_rate));
+ if (ncp_div > 128 || dac_rate / ncp_div > 2048000) {
+ /* run NCP no faster than 2048000 Hz, but why? */
+ ncp_div = DIV_ROUND_UP(dac_rate, 2048000);
+ if (ncp_div > 128) {
+ dev_err(dev, "Failed to find NCP divider\n");
+ return -EINVAL;
+ }
+ }
+
+ idac = mck_rate / (dsp_div * sample_rate);
+
+ ret = regmap_write(pcm512x->regmap, PCM512x_DSP_CLKDIV, dsp_div - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write DSP divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap, PCM512x_DAC_CLKDIV, dac_div - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write DAC divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap, PCM512x_NCP_CLKDIV, ncp_div - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write NCP divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap, PCM512x_OSR_CLKDIV, osr_div - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write OSR divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_MASTER_CLKDIV_1, bclk_div - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write BCLK divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_MASTER_CLKDIV_2, lrclk_div - 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write LRCLK divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap, PCM512x_IDAC_1, idac >> 8);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write IDAC msb divider: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(pcm512x->regmap, PCM512x_IDAC_2, idac & 0xff);
+ if (ret != 0) {
+ dev_err(dev, "Failed to write IDAC lsb divider: %d\n", ret);
+ return ret;
+ }
+
+ if (sample_rate <= pcm512x_dac_max(pcm512x, 48000))
+ fssp = PCM512x_FSSP_48KHZ;
+ else if (sample_rate <= pcm512x_dac_max(pcm512x, 96000))
+ fssp = PCM512x_FSSP_96KHZ;
+ else if (sample_rate <= pcm512x_dac_max(pcm512x, 192000))
+ fssp = PCM512x_FSSP_192KHZ;
+ else
+ fssp = PCM512x_FSSP_384KHZ;
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_FS_SPEED_MODE,
+ PCM512x_FSSP, fssp);
+ if (ret != 0) {
+ dev_err(codec->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);
+
+ return 0;
+}
+
+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);
+ int alen;
+ int gpio;
+ int clock_output;
+ int master_mode;
+ int ret;
+
+ dev_dbg(codec->dev, "hw_params %u Hz, %u channels\n",
+ params_rate(params),
+ params_channels(params));
+
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ alen = PCM512x_ALEN_16;
+ break;
+ case 20:
+ alen = PCM512x_ALEN_20;
+ break;
+ case 24:
+ alen = PCM512x_ALEN_24;
+ break;
+ case 32:
+ alen = PCM512x_ALEN_32;
+ break;
+ default:
+ dev_err(codec->dev, "Bad frame size: %d\n",
+ snd_pcm_format_width(params_format(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_ERROR_DETECT,
+ PCM512x_DCAS, 0);
+ if (ret != 0) {
+ dev_err(codec->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;
+ }
+
+ 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);
+ 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);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT,
+ PCM512x_IDFS | PCM512x_IDBK
+ | PCM512x_IDSK | PCM512x_IDCH
+ | PCM512x_IDCM | PCM512x_DCAS
+ | PCM512x_IPLK,
+ PCM512x_IDFS | PCM512x_IDBK
+ | PCM512x_IDSK | PCM512x_IDCH
+ | PCM512x_DCAS);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to ignore auto-clock failures: %d\n",
+ ret);
+ return ret;
+ }
+ } else {
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT,
+ PCM512x_IDFS | PCM512x_IDBK
+ | PCM512x_IDSK | PCM512x_IDCH
+ | PCM512x_IDCM | PCM512x_DCAS
+ | PCM512x_IPLK,
+ PCM512x_IDFS | PCM512x_IDBK
+ | PCM512x_IDSK | PCM512x_IDCH
+ | PCM512x_DCAS | PCM512x_IPLK);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to ignore auto-clock failures: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN,
+ PCM512x_PLLE, 0);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to disable pll: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = pcm512x_set_dividers(dai, params);
+ if (ret != 0)
+ return ret;
+
+ if (pcm512x->pll_out) {
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_REF,
+ PCM512x_SREF, PCM512x_SREF_GPIO);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to set gpio as pllref: %d\n", ret);
+ return ret;
+ }
+
+ gpio = PCM512x_GREF_GPIO1 + pcm512x->pll_in - 1;
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_PLLIN,
+ PCM512x_GREF, gpio);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to set gpio %d as pllin: %d\n",
+ pcm512x->pll_in, ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN,
+ PCM512x_PLLE, PCM512x_PLLE);
+ if (ret != 0) {
+ dev_err(codec->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",
+ pcm512x->pll_out, ret);
+ return ret;
+ }
+
+ gpio = PCM512x_GPIO_OUTPUT_1 + pcm512x->pll_out - 1;
+ ret = regmap_update_bits(pcm512x->regmap, gpio,
+ PCM512x_GxSL, PCM512x_GxSL_PLLCK);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to output pll on %d: %d\n",
+ ret, pcm512x->pll_out);
+ return ret;
+ }
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_SYNCHRONIZE,
+ PCM512x_RQSY, PCM512x_RQSY_HALT);
+ if (ret != 0) {
+ dev_err(codec->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);
+ return ret;
+ }
+
+ 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);
+
+ pcm512x->fmt = fmt;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm512x_dai_ops = {
+ .startup = pcm512x_dai_startup,
+ .hw_params = pcm512x_hw_params,
+ .set_fmt = pcm512x_set_fmt,
+};
+
+static struct snd_soc_dai_driver pcm512x_dai = {
+ .name = "pcm512x-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE
+ },
+ .ops = &pcm512x_dai_ops,
+};
+
+static 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 regmap_range_cfg pcm512x_range = {
+ .name = "Pages", .range_min = PCM512x_VIRT_BASE,
+ .range_max = PCM512x_MAX_REGISTER,
+ .selector_reg = PCM512x_PAGE,
+ .selector_mask = 0xff,
+ .window_start = 0, .window_len = 0x100,
+};
+
+const struct regmap_config pcm512x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = pcm512x_readable,
+ .volatile_reg = pcm512x_volatile,
+
+ .ranges = &pcm512x_range,
+ .num_ranges = 1,
+
+ .max_register = PCM512x_MAX_REGISTER,
+ .reg_defaults = pcm512x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm512x_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(pcm512x_regmap);
+
+int pcm512x_probe(struct device *dev, struct regmap *regmap)
+{
+ struct pcm512x_priv *pcm512x;
+ int i, ret;
+
+ pcm512x = devm_kzalloc(dev, sizeof(struct pcm512x_priv), GFP_KERNEL);
+ if (!pcm512x)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, pcm512x);
+ pcm512x->regmap = regmap;
+
+ for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++)
+ pcm512x->supplies[i].supply = pcm512x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to get supplies: %d\n", ret);
+ return ret;
+ }
+
+ pcm512x->supply_nb[0].notifier_call = pcm512x_regulator_event_0;
+ pcm512x->supply_nb[1].notifier_call = pcm512x_regulator_event_1;
+ pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2;
+
+ for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) {
+ ret = regulator_register_notifier(pcm512x->supplies[i].consumer,
+ &pcm512x->supply_nb[i]);
+ if (ret != 0) {
+ dev_err(dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the device, verifying I/O in the process for I2C */
+ ret = regmap_write(regmap, PCM512x_RESET,
+ PCM512x_RSTM | PCM512x_RSTR);
+ if (ret != 0) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_write(regmap, PCM512x_RESET, 0);
+ if (ret != 0) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err;
+ }
+
+ pcm512x->sclk = devm_clk_get(dev, NULL);
+ if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ 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;
+ }
+ }
+
+ /* Default to standby mode */
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQST, PCM512x_RQST);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request standby: %d\n",
+ ret);
+ goto err_clk;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+#ifdef CONFIG_OF
+ if (dev->of_node) {
+ const struct device_node *np = dev->of_node;
+ u32 val;
+
+ if (of_property_read_u32(np, "pll-in", &val) >= 0) {
+ if (val > 6) {
+ dev_err(dev, "Invalid pll-in\n");
+ ret = -EINVAL;
+ goto err_clk;
+ }
+ pcm512x->pll_in = val;
+ }
+
+ if (of_property_read_u32(np, "pll-out", &val) >= 0) {
+ if (val > 6) {
+ dev_err(dev, "Invalid pll-out\n");
+ ret = -EINVAL;
+ goto err_clk;
+ }
+ pcm512x->pll_out = val;
+ }
+
+ if (!pcm512x->pll_in != !pcm512x->pll_out) {
+ dev_err(dev,
+ "Error: both pll-in and pll-out, or none\n");
+ ret = -EINVAL;
+ goto err_clk;
+ }
+ if (pcm512x->pll_in && pcm512x->pll_in == pcm512x->pll_out) {
+ dev_err(dev, "Error: pll-in == pll-out\n");
+ ret = -EINVAL;
+ goto err_clk;
+ }
+ }
+#endif
+
+ ret = snd_soc_register_codec(dev, &pcm512x_codec_driver,
+ &pcm512x_dai, 1);
+ if (ret != 0) {
+ dev_err(dev, "Failed to register CODEC: %d\n", ret);
+ goto err_pm;
+ }
+
+ return 0;
+
+err_pm:
+ pm_runtime_disable(dev);
+err_clk:
+ if (!IS_ERR(pcm512x->sclk))
+ clk_disable_unprepare(pcm512x->sclk);
+err:
+ regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pcm512x_probe);
+
+void pcm512x_remove(struct device *dev)
+{
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
+
+ snd_soc_unregister_codec(dev);
+ pm_runtime_disable(dev);
+ if (!IS_ERR(pcm512x->sclk))
+ clk_disable_unprepare(pcm512x->sclk);
+ regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+}
+EXPORT_SYMBOL_GPL(pcm512x_remove);
+
+#ifdef CONFIG_PM
+static int pcm512x_suspend(struct device *dev)
+{
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQPD, PCM512x_RQPD);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request power down: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to disable supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (!IS_ERR(pcm512x->sclk))
+ clk_disable_unprepare(pcm512x->sclk);
+
+ return 0;
+}
+
+static int pcm512x_resume(struct device *dev)
+{
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
+ int ret;
+
+ if (!IS_ERR(pcm512x->sclk)) {
+ ret = clk_prepare_enable(pcm512x->sclk);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable SCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
+ pcm512x->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(pcm512x->regmap, false);
+ ret = regcache_sync(pcm512x->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+ PCM512x_RQPD, 0);
+ if (ret != 0) {
+ dev_err(dev, "Failed to remove power down: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+const struct dev_pm_ops pcm512x_pm_ops = {
+ SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(pcm512x_pm_ops);
+
+MODULE_DESCRIPTION("ASoC PCM512x codec driver");
+MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/pcm512x.h b/kernel/sound/soc/codecs/pcm512x.h
new file mode 100644
index 000000000..b7c310207
--- /dev/null
+++ b/kernel/sound/soc/codecs/pcm512x.h
@@ -0,0 +1,270 @@
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author: Mark Brown <broonie@linaro.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
+#define _SND_SOC_PCM512X
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+#define PCM512x_VIRT_BASE 0x100
+#define PCM512x_PAGE_LEN 0x100
+#define PCM512x_PAGE_BASE(n) (PCM512x_VIRT_BASE + (PCM512x_PAGE_LEN * n))
+
+#define PCM512x_PAGE 0
+
+#define PCM512x_RESET (PCM512x_PAGE_BASE(0) + 1)
+#define PCM512x_POWER (PCM512x_PAGE_BASE(0) + 2)
+#define PCM512x_MUTE (PCM512x_PAGE_BASE(0) + 3)
+#define PCM512x_PLL_EN (PCM512x_PAGE_BASE(0) + 4)
+#define PCM512x_SPI_MISO_FUNCTION (PCM512x_PAGE_BASE(0) + 6)
+#define PCM512x_DSP (PCM512x_PAGE_BASE(0) + 7)
+#define PCM512x_GPIO_EN (PCM512x_PAGE_BASE(0) + 8)
+#define PCM512x_BCLK_LRCLK_CFG (PCM512x_PAGE_BASE(0) + 9)
+#define PCM512x_DSP_GPIO_INPUT (PCM512x_PAGE_BASE(0) + 10)
+#define PCM512x_MASTER_MODE (PCM512x_PAGE_BASE(0) + 12)
+#define PCM512x_PLL_REF (PCM512x_PAGE_BASE(0) + 13)
+#define PCM512x_DAC_REF (PCM512x_PAGE_BASE(0) + 14)
+#define PCM512x_GPIO_DACIN (PCM512x_PAGE_BASE(0) + 16)
+#define PCM512x_GPIO_PLLIN (PCM512x_PAGE_BASE(0) + 18)
+#define PCM512x_SYNCHRONIZE (PCM512x_PAGE_BASE(0) + 19)
+#define PCM512x_PLL_COEFF_0 (PCM512x_PAGE_BASE(0) + 20)
+#define PCM512x_PLL_COEFF_1 (PCM512x_PAGE_BASE(0) + 21)
+#define PCM512x_PLL_COEFF_2 (PCM512x_PAGE_BASE(0) + 22)
+#define PCM512x_PLL_COEFF_3 (PCM512x_PAGE_BASE(0) + 23)
+#define PCM512x_PLL_COEFF_4 (PCM512x_PAGE_BASE(0) + 24)
+#define PCM512x_DSP_CLKDIV (PCM512x_PAGE_BASE(0) + 27)
+#define PCM512x_DAC_CLKDIV (PCM512x_PAGE_BASE(0) + 28)
+#define PCM512x_NCP_CLKDIV (PCM512x_PAGE_BASE(0) + 29)
+#define PCM512x_OSR_CLKDIV (PCM512x_PAGE_BASE(0) + 30)
+#define PCM512x_MASTER_CLKDIV_1 (PCM512x_PAGE_BASE(0) + 32)
+#define PCM512x_MASTER_CLKDIV_2 (PCM512x_PAGE_BASE(0) + 33)
+#define PCM512x_FS_SPEED_MODE (PCM512x_PAGE_BASE(0) + 34)
+#define PCM512x_IDAC_1 (PCM512x_PAGE_BASE(0) + 35)
+#define PCM512x_IDAC_2 (PCM512x_PAGE_BASE(0) + 36)
+#define PCM512x_ERROR_DETECT (PCM512x_PAGE_BASE(0) + 37)
+#define PCM512x_I2S_1 (PCM512x_PAGE_BASE(0) + 40)
+#define PCM512x_I2S_2 (PCM512x_PAGE_BASE(0) + 41)
+#define PCM512x_DAC_ROUTING (PCM512x_PAGE_BASE(0) + 42)
+#define PCM512x_DSP_PROGRAM (PCM512x_PAGE_BASE(0) + 43)
+#define PCM512x_CLKDET (PCM512x_PAGE_BASE(0) + 44)
+#define PCM512x_AUTO_MUTE (PCM512x_PAGE_BASE(0) + 59)
+#define PCM512x_DIGITAL_VOLUME_1 (PCM512x_PAGE_BASE(0) + 60)
+#define PCM512x_DIGITAL_VOLUME_2 (PCM512x_PAGE_BASE(0) + 61)
+#define PCM512x_DIGITAL_VOLUME_3 (PCM512x_PAGE_BASE(0) + 62)
+#define PCM512x_DIGITAL_MUTE_1 (PCM512x_PAGE_BASE(0) + 63)
+#define PCM512x_DIGITAL_MUTE_2 (PCM512x_PAGE_BASE(0) + 64)
+#define PCM512x_DIGITAL_MUTE_3 (PCM512x_PAGE_BASE(0) + 65)
+#define PCM512x_GPIO_OUTPUT_1 (PCM512x_PAGE_BASE(0) + 80)
+#define PCM512x_GPIO_OUTPUT_2 (PCM512x_PAGE_BASE(0) + 81)
+#define PCM512x_GPIO_OUTPUT_3 (PCM512x_PAGE_BASE(0) + 82)
+#define PCM512x_GPIO_OUTPUT_4 (PCM512x_PAGE_BASE(0) + 83)
+#define PCM512x_GPIO_OUTPUT_5 (PCM512x_PAGE_BASE(0) + 84)
+#define PCM512x_GPIO_OUTPUT_6 (PCM512x_PAGE_BASE(0) + 85)
+#define PCM512x_GPIO_CONTROL_1 (PCM512x_PAGE_BASE(0) + 86)
+#define PCM512x_GPIO_CONTROL_2 (PCM512x_PAGE_BASE(0) + 87)
+#define PCM512x_OVERFLOW (PCM512x_PAGE_BASE(0) + 90)
+#define PCM512x_RATE_DET_1 (PCM512x_PAGE_BASE(0) + 91)
+#define PCM512x_RATE_DET_2 (PCM512x_PAGE_BASE(0) + 92)
+#define PCM512x_RATE_DET_3 (PCM512x_PAGE_BASE(0) + 93)
+#define PCM512x_RATE_DET_4 (PCM512x_PAGE_BASE(0) + 94)
+#define PCM512x_CLOCK_STATUS (PCM512x_PAGE_BASE(0) + 95)
+#define PCM512x_ANALOG_MUTE_DET (PCM512x_PAGE_BASE(0) + 108)
+#define PCM512x_GPIN (PCM512x_PAGE_BASE(0) + 119)
+#define PCM512x_DIGITAL_MUTE_DET (PCM512x_PAGE_BASE(0) + 120)
+
+#define PCM512x_OUTPUT_AMPLITUDE (PCM512x_PAGE_BASE(1) + 1)
+#define PCM512x_ANALOG_GAIN_CTRL (PCM512x_PAGE_BASE(1) + 2)
+#define PCM512x_UNDERVOLTAGE_PROT (PCM512x_PAGE_BASE(1) + 5)
+#define PCM512x_ANALOG_MUTE_CTRL (PCM512x_PAGE_BASE(1) + 6)
+#define PCM512x_ANALOG_GAIN_BOOST (PCM512x_PAGE_BASE(1) + 7)
+#define PCM512x_VCOM_CTRL_1 (PCM512x_PAGE_BASE(1) + 8)
+#define PCM512x_VCOM_CTRL_2 (PCM512x_PAGE_BASE(1) + 9)
+
+#define PCM512x_CRAM_CTRL (PCM512x_PAGE_BASE(44) + 1)
+
+#define PCM512x_FLEX_A (PCM512x_PAGE_BASE(253) + 63)
+#define PCM512x_FLEX_B (PCM512x_PAGE_BASE(253) + 64)
+
+#define PCM512x_MAX_REGISTER (PCM512x_PAGE_BASE(253) + 64)
+
+/* Page 0, Register 1 - reset */
+#define PCM512x_RSTR (1 << 0)
+#define PCM512x_RSTM (1 << 4)
+
+/* Page 0, Register 2 - power */
+#define PCM512x_RQPD (1 << 0)
+#define PCM512x_RQPD_SHIFT 0
+#define PCM512x_RQST (1 << 4)
+#define PCM512x_RQST_SHIFT 4
+
+/* Page 0, Register 3 - mute */
+#define PCM512x_RQMR_SHIFT 0
+#define PCM512x_RQML_SHIFT 4
+
+/* Page 0, Register 4 - PLL */
+#define PCM512x_PLLE (1 << 0)
+#define PCM512x_PLLE_SHIFT 0
+#define PCM512x_PLCK (1 << 4)
+#define PCM512x_PLCK_SHIFT 4
+
+/* Page 0, Register 7 - DSP */
+#define PCM512x_SDSL (1 << 0)
+#define PCM512x_SDSL_SHIFT 0
+#define PCM512x_DEMP (1 << 4)
+#define PCM512x_DEMP_SHIFT 4
+
+/* Page 0, Register 8 - GPIO output enable */
+#define PCM512x_G1OE (1 << 0)
+#define PCM512x_G2OE (1 << 1)
+#define PCM512x_G3OE (1 << 2)
+#define PCM512x_G4OE (1 << 3)
+#define PCM512x_G5OE (1 << 4)
+#define PCM512x_G6OE (1 << 5)
+
+/* Page 0, Register 9 - BCK, LRCLK configuration */
+#define PCM512x_LRKO (1 << 0)
+#define PCM512x_LRKO_SHIFT 0
+#define PCM512x_BCKO (1 << 4)
+#define PCM512x_BCKO_SHIFT 4
+#define PCM512x_BCKP (1 << 5)
+#define PCM512x_BCKP_SHIFT 5
+
+/* Page 0, Register 12 - Master mode BCK, LRCLK reset */
+#define PCM512x_RLRK (1 << 0)
+#define PCM512x_RLRK_SHIFT 0
+#define PCM512x_RBCK (1 << 1)
+#define PCM512x_RBCK_SHIFT 1
+
+/* Page 0, Register 13 - PLL reference */
+#define PCM512x_SREF (7 << 4)
+#define PCM512x_SREF_SHIFT 4
+#define PCM512x_SREF_SCK (0 << 4)
+#define PCM512x_SREF_BCK (1 << 4)
+#define PCM512x_SREF_GPIO (3 << 4)
+
+/* Page 0, Register 14 - DAC reference */
+#define PCM512x_SDAC (7 << 4)
+#define PCM512x_SDAC_SHIFT 4
+#define PCM512x_SDAC_MCK (0 << 4)
+#define PCM512x_SDAC_PLL (1 << 4)
+#define PCM512x_SDAC_SCK (3 << 4)
+#define PCM512x_SDAC_BCK (4 << 4)
+#define PCM512x_SDAC_GPIO (5 << 4)
+
+/* Page 0, Register 16, 18 - GPIO source for DAC, PLL */
+#define PCM512x_GREF (7 << 0)
+#define PCM512x_GREF_SHIFT 0
+#define PCM512x_GREF_GPIO1 (0 << 0)
+#define PCM512x_GREF_GPIO2 (1 << 0)
+#define PCM512x_GREF_GPIO3 (2 << 0)
+#define PCM512x_GREF_GPIO4 (3 << 0)
+#define PCM512x_GREF_GPIO5 (4 << 0)
+#define PCM512x_GREF_GPIO6 (5 << 0)
+
+/* Page 0, Register 19 - synchronize */
+#define PCM512x_RQSY (1 << 0)
+#define PCM512x_RQSY_RESUME (0 << 0)
+#define PCM512x_RQSY_HALT (1 << 0)
+
+/* Page 0, Register 34 - fs speed mode */
+#define PCM512x_FSSP (3 << 0)
+#define PCM512x_FSSP_SHIFT 0
+#define PCM512x_FSSP_48KHZ (0 << 0)
+#define PCM512x_FSSP_96KHZ (1 << 0)
+#define PCM512x_FSSP_192KHZ (2 << 0)
+#define PCM512x_FSSP_384KHZ (3 << 0)
+
+/* Page 0, Register 37 - Error detection */
+#define PCM512x_IPLK (1 << 0)
+#define PCM512x_DCAS (1 << 1)
+#define PCM512x_IDCM (1 << 2)
+#define PCM512x_IDCH (1 << 3)
+#define PCM512x_IDSK (1 << 4)
+#define PCM512x_IDBK (1 << 5)
+#define PCM512x_IDFS (1 << 6)
+
+/* Page 0, Register 40 - I2S configuration */
+#define PCM512x_ALEN (3 << 0)
+#define PCM512x_ALEN_SHIFT 0
+#define PCM512x_ALEN_16 (0 << 0)
+#define PCM512x_ALEN_20 (1 << 0)
+#define PCM512x_ALEN_24 (2 << 0)
+#define PCM512x_ALEN_32 (3 << 0)
+#define PCM512x_AFMT (3 << 4)
+#define PCM512x_AFMT_SHIFT 4
+#define PCM512x_AFMT_I2S (0 << 4)
+#define PCM512x_AFMT_DSP (1 << 4)
+#define PCM512x_AFMT_RTJ (2 << 4)
+#define PCM512x_AFMT_LTJ (3 << 4)
+
+/* Page 0, Register 42 - DAC routing */
+#define PCM512x_AUPR_SHIFT 0
+#define PCM512x_AUPL_SHIFT 4
+
+/* Page 0, Register 59 - auto mute */
+#define PCM512x_ATMR_SHIFT 0
+#define PCM512x_ATML_SHIFT 4
+
+/* Page 0, Register 63 - ramp rates */
+#define PCM512x_VNDF_SHIFT 6
+#define PCM512x_VNDS_SHIFT 4
+#define PCM512x_VNUF_SHIFT 2
+#define PCM512x_VNUS_SHIFT 0
+
+/* Page 0, Register 64 - emergency ramp rates */
+#define PCM512x_VEDF_SHIFT 6
+#define PCM512x_VEDS_SHIFT 4
+
+/* Page 0, Register 65 - Digital mute enables */
+#define PCM512x_ACTL_SHIFT 2
+#define PCM512x_AMLE_SHIFT 1
+#define PCM512x_AMRE_SHIFT 0
+
+/* Page 0, Register 80-85, GPIO output selection */
+#define PCM512x_GxSL (31 << 0)
+#define PCM512x_GxSL_SHIFT 0
+#define PCM512x_GxSL_OFF (0 << 0)
+#define PCM512x_GxSL_DSP (1 << 0)
+#define PCM512x_GxSL_REG (2 << 0)
+#define PCM512x_GxSL_AMUTB (3 << 0)
+#define PCM512x_GxSL_AMUTL (4 << 0)
+#define PCM512x_GxSL_AMUTR (5 << 0)
+#define PCM512x_GxSL_CLKI (6 << 0)
+#define PCM512x_GxSL_SDOUT (7 << 0)
+#define PCM512x_GxSL_ANMUL (8 << 0)
+#define PCM512x_GxSL_ANMUR (9 << 0)
+#define PCM512x_GxSL_PLLLK (10 << 0)
+#define PCM512x_GxSL_CPCLK (11 << 0)
+#define PCM512x_GxSL_UV0_7 (14 << 0)
+#define PCM512x_GxSL_UV0_3 (15 << 0)
+#define PCM512x_GxSL_PLLCK (16 << 0)
+
+/* Page 1, Register 2 - analog volume control */
+#define PCM512x_RAGN_SHIFT 0
+#define PCM512x_LAGN_SHIFT 4
+
+/* Page 1, Register 7 - analog boost control */
+#define PCM512x_AGBR_SHIFT 0
+#define PCM512x_AGBL_SHIFT 4
+
+extern const struct dev_pm_ops pcm512x_pm_ops;
+extern const struct regmap_config pcm512x_regmap;
+
+int pcm512x_probe(struct device *dev, struct regmap *regmap);
+void pcm512x_remove(struct device *dev);
+
+#endif
diff --git a/kernel/sound/soc/codecs/rl6231.c b/kernel/sound/soc/codecs/rl6231.c
new file mode 100644
index 000000000..56650d6c2
--- /dev/null
+++ b/kernel/sound/soc/codecs/rl6231.c
@@ -0,0 +1,133 @@
+/*
+ * 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 "rl6231.h"
+
+/**
+ * rl6231_calc_dmic_clk - Calculate the parameter of dmic.
+ *
+ * @rate: base clock rate.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+int rl6231_calc_dmic_clk(int rate)
+{
+ int div[] = {2, 3, 4, 6, 8, 12}, idx = -EINVAL;
+ int i, red, bound, temp;
+
+ red = 3000000 * 12;
+ for (i = 0; i < ARRAY_SIZE(div); i++) {
+ bound = div[i] * 3000000;
+ if (rate > bound)
+ continue;
+ temp = bound - rate;
+ if (temp < red) {
+ red = temp;
+ idx = i;
+ }
+ }
+
+ return idx;
+}
+EXPORT_SYMBOL_GPL(rl6231_calc_dmic_clk);
+
+/**
+ * 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.
+ *
+ * Calcualte M/N/K code to configure PLL for codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+int rl6231_pll_calc(const unsigned int freq_in,
+ const unsigned int freq_out, struct rl6231_pll_code *pll_code)
+{
+ int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX;
+ int 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;
+
+ 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;
+ goto code_find;
+ }
+ red = abs(in_t - pll_out);
+ if (red < red_t) {
+ 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) {
+ 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 = bypass;
+ pll_code->m_code = m;
+ pll_code->n_code = n;
+ pll_code->k_code = k;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rl6231_pll_calc);
+
+int rl6231_get_clk_info(int sclk, int rate)
+{
+ int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(rl6231_get_clk_info);
+
+MODULE_DESCRIPTION("RL6231 class device shared support");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/rl6231.h b/kernel/sound/soc/codecs/rl6231.h
new file mode 100644
index 000000000..0f7b057ed
--- /dev/null
+++ b/kernel/sound/soc/codecs/rl6231.h
@@ -0,0 +1,34 @@
+/*
+ * 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_MIN 256000
+#define RL6231_PLL_N_MAX 0x1ff
+#define RL6231_PLL_K_MAX 0x1f
+#define RL6231_PLL_M_MAX 0xf
+
+struct rl6231_pll_code {
+ bool m_bp; /* Indicates bypass m code or not. */
+ int m_code;
+ int n_code;
+ int k_code;
+};
+
+int rl6231_calc_dmic_clk(int rate);
+int rl6231_pll_calc(const unsigned int freq_in,
+ const unsigned int freq_out, struct rl6231_pll_code *pll_code);
+int rl6231_get_clk_info(int sclk, int rate);
+
+#endif /* __RL6231_H__ */
diff --git a/kernel/sound/soc/codecs/rt286.c b/kernel/sound/soc/codecs/rt286.c
new file mode 100644
index 000000000..0fcda35a3
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt286.c
@@ -0,0 +1,1358 @@
+/*
+ * 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>
+#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/rt286.h>
+#include <sound/hda_verbs.h>
+
+#include "rt286.h"
+
+#define RT286_VENDOR_ID 0x10ec0286
+#define RT288_VENDOR_ID 0x10ec0288
+
+struct rt286_priv {
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ 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[] = {
+ { 0x01, 0xaaaa },
+ { 0x02, 0x8aaa },
+ { 0x03, 0x0002 },
+ { 0x04, 0xaf01 },
+ { 0x08, 0x000d },
+ { 0x09, 0xd810 },
+ { 0x0a, 0x0120 },
+ { 0x0b, 0x0000 },
+ { 0x0d, 0x2800 },
+ { 0x0f, 0x0000 },
+ { 0x19, 0x0a17 },
+ { 0x20, 0x0020 },
+ { 0x33, 0x0208 },
+ { 0x49, 0x0004 },
+ { 0x4f, 0x50e9 },
+ { 0x50, 0x2000 },
+ { 0x63, 0x2902 },
+ { 0x67, 0x1111 },
+ { 0x68, 0x1016 },
+ { 0x69, 0x273f },
+};
+#define INDEX_CACHE_SIZE ARRAY_SIZE(rt286_index_def)
+
+static const struct reg_default rt286_reg[] = {
+ { 0x00170500, 0x00000400 },
+ { 0x00220000, 0x00000031 },
+ { 0x00239000, 0x0000007f },
+ { 0x0023a000, 0x0000007f },
+ { 0x00270500, 0x00000400 },
+ { 0x00370500, 0x00000400 },
+ { 0x00870500, 0x00000400 },
+ { 0x00920000, 0x00000031 },
+ { 0x00935000, 0x000000c3 },
+ { 0x00936000, 0x000000c3 },
+ { 0x00970500, 0x00000400 },
+ { 0x00b37000, 0x00000097 },
+ { 0x00b37200, 0x00000097 },
+ { 0x00b37300, 0x00000097 },
+ { 0x00c37000, 0x00000000 },
+ { 0x00c37100, 0x00000080 },
+ { 0x01270500, 0x00000400 },
+ { 0x01370500, 0x00000400 },
+ { 0x01371f00, 0x411111f0 },
+ { 0x01439000, 0x00000080 },
+ { 0x0143a000, 0x00000080 },
+ { 0x01470700, 0x00000000 },
+ { 0x01470500, 0x00000400 },
+ { 0x01470c00, 0x00000000 },
+ { 0x01470100, 0x00000000 },
+ { 0x01837000, 0x00000000 },
+ { 0x01870500, 0x00000400 },
+ { 0x02050000, 0x00000000 },
+ { 0x02139000, 0x00000080 },
+ { 0x0213a000, 0x00000080 },
+ { 0x02170100, 0x00000000 },
+ { 0x02170500, 0x00000400 },
+ { 0x02170700, 0x00000000 },
+ { 0x02270100, 0x00000000 },
+ { 0x02370100, 0x00000000 },
+ { 0x01870700, 0x00000020 },
+ { 0x00830000, 0x000000c3 },
+ { 0x00930000, 0x000000c3 },
+ { 0x01270700, 0x00000000 },
+};
+
+static bool rt286_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT286_GET_HP_SENSE:
+ case RT286_GET_MIC1_SENSE:
+ case RT286_PROC_COEF:
+ return true;
+ default:
+ return false;
+ }
+
+
+}
+
+static bool rt286_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT286_GET_HP_SENSE:
+ case RT286_GET_MIC1_SENSE:
+ case RT286_SET_AUDIO_POWER:
+ case RT286_SET_HPO_POWER:
+ case RT286_SET_SPK_POWER:
+ case RT286_SET_DMIC1_POWER:
+ case RT286_SPK_MUX:
+ case RT286_HPO_MUX:
+ case RT286_ADC0_MUX:
+ case RT286_ADC1_MUX:
+ case RT286_SET_MIC1:
+ case RT286_SET_PIN_HPO:
+ case RT286_SET_PIN_SPK:
+ case RT286_SET_PIN_DMIC1:
+ case RT286_SPK_EAPD:
+ case RT286_SET_AMP_GAIN_HPO:
+ case RT286_SET_DMIC2_DEFAULT:
+ case RT286_DACL_GAIN:
+ case RT286_DACR_GAIN:
+ case RT286_ADCL_GAIN:
+ case RT286_ADCR_GAIN:
+ case RT286_MIC_GAIN:
+ case RT286_SPOL_GAIN:
+ case RT286_SPOR_GAIN:
+ case RT286_HPOL_GAIN:
+ case RT286_HPOR_GAIN:
+ case RT286_F_DAC_SWITCH:
+ case RT286_F_RECMIX_SWITCH:
+ case RT286_REC_MIC_SWITCH:
+ case RT286_REC_I2S_SWITCH:
+ case RT286_REC_LINE_SWITCH:
+ case RT286_REC_BEEP_SWITCH:
+ case RT286_DAC_FORMAT:
+ case RT286_ADC_FORMAT:
+ case RT286_COEF_INDEX:
+ case RT286_PROC_COEF:
+ case RT286_SET_AMP_GAIN_ADC_IN1:
+ case RT286_SET_AMP_GAIN_ADC_IN2:
+ case RT286_SET_POWER(RT286_DAC_OUT1):
+ case RT286_SET_POWER(RT286_DAC_OUT2):
+ case RT286_SET_POWER(RT286_ADC_IN1):
+ case RT286_SET_POWER(RT286_ADC_IN2):
+ case RT286_SET_POWER(RT286_DMIC2):
+ case RT286_SET_POWER(RT286_MIC1):
+ return true;
+ default:
+ return false;
+ }
+}
+
+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)
+{
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ for (i = 0; i < INDEX_CACHE_SIZE; i++) {
+ snd_soc_write(codec, rt286->index_cache[i].reg,
+ rt286->index_cache[i].def);
+ }
+}
+#endif
+
+static int rt286_support_power_controls[] = {
+ RT286_DAC_OUT1,
+ RT286_DAC_OUT2,
+ RT286_ADC_IN1,
+ RT286_ADC_IN2,
+ RT286_MIC1,
+ RT286_DMIC1,
+ RT286_DMIC2,
+ RT286_SPK_OUT,
+ RT286_HP_OUT,
+};
+#define RT286_POWER_REG_LEN ARRAY_SIZE(rt286_support_power_controls)
+
+static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
+{
+ unsigned int val, buf;
+
+ *hp = false;
+ *mic = false;
+
+ if (!rt286->codec)
+ return -EINVAL;
+ if (rt286->pdata.cbj_en) {
+ regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
+ *hp = buf & 0x80000000;
+ if (*hp) {
+ /* power on HV,VERF */
+ regmap_update_bits(rt286->regmap,
+ RT286_DC_GAIN, 0x200, 0x200);
+
+ snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
+ "HV");
+ snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
+ "VREF");
+ /* power LDO1 */
+ snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
+ "LDO1");
+ snd_soc_dapm_sync(&rt286->codec->dapm);
+
+ regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
+ msleep(50);
+
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1, 0xfcc0, 0xd400);
+ msleep(300);
+ regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val);
+
+ if (0x0070 == (val & 0x0070)) {
+ *mic = true;
+ } else {
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1, 0xfcc0, 0xe400);
+ msleep(300);
+ regmap_read(rt286->regmap,
+ RT286_CBJ_CTRL2, &val);
+ if (0x0070 == (val & 0x0070))
+ *mic = true;
+ else
+ *mic = false;
+ }
+ regmap_update_bits(rt286->regmap,
+ RT286_DC_GAIN, 0x200, 0x0);
+
+ } else {
+ *mic = false;
+ regmap_write(rt286->regmap, RT286_SET_MIC1, 0x20);
+ }
+ } else {
+ regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
+ *hp = buf & 0x80000000;
+ regmap_read(rt286->regmap, RT286_GET_MIC1_SENSE, &buf);
+ *mic = buf & 0x80000000;
+ }
+
+ 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);
+
+ return 0;
+}
+
+static void rt286_jack_detect_work(struct work_struct *work)
+{
+ struct rt286_priv *rt286 =
+ container_of(work, struct rt286_priv, jack_detect_work.work);
+ int status = 0;
+ bool hp = false;
+ bool mic = false;
+
+ rt286_jack_detect(rt286, &hp, &mic);
+
+ if (hp == true)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic == true)
+ 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)
+{
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+ rt286->jack = jack;
+
+ if (jack) {
+ /* enable IRQ */
+ if (rt286->jack->status & SND_JACK_HEADPHONE)
+ snd_soc_dapm_force_enable_pin(&codec->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(&codec->dapm, "LDO1");
+ }
+ snd_soc_dapm_sync(&codec->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);
+
+ if (rt286->clk_id == RT286_SCLK_S_MCLK)
+ return 1;
+ else
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt286_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT286_DACL_GAIN,
+ RT286_DACR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_DOUBLE_R("ADC0 Capture Switch", RT286_ADCL_GAIN,
+ RT286_ADCR_GAIN, 7, 1, 1),
+ SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT286_ADCL_GAIN,
+ RT286_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_SINGLE_TLV("AMIC Volume", RT286_MIC_GAIN,
+ 0, 0x3, 0, mic_vol_tlv),
+ SOC_DOUBLE_R("Speaker Playback Switch", RT286_SPOL_GAIN,
+ RT286_SPOR_GAIN, RT286_MUTE_SFT, 1, 1),
+};
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt286_front_mix[] = {
+ SOC_DAPM_SINGLE("DAC Switch", RT286_F_DAC_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIX Switch", RT286_F_RECMIX_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt286_rec_mix[] = {
+ SOC_DAPM_SINGLE("Mic1 Switch", RT286_REC_MIC_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("I2S Switch", RT286_REC_I2S_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("Line1 Switch", RT286_REC_LINE_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("Beep Switch", RT286_REC_BEEP_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new spo_enable_control =
+ SOC_DAPM_SINGLE("Switch", RT286_SET_PIN_SPK,
+ RT286_SET_PIN_SFT, 1, 0);
+
+static const struct snd_kcontrol_new hpol_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOL_GAIN,
+ RT286_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpor_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOR_GAIN,
+ RT286_MUTE_SFT, 1, 1);
+
+/* ADC0 source */
+static const char * const rt286_adc_src[] = {
+ "Mic", "RECMIX", "Dmic"
+};
+
+static const int rt286_adc_values[] = {
+ 0, 4, 5,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(
+ rt286_adc0_enum, RT286_ADC0_MUX, RT286_ADC_SEL_SFT,
+ RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values);
+
+static const struct snd_kcontrol_new rt286_adc0_mux =
+ SOC_DAPM_ENUM("ADC 0 source", rt286_adc0_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(
+ rt286_adc1_enum, RT286_ADC1_MUX, RT286_ADC_SEL_SFT,
+ RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values);
+
+static const struct snd_kcontrol_new rt286_adc1_mux =
+ SOC_DAPM_ENUM("ADC 1 source", rt286_adc1_enum);
+
+static const char * const rt286_dac_src[] = {
+ "Front", "Surround"
+};
+/* HP-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt286_hpo_enum, RT286_HPO_MUX,
+ 0, rt286_dac_src);
+
+static const struct snd_kcontrol_new rt286_hpo_mux =
+SOC_DAPM_ENUM("HPO source", rt286_hpo_enum);
+
+/* SPK-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt286_spo_enum, RT286_SPK_MUX,
+ 0, rt286_dac_src);
+
+static const struct snd_kcontrol_new rt286_spo_mux =
+SOC_DAPM_ENUM("SPO source", rt286_spo_enum);
+
+static int rt286_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_write(codec,
+ RT286_SPK_EAPD, RT286_SET_EAPD_HIGH);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_write(codec,
+ RT286_SPK_EAPD, RT286_SET_EAPD_LOW);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_write(codec, 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);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x08);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x30);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt286_mic1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec,
+ RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
+ snd_soc_update_bits(codec,
+ RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec,
+ RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
+ snd_soc_update_bits(codec,
+ RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY_S("HV", 1, RT286_POWER_CTRL1,
+ 12, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF", RT286_POWER_CTRL1,
+ 0, 1, rt286_vref_event, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT286_POWER_CTRL2,
+ 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("LDO2", 2, RT286_POWER_CTRL1,
+ 13, 1, rt286_ldo2_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("MCLK MODE", RT286_PLL_CTRL1,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC1 Input Buffer", SND_SOC_NOPM,
+ 0, 0, rt286_mic1_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC2 Pin"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("Beep"),
+
+ /* DMIC */
+ SND_SOC_DAPM_PGA_E("DMIC1", RT286_SET_POWER(RT286_DMIC1), 0, 1,
+ NULL, 0, rt286_set_dmic1_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA("DMIC2", RT286_SET_POWER(RT286_DMIC2), 0, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC Receiver", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX", SND_SOC_NOPM, 0, 0,
+ rt286_rec_mix, ARRAY_SIZE(rt286_rec_mix)),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC 0", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("ADC 0 Mux", RT286_SET_POWER(RT286_ADC_IN1), 0, 1,
+ &rt286_adc0_mux),
+ SND_SOC_DAPM_MUX("ADC 1 Mux", RT286_SET_POWER(RT286_ADC_IN2), 0, 1,
+ &rt286_adc1_mux),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC 0", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC 1", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Output Mux */
+ SND_SOC_DAPM_MUX("SPK Mux", SND_SOC_NOPM, 0, 0, &rt286_spo_mux),
+ SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt286_hpo_mux),
+
+ SND_SOC_DAPM_SUPPLY("HP Power", RT286_SET_PIN_HPO,
+ RT286_SET_PIN_SFT, 0, NULL, 0),
+
+ /* Output Mixer */
+ SND_SOC_DAPM_MIXER("Front", RT286_SET_POWER(RT286_DAC_OUT1), 0, 1,
+ rt286_front_mix, ARRAY_SIZE(rt286_front_mix)),
+ SND_SOC_DAPM_PGA("Surround", RT286_SET_POWER(RT286_DAC_OUT2), 0, 1,
+ NULL, 0),
+
+ /* Output Pga */
+ SND_SOC_DAPM_SWITCH_E("SPO", SND_SOC_NOPM, 0, 0,
+ &spo_enable_control, rt286_spk_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0,
+ &hpol_enable_control),
+ SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0,
+ &hpor_enable_control),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+ SND_SOC_DAPM_OUTPUT("HPO Pin"),
+ SND_SOC_DAPM_OUTPUT("SPDIF"),
+};
+
+static const struct snd_soc_dapm_route rt286_dapm_routes[] = {
+ {"ADC 0", NULL, "MCLK MODE", is_mclk_mode},
+ {"ADC 1", NULL, "MCLK MODE", is_mclk_mode},
+ {"Front", NULL, "MCLK MODE", is_mclk_mode},
+ {"Surround", NULL, "MCLK MODE", is_mclk_mode},
+
+ {"HP Power", NULL, "LDO1"},
+ {"HP Power", NULL, "LDO2"},
+
+ {"MIC1", NULL, "LDO1"},
+ {"MIC1", NULL, "LDO2"},
+ {"MIC1", NULL, "HV"},
+ {"MIC1", NULL, "VREF"},
+ {"MIC1", NULL, "MIC1 Input Buffer"},
+
+ {"SPO", NULL, "LDO1"},
+ {"SPO", NULL, "LDO2"},
+ {"SPO", NULL, "HV"},
+ {"SPO", NULL, "VREF"},
+
+ {"DMIC1", NULL, "DMIC1 Pin"},
+ {"DMIC2", NULL, "DMIC2 Pin"},
+ {"DMIC1", NULL, "DMIC Receiver"},
+ {"DMIC2", NULL, "DMIC Receiver"},
+
+ {"RECMIX", "Beep Switch", "Beep"},
+ {"RECMIX", "Line1 Switch", "LINE1"},
+ {"RECMIX", "Mic1 Switch", "MIC1"},
+
+ {"ADC 0 Mux", "Dmic", "DMIC1"},
+ {"ADC 0 Mux", "RECMIX", "RECMIX"},
+ {"ADC 0 Mux", "Mic", "MIC1"},
+ {"ADC 1 Mux", "Dmic", "DMIC2"},
+ {"ADC 1 Mux", "RECMIX", "RECMIX"},
+ {"ADC 1 Mux", "Mic", "MIC1"},
+
+ {"ADC 0", NULL, "ADC 0 Mux"},
+ {"ADC 1", NULL, "ADC 1 Mux"},
+
+ {"AIF1TX", NULL, "ADC 0"},
+ {"AIF2TX", NULL, "ADC 1"},
+
+ {"DAC 0", NULL, "AIF1RX"},
+ {"DAC 1", NULL, "AIF2RX"},
+
+ {"Front", "DAC Switch", "DAC 0"},
+ {"Front", "RECMIX Switch", "RECMIX"},
+
+ {"Surround", NULL, "DAC 1"},
+
+ {"SPK Mux", "Front", "Front"},
+ {"SPK Mux", "Surround", "Surround"},
+
+ {"HPO Mux", "Front", "Front"},
+ {"HPO Mux", "Surround", "Surround"},
+
+ {"SPO", "Switch", "SPK Mux"},
+ {"HPO L", "Switch", "HPO Mux"},
+ {"HPO R", "Switch", "HPO Mux"},
+ {"HPO L", NULL, "HP Power"},
+ {"HPO R", NULL, "HP Power"},
+
+ {"SPOL", NULL, "SPO"},
+ {"SPOR", NULL, "SPO"},
+ {"HPO Pin", NULL, "HPO L"},
+ {"HPO Pin", NULL, "HPO R"},
+};
+
+static int rt286_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = 0;
+ int d_len_code;
+
+ switch (params_rate(params)) {
+ /* bit 14 0:48K 1:44.1K */
+ case 44100:
+ val |= 0x4000;
+ break;
+ case 48000:
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported sample rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ switch (rt286->sys_clk) {
+ case 12288000:
+ case 24576000:
+ if (params_rate(params) != 48000) {
+ dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt286->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ case 11289600:
+ case 22579200:
+ if (params_rate(params) != 44100) {
+ dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt286->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(codec->dev, "Unsupported channels %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ d_len_code = 0;
+ switch (params_width(params)) {
+ /* bit 6:4 Bits per Sample */
+ case 16:
+ d_len_code = 0;
+ val |= (0x1 << 4);
+ break;
+ case 32:
+ d_len_code = 2;
+ val |= (0x4 << 4);
+ break;
+ case 20:
+ d_len_code = 1;
+ val |= (0x2 << 4);
+ break;
+ case 24:
+ d_len_code = 2;
+ val |= (0x3 << 4);
+ break;
+ case 8:
+ d_len_code = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x0018, d_len_code << 3);
+ dev_dbg(codec->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);
+
+ return 0;
+}
+
+static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x800, 0x800);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x800, 0x0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x300, 0x0);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x300, 0x1 << 8);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x300, 0x2 << 8);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ snd_soc_update_bits(codec,
+ 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);
+
+ return 0;
+}
+
+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);
+
+ dev_dbg(codec->dev, "%s freq=%d\n", __func__, freq);
+
+ if (RT286_SCLK_S_MCLK == clk_id) {
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL2, 0x0100, 0x0);
+ snd_soc_update_bits(codec,
+ RT286_PLL_CTRL1, 0x20, 0x20);
+ } else {
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL2, 0x0100, 0x0100);
+ snd_soc_update_bits(codec,
+ RT286_PLL_CTRL, 0x4, 0x4);
+ snd_soc_update_bits(codec,
+ 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");
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL2, 0x40, 0x40);
+ break;
+ case 24000000:
+ if (RT286_SCLK_S_MCLK == clk_id) {
+ dev_err(codec->dev, "Should not use MCLK\n");
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL2, 0x40, 0x0);
+ break;
+ case 12288000:
+ case 11289600:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL2, 0x8, 0x0);
+ snd_soc_update_bits(codec,
+ RT286_CLK_DIV, 0xfc1e, 0x0004);
+ break;
+ case 24576000:
+ case 22579200:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL2, 0x8, 0x8);
+ snd_soc_update_bits(codec,
+ RT286_CLK_DIV, 0xfc1e, 0x5406);
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported system clock\n");
+ return -EINVAL;
+ }
+
+ rt286->sys_clk = freq;
+ rt286->clk_id = clk_id;
+
+ return 0;
+}
+
+static int rt286_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+ if (50 == ratio)
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x1000, 0x1000);
+ else
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x1000, 0x0);
+
+
+ return 0;
+}
+
+static int rt286_set_bias_level(struct snd_soc_codec *codec,
+ 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,
+ RT286_SET_AUDIO_POWER, AC_PWRST_D0);
+ snd_soc_update_bits(codec,
+ 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,
+ RT286_DC_GAIN, 0x200, 0x0);
+
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_write(codec,
+ 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;
+}
+
+static irqreturn_t rt286_irq(int irq, void *data)
+{
+ struct rt286_priv *rt286 = data;
+ bool hp = false;
+ bool mic = false;
+ int status = 0;
+
+ rt286_jack_detect(rt286, &hp, &mic);
+
+ /* Clear IRQ */
+ regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x1, 0x1);
+
+ if (hp == true)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic == true)
+ status |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(rt286->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+
+ pm_wakeup_event(&rt286->i2c->dev, 300);
+
+ return IRQ_HANDLED;
+}
+
+static int rt286_probe(struct snd_soc_codec *codec)
+{
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+ rt286->codec = codec;
+
+ 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);
+ schedule_delayed_work(&rt286->jack_detect_work,
+ msecs_to_jiffies(1250));
+ }
+
+ return 0;
+}
+
+static int rt286_remove(struct snd_soc_codec *codec)
+{
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+ cancel_delayed_work_sync(&rt286->jack_detect_work);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt286_suspend(struct snd_soc_codec *codec)
+{
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt286->regmap, true);
+ regcache_mark_dirty(rt286->regmap);
+
+ return 0;
+}
+
+static int rt286_resume(struct snd_soc_codec *codec)
+{
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt286->regmap, false);
+ rt286_index_sync(codec);
+ regcache_sync(rt286->regmap);
+
+ return 0;
+}
+#else
+#define rt286_suspend NULL
+#define rt286_resume NULL
+#endif
+
+#define RT286_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT286_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt286_aif_dai_ops = {
+ .hw_params = rt286_hw_params,
+ .set_fmt = rt286_set_dai_fmt,
+ .set_sysclk = rt286_set_dai_sysclk,
+ .set_bclk_ratio = rt286_set_bclk_ratio,
+};
+
+static struct snd_soc_dai_driver rt286_dai[] = {
+ {
+ .name = "rt286-aif1",
+ .id = RT286_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT286_STEREO_RATES,
+ .formats = RT286_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT286_STEREO_RATES,
+ .formats = RT286_FORMATS,
+ },
+ .ops = &rt286_aif_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "rt286-aif2",
+ .id = RT286_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT286_STEREO_RATES,
+ .formats = RT286_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT286_STEREO_RATES,
+ .formats = RT286_FORMATS,
+ },
+ .ops = &rt286_aif_dai_ops,
+ .symmetric_rates = 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 regmap_config rt286_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .max_register = 0x02370100,
+ .volatile_reg = rt286_volatile_register,
+ .readable_reg = rt286_readable_register,
+ .reg_write = rt286_hw_write,
+ .reg_read = rt286_hw_read,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt286_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt286_reg),
+};
+
+static const struct i2c_device_id rt286_i2c_id[] = {
+ {"rt286", 0},
+ {"rt288", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt286_i2c_id);
+
+static const struct acpi_device_id rt286_acpi_match[] = {
+ { "INT343A", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
+
+static struct dmi_system_id force_combo_jack_table[] = {
+ {
+ .ident = "Intel Wilson Beach",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "Wilson Beach SDS")
+ }
+ },
+ { }
+};
+
+static struct dmi_system_id dmi_dell_dino[] = {
+ {
+ .ident = "Dell Dino",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343")
+ }
+ },
+ { }
+};
+
+static int rt286_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt286_priv *rt286;
+ int i, ret, val;
+
+ rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286),
+ GFP_KERNEL);
+ if (NULL == rt286)
+ return -ENOMEM;
+
+ rt286->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt286_regmap);
+ if (IS_ERR(rt286->regmap)) {
+ ret = PTR_ERR(rt286->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regmap_read(rt286->regmap,
+ RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "I2C error %d\n", ret);
+ return ret;
+ }
+ if (val != RT286_VENDOR_ID && val != RT288_VENDOR_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt286\n", val);
+ return -ENODEV;
+ }
+
+ rt286->index_cache = rt286_index_def;
+ 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))
+ rt286->pdata.cbj_en = true;
+
+ regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3);
+
+ for (i = 0; i < RT286_POWER_REG_LEN; i++)
+ regmap_write(rt286->regmap,
+ RT286_SET_POWER(rt286_support_power_controls[i]),
+ AC_PWRST_D1);
+
+ if (!rt286->pdata.cbj_en) {
+ regmap_write(rt286->regmap, RT286_CBJ_CTRL2, 0x0000);
+ regmap_write(rt286->regmap, RT286_MIC1_DET_CTRL, 0x0816);
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1, 0xf000, 0xb000);
+ } else {
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1, 0xf000, 0x5000);
+ }
+
+ mdelay(10);
+
+ if (!rt286->pdata.gpio2_en)
+ regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0x4000);
+ else
+ regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0);
+
+ mdelay(10);
+
+ regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
+ /* Power down LDO, VREF */
+ regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0xc, 0x0);
+ regmap_update_bits(rt286->regmap, RT286_POWER_CTRL1, 0x1001, 0x1001);
+
+ /* Set depop parameter */
+ regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL2, 0x403a, 0x401a);
+ regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
+ regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
+
+ if (dmi_check_system(dmi_dell_dino)) {
+ regmap_update_bits(rt286->regmap,
+ RT286_SET_GPIO_MASK, 0x40, 0x40);
+ regmap_update_bits(rt286->regmap,
+ RT286_SET_GPIO_DIRECTION, 0x40, 0x40);
+ regmap_update_bits(rt286->regmap,
+ RT286_SET_GPIO_DATA, 0x40, 0x40);
+ regmap_update_bits(rt286->regmap,
+ RT286_GPIO_CTRL, 0xc, 0x8);
+ }
+
+ if (rt286->i2c->irq) {
+ ret = request_threaded_irq(rt286->i2c->irq, NULL, rt286_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286);
+ if (ret != 0) {
+ dev_err(&i2c->dev,
+ "Failed to reguest IRQ: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt286,
+ rt286_dai, ARRAY_SIZE(rt286_dai));
+
+ return ret;
+}
+
+static int 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,
+ .remove = rt286_i2c_remove,
+ .id_table = rt286_i2c_id,
+};
+
+module_i2c_driver(rt286_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT286 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/rt286.h b/kernel/sound/soc/codecs/rt286.h
new file mode 100644
index 000000000..7130edb15
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt286.h
@@ -0,0 +1,205 @@
+/*
+ * 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__
+#define __RT286_H__
+
+#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
+
+#define RT286_AUDIO_FUNCTION_GROUP 0x01
+#define RT286_DAC_OUT1 0x02
+#define RT286_DAC_OUT2 0x03
+#define RT286_ADC_IN1 0x09
+#define RT286_ADC_IN2 0x08
+#define RT286_MIXER_IN 0x0b
+#define RT286_MIXER_OUT1 0x0c
+#define RT286_MIXER_OUT2 0x0d
+#define RT286_DMIC1 0x12
+#define RT286_DMIC2 0x13
+#define RT286_SPK_OUT 0x14
+#define RT286_MIC1 0x18
+#define RT286_LINE1 0x1a
+#define RT286_BEEP 0x1d
+#define RT286_SPDIF 0x1e
+#define RT286_VENDOR_REGISTERS 0x20
+#define RT286_HP_OUT 0x21
+#define RT286_MIXER_IN1 0x22
+#define RT286_MIXER_IN2 0x23
+
+#define RT286_SET_PIN_SFT 6
+#define RT286_SET_PIN_ENABLE 0x40
+#define RT286_SET_PIN_DISABLE 0
+#define RT286_SET_EAPD_HIGH 0x2
+#define RT286_SET_EAPD_LOW 0
+
+#define RT286_MUTE_SFT 7
+
+/* Verb commands */
+#define RT286_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM)
+#define RT286_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0)
+#define RT286_SET_AUDIO_POWER RT286_SET_POWER(RT286_AUDIO_FUNCTION_GROUP)
+#define RT286_SET_HPO_POWER RT286_SET_POWER(RT286_HP_OUT)
+#define RT286_SET_SPK_POWER RT286_SET_POWER(RT286_SPK_OUT)
+#define RT286_SET_DMIC1_POWER RT286_SET_POWER(RT286_DMIC1)
+#define RT286_SPK_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_SPK_OUT, 0)
+#define RT286_HPO_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_HP_OUT, 0)
+#define RT286_ADC0_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN1, 0)
+#define RT286_ADC1_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN2, 0)
+#define RT286_SET_MIC1\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_MIC1, 0)
+#define RT286_SET_PIN_HPO\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_HP_OUT, 0)
+#define RT286_SET_PIN_SPK\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_SPK_OUT, 0)
+#define RT286_SET_PIN_DMIC1\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_DMIC1, 0)
+#define RT286_SPK_EAPD\
+ VERB_CMD(AC_VERB_SET_EAPD_BTLENABLE, RT286_SPK_OUT, 0)
+#define RT286_SET_AMP_GAIN_HPO\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0)
+#define RT286_SET_AMP_GAIN_ADC_IN1\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0)
+#define RT286_SET_AMP_GAIN_ADC_IN2\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN2, 0)
+#define RT286_GET_HP_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_HP_OUT, 0)
+#define RT286_GET_MIC1_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_MIC1, 0)
+#define RT286_SET_DMIC2_DEFAULT\
+ VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT286_DMIC2, 0)
+#define RT286_DACL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0xa000)
+#define RT286_DACR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0x9000)
+#define RT286_ADCL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x6000)
+#define RT286_ADCR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x5000)
+#define RT286_MIC_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIC1, 0x7000)
+#define RT286_SPOL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0xa000)
+#define RT286_SPOR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0x9000)
+#define RT286_HPOL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0xa000)
+#define RT286_HPOR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0x9000)
+#define RT286_F_DAC_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7000)
+#define RT286_F_RECMIX_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7100)
+#define RT286_REC_MIC_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7000)
+#define RT286_REC_I2S_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7100)
+#define RT286_REC_LINE_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7200)
+#define RT286_REC_BEEP_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7300)
+#define RT286_DAC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_DAC_OUT1, 0)
+#define RT286_ADC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_ADC_IN1, 0)
+#define RT286_COEF_INDEX\
+ VERB_CMD(AC_VERB_SET_COEF_INDEX, RT286_VENDOR_REGISTERS, 0)
+#define RT286_PROC_COEF\
+ VERB_CMD(AC_VERB_SET_PROC_COEF, RT286_VENDOR_REGISTERS, 0)
+#define RT286_SET_GPIO_MASK\
+ VERB_CMD(AC_VERB_SET_GPIO_MASK, RT286_AUDIO_FUNCTION_GROUP, 0)
+#define RT286_SET_GPIO_DIRECTION\
+ VERB_CMD(AC_VERB_SET_GPIO_DIRECTION, RT286_AUDIO_FUNCTION_GROUP, 0)
+#define RT286_SET_GPIO_DATA\
+ VERB_CMD(AC_VERB_SET_GPIO_DATA, RT286_AUDIO_FUNCTION_GROUP, 0)
+
+/* Index registers */
+#define RT286_A_BIAS_CTRL1 0x01
+#define RT286_A_BIAS_CTRL2 0x02
+#define RT286_POWER_CTRL1 0x03
+#define RT286_A_BIAS_CTRL3 0x04
+#define RT286_POWER_CTRL2 0x08
+#define RT286_I2S_CTRL1 0x09
+#define RT286_I2S_CTRL2 0x0a
+#define RT286_CLK_DIV 0x0b
+#define RT286_DC_GAIN 0x0d
+#define RT286_POWER_CTRL3 0x0f
+#define RT286_MIC1_DET_CTRL 0x19
+#define RT286_MISC_CTRL1 0x20
+#define RT286_GPIO_CTRL 0x29
+#define RT286_IRQ_CTRL 0x33
+#define RT286_PLL_CTRL1 0x49
+#define RT286_CBJ_CTRL1 0x4f
+#define RT286_CBJ_CTRL2 0x50
+#define RT286_PLL_CTRL 0x63
+#define RT286_DEPOP_CTRL1 0x66
+#define RT286_DEPOP_CTRL2 0x67
+#define RT286_DEPOP_CTRL3 0x68
+#define RT286_DEPOP_CTRL4 0x69
+
+/* SPDIF (0x06) */
+#define RT286_SPDIF_SEL_SFT 0
+#define RT286_SPDIF_SEL_PCM0 0
+#define RT286_SPDIF_SEL_PCM1 1
+#define RT286_SPDIF_SEL_SPOUT 2
+#define RT286_SPDIF_SEL_PP 3
+
+/* RECMIX (0x0b) */
+#define RT286_M_REC_BEEP_SFT 0
+#define RT286_M_REC_LINE1_SFT 1
+#define RT286_M_REC_MIC1_SFT 2
+#define RT286_M_REC_I2S_SFT 3
+
+/* Front (0x0c) */
+#define RT286_M_FRONT_DAC_SFT 0
+#define RT286_M_FRONT_REC_SFT 1
+
+/* SPK-OUT (0x14) */
+#define RT286_M_SPK_MUX_SFT 14
+#define RT286_SPK_SEL_MASK 0x1
+#define RT286_SPK_SEL_SFT 0
+#define RT286_SPK_SEL_F 0
+#define RT286_SPK_SEL_S 1
+
+/* HP-OUT (0x21) */
+#define RT286_M_HP_MUX_SFT 14
+#define RT286_HP_SEL_MASK 0x1
+#define RT286_HP_SEL_SFT 0
+#define RT286_HP_SEL_F 0
+#define RT286_HP_SEL_S 1
+
+/* ADC (0x22) (0x23) */
+#define RT286_ADC_SEL_MASK 0x7
+#define RT286_ADC_SEL_SFT 0
+#define RT286_ADC_SEL_SURR 0
+#define RT286_ADC_SEL_FRONT 1
+#define RT286_ADC_SEL_DMIC 2
+#define RT286_ADC_SEL_BEEP 4
+#define RT286_ADC_SEL_LINE1 5
+#define RT286_ADC_SEL_I2S 6
+#define RT286_ADC_SEL_MIC1 7
+
+#define RT286_SCLK_S_MCLK 0
+#define RT286_SCLK_S_PLL 1
+
+enum {
+ RT286_AIF1,
+ RT286_AIF2,
+ RT286_AIFS,
+};
+
+int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+
+#endif /* __RT286_H__ */
+
diff --git a/kernel/sound/soc/codecs/rt5631.c b/kernel/sound/soc/codecs/rt5631.c
new file mode 100644
index 000000000..2c10d7772
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5631.c
@@ -0,0 +1,1741 @@
+/*
+ * rt5631.c -- RT5631 ALSA Soc Audio driver
+ *
+ * Copyright 2011 Realtek Microelectronics
+ *
+ * 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>
+#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/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rt5631.h"
+
+struct rt5631_priv {
+ struct regmap *regmap;
+ int codec_version;
+ int master;
+ int sysclk;
+ int rx_rate;
+ int bclk_rate;
+ int dmic_used_flag;
+};
+
+static const struct reg_default rt5631_reg[] = {
+ { RT5631_SPK_OUT_VOL, 0x8888 },
+ { RT5631_HP_OUT_VOL, 0x8080 },
+ { RT5631_MONO_AXO_1_2_VOL, 0xa080 },
+ { RT5631_AUX_IN_VOL, 0x0808 },
+ { RT5631_ADC_REC_MIXER, 0xf0f0 },
+ { RT5631_VDAC_DIG_VOL, 0x0010 },
+ { RT5631_OUTMIXER_L_CTRL, 0xffc0 },
+ { RT5631_OUTMIXER_R_CTRL, 0xffc0 },
+ { RT5631_AXO1MIXER_CTRL, 0x88c0 },
+ { RT5631_AXO2MIXER_CTRL, 0x88c0 },
+ { RT5631_DIG_MIC_CTRL, 0x3000 },
+ { RT5631_MONO_INPUT_VOL, 0x8808 },
+ { RT5631_SPK_MIXER_CTRL, 0xf8f8 },
+ { RT5631_SPK_MONO_OUT_CTRL, 0xfc00 },
+ { RT5631_SPK_MONO_HP_OUT_CTRL, 0x4440 },
+ { RT5631_SDP_CTRL, 0x8000 },
+ { RT5631_MONO_SDP_CTRL, 0x8000 },
+ { RT5631_STEREO_AD_DA_CLK_CTRL, 0x2010 },
+ { RT5631_GEN_PUR_CTRL_REG, 0x0e00 },
+ { RT5631_INT_ST_IRQ_CTRL_2, 0x071a },
+ { RT5631_MISC_CTRL, 0x2040 },
+ { RT5631_DEPOP_FUN_CTRL_2, 0x8000 },
+ { RT5631_SOFT_VOL_CTRL, 0x07e0 },
+ { RT5631_ALC_CTRL_1, 0x0206 },
+ { RT5631_ALC_CTRL_3, 0x2000 },
+ { RT5631_PSEUDO_SPATL_CTRL, 0x0553 },
+};
+
+/**
+ * rt5631_write_index - write index register of 2nd layer
+ */
+static void rt5631_write_index(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ snd_soc_write(codec, RT5631_INDEX_ADD, reg);
+ snd_soc_write(codec, RT5631_INDEX_DATA, value);
+}
+
+/**
+ * rt5631_read_index - read index register of 2nd layer
+ */
+static unsigned int rt5631_read_index(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ unsigned int value;
+
+ snd_soc_write(codec, RT5631_INDEX_ADD, reg);
+ value = snd_soc_read(codec, RT5631_INDEX_DATA);
+
+ return value;
+}
+
+static int rt5631_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, RT5631_RESET, 0);
+}
+
+static bool rt5631_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5631_RESET:
+ case RT5631_INT_ST_IRQ_CTRL_2:
+ case RT5631_INDEX_ADD:
+ case RT5631_INDEX_DATA:
+ case RT5631_EQ_CTRL:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static bool rt5631_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5631_RESET:
+ case RT5631_SPK_OUT_VOL:
+ case RT5631_HP_OUT_VOL:
+ case RT5631_MONO_AXO_1_2_VOL:
+ case RT5631_AUX_IN_VOL:
+ case RT5631_STEREO_DAC_VOL_1:
+ case RT5631_MIC_CTRL_1:
+ case RT5631_STEREO_DAC_VOL_2:
+ case RT5631_ADC_CTRL_1:
+ case RT5631_ADC_REC_MIXER:
+ case RT5631_ADC_CTRL_2:
+ case RT5631_VDAC_DIG_VOL:
+ case RT5631_OUTMIXER_L_CTRL:
+ case RT5631_OUTMIXER_R_CTRL:
+ case RT5631_AXO1MIXER_CTRL:
+ case RT5631_AXO2MIXER_CTRL:
+ case RT5631_MIC_CTRL_2:
+ case RT5631_DIG_MIC_CTRL:
+ case RT5631_MONO_INPUT_VOL:
+ case RT5631_SPK_MIXER_CTRL:
+ case RT5631_SPK_MONO_OUT_CTRL:
+ case RT5631_SPK_MONO_HP_OUT_CTRL:
+ case RT5631_SDP_CTRL:
+ case RT5631_MONO_SDP_CTRL:
+ case RT5631_STEREO_AD_DA_CLK_CTRL:
+ case RT5631_PWR_MANAG_ADD1:
+ case RT5631_PWR_MANAG_ADD2:
+ case RT5631_PWR_MANAG_ADD3:
+ case RT5631_PWR_MANAG_ADD4:
+ case RT5631_GEN_PUR_CTRL_REG:
+ case RT5631_GLOBAL_CLK_CTRL:
+ case RT5631_PLL_CTRL:
+ case RT5631_INT_ST_IRQ_CTRL_1:
+ case RT5631_INT_ST_IRQ_CTRL_2:
+ case RT5631_GPIO_CTRL:
+ case RT5631_MISC_CTRL:
+ case RT5631_DEPOP_FUN_CTRL_1:
+ case RT5631_DEPOP_FUN_CTRL_2:
+ case RT5631_JACK_DET_CTRL:
+ case RT5631_SOFT_VOL_CTRL:
+ case RT5631_ALC_CTRL_1:
+ case RT5631_ALC_CTRL_2:
+ case RT5631_ALC_CTRL_3:
+ case RT5631_PSEUDO_SPATL_CTRL:
+ case RT5631_INDEX_ADD:
+ case RT5631_INDEX_DATA:
+ case RT5631_EQ_CTRL:
+ case RT5631_VENDOR_ID:
+ case RT5631_VENDOR_ID1:
+ case RT5631_VENDOR_ID2:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+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),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
+};
+
+static int rt5631_dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = rt5631->dmic_used_flag;
+
+ return 0;
+}
+
+static int rt5631_dmic_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+
+ rt5631->dmic_used_flag = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+/* MIC Input Type */
+static const char *rt5631_input_mode[] = {
+ "Single ended", "Differential"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_mic1_mode_enum, RT5631_MIC_CTRL_1,
+ RT5631_MIC1_DIFF_INPUT_SHIFT, rt5631_input_mode);
+
+static SOC_ENUM_SINGLE_DECL(rt5631_mic2_mode_enum, RT5631_MIC_CTRL_1,
+ RT5631_MIC2_DIFF_INPUT_SHIFT, rt5631_input_mode);
+
+/* MONO Input Type */
+static SOC_ENUM_SINGLE_DECL(rt5631_monoin_mode_enum, RT5631_MONO_INPUT_VOL,
+ RT5631_MONO_DIFF_INPUT_SHIFT, rt5631_input_mode);
+
+/* SPK Ratio Gain Control */
+static const char *rt5631_spk_ratio[] = {"1.00x", "1.09x", "1.27x", "1.44x",
+ "1.56x", "1.68x", "1.99x", "2.34x"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_spk_ratio_enum, RT5631_GEN_PUR_CTRL_REG,
+ RT5631_SPK_AMP_RATIO_CTRL_SHIFT, rt5631_spk_ratio);
+
+static const struct snd_kcontrol_new rt5631_snd_controls[] = {
+ /* MIC */
+ SOC_ENUM("MIC1 Mode Control", rt5631_mic1_mode_enum),
+ SOC_SINGLE_TLV("MIC1 Boost", 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,
+ RT5631_MIC2_BOOST_SHIFT, 8, 0, mic_bst_tlv),
+ /* MONO IN */
+ SOC_ENUM("MONOIN Mode Control", rt5631_monoin_mode_enum),
+ SOC_DOUBLE_TLV("MONOIN_RX Capture Volume", RT5631_MONO_INPUT_VOL,
+ RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT,
+ RT5631_VOL_MASK, 1, in_vol_tlv),
+ /* AXI */
+ SOC_DOUBLE_TLV("AXI Capture Volume", RT5631_AUX_IN_VOL,
+ RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT,
+ RT5631_VOL_MASK, 1, in_vol_tlv),
+ /* DAC */
+ SOC_DOUBLE_TLV("PCM Playback Volume", RT5631_STEREO_DAC_VOL_2,
+ RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT,
+ RT5631_DAC_VOL_MASK, 1, dac_vol_tlv),
+ SOC_DOUBLE("PCM Playback Switch", RT5631_STEREO_DAC_VOL_1,
+ RT5631_L_MUTE_SHIFT, RT5631_R_MUTE_SHIFT, 1, 1),
+ /* AXO */
+ SOC_SINGLE("AXO1 Playback Switch", RT5631_MONO_AXO_1_2_VOL,
+ RT5631_L_MUTE_SHIFT, 1, 1),
+ SOC_SINGLE("AXO2 Playback Switch", RT5631_MONO_AXO_1_2_VOL,
+ RT5631_R_VOL_SHIFT, 1, 1),
+ /* OUTVOL */
+ SOC_DOUBLE("OUTVOL Channel Switch", RT5631_SPK_OUT_VOL,
+ RT5631_L_EN_SHIFT, RT5631_R_EN_SHIFT, 1, 0),
+
+ /* SPK */
+ SOC_DOUBLE("Speaker Playback Switch", RT5631_SPK_OUT_VOL,
+ RT5631_L_MUTE_SHIFT, RT5631_R_MUTE_SHIFT, 1, 1),
+ SOC_DOUBLE_TLV("Speaker Playback Volume", RT5631_SPK_OUT_VOL,
+ RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT, 39, 1, out_vol_tlv),
+ /* MONO OUT */
+ SOC_SINGLE("MONO Playback Switch", RT5631_MONO_AXO_1_2_VOL,
+ RT5631_MUTE_MONO_SHIFT, 1, 1),
+ /* HP */
+ SOC_DOUBLE("HP Playback Switch", RT5631_HP_OUT_VOL,
+ RT5631_L_MUTE_SHIFT, RT5631_R_MUTE_SHIFT, 1, 1),
+ SOC_DOUBLE_TLV("HP Playback Volume", RT5631_HP_OUT_VOL,
+ RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT,
+ RT5631_VOL_MASK, 1, out_vol_tlv),
+ /* DMIC */
+ SOC_SINGLE_EXT("DMIC Switch", 0, 0, 1, 0,
+ rt5631_dmic_get, rt5631_dmic_put),
+ SOC_DOUBLE("DMIC Capture Switch", RT5631_DIG_MIC_CTRL,
+ RT5631_DMIC_L_CH_MUTE_SHIFT,
+ RT5631_DMIC_R_CH_MUTE_SHIFT, 1, 1),
+
+ /* SPK Ratio Gain Control */
+ SOC_ENUM("SPK Ratio Control", rt5631_spk_ratio_enum),
+};
+
+static int check_sysclk1_source(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, 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);
+ 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);
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, 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);
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, 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);
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, 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);
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, 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);
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, 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);
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, RT5631_ADC_REC_MIXER);
+ return !(reg & RT5631_M_MIC2_TO_RECMIXER_R);
+}
+
+/**
+ * onebit_depop_power_stage - auto depop in power stage.
+ * @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)
+{
+ unsigned int soft_vol, hp_zc;
+
+ /* enable one-bit depop function */
+ snd_soc_update_bits(codec, 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);
+ 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);
+ /* power on capless block */
+ snd_soc_write(codec, 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);
+ 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);
+}
+
+/**
+ * onebit_depop_mute_stage - auto depop in mute stage.
+ * @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)
+{
+ unsigned int soft_vol, hp_zc;
+
+ /* enable one-bit depop function */
+ snd_soc_update_bits(codec, 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);
+ 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_L_MUTE | RT5631_R_MUTE, 0);
+ msleep(300);
+ } else {
+ snd_soc_update_bits(codec, 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);
+}
+
+/**
+ * onebit_depop_power_stage - step by step depop sequence in power stage.
+ * @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)
+{
+ unsigned int soft_vol, hp_zc;
+
+ /* depop control by register */
+ snd_soc_update_bits(codec, 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);
+ if (enable) {
+ /* config depop sequence parameter */
+ rt5631_write_index(codec, RT5631_SPK_INTL_CTRL, 0x303e);
+
+ /* power on headphone and charge pump */
+ snd_soc_update_bits(codec, 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,
+ RT5631_POW_ON_SOFT_GEN | RT5631_EN_DEPOP2_FOR_HP);
+ msleep(100);
+
+ /* stop depop mode */
+ snd_soc_update_bits(codec, 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_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,
+ 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,
+ RT5631_PWR_HP_DEPOP_DIS, 0);
+
+ /* config depop sequence parameter */
+ snd_soc_write(codec, 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,
+ RT5631_POW_ON_SOFT_GEN);
+
+ /* power down headphone and charge pump */
+ snd_soc_update_bits(codec, 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);
+}
+
+/**
+ * depop_seq_mute_stage - step by step depop sequence in mute stage.
+ * @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)
+{
+ unsigned int soft_vol, hp_zc;
+
+ /* depop control by register */
+ snd_soc_update_bits(codec, 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);
+ 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_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,
+ 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_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,
+ 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);
+}
+
+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);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ if (rt5631->codec_version) {
+ onebit_depop_mute_stage(codec, 0);
+ onebit_depop_power_stage(codec, 0);
+ } else {
+ depop_seq_mute_stage(codec, 0);
+ depop_seq_power_stage(codec, 0);
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ if (rt5631->codec_version) {
+ onebit_depop_power_stage(codec, 1);
+ onebit_depop_mute_stage(codec, 1);
+ } else {
+ depop_seq_power_stage(codec, 1);
+ depop_seq_mute_stage(codec, 1);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int set_dmic_params(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+
+ switch (rt5631->rx_rate) {
+ case 44100:
+ case 48000:
+ snd_soc_update_bits(codec, 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,
+ RT5631_DMIC_CLK_CTRL_MASK,
+ RT5631_DMIC_CLK_CTRL_TO_64FS);
+ break;
+
+ case 16000:
+ case 11025:
+ case 8000:
+ snd_soc_update_bits(codec, RT5631_DIG_MIC_CTRL,
+ RT5631_DMIC_CLK_CTRL_MASK,
+ RT5631_DMIC_CLK_CTRL_TO_128FS);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt5631_recmixl_mixer_controls[] = {
+ SOC_DAPM_SINGLE("OUTMIXL Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_OUTMIXL_RECMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC1_BST1 Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_MIC1_RECMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("AXILVOL Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_AXIL_RECMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MONOIN_RX Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_MONO_IN_RECMIXL_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_recmixr_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MONOIN_RX Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_MONO_IN_RECMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("AXIRVOL Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_AXIR_RECMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC2_BST2 Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_MIC2_RECMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("OUTMIXR Capture Switch", RT5631_ADC_REC_MIXER,
+ RT5631_M_OUTMIXR_RECMIXR_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_spkmixl_mixer_controls[] = {
+ SOC_DAPM_SINGLE("RECMIXL Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_RECMIXL_SPKMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC1_P Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_MIC1P_SPKMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("DACL Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_DACL_SPKMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("OUTMIXL Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_OUTMIXL_SPKMIXL_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_spkmixr_mixer_controls[] = {
+ SOC_DAPM_SINGLE("OUTMIXR Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_OUTMIXR_SPKMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("DACR Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_DACR_SPKMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC2_P Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_MIC2P_SPKMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIXR Playback Switch", RT5631_SPK_MIXER_CTRL,
+ RT5631_M_RECMIXR_SPKMIXR_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_outmixl_mixer_controls[] = {
+ SOC_DAPM_SINGLE("RECMIXL Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_RECMIXL_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIXR Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_RECMIXR_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("DACL Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_DACL_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_MIC1_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_MIC2_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MONOIN_RXP Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_MONO_INP_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("AXILVOL Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_AXIL_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("AXIRVOL Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_AXIR_OUTMIXL_BIT, 1, 1),
+ SOC_DAPM_SINGLE("VDAC Playback Switch", RT5631_OUTMIXER_L_CTRL,
+ RT5631_M_VDAC_OUTMIXL_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_outmixr_mixer_controls[] = {
+ SOC_DAPM_SINGLE("VDAC Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_VDAC_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("AXIRVOL Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_AXIR_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("AXILVOL Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_AXIL_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MONOIN_RXN Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_MONO_INN_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_MIC2_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_MIC1_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("DACR Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_DACR_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIXR Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_RECMIXR_OUTMIXR_BIT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIXL Playback Switch", RT5631_OUTMIXER_R_CTRL,
+ RT5631_M_RECMIXL_OUTMIXR_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_AXO1MIX_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_AXO1MIXER_CTRL,
+ RT5631_M_MIC1_AXO1MIX_BIT , 1, 1),
+ SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_AXO1MIXER_CTRL,
+ RT5631_M_MIC2_AXO1MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOLL Playback Switch", RT5631_AXO1MIXER_CTRL,
+ RT5631_M_OUTMIXL_AXO1MIX_BIT , 1 , 1),
+ SOC_DAPM_SINGLE("OUTVOLR Playback Switch", RT5631_AXO1MIXER_CTRL,
+ RT5631_M_OUTMIXR_AXO1MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_AXO2MIX_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_AXO2MIXER_CTRL,
+ RT5631_M_MIC1_AXO2MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_AXO2MIXER_CTRL,
+ RT5631_M_MIC2_AXO2MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOLL Playback Switch", RT5631_AXO2MIXER_CTRL,
+ RT5631_M_OUTMIXL_AXO2MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOLR Playback Switch", RT5631_AXO2MIXER_CTRL,
+ RT5631_M_OUTMIXR_AXO2MIX_BIT, 1 , 1),
+};
+
+static const struct snd_kcontrol_new rt5631_spolmix_mixer_controls[] = {
+ SOC_DAPM_SINGLE("SPKVOLL Playback Switch", RT5631_SPK_MONO_OUT_CTRL,
+ RT5631_M_SPKVOLL_SPOLMIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOLR Playback Switch", RT5631_SPK_MONO_OUT_CTRL,
+ RT5631_M_SPKVOLR_SPOLMIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_spormix_mixer_controls[] = {
+ SOC_DAPM_SINGLE("SPKVOLL Playback Switch", RT5631_SPK_MONO_OUT_CTRL,
+ RT5631_M_SPKVOLL_SPORMIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOLR Playback Switch", RT5631_SPK_MONO_OUT_CTRL,
+ RT5631_M_SPKVOLR_SPORMIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5631_monomix_mixer_controls[] = {
+ SOC_DAPM_SINGLE("OUTVOLL Playback Switch", RT5631_SPK_MONO_OUT_CTRL,
+ RT5631_M_OUTVOLL_MONOMIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOLR Playback Switch", RT5631_SPK_MONO_OUT_CTRL,
+ RT5631_M_OUTVOLR_MONOMIX_BIT, 1, 1),
+};
+
+/* Left SPK Volume Input */
+static const char *rt5631_spkvoll_sel[] = {"Vmid", "SPKMIXL"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_spkvoll_enum, RT5631_SPK_OUT_VOL,
+ RT5631_L_EN_SHIFT, rt5631_spkvoll_sel);
+
+static const struct snd_kcontrol_new rt5631_spkvoll_mux_control =
+ SOC_DAPM_ENUM("Left SPKVOL SRC", rt5631_spkvoll_enum);
+
+/* Left HP Volume Input */
+static const char *rt5631_hpvoll_sel[] = {"Vmid", "OUTMIXL"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_hpvoll_enum, RT5631_HP_OUT_VOL,
+ RT5631_L_EN_SHIFT, rt5631_hpvoll_sel);
+
+static const struct snd_kcontrol_new rt5631_hpvoll_mux_control =
+ SOC_DAPM_ENUM("Left HPVOL SRC", rt5631_hpvoll_enum);
+
+/* Left Out Volume Input */
+static const char *rt5631_outvoll_sel[] = {"Vmid", "OUTMIXL"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_outvoll_enum, RT5631_MONO_AXO_1_2_VOL,
+ RT5631_L_EN_SHIFT, rt5631_outvoll_sel);
+
+static const struct snd_kcontrol_new rt5631_outvoll_mux_control =
+ SOC_DAPM_ENUM("Left OUTVOL SRC", rt5631_outvoll_enum);
+
+/* Right Out Volume Input */
+static const char *rt5631_outvolr_sel[] = {"Vmid", "OUTMIXR"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_outvolr_enum, RT5631_MONO_AXO_1_2_VOL,
+ RT5631_R_EN_SHIFT, rt5631_outvolr_sel);
+
+static const struct snd_kcontrol_new rt5631_outvolr_mux_control =
+ SOC_DAPM_ENUM("Right OUTVOL SRC", rt5631_outvolr_enum);
+
+/* Right HP Volume Input */
+static const char *rt5631_hpvolr_sel[] = {"Vmid", "OUTMIXR"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_hpvolr_enum, RT5631_HP_OUT_VOL,
+ RT5631_R_EN_SHIFT, rt5631_hpvolr_sel);
+
+static const struct snd_kcontrol_new rt5631_hpvolr_mux_control =
+ SOC_DAPM_ENUM("Right HPVOL SRC", rt5631_hpvolr_enum);
+
+/* Right SPK Volume Input */
+static const char *rt5631_spkvolr_sel[] = {"Vmid", "SPKMIXR"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_spkvolr_enum, RT5631_SPK_OUT_VOL,
+ RT5631_R_EN_SHIFT, rt5631_spkvolr_sel);
+
+static const struct snd_kcontrol_new rt5631_spkvolr_mux_control =
+ SOC_DAPM_ENUM("Right SPKVOL SRC", rt5631_spkvolr_enum);
+
+/* SPO Left Channel Input */
+static const char *rt5631_spol_src_sel[] = {
+ "SPOLMIX", "MONOIN_RX", "VDAC", "DACL"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_spol_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+ RT5631_SPK_L_MUX_SEL_SHIFT, rt5631_spol_src_sel);
+
+static const struct snd_kcontrol_new rt5631_spol_mux_control =
+ SOC_DAPM_ENUM("SPOL SRC", rt5631_spol_src_enum);
+
+/* SPO Right Channel Input */
+static const char *rt5631_spor_src_sel[] = {
+ "SPORMIX", "MONOIN_RX", "VDAC", "DACR"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_spor_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+ RT5631_SPK_R_MUX_SEL_SHIFT, rt5631_spor_src_sel);
+
+static const struct snd_kcontrol_new rt5631_spor_mux_control =
+ SOC_DAPM_ENUM("SPOR SRC", rt5631_spor_src_enum);
+
+/* MONO Input */
+static const char *rt5631_mono_src_sel[] = {"MONOMIX", "MONOIN_RX", "VDAC"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_mono_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+ RT5631_MONO_MUX_SEL_SHIFT, rt5631_mono_src_sel);
+
+static const struct snd_kcontrol_new rt5631_mono_mux_control =
+ SOC_DAPM_ENUM("MONO SRC", rt5631_mono_src_enum);
+
+/* Left HPO Input */
+static const char *rt5631_hpl_src_sel[] = {"Left HPVOL", "Left DAC"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_hpl_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+ RT5631_HP_L_MUX_SEL_SHIFT, rt5631_hpl_src_sel);
+
+static const struct snd_kcontrol_new rt5631_hpl_mux_control =
+ SOC_DAPM_ENUM("HPL SRC", rt5631_hpl_src_enum);
+
+/* Right HPO Input */
+static const char *rt5631_hpr_src_sel[] = {"Right HPVOL", "Right DAC"};
+
+static SOC_ENUM_SINGLE_DECL(rt5631_hpr_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+ RT5631_HP_R_MUX_SEL_SHIFT, rt5631_hpr_src_sel);
+
+static const struct snd_kcontrol_new rt5631_hpr_mux_control =
+ SOC_DAPM_ENUM("HPR SRC", rt5631_hpr_src_enum);
+
+static const struct snd_soc_dapm_widget rt5631_dapm_widgets[] = {
+ /* Vmid */
+ SND_SOC_DAPM_VMID("Vmid"),
+ /* PLL1 */
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_PLL1_BIT, 0, NULL, 0),
+
+ /* Input Side */
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("AXIL"),
+ SND_SOC_DAPM_INPUT("AXIR"),
+ SND_SOC_DAPM_INPUT("MONOIN_RXN"),
+ SND_SOC_DAPM_INPUT("MONOIN_RXP"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+
+ /* MICBIAS */
+ SND_SOC_DAPM_MICBIAS("MIC Bias1", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_MICBIAS1_VOL_BIT, 0),
+ SND_SOC_DAPM_MICBIAS("MIC Bias2", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_MICBIAS2_VOL_BIT, 0),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA("MIC1 Boost", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_MIC1_BOOT_GAIN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC2 Boost", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_MIC2_BOOT_GAIN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MONOIN_RXP Boost", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_MONO_IN_P_VOL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MONOIN_RXN Boost", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_MONO_IN_N_VOL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AXIL Boost", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_AXIL_IN_VOL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AXIR Boost", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_AXIR_IN_VOL_BIT, 0, NULL, 0),
+
+ /* MONO In */
+ SND_SOC_DAPM_MIXER("MONO_IN", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIXL Mixer", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_RECMIXER_L_BIT, 0,
+ &rt5631_recmixl_mixer_controls[0],
+ ARRAY_SIZE(rt5631_recmixl_mixer_controls)),
+ SND_SOC_DAPM_MIXER("RECMIXR Mixer", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_RECMIXER_R_BIT, 0,
+ &rt5631_recmixr_mixer_controls[0],
+ ARRAY_SIZE(rt5631_recmixr_mixer_controls)),
+ /* Because of record duplication for L/R channel,
+ * L/R ADCs need power up at the same time */
+ SND_SOC_DAPM_MIXER("ADC Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DMIC */
+ SND_SOC_DAPM_SUPPLY("DMIC Supply", RT5631_DIG_MIC_CTRL,
+ RT5631_DMIC_ENA_SHIFT, 0,
+ set_dmic_params, SND_SOC_DAPM_PRE_PMU),
+ /* ADC Data Srouce */
+ SND_SOC_DAPM_SUPPLY("Left ADC Select", RT5631_INT_ST_IRQ_CTRL_2,
+ RT5631_ADC_DATA_SEL_MIC1_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Right ADC Select", RT5631_INT_ST_IRQ_CTRL_2,
+ RT5631_ADC_DATA_SEL_MIC2_SHIFT, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("Left ADC", "HIFI Capture",
+ RT5631_PWR_MANAG_ADD1, RT5631_PWR_ADC_L_CLK_BIT, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "HIFI Capture",
+ RT5631_PWR_MANAG_ADD1, RT5631_PWR_ADC_R_CLK_BIT, 0),
+
+ /* DAC and ADC supply power */
+ SND_SOC_DAPM_SUPPLY("I2S", RT5631_PWR_MANAG_ADD1,
+ RT5631_PWR_MAIN_I2S_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC REF", RT5631_PWR_MANAG_ADD1,
+ RT5631_PWR_DAC_REF_BIT, 0, NULL, 0),
+
+ /* Output Side */
+ /* DACs */
+ SND_SOC_DAPM_DAC("Left DAC", "HIFI Playback",
+ RT5631_PWR_MANAG_ADD1, RT5631_PWR_DAC_L_CLK_BIT, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "HIFI Playback",
+ RT5631_PWR_MANAG_ADD1, RT5631_PWR_DAC_R_CLK_BIT, 0),
+ SND_SOC_DAPM_DAC("Voice DAC", "Voice DAC Mono Playback",
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA("Voice DAC Boost", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* DAC supply power */
+ SND_SOC_DAPM_SUPPLY("Left DAC To Mixer", RT5631_PWR_MANAG_ADD1,
+ RT5631_PWR_DAC_L_TO_MIXER_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Right DAC To Mixer", RT5631_PWR_MANAG_ADD1,
+ RT5631_PWR_DAC_R_TO_MIXER_BIT, 0, NULL, 0),
+
+ /* Left SPK Mixer */
+ SND_SOC_DAPM_MIXER("SPKMIXL Mixer", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_SPKMIXER_L_BIT, 0,
+ &rt5631_spkmixl_mixer_controls[0],
+ ARRAY_SIZE(rt5631_spkmixl_mixer_controls)),
+ /* Left Out Mixer */
+ SND_SOC_DAPM_MIXER("OUTMIXL Mixer", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_OUTMIXER_L_BIT, 0,
+ &rt5631_outmixl_mixer_controls[0],
+ ARRAY_SIZE(rt5631_outmixl_mixer_controls)),
+ /* Right Out Mixer */
+ SND_SOC_DAPM_MIXER("OUTMIXR Mixer", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_OUTMIXER_R_BIT, 0,
+ &rt5631_outmixr_mixer_controls[0],
+ ARRAY_SIZE(rt5631_outmixr_mixer_controls)),
+ /* Right SPK Mixer */
+ SND_SOC_DAPM_MIXER("SPKMIXR Mixer", RT5631_PWR_MANAG_ADD2,
+ RT5631_PWR_SPKMIXER_R_BIT, 0,
+ &rt5631_spkmixr_mixer_controls[0],
+ ARRAY_SIZE(rt5631_spkmixr_mixer_controls)),
+
+ /* Volume Mux */
+ SND_SOC_DAPM_MUX("Left SPKVOL Mux", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_SPK_L_VOL_BIT, 0,
+ &rt5631_spkvoll_mux_control),
+ SND_SOC_DAPM_MUX("Left HPVOL Mux", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_HP_L_OUT_VOL_BIT, 0,
+ &rt5631_hpvoll_mux_control),
+ SND_SOC_DAPM_MUX("Left OUTVOL Mux", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_LOUT_VOL_BIT, 0,
+ &rt5631_outvoll_mux_control),
+ SND_SOC_DAPM_MUX("Right OUTVOL Mux", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_ROUT_VOL_BIT, 0,
+ &rt5631_outvolr_mux_control),
+ SND_SOC_DAPM_MUX("Right HPVOL Mux", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_HP_R_OUT_VOL_BIT, 0,
+ &rt5631_hpvolr_mux_control),
+ SND_SOC_DAPM_MUX("Right SPKVOL Mux", RT5631_PWR_MANAG_ADD4,
+ RT5631_PWR_SPK_R_VOL_BIT, 0,
+ &rt5631_spkvolr_mux_control),
+
+ /* DAC To HP */
+ SND_SOC_DAPM_PGA_S("Left DAC_HP", 0, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Right DAC_HP", 0, SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* HP Depop */
+ SND_SOC_DAPM_PGA_S("HP Depop", 1, SND_SOC_NOPM, 0, 0,
+ hp_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ /* AXO1 Mixer */
+ SND_SOC_DAPM_MIXER("AXO1MIX Mixer", RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_AXO1MIXER_BIT, 0,
+ &rt5631_AXO1MIX_mixer_controls[0],
+ ARRAY_SIZE(rt5631_AXO1MIX_mixer_controls)),
+ /* SPOL Mixer */
+ SND_SOC_DAPM_MIXER("SPOLMIX Mixer", SND_SOC_NOPM, 0, 0,
+ &rt5631_spolmix_mixer_controls[0],
+ ARRAY_SIZE(rt5631_spolmix_mixer_controls)),
+ /* MONO Mixer */
+ SND_SOC_DAPM_MIXER("MONOMIX Mixer", RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_MONOMIXER_BIT, 0,
+ &rt5631_monomix_mixer_controls[0],
+ ARRAY_SIZE(rt5631_monomix_mixer_controls)),
+ /* SPOR Mixer */
+ SND_SOC_DAPM_MIXER("SPORMIX Mixer", SND_SOC_NOPM, 0, 0,
+ &rt5631_spormix_mixer_controls[0],
+ ARRAY_SIZE(rt5631_spormix_mixer_controls)),
+ /* AXO2 Mixer */
+ SND_SOC_DAPM_MIXER("AXO2MIX Mixer", RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_AXO2MIXER_BIT, 0,
+ &rt5631_AXO2MIX_mixer_controls[0],
+ ARRAY_SIZE(rt5631_AXO2MIX_mixer_controls)),
+
+ /* Mux */
+ SND_SOC_DAPM_MUX("SPOL Mux", SND_SOC_NOPM, 0, 0,
+ &rt5631_spol_mux_control),
+ SND_SOC_DAPM_MUX("SPOR Mux", SND_SOC_NOPM, 0, 0,
+ &rt5631_spor_mux_control),
+ SND_SOC_DAPM_MUX("MONO Mux", SND_SOC_NOPM, 0, 0,
+ &rt5631_mono_mux_control),
+ SND_SOC_DAPM_MUX("HPL Mux", SND_SOC_NOPM, 0, 0,
+ &rt5631_hpl_mux_control),
+ SND_SOC_DAPM_MUX("HPR Mux", SND_SOC_NOPM, 0, 0,
+ &rt5631_hpr_mux_control),
+
+ /* AMP supply */
+ SND_SOC_DAPM_SUPPLY("MONO Depop", RT5631_PWR_MANAG_ADD3,
+ RT5631_PWR_MONO_DEPOP_DIS_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D", RT5631_PWR_MANAG_ADD1,
+ RT5631_PWR_CLASS_D_BIT, 0, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("AUXO1"),
+ SND_SOC_DAPM_OUTPUT("AUXO2"),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("MONO"),
+};
+
+static const struct snd_soc_dapm_route rt5631_dapm_routes[] = {
+ {"MIC1 Boost", NULL, "MIC1"},
+ {"MIC2 Boost", NULL, "MIC2"},
+ {"MONOIN_RXP Boost", NULL, "MONOIN_RXP"},
+ {"MONOIN_RXN Boost", NULL, "MONOIN_RXN"},
+ {"AXIL Boost", NULL, "AXIL"},
+ {"AXIR Boost", NULL, "AXIR"},
+
+ {"MONO_IN", NULL, "MONOIN_RXP Boost"},
+ {"MONO_IN", NULL, "MONOIN_RXN Boost"},
+
+ {"RECMIXL Mixer", "OUTMIXL Capture Switch", "OUTMIXL Mixer"},
+ {"RECMIXL Mixer", "MIC1_BST1 Capture Switch", "MIC1 Boost"},
+ {"RECMIXL Mixer", "AXILVOL Capture Switch", "AXIL Boost"},
+ {"RECMIXL Mixer", "MONOIN_RX Capture Switch", "MONO_IN"},
+
+ {"RECMIXR Mixer", "OUTMIXR Capture Switch", "OUTMIXR Mixer"},
+ {"RECMIXR Mixer", "MIC2_BST2 Capture Switch", "MIC2 Boost"},
+ {"RECMIXR Mixer", "AXIRVOL Capture Switch", "AXIR Boost"},
+ {"RECMIXR Mixer", "MONOIN_RX Capture Switch", "MONO_IN"},
+
+ {"ADC Mixer", NULL, "RECMIXL Mixer"},
+ {"ADC Mixer", NULL, "RECMIXR Mixer"},
+
+ {"Left ADC", NULL, "ADC Mixer"},
+ {"Left ADC", NULL, "Left ADC Select", check_adcl_select},
+ {"Left ADC", NULL, "PLL1", check_sysclk1_source},
+ {"Left ADC", NULL, "I2S"},
+ {"Left ADC", NULL, "DAC REF"},
+
+ {"Right ADC", NULL, "ADC Mixer"},
+ {"Right ADC", NULL, "Right ADC Select", check_adcr_select},
+ {"Right ADC", NULL, "PLL1", check_sysclk1_source},
+ {"Right ADC", NULL, "I2S"},
+ {"Right ADC", NULL, "DAC REF"},
+
+ {"DMIC", NULL, "DMIC Supply", check_dmic_used},
+ {"Left ADC", NULL, "DMIC"},
+ {"Right ADC", NULL, "DMIC"},
+
+ {"Left DAC", NULL, "PLL1", check_sysclk1_source},
+ {"Left DAC", NULL, "I2S"},
+ {"Left DAC", NULL, "DAC REF"},
+ {"Right DAC", NULL, "PLL1", check_sysclk1_source},
+ {"Right DAC", NULL, "I2S"},
+ {"Right DAC", NULL, "DAC REF"},
+
+ {"Voice DAC Boost", NULL, "Voice DAC"},
+
+ {"SPKMIXL Mixer", NULL, "Left DAC To Mixer", check_dacl_to_spkmixl},
+ {"SPKMIXL Mixer", "RECMIXL Playback Switch", "RECMIXL Mixer"},
+ {"SPKMIXL Mixer", "MIC1_P Playback Switch", "MIC1"},
+ {"SPKMIXL Mixer", "DACL Playback Switch", "Left DAC"},
+ {"SPKMIXL Mixer", "OUTMIXL Playback Switch", "OUTMIXL Mixer"},
+
+ {"SPKMIXR Mixer", NULL, "Right DAC To Mixer", check_dacr_to_spkmixr},
+ {"SPKMIXR Mixer", "OUTMIXR Playback Switch", "OUTMIXR Mixer"},
+ {"SPKMIXR Mixer", "DACR Playback Switch", "Right DAC"},
+ {"SPKMIXR Mixer", "MIC2_P Playback Switch", "MIC2"},
+ {"SPKMIXR Mixer", "RECMIXR Playback Switch", "RECMIXR Mixer"},
+
+ {"OUTMIXL Mixer", NULL, "Left DAC To Mixer", check_dacl_to_outmixl},
+ {"OUTMIXL Mixer", "RECMIXL Playback Switch", "RECMIXL Mixer"},
+ {"OUTMIXL Mixer", "RECMIXR Playback Switch", "RECMIXR Mixer"},
+ {"OUTMIXL Mixer", "DACL Playback Switch", "Left DAC"},
+ {"OUTMIXL Mixer", "MIC1_BST1 Playback Switch", "MIC1 Boost"},
+ {"OUTMIXL Mixer", "MIC2_BST2 Playback Switch", "MIC2 Boost"},
+ {"OUTMIXL Mixer", "MONOIN_RXP Playback Switch", "MONOIN_RXP Boost"},
+ {"OUTMIXL Mixer", "AXILVOL Playback Switch", "AXIL Boost"},
+ {"OUTMIXL Mixer", "AXIRVOL Playback Switch", "AXIR Boost"},
+ {"OUTMIXL Mixer", "VDAC Playback Switch", "Voice DAC Boost"},
+
+ {"OUTMIXR Mixer", NULL, "Right DAC To Mixer", check_dacr_to_outmixr},
+ {"OUTMIXR Mixer", "RECMIXL Playback Switch", "RECMIXL Mixer"},
+ {"OUTMIXR Mixer", "RECMIXR Playback Switch", "RECMIXR Mixer"},
+ {"OUTMIXR Mixer", "DACR Playback Switch", "Right DAC"},
+ {"OUTMIXR Mixer", "MIC1_BST1 Playback Switch", "MIC1 Boost"},
+ {"OUTMIXR Mixer", "MIC2_BST2 Playback Switch", "MIC2 Boost"},
+ {"OUTMIXR Mixer", "MONOIN_RXN Playback Switch", "MONOIN_RXN Boost"},
+ {"OUTMIXR Mixer", "AXILVOL Playback Switch", "AXIL Boost"},
+ {"OUTMIXR Mixer", "AXIRVOL Playback Switch", "AXIR Boost"},
+ {"OUTMIXR Mixer", "VDAC Playback Switch", "Voice DAC Boost"},
+
+ {"Left SPKVOL Mux", "SPKMIXL", "SPKMIXL Mixer"},
+ {"Left SPKVOL Mux", "Vmid", "Vmid"},
+ {"Left HPVOL Mux", "OUTMIXL", "OUTMIXL Mixer"},
+ {"Left HPVOL Mux", "Vmid", "Vmid"},
+ {"Left OUTVOL Mux", "OUTMIXL", "OUTMIXL Mixer"},
+ {"Left OUTVOL Mux", "Vmid", "Vmid"},
+ {"Right OUTVOL Mux", "OUTMIXR", "OUTMIXR Mixer"},
+ {"Right OUTVOL Mux", "Vmid", "Vmid"},
+ {"Right HPVOL Mux", "OUTMIXR", "OUTMIXR Mixer"},
+ {"Right HPVOL Mux", "Vmid", "Vmid"},
+ {"Right SPKVOL Mux", "SPKMIXR", "SPKMIXR Mixer"},
+ {"Right SPKVOL Mux", "Vmid", "Vmid"},
+
+ {"AXO1MIX Mixer", "MIC1_BST1 Playback Switch", "MIC1 Boost"},
+ {"AXO1MIX Mixer", "OUTVOLL Playback Switch", "Left OUTVOL Mux"},
+ {"AXO1MIX Mixer", "OUTVOLR Playback Switch", "Right OUTVOL Mux"},
+ {"AXO1MIX Mixer", "MIC2_BST2 Playback Switch", "MIC2 Boost"},
+
+ {"AXO2MIX Mixer", "MIC1_BST1 Playback Switch", "MIC1 Boost"},
+ {"AXO2MIX Mixer", "OUTVOLL Playback Switch", "Left OUTVOL Mux"},
+ {"AXO2MIX Mixer", "OUTVOLR Playback Switch", "Right OUTVOL Mux"},
+ {"AXO2MIX Mixer", "MIC2_BST2 Playback Switch", "MIC2 Boost"},
+
+ {"SPOLMIX Mixer", "SPKVOLL Playback Switch", "Left SPKVOL Mux"},
+ {"SPOLMIX Mixer", "SPKVOLR Playback Switch", "Right SPKVOL Mux"},
+
+ {"SPORMIX Mixer", "SPKVOLL Playback Switch", "Left SPKVOL Mux"},
+ {"SPORMIX Mixer", "SPKVOLR Playback Switch", "Right SPKVOL Mux"},
+
+ {"MONOMIX Mixer", "OUTVOLL Playback Switch", "Left OUTVOL Mux"},
+ {"MONOMIX Mixer", "OUTVOLR Playback Switch", "Right OUTVOL Mux"},
+
+ {"SPOL Mux", "SPOLMIX", "SPOLMIX Mixer"},
+ {"SPOL Mux", "MONOIN_RX", "MONO_IN"},
+ {"SPOL Mux", "VDAC", "Voice DAC Boost"},
+ {"SPOL Mux", "DACL", "Left DAC"},
+
+ {"SPOR Mux", "SPORMIX", "SPORMIX Mixer"},
+ {"SPOR Mux", "MONOIN_RX", "MONO_IN"},
+ {"SPOR Mux", "VDAC", "Voice DAC Boost"},
+ {"SPOR Mux", "DACR", "Right DAC"},
+
+ {"MONO Mux", "MONOMIX", "MONOMIX Mixer"},
+ {"MONO Mux", "MONOIN_RX", "MONO_IN"},
+ {"MONO Mux", "VDAC", "Voice DAC Boost"},
+
+ {"Right DAC_HP", NULL, "Right DAC"},
+ {"Left DAC_HP", NULL, "Left DAC"},
+
+ {"HPL Mux", "Left HPVOL", "Left HPVOL Mux"},
+ {"HPL Mux", "Left DAC", "Left DAC_HP"},
+ {"HPR Mux", "Right HPVOL", "Right HPVOL Mux"},
+ {"HPR Mux", "Right DAC", "Right DAC_HP"},
+
+ {"HP Depop", NULL, "HPL Mux"},
+ {"HP Depop", NULL, "HPR Mux"},
+
+ {"AUXO1", NULL, "AXO1MIX Mixer"},
+ {"AUXO2", NULL, "AXO2MIX Mixer"},
+
+ {"SPOL", NULL, "Class D"},
+ {"SPOL", NULL, "SPOL Mux"},
+ {"SPOR", NULL, "Class D"},
+ {"SPOR", NULL, "SPOR Mux"},
+
+ {"HPOL", NULL, "HP Depop"},
+ {"HPOR", NULL, "HP Depop"},
+
+ {"MONO", NULL, "MONO Depop"},
+ {"MONO", NULL, "MONO Mux"},
+};
+
+struct coeff_clk_div {
+ u32 mclk;
+ u32 bclk;
+ u32 rate;
+ u16 reg_val;
+};
+
+/* PLL divisors */
+struct pll_div {
+ u32 pll_in;
+ u32 pll_out;
+ u16 reg_val;
+};
+
+static const struct pll_div codec_master_pll_div[] = {
+ {2048000, 8192000, 0x0ea0},
+ {3686400, 8192000, 0x4e27},
+ {12000000, 8192000, 0x456b},
+ {13000000, 8192000, 0x495f},
+ {13100000, 8192000, 0x0320},
+ {2048000, 11289600, 0xf637},
+ {3686400, 11289600, 0x2f22},
+ {12000000, 11289600, 0x3e2f},
+ {13000000, 11289600, 0x4d5b},
+ {13100000, 11289600, 0x363b},
+ {2048000, 16384000, 0x1ea0},
+ {3686400, 16384000, 0x9e27},
+ {12000000, 16384000, 0x452b},
+ {13000000, 16384000, 0x542f},
+ {13100000, 16384000, 0x03a0},
+ {2048000, 16934400, 0xe625},
+ {3686400, 16934400, 0x9126},
+ {12000000, 16934400, 0x4d2c},
+ {13000000, 16934400, 0x742f},
+ {13100000, 16934400, 0x3c27},
+ {2048000, 22579200, 0x2aa0},
+ {3686400, 22579200, 0x2f20},
+ {12000000, 22579200, 0x7e2f},
+ {13000000, 22579200, 0x742f},
+ {13100000, 22579200, 0x3c27},
+ {2048000, 24576000, 0x2ea0},
+ {3686400, 24576000, 0xee27},
+ {12000000, 24576000, 0x2915},
+ {13000000, 24576000, 0x772e},
+ {13100000, 24576000, 0x0d20},
+ {26000000, 24576000, 0x2027},
+ {26000000, 22579200, 0x392f},
+ {24576000, 22579200, 0x0921},
+ {24576000, 24576000, 0x02a0},
+};
+
+static const struct pll_div codec_slave_pll_div[] = {
+ {256000, 2048000, 0x46f0},
+ {256000, 4096000, 0x3ea0},
+ {352800, 5644800, 0x3ea0},
+ {512000, 8192000, 0x3ea0},
+ {1024000, 8192000, 0x46f0},
+ {705600, 11289600, 0x3ea0},
+ {1024000, 16384000, 0x3ea0},
+ {1411200, 22579200, 0x3ea0},
+ {1536000, 24576000, 0x3ea0},
+ {2048000, 16384000, 0x1ea0},
+ {2822400, 22579200, 0x1ea0},
+ {2822400, 45158400, 0x5ec0},
+ {5644800, 45158400, 0x46f0},
+ {3072000, 24576000, 0x1ea0},
+ {3072000, 49152000, 0x5ec0},
+ {6144000, 49152000, 0x46f0},
+ {705600, 11289600, 0x3ea0},
+ {705600, 8467200, 0x3ab0},
+ {24576000, 24576000, 0x02a0},
+ {1411200, 11289600, 0x1690},
+ {2822400, 11289600, 0x0a90},
+ {1536000, 12288000, 0x1690},
+ {3072000, 12288000, 0x0a90},
+};
+
+static struct coeff_clk_div coeff_div[] = {
+ /* sysclk is 256fs */
+ {2048000, 8000 * 32, 8000, 0x1000},
+ {2048000, 8000 * 64, 8000, 0x0000},
+ {2822400, 11025 * 32, 11025, 0x1000},
+ {2822400, 11025 * 64, 11025, 0x0000},
+ {4096000, 16000 * 32, 16000, 0x1000},
+ {4096000, 16000 * 64, 16000, 0x0000},
+ {5644800, 22050 * 32, 22050, 0x1000},
+ {5644800, 22050 * 64, 22050, 0x0000},
+ {8192000, 32000 * 32, 32000, 0x1000},
+ {8192000, 32000 * 64, 32000, 0x0000},
+ {11289600, 44100 * 32, 44100, 0x1000},
+ {11289600, 44100 * 64, 44100, 0x0000},
+ {12288000, 48000 * 32, 48000, 0x1000},
+ {12288000, 48000 * 64, 48000, 0x0000},
+ {22579200, 88200 * 32, 88200, 0x1000},
+ {22579200, 88200 * 64, 88200, 0x0000},
+ {24576000, 96000 * 32, 96000, 0x1000},
+ {24576000, 96000 * 64, 96000, 0x0000},
+ /* sysclk is 512fs */
+ {4096000, 8000 * 32, 8000, 0x3000},
+ {4096000, 8000 * 64, 8000, 0x2000},
+ {5644800, 11025 * 32, 11025, 0x3000},
+ {5644800, 11025 * 64, 11025, 0x2000},
+ {8192000, 16000 * 32, 16000, 0x3000},
+ {8192000, 16000 * 64, 16000, 0x2000},
+ {11289600, 22050 * 32, 22050, 0x3000},
+ {11289600, 22050 * 64, 22050, 0x2000},
+ {16384000, 32000 * 32, 32000, 0x3000},
+ {16384000, 32000 * 64, 32000, 0x2000},
+ {22579200, 44100 * 32, 44100, 0x3000},
+ {22579200, 44100 * 64, 44100, 0x2000},
+ {24576000, 48000 * 32, 48000, 0x3000},
+ {24576000, 48000 * 64, 48000, 0x2000},
+ {45158400, 88200 * 32, 88200, 0x3000},
+ {45158400, 88200 * 64, 88200, 0x2000},
+ {49152000, 96000 * 32, 96000, 0x3000},
+ {49152000, 96000 * 64, 96000, 0x2000},
+ /* sysclk is 24.576Mhz or 22.5792Mhz */
+ {24576000, 8000 * 32, 8000, 0x7080},
+ {24576000, 8000 * 64, 8000, 0x6080},
+ {24576000, 16000 * 32, 16000, 0x5080},
+ {24576000, 16000 * 64, 16000, 0x4080},
+ {24576000, 24000 * 32, 24000, 0x5000},
+ {24576000, 24000 * 64, 24000, 0x4000},
+ {24576000, 32000 * 32, 32000, 0x3080},
+ {24576000, 32000 * 64, 32000, 0x2080},
+ {22579200, 11025 * 32, 11025, 0x7000},
+ {22579200, 11025 * 64, 11025, 0x6000},
+ {22579200, 22050 * 32, 22050, 0x5000},
+ {22579200, 22050 * 64, 22050, 0x4000},
+};
+
+static int get_coeff(int mclk, int rate, int timesofbclk)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].mclk == mclk && coeff_div[i].rate == rate &&
+ (coeff_div[i].bclk / coeff_div[i].rate) == timesofbclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ int timesofbclk = 32, coeff;
+ unsigned int iface = 0;
+
+ dev_dbg(codec->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");
+ return rt5631->bclk_rate;
+ }
+ rt5631->rx_rate = params_rate(params);
+
+ if (rt5631->master)
+ coeff = get_coeff(rt5631->sysclk, rt5631->rx_rate,
+ rt5631->bclk_rate / rt5631->rx_rate);
+ else
+ coeff = get_coeff(rt5631->sysclk, rt5631->rx_rate,
+ timesofbclk);
+ if (coeff < 0) {
+ dev_err(codec->dev, "Fail to get coeff\n");
+ return coeff;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface |= RT5631_SDP_I2S_DL_20;
+ break;
+ case 24:
+ iface |= RT5631_SDP_I2S_DL_24;
+ break;
+ case 8:
+ iface |= RT5631_SDP_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, RT5631_SDP_CTRL,
+ RT5631_SDP_I2S_DL_MASK, iface);
+ snd_soc_write(codec, RT5631_STEREO_AD_DA_CLK_CTRL,
+ coeff_div[coeff].reg_val);
+
+ return 0;
+}
+
+static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ unsigned int iface = 0;
+
+ dev_dbg(codec->dev, "enter %s\n", __func__);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rt5631->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ iface |= RT5631_SDP_MODE_SEL_SLAVE;
+ rt5631->master = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= RT5631_SDP_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= RT5631_SDP_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= RT5631_SDP_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= RT5631_SDP_I2S_BCLK_POL_CTRL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, RT5631_SDP_CTRL, iface);
+
+ return 0;
+}
+
+static int rt5631_hifi_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "enter %s, syclk=%d\n", __func__, freq);
+
+ if ((freq >= (256 * 8000)) && (freq <= (512 * 96000))) {
+ rt5631->sysclk = freq;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ int i, ret = -EINVAL;
+
+ dev_dbg(codec->dev, "enter %s\n", __func__);
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(codec->dev, "PLL disabled\n");
+
+ snd_soc_update_bits(codec, RT5631_GLOBAL_CLK_CTRL,
+ RT5631_SYSCLK_SOUR_SEL_MASK,
+ RT5631_SYSCLK_SOUR_SEL_MCLK);
+
+ return 0;
+ }
+
+ if (rt5631->master) {
+ for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++)
+ if (freq_in == codec_master_pll_div[i].pll_in &&
+ freq_out == codec_master_pll_div[i].pll_out) {
+ dev_info(codec->dev,
+ "change PLL in master mode\n");
+ snd_soc_write(codec, RT5631_PLL_CTRL,
+ codec_master_pll_div[i].reg_val);
+ schedule_timeout_uninterruptible(
+ msecs_to_jiffies(20));
+ snd_soc_update_bits(codec,
+ RT5631_GLOBAL_CLK_CTRL,
+ RT5631_SYSCLK_SOUR_SEL_MASK |
+ RT5631_PLLCLK_SOUR_SEL_MASK,
+ RT5631_SYSCLK_SOUR_SEL_PLL |
+ RT5631_PLLCLK_SOUR_SEL_MCLK);
+ ret = 0;
+ break;
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++)
+ if (freq_in == codec_slave_pll_div[i].pll_in &&
+ freq_out == codec_slave_pll_div[i].pll_out) {
+ dev_info(codec->dev,
+ "change PLL in slave mode\n");
+ snd_soc_write(codec, RT5631_PLL_CTRL,
+ codec_slave_pll_div[i].reg_val);
+ schedule_timeout_uninterruptible(
+ msecs_to_jiffies(20));
+ snd_soc_update_bits(codec,
+ RT5631_GLOBAL_CLK_CTRL,
+ RT5631_SYSCLK_SOUR_SEL_MASK |
+ RT5631_PLLCLK_SOUR_SEL_MASK,
+ RT5631_SYSCLK_SOUR_SEL_PLL |
+ RT5631_PLLCLK_SOUR_SEL_BCLK);
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int rt5631_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_update_bits(codec, 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,
+ 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,
+ RT5631_PWR_FAST_VREF_CTRL,
+ RT5631_PWR_FAST_VREF_CTRL);
+ regcache_cache_only(rt5631->regmap, false);
+ regcache_sync(rt5631->regmap);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_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);
+ break;
+
+ default:
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int rt5631_probe(struct snd_soc_codec *codec)
+{
+ struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ val = rt5631_read_index(codec, 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_PWR_VREF | RT5631_PWR_MAIN_BIAS,
+ RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS);
+ msleep(80);
+ snd_soc_update_bits(codec, 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);
+ /* power off ClassD auto Recovery */
+ if (rt5631->codec_version)
+ snd_soc_update_bits(codec, RT5631_INT_ST_IRQ_CTRL_2,
+ 0x2000, 0x2000);
+ else
+ snd_soc_update_bits(codec, RT5631_INT_ST_IRQ_CTRL_2,
+ 0x2000, 0);
+ /* DMIC */
+ if (rt5631->dmic_used_flag) {
+ snd_soc_update_bits(codec, 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,
+ 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;
+
+ return 0;
+}
+
+#define RT5631_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5631_FORMAT (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5631_ops = {
+ .hw_params = rt5631_hifi_pcm_params,
+ .set_fmt = rt5631_hifi_codec_set_dai_fmt,
+ .set_sysclk = rt5631_hifi_codec_set_dai_sysclk,
+ .set_pll = rt5631_codec_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver rt5631_dai[] = {
+ {
+ .name = "rt5631-hifi",
+ .id = 1,
+ .playback = {
+ .stream_name = "HIFI Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5631_STEREO_RATES,
+ .formats = RT5631_FORMAT,
+ },
+ .capture = {
+ .stream_name = "HIFI Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5631_STEREO_RATES,
+ .formats = RT5631_FORMAT,
+ },
+ .ops = &rt5631_ops,
+ },
+};
+
+static 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 i2c_device_id rt5631_i2c_id[] = {
+ { "rt5631", 0 },
+ { "alc5631", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt5631_i2c_dt_ids[] = {
+ { .compatible = "realtek,rt5631"},
+ { .compatible = "realtek,alc5631"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5631_i2c_dt_ids);
+#endif
+
+static const struct regmap_config rt5631_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .readable_reg = rt5631_readable_register,
+ .volatile_reg = rt5631_volatile_register,
+ .max_register = RT5631_VENDOR_ID2,
+ .reg_defaults = rt5631_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5631_reg),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int rt5631_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt5631_priv *rt5631;
+ int ret;
+
+ rt5631 = devm_kzalloc(&i2c->dev, sizeof(struct rt5631_priv),
+ GFP_KERNEL);
+ if (NULL == rt5631)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5631);
+
+ rt5631->regmap = devm_regmap_init_i2c(i2c, &rt5631_regmap_config);
+ if (IS_ERR(rt5631->regmap))
+ return PTR_ERR(rt5631->regmap);
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_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 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,
+ .remove = rt5631_i2c_remove,
+ .id_table = rt5631_i2c_id,
+};
+
+module_i2c_driver(rt5631_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5631 driver");
+MODULE_AUTHOR("flove <flove@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/rt5631.h b/kernel/sound/soc/codecs/rt5631.h
new file mode 100644
index 000000000..13401581b
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5631.h
@@ -0,0 +1,701 @@
+#ifndef __RTCODEC5631_H__
+#define __RTCODEC5631_H__
+
+
+#define RT5631_RESET 0x00
+#define RT5631_SPK_OUT_VOL 0x02
+#define RT5631_HP_OUT_VOL 0x04
+#define RT5631_MONO_AXO_1_2_VOL 0x06
+#define RT5631_AUX_IN_VOL 0x0A
+#define RT5631_STEREO_DAC_VOL_1 0x0C
+#define RT5631_MIC_CTRL_1 0x0E
+#define RT5631_STEREO_DAC_VOL_2 0x10
+#define RT5631_ADC_CTRL_1 0x12
+#define RT5631_ADC_REC_MIXER 0x14
+#define RT5631_ADC_CTRL_2 0x16
+#define RT5631_VDAC_DIG_VOL 0x18
+#define RT5631_OUTMIXER_L_CTRL 0x1A
+#define RT5631_OUTMIXER_R_CTRL 0x1C
+#define RT5631_AXO1MIXER_CTRL 0x1E
+#define RT5631_AXO2MIXER_CTRL 0x20
+#define RT5631_MIC_CTRL_2 0x22
+#define RT5631_DIG_MIC_CTRL 0x24
+#define RT5631_MONO_INPUT_VOL 0x26
+#define RT5631_SPK_MIXER_CTRL 0x28
+#define RT5631_SPK_MONO_OUT_CTRL 0x2A
+#define RT5631_SPK_MONO_HP_OUT_CTRL 0x2C
+#define RT5631_SDP_CTRL 0x34
+#define RT5631_MONO_SDP_CTRL 0x36
+#define RT5631_STEREO_AD_DA_CLK_CTRL 0x38
+#define RT5631_PWR_MANAG_ADD1 0x3A
+#define RT5631_PWR_MANAG_ADD2 0x3B
+#define RT5631_PWR_MANAG_ADD3 0x3C
+#define RT5631_PWR_MANAG_ADD4 0x3E
+#define RT5631_GEN_PUR_CTRL_REG 0x40
+#define RT5631_GLOBAL_CLK_CTRL 0x42
+#define RT5631_PLL_CTRL 0x44
+#define RT5631_INT_ST_IRQ_CTRL_1 0x48
+#define RT5631_INT_ST_IRQ_CTRL_2 0x4A
+#define RT5631_GPIO_CTRL 0x4C
+#define RT5631_MISC_CTRL 0x52
+#define RT5631_DEPOP_FUN_CTRL_1 0x54
+#define RT5631_DEPOP_FUN_CTRL_2 0x56
+#define RT5631_JACK_DET_CTRL 0x5A
+#define RT5631_SOFT_VOL_CTRL 0x5C
+#define RT5631_ALC_CTRL_1 0x64
+#define RT5631_ALC_CTRL_2 0x65
+#define RT5631_ALC_CTRL_3 0x66
+#define RT5631_PSEUDO_SPATL_CTRL 0x68
+#define RT5631_INDEX_ADD 0x6A
+#define RT5631_INDEX_DATA 0x6C
+#define RT5631_EQ_CTRL 0x6E
+#define RT5631_VENDOR_ID 0x7A
+#define RT5631_VENDOR_ID1 0x7C
+#define RT5631_VENDOR_ID2 0x7E
+
+/* Index of Codec Private Register definition */
+#define RT5631_EQ_BW_LOP 0x00
+#define RT5631_EQ_GAIN_LOP 0x01
+#define RT5631_EQ_FC_BP1 0x02
+#define RT5631_EQ_BW_BP1 0x03
+#define RT5631_EQ_GAIN_BP1 0x04
+#define RT5631_EQ_FC_BP2 0x05
+#define RT5631_EQ_BW_BP2 0x06
+#define RT5631_EQ_GAIN_BP2 0x07
+#define RT5631_EQ_FC_BP3 0x08
+#define RT5631_EQ_BW_BP3 0x09
+#define RT5631_EQ_GAIN_BP3 0x0a
+#define RT5631_EQ_BW_HIP 0x0b
+#define RT5631_EQ_GAIN_HIP 0x0c
+#define RT5631_EQ_HPF_A1 0x0d
+#define RT5631_EQ_HPF_A2 0x0e
+#define RT5631_EQ_HPF_GAIN 0x0f
+#define RT5631_EQ_PRE_VOL_CTRL 0x11
+#define RT5631_EQ_POST_VOL_CTRL 0x12
+#define RT5631_TEST_MODE_CTRL 0x39
+#define RT5631_CP_INTL_REG2 0x45
+#define RT5631_ADDA_MIXER_INTL_REG3 0x52
+#define RT5631_SPK_INTL_CTRL 0x56
+
+
+/* global definition */
+#define RT5631_L_MUTE (0x1 << 15)
+#define RT5631_L_MUTE_SHIFT 15
+#define RT5631_L_EN (0x1 << 14)
+#define RT5631_L_EN_SHIFT 14
+#define RT5631_R_MUTE (0x1 << 7)
+#define RT5631_R_MUTE_SHIFT 7
+#define RT5631_R_EN (0x1 << 6)
+#define RT5631_R_EN_SHIFT 6
+#define RT5631_VOL_MASK 0x1f
+#define RT5631_L_VOL_SHIFT 8
+#define RT5631_R_VOL_SHIFT 0
+
+/* Speaker Output Control(0x02) */
+#define RT5631_SPK_L_VOL_SEL_MASK (0x1 << 14)
+#define RT5631_SPK_L_VOL_SEL_VMID (0x0 << 14)
+#define RT5631_SPK_L_VOL_SEL_SPKMIX_L (0x1 << 14)
+#define RT5631_SPK_R_VOL_SEL_MASK (0x1 << 6)
+#define RT5631_SPK_R_VOL_SEL_VMID (0x0 << 6)
+#define RT5631_SPK_R_VOL_SEL_SPKMIX_R (0x1 << 6)
+
+/* Headphone Output Control(0x04) */
+#define RT5631_HP_L_VOL_SEL_MASK (0x1 << 14)
+#define RT5631_HP_L_VOL_SEL_VMID (0x0 << 14)
+#define RT5631_HP_L_VOL_SEL_OUTMIX_L (0x1 << 14)
+#define RT5631_HP_R_VOL_SEL_MASK (0x1 << 6)
+#define RT5631_HP_R_VOL_SEL_VMID (0x0 << 6)
+#define RT5631_HP_R_VOL_SEL_OUTMIX_R (0x1 << 6)
+
+/* Output Control for AUXOUT/MONO(0x06) */
+#define RT5631_AUXOUT_1_VOL_SEL_MASK (0x1 << 14)
+#define RT5631_AUXOUT_1_VOL_SEL_VMID (0x0 << 14)
+#define RT5631_AUXOUT_1_VOL_SEL_OUTMIX_L (0x1 << 14)
+#define RT5631_MUTE_MONO (0x1 << 13)
+#define RT5631_MUTE_MONO_SHIFT 13
+#define RT5631_AUXOUT_2_VOL_SEL_MASK (0x1 << 6)
+#define RT5631_AUXOUT_2_VOL_SEL_VMID (0x0 << 6)
+#define RT5631_AUXOUT_2_VOL_SEL_OUTMIX_R (0x1 << 6)
+
+/* Microphone Input Control 1(0x0E) */
+#define RT5631_MIC1_DIFF_INPUT_CTRL (0x1 << 15)
+#define RT5631_MIC1_DIFF_INPUT_SHIFT 15
+#define RT5631_MIC2_DIFF_INPUT_CTRL (0x1 << 7)
+#define RT5631_MIC2_DIFF_INPUT_SHIFT 7
+
+/* Stereo DAC Digital Volume2(0x10) */
+#define RT5631_DAC_VOL_MASK 0xff
+
+/* ADC Recording Mixer Control(0x14) */
+#define RT5631_M_OUTMIXER_L_TO_RECMIXER_L (0x1 << 15)
+#define RT5631_M_OUTMIXL_RECMIXL_BIT 15
+#define RT5631_M_MIC1_TO_RECMIXER_L (0x1 << 14)
+#define RT5631_M_MIC1_RECMIXL_BIT 14
+#define RT5631_M_AXIL_TO_RECMIXER_L (0x1 << 13)
+#define RT5631_M_AXIL_RECMIXL_BIT 13
+#define RT5631_M_MONO_IN_TO_RECMIXER_L (0x1 << 12)
+#define RT5631_M_MONO_IN_RECMIXL_BIT 12
+#define RT5631_M_OUTMIXER_R_TO_RECMIXER_R (0x1 << 7)
+#define RT5631_M_OUTMIXR_RECMIXR_BIT 7
+#define RT5631_M_MIC2_TO_RECMIXER_R (0x1 << 6)
+#define RT5631_M_MIC2_RECMIXR_BIT 6
+#define RT5631_M_AXIR_TO_RECMIXER_R (0x1 << 5)
+#define RT5631_M_AXIR_RECMIXR_BIT 5
+#define RT5631_M_MONO_IN_TO_RECMIXER_R (0x1 << 4)
+#define RT5631_M_MONO_IN_RECMIXR_BIT 4
+
+/* Left Output Mixer Control(0x1A) */
+#define RT5631_M_RECMIXER_L_TO_OUTMIXER_L (0x1 << 15)
+#define RT5631_M_RECMIXL_OUTMIXL_BIT 15
+#define RT5631_M_RECMIXER_R_TO_OUTMIXER_L (0x1 << 14)
+#define RT5631_M_RECMIXR_OUTMIXL_BIT 14
+#define RT5631_M_DAC_L_TO_OUTMIXER_L (0x1 << 13)
+#define RT5631_M_DACL_OUTMIXL_BIT 13
+#define RT5631_M_MIC1_TO_OUTMIXER_L (0x1 << 12)
+#define RT5631_M_MIC1_OUTMIXL_BIT 12
+#define RT5631_M_MIC2_TO_OUTMIXER_L (0x1 << 11)
+#define RT5631_M_MIC2_OUTMIXL_BIT 11
+#define RT5631_M_MONO_IN_P_TO_OUTMIXER_L (0x1 << 10)
+#define RT5631_M_MONO_INP_OUTMIXL_BIT 10
+#define RT5631_M_AXIL_TO_OUTMIXER_L (0x1 << 9)
+#define RT5631_M_AXIL_OUTMIXL_BIT 9
+#define RT5631_M_AXIR_TO_OUTMIXER_L (0x1 << 8)
+#define RT5631_M_AXIR_OUTMIXL_BIT 8
+#define RT5631_M_VDAC_TO_OUTMIXER_L (0x1 << 7)
+#define RT5631_M_VDAC_OUTMIXL_BIT 7
+
+/* Right Output Mixer Control(0x1C) */
+#define RT5631_M_RECMIXER_L_TO_OUTMIXER_R (0x1 << 15)
+#define RT5631_M_RECMIXL_OUTMIXR_BIT 15
+#define RT5631_M_RECMIXER_R_TO_OUTMIXER_R (0x1 << 14)
+#define RT5631_M_RECMIXR_OUTMIXR_BIT 14
+#define RT5631_M_DAC_R_TO_OUTMIXER_R (0x1 << 13)
+#define RT5631_M_DACR_OUTMIXR_BIT 13
+#define RT5631_M_MIC1_TO_OUTMIXER_R (0x1 << 12)
+#define RT5631_M_MIC1_OUTMIXR_BIT 12
+#define RT5631_M_MIC2_TO_OUTMIXER_R (0x1 << 11)
+#define RT5631_M_MIC2_OUTMIXR_BIT 11
+#define RT5631_M_MONO_IN_N_TO_OUTMIXER_R (0x1 << 10)
+#define RT5631_M_MONO_INN_OUTMIXR_BIT 10
+#define RT5631_M_AXIL_TO_OUTMIXER_R (0x1 << 9)
+#define RT5631_M_AXIL_OUTMIXR_BIT 9
+#define RT5631_M_AXIR_TO_OUTMIXER_R (0x1 << 8)
+#define RT5631_M_AXIR_OUTMIXR_BIT 8
+#define RT5631_M_VDAC_TO_OUTMIXER_R (0x1 << 7)
+#define RT5631_M_VDAC_OUTMIXR_BIT 7
+
+/* Lout Mixer Control(0x1E) */
+#define RT5631_M_MIC1_TO_AXO1MIXER (0x1 << 15)
+#define RT5631_M_MIC1_AXO1MIX_BIT 15
+#define RT5631_M_MIC2_TO_AXO1MIXER (0x1 << 11)
+#define RT5631_M_MIC2_AXO1MIX_BIT 11
+#define RT5631_M_OUTMIXER_L_TO_AXO1MIXER (0x1 << 7)
+#define RT5631_M_OUTMIXL_AXO1MIX_BIT 7
+#define RT5631_M_OUTMIXER_R_TO_AXO1MIXER (0x1 << 6)
+#define RT5631_M_OUTMIXR_AXO1MIX_BIT 6
+
+/* Rout Mixer Control(0x20) */
+#define RT5631_M_MIC1_TO_AXO2MIXER (0x1 << 15)
+#define RT5631_M_MIC1_AXO2MIX_BIT 15
+#define RT5631_M_MIC2_TO_AXO2MIXER (0x1 << 11)
+#define RT5631_M_MIC2_AXO2MIX_BIT 11
+#define RT5631_M_OUTMIXER_L_TO_AXO2MIXER (0x1 << 7)
+#define RT5631_M_OUTMIXL_AXO2MIX_BIT 7
+#define RT5631_M_OUTMIXER_R_TO_AXO2MIXER (0x1 << 6)
+#define RT5631_M_OUTMIXR_AXO2MIX_BIT 6
+
+/* Micphone Input Control 2(0x22) */
+#define RT5631_MIC_BIAS_90_PRECNET_AVDD 1
+#define RT5631_MIC_BIAS_75_PRECNET_AVDD 2
+
+#define RT5631_MIC1_BOOST_CTRL_MASK (0xf << 12)
+#define RT5631_MIC1_BOOST_CTRL_BYPASS (0x0 << 12)
+#define RT5631_MIC1_BOOST_CTRL_20DB (0x1 << 12)
+#define RT5631_MIC1_BOOST_CTRL_24DB (0x2 << 12)
+#define RT5631_MIC1_BOOST_CTRL_30DB (0x3 << 12)
+#define RT5631_MIC1_BOOST_CTRL_35DB (0x4 << 12)
+#define RT5631_MIC1_BOOST_CTRL_40DB (0x5 << 12)
+#define RT5631_MIC1_BOOST_CTRL_34DB (0x6 << 12)
+#define RT5631_MIC1_BOOST_CTRL_50DB (0x7 << 12)
+#define RT5631_MIC1_BOOST_CTRL_52DB (0x8 << 12)
+#define RT5631_MIC1_BOOST_SHIFT 12
+
+#define RT5631_MIC2_BOOST_CTRL_MASK (0xf << 8)
+#define RT5631_MIC2_BOOST_CTRL_BYPASS (0x0 << 8)
+#define RT5631_MIC2_BOOST_CTRL_20DB (0x1 << 8)
+#define RT5631_MIC2_BOOST_CTRL_24DB (0x2 << 8)
+#define RT5631_MIC2_BOOST_CTRL_30DB (0x3 << 8)
+#define RT5631_MIC2_BOOST_CTRL_35DB (0x4 << 8)
+#define RT5631_MIC2_BOOST_CTRL_40DB (0x5 << 8)
+#define RT5631_MIC2_BOOST_CTRL_34DB (0x6 << 8)
+#define RT5631_MIC2_BOOST_CTRL_50DB (0x7 << 8)
+#define RT5631_MIC2_BOOST_CTRL_52DB (0x8 << 8)
+#define RT5631_MIC2_BOOST_SHIFT 8
+
+#define RT5631_MICBIAS1_VOLT_CTRL_MASK (0x1 << 7)
+#define RT5631_MICBIAS1_VOLT_CTRL_90P (0x0 << 7)
+#define RT5631_MICBIAS1_VOLT_CTRL_75P (0x1 << 7)
+
+#define RT5631_MICBIAS1_S_C_DET_MASK (0x1 << 6)
+#define RT5631_MICBIAS1_S_C_DET_DIS (0x0 << 6)
+#define RT5631_MICBIAS1_S_C_DET_ENA (0x1 << 6)
+
+#define RT5631_MICBIAS1_SHORT_CURR_DET_MASK (0x3 << 4)
+#define RT5631_MICBIAS1_SHORT_CURR_DET_600UA (0x0 << 4)
+#define RT5631_MICBIAS1_SHORT_CURR_DET_1500UA (0x1 << 4)
+#define RT5631_MICBIAS1_SHORT_CURR_DET_2000UA (0x2 << 4)
+
+#define RT5631_MICBIAS2_VOLT_CTRL_MASK (0x1 << 3)
+#define RT5631_MICBIAS2_VOLT_CTRL_90P (0x0 << 3)
+#define RT5631_MICBIAS2_VOLT_CTRL_75P (0x1 << 3)
+
+#define RT5631_MICBIAS2_S_C_DET_MASK (0x1 << 2)
+#define RT5631_MICBIAS2_S_C_DET_DIS (0x0 << 2)
+#define RT5631_MICBIAS2_S_C_DET_ENA (0x1 << 2)
+
+#define RT5631_MICBIAS2_SHORT_CURR_DET_MASK (0x3)
+#define RT5631_MICBIAS2_SHORT_CURR_DET_600UA (0x0)
+#define RT5631_MICBIAS2_SHORT_CURR_DET_1500UA (0x1)
+#define RT5631_MICBIAS2_SHORT_CURR_DET_2000UA (0x2)
+
+
+/* Digital Microphone Control(0x24) */
+#define RT5631_DMIC_ENA_MASK (0x1 << 15)
+#define RT5631_DMIC_ENA_SHIFT 15
+/* DMIC_ENA: DMIC to ADC Digital filter */
+#define RT5631_DMIC_ENA (0x1 << 15)
+/* DMIC_DIS: ADC mixer to ADC Digital filter */
+#define RT5631_DMIC_DIS (0x0 << 15)
+#define RT5631_DMIC_L_CH_MUTE (0x1 << 13)
+#define RT5631_DMIC_L_CH_MUTE_SHIFT 13
+#define RT5631_DMIC_R_CH_MUTE (0x1 << 12)
+#define RT5631_DMIC_R_CH_MUTE_SHIFT 12
+#define RT5631_DMIC_L_CH_LATCH_MASK (0x1 << 9)
+#define RT5631_DMIC_L_CH_LATCH_RISING (0x1 << 9)
+#define RT5631_DMIC_L_CH_LATCH_FALLING (0x0 << 9)
+#define RT5631_DMIC_R_CH_LATCH_MASK (0x1 << 8)
+#define RT5631_DMIC_R_CH_LATCH_RISING (0x1 << 8)
+#define RT5631_DMIC_R_CH_LATCH_FALLING (0x0 << 8)
+#define RT5631_DMIC_CLK_CTRL_MASK (0x3 << 4)
+#define RT5631_DMIC_CLK_CTRL_TO_128FS (0x0 << 4)
+#define RT5631_DMIC_CLK_CTRL_TO_64FS (0x1 << 4)
+#define RT5631_DMIC_CLK_CTRL_TO_32FS (0x2 << 4)
+
+/* Microphone Input Volume(0x26) */
+#define RT5631_MONO_DIFF_INPUT_SHIFT 15
+
+/* Speaker Mixer Control(0x28) */
+#define RT5631_M_RECMIXER_L_TO_SPKMIXER_L (0x1 << 15)
+#define RT5631_M_RECMIXL_SPKMIXL_BIT 15
+#define RT5631_M_MIC1_P_TO_SPKMIXER_L (0x1 << 14)
+#define RT5631_M_MIC1P_SPKMIXL_BIT 14
+#define RT5631_M_DAC_L_TO_SPKMIXER_L (0x1 << 13)
+#define RT5631_M_DACL_SPKMIXL_BIT 13
+#define RT5631_M_OUTMIXER_L_TO_SPKMIXER_L (0x1 << 12)
+#define RT5631_M_OUTMIXL_SPKMIXL_BIT 12
+
+#define RT5631_M_RECMIXER_R_TO_SPKMIXER_R (0x1 << 7)
+#define RT5631_M_RECMIXR_SPKMIXR_BIT 7
+#define RT5631_M_MIC2_P_TO_SPKMIXER_R (0x1 << 6)
+#define RT5631_M_MIC2P_SPKMIXR_BIT 6
+#define RT5631_M_DAC_R_TO_SPKMIXER_R (0x1 << 5)
+#define RT5631_M_DACR_SPKMIXR_BIT 5
+#define RT5631_M_OUTMIXER_R_TO_SPKMIXER_R (0x1 << 4)
+#define RT5631_M_OUTMIXR_SPKMIXR_BIT 4
+
+/* Speaker/Mono Output Control(0x2A) */
+#define RT5631_M_SPKVOL_L_TO_SPOL_MIXER (0x1 << 15)
+#define RT5631_M_SPKVOLL_SPOLMIX_BIT 15
+#define RT5631_M_SPKVOL_R_TO_SPOL_MIXER (0x1 << 14)
+#define RT5631_M_SPKVOLR_SPOLMIX_BIT 14
+#define RT5631_M_SPKVOL_L_TO_SPOR_MIXER (0x1 << 13)
+#define RT5631_M_SPKVOLL_SPORMIX_BIT 13
+#define RT5631_M_SPKVOL_R_TO_SPOR_MIXER (0x1 << 12)
+#define RT5631_M_SPKVOLR_SPORMIX_BIT 12
+#define RT5631_M_OUTVOL_L_TO_MONOMIXER (0x1 << 11)
+#define RT5631_M_OUTVOLL_MONOMIX_BIT 11
+#define RT5631_M_OUTVOL_R_TO_MONOMIXER (0x1 << 10)
+#define RT5631_M_OUTVOLR_MONOMIX_BIT 10
+
+/* Speaker/Mono/HP Output Control(0x2C) */
+#define RT5631_SPK_L_MUX_SEL_MASK (0x3 << 14)
+#define RT5631_SPK_L_MUX_SEL_SPKMIXER_L (0x0 << 14)
+#define RT5631_SPK_L_MUX_SEL_MONO_IN (0x1 << 14)
+#define RT5631_SPK_L_MUX_SEL_DAC_L (0x3 << 14)
+#define RT5631_SPK_L_MUX_SEL_SHIFT 14
+
+#define RT5631_SPK_R_MUX_SEL_MASK (0x3 << 10)
+#define RT5631_SPK_R_MUX_SEL_SPKMIXER_R (0x0 << 10)
+#define RT5631_SPK_R_MUX_SEL_MONO_IN (0x1 << 10)
+#define RT5631_SPK_R_MUX_SEL_DAC_R (0x3 << 10)
+#define RT5631_SPK_R_MUX_SEL_SHIFT 10
+
+#define RT5631_MONO_MUX_SEL_MASK (0x3 << 6)
+#define RT5631_MONO_MUX_SEL_MONOMIXER (0x0 << 6)
+#define RT5631_MONO_MUX_SEL_MONO_IN (0x1 << 6)
+#define RT5631_MONO_MUX_SEL_SHIFT 6
+
+#define RT5631_HP_L_MUX_SEL_MASK (0x1 << 3)
+#define RT5631_HP_L_MUX_SEL_HPVOL_L (0x0 << 3)
+#define RT5631_HP_L_MUX_SEL_DAC_L (0x1 << 3)
+#define RT5631_HP_L_MUX_SEL_SHIFT 3
+
+#define RT5631_HP_R_MUX_SEL_MASK (0x1 << 2)
+#define RT5631_HP_R_MUX_SEL_HPVOL_R (0x0 << 2)
+#define RT5631_HP_R_MUX_SEL_DAC_R (0x1 << 2)
+#define RT5631_HP_R_MUX_SEL_SHIFT 2
+
+/* Stereo I2S Serial Data Port Control(0x34) */
+#define RT5631_SDP_MODE_SEL_MASK (0x1 << 15)
+#define RT5631_SDP_MODE_SEL_MASTER (0x0 << 15)
+#define RT5631_SDP_MODE_SEL_SLAVE (0x1 << 15)
+
+#define RT5631_SDP_ADC_CPS_SEL_MASK (0x3 << 10)
+#define RT5631_SDP_ADC_CPS_SEL_OFF (0x0 << 10)
+#define RT5631_SDP_ADC_CPS_SEL_U_LAW (0x1 << 10)
+#define RT5631_SDP_ADC_CPS_SEL_A_LAW (0x2 << 10)
+
+#define RT5631_SDP_DAC_CPS_SEL_MASK (0x3 << 8)
+#define RT5631_SDP_DAC_CPS_SEL_OFF (0x0 << 8)
+#define RT5631_SDP_DAC_CPS_SEL_U_LAW (0x1 << 8)
+#define RT5631_SDP_DAC_CPS_SEL_A_LAW (0x2 << 8)
+/* 0:Normal 1:Invert */
+#define RT5631_SDP_I2S_BCLK_POL_CTRL (0x1 << 7)
+/* 0:Normal 1:Invert */
+#define RT5631_SDP_DAC_R_INV (0x1 << 6)
+/* 0:ADC data appear at left phase of LRCK
+ * 1:ADC data appear at right phase of LRCK
+ */
+#define RT5631_SDP_ADC_DATA_L_R_SWAP (0x1 << 5)
+/* 0:DAC data appear at left phase of LRCK
+ * 1:DAC data appear at right phase of LRCK
+ */
+#define RT5631_SDP_DAC_DATA_L_R_SWAP (0x1 << 4)
+
+/* Data Length Slection */
+#define RT5631_SDP_I2S_DL_MASK (0x3 << 2)
+#define RT5631_SDP_I2S_DL_16 (0x0 << 2)
+#define RT5631_SDP_I2S_DL_20 (0x1 << 2)
+#define RT5631_SDP_I2S_DL_24 (0x2 << 2)
+#define RT5631_SDP_I2S_DL_8 (0x3 << 2)
+
+/* PCM Data Format Selection */
+#define RT5631_SDP_I2S_DF_MASK (0x3)
+#define RT5631_SDP_I2S_DF_I2S (0x0)
+#define RT5631_SDP_I2S_DF_LEFT (0x1)
+#define RT5631_SDP_I2S_DF_PCM_A (0x2)
+#define RT5631_SDP_I2S_DF_PCM_B (0x3)
+
+/* Stereo AD/DA Clock Control(0x38h) */
+#define RT5631_I2S_PRE_DIV_MASK (0x7 << 13)
+#define RT5631_I2S_PRE_DIV_1 (0x0 << 13)
+#define RT5631_I2S_PRE_DIV_2 (0x1 << 13)
+#define RT5631_I2S_PRE_DIV_4 (0x2 << 13)
+#define RT5631_I2S_PRE_DIV_8 (0x3 << 13)
+#define RT5631_I2S_PRE_DIV_16 (0x4 << 13)
+#define RT5631_I2S_PRE_DIV_32 (0x5 << 13)
+/* CLOCK RELATIVE OF BCLK AND LCRK */
+#define RT5631_I2S_LRCK_SEL_N_BCLK_MASK (0x1 << 12)
+#define RT5631_I2S_LRCK_SEL_64_BCLK (0x0 << 12) /* 64FS */
+#define RT5631_I2S_LRCK_SEL_32_BCLK (0x1 << 12) /* 32FS */
+
+#define RT5631_DAC_OSR_SEL_MASK (0x3 << 10)
+#define RT5631_DAC_OSR_SEL_128FS (0x3 << 10)
+#define RT5631_DAC_OSR_SEL_64FS (0x3 << 10)
+#define RT5631_DAC_OSR_SEL_32FS (0x3 << 10)
+#define RT5631_DAC_OSR_SEL_16FS (0x3 << 10)
+
+#define RT5631_ADC_OSR_SEL_MASK (0x3 << 8)
+#define RT5631_ADC_OSR_SEL_128FS (0x3 << 8)
+#define RT5631_ADC_OSR_SEL_64FS (0x3 << 8)
+#define RT5631_ADC_OSR_SEL_32FS (0x3 << 8)
+#define RT5631_ADC_OSR_SEL_16FS (0x3 << 8)
+
+#define RT5631_ADDA_FILTER_CLK_SEL_256FS (0 << 7) /* 256FS */
+#define RT5631_ADDA_FILTER_CLK_SEL_384FS (1 << 7) /* 384FS */
+
+/* Power managment addition 1 (0x3A) */
+#define RT5631_PWR_MAIN_I2S_EN (0x1 << 15)
+#define RT5631_PWR_MAIN_I2S_BIT 15
+#define RT5631_PWR_CLASS_D (0x1 << 12)
+#define RT5631_PWR_CLASS_D_BIT 12
+#define RT5631_PWR_ADC_L_CLK (0x1 << 11)
+#define RT5631_PWR_ADC_L_CLK_BIT 11
+#define RT5631_PWR_ADC_R_CLK (0x1 << 10)
+#define RT5631_PWR_ADC_R_CLK_BIT 10
+#define RT5631_PWR_DAC_L_CLK (0x1 << 9)
+#define RT5631_PWR_DAC_L_CLK_BIT 9
+#define RT5631_PWR_DAC_R_CLK (0x1 << 8)
+#define RT5631_PWR_DAC_R_CLK_BIT 8
+#define RT5631_PWR_DAC_REF (0x1 << 7)
+#define RT5631_PWR_DAC_REF_BIT 7
+#define RT5631_PWR_DAC_L_TO_MIXER (0x1 << 6)
+#define RT5631_PWR_DAC_L_TO_MIXER_BIT 6
+#define RT5631_PWR_DAC_R_TO_MIXER (0x1 << 5)
+#define RT5631_PWR_DAC_R_TO_MIXER_BIT 5
+
+/* Power managment addition 2 (0x3B) */
+#define RT5631_PWR_OUTMIXER_L (0x1 << 15)
+#define RT5631_PWR_OUTMIXER_L_BIT 15
+#define RT5631_PWR_OUTMIXER_R (0x1 << 14)
+#define RT5631_PWR_OUTMIXER_R_BIT 14
+#define RT5631_PWR_SPKMIXER_L (0x1 << 13)
+#define RT5631_PWR_SPKMIXER_L_BIT 13
+#define RT5631_PWR_SPKMIXER_R (0x1 << 12)
+#define RT5631_PWR_SPKMIXER_R_BIT 12
+#define RT5631_PWR_RECMIXER_L (0x1 << 11)
+#define RT5631_PWR_RECMIXER_L_BIT 11
+#define RT5631_PWR_RECMIXER_R (0x1 << 10)
+#define RT5631_PWR_RECMIXER_R_BIT 10
+#define RT5631_PWR_MIC1_BOOT_GAIN (0x1 << 5)
+#define RT5631_PWR_MIC1_BOOT_GAIN_BIT 5
+#define RT5631_PWR_MIC2_BOOT_GAIN (0x1 << 4)
+#define RT5631_PWR_MIC2_BOOT_GAIN_BIT 4
+#define RT5631_PWR_MICBIAS1_VOL (0x1 << 3)
+#define RT5631_PWR_MICBIAS1_VOL_BIT 3
+#define RT5631_PWR_MICBIAS2_VOL (0x1 << 2)
+#define RT5631_PWR_MICBIAS2_VOL_BIT 2
+#define RT5631_PWR_PLL1 (0x1 << 1)
+#define RT5631_PWR_PLL1_BIT 1
+#define RT5631_PWR_PLL2 (0x1 << 0)
+#define RT5631_PWR_PLL2_BIT 0
+
+/* Power managment addition 3(0x3C) */
+#define RT5631_PWR_VREF (0x1 << 15)
+#define RT5631_PWR_VREF_BIT 15
+#define RT5631_PWR_FAST_VREF_CTRL (0x1 << 14)
+#define RT5631_PWR_FAST_VREF_CTRL_BIT 14
+#define RT5631_PWR_MAIN_BIAS (0x1 << 13)
+#define RT5631_PWR_MAIN_BIAS_BIT 13
+#define RT5631_PWR_AXO1MIXER (0x1 << 11)
+#define RT5631_PWR_AXO1MIXER_BIT 11
+#define RT5631_PWR_AXO2MIXER (0x1 << 10)
+#define RT5631_PWR_AXO2MIXER_BIT 10
+#define RT5631_PWR_MONOMIXER (0x1 << 9)
+#define RT5631_PWR_MONOMIXER_BIT 9
+#define RT5631_PWR_MONO_DEPOP_DIS (0x1 << 8)
+#define RT5631_PWR_MONO_DEPOP_DIS_BIT 8
+#define RT5631_PWR_MONO_AMP_EN (0x1 << 7)
+#define RT5631_PWR_MONO_AMP_EN_BIT 7
+#define RT5631_PWR_CHARGE_PUMP (0x1 << 4)
+#define RT5631_PWR_CHARGE_PUMP_BIT 4
+#define RT5631_PWR_HP_L_AMP (0x1 << 3)
+#define RT5631_PWR_HP_L_AMP_BIT 3
+#define RT5631_PWR_HP_R_AMP (0x1 << 2)
+#define RT5631_PWR_HP_R_AMP_BIT 2
+#define RT5631_PWR_HP_DEPOP_DIS (0x1 << 1)
+#define RT5631_PWR_HP_DEPOP_DIS_BIT 1
+#define RT5631_PWR_HP_AMP_DRIVING (0x1 << 0)
+#define RT5631_PWR_HP_AMP_DRIVING_BIT 0
+
+/* Power managment addition 4(0x3E) */
+#define RT5631_PWR_SPK_L_VOL (0x1 << 15)
+#define RT5631_PWR_SPK_L_VOL_BIT 15
+#define RT5631_PWR_SPK_R_VOL (0x1 << 14)
+#define RT5631_PWR_SPK_R_VOL_BIT 14
+#define RT5631_PWR_LOUT_VOL (0x1 << 13)
+#define RT5631_PWR_LOUT_VOL_BIT 13
+#define RT5631_PWR_ROUT_VOL (0x1 << 12)
+#define RT5631_PWR_ROUT_VOL_BIT 12
+#define RT5631_PWR_HP_L_OUT_VOL (0x1 << 11)
+#define RT5631_PWR_HP_L_OUT_VOL_BIT 11
+#define RT5631_PWR_HP_R_OUT_VOL (0x1 << 10)
+#define RT5631_PWR_HP_R_OUT_VOL_BIT 10
+#define RT5631_PWR_AXIL_IN_VOL (0x1 << 9)
+#define RT5631_PWR_AXIL_IN_VOL_BIT 9
+#define RT5631_PWR_AXIR_IN_VOL (0x1 << 8)
+#define RT5631_PWR_AXIR_IN_VOL_BIT 8
+#define RT5631_PWR_MONO_IN_P_VOL (0x1 << 7)
+#define RT5631_PWR_MONO_IN_P_VOL_BIT 7
+#define RT5631_PWR_MONO_IN_N_VOL (0x1 << 6)
+#define RT5631_PWR_MONO_IN_N_VOL_BIT 6
+
+/* General Purpose Control Register(0x40) */
+#define RT5631_SPK_AMP_AUTO_RATIO_EN (0x1 << 15)
+
+#define RT5631_SPK_AMP_RATIO_CTRL_MASK (0x7 << 12)
+#define RT5631_SPK_AMP_RATIO_CTRL_2_34 (0x0 << 12) /* 7.40DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_99 (0x1 << 12) /* 5.99DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_68 (0x2 << 12) /* 4.50DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_56 (0x3 << 12) /* 3.86DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_44 (0x4 << 12) /* 3.16DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_27 (0x5 << 12) /* 2.10DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_09 (0x6 << 12) /* 0.80DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_1_00 (0x7 << 12) /* 0.00DB */
+#define RT5631_SPK_AMP_RATIO_CTRL_SHIFT 12
+
+#define RT5631_STEREO_DAC_HI_PASS_FILT_EN (0x1 << 11)
+#define RT5631_STEREO_ADC_HI_PASS_FILT_EN (0x1 << 10)
+/* Select ADC Wind Filter Clock type */
+#define RT5631_ADC_WIND_FILT_MASK (0x3 << 4)
+#define RT5631_ADC_WIND_FILT_8_16_32K (0x0 << 4) /*8/16/32k*/
+#define RT5631_ADC_WIND_FILT_11_22_44K (0x1 << 4) /*11/22/44k*/
+#define RT5631_ADC_WIND_FILT_12_24_48K (0x2 << 4) /*12/24/48k*/
+#define RT5631_ADC_WIND_FILT_EN (0x1 << 3)
+/* SelectADC Wind Filter Corner Frequency */
+#define RT5631_ADC_WIND_CNR_FREQ_MASK (0x7 << 0)
+#define RT5631_ADC_WIND_CNR_FREQ_82_113_122 (0x0 << 0) /* 82/113/122 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_102_141_153 (0x1 << 0) /* 102/141/153 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_131_180_156 (0x2 << 0) /* 131/180/156 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_163_225_245 (0x3 << 0) /* 163/225/245 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_204_281_306 (0x4 << 0) /* 204/281/306 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_261_360_392 (0x5 << 0) /* 261/360/392 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_327_450_490 (0x6 << 0) /* 327/450/490 Hz */
+#define RT5631_ADC_WIND_CNR_FREQ_408_563_612 (0x7 << 0) /* 408/563/612 Hz */
+
+/* Global Clock Control Register(0x42) */
+#define RT5631_SYSCLK_SOUR_SEL_MASK (0x3 << 14)
+#define RT5631_SYSCLK_SOUR_SEL_MCLK (0x0 << 14)
+#define RT5631_SYSCLK_SOUR_SEL_PLL (0x1 << 14)
+#define RT5631_SYSCLK_SOUR_SEL_PLL_TCK (0x2 << 14)
+
+#define RT5631_PLLCLK_SOUR_SEL_MASK (0x3 << 12)
+#define RT5631_PLLCLK_SOUR_SEL_MCLK (0x0 << 12)
+#define RT5631_PLLCLK_SOUR_SEL_BCLK (0x1 << 12)
+#define RT5631_PLLCLK_SOUR_SEL_VBCLK (0x2 << 12)
+
+#define RT5631_PLLCLK_PRE_DIV1 (0x0 << 11)
+#define RT5631_PLLCLK_PRE_DIV2 (0x1 << 11)
+
+/* PLL Control(0x44) */
+#define RT5631_PLL_CTRL_M_VAL(m) ((m)&0xf)
+#define RT5631_PLL_CTRL_K_VAL(k) (((k)&0x7) << 4)
+#define RT5631_PLL_CTRL_N_VAL(n) (((n)&0xff) << 8)
+
+/* Internal Status and IRQ Control2(0x4A) */
+#define RT5631_ADC_DATA_SEL_MASK (0x3 << 14)
+#define RT5631_ADC_DATA_SEL_Disable (0x0 << 14)
+#define RT5631_ADC_DATA_SEL_MIC1 (0x1 << 14)
+#define RT5631_ADC_DATA_SEL_MIC1_SHIFT 14
+#define RT5631_ADC_DATA_SEL_MIC2 (0x2 << 14)
+#define RT5631_ADC_DATA_SEL_MIC2_SHIFT 15
+#define RT5631_ADC_DATA_SEL_STO (0x3 << 14)
+#define RT5631_ADC_DATA_SEL_SHIFT 14
+
+/* GPIO Pin Configuration(0x4C) */
+#define RT5631_GPIO_PIN_FUN_SEL_MASK (0x1 << 15)
+#define RT5631_GPIO_PIN_FUN_SEL_IRQ (0x1 << 15)
+#define RT5631_GPIO_PIN_FUN_SEL_GPIO_DIMC (0x0 << 15)
+
+#define RT5631_GPIO_DMIC_FUN_SEL_MASK (0x1 << 3)
+#define RT5631_GPIO_DMIC_FUN_SEL_DIMC (0x1 << 3)
+#define RT5631_GPIO_DMIC_FUN_SEL_GPIO (0x0 << 3)
+
+#define RT5631_GPIO_PIN_CON_MASK (0x1 << 2)
+#define RT5631_GPIO_PIN_SET_INPUT (0x0 << 2)
+#define RT5631_GPIO_PIN_SET_OUTPUT (0x1 << 2)
+
+/* De-POP function Control 1(0x54) */
+#define RT5631_POW_ON_SOFT_GEN (0x1 << 15)
+#define RT5631_EN_MUTE_UNMUTE_DEPOP (0x1 << 14)
+#define RT5631_EN_DEPOP2_FOR_HP (0x1 << 7)
+/* Power Down HPAMP_L Starts Up Signal */
+#define RT5631_PD_HPAMP_L_ST_UP (0x1 << 5)
+/* Power Down HPAMP_R Starts Up Signal */
+#define RT5631_PD_HPAMP_R_ST_UP (0x1 << 4)
+/* Enable left HP mute/unmute depop */
+#define RT5631_EN_HP_L_M_UN_MUTE_DEPOP (0x1 << 1)
+/* Enable right HP mute/unmute depop */
+#define RT5631_EN_HP_R_M_UN_MUTE_DEPOP (0x1 << 0)
+
+/* De-POP Fnction Control(0x56) */
+#define RT5631_EN_ONE_BIT_DEPOP (0x1 << 15)
+#define RT5631_EN_CAP_FREE_DEPOP (0x1 << 14)
+
+/* Jack Detect Control Register(0x5A) */
+#define RT5631_JD_USE_MASK (0x3 << 14)
+#define RT5631_JD_USE_JD2 (0x3 << 14)
+#define RT5631_JD_USE_JD1 (0x2 << 14)
+#define RT5631_JD_USE_GPIO (0x1 << 14)
+#define RT5631_JD_OFF (0x0 << 14)
+/* JD trigger enable for HP */
+#define RT5631_JD_HP_EN (0x1 << 11)
+#define RT5631_JD_HP_TRI_MASK (0x1 << 10)
+#define RT5631_JD_HP_TRI_HI (0x1 << 10)
+#define RT5631_JD_HP_TRI_LO (0x1 << 10)
+/* JD trigger enable for speaker LP/LN */
+#define RT5631_JD_SPK_L_EN (0x1 << 9)
+#define RT5631_JD_SPK_L_TRI_MASK (0x1 << 8)
+#define RT5631_JD_SPK_L_TRI_HI (0x1 << 8)
+#define RT5631_JD_SPK_L_TRI_LO (0x0 << 8)
+/* JD trigger enable for speaker RP/RN */
+#define RT5631_JD_SPK_R_EN (0x1 << 7)
+#define RT5631_JD_SPK_R_TRI_MASK (0x1 << 6)
+#define RT5631_JD_SPK_R_TRI_HI (0x1 << 6)
+#define RT5631_JD_SPK_R_TRI_LO (0x0 << 6)
+/* JD trigger enable for monoout */
+#define RT5631_JD_MONO_EN (0x1 << 5)
+#define RT5631_JD_MONO_TRI_MASK (0x1 << 4)
+#define RT5631_JD_MONO_TRI_HI (0x1 << 4)
+#define RT5631_JD_MONO_TRI_LO (0x0 << 4)
+/* JD trigger enable for Lout */
+#define RT5631_JD_AUX_1_EN (0x1 << 3)
+#define RT5631_JD_AUX_1_MASK (0x1 << 2)
+#define RT5631_JD_AUX_1_TRI_HI (0x1 << 2)
+#define RT5631_JD_AUX_1_TRI_LO (0x0 << 2)
+/* JD trigger enable for Rout */
+#define RT5631_JD_AUX_2_EN (0x1 << 1)
+#define RT5631_JD_AUX_2_MASK (0x1 << 0)
+#define RT5631_JD_AUX_2_TRI_HI (0x1 << 0)
+#define RT5631_JD_AUX_2_TRI_LO (0x0 << 0)
+
+/* ALC CONTROL 1(0x64) */
+#define RT5631_ALC_ATTACK_RATE_MASK (0x1F << 8)
+#define RT5631_ALC_RECOVERY_RATE_MASK (0x1F << 0)
+
+/* ALC CONTROL 2(0x65) */
+/* select Compensation gain for Noise gate function */
+#define RT5631_ALC_COM_NOISE_GATE_MASK (0xF << 0)
+
+/* ALC CONTROL 3(0x66) */
+#define RT5631_ALC_FUN_MASK (0x3 << 14)
+#define RT5631_ALC_FUN_DIS (0x0 << 14)
+#define RT5631_ALC_ENA_DAC_PATH (0x1 << 14)
+#define RT5631_ALC_ENA_ADC_PATH (0x3 << 14)
+#define RT5631_ALC_PARA_UPDATE (0x1 << 13)
+#define RT5631_ALC_LIMIT_LEVEL_MASK (0x1F << 8)
+#define RT5631_ALC_NOISE_GATE_FUN_MASK (0x1 << 7)
+#define RT5631_ALC_NOISE_GATE_FUN_DIS (0x0 << 7)
+#define RT5631_ALC_NOISE_GATE_FUN_ENA (0x1 << 7)
+/* ALC noise gate hold data function */
+#define RT5631_ALC_NOISE_GATE_H_D_MASK (0x1 << 6)
+#define RT5631_ALC_NOISE_GATE_H_D_DIS (0x0 << 6)
+#define RT5631_ALC_NOISE_GATE_H_D_ENA (0x1 << 6)
+
+/* Psedueo Stereo & Spatial Effect Block Control(0x68) */
+#define RT5631_SPATIAL_CTRL_EN (0x1 << 15)
+#define RT5631_ALL_PASS_FILTER_EN (0x1 << 14)
+#define RT5631_PSEUDO_STEREO_EN (0x1 << 13)
+#define RT5631_STEREO_EXPENSION_EN (0x1 << 12)
+/* 3D gain parameter */
+#define RT5631_GAIN_3D_PARA_MASK (0x3 << 6)
+#define RT5631_GAIN_3D_PARA_1_00 (0x0 << 6) /* 3D gain 1.0 */
+#define RT5631_GAIN_3D_PARA_1_50 (0x1 << 6) /* 3D gain 1.5 */
+#define RT5631_GAIN_3D_PARA_2_00 (0x2 << 6) /* 3D gain 2.0 */
+/* 3D ratio parameter */
+#define RT5631_RATIO_3D_MASK (0x3 << 4)
+#define RT5631_RATIO_3D_0_0 (0x0 << 4) /* 3D ratio 0.0 */
+#define RT5631_RATIO_3D_0_66 (0x1 << 4) /* 3D ratio 0.66 */
+#define RT5631_RATIO_3D_1_0 (0x2 << 4) /* 3D ratio 1.0 */
+/* select samplerate for all pass filter */
+#define RT5631_APF_FUN_SLE_MASK (0x3 << 0)
+#define RT5631_APF_FUN_SEL_48K (0x3 << 0)
+#define RT5631_APF_FUN_SEL_44_1K (0x2 << 0)
+#define RT5631_APF_FUN_SEL_32K (0x1 << 0)
+#define RT5631_APF_FUN_DIS (0x0 << 0)
+
+/* EQ CONTROL 1(0x6E) */
+#define RT5631_HW_EQ_PATH_SEL_MASK (0x1 << 15)
+#define RT5631_HW_EQ_PATH_SEL_DAC (0x0 << 15)
+#define RT5631_HW_EQ_PATH_SEL_ADC (0x1 << 15)
+#define RT5631_HW_EQ_UPDATE_CTRL (0x1 << 14)
+
+#define RT5631_EN_HW_EQ_HPF2 (0x1 << 5)
+#define RT5631_EN_HW_EQ_HPF1 (0x1 << 4)
+#define RT5631_EN_HW_EQ_BP3 (0x1 << 3)
+#define RT5631_EN_HW_EQ_BP2 (0x1 << 2)
+#define RT5631_EN_HW_EQ_BP1 (0x1 << 1)
+#define RT5631_EN_HW_EQ_LPF (0x1 << 0)
+
+
+#endif /* __RTCODEC5631_H__ */
diff --git a/kernel/sound/soc/codecs/rt5640.c b/kernel/sound/soc/codecs/rt5640.c
new file mode 100644
index 000000000..178e55d4d
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5640.c
@@ -0,0 +1,2258 @@
+/*
+ * 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>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gpio.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/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 "rt5640.h"
+
+#define RT5640_DEVICE_ID 0x6231
+
+#define RT5640_PR_RANGE_BASE (0xff + 1)
+#define RT5640_PR_SPACING 0x100
+
+#define RT5640_PR_BASE (RT5640_PR_RANGE_BASE + (0 * RT5640_PR_SPACING))
+
+static const struct regmap_range_cfg rt5640_ranges[] = {
+ { .name = "PR", .range_min = RT5640_PR_BASE,
+ .range_max = RT5640_PR_BASE + 0xb4,
+ .selector_reg = RT5640_PRIV_INDEX,
+ .selector_mask = 0xff,
+ .selector_shift = 0x0,
+ .window_start = RT5640_PRIV_DATA,
+ .window_len = 0x1, },
+};
+
+static struct reg_default 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 },
+ { 0x01, 0xc8c8 },
+ { 0x02, 0xc8c8 },
+ { 0x03, 0xc8c8 },
+ { 0x04, 0x8000 },
+ { 0x0d, 0x0000 },
+ { 0x0e, 0x0000 },
+ { 0x0f, 0x0808 },
+ { 0x19, 0xafaf },
+ { 0x1a, 0xafaf },
+ { 0x1b, 0x0000 },
+ { 0x1c, 0x2f2f },
+ { 0x1d, 0x2f2f },
+ { 0x1e, 0x0000 },
+ { 0x27, 0x7060 },
+ { 0x28, 0x7070 },
+ { 0x29, 0x8080 },
+ { 0x2a, 0x5454 },
+ { 0x2b, 0x5454 },
+ { 0x2c, 0xaa00 },
+ { 0x2d, 0x0000 },
+ { 0x2e, 0xa000 },
+ { 0x2f, 0x0000 },
+ { 0x3b, 0x0000 },
+ { 0x3c, 0x007f },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x007f },
+ { 0x45, 0xe000 },
+ { 0x46, 0x003e },
+ { 0x47, 0x003e },
+ { 0x48, 0xf800 },
+ { 0x49, 0x3800 },
+ { 0x4a, 0x0004 },
+ { 0x4c, 0xfc00 },
+ { 0x4d, 0x0000 },
+ { 0x4f, 0x01ff },
+ { 0x50, 0x0000 },
+ { 0x51, 0x0000 },
+ { 0x52, 0x01ff },
+ { 0x53, 0xf000 },
+ { 0x61, 0x0000 },
+ { 0x62, 0x0000 },
+ { 0x63, 0x00c0 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x0000 },
+ { 0x66, 0x0000 },
+ { 0x6a, 0x0000 },
+ { 0x6c, 0x0000 },
+ { 0x70, 0x8000 },
+ { 0x71, 0x8000 },
+ { 0x72, 0x8000 },
+ { 0x73, 0x1114 },
+ { 0x74, 0x0c00 },
+ { 0x75, 0x1d00 },
+ { 0x80, 0x0000 },
+ { 0x81, 0x0000 },
+ { 0x82, 0x0000 },
+ { 0x83, 0x0000 },
+ { 0x84, 0x0000 },
+ { 0x85, 0x0008 },
+ { 0x89, 0x0000 },
+ { 0x8a, 0x0000 },
+ { 0x8b, 0x0600 },
+ { 0x8c, 0x0228 },
+ { 0x8d, 0xa000 },
+ { 0x8e, 0x0004 },
+ { 0x8f, 0x1100 },
+ { 0x90, 0x0646 },
+ { 0x91, 0x0c00 },
+ { 0x92, 0x0000 },
+ { 0x93, 0x3000 },
+ { 0xb0, 0x2080 },
+ { 0xb1, 0x0000 },
+ { 0xb4, 0x2206 },
+ { 0xb5, 0x1f00 },
+ { 0xb6, 0x0000 },
+ { 0xb8, 0x034b },
+ { 0xb9, 0x0066 },
+ { 0xba, 0x000b },
+ { 0xbb, 0x0000 },
+ { 0xbc, 0x0000 },
+ { 0xbd, 0x0000 },
+ { 0xbe, 0x0000 },
+ { 0xbf, 0x0000 },
+ { 0xc0, 0x0400 },
+ { 0xc2, 0x0000 },
+ { 0xc4, 0x0000 },
+ { 0xc5, 0x0000 },
+ { 0xc6, 0x2000 },
+ { 0xc8, 0x0000 },
+ { 0xc9, 0x0000 },
+ { 0xca, 0x0000 },
+ { 0xcb, 0x0000 },
+ { 0xcc, 0x0000 },
+ { 0xcf, 0x0013 },
+ { 0xd0, 0x0680 },
+ { 0xd1, 0x1c17 },
+ { 0xd2, 0x8c00 },
+ { 0xd3, 0xaa20 },
+ { 0xd6, 0x0400 },
+ { 0xd9, 0x0809 },
+ { 0xfe, 0x10ec },
+ { 0xff, 0x6231 },
+};
+
+static int rt5640_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, RT5640_RESET, 0);
+}
+
+static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5640_ranges); i++)
+ if ((reg >= rt5640_ranges[i].window_start &&
+ reg <= rt5640_ranges[i].window_start +
+ rt5640_ranges[i].window_len) ||
+ (reg >= rt5640_ranges[i].range_min &&
+ reg <= rt5640_ranges[i].range_max))
+ return true;
+
+ switch (reg) {
+ case RT5640_RESET:
+ case RT5640_ASRC_5:
+ case RT5640_EQ_CTRL1:
+ case RT5640_DRC_AGC_1:
+ case RT5640_ANC_CTRL1:
+ case RT5640_IRQ_CTRL2:
+ case RT5640_INT_IRQ_ST:
+ case RT5640_DSP_CTRL2:
+ case RT5640_DSP_CTRL3:
+ case RT5640_PRIV_INDEX:
+ case RT5640_PRIV_DATA:
+ case RT5640_PGM_REG_ARR1:
+ case RT5640_PGM_REG_ARR3:
+ case RT5640_VENDOR_ID:
+ case RT5640_VENDOR_ID1:
+ case RT5640_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5640_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5640_ranges); i++)
+ if ((reg >= rt5640_ranges[i].window_start &&
+ reg <= rt5640_ranges[i].window_start +
+ rt5640_ranges[i].window_len) ||
+ (reg >= rt5640_ranges[i].range_min &&
+ reg <= rt5640_ranges[i].range_max))
+ return true;
+
+ switch (reg) {
+ case RT5640_RESET:
+ case RT5640_SPK_VOL:
+ case RT5640_HP_VOL:
+ case RT5640_OUTPUT:
+ case RT5640_MONO_OUT:
+ case RT5640_IN1_IN2:
+ case RT5640_IN3_IN4:
+ case RT5640_INL_INR_VOL:
+ case RT5640_DAC1_DIG_VOL:
+ case RT5640_DAC2_DIG_VOL:
+ case RT5640_DAC2_CTRL:
+ case RT5640_ADC_DIG_VOL:
+ case RT5640_ADC_DATA:
+ case RT5640_ADC_BST_VOL:
+ case RT5640_STO_ADC_MIXER:
+ case RT5640_MONO_ADC_MIXER:
+ case RT5640_AD_DA_MIXER:
+ case RT5640_STO_DAC_MIXER:
+ case RT5640_MONO_DAC_MIXER:
+ case RT5640_DIG_MIXER:
+ case RT5640_DSP_PATH1:
+ case RT5640_DSP_PATH2:
+ case RT5640_DIG_INF_DATA:
+ case RT5640_REC_L1_MIXER:
+ case RT5640_REC_L2_MIXER:
+ case RT5640_REC_R1_MIXER:
+ case RT5640_REC_R2_MIXER:
+ case RT5640_HPO_MIXER:
+ case RT5640_SPK_L_MIXER:
+ case RT5640_SPK_R_MIXER:
+ case RT5640_SPO_L_MIXER:
+ case RT5640_SPO_R_MIXER:
+ case RT5640_SPO_CLSD_RATIO:
+ case RT5640_MONO_MIXER:
+ case RT5640_OUT_L1_MIXER:
+ case RT5640_OUT_L2_MIXER:
+ case RT5640_OUT_L3_MIXER:
+ case RT5640_OUT_R1_MIXER:
+ case RT5640_OUT_R2_MIXER:
+ case RT5640_OUT_R3_MIXER:
+ case RT5640_LOUT_MIXER:
+ case RT5640_PWR_DIG1:
+ case RT5640_PWR_DIG2:
+ case RT5640_PWR_ANLG1:
+ case RT5640_PWR_ANLG2:
+ case RT5640_PWR_MIXER:
+ case RT5640_PWR_VOL:
+ case RT5640_PRIV_INDEX:
+ case RT5640_PRIV_DATA:
+ case RT5640_I2S1_SDP:
+ case RT5640_I2S2_SDP:
+ case RT5640_ADDA_CLK1:
+ case RT5640_ADDA_CLK2:
+ case RT5640_DMIC:
+ case RT5640_GLB_CLK:
+ case RT5640_PLL_CTRL1:
+ case RT5640_PLL_CTRL2:
+ case RT5640_ASRC_1:
+ case RT5640_ASRC_2:
+ case RT5640_ASRC_3:
+ case RT5640_ASRC_4:
+ case RT5640_ASRC_5:
+ case RT5640_HP_OVCD:
+ case RT5640_CLS_D_OVCD:
+ case RT5640_CLS_D_OUT:
+ case RT5640_DEPOP_M1:
+ case RT5640_DEPOP_M2:
+ case RT5640_DEPOP_M3:
+ case RT5640_CHARGE_PUMP:
+ case RT5640_PV_DET_SPK_G:
+ case RT5640_MICBIAS:
+ case RT5640_EQ_CTRL1:
+ case RT5640_EQ_CTRL2:
+ case RT5640_WIND_FILTER:
+ case RT5640_DRC_AGC_1:
+ case RT5640_DRC_AGC_2:
+ case RT5640_DRC_AGC_3:
+ case RT5640_SVOL_ZC:
+ case RT5640_ANC_CTRL1:
+ case RT5640_ANC_CTRL2:
+ case RT5640_ANC_CTRL3:
+ case RT5640_JD_CTRL:
+ case RT5640_ANC_JD:
+ case RT5640_IRQ_CTRL1:
+ case RT5640_IRQ_CTRL2:
+ case RT5640_INT_IRQ_ST:
+ case RT5640_GPIO_CTRL1:
+ case RT5640_GPIO_CTRL2:
+ case RT5640_GPIO_CTRL3:
+ case RT5640_DSP_CTRL1:
+ case RT5640_DSP_CTRL2:
+ case RT5640_DSP_CTRL3:
+ case RT5640_DSP_CTRL4:
+ case RT5640_PGM_REG_ARR1:
+ case RT5640_PGM_REG_ARR2:
+ case RT5640_PGM_REG_ARR3:
+ case RT5640_PGM_REG_ARR4:
+ case RT5640_PGM_REG_ARR5:
+ case RT5640_SCB_FUNC:
+ case RT5640_SCB_CTRL:
+ case RT5640_BASE_BACK:
+ case RT5640_MP3_PLUS1:
+ case RT5640_MP3_PLUS2:
+ case RT5640_3D_HP:
+ case RT5640_ADJ_HPF:
+ case RT5640_HP_CALIB_AMP_DET:
+ case RT5640_HP_CALIB2:
+ case RT5640_SV_ZCD1:
+ case RT5640_SV_ZCD2:
+ case RT5640_DUMMY1:
+ case RT5640_DUMMY2:
+ case RT5640_DUMMY3:
+ case RT5640_VENDOR_ID:
+ case RT5640_VENDOR_ID1:
+ case RT5640_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_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 unsigned int bst_tlv[] = {
+ TLV_DB_RANGE_HEAD(7),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
+};
+
+/* Interface data select */
+static const char * const rt5640_data_select[] = {
+ "Normal", "left copy to right", "right copy to left", "Swap"};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF1_DAC_SEL_SFT, rt5640_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5640_if1_adc_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF1_ADC_SEL_SFT, rt5640_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5640_if2_dac_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF2_DAC_SEL_SFT, rt5640_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5640_if2_adc_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF2_ADC_SEL_SFT, rt5640_data_select);
+
+/* Class D speaker gain ratio */
+static const char * const rt5640_clsd_spk_ratio[] = {"1.66x", "1.83x", "1.94x",
+ "2x", "2.11x", "2.22x", "2.33x", "2.44x", "2.55x", "2.66x", "2.77x"};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_clsd_spk_ratio_enum, RT5640_CLS_D_OUT,
+ RT5640_CLSD_RATIO_SFT, rt5640_clsd_spk_ratio);
+
+static const struct snd_kcontrol_new rt5640_snd_controls[] = {
+ /* Speaker Output Volume */
+ SOC_DOUBLE("Speaker Channel Switch", RT5640_SPK_VOL,
+ RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("Speaker Playback Volume", RT5640_SPK_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* Headphone Output Volume */
+ SOC_DOUBLE("HP Channel Switch", RT5640_HP_VOL,
+ RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("HP Playback Volume", RT5640_HP_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* OUTPUT Control */
+ SOC_DOUBLE("OUT Playback Switch", RT5640_OUTPUT,
+ RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("OUT Channel Switch", RT5640_OUTPUT,
+ RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("OUT Playback Volume", RT5640_OUTPUT,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
+
+ /* DAC Digital Volume */
+ SOC_DOUBLE("DAC2 Playback Switch", RT5640_DAC2_CTRL,
+ RT5640_M_DAC_L2_VOL_SFT, RT5640_M_DAC_R2_VOL_SFT, 1, 1),
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
+ /* IN1/IN2 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),
+ /* INL/INR Volume Control */
+ SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL,
+ RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT,
+ 31, 1, in_vol_tlv),
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("ADC Capture Switch", RT5640_ADC_DIG_VOL,
+ RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("ADC Capture Volume", RT5640_ADC_DIG_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
+ SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5640_ADC_DATA,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("ADC Boost Gain", RT5640_ADC_BST_VOL,
+ RT5640_ADC_L_BST_SFT, RT5640_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+ /* Class D speaker gain ratio */
+ SOC_ENUM("Class D SPK Ratio Control", rt5640_clsd_spk_ratio_enum),
+
+ SOC_ENUM("ADC IF1 Data Switch", rt5640_if1_adc_enum),
+ SOC_ENUM("DAC IF1 Data Switch", rt5640_if1_dac_enum),
+ SOC_ENUM("ADC IF2 Data Switch", rt5640_if2_adc_enum),
+ SOC_ENUM("DAC IF2 Data Switch", rt5640_if2_dac_enum),
+};
+
+static const struct snd_kcontrol_new rt5640_specific_snd_controls[] = {
+ /* MONO Output Control */
+ SOC_SINGLE("Mono Playback Switch", RT5640_MONO_OUT, RT5640_L_MUTE_SFT,
+ 1, 1),
+
+ SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5640_DAC2_DIG_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 175, 0, dac_vol_tlv),
+};
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_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);
+
+ if (idx < 0)
+ dev_err(codec->dev, "Failed to set DMIC clock\n");
+ else
+ snd_soc_update_bits(codec, 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,
+ 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, RT5640_GLB_CLK);
+ val &= RT5640_SCLK_SRC_MASK;
+ if (val == RT5640_SCLK_SRC_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5640_sto_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_sto_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_IF1_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_IF1_DAC_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_sto_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_L2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ANC Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_ANC_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_sto_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_R2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ANC Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_ANC_DAC_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5639_sto_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5639_sto_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_L1_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_L2_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_R2_MONO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_R1_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_R2_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_L2_MONO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dig_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_DIG_MIXER,
+ RT5640_M_STO_L_DAC_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_DIG_MIXER,
+ RT5640_M_DAC_L2_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dig_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_DIG_MIXER,
+ RT5640_M_STO_R_DAC_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_DIG_MIXER,
+ RT5640_M_DAC_R2_DAC_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5640_rec_l_mix[] = {
+ SOC_DAPM_SINGLE("HPOL Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_HP_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_IN_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_BST4_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_BST1_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXL Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_OM_L_RM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_rec_r_mix[] = {
+ SOC_DAPM_SINGLE("HPOR Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_HP_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_IN_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_BST4_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_BST1_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXR Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_OM_R_RM_R_SFT, 1, 1),
+};
+
+/* Analog Output Mixer */
+static const struct snd_kcontrol_new rt5640_spk_l_mix[] = {
+ SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_RM_L_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_IN_L_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_DAC_L1_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_DAC_L2_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXL Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_OM_L_SM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_spk_r_mix[] = {
+ SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_RM_R_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_IN_R_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_DAC_R1_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_DAC_R2_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXR Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_OM_R_SM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_out_l_mix[] = {
+ SOC_DAPM_SINGLE("SPK MIXL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_SM_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_BST1_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_IN_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_RM_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_R2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_L2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_out_r_mix[] = {
+ SOC_DAPM_SINGLE("SPK MIXR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_SM_L_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_BST4_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_BST1_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_IN_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_RM_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_L2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_R2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5639_out_l_mix[] = {
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_BST1_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_IN_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_RM_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5639_out_r_mix[] = {
+ SOC_DAPM_SINGLE("BST2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_BST4_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_BST1_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_IN_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_RM_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_spo_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_DAC_R1_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_DAC_L1_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL R Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_SV_R_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL L Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_SV_L_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_BST1_SPM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_spo_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPO_R_MIXER,
+ RT5640_M_DAC_R1_SPM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL R Switch", RT5640_SPO_R_MIXER,
+ RT5640_M_SV_R_SPM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_SPO_R_MIXER,
+ RT5640_M_BST1_SPM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_hpo_mix[] = {
+ SOC_DAPM_SINGLE("HPO MIX DAC2 Switch", RT5640_HPO_MIXER,
+ RT5640_M_DAC2_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5640_HPO_MIXER,
+ RT5640_M_DAC1_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5640_HPO_MIXER,
+ RT5640_M_HPVOL_HM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5639_hpo_mix[] = {
+ SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5640_HPO_MIXER,
+ RT5640_M_DAC1_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5640_HPO_MIXER,
+ RT5640_M_HPVOL_HM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_lout_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_LOUT_MIXER,
+ RT5640_M_DAC_L1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_LOUT_MIXER,
+ RT5640_M_DAC_R1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL L Switch", RT5640_LOUT_MIXER,
+ RT5640_M_OV_L_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL R Switch", RT5640_LOUT_MIXER,
+ RT5640_M_OV_R_LM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_mix[] = {
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_MIXER,
+ RT5640_M_DAC_R2_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_MIXER,
+ RT5640_M_DAC_L2_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL R Switch", RT5640_MONO_MIXER,
+ RT5640_M_OV_R_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL L Switch", RT5640_MONO_MIXER,
+ RT5640_M_OV_L_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_MONO_MIXER,
+ RT5640_M_BST1_MM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new spk_l_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL,
+ RT5640_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new spk_r_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL,
+ RT5640_R_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hp_l_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL,
+ RT5640_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hp_r_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL,
+ RT5640_R_MUTE_SFT, 1, 1);
+
+/* Stereo ADC source */
+static const char * const rt5640_stereo_adc1_src[] = {
+ "DIG MIX", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_stereo_adc1_enum, RT5640_STO_ADC_MIXER,
+ RT5640_ADC_1_SRC_SFT, rt5640_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5640_sto_adc_1_mux =
+ SOC_DAPM_ENUM("Stereo ADC1 Mux", rt5640_stereo_adc1_enum);
+
+static const char * const rt5640_stereo_adc2_src[] = {
+ "DMIC1", "DMIC2", "DIG MIX"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_stereo_adc2_enum, RT5640_STO_ADC_MIXER,
+ RT5640_ADC_2_SRC_SFT, rt5640_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5640_sto_adc_2_mux =
+ SOC_DAPM_ENUM("Stereo ADC2 Mux", rt5640_stereo_adc2_enum);
+
+/* Mono ADC source */
+static const char * const rt5640_mono_adc_l1_src[] = {
+ "Mono DAC MIXL", "ADCL"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_l1_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_L1_SRC_SFT, rt5640_mono_adc_l1_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_l1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 left source", rt5640_mono_adc_l1_enum);
+
+static const char * const rt5640_mono_adc_l2_src[] = {
+ "DMIC L1", "DMIC L2", "Mono DAC MIXL"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_l2_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_L2_SRC_SFT, rt5640_mono_adc_l2_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_l2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 left source", rt5640_mono_adc_l2_enum);
+
+static const char * const rt5640_mono_adc_r1_src[] = {
+ "Mono DAC MIXR", "ADCR"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_r1_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_R1_SRC_SFT, rt5640_mono_adc_r1_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_r1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 right source", rt5640_mono_adc_r1_enum);
+
+static const char * const rt5640_mono_adc_r2_src[] = {
+ "DMIC R1", "DMIC R2", "Mono DAC MIXR"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_r2_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_R2_SRC_SFT, rt5640_mono_adc_r2_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_r2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 right source", rt5640_mono_adc_r2_enum);
+
+/* DAC2 channel source */
+static const char * const rt5640_dac_l2_src[] = {
+ "IF2", "Base L/R"
+};
+
+static int rt5640_dac_l2_values[] = {
+ 0,
+ 3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dac_l2_enum,
+ RT5640_DSP_PATH2, RT5640_DAC_L2_SEL_SFT,
+ 0x3, rt5640_dac_l2_src, rt5640_dac_l2_values);
+
+static const struct snd_kcontrol_new rt5640_dac_l2_mux =
+ SOC_DAPM_ENUM("DAC2 left channel source", rt5640_dac_l2_enum);
+
+static const char * const rt5640_dac_r2_src[] = {
+ "IF2",
+};
+
+static int rt5640_dac_r2_values[] = {
+ 0,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dac_r2_enum,
+ RT5640_DSP_PATH2, RT5640_DAC_R2_SEL_SFT,
+ 0x3, rt5640_dac_r2_src, rt5640_dac_r2_values);
+
+static const struct snd_kcontrol_new rt5640_dac_r2_mux =
+ SOC_DAPM_ENUM("DAC2 right channel source", rt5640_dac_r2_enum);
+
+/* digital interface and iis interface map */
+static const char * const rt5640_dai_iis_map[] = {
+ "1:1|2:2", "1:2|2:1", "1:1|2:1", "1:2|2:2"
+};
+
+static int rt5640_dai_iis_map_values[] = {
+ 0,
+ 5,
+ 6,
+ 7,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dai_iis_map_enum,
+ RT5640_I2S1_SDP, RT5640_I2S_IF_SFT,
+ 0x7, rt5640_dai_iis_map,
+ rt5640_dai_iis_map_values);
+
+static const struct snd_kcontrol_new rt5640_dai_mux =
+ SOC_DAPM_ENUM("DAI select", rt5640_dai_iis_map_enum);
+
+/* SDI select */
+static const char * const rt5640_sdi_sel[] = {
+ "IF1", "IF2"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5640_sdi_sel_enum, RT5640_I2S2_SDP,
+ RT5640_I2S2_SDI_SFT, rt5640_sdi_sel);
+
+static const struct snd_kcontrol_new rt5640_sdi_mux =
+ SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum);
+
+static void hp_amp_power_on(struct snd_soc_codec *codec)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ /* depop parameters */
+ regmap_update_bits(rt5640->regmap, RT5640_PR_BASE +
+ RT5640_CHPUMP_INT_REG1, 0x0700, 0x0200);
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2,
+ RT5640_DEPOP_MASK, RT5640_DEPOP_MAN);
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1,
+ RT5640_HP_CP_MASK | RT5640_HP_SG_MASK | RT5640_HP_CB_MASK,
+ RT5640_HP_CP_PU | RT5640_HP_SG_DIS | RT5640_HP_CB_PU);
+ regmap_write(rt5640->regmap, RT5640_PR_BASE + RT5640_HP_DCC_INT1,
+ 0x9f00);
+ /* headphone amp power on */
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2, 0);
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
+ RT5640_PWR_HA,
+ RT5640_PWR_HA);
+ usleep_range(10000, 15000);
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2 ,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2);
+}
+
+static void rt5640_pmu_depop(struct snd_soc_codec *codec)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2,
+ RT5640_DEPOP_MASK | RT5640_DIG_DP_MASK,
+ RT5640_DEPOP_AUTO | RT5640_DIG_DP_EN);
+ regmap_update_bits(rt5640->regmap, RT5640_CHARGE_PUMP,
+ RT5640_PM_HP_MASK, RT5640_PM_HP_HV);
+
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M3,
+ RT5640_CP_FQ1_MASK | RT5640_CP_FQ2_MASK | RT5640_CP_FQ3_MASK,
+ (RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ1_SFT) |
+ (RT5640_CP_FQ_12_KHZ << RT5640_CP_FQ2_SFT) |
+ (RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ3_SFT));
+
+ regmap_write(rt5640->regmap, RT5640_PR_BASE +
+ RT5640_MAMP_INT_REG2, 0x1c00);
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1,
+ RT5640_HP_CP_MASK | RT5640_HP_SG_MASK,
+ RT5640_HP_CP_PD | RT5640_HP_SG_EN);
+ regmap_update_bits(rt5640->regmap, RT5640_PR_BASE +
+ RT5640_CHPUMP_INT_REG1, 0x0700, 0x0400);
+}
+
+static int rt5640_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt5640_pmu_depop(codec);
+ rt5640->hp_mute = 0;
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ rt5640->hp_mute = 1;
+ usleep_range(70000, 75000);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ hp_amp_power_on(codec);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!rt5640->hp_mute)
+ usleep_range(80000, 85000);
+
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+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),
+ /* Input Side */
+ /* micbias */
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5640_PWR_ANLG1,
+ RT5640_PWR_LDO2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5640_PWR_ANLG2,
+ RT5640_PWR_MB1_BIT, 0, NULL, 0),
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("IN1P"),
+ SND_SOC_DAPM_INPUT("IN1N"),
+ SND_SOC_DAPM_INPUT("IN2P"),
+ SND_SOC_DAPM_INPUT("IN2N"),
+ SND_SOC_DAPM_PGA("DMIC L1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC R1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC L2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC R2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5640_DMIC, RT5640_DMIC_1_EN_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5640_DMIC, RT5640_DMIC_2_EN_SFT, 0,
+ NULL, 0),
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1", RT5640_PWR_ANLG2,
+ RT5640_PWR_BST1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST2", RT5640_PWR_ANLG2,
+ RT5640_PWR_BST4_BIT, 0, NULL, 0),
+ /* Input Volume */
+ SND_SOC_DAPM_PGA("INL VOL", RT5640_PWR_VOL,
+ RT5640_PWR_IN_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR VOL", RT5640_PWR_VOL,
+ RT5640_PWR_IN_R_BIT, 0, NULL, 0),
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIXL", RT5640_PWR_MIXER, RT5640_PWR_RM_L_BIT, 0,
+ rt5640_rec_l_mix, ARRAY_SIZE(rt5640_rec_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIXR", RT5640_PWR_MIXER, RT5640_PWR_RM_R_BIT, 0,
+ rt5640_rec_r_mix, ARRAY_SIZE(rt5640_rec_r_mix)),
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC L", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_ADC_L_BIT, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_ADC_R_BIT, 0),
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_2_mux),
+ SND_SOC_DAPM_MUX("Stereo ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_2_mux),
+ SND_SOC_DAPM_MUX("Stereo ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_1_mux),
+ SND_SOC_DAPM_MUX("Stereo ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_l2_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_l1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_r1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_r2_mux),
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("Stereo Filter", RT5640_PWR_DIG2,
+ RT5640_PWR_ADC_SF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Stereo ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_adc_l_mix, ARRAY_SIZE(rt5640_sto_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_adc_r_mix, ARRAY_SIZE(rt5640_sto_adc_r_mix)),
+ SND_SOC_DAPM_SUPPLY("Mono Left Filter", RT5640_PWR_DIG2,
+ RT5640_PWR_ADC_MF_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_adc_l_mix, ARRAY_SIZE(rt5640_mono_adc_l_mix)),
+ SND_SOC_DAPM_SUPPLY("Mono Right Filter", RT5640_PWR_DIG2,
+ RT5640_PWR_ADC_MF_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_adc_r_mix, ARRAY_SIZE(rt5640_mono_adc_r_mix)),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5640_PWR_DIG1,
+ RT5640_PWR_I2S1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S2", RT5640_PWR_DIG1,
+ RT5640_PWR_I2S2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("DAI1 RX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI1 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI1 IF1 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI1 IF2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("SDI1 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_sdi_mux),
+ SND_SOC_DAPM_MUX("DAI2 RX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI2 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI2 IF1 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI2 IF2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("SDI2 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_sdi_mux),
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_dac_l_mix, ARRAY_SIZE(rt5640_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_dac_r_mix, ARRAY_SIZE(rt5640_dac_r_mix)),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_dac_l_mix, ARRAY_SIZE(rt5640_mono_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_dac_r_mix, ARRAY_SIZE(rt5640_mono_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("DIG MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_dig_l_mix, ARRAY_SIZE(rt5640_dig_l_mix)),
+ SND_SOC_DAPM_MIXER("DIG MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_dig_r_mix, ARRAY_SIZE(rt5640_dig_r_mix)),
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, 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),
+
+ /* SPK/OUT Mixer */
+ SND_SOC_DAPM_MIXER("SPK MIXL", RT5640_PWR_MIXER, RT5640_PWR_SM_L_BIT,
+ 0, rt5640_spk_l_mix, ARRAY_SIZE(rt5640_spk_l_mix)),
+ SND_SOC_DAPM_MIXER("SPK MIXR", RT5640_PWR_MIXER, RT5640_PWR_SM_R_BIT,
+ 0, rt5640_spk_r_mix, ARRAY_SIZE(rt5640_spk_r_mix)),
+ /* Ouput Volume */
+ SND_SOC_DAPM_PGA("SPKVOL L", RT5640_PWR_VOL,
+ RT5640_PWR_SV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPKVOL R", RT5640_PWR_VOL,
+ RT5640_PWR_SV_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OUTVOL L", RT5640_PWR_VOL,
+ RT5640_PWR_OV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OUTVOL R", RT5640_PWR_VOL,
+ RT5640_PWR_OV_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL L", RT5640_PWR_VOL,
+ RT5640_PWR_HV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL R", RT5640_PWR_VOL,
+ RT5640_PWR_HV_R_BIT, 0, NULL, 0),
+ /* SPO/HPO/LOUT/Mono Mixer */
+ SND_SOC_DAPM_MIXER("SPOL MIX", SND_SOC_NOPM, 0,
+ 0, rt5640_spo_l_mix, ARRAY_SIZE(rt5640_spo_l_mix)),
+ SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0,
+ 0, rt5640_spo_r_mix, ARRAY_SIZE(rt5640_spo_r_mix)),
+ SND_SOC_DAPM_MIXER("LOUT MIX", RT5640_PWR_ANLG1, RT5640_PWR_LM_BIT, 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_SUPPLY("HP L Amp", RT5640_PWR_ANLG1,
+ RT5640_PWR_HP_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HP R Amp", RT5640_PWR_ANLG1,
+ RT5640_PWR_HP_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Improve SPK Amp Drv", RT5640_PWR_DIG1,
+ RT5640_PWR_CLS_D_BIT, 0, NULL, 0),
+
+ /* Output Switch */
+ SND_SOC_DAPM_SWITCH("Speaker L Playback", SND_SOC_NOPM, 0, 0,
+ &spk_l_enable_control),
+ SND_SOC_DAPM_SWITCH("Speaker R Playback", SND_SOC_NOPM, 0, 0,
+ &spk_r_enable_control),
+ SND_SOC_DAPM_SWITCH("HP L Playback", SND_SOC_NOPM, 0, 0,
+ &hp_l_enable_control),
+ SND_SOC_DAPM_SWITCH("HP R Playback", SND_SOC_NOPM, 0, 0,
+ &hp_r_enable_control),
+ SND_SOC_DAPM_POST("HP Post", rt5640_hp_post_event),
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("SPOLP"),
+ SND_SOC_DAPM_OUTPUT("SPOLN"),
+ SND_SOC_DAPM_OUTPUT("SPORP"),
+ SND_SOC_DAPM_OUTPUT("SPORN"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("LOUTL"),
+ SND_SOC_DAPM_OUTPUT("LOUTR"),
+};
+
+static const struct snd_soc_dapm_widget rt5640_specific_dapm_widgets[] = {
+ /* Audio DSP */
+ SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* ANC */
+ SND_SOC_DAPM_PGA("ANC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DAC2 channel Mux */
+ SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dac_l2_mux),
+ SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dac_r2_mux),
+
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_dac_l_mix, ARRAY_SIZE(rt5640_sto_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_dac_r_mix, ARRAY_SIZE(rt5640_sto_dac_r_mix)),
+
+ SND_SOC_DAPM_DAC("DAC R2", NULL, RT5640_PWR_DIG1, RT5640_PWR_DAC_R2_BIT,
+ 0),
+ SND_SOC_DAPM_DAC("DAC L2", NULL, RT5640_PWR_DIG1, RT5640_PWR_DAC_L2_BIT,
+ 0),
+
+ SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
+ 0, rt5640_out_l_mix, ARRAY_SIZE(rt5640_out_l_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT,
+ 0, rt5640_out_r_mix, ARRAY_SIZE(rt5640_out_r_mix)),
+
+ SND_SOC_DAPM_MIXER("HPO MIX L", SND_SOC_NOPM, 0, 0,
+ rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)),
+ SND_SOC_DAPM_MIXER("HPO MIX R", SND_SOC_NOPM, 0, 0,
+ rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)),
+
+ SND_SOC_DAPM_MIXER("Mono MIX", RT5640_PWR_ANLG1, RT5640_PWR_MM_BIT, 0,
+ rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)),
+ SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1,
+ RT5640_PWR_MA_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("MONOP"),
+ SND_SOC_DAPM_OUTPUT("MONON"),
+};
+
+static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5639_sto_dac_l_mix, ARRAY_SIZE(rt5639_sto_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5639_sto_dac_r_mix, ARRAY_SIZE(rt5639_sto_dac_r_mix)),
+
+ SND_SOC_DAPM_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,
+ 0, rt5639_out_r_mix, ARRAY_SIZE(rt5639_out_r_mix)),
+
+ SND_SOC_DAPM_MIXER("HPO MIX L", SND_SOC_NOPM, 0, 0,
+ rt5639_hpo_mix, ARRAY_SIZE(rt5639_hpo_mix)),
+ SND_SOC_DAPM_MIXER("HPO MIX R", SND_SOC_NOPM, 0, 0,
+ rt5639_hpo_mix, ARRAY_SIZE(rt5639_hpo_mix)),
+};
+
+static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
+ {"IN1P", NULL, "LDO2"},
+ {"IN2P", NULL, "LDO2"},
+
+ {"DMIC L1", NULL, "DMIC1"},
+ {"DMIC R1", NULL, "DMIC1"},
+ {"DMIC L2", NULL, "DMIC2"},
+ {"DMIC R2", NULL, "DMIC2"},
+
+ {"BST1", NULL, "IN1P"},
+ {"BST1", NULL, "IN1N"},
+ {"BST2", NULL, "IN2P"},
+ {"BST2", NULL, "IN2N"},
+
+ {"INL VOL", NULL, "IN2P"},
+ {"INR VOL", NULL, "IN2N"},
+
+ {"RECMIXL", "HPOL Switch", "HPOL"},
+ {"RECMIXL", "INL Switch", "INL VOL"},
+ {"RECMIXL", "BST2 Switch", "BST2"},
+ {"RECMIXL", "BST1 Switch", "BST1"},
+ {"RECMIXL", "OUT MIXL Switch", "OUT MIXL"},
+
+ {"RECMIXR", "HPOR Switch", "HPOR"},
+ {"RECMIXR", "INR Switch", "INR VOL"},
+ {"RECMIXR", "BST2 Switch", "BST2"},
+ {"RECMIXR", "BST1 Switch", "BST1"},
+ {"RECMIXR", "OUT MIXR Switch", "OUT MIXR"},
+
+ {"ADC L", NULL, "RECMIXL"},
+ {"ADC R", NULL, "RECMIXR"},
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC L2", NULL, "DMIC CLK"},
+ {"DMIC L2", NULL, "DMIC2 Power"},
+ {"DMIC R2", NULL, "DMIC CLK"},
+ {"DMIC R2", NULL, "DMIC2 Power"},
+
+ {"Stereo ADC L2 Mux", "DMIC1", "DMIC L1"},
+ {"Stereo ADC L2 Mux", "DMIC2", "DMIC L2"},
+ {"Stereo ADC L2 Mux", "DIG MIX", "DIG MIXL"},
+ {"Stereo ADC L1 Mux", "ADC", "ADC L"},
+ {"Stereo ADC L1 Mux", "DIG MIX", "DIG MIXL"},
+
+ {"Stereo ADC R1 Mux", "ADC", "ADC R"},
+ {"Stereo ADC R1 Mux", "DIG MIX", "DIG MIXR"},
+ {"Stereo ADC R2 Mux", "DMIC1", "DMIC R1"},
+ {"Stereo ADC R2 Mux", "DMIC2", "DMIC R2"},
+ {"Stereo ADC R2 Mux", "DIG MIX", "DIG MIXR"},
+
+ {"Mono ADC L2 Mux", "DMIC L1", "DMIC L1"},
+ {"Mono ADC L2 Mux", "DMIC L2", "DMIC L2"},
+ {"Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL"},
+ {"Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL"},
+ {"Mono ADC L1 Mux", "ADCL", "ADC L"},
+
+ {"Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR"},
+ {"Mono ADC R1 Mux", "ADCR", "ADC R"},
+ {"Mono ADC R2 Mux", "DMIC R1", "DMIC R1"},
+ {"Mono ADC R2 Mux", "DMIC R2", "DMIC R2"},
+ {"Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR"},
+
+ {"Stereo ADC MIXL", "ADC1 Switch", "Stereo ADC L1 Mux"},
+ {"Stereo ADC MIXL", "ADC2 Switch", "Stereo ADC L2 Mux"},
+ {"Stereo ADC MIXL", NULL, "Stereo Filter"},
+ {"Stereo 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"},
+ {"IF1 ADC L", NULL, "Stereo ADC MIXL"},
+ {"IF1 ADC R", NULL, "Stereo ADC MIXR"},
+
+ {"IF1 ADC", NULL, "I2S1"},
+ {"IF1 ADC", NULL, "IF1 ADC L"},
+ {"IF1 ADC", NULL, "IF1 ADC R"},
+ {"IF2 ADC", NULL, "I2S2"},
+ {"IF2 ADC", NULL, "IF2 ADC L"},
+ {"IF2 ADC", NULL, "IF2 ADC R"},
+
+ {"DAI1 TX Mux", "1:1|2:2", "IF1 ADC"},
+ {"DAI1 TX Mux", "1:2|2:1", "IF2 ADC"},
+ {"DAI1 IF1 Mux", "1:1|2:1", "IF1 ADC"},
+ {"DAI1 IF2 Mux", "1:1|2:1", "IF2 ADC"},
+ {"SDI1 TX Mux", "IF1", "DAI1 IF1 Mux"},
+ {"SDI1 TX Mux", "IF2", "DAI1 IF2 Mux"},
+
+ {"DAI2 TX Mux", "1:2|2:1", "IF1 ADC"},
+ {"DAI2 TX Mux", "1:1|2:2", "IF2 ADC"},
+ {"DAI2 IF1 Mux", "1:2|2:2", "IF1 ADC"},
+ {"DAI2 IF2 Mux", "1:2|2:2", "IF2 ADC"},
+ {"SDI2 TX Mux", "IF1", "DAI2 IF1 Mux"},
+ {"SDI2 TX Mux", "IF2", "DAI2 IF2 Mux"},
+
+ {"AIF1TX", NULL, "DAI1 TX Mux"},
+ {"AIF1TX", NULL, "SDI1 TX Mux"},
+ {"AIF2TX", NULL, "DAI2 TX Mux"},
+ {"AIF2TX", NULL, "SDI2 TX Mux"},
+
+ {"DAI1 RX Mux", "1:1|2:2", "AIF1RX"},
+ {"DAI1 RX Mux", "1:1|2:1", "AIF1RX"},
+ {"DAI1 RX Mux", "1:2|2:1", "AIF2RX"},
+ {"DAI1 RX Mux", "1:2|2:2", "AIF2RX"},
+
+ {"DAI2 RX Mux", "1:2|2:1", "AIF1RX"},
+ {"DAI2 RX Mux", "1:1|2:1", "AIF1RX"},
+ {"DAI2 RX Mux", "1:1|2:2", "AIF2RX"},
+ {"DAI2 RX Mux", "1:2|2:2", "AIF2RX"},
+
+ {"IF1 DAC", NULL, "I2S1"},
+ {"IF1 DAC", NULL, "DAI1 RX Mux"},
+ {"IF2 DAC", NULL, "I2S2"},
+ {"IF2 DAC", NULL, "DAI2 RX Mux"},
+
+ {"IF1 DAC L", NULL, "IF1 DAC"},
+ {"IF1 DAC R", NULL, "IF1 DAC"},
+ {"IF2 DAC L", NULL, "IF2 DAC"},
+ {"IF2 DAC R", NULL, "IF2 DAC"},
+
+ {"DAC MIXL", "Stereo ADC Switch", "Stereo ADC MIXL"},
+ {"DAC MIXL", "INF1 Switch", "IF1 DAC L"},
+ {"DAC MIXR", "Stereo ADC Switch", "Stereo ADC MIXR"},
+ {"DAC MIXR", "INF1 Switch", "IF1 DAC R"},
+
+ {"Stereo DAC MIXL", "DAC L1 Switch", "DAC MIXL"},
+ {"Stereo DAC MIXR", "DAC R1 Switch", "DAC MIXR"},
+
+ {"Mono DAC MIXL", "DAC L1 Switch", "DAC MIXL"},
+ {"Mono DAC MIXR", "DAC R1 Switch", "DAC MIXR"},
+
+ {"DIG MIXL", "DAC L1 Switch", "DAC MIXL"},
+ {"DIG MIXR", "DAC R1 Switch", "DAC MIXR"},
+
+ {"DAC L1", NULL, "Stereo DAC MIXL"},
+ {"DAC L1", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC R1", NULL, "Stereo DAC MIXR"},
+ {"DAC R1", NULL, "PLL1", is_sys_clk_from_pll},
+
+ {"SPK MIXL", "REC MIXL Switch", "RECMIXL"},
+ {"SPK MIXL", "INL Switch", "INL VOL"},
+ {"SPK MIXL", "DAC L1 Switch", "DAC L1"},
+ {"SPK MIXL", "OUT MIXL Switch", "OUT MIXL"},
+ {"SPK MIXR", "REC MIXR Switch", "RECMIXR"},
+ {"SPK MIXR", "INR Switch", "INR VOL"},
+ {"SPK MIXR", "DAC R1 Switch", "DAC R1"},
+ {"SPK MIXR", "OUT MIXR Switch", "OUT MIXR"},
+
+ {"OUT MIXL", "BST1 Switch", "BST1"},
+ {"OUT MIXL", "INL Switch", "INL VOL"},
+ {"OUT MIXL", "REC MIXL Switch", "RECMIXL"},
+ {"OUT MIXL", "DAC L1 Switch", "DAC L1"},
+
+ {"OUT MIXR", "BST2 Switch", "BST2"},
+ {"OUT MIXR", "BST1 Switch", "BST1"},
+ {"OUT MIXR", "INR Switch", "INR VOL"},
+ {"OUT MIXR", "REC MIXR Switch", "RECMIXR"},
+ {"OUT MIXR", "DAC R1 Switch", "DAC R1"},
+
+ {"SPKVOL L", NULL, "SPK MIXL"},
+ {"SPKVOL R", NULL, "SPK MIXR"},
+ {"HPOVOL L", NULL, "OUT MIXL"},
+ {"HPOVOL R", NULL, "OUT MIXR"},
+ {"OUTVOL L", NULL, "OUT MIXL"},
+ {"OUTVOL R", NULL, "OUT MIXR"},
+
+ {"SPOL MIX", "DAC R1 Switch", "DAC R1"},
+ {"SPOL MIX", "DAC L1 Switch", "DAC L1"},
+ {"SPOL MIX", "SPKVOL R Switch", "SPKVOL R"},
+ {"SPOL MIX", "SPKVOL L Switch", "SPKVOL L"},
+ {"SPOL MIX", "BST1 Switch", "BST1"},
+ {"SPOR MIX", "DAC R1 Switch", "DAC R1"},
+ {"SPOR MIX", "SPKVOL R Switch", "SPKVOL R"},
+ {"SPOR MIX", "BST1 Switch", "BST1"},
+
+ {"HPO MIX L", "HPO MIX DAC1 Switch", "DAC L1"},
+ {"HPO MIX L", "HPO MIX HPVOL Switch", "HPOVOL L"},
+ {"HPO MIX L", NULL, "HP L Amp"},
+ {"HPO MIX R", "HPO MIX DAC1 Switch", "DAC R1"},
+ {"HPO MIX R", "HPO MIX HPVOL Switch", "HPOVOL R"},
+ {"HPO MIX R", NULL, "HP R Amp"},
+
+ {"LOUT MIX", "DAC L1 Switch", "DAC L1"},
+ {"LOUT MIX", "DAC R1 Switch", "DAC R1"},
+ {"LOUT MIX", "OUTVOL L Switch", "OUTVOL L"},
+ {"LOUT MIX", "OUTVOL R Switch", "OUTVOL R"},
+
+ {"HP Amp", NULL, "HPO MIX L"},
+ {"HP Amp", NULL, "HPO MIX R"},
+
+ {"Speaker L Playback", "Switch", "SPOL MIX"},
+ {"Speaker R Playback", "Switch", "SPOR MIX"},
+ {"SPOLP", NULL, "Speaker L Playback"},
+ {"SPOLN", NULL, "Speaker L Playback"},
+ {"SPORP", NULL, "Speaker R Playback"},
+ {"SPORN", NULL, "Speaker R Playback"},
+
+ {"SPOLP", NULL, "Improve SPK Amp Drv"},
+ {"SPOLN", NULL, "Improve SPK Amp Drv"},
+ {"SPORP", NULL, "Improve SPK Amp Drv"},
+ {"SPORN", NULL, "Improve SPK Amp Drv"},
+
+ {"HPOL", NULL, "Improve HP Amp Drv"},
+ {"HPOR", NULL, "Improve HP Amp Drv"},
+
+ {"HP L Playback", "Switch", "HP Amp"},
+ {"HP R Playback", "Switch", "HP Amp"},
+ {"HPOL", NULL, "HP L Playback"},
+ {"HPOR", NULL, "HP R Playback"},
+ {"LOUTL", NULL, "LOUT MIX"},
+ {"LOUTR", NULL, "LOUT MIX"},
+};
+
+static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = {
+ {"ANC", NULL, "Stereo ADC MIXL"},
+ {"ANC", NULL, "Stereo ADC MIXR"},
+
+ {"Audio DSP", NULL, "DAC MIXL"},
+ {"Audio DSP", NULL, "DAC MIXR"},
+
+ {"DAC L2 Mux", "IF2", "IF2 DAC L"},
+ {"DAC L2 Mux", "Base L/R", "Audio DSP"},
+
+ {"DAC R2 Mux", "IF2", "IF2 DAC R"},
+
+ {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+ {"Stereo DAC MIXL", "ANC Switch", "ANC"},
+ {"Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+ {"Stereo DAC MIXR", "ANC Switch", "ANC"},
+
+ {"Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+ {"Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"},
+
+ {"Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+ {"Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"},
+
+ {"DIG MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+ {"DIG MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+
+ {"DAC L2", NULL, "Mono DAC MIXL"},
+ {"DAC L2", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC R2", NULL, "Mono DAC MIXR"},
+ {"DAC R2", NULL, "PLL1", is_sys_clk_from_pll},
+
+ {"SPK MIXL", "DAC L2 Switch", "DAC L2"},
+ {"SPK MIXR", "DAC R2 Switch", "DAC R2"},
+
+ {"OUT MIXL", "SPK MIXL Switch", "SPK MIXL"},
+ {"OUT MIXR", "SPK MIXR Switch", "SPK MIXR"},
+
+ {"OUT MIXL", "DAC R2 Switch", "DAC R2"},
+ {"OUT MIXL", "DAC L2 Switch", "DAC L2"},
+
+ {"OUT MIXR", "DAC L2 Switch", "DAC L2"},
+ {"OUT MIXR", "DAC R2 Switch", "DAC R2"},
+
+ {"HPO MIX L", "HPO MIX DAC2 Switch", "DAC L2"},
+ {"HPO MIX R", "HPO MIX DAC2 Switch", "DAC R2"},
+
+ {"Mono MIX", "DAC R2 Switch", "DAC R2"},
+ {"Mono MIX", "DAC L2 Switch", "DAC L2"},
+ {"Mono MIX", "OUTVOL R Switch", "OUTVOL R"},
+ {"Mono MIX", "OUTVOL L Switch", "OUTVOL L"},
+ {"Mono MIX", "BST1 Switch", "BST1"},
+
+ {"MONOP", NULL, "Mono MIX"},
+ {"MONON", NULL, "Mono MIX"},
+ {"MONOP", NULL, "Improve MONO Amp Drv"},
+};
+
+static const struct snd_soc_dapm_route rt5639_specific_dapm_routes[] = {
+ {"Stereo DAC MIXL", "DAC L2 Switch", "IF2 DAC L"},
+ {"Stereo DAC MIXR", "DAC R2 Switch", "IF2 DAC R"},
+
+ {"Mono DAC MIXL", "DAC L2 Switch", "IF2 DAC L"},
+ {"Mono DAC MIXL", "DAC R2 Switch", "IF2 DAC R"},
+
+ {"Mono DAC MIXR", "DAC R2 Switch", "IF2 DAC R"},
+ {"Mono DAC MIXR", "DAC L2 Switch", "IF2 DAC L"},
+
+ {"DIG MIXL", "DAC L2 Switch", "IF2 DAC L"},
+ {"DIG MIXR", "DAC R2 Switch", "IF2 DAC R"},
+
+ {"IF2 DAC L", NULL, "DAC L2 Filter"},
+ {"IF2 DAC R", NULL, "DAC R2 Filter"},
+};
+
+static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
+{
+ int ret = 0, val;
+
+ if (codec == NULL)
+ return -EINVAL;
+
+ val = snd_soc_read(codec, RT5640_I2S1_SDP);
+ val = (val & RT5640_I2S_IF_MASK) >> RT5640_I2S_IF_SFT;
+ switch (dai_id) {
+ case RT5640_AIF1:
+ switch (val) {
+ case RT5640_IF_123:
+ case RT5640_IF_132:
+ ret |= RT5640_U_IF1;
+ break;
+ case RT5640_IF_113:
+ ret |= RT5640_U_IF1;
+ case RT5640_IF_312:
+ case RT5640_IF_213:
+ ret |= RT5640_U_IF2;
+ break;
+ }
+ break;
+
+ case RT5640_AIF2:
+ switch (val) {
+ case RT5640_IF_231:
+ case RT5640_IF_213:
+ ret |= RT5640_U_IF1;
+ break;
+ case RT5640_IF_223:
+ ret |= RT5640_U_IF1;
+ case RT5640_IF_123:
+ case RT5640_IF_321:
+ ret |= RT5640_U_IF2;
+ break;
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int rt5640_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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",
+ 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);
+ return frame_size;
+ }
+ if (frame_size > 32)
+ bclk_ms = 1;
+ else
+ bclk_ms = 0;
+ rt5640->bclk[dai->id] = rt5640->lrck[dai->id] * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt5640->bclk[dai->id], rt5640->lrck[dai->id]);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ val_len |= RT5640_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= RT5640_I2S_DL_24;
+ break;
+ case 8:
+ val_len |= RT5640_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dai_sel = get_sdp_info(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) {
+ 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,
+ RT5640_I2S_DL_MASK, val_len);
+ snd_soc_update_bits(codec, 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,
+ RT5640_I2S_DL_MASK, val_len);
+ snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk);
+ }
+
+ return 0;
+}
+
+static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+ int dai_sel;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rt5640->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT5640_I2S_MS_S;
+ rt5640->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5640_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5640_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5640_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5640_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dai_sel = get_sdp_info(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_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,
+ RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK |
+ RT5640_I2S_DF_MASK, reg_val);
+ }
+
+ return 0;
+}
+
+static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+
+ if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5640_SCLK_S_MCLK:
+ reg_val |= RT5640_SCLK_SRC_MCLK;
+ break;
+ case RT5640_SCLK_S_PLL1:
+ reg_val |= RT5640_SCLK_SRC_PLL1;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_SCLK_SRC_MASK, reg_val);
+ rt5640->sysclk = freq;
+ rt5640->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ return 0;
+}
+
+static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rl6231_pll_code pll_code;
+ int ret, dai_sel;
+
+ 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");
+
+ rt5640->pll_in = 0;
+ rt5640->pll_out = 0;
+ snd_soc_update_bits(codec, 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,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_MCLK);
+ break;
+ case RT5640_PLL1_S_BCLK1:
+ 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);
+ }
+ break;
+ default:
+ dev_err(codec->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);
+ return ret;
+ }
+
+ dev_dbg(codec->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);
+
+ rt5640->pll_in = freq_in;
+ rt5640->pll_out = freq_out;
+ rt5640->pll_src = source;
+
+ return 0;
+}
+
+static int rt5640_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+ snd_soc_update_bits(codec, 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,
+ 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,
+ 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);
+ break;
+
+ default:
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+int rt5640_dmic_enable(struct snd_soc_codec *codec,
+ bool dmic1_data_pin, bool dmic2_data_pin)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
+ RT5640_GP2_PIN_MASK, RT5640_GP2_PIN_DMIC1_SCL);
+
+ if (dmic1_data_pin) {
+ regmap_update_bits(rt5640->regmap, RT5640_DMIC,
+ RT5640_DMIC_1_DP_MASK, RT5640_DMIC_1_DP_GPIO3);
+ regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
+ RT5640_GP3_PIN_MASK, RT5640_GP3_PIN_DMIC1_SDA);
+ }
+
+ if (dmic2_data_pin) {
+ regmap_update_bits(rt5640->regmap, RT5640_DMIC,
+ RT5640_DMIC_2_DP_MASK, RT5640_DMIC_2_DP_GPIO4);
+ regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
+ RT5640_GP4_PIN_MASK, RT5640_GP4_PIN_DMIC2_SDA);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
+
+static int rt5640_probe(struct snd_soc_codec *codec)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ rt5640->codec = codec;
+
+ rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ 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);
+
+ switch (snd_soc_read(codec, RT5640_RESET) & RT5640_ID_MASK) {
+ case RT5640_ID_5640:
+ case RT5640_ID_5642:
+ snd_soc_add_codec_controls(codec,
+ rt5640_specific_snd_controls,
+ ARRAY_SIZE(rt5640_specific_snd_controls));
+ snd_soc_dapm_new_controls(&codec->dapm,
+ rt5640_specific_dapm_widgets,
+ ARRAY_SIZE(rt5640_specific_dapm_widgets));
+ snd_soc_dapm_add_routes(&codec->dapm,
+ rt5640_specific_dapm_routes,
+ ARRAY_SIZE(rt5640_specific_dapm_routes));
+ break;
+ case RT5640_ID_5639:
+ snd_soc_dapm_new_controls(&codec->dapm,
+ rt5639_specific_dapm_widgets,
+ ARRAY_SIZE(rt5639_specific_dapm_widgets));
+ snd_soc_dapm_add_routes(&codec->dapm,
+ rt5639_specific_dapm_routes,
+ ARRAY_SIZE(rt5639_specific_dapm_routes));
+ break;
+ default:
+ dev_err(codec->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);
+
+ return 0;
+}
+
+static int rt5640_remove(struct snd_soc_codec *codec)
+{
+ rt5640_reset(codec);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt5640_suspend(struct snd_soc_codec *codec)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ rt5640_reset(codec);
+ 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);
+
+ return 0;
+}
+
+static int rt5640_resume(struct snd_soc_codec *codec)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ if (gpio_is_valid(rt5640->pdata.ldo1_en)) {
+ gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 1);
+ msleep(400);
+ }
+
+ regcache_cache_only(rt5640->regmap, false);
+ regcache_sync(rt5640->regmap);
+
+ return 0;
+}
+#else
+#define rt5640_suspend NULL
+#define rt5640_resume NULL
+#endif
+
+#define RT5640_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5640_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5640_aif_dai_ops = {
+ .hw_params = rt5640_hw_params,
+ .set_fmt = rt5640_set_dai_fmt,
+ .set_sysclk = rt5640_set_dai_sysclk,
+ .set_pll = rt5640_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver rt5640_dai[] = {
+ {
+ .name = "rt5640-aif1",
+ .id = RT5640_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .ops = &rt5640_aif_dai_ops,
+ },
+ {
+ .name = "rt5640-aif2",
+ .id = RT5640_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .ops = &rt5640_aif_dai_ops,
+ },
+};
+
+static 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 regmap_config rt5640_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .use_single_rw = 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,
+ .reg_defaults = rt5640_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5640_reg),
+ .ranges = rt5640_ranges,
+ .num_ranges = ARRAY_SIZE(rt5640_ranges),
+};
+
+static const struct i2c_device_id rt5640_i2c_id[] = {
+ { "rt5640", 0 },
+ { "rt5639", 0 },
+ { "rt5642", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt5640_of_match[] = {
+ { .compatible = "realtek,rt5639", },
+ { .compatible = "realtek,rt5640", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5640_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static struct acpi_device_id rt5640_acpi_match[] = {
+ { "INT33CA", 0 },
+ { "10EC5640", 0 },
+ { "10EC5642", 0 },
+ { },
+};
+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)
+{
+ struct rt5640_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5640_priv *rt5640;
+ int ret;
+ unsigned int val;
+
+ rt5640 = devm_kzalloc(&i2c->dev,
+ sizeof(struct rt5640_priv),
+ GFP_KERNEL);
+ if (NULL == rt5640)
+ return -ENOMEM;
+ i2c_set_clientdata(i2c, rt5640);
+
+ 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->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap);
+ if (IS_ERR(rt5640->regmap)) {
+ ret = PTR_ERR(rt5640->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ 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) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt5640/39\n", val);
+ return -ENODEV;
+ }
+
+ regmap_write(rt5640->regmap, RT5640_RESET, 0);
+
+ ret = regmap_register_patch(rt5640->regmap, init_list,
+ ARRAY_SIZE(init_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ if (rt5640->pdata.in1_diff)
+ regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2,
+ RT5640_IN_DF1, RT5640_IN_DF1);
+
+ if (rt5640->pdata.in2_diff)
+ regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
+ RT5640_IN_DF2, RT5640_IN_DF2);
+
+ rt5640->hp_mute = 1;
+
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_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);
+
+MODULE_DESCRIPTION("ASoC RT5640/RT5639 driver");
+MODULE_AUTHOR("Johnny Hsu <johnnyhsu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/rt5640.h b/kernel/sound/soc/codecs/rt5640.h
new file mode 100644
index 000000000..3deb8babe
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5640.h
@@ -0,0 +1,2103 @@
+/*
+ * 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>
+
+/* Info */
+#define RT5640_RESET 0x00
+#define RT5640_VENDOR_ID 0xfd
+#define RT5640_VENDOR_ID1 0xfe
+#define RT5640_VENDOR_ID2 0xff
+/* I/O - Output */
+#define RT5640_SPK_VOL 0x01
+#define RT5640_HP_VOL 0x02
+#define RT5640_OUTPUT 0x03
+#define RT5640_MONO_OUT 0x04
+/* I/O - Input */
+#define RT5640_IN1_IN2 0x0d
+#define RT5640_IN3_IN4 0x0e
+#define RT5640_INL_INR_VOL 0x0f
+/* I/O - ADC/DAC/DMIC */
+#define RT5640_DAC1_DIG_VOL 0x19
+#define RT5640_DAC2_DIG_VOL 0x1a
+#define RT5640_DAC2_CTRL 0x1b
+#define RT5640_ADC_DIG_VOL 0x1c
+#define RT5640_ADC_DATA 0x1d
+#define RT5640_ADC_BST_VOL 0x1e
+/* Mixer - D-D */
+#define RT5640_STO_ADC_MIXER 0x27
+#define RT5640_MONO_ADC_MIXER 0x28
+#define RT5640_AD_DA_MIXER 0x29
+#define RT5640_STO_DAC_MIXER 0x2a
+#define RT5640_MONO_DAC_MIXER 0x2b
+#define RT5640_DIG_MIXER 0x2c
+#define RT5640_DSP_PATH1 0x2d
+#define RT5640_DSP_PATH2 0x2e
+#define RT5640_DIG_INF_DATA 0x2f
+/* Mixer - ADC */
+#define RT5640_REC_L1_MIXER 0x3b
+#define RT5640_REC_L2_MIXER 0x3c
+#define RT5640_REC_R1_MIXER 0x3d
+#define RT5640_REC_R2_MIXER 0x3e
+/* Mixer - DAC */
+#define RT5640_HPO_MIXER 0x45
+#define RT5640_SPK_L_MIXER 0x46
+#define RT5640_SPK_R_MIXER 0x47
+#define RT5640_SPO_L_MIXER 0x48
+#define RT5640_SPO_R_MIXER 0x49
+#define RT5640_SPO_CLSD_RATIO 0x4a
+#define RT5640_MONO_MIXER 0x4c
+#define RT5640_OUT_L1_MIXER 0x4d
+#define RT5640_OUT_L2_MIXER 0x4e
+#define RT5640_OUT_L3_MIXER 0x4f
+#define RT5640_OUT_R1_MIXER 0x50
+#define RT5640_OUT_R2_MIXER 0x51
+#define RT5640_OUT_R3_MIXER 0x52
+#define RT5640_LOUT_MIXER 0x53
+/* Power */
+#define RT5640_PWR_DIG1 0x61
+#define RT5640_PWR_DIG2 0x62
+#define RT5640_PWR_ANLG1 0x63
+#define RT5640_PWR_ANLG2 0x64
+#define RT5640_PWR_MIXER 0x65
+#define RT5640_PWR_VOL 0x66
+/* Private Register Control */
+#define RT5640_PRIV_INDEX 0x6a
+#define RT5640_PRIV_DATA 0x6c
+/* Format - ADC/DAC */
+#define RT5640_I2S1_SDP 0x70
+#define RT5640_I2S2_SDP 0x71
+#define RT5640_ADDA_CLK1 0x73
+#define RT5640_ADDA_CLK2 0x74
+#define RT5640_DMIC 0x75
+/* Function - Analog */
+#define RT5640_GLB_CLK 0x80
+#define RT5640_PLL_CTRL1 0x81
+#define RT5640_PLL_CTRL2 0x82
+#define RT5640_ASRC_1 0x83
+#define RT5640_ASRC_2 0x84
+#define RT5640_ASRC_3 0x85
+#define RT5640_ASRC_4 0x89
+#define RT5640_ASRC_5 0x8a
+#define RT5640_HP_OVCD 0x8b
+#define RT5640_CLS_D_OVCD 0x8c
+#define RT5640_CLS_D_OUT 0x8d
+#define RT5640_DEPOP_M1 0x8e
+#define RT5640_DEPOP_M2 0x8f
+#define RT5640_DEPOP_M3 0x90
+#define RT5640_CHARGE_PUMP 0x91
+#define RT5640_PV_DET_SPK_G 0x92
+#define RT5640_MICBIAS 0x93
+/* Function - Digital */
+#define RT5640_EQ_CTRL1 0xb0
+#define RT5640_EQ_CTRL2 0xb1
+#define RT5640_WIND_FILTER 0xb2
+#define RT5640_DRC_AGC_1 0xb4
+#define RT5640_DRC_AGC_2 0xb5
+#define RT5640_DRC_AGC_3 0xb6
+#define RT5640_SVOL_ZC 0xb7
+#define RT5640_ANC_CTRL1 0xb8
+#define RT5640_ANC_CTRL2 0xb9
+#define RT5640_ANC_CTRL3 0xba
+#define RT5640_JD_CTRL 0xbb
+#define RT5640_ANC_JD 0xbc
+#define RT5640_IRQ_CTRL1 0xbd
+#define RT5640_IRQ_CTRL2 0xbe
+#define RT5640_INT_IRQ_ST 0xbf
+#define RT5640_GPIO_CTRL1 0xc0
+#define RT5640_GPIO_CTRL2 0xc1
+#define RT5640_GPIO_CTRL3 0xc2
+#define RT5640_DSP_CTRL1 0xc4
+#define RT5640_DSP_CTRL2 0xc5
+#define RT5640_DSP_CTRL3 0xc6
+#define RT5640_DSP_CTRL4 0xc7
+#define RT5640_PGM_REG_ARR1 0xc8
+#define RT5640_PGM_REG_ARR2 0xc9
+#define RT5640_PGM_REG_ARR3 0xca
+#define RT5640_PGM_REG_ARR4 0xcb
+#define RT5640_PGM_REG_ARR5 0xcc
+#define RT5640_SCB_FUNC 0xcd
+#define RT5640_SCB_CTRL 0xce
+#define RT5640_BASE_BACK 0xcf
+#define RT5640_MP3_PLUS1 0xd0
+#define RT5640_MP3_PLUS2 0xd1
+#define RT5640_3D_HP 0xd2
+#define RT5640_ADJ_HPF 0xd3
+#define RT5640_HP_CALIB_AMP_DET 0xd6
+#define RT5640_HP_CALIB2 0xd7
+#define RT5640_SV_ZCD1 0xd9
+#define RT5640_SV_ZCD2 0xda
+/* Dummy Register */
+#define RT5640_DUMMY1 0xfa
+#define RT5640_DUMMY2 0xfb
+#define RT5640_DUMMY3 0xfc
+
+
+/* Index of Codec Private Register definition */
+#define RT5640_CHPUMP_INT_REG1 0x24
+#define RT5640_MAMP_INT_REG2 0x37
+#define RT5640_3D_SPK 0x63
+#define RT5640_WND_1 0x6c
+#define RT5640_WND_2 0x6d
+#define RT5640_WND_3 0x6e
+#define RT5640_WND_4 0x6f
+#define RT5640_WND_5 0x70
+#define RT5640_WND_8 0x73
+#define RT5640_DIP_SPK_INF 0x75
+#define RT5640_HP_DCC_INT1 0x77
+#define RT5640_EQ_BW_LOP 0xa0
+#define RT5640_EQ_GN_LOP 0xa1
+#define RT5640_EQ_FC_BP1 0xa2
+#define RT5640_EQ_BW_BP1 0xa3
+#define RT5640_EQ_GN_BP1 0xa4
+#define RT5640_EQ_FC_BP2 0xa5
+#define RT5640_EQ_BW_BP2 0xa6
+#define RT5640_EQ_GN_BP2 0xa7
+#define RT5640_EQ_FC_BP3 0xa8
+#define RT5640_EQ_BW_BP3 0xa9
+#define RT5640_EQ_GN_BP3 0xaa
+#define RT5640_EQ_FC_BP4 0xab
+#define RT5640_EQ_BW_BP4 0xac
+#define RT5640_EQ_GN_BP4 0xad
+#define RT5640_EQ_FC_HIP1 0xae
+#define RT5640_EQ_GN_HIP1 0xaf
+#define RT5640_EQ_FC_HIP2 0xb0
+#define RT5640_EQ_BW_HIP2 0xb1
+#define RT5640_EQ_GN_HIP2 0xb2
+#define RT5640_EQ_PRE_VOL 0xb3
+#define RT5640_EQ_PST_VOL 0xb4
+
+/* global definition */
+#define RT5640_L_MUTE (0x1 << 15)
+#define RT5640_L_MUTE_SFT 15
+#define RT5640_VOL_L_MUTE (0x1 << 14)
+#define RT5640_VOL_L_SFT 14
+#define RT5640_R_MUTE (0x1 << 7)
+#define RT5640_R_MUTE_SFT 7
+#define RT5640_VOL_R_MUTE (0x1 << 6)
+#define RT5640_VOL_R_SFT 6
+#define RT5640_L_VOL_MASK (0x3f << 8)
+#define RT5640_L_VOL_SFT 8
+#define RT5640_R_VOL_MASK (0x3f)
+#define RT5640_R_VOL_SFT 0
+
+/* SW Reset & Device ID (0x00) */
+#define RT5640_ID_MASK (0x3 << 1)
+#define RT5640_ID_5639 (0x0 << 1)
+#define RT5640_ID_5640 (0x2 << 1)
+#define RT5640_ID_5642 (0x3 << 1)
+
+
+/* IN1 and IN2 Control (0x0d) */
+/* IN3 and IN4 Control (0x0e) */
+#define RT5640_BST_SFT1 12
+#define RT5640_BST_SFT2 8
+#define RT5640_IN_DF1 (0x1 << 7)
+#define RT5640_IN_SFT1 7
+#define RT5640_IN_DF2 (0x1 << 6)
+#define RT5640_IN_SFT2 6
+
+/* INL and INR Volume Control (0x0f) */
+#define RT5640_INL_SEL_MASK (0x1 << 15)
+#define RT5640_INL_SEL_SFT 15
+#define RT5640_INL_SEL_IN4P (0x0 << 15)
+#define RT5640_INL_SEL_MONOP (0x1 << 15)
+#define RT5640_INL_VOL_MASK (0x1f << 8)
+#define RT5640_INL_VOL_SFT 8
+#define RT5640_INR_SEL_MASK (0x1 << 7)
+#define RT5640_INR_SEL_SFT 7
+#define RT5640_INR_SEL_IN4N (0x0 << 7)
+#define RT5640_INR_SEL_MONON (0x1 << 7)
+#define RT5640_INR_VOL_MASK (0x1f)
+#define RT5640_INR_VOL_SFT 0
+
+/* DAC1 Digital Volume (0x19) */
+#define RT5640_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5640_DAC_L1_VOL_SFT 8
+#define RT5640_DAC_R1_VOL_MASK (0xff)
+#define RT5640_DAC_R1_VOL_SFT 0
+
+/* DAC2 Digital Volume (0x1a) */
+#define RT5640_DAC_L2_VOL_MASK (0xff << 8)
+#define RT5640_DAC_L2_VOL_SFT 8
+#define RT5640_DAC_R2_VOL_MASK (0xff)
+#define RT5640_DAC_R2_VOL_SFT 0
+
+/* DAC2 Control (0x1b) */
+#define RT5640_M_DAC_L2_VOL (0x1 << 13)
+#define RT5640_M_DAC_L2_VOL_SFT 13
+#define RT5640_M_DAC_R2_VOL (0x1 << 12)
+#define RT5640_M_DAC_R2_VOL_SFT 12
+
+/* ADC Digital Volume Control (0x1c) */
+#define RT5640_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5640_ADC_L_VOL_SFT 8
+#define RT5640_ADC_R_VOL_MASK (0x7f)
+#define RT5640_ADC_R_VOL_SFT 0
+
+/* Mono ADC Digital Volume Control (0x1d) */
+#define RT5640_MONO_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5640_MONO_ADC_L_VOL_SFT 8
+#define RT5640_MONO_ADC_R_VOL_MASK (0x7f)
+#define RT5640_MONO_ADC_R_VOL_SFT 0
+
+/* ADC Boost Volume Control (0x1e) */
+#define RT5640_ADC_L_BST_MASK (0x3 << 14)
+#define RT5640_ADC_L_BST_SFT 14
+#define RT5640_ADC_R_BST_MASK (0x3 << 12)
+#define RT5640_ADC_R_BST_SFT 12
+#define RT5640_ADC_COMP_MASK (0x3 << 10)
+#define RT5640_ADC_COMP_SFT 10
+
+/* Stereo ADC Mixer Control (0x27) */
+#define RT5640_M_ADC_L1 (0x1 << 14)
+#define RT5640_M_ADC_L1_SFT 14
+#define RT5640_M_ADC_L2 (0x1 << 13)
+#define RT5640_M_ADC_L2_SFT 13
+#define RT5640_ADC_1_SRC_MASK (0x1 << 12)
+#define RT5640_ADC_1_SRC_SFT 12
+#define RT5640_ADC_1_SRC_ADC (0x1 << 12)
+#define RT5640_ADC_1_SRC_DACMIX (0x0 << 12)
+#define RT5640_ADC_2_SRC_MASK (0x3 << 10)
+#define RT5640_ADC_2_SRC_SFT 10
+#define RT5640_ADC_2_SRC_DMIC1 (0x0 << 10)
+#define RT5640_ADC_2_SRC_DMIC2 (0x1 << 10)
+#define RT5640_ADC_2_SRC_DACMIX (0x2 << 10)
+#define RT5640_M_ADC_R1 (0x1 << 6)
+#define RT5640_M_ADC_R1_SFT 6
+#define RT5640_M_ADC_R2 (0x1 << 5)
+#define RT5640_M_ADC_R2_SFT 5
+
+/* Mono ADC Mixer Control (0x28) */
+#define RT5640_M_MONO_ADC_L1 (0x1 << 14)
+#define RT5640_M_MONO_ADC_L1_SFT 14
+#define RT5640_M_MONO_ADC_L2 (0x1 << 13)
+#define RT5640_M_MONO_ADC_L2_SFT 13
+#define RT5640_MONO_ADC_L1_SRC_MASK (0x1 << 12)
+#define RT5640_MONO_ADC_L1_SRC_SFT 12
+#define RT5640_MONO_ADC_L1_SRC_DACMIXL (0x0 << 12)
+#define RT5640_MONO_ADC_L1_SRC_ADCL (0x1 << 12)
+#define RT5640_MONO_ADC_L2_SRC_MASK (0x3 << 10)
+#define RT5640_MONO_ADC_L2_SRC_SFT 10
+#define RT5640_MONO_ADC_L2_SRC_DMIC_L1 (0x0 << 10)
+#define RT5640_MONO_ADC_L2_SRC_DMIC_L2 (0x1 << 10)
+#define RT5640_MONO_ADC_L2_SRC_DACMIXL (0x2 << 10)
+#define RT5640_M_MONO_ADC_R1 (0x1 << 6)
+#define RT5640_M_MONO_ADC_R1_SFT 6
+#define RT5640_M_MONO_ADC_R2 (0x1 << 5)
+#define RT5640_M_MONO_ADC_R2_SFT 5
+#define RT5640_MONO_ADC_R1_SRC_MASK (0x1 << 4)
+#define RT5640_MONO_ADC_R1_SRC_SFT 4
+#define RT5640_MONO_ADC_R1_SRC_ADCR (0x1 << 4)
+#define RT5640_MONO_ADC_R1_SRC_DACMIXR (0x0 << 4)
+#define RT5640_MONO_ADC_R2_SRC_MASK (0x3 << 2)
+#define RT5640_MONO_ADC_R2_SRC_SFT 2
+#define RT5640_MONO_ADC_R2_SRC_DMIC_R1 (0x0 << 2)
+#define RT5640_MONO_ADC_R2_SRC_DMIC_R2 (0x1 << 2)
+#define RT5640_MONO_ADC_R2_SRC_DACMIXR (0x2 << 2)
+
+/* ADC Mixer to DAC Mixer Control (0x29) */
+#define RT5640_M_ADCMIX_L (0x1 << 15)
+#define RT5640_M_ADCMIX_L_SFT 15
+#define RT5640_M_IF1_DAC_L (0x1 << 14)
+#define RT5640_M_IF1_DAC_L_SFT 14
+#define RT5640_M_ADCMIX_R (0x1 << 7)
+#define RT5640_M_ADCMIX_R_SFT 7
+#define RT5640_M_IF1_DAC_R (0x1 << 6)
+#define RT5640_M_IF1_DAC_R_SFT 6
+
+/* Stereo DAC Mixer Control (0x2a) */
+#define RT5640_M_DAC_L1 (0x1 << 14)
+#define RT5640_M_DAC_L1_SFT 14
+#define RT5640_DAC_L1_STO_L_VOL_MASK (0x1 << 13)
+#define RT5640_DAC_L1_STO_L_VOL_SFT 13
+#define RT5640_M_DAC_L2 (0x1 << 12)
+#define RT5640_M_DAC_L2_SFT 12
+#define RT5640_DAC_L2_STO_L_VOL_MASK (0x1 << 11)
+#define RT5640_DAC_L2_STO_L_VOL_SFT 11
+#define RT5640_M_ANC_DAC_L (0x1 << 10)
+#define RT5640_M_ANC_DAC_L_SFT 10
+#define RT5640_M_DAC_R1 (0x1 << 6)
+#define RT5640_M_DAC_R1_SFT 6
+#define RT5640_DAC_R1_STO_R_VOL_MASK (0x1 << 5)
+#define RT5640_DAC_R1_STO_R_VOL_SFT 5
+#define RT5640_M_DAC_R2 (0x1 << 4)
+#define RT5640_M_DAC_R2_SFT 4
+#define RT5640_DAC_R2_STO_R_VOL_MASK (0x1 << 3)
+#define RT5640_DAC_R2_STO_R_VOL_SFT 3
+#define RT5640_M_ANC_DAC_R (0x1 << 2)
+#define RT5640_M_ANC_DAC_R_SFT 2
+
+/* Mono DAC Mixer Control (0x2b) */
+#define RT5640_M_DAC_L1_MONO_L (0x1 << 14)
+#define RT5640_M_DAC_L1_MONO_L_SFT 14
+#define RT5640_DAC_L1_MONO_L_VOL_MASK (0x1 << 13)
+#define RT5640_DAC_L1_MONO_L_VOL_SFT 13
+#define RT5640_M_DAC_L2_MONO_L (0x1 << 12)
+#define RT5640_M_DAC_L2_MONO_L_SFT 12
+#define RT5640_DAC_L2_MONO_L_VOL_MASK (0x1 << 11)
+#define RT5640_DAC_L2_MONO_L_VOL_SFT 11
+#define RT5640_M_DAC_R2_MONO_L (0x1 << 10)
+#define RT5640_M_DAC_R2_MONO_L_SFT 10
+#define RT5640_DAC_R2_MONO_L_VOL_MASK (0x1 << 9)
+#define RT5640_DAC_R2_MONO_L_VOL_SFT 9
+#define RT5640_M_DAC_R1_MONO_R (0x1 << 6)
+#define RT5640_M_DAC_R1_MONO_R_SFT 6
+#define RT5640_DAC_R1_MONO_R_VOL_MASK (0x1 << 5)
+#define RT5640_DAC_R1_MONO_R_VOL_SFT 5
+#define RT5640_M_DAC_R2_MONO_R (0x1 << 4)
+#define RT5640_M_DAC_R2_MONO_R_SFT 4
+#define RT5640_DAC_R2_MONO_R_VOL_MASK (0x1 << 3)
+#define RT5640_DAC_R2_MONO_R_VOL_SFT 3
+#define RT5640_M_DAC_L2_MONO_R (0x1 << 2)
+#define RT5640_M_DAC_L2_MONO_R_SFT 2
+#define RT5640_DAC_L2_MONO_R_VOL_MASK (0x1 << 1)
+#define RT5640_DAC_L2_MONO_R_VOL_SFT 1
+
+/* Digital Mixer Control (0x2c) */
+#define RT5640_M_STO_L_DAC_L (0x1 << 15)
+#define RT5640_M_STO_L_DAC_L_SFT 15
+#define RT5640_STO_L_DAC_L_VOL_MASK (0x1 << 14)
+#define RT5640_STO_L_DAC_L_VOL_SFT 14
+#define RT5640_M_DAC_L2_DAC_L (0x1 << 13)
+#define RT5640_M_DAC_L2_DAC_L_SFT 13
+#define RT5640_DAC_L2_DAC_L_VOL_MASK (0x1 << 12)
+#define RT5640_DAC_L2_DAC_L_VOL_SFT 12
+#define RT5640_M_STO_R_DAC_R (0x1 << 11)
+#define RT5640_M_STO_R_DAC_R_SFT 11
+#define RT5640_STO_R_DAC_R_VOL_MASK (0x1 << 10)
+#define RT5640_STO_R_DAC_R_VOL_SFT 10
+#define RT5640_M_DAC_R2_DAC_R (0x1 << 9)
+#define RT5640_M_DAC_R2_DAC_R_SFT 9
+#define RT5640_DAC_R2_DAC_R_VOL_MASK (0x1 << 8)
+#define RT5640_DAC_R2_DAC_R_VOL_SFT 8
+
+/* DSP Path Control 1 (0x2d) */
+#define RT5640_RXDP_SRC_MASK (0x1 << 15)
+#define RT5640_RXDP_SRC_SFT 15
+#define RT5640_RXDP_SRC_NOR (0x0 << 15)
+#define RT5640_RXDP_SRC_DIV3 (0x1 << 15)
+#define RT5640_TXDP_SRC_MASK (0x1 << 14)
+#define RT5640_TXDP_SRC_SFT 14
+#define RT5640_TXDP_SRC_NOR (0x0 << 14)
+#define RT5640_TXDP_SRC_DIV3 (0x1 << 14)
+
+/* DSP Path Control 2 (0x2e) */
+#define RT5640_DAC_L2_SEL_MASK (0x3 << 14)
+#define RT5640_DAC_L2_SEL_SFT 14
+#define RT5640_DAC_L2_SEL_IF2 (0x0 << 14)
+#define RT5640_DAC_L2_SEL_IF3 (0x1 << 14)
+#define RT5640_DAC_L2_SEL_TXDC (0x2 << 14)
+#define RT5640_DAC_L2_SEL_BASS (0x3 << 14)
+#define RT5640_DAC_R2_SEL_MASK (0x3 << 12)
+#define RT5640_DAC_R2_SEL_SFT 12
+#define RT5640_DAC_R2_SEL_IF2 (0x0 << 12)
+#define RT5640_DAC_R2_SEL_IF3 (0x1 << 12)
+#define RT5640_DAC_R2_SEL_TXDC (0x2 << 12)
+#define RT5640_IF2_ADC_L_SEL_MASK (0x1 << 11)
+#define RT5640_IF2_ADC_L_SEL_SFT 11
+#define RT5640_IF2_ADC_L_SEL_TXDP (0x0 << 11)
+#define RT5640_IF2_ADC_L_SEL_PASS (0x1 << 11)
+#define RT5640_IF2_ADC_R_SEL_MASK (0x1 << 10)
+#define RT5640_IF2_ADC_R_SEL_SFT 10
+#define RT5640_IF2_ADC_R_SEL_TXDP (0x0 << 10)
+#define RT5640_IF2_ADC_R_SEL_PASS (0x1 << 10)
+#define RT5640_RXDC_SEL_MASK (0x3 << 8)
+#define RT5640_RXDC_SEL_SFT 8
+#define RT5640_RXDC_SEL_NOR (0x0 << 8)
+#define RT5640_RXDC_SEL_L2R (0x1 << 8)
+#define RT5640_RXDC_SEL_R2L (0x2 << 8)
+#define RT5640_RXDC_SEL_SWAP (0x3 << 8)
+#define RT5640_RXDP_SEL_MASK (0x3 << 6)
+#define RT5640_RXDP_SEL_SFT 6
+#define RT5640_RXDP_SEL_NOR (0x0 << 6)
+#define RT5640_RXDP_SEL_L2R (0x1 << 6)
+#define RT5640_RXDP_SEL_R2L (0x2 << 6)
+#define RT5640_RXDP_SEL_SWAP (0x3 << 6)
+#define RT5640_TXDC_SEL_MASK (0x3 << 4)
+#define RT5640_TXDC_SEL_SFT 4
+#define RT5640_TXDC_SEL_NOR (0x0 << 4)
+#define RT5640_TXDC_SEL_L2R (0x1 << 4)
+#define RT5640_TXDC_SEL_R2L (0x2 << 4)
+#define RT5640_TXDC_SEL_SWAP (0x3 << 4)
+#define RT5640_TXDP_SEL_MASK (0x3 << 2)
+#define RT5640_TXDP_SEL_SFT 2
+#define RT5640_TXDP_SEL_NOR (0x0 << 2)
+#define RT5640_TXDP_SEL_L2R (0x1 << 2)
+#define RT5640_TXDP_SEL_R2L (0x2 << 2)
+#define RT5640_TRXDP_SEL_SWAP (0x3 << 2)
+
+/* Digital Interface Data Control (0x2f) */
+#define RT5640_IF1_DAC_SEL_MASK (0x3 << 14)
+#define RT5640_IF1_DAC_SEL_SFT 14
+#define RT5640_IF1_DAC_SEL_NOR (0x0 << 14)
+#define RT5640_IF1_DAC_SEL_L2R (0x1 << 14)
+#define RT5640_IF1_DAC_SEL_R2L (0x2 << 14)
+#define RT5640_IF1_DAC_SEL_SWAP (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_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_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_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_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)
+
+/* REC Left Mixer Control 1 (0x3b) */
+#define RT5640_G_HP_L_RM_L_MASK (0x7 << 13)
+#define RT5640_G_HP_L_RM_L_SFT 13
+#define RT5640_G_IN_L_RM_L_MASK (0x7 << 10)
+#define RT5640_G_IN_L_RM_L_SFT 10
+#define RT5640_G_BST4_RM_L_MASK (0x7 << 7)
+#define RT5640_G_BST4_RM_L_SFT 7
+#define RT5640_G_BST3_RM_L_MASK (0x7 << 4)
+#define RT5640_G_BST3_RM_L_SFT 4
+#define RT5640_G_BST2_RM_L_MASK (0x7 << 1)
+#define RT5640_G_BST2_RM_L_SFT 1
+
+/* REC Left Mixer Control 2 (0x3c) */
+#define RT5640_G_BST1_RM_L_MASK (0x7 << 13)
+#define RT5640_G_BST1_RM_L_SFT 13
+#define RT5640_G_OM_L_RM_L_MASK (0x7 << 10)
+#define RT5640_G_OM_L_RM_L_SFT 10
+#define RT5640_M_HP_L_RM_L (0x1 << 6)
+#define RT5640_M_HP_L_RM_L_SFT 6
+#define RT5640_M_IN_L_RM_L (0x1 << 5)
+#define RT5640_M_IN_L_RM_L_SFT 5
+#define RT5640_M_BST4_RM_L (0x1 << 4)
+#define RT5640_M_BST4_RM_L_SFT 4
+#define RT5640_M_BST3_RM_L (0x1 << 3)
+#define RT5640_M_BST3_RM_L_SFT 3
+#define RT5640_M_BST2_RM_L (0x1 << 2)
+#define RT5640_M_BST2_RM_L_SFT 2
+#define RT5640_M_BST1_RM_L (0x1 << 1)
+#define RT5640_M_BST1_RM_L_SFT 1
+#define RT5640_M_OM_L_RM_L (0x1)
+#define RT5640_M_OM_L_RM_L_SFT 0
+
+/* REC Right Mixer Control 1 (0x3d) */
+#define RT5640_G_HP_R_RM_R_MASK (0x7 << 13)
+#define RT5640_G_HP_R_RM_R_SFT 13
+#define RT5640_G_IN_R_RM_R_MASK (0x7 << 10)
+#define RT5640_G_IN_R_RM_R_SFT 10
+#define RT5640_G_BST4_RM_R_MASK (0x7 << 7)
+#define RT5640_G_BST4_RM_R_SFT 7
+#define RT5640_G_BST3_RM_R_MASK (0x7 << 4)
+#define RT5640_G_BST3_RM_R_SFT 4
+#define RT5640_G_BST2_RM_R_MASK (0x7 << 1)
+#define RT5640_G_BST2_RM_R_SFT 1
+
+/* REC Right Mixer Control 2 (0x3e) */
+#define RT5640_G_BST1_RM_R_MASK (0x7 << 13)
+#define RT5640_G_BST1_RM_R_SFT 13
+#define RT5640_G_OM_R_RM_R_MASK (0x7 << 10)
+#define RT5640_G_OM_R_RM_R_SFT 10
+#define RT5640_M_HP_R_RM_R (0x1 << 6)
+#define RT5640_M_HP_R_RM_R_SFT 6
+#define RT5640_M_IN_R_RM_R (0x1 << 5)
+#define RT5640_M_IN_R_RM_R_SFT 5
+#define RT5640_M_BST4_RM_R (0x1 << 4)
+#define RT5640_M_BST4_RM_R_SFT 4
+#define RT5640_M_BST3_RM_R (0x1 << 3)
+#define RT5640_M_BST3_RM_R_SFT 3
+#define RT5640_M_BST2_RM_R (0x1 << 2)
+#define RT5640_M_BST2_RM_R_SFT 2
+#define RT5640_M_BST1_RM_R (0x1 << 1)
+#define RT5640_M_BST1_RM_R_SFT 1
+#define RT5640_M_OM_R_RM_R (0x1)
+#define RT5640_M_OM_R_RM_R_SFT 0
+
+/* HPMIX Control (0x45) */
+#define RT5640_M_DAC2_HM (0x1 << 15)
+#define RT5640_M_DAC2_HM_SFT 15
+#define RT5640_M_DAC1_HM (0x1 << 14)
+#define RT5640_M_DAC1_HM_SFT 14
+#define RT5640_M_HPVOL_HM (0x1 << 13)
+#define RT5640_M_HPVOL_HM_SFT 13
+#define RT5640_G_HPOMIX_MASK (0x1 << 12)
+#define RT5640_G_HPOMIX_SFT 12
+
+/* SPK Left Mixer Control (0x46) */
+#define RT5640_G_RM_L_SM_L_MASK (0x3 << 14)
+#define RT5640_G_RM_L_SM_L_SFT 14
+#define RT5640_G_IN_L_SM_L_MASK (0x3 << 12)
+#define RT5640_G_IN_L_SM_L_SFT 12
+#define RT5640_G_DAC_L1_SM_L_MASK (0x3 << 10)
+#define RT5640_G_DAC_L1_SM_L_SFT 10
+#define RT5640_G_DAC_L2_SM_L_MASK (0x3 << 8)
+#define RT5640_G_DAC_L2_SM_L_SFT 8
+#define RT5640_G_OM_L_SM_L_MASK (0x3 << 6)
+#define RT5640_G_OM_L_SM_L_SFT 6
+#define RT5640_M_RM_L_SM_L (0x1 << 5)
+#define RT5640_M_RM_L_SM_L_SFT 5
+#define RT5640_M_IN_L_SM_L (0x1 << 4)
+#define RT5640_M_IN_L_SM_L_SFT 4
+#define RT5640_M_DAC_L1_SM_L (0x1 << 3)
+#define RT5640_M_DAC_L1_SM_L_SFT 3
+#define RT5640_M_DAC_L2_SM_L (0x1 << 2)
+#define RT5640_M_DAC_L2_SM_L_SFT 2
+#define RT5640_M_OM_L_SM_L (0x1 << 1)
+#define RT5640_M_OM_L_SM_L_SFT 1
+
+/* SPK Right Mixer Control (0x47) */
+#define RT5640_G_RM_R_SM_R_MASK (0x3 << 14)
+#define RT5640_G_RM_R_SM_R_SFT 14
+#define RT5640_G_IN_R_SM_R_MASK (0x3 << 12)
+#define RT5640_G_IN_R_SM_R_SFT 12
+#define RT5640_G_DAC_R1_SM_R_MASK (0x3 << 10)
+#define RT5640_G_DAC_R1_SM_R_SFT 10
+#define RT5640_G_DAC_R2_SM_R_MASK (0x3 << 8)
+#define RT5640_G_DAC_R2_SM_R_SFT 8
+#define RT5640_G_OM_R_SM_R_MASK (0x3 << 6)
+#define RT5640_G_OM_R_SM_R_SFT 6
+#define RT5640_M_RM_R_SM_R (0x1 << 5)
+#define RT5640_M_RM_R_SM_R_SFT 5
+#define RT5640_M_IN_R_SM_R (0x1 << 4)
+#define RT5640_M_IN_R_SM_R_SFT 4
+#define RT5640_M_DAC_R1_SM_R (0x1 << 3)
+#define RT5640_M_DAC_R1_SM_R_SFT 3
+#define RT5640_M_DAC_R2_SM_R (0x1 << 2)
+#define RT5640_M_DAC_R2_SM_R_SFT 2
+#define RT5640_M_OM_R_SM_R (0x1 << 1)
+#define RT5640_M_OM_R_SM_R_SFT 1
+
+/* SPOLMIX Control (0x48) */
+#define RT5640_M_DAC_R1_SPM_L (0x1 << 15)
+#define RT5640_M_DAC_R1_SPM_L_SFT 15
+#define RT5640_M_DAC_L1_SPM_L (0x1 << 14)
+#define RT5640_M_DAC_L1_SPM_L_SFT 14
+#define RT5640_M_SV_R_SPM_L (0x1 << 13)
+#define RT5640_M_SV_R_SPM_L_SFT 13
+#define RT5640_M_SV_L_SPM_L (0x1 << 12)
+#define RT5640_M_SV_L_SPM_L_SFT 12
+#define RT5640_M_BST1_SPM_L (0x1 << 11)
+#define RT5640_M_BST1_SPM_L_SFT 11
+
+/* SPORMIX Control (0x49) */
+#define RT5640_M_DAC_R1_SPM_R (0x1 << 13)
+#define RT5640_M_DAC_R1_SPM_R_SFT 13
+#define RT5640_M_SV_R_SPM_R (0x1 << 12)
+#define RT5640_M_SV_R_SPM_R_SFT 12
+#define RT5640_M_BST1_SPM_R (0x1 << 11)
+#define RT5640_M_BST1_SPM_R_SFT 11
+
+/* SPOLMIX / SPORMIX Ratio Control (0x4a) */
+#define RT5640_SPO_CLSD_RATIO_MASK (0x7)
+#define RT5640_SPO_CLSD_RATIO_SFT 0
+
+/* Mono Output Mixer Control (0x4c) */
+#define RT5640_M_DAC_R2_MM (0x1 << 15)
+#define RT5640_M_DAC_R2_MM_SFT 15
+#define RT5640_M_DAC_L2_MM (0x1 << 14)
+#define RT5640_M_DAC_L2_MM_SFT 14
+#define RT5640_M_OV_R_MM (0x1 << 13)
+#define RT5640_M_OV_R_MM_SFT 13
+#define RT5640_M_OV_L_MM (0x1 << 12)
+#define RT5640_M_OV_L_MM_SFT 12
+#define RT5640_M_BST1_MM (0x1 << 11)
+#define RT5640_M_BST1_MM_SFT 11
+#define RT5640_G_MONOMIX_MASK (0x1 << 10)
+#define RT5640_G_MONOMIX_SFT 10
+
+/* Output Left Mixer Control 1 (0x4d) */
+#define RT5640_G_BST3_OM_L_MASK (0x7 << 13)
+#define RT5640_G_BST3_OM_L_SFT 13
+#define RT5640_G_BST2_OM_L_MASK (0x7 << 10)
+#define RT5640_G_BST2_OM_L_SFT 10
+#define RT5640_G_BST1_OM_L_MASK (0x7 << 7)
+#define RT5640_G_BST1_OM_L_SFT 7
+#define RT5640_G_IN_L_OM_L_MASK (0x7 << 4)
+#define RT5640_G_IN_L_OM_L_SFT 4
+#define RT5640_G_RM_L_OM_L_MASK (0x7 << 1)
+#define RT5640_G_RM_L_OM_L_SFT 1
+
+/* Output Left Mixer Control 2 (0x4e) */
+#define RT5640_G_DAC_R2_OM_L_MASK (0x7 << 13)
+#define RT5640_G_DAC_R2_OM_L_SFT 13
+#define RT5640_G_DAC_L2_OM_L_MASK (0x7 << 10)
+#define RT5640_G_DAC_L2_OM_L_SFT 10
+#define RT5640_G_DAC_L1_OM_L_MASK (0x7 << 7)
+#define RT5640_G_DAC_L1_OM_L_SFT 7
+
+/* Output Left Mixer Control 3 (0x4f) */
+#define RT5640_M_SM_L_OM_L (0x1 << 8)
+#define RT5640_M_SM_L_OM_L_SFT 8
+#define RT5640_M_BST3_OM_L (0x1 << 7)
+#define RT5640_M_BST3_OM_L_SFT 7
+#define RT5640_M_BST2_OM_L (0x1 << 6)
+#define RT5640_M_BST2_OM_L_SFT 6
+#define RT5640_M_BST1_OM_L (0x1 << 5)
+#define RT5640_M_BST1_OM_L_SFT 5
+#define RT5640_M_IN_L_OM_L (0x1 << 4)
+#define RT5640_M_IN_L_OM_L_SFT 4
+#define RT5640_M_RM_L_OM_L (0x1 << 3)
+#define RT5640_M_RM_L_OM_L_SFT 3
+#define RT5640_M_DAC_R2_OM_L (0x1 << 2)
+#define RT5640_M_DAC_R2_OM_L_SFT 2
+#define RT5640_M_DAC_L2_OM_L (0x1 << 1)
+#define RT5640_M_DAC_L2_OM_L_SFT 1
+#define RT5640_M_DAC_L1_OM_L (0x1)
+#define RT5640_M_DAC_L1_OM_L_SFT 0
+
+/* Output Right Mixer Control 1 (0x50) */
+#define RT5640_G_BST4_OM_R_MASK (0x7 << 13)
+#define RT5640_G_BST4_OM_R_SFT 13
+#define RT5640_G_BST2_OM_R_MASK (0x7 << 10)
+#define RT5640_G_BST2_OM_R_SFT 10
+#define RT5640_G_BST1_OM_R_MASK (0x7 << 7)
+#define RT5640_G_BST1_OM_R_SFT 7
+#define RT5640_G_IN_R_OM_R_MASK (0x7 << 4)
+#define RT5640_G_IN_R_OM_R_SFT 4
+#define RT5640_G_RM_R_OM_R_MASK (0x7 << 1)
+#define RT5640_G_RM_R_OM_R_SFT 1
+
+/* Output Right Mixer Control 2 (0x51) */
+#define RT5640_G_DAC_L2_OM_R_MASK (0x7 << 13)
+#define RT5640_G_DAC_L2_OM_R_SFT 13
+#define RT5640_G_DAC_R2_OM_R_MASK (0x7 << 10)
+#define RT5640_G_DAC_R2_OM_R_SFT 10
+#define RT5640_G_DAC_R1_OM_R_MASK (0x7 << 7)
+#define RT5640_G_DAC_R1_OM_R_SFT 7
+
+/* Output Right Mixer Control 3 (0x52) */
+#define RT5640_M_SM_L_OM_R (0x1 << 8)
+#define RT5640_M_SM_L_OM_R_SFT 8
+#define RT5640_M_BST4_OM_R (0x1 << 7)
+#define RT5640_M_BST4_OM_R_SFT 7
+#define RT5640_M_BST2_OM_R (0x1 << 6)
+#define RT5640_M_BST2_OM_R_SFT 6
+#define RT5640_M_BST1_OM_R (0x1 << 5)
+#define RT5640_M_BST1_OM_R_SFT 5
+#define RT5640_M_IN_R_OM_R (0x1 << 4)
+#define RT5640_M_IN_R_OM_R_SFT 4
+#define RT5640_M_RM_R_OM_R (0x1 << 3)
+#define RT5640_M_RM_R_OM_R_SFT 3
+#define RT5640_M_DAC_L2_OM_R (0x1 << 2)
+#define RT5640_M_DAC_L2_OM_R_SFT 2
+#define RT5640_M_DAC_R2_OM_R (0x1 << 1)
+#define RT5640_M_DAC_R2_OM_R_SFT 1
+#define RT5640_M_DAC_R1_OM_R (0x1)
+#define RT5640_M_DAC_R1_OM_R_SFT 0
+
+/* LOUT Mixer Control (0x53) */
+#define RT5640_M_DAC_L1_LM (0x1 << 15)
+#define RT5640_M_DAC_L1_LM_SFT 15
+#define RT5640_M_DAC_R1_LM (0x1 << 14)
+#define RT5640_M_DAC_R1_LM_SFT 14
+#define RT5640_M_OV_L_LM (0x1 << 13)
+#define RT5640_M_OV_L_LM_SFT 13
+#define RT5640_M_OV_R_LM (0x1 << 12)
+#define RT5640_M_OV_R_LM_SFT 12
+#define RT5640_G_LOUTMIX_MASK (0x1 << 11)
+#define RT5640_G_LOUTMIX_SFT 11
+
+/* Power Management for Digital 1 (0x61) */
+#define RT5640_PWR_I2S1 (0x1 << 15)
+#define RT5640_PWR_I2S1_BIT 15
+#define RT5640_PWR_I2S2 (0x1 << 14)
+#define RT5640_PWR_I2S2_BIT 14
+#define RT5640_PWR_DAC_L1 (0x1 << 12)
+#define RT5640_PWR_DAC_L1_BIT 12
+#define RT5640_PWR_DAC_R1 (0x1 << 11)
+#define RT5640_PWR_DAC_R1_BIT 11
+#define RT5640_PWR_DAC_L2 (0x1 << 7)
+#define RT5640_PWR_DAC_L2_BIT 7
+#define RT5640_PWR_DAC_R2 (0x1 << 6)
+#define RT5640_PWR_DAC_R2_BIT 6
+#define RT5640_PWR_ADC_L (0x1 << 2)
+#define RT5640_PWR_ADC_L_BIT 2
+#define RT5640_PWR_ADC_R (0x1 << 1)
+#define RT5640_PWR_ADC_R_BIT 1
+#define RT5640_PWR_CLS_D (0x1)
+#define RT5640_PWR_CLS_D_BIT 0
+
+/* Power Management for Digital 2 (0x62) */
+#define RT5640_PWR_ADC_SF (0x1 << 15)
+#define RT5640_PWR_ADC_SF_BIT 15
+#define RT5640_PWR_ADC_MF_L (0x1 << 14)
+#define RT5640_PWR_ADC_MF_L_BIT 14
+#define RT5640_PWR_ADC_MF_R (0x1 << 13)
+#define RT5640_PWR_ADC_MF_R_BIT 13
+#define RT5640_PWR_I2S_DSP (0x1 << 12)
+#define RT5640_PWR_I2S_DSP_BIT 12
+
+/* Power Management for Analog 1 (0x63) */
+#define RT5640_PWR_VREF1 (0x1 << 15)
+#define RT5640_PWR_VREF1_BIT 15
+#define RT5640_PWR_FV1 (0x1 << 14)
+#define RT5640_PWR_FV1_BIT 14
+#define RT5640_PWR_MB (0x1 << 13)
+#define RT5640_PWR_MB_BIT 13
+#define RT5640_PWR_LM (0x1 << 12)
+#define RT5640_PWR_LM_BIT 12
+#define RT5640_PWR_BG (0x1 << 11)
+#define RT5640_PWR_BG_BIT 11
+#define RT5640_PWR_MM (0x1 << 10)
+#define RT5640_PWR_MM_BIT 10
+#define RT5640_PWR_MA (0x1 << 8)
+#define RT5640_PWR_MA_BIT 8
+#define RT5640_PWR_HP_L (0x1 << 7)
+#define RT5640_PWR_HP_L_BIT 7
+#define RT5640_PWR_HP_R (0x1 << 6)
+#define RT5640_PWR_HP_R_BIT 6
+#define RT5640_PWR_HA (0x1 << 5)
+#define RT5640_PWR_HA_BIT 5
+#define RT5640_PWR_VREF2 (0x1 << 4)
+#define RT5640_PWR_VREF2_BIT 4
+#define RT5640_PWR_FV2 (0x1 << 3)
+#define RT5640_PWR_FV2_BIT 3
+#define RT5640_PWR_LDO2 (0x1 << 2)
+#define RT5640_PWR_LDO2_BIT 2
+
+/* Power Management for Analog 2 (0x64) */
+#define RT5640_PWR_BST1 (0x1 << 15)
+#define RT5640_PWR_BST1_BIT 15
+#define RT5640_PWR_BST2 (0x1 << 14)
+#define RT5640_PWR_BST2_BIT 14
+#define RT5640_PWR_BST3 (0x1 << 13)
+#define RT5640_PWR_BST3_BIT 13
+#define RT5640_PWR_BST4 (0x1 << 12)
+#define RT5640_PWR_BST4_BIT 12
+#define RT5640_PWR_MB1 (0x1 << 11)
+#define RT5640_PWR_MB1_BIT 11
+#define RT5640_PWR_PLL (0x1 << 9)
+#define RT5640_PWR_PLL_BIT 9
+
+/* Power Management for Mixer (0x65) */
+#define RT5640_PWR_OM_L (0x1 << 15)
+#define RT5640_PWR_OM_L_BIT 15
+#define RT5640_PWR_OM_R (0x1 << 14)
+#define RT5640_PWR_OM_R_BIT 14
+#define RT5640_PWR_SM_L (0x1 << 13)
+#define RT5640_PWR_SM_L_BIT 13
+#define RT5640_PWR_SM_R (0x1 << 12)
+#define RT5640_PWR_SM_R_BIT 12
+#define RT5640_PWR_RM_L (0x1 << 11)
+#define RT5640_PWR_RM_L_BIT 11
+#define RT5640_PWR_RM_R (0x1 << 10)
+#define RT5640_PWR_RM_R_BIT 10
+
+/* Power Management for Volume (0x66) */
+#define RT5640_PWR_SV_L (0x1 << 15)
+#define RT5640_PWR_SV_L_BIT 15
+#define RT5640_PWR_SV_R (0x1 << 14)
+#define RT5640_PWR_SV_R_BIT 14
+#define RT5640_PWR_OV_L (0x1 << 13)
+#define RT5640_PWR_OV_L_BIT 13
+#define RT5640_PWR_OV_R (0x1 << 12)
+#define RT5640_PWR_OV_R_BIT 12
+#define RT5640_PWR_HV_L (0x1 << 11)
+#define RT5640_PWR_HV_L_BIT 11
+#define RT5640_PWR_HV_R (0x1 << 10)
+#define RT5640_PWR_HV_R_BIT 10
+#define RT5640_PWR_IN_L (0x1 << 9)
+#define RT5640_PWR_IN_L_BIT 9
+#define RT5640_PWR_IN_R (0x1 << 8)
+#define RT5640_PWR_IN_R_BIT 8
+
+/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71 0x72) */
+#define RT5640_I2S_MS_MASK (0x1 << 15)
+#define RT5640_I2S_MS_SFT 15
+#define RT5640_I2S_MS_M (0x0 << 15)
+#define RT5640_I2S_MS_S (0x1 << 15)
+#define RT5640_I2S_IF_MASK (0x7 << 12)
+#define RT5640_I2S_IF_SFT 12
+#define RT5640_I2S_O_CP_MASK (0x3 << 10)
+#define RT5640_I2S_O_CP_SFT 10
+#define RT5640_I2S_O_CP_OFF (0x0 << 10)
+#define RT5640_I2S_O_CP_U_LAW (0x1 << 10)
+#define RT5640_I2S_O_CP_A_LAW (0x2 << 10)
+#define RT5640_I2S_I_CP_MASK (0x3 << 8)
+#define RT5640_I2S_I_CP_SFT 8
+#define RT5640_I2S_I_CP_OFF (0x0 << 8)
+#define RT5640_I2S_I_CP_U_LAW (0x1 << 8)
+#define RT5640_I2S_I_CP_A_LAW (0x2 << 8)
+#define RT5640_I2S_BP_MASK (0x1 << 7)
+#define RT5640_I2S_BP_SFT 7
+#define RT5640_I2S_BP_NOR (0x0 << 7)
+#define RT5640_I2S_BP_INV (0x1 << 7)
+#define RT5640_I2S_DL_MASK (0x3 << 2)
+#define RT5640_I2S_DL_SFT 2
+#define RT5640_I2S_DL_16 (0x0 << 2)
+#define RT5640_I2S_DL_20 (0x1 << 2)
+#define RT5640_I2S_DL_24 (0x2 << 2)
+#define RT5640_I2S_DL_8 (0x3 << 2)
+#define RT5640_I2S_DF_MASK (0x3)
+#define RT5640_I2S_DF_SFT 0
+#define RT5640_I2S_DF_I2S (0x0)
+#define RT5640_I2S_DF_LEFT (0x1)
+#define RT5640_I2S_DF_PCM_A (0x2)
+#define RT5640_I2S_DF_PCM_B (0x3)
+
+/* I2S2 Audio Serial Data Port Control (0x71) */
+#define RT5640_I2S2_SDI_MASK (0x1 << 6)
+#define RT5640_I2S2_SDI_SFT 6
+#define RT5640_I2S2_SDI_I2S1 (0x0 << 6)
+#define RT5640_I2S2_SDI_I2S2 (0x1 << 6)
+
+/* ADC/DAC Clock Control 1 (0x73) */
+#define RT5640_I2S_BCLK_MS1_MASK (0x1 << 15)
+#define RT5640_I2S_BCLK_MS1_SFT 15
+#define RT5640_I2S_BCLK_MS1_32 (0x0 << 15)
+#define RT5640_I2S_BCLK_MS1_64 (0x1 << 15)
+#define RT5640_I2S_PD1_MASK (0x7 << 12)
+#define RT5640_I2S_PD1_SFT 12
+#define RT5640_I2S_PD1_1 (0x0 << 12)
+#define RT5640_I2S_PD1_2 (0x1 << 12)
+#define RT5640_I2S_PD1_3 (0x2 << 12)
+#define RT5640_I2S_PD1_4 (0x3 << 12)
+#define RT5640_I2S_PD1_6 (0x4 << 12)
+#define RT5640_I2S_PD1_8 (0x5 << 12)
+#define RT5640_I2S_PD1_12 (0x6 << 12)
+#define RT5640_I2S_PD1_16 (0x7 << 12)
+#define RT5640_I2S_BCLK_MS2_MASK (0x1 << 11)
+#define RT5640_I2S_BCLK_MS2_SFT 11
+#define RT5640_I2S_BCLK_MS2_32 (0x0 << 11)
+#define RT5640_I2S_BCLK_MS2_64 (0x1 << 11)
+#define RT5640_I2S_PD2_MASK (0x7 << 8)
+#define RT5640_I2S_PD2_SFT 8
+#define RT5640_I2S_PD2_1 (0x0 << 8)
+#define RT5640_I2S_PD2_2 (0x1 << 8)
+#define RT5640_I2S_PD2_3 (0x2 << 8)
+#define RT5640_I2S_PD2_4 (0x3 << 8)
+#define RT5640_I2S_PD2_6 (0x4 << 8)
+#define RT5640_I2S_PD2_8 (0x5 << 8)
+#define RT5640_I2S_PD2_12 (0x6 << 8)
+#define RT5640_I2S_PD2_16 (0x7 << 8)
+#define RT5640_I2S_BCLK_MS3_MASK (0x1 << 7)
+#define RT5640_I2S_BCLK_MS3_SFT 7
+#define RT5640_I2S_BCLK_MS3_32 (0x0 << 7)
+#define RT5640_I2S_BCLK_MS3_64 (0x1 << 7)
+#define RT5640_I2S_PD3_MASK (0x7 << 4)
+#define RT5640_I2S_PD3_SFT 4
+#define RT5640_I2S_PD3_1 (0x0 << 4)
+#define RT5640_I2S_PD3_2 (0x1 << 4)
+#define RT5640_I2S_PD3_3 (0x2 << 4)
+#define RT5640_I2S_PD3_4 (0x3 << 4)
+#define RT5640_I2S_PD3_6 (0x4 << 4)
+#define RT5640_I2S_PD3_8 (0x5 << 4)
+#define RT5640_I2S_PD3_12 (0x6 << 4)
+#define RT5640_I2S_PD3_16 (0x7 << 4)
+#define RT5640_DAC_OSR_MASK (0x3 << 2)
+#define RT5640_DAC_OSR_SFT 2
+#define RT5640_DAC_OSR_128 (0x0 << 2)
+#define RT5640_DAC_OSR_64 (0x1 << 2)
+#define RT5640_DAC_OSR_32 (0x2 << 2)
+#define RT5640_DAC_OSR_16 (0x3 << 2)
+#define RT5640_ADC_OSR_MASK (0x3)
+#define RT5640_ADC_OSR_SFT 0
+#define RT5640_ADC_OSR_128 (0x0)
+#define RT5640_ADC_OSR_64 (0x1)
+#define RT5640_ADC_OSR_32 (0x2)
+#define RT5640_ADC_OSR_16 (0x3)
+
+/* ADC/DAC Clock Control 2 (0x74) */
+#define RT5640_DAC_L_OSR_MASK (0x3 << 14)
+#define RT5640_DAC_L_OSR_SFT 14
+#define RT5640_DAC_L_OSR_128 (0x0 << 14)
+#define RT5640_DAC_L_OSR_64 (0x1 << 14)
+#define RT5640_DAC_L_OSR_32 (0x2 << 14)
+#define RT5640_DAC_L_OSR_16 (0x3 << 14)
+#define RT5640_ADC_R_OSR_MASK (0x3 << 12)
+#define RT5640_ADC_R_OSR_SFT 12
+#define RT5640_ADC_R_OSR_128 (0x0 << 12)
+#define RT5640_ADC_R_OSR_64 (0x1 << 12)
+#define RT5640_ADC_R_OSR_32 (0x2 << 12)
+#define RT5640_ADC_R_OSR_16 (0x3 << 12)
+#define RT5640_DAHPF_EN (0x1 << 11)
+#define RT5640_DAHPF_EN_SFT 11
+#define RT5640_ADHPF_EN (0x1 << 10)
+#define RT5640_ADHPF_EN_SFT 10
+
+/* Digital Microphone Control (0x75) */
+#define RT5640_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5640_DMIC_1_EN_SFT 15
+#define RT5640_DMIC_1_DIS (0x0 << 15)
+#define RT5640_DMIC_1_EN (0x1 << 15)
+#define RT5640_DMIC_2_EN_MASK (0x1 << 14)
+#define RT5640_DMIC_2_EN_SFT 14
+#define RT5640_DMIC_2_DIS (0x0 << 14)
+#define RT5640_DMIC_2_EN (0x1 << 14)
+#define RT5640_DMIC_1L_LH_MASK (0x1 << 13)
+#define RT5640_DMIC_1L_LH_SFT 13
+#define RT5640_DMIC_1L_LH_FALLING (0x0 << 13)
+#define RT5640_DMIC_1L_LH_RISING (0x1 << 13)
+#define RT5640_DMIC_1R_LH_MASK (0x1 << 12)
+#define RT5640_DMIC_1R_LH_SFT 12
+#define RT5640_DMIC_1R_LH_FALLING (0x0 << 12)
+#define RT5640_DMIC_1R_LH_RISING (0x1 << 12)
+#define RT5640_DMIC_1_DP_MASK (0x1 << 11)
+#define RT5640_DMIC_1_DP_SFT 11
+#define RT5640_DMIC_1_DP_GPIO3 (0x0 << 11)
+#define RT5640_DMIC_1_DP_IN1P (0x1 << 11)
+#define RT5640_DMIC_2_DP_MASK (0x1 << 10)
+#define RT5640_DMIC_2_DP_SFT 10
+#define RT5640_DMIC_2_DP_GPIO4 (0x0 << 10)
+#define RT5640_DMIC_2_DP_IN1N (0x1 << 10)
+#define RT5640_DMIC_2L_LH_MASK (0x1 << 9)
+#define RT5640_DMIC_2L_LH_SFT 9
+#define RT5640_DMIC_2L_LH_FALLING (0x0 << 9)
+#define RT5640_DMIC_2L_LH_RISING (0x1 << 9)
+#define RT5640_DMIC_2R_LH_MASK (0x1 << 8)
+#define RT5640_DMIC_2R_LH_SFT 8
+#define RT5640_DMIC_2R_LH_FALLING (0x0 << 8)
+#define RT5640_DMIC_2R_LH_RISING (0x1 << 8)
+#define RT5640_DMIC_CLK_MASK (0x7 << 5)
+#define RT5640_DMIC_CLK_SFT 5
+
+/* Global Clock Control (0x80) */
+#define RT5640_SCLK_SRC_MASK (0x3 << 14)
+#define RT5640_SCLK_SRC_SFT 14
+#define RT5640_SCLK_SRC_MCLK (0x0 << 14)
+#define RT5640_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5640_PLL1_SRC_MASK (0x3 << 12)
+#define RT5640_PLL1_SRC_SFT 12
+#define RT5640_PLL1_SRC_MCLK (0x0 << 12)
+#define RT5640_PLL1_SRC_BCLK1 (0x1 << 12)
+#define RT5640_PLL1_SRC_BCLK2 (0x2 << 12)
+#define RT5640_PLL1_SRC_BCLK3 (0x3 << 12)
+#define RT5640_PLL1_PD_MASK (0x1 << 3)
+#define RT5640_PLL1_PD_SFT 3
+#define RT5640_PLL1_PD_1 (0x0 << 3)
+#define RT5640_PLL1_PD_2 (0x1 << 3)
+
+#define RT5640_PLL_INP_MAX 40000000
+#define RT5640_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x81) */
+#define RT5640_PLL_N_MAX 0x1ff
+#define RT5640_PLL_N_MASK (RT5640_PLL_N_MAX << 7)
+#define RT5640_PLL_N_SFT 7
+#define RT5640_PLL_K_MAX 0x1f
+#define RT5640_PLL_K_MASK (RT5640_PLL_K_MAX)
+#define RT5640_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x82) */
+#define RT5640_PLL_M_MAX 0xf
+#define RT5640_PLL_M_MASK (RT5640_PLL_M_MAX << 12)
+#define RT5640_PLL_M_SFT 12
+#define RT5640_PLL_M_BP (0x1 << 11)
+#define RT5640_PLL_M_BP_SFT 11
+
+/* ASRC Control 1 (0x83) */
+#define RT5640_STO_T_MASK (0x1 << 15)
+#define RT5640_STO_T_SFT 15
+#define RT5640_STO_T_SCLK (0x0 << 15)
+#define RT5640_STO_T_LRCK1 (0x1 << 15)
+#define RT5640_M1_T_MASK (0x1 << 14)
+#define RT5640_M1_T_SFT 14
+#define RT5640_M1_T_I2S2 (0x0 << 14)
+#define RT5640_M1_T_I2S2_D3 (0x1 << 14)
+#define RT5640_I2S2_F_MASK (0x1 << 12)
+#define RT5640_I2S2_F_SFT 12
+#define RT5640_I2S2_F_I2S2_D2 (0x0 << 12)
+#define RT5640_I2S2_F_I2S1_TCLK (0x1 << 12)
+#define RT5640_DMIC_1_M_MASK (0x1 << 9)
+#define RT5640_DMIC_1_M_SFT 9
+#define RT5640_DMIC_1_M_NOR (0x0 << 9)
+#define RT5640_DMIC_1_M_ASYN (0x1 << 9)
+#define RT5640_DMIC_2_M_MASK (0x1 << 8)
+#define RT5640_DMIC_2_M_SFT 8
+#define RT5640_DMIC_2_M_NOR (0x0 << 8)
+#define RT5640_DMIC_2_M_ASYN (0x1 << 8)
+
+/* ASRC Control 2 (0x84) */
+#define RT5640_MDA_L_M_MASK (0x1 << 15)
+#define RT5640_MDA_L_M_SFT 15
+#define RT5640_MDA_L_M_NOR (0x0 << 15)
+#define RT5640_MDA_L_M_ASYN (0x1 << 15)
+#define RT5640_MDA_R_M_MASK (0x1 << 14)
+#define RT5640_MDA_R_M_SFT 14
+#define RT5640_MDA_R_M_NOR (0x0 << 14)
+#define RT5640_MDA_R_M_ASYN (0x1 << 14)
+#define RT5640_MAD_L_M_MASK (0x1 << 13)
+#define RT5640_MAD_L_M_SFT 13
+#define RT5640_MAD_L_M_NOR (0x0 << 13)
+#define RT5640_MAD_L_M_ASYN (0x1 << 13)
+#define RT5640_MAD_R_M_MASK (0x1 << 12)
+#define RT5640_MAD_R_M_SFT 12
+#define RT5640_MAD_R_M_NOR (0x0 << 12)
+#define RT5640_MAD_R_M_ASYN (0x1 << 12)
+#define RT5640_ADC_M_MASK (0x1 << 11)
+#define RT5640_ADC_M_SFT 11
+#define RT5640_ADC_M_NOR (0x0 << 11)
+#define RT5640_ADC_M_ASYN (0x1 << 11)
+#define RT5640_STO_DAC_M_MASK (0x1 << 5)
+#define RT5640_STO_DAC_M_SFT 5
+#define RT5640_STO_DAC_M_NOR (0x0 << 5)
+#define RT5640_STO_DAC_M_ASYN (0x1 << 5)
+#define RT5640_I2S1_R_D_MASK (0x1 << 4)
+#define RT5640_I2S1_R_D_SFT 4
+#define RT5640_I2S1_R_D_DIS (0x0 << 4)
+#define RT5640_I2S1_R_D_EN (0x1 << 4)
+#define RT5640_I2S2_R_D_MASK (0x1 << 3)
+#define RT5640_I2S2_R_D_SFT 3
+#define RT5640_I2S2_R_D_DIS (0x0 << 3)
+#define RT5640_I2S2_R_D_EN (0x1 << 3)
+#define RT5640_PRE_SCLK_MASK (0x3)
+#define RT5640_PRE_SCLK_SFT 0
+#define RT5640_PRE_SCLK_512 (0x0)
+#define RT5640_PRE_SCLK_1024 (0x1)
+#define RT5640_PRE_SCLK_2048 (0x2)
+
+/* ASRC Control 3 (0x85) */
+#define RT5640_I2S1_RATE_MASK (0xf << 12)
+#define RT5640_I2S1_RATE_SFT 12
+#define RT5640_I2S2_RATE_MASK (0xf << 8)
+#define RT5640_I2S2_RATE_SFT 8
+
+/* ASRC Control 4 (0x89) */
+#define RT5640_I2S1_PD_MASK (0x7 << 12)
+#define RT5640_I2S1_PD_SFT 12
+#define RT5640_I2S2_PD_MASK (0x7 << 8)
+#define RT5640_I2S2_PD_SFT 8
+
+/* HPOUT Over Current Detection (0x8b) */
+#define RT5640_HP_OVCD_MASK (0x1 << 10)
+#define RT5640_HP_OVCD_SFT 10
+#define RT5640_HP_OVCD_DIS (0x0 << 10)
+#define RT5640_HP_OVCD_EN (0x1 << 10)
+#define RT5640_HP_OC_TH_MASK (0x3 << 8)
+#define RT5640_HP_OC_TH_SFT 8
+#define RT5640_HP_OC_TH_90 (0x0 << 8)
+#define RT5640_HP_OC_TH_105 (0x1 << 8)
+#define RT5640_HP_OC_TH_120 (0x2 << 8)
+#define RT5640_HP_OC_TH_135 (0x3 << 8)
+
+/* Class D Over Current Control (0x8c) */
+#define RT5640_CLSD_OC_MASK (0x1 << 9)
+#define RT5640_CLSD_OC_SFT 9
+#define RT5640_CLSD_OC_PU (0x0 << 9)
+#define RT5640_CLSD_OC_PD (0x1 << 9)
+#define RT5640_AUTO_PD_MASK (0x1 << 8)
+#define RT5640_AUTO_PD_SFT 8
+#define RT5640_AUTO_PD_DIS (0x0 << 8)
+#define RT5640_AUTO_PD_EN (0x1 << 8)
+#define RT5640_CLSD_OC_TH_MASK (0x3f)
+#define RT5640_CLSD_OC_TH_SFT 0
+
+/* Class D Output Control (0x8d) */
+#define RT5640_CLSD_RATIO_MASK (0xf << 12)
+#define RT5640_CLSD_RATIO_SFT 12
+#define RT5640_CLSD_OM_MASK (0x1 << 11)
+#define RT5640_CLSD_OM_SFT 11
+#define RT5640_CLSD_OM_MONO (0x0 << 11)
+#define RT5640_CLSD_OM_STO (0x1 << 11)
+#define RT5640_CLSD_SCH_MASK (0x1 << 10)
+#define RT5640_CLSD_SCH_SFT 10
+#define RT5640_CLSD_SCH_L (0x0 << 10)
+#define RT5640_CLSD_SCH_S (0x1 << 10)
+
+/* Depop Mode Control 1 (0x8e) */
+#define RT5640_SMT_TRIG_MASK (0x1 << 15)
+#define RT5640_SMT_TRIG_SFT 15
+#define RT5640_SMT_TRIG_DIS (0x0 << 15)
+#define RT5640_SMT_TRIG_EN (0x1 << 15)
+#define RT5640_HP_L_SMT_MASK (0x1 << 9)
+#define RT5640_HP_L_SMT_SFT 9
+#define RT5640_HP_L_SMT_DIS (0x0 << 9)
+#define RT5640_HP_L_SMT_EN (0x1 << 9)
+#define RT5640_HP_R_SMT_MASK (0x1 << 8)
+#define RT5640_HP_R_SMT_SFT 8
+#define RT5640_HP_R_SMT_DIS (0x0 << 8)
+#define RT5640_HP_R_SMT_EN (0x1 << 8)
+#define RT5640_HP_CD_PD_MASK (0x1 << 7)
+#define RT5640_HP_CD_PD_SFT 7
+#define RT5640_HP_CD_PD_DIS (0x0 << 7)
+#define RT5640_HP_CD_PD_EN (0x1 << 7)
+#define RT5640_RSTN_MASK (0x1 << 6)
+#define RT5640_RSTN_SFT 6
+#define RT5640_RSTN_DIS (0x0 << 6)
+#define RT5640_RSTN_EN (0x1 << 6)
+#define RT5640_RSTP_MASK (0x1 << 5)
+#define RT5640_RSTP_SFT 5
+#define RT5640_RSTP_DIS (0x0 << 5)
+#define RT5640_RSTP_EN (0x1 << 5)
+#define RT5640_HP_CO_MASK (0x1 << 4)
+#define RT5640_HP_CO_SFT 4
+#define RT5640_HP_CO_DIS (0x0 << 4)
+#define RT5640_HP_CO_EN (0x1 << 4)
+#define RT5640_HP_CP_MASK (0x1 << 3)
+#define RT5640_HP_CP_SFT 3
+#define RT5640_HP_CP_PD (0x0 << 3)
+#define RT5640_HP_CP_PU (0x1 << 3)
+#define RT5640_HP_SG_MASK (0x1 << 2)
+#define RT5640_HP_SG_SFT 2
+#define RT5640_HP_SG_DIS (0x0 << 2)
+#define RT5640_HP_SG_EN (0x1 << 2)
+#define RT5640_HP_DP_MASK (0x1 << 1)
+#define RT5640_HP_DP_SFT 1
+#define RT5640_HP_DP_PD (0x0 << 1)
+#define RT5640_HP_DP_PU (0x1 << 1)
+#define RT5640_HP_CB_MASK (0x1)
+#define RT5640_HP_CB_SFT 0
+#define RT5640_HP_CB_PD (0x0)
+#define RT5640_HP_CB_PU (0x1)
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5640_DEPOP_MASK (0x1 << 13)
+#define RT5640_DEPOP_SFT 13
+#define RT5640_DEPOP_AUTO (0x0 << 13)
+#define RT5640_DEPOP_MAN (0x1 << 13)
+#define RT5640_RAMP_MASK (0x1 << 12)
+#define RT5640_RAMP_SFT 12
+#define RT5640_RAMP_DIS (0x0 << 12)
+#define RT5640_RAMP_EN (0x1 << 12)
+#define RT5640_BPS_MASK (0x1 << 11)
+#define RT5640_BPS_SFT 11
+#define RT5640_BPS_DIS (0x0 << 11)
+#define RT5640_BPS_EN (0x1 << 11)
+#define RT5640_FAST_UPDN_MASK (0x1 << 10)
+#define RT5640_FAST_UPDN_SFT 10
+#define RT5640_FAST_UPDN_DIS (0x0 << 10)
+#define RT5640_FAST_UPDN_EN (0x1 << 10)
+#define RT5640_MRES_MASK (0x3 << 8)
+#define RT5640_MRES_SFT 8
+#define RT5640_MRES_15MO (0x0 << 8)
+#define RT5640_MRES_25MO (0x1 << 8)
+#define RT5640_MRES_35MO (0x2 << 8)
+#define RT5640_MRES_45MO (0x3 << 8)
+#define RT5640_VLO_MASK (0x1 << 7)
+#define RT5640_VLO_SFT 7
+#define RT5640_VLO_3V (0x0 << 7)
+#define RT5640_VLO_32V (0x1 << 7)
+#define RT5640_DIG_DP_MASK (0x1 << 6)
+#define RT5640_DIG_DP_SFT 6
+#define RT5640_DIG_DP_DIS (0x0 << 6)
+#define RT5640_DIG_DP_EN (0x1 << 6)
+#define RT5640_DP_TH_MASK (0x3 << 4)
+#define RT5640_DP_TH_SFT 4
+
+/* Depop Mode Control 3 (0x90) */
+#define RT5640_CP_SYS_MASK (0x7 << 12)
+#define RT5640_CP_SYS_SFT 12
+#define RT5640_CP_FQ1_MASK (0x7 << 8)
+#define RT5640_CP_FQ1_SFT 8
+#define RT5640_CP_FQ2_MASK (0x7 << 4)
+#define RT5640_CP_FQ2_SFT 4
+#define RT5640_CP_FQ3_MASK (0x7)
+#define RT5640_CP_FQ3_SFT 0
+#define RT5640_CP_FQ_1_5_KHZ 0
+#define RT5640_CP_FQ_3_KHZ 1
+#define RT5640_CP_FQ_6_KHZ 2
+#define RT5640_CP_FQ_12_KHZ 3
+#define RT5640_CP_FQ_24_KHZ 4
+#define RT5640_CP_FQ_48_KHZ 5
+#define RT5640_CP_FQ_96_KHZ 6
+#define RT5640_CP_FQ_192_KHZ 7
+
+/* HPOUT charge pump (0x91) */
+#define RT5640_OSW_L_MASK (0x1 << 11)
+#define RT5640_OSW_L_SFT 11
+#define RT5640_OSW_L_DIS (0x0 << 11)
+#define RT5640_OSW_L_EN (0x1 << 11)
+#define RT5640_OSW_R_MASK (0x1 << 10)
+#define RT5640_OSW_R_SFT 10
+#define RT5640_OSW_R_DIS (0x0 << 10)
+#define RT5640_OSW_R_EN (0x1 << 10)
+#define RT5640_PM_HP_MASK (0x3 << 8)
+#define RT5640_PM_HP_SFT 8
+#define RT5640_PM_HP_LV (0x0 << 8)
+#define RT5640_PM_HP_MV (0x1 << 8)
+#define RT5640_PM_HP_HV (0x2 << 8)
+#define RT5640_IB_HP_MASK (0x3 << 6)
+#define RT5640_IB_HP_SFT 6
+#define RT5640_IB_HP_125IL (0x0 << 6)
+#define RT5640_IB_HP_25IL (0x1 << 6)
+#define RT5640_IB_HP_5IL (0x2 << 6)
+#define RT5640_IB_HP_1IL (0x3 << 6)
+
+/* PV detection and SPK gain control (0x92) */
+#define RT5640_PVDD_DET_MASK (0x1 << 15)
+#define RT5640_PVDD_DET_SFT 15
+#define RT5640_PVDD_DET_DIS (0x0 << 15)
+#define RT5640_PVDD_DET_EN (0x1 << 15)
+#define RT5640_SPK_AG_MASK (0x1 << 14)
+#define RT5640_SPK_AG_SFT 14
+#define RT5640_SPK_AG_DIS (0x0 << 14)
+#define RT5640_SPK_AG_EN (0x1 << 14)
+
+/* Micbias Control (0x93) */
+#define RT5640_MIC1_BS_MASK (0x1 << 15)
+#define RT5640_MIC1_BS_SFT 15
+#define RT5640_MIC1_BS_9AV (0x0 << 15)
+#define RT5640_MIC1_BS_75AV (0x1 << 15)
+#define RT5640_MIC2_BS_MASK (0x1 << 14)
+#define RT5640_MIC2_BS_SFT 14
+#define RT5640_MIC2_BS_9AV (0x0 << 14)
+#define RT5640_MIC2_BS_75AV (0x1 << 14)
+#define RT5640_MIC1_CLK_MASK (0x1 << 13)
+#define RT5640_MIC1_CLK_SFT 13
+#define RT5640_MIC1_CLK_DIS (0x0 << 13)
+#define RT5640_MIC1_CLK_EN (0x1 << 13)
+#define RT5640_MIC2_CLK_MASK (0x1 << 12)
+#define RT5640_MIC2_CLK_SFT 12
+#define RT5640_MIC2_CLK_DIS (0x0 << 12)
+#define RT5640_MIC2_CLK_EN (0x1 << 12)
+#define RT5640_MIC1_OVCD_MASK (0x1 << 11)
+#define RT5640_MIC1_OVCD_SFT 11
+#define RT5640_MIC1_OVCD_DIS (0x0 << 11)
+#define RT5640_MIC1_OVCD_EN (0x1 << 11)
+#define RT5640_MIC1_OVTH_MASK (0x3 << 9)
+#define RT5640_MIC1_OVTH_SFT 9
+#define RT5640_MIC1_OVTH_600UA (0x0 << 9)
+#define RT5640_MIC1_OVTH_1500UA (0x1 << 9)
+#define RT5640_MIC1_OVTH_2000UA (0x2 << 9)
+#define RT5640_MIC2_OVCD_MASK (0x1 << 8)
+#define RT5640_MIC2_OVCD_SFT 8
+#define RT5640_MIC2_OVCD_DIS (0x0 << 8)
+#define RT5640_MIC2_OVCD_EN (0x1 << 8)
+#define RT5640_MIC2_OVTH_MASK (0x3 << 6)
+#define RT5640_MIC2_OVTH_SFT 6
+#define RT5640_MIC2_OVTH_600UA (0x0 << 6)
+#define RT5640_MIC2_OVTH_1500UA (0x1 << 6)
+#define RT5640_MIC2_OVTH_2000UA (0x2 << 6)
+#define RT5640_PWR_MB_MASK (0x1 << 5)
+#define RT5640_PWR_MB_SFT 5
+#define RT5640_PWR_MB_PD (0x0 << 5)
+#define RT5640_PWR_MB_PU (0x1 << 5)
+#define RT5640_PWR_CLK25M_MASK (0x1 << 4)
+#define RT5640_PWR_CLK25M_SFT 4
+#define RT5640_PWR_CLK25M_PD (0x0 << 4)
+#define RT5640_PWR_CLK25M_PU (0x1 << 4)
+
+/* EQ Control 1 (0xb0) */
+#define RT5640_EQ_SRC_MASK (0x1 << 15)
+#define RT5640_EQ_SRC_SFT 15
+#define RT5640_EQ_SRC_DAC (0x0 << 15)
+#define RT5640_EQ_SRC_ADC (0x1 << 15)
+#define RT5640_EQ_UPD (0x1 << 14)
+#define RT5640_EQ_UPD_BIT 14
+#define RT5640_EQ_CD_MASK (0x1 << 13)
+#define RT5640_EQ_CD_SFT 13
+#define RT5640_EQ_CD_DIS (0x0 << 13)
+#define RT5640_EQ_CD_EN (0x1 << 13)
+#define RT5640_EQ_DITH_MASK (0x3 << 8)
+#define RT5640_EQ_DITH_SFT 8
+#define RT5640_EQ_DITH_NOR (0x0 << 8)
+#define RT5640_EQ_DITH_LSB (0x1 << 8)
+#define RT5640_EQ_DITH_LSB_1 (0x2 << 8)
+#define RT5640_EQ_DITH_LSB_2 (0x3 << 8)
+
+/* EQ Control 2 (0xb1) */
+#define RT5640_EQ_HPF1_M_MASK (0x1 << 8)
+#define RT5640_EQ_HPF1_M_SFT 8
+#define RT5640_EQ_HPF1_M_HI (0x0 << 8)
+#define RT5640_EQ_HPF1_M_1ST (0x1 << 8)
+#define RT5640_EQ_LPF1_M_MASK (0x1 << 7)
+#define RT5640_EQ_LPF1_M_SFT 7
+#define RT5640_EQ_LPF1_M_LO (0x0 << 7)
+#define RT5640_EQ_LPF1_M_1ST (0x1 << 7)
+#define RT5640_EQ_HPF2_MASK (0x1 << 6)
+#define RT5640_EQ_HPF2_SFT 6
+#define RT5640_EQ_HPF2_DIS (0x0 << 6)
+#define RT5640_EQ_HPF2_EN (0x1 << 6)
+#define RT5640_EQ_HPF1_MASK (0x1 << 5)
+#define RT5640_EQ_HPF1_SFT 5
+#define RT5640_EQ_HPF1_DIS (0x0 << 5)
+#define RT5640_EQ_HPF1_EN (0x1 << 5)
+#define RT5640_EQ_BPF4_MASK (0x1 << 4)
+#define RT5640_EQ_BPF4_SFT 4
+#define RT5640_EQ_BPF4_DIS (0x0 << 4)
+#define RT5640_EQ_BPF4_EN (0x1 << 4)
+#define RT5640_EQ_BPF3_MASK (0x1 << 3)
+#define RT5640_EQ_BPF3_SFT 3
+#define RT5640_EQ_BPF3_DIS (0x0 << 3)
+#define RT5640_EQ_BPF3_EN (0x1 << 3)
+#define RT5640_EQ_BPF2_MASK (0x1 << 2)
+#define RT5640_EQ_BPF2_SFT 2
+#define RT5640_EQ_BPF2_DIS (0x0 << 2)
+#define RT5640_EQ_BPF2_EN (0x1 << 2)
+#define RT5640_EQ_BPF1_MASK (0x1 << 1)
+#define RT5640_EQ_BPF1_SFT 1
+#define RT5640_EQ_BPF1_DIS (0x0 << 1)
+#define RT5640_EQ_BPF1_EN (0x1 << 1)
+#define RT5640_EQ_LPF_MASK (0x1)
+#define RT5640_EQ_LPF_SFT 0
+#define RT5640_EQ_LPF_DIS (0x0)
+#define RT5640_EQ_LPF_EN (0x1)
+
+/* Memory Test (0xb2) */
+#define RT5640_MT_MASK (0x1 << 15)
+#define RT5640_MT_SFT 15
+#define RT5640_MT_DIS (0x0 << 15)
+#define RT5640_MT_EN (0x1 << 15)
+
+/* DRC/AGC Control 1 (0xb4) */
+#define RT5640_DRC_AGC_P_MASK (0x1 << 15)
+#define RT5640_DRC_AGC_P_SFT 15
+#define RT5640_DRC_AGC_P_DAC (0x0 << 15)
+#define RT5640_DRC_AGC_P_ADC (0x1 << 15)
+#define RT5640_DRC_AGC_MASK (0x1 << 14)
+#define RT5640_DRC_AGC_SFT 14
+#define RT5640_DRC_AGC_DIS (0x0 << 14)
+#define RT5640_DRC_AGC_EN (0x1 << 14)
+#define RT5640_DRC_AGC_UPD (0x1 << 13)
+#define RT5640_DRC_AGC_UPD_BIT 13
+#define RT5640_DRC_AGC_AR_MASK (0x1f << 8)
+#define RT5640_DRC_AGC_AR_SFT 8
+#define RT5640_DRC_AGC_R_MASK (0x7 << 5)
+#define RT5640_DRC_AGC_R_SFT 5
+#define RT5640_DRC_AGC_R_48K (0x1 << 5)
+#define RT5640_DRC_AGC_R_96K (0x2 << 5)
+#define RT5640_DRC_AGC_R_192K (0x3 << 5)
+#define RT5640_DRC_AGC_R_441K (0x5 << 5)
+#define RT5640_DRC_AGC_R_882K (0x6 << 5)
+#define RT5640_DRC_AGC_R_1764K (0x7 << 5)
+#define RT5640_DRC_AGC_RC_MASK (0x1f)
+#define RT5640_DRC_AGC_RC_SFT 0
+
+/* DRC/AGC Control 2 (0xb5) */
+#define RT5640_DRC_AGC_POB_MASK (0x3f << 8)
+#define RT5640_DRC_AGC_POB_SFT 8
+#define RT5640_DRC_AGC_CP_MASK (0x1 << 7)
+#define RT5640_DRC_AGC_CP_SFT 7
+#define RT5640_DRC_AGC_CP_DIS (0x0 << 7)
+#define RT5640_DRC_AGC_CP_EN (0x1 << 7)
+#define RT5640_DRC_AGC_CPR_MASK (0x3 << 5)
+#define RT5640_DRC_AGC_CPR_SFT 5
+#define RT5640_DRC_AGC_CPR_1_1 (0x0 << 5)
+#define RT5640_DRC_AGC_CPR_1_2 (0x1 << 5)
+#define RT5640_DRC_AGC_CPR_1_3 (0x2 << 5)
+#define RT5640_DRC_AGC_CPR_1_4 (0x3 << 5)
+#define RT5640_DRC_AGC_PRB_MASK (0x1f)
+#define RT5640_DRC_AGC_PRB_SFT 0
+
+/* DRC/AGC Control 3 (0xb6) */
+#define RT5640_DRC_AGC_NGB_MASK (0xf << 12)
+#define RT5640_DRC_AGC_NGB_SFT 12
+#define RT5640_DRC_AGC_TAR_MASK (0x1f << 7)
+#define RT5640_DRC_AGC_TAR_SFT 7
+#define RT5640_DRC_AGC_NG_MASK (0x1 << 6)
+#define RT5640_DRC_AGC_NG_SFT 6
+#define RT5640_DRC_AGC_NG_DIS (0x0 << 6)
+#define RT5640_DRC_AGC_NG_EN (0x1 << 6)
+#define RT5640_DRC_AGC_NGH_MASK (0x1 << 5)
+#define RT5640_DRC_AGC_NGH_SFT 5
+#define RT5640_DRC_AGC_NGH_DIS (0x0 << 5)
+#define RT5640_DRC_AGC_NGH_EN (0x1 << 5)
+#define RT5640_DRC_AGC_NGT_MASK (0x1f)
+#define RT5640_DRC_AGC_NGT_SFT 0
+
+/* ANC Control 1 (0xb8) */
+#define RT5640_ANC_M_MASK (0x1 << 15)
+#define RT5640_ANC_M_SFT 15
+#define RT5640_ANC_M_NOR (0x0 << 15)
+#define RT5640_ANC_M_REV (0x1 << 15)
+#define RT5640_ANC_MASK (0x1 << 14)
+#define RT5640_ANC_SFT 14
+#define RT5640_ANC_DIS (0x0 << 14)
+#define RT5640_ANC_EN (0x1 << 14)
+#define RT5640_ANC_MD_MASK (0x3 << 12)
+#define RT5640_ANC_MD_SFT 12
+#define RT5640_ANC_MD_DIS (0x0 << 12)
+#define RT5640_ANC_MD_67MS (0x1 << 12)
+#define RT5640_ANC_MD_267MS (0x2 << 12)
+#define RT5640_ANC_MD_1067MS (0x3 << 12)
+#define RT5640_ANC_SN_MASK (0x1 << 11)
+#define RT5640_ANC_SN_SFT 11
+#define RT5640_ANC_SN_DIS (0x0 << 11)
+#define RT5640_ANC_SN_EN (0x1 << 11)
+#define RT5640_ANC_CLK_MASK (0x1 << 10)
+#define RT5640_ANC_CLK_SFT 10
+#define RT5640_ANC_CLK_ANC (0x0 << 10)
+#define RT5640_ANC_CLK_REG (0x1 << 10)
+#define RT5640_ANC_ZCD_MASK (0x3 << 8)
+#define RT5640_ANC_ZCD_SFT 8
+#define RT5640_ANC_ZCD_DIS (0x0 << 8)
+#define RT5640_ANC_ZCD_T1 (0x1 << 8)
+#define RT5640_ANC_ZCD_T2 (0x2 << 8)
+#define RT5640_ANC_ZCD_WT (0x3 << 8)
+#define RT5640_ANC_CS_MASK (0x1 << 7)
+#define RT5640_ANC_CS_SFT 7
+#define RT5640_ANC_CS_DIS (0x0 << 7)
+#define RT5640_ANC_CS_EN (0x1 << 7)
+#define RT5640_ANC_SW_MASK (0x1 << 6)
+#define RT5640_ANC_SW_SFT 6
+#define RT5640_ANC_SW_NOR (0x0 << 6)
+#define RT5640_ANC_SW_AUTO (0x1 << 6)
+#define RT5640_ANC_CO_L_MASK (0x3f)
+#define RT5640_ANC_CO_L_SFT 0
+
+/* ANC Control 2 (0xb6) */
+#define RT5640_ANC_FG_R_MASK (0xf << 12)
+#define RT5640_ANC_FG_R_SFT 12
+#define RT5640_ANC_FG_L_MASK (0xf << 8)
+#define RT5640_ANC_FG_L_SFT 8
+#define RT5640_ANC_CG_R_MASK (0xf << 4)
+#define RT5640_ANC_CG_R_SFT 4
+#define RT5640_ANC_CG_L_MASK (0xf)
+#define RT5640_ANC_CG_L_SFT 0
+
+/* ANC Control 3 (0xb6) */
+#define RT5640_ANC_CD_MASK (0x1 << 6)
+#define RT5640_ANC_CD_SFT 6
+#define RT5640_ANC_CD_BOTH (0x0 << 6)
+#define RT5640_ANC_CD_IND (0x1 << 6)
+#define RT5640_ANC_CO_R_MASK (0x3f)
+#define RT5640_ANC_CO_R_SFT 0
+
+/* Jack Detect Control (0xbb) */
+#define RT5640_JD_MASK (0x7 << 13)
+#define RT5640_JD_SFT 13
+#define RT5640_JD_DIS (0x0 << 13)
+#define RT5640_JD_GPIO1 (0x1 << 13)
+#define RT5640_JD_JD1_IN4P (0x2 << 13)
+#define RT5640_JD_JD2_IN4N (0x3 << 13)
+#define RT5640_JD_GPIO2 (0x4 << 13)
+#define RT5640_JD_GPIO3 (0x5 << 13)
+#define RT5640_JD_GPIO4 (0x6 << 13)
+#define RT5640_JD_HP_MASK (0x1 << 11)
+#define RT5640_JD_HP_SFT 11
+#define RT5640_JD_HP_DIS (0x0 << 11)
+#define RT5640_JD_HP_EN (0x1 << 11)
+#define RT5640_JD_HP_TRG_MASK (0x1 << 10)
+#define RT5640_JD_HP_TRG_SFT 10
+#define RT5640_JD_HP_TRG_LO (0x0 << 10)
+#define RT5640_JD_HP_TRG_HI (0x1 << 10)
+#define RT5640_JD_SPL_MASK (0x1 << 9)
+#define RT5640_JD_SPL_SFT 9
+#define RT5640_JD_SPL_DIS (0x0 << 9)
+#define RT5640_JD_SPL_EN (0x1 << 9)
+#define RT5640_JD_SPL_TRG_MASK (0x1 << 8)
+#define RT5640_JD_SPL_TRG_SFT 8
+#define RT5640_JD_SPL_TRG_LO (0x0 << 8)
+#define RT5640_JD_SPL_TRG_HI (0x1 << 8)
+#define RT5640_JD_SPR_MASK (0x1 << 7)
+#define RT5640_JD_SPR_SFT 7
+#define RT5640_JD_SPR_DIS (0x0 << 7)
+#define RT5640_JD_SPR_EN (0x1 << 7)
+#define RT5640_JD_SPR_TRG_MASK (0x1 << 6)
+#define RT5640_JD_SPR_TRG_SFT 6
+#define RT5640_JD_SPR_TRG_LO (0x0 << 6)
+#define RT5640_JD_SPR_TRG_HI (0x1 << 6)
+#define RT5640_JD_MO_MASK (0x1 << 5)
+#define RT5640_JD_MO_SFT 5
+#define RT5640_JD_MO_DIS (0x0 << 5)
+#define RT5640_JD_MO_EN (0x1 << 5)
+#define RT5640_JD_MO_TRG_MASK (0x1 << 4)
+#define RT5640_JD_MO_TRG_SFT 4
+#define RT5640_JD_MO_TRG_LO (0x0 << 4)
+#define RT5640_JD_MO_TRG_HI (0x1 << 4)
+#define RT5640_JD_LO_MASK (0x1 << 3)
+#define RT5640_JD_LO_SFT 3
+#define RT5640_JD_LO_DIS (0x0 << 3)
+#define RT5640_JD_LO_EN (0x1 << 3)
+#define RT5640_JD_LO_TRG_MASK (0x1 << 2)
+#define RT5640_JD_LO_TRG_SFT 2
+#define RT5640_JD_LO_TRG_LO (0x0 << 2)
+#define RT5640_JD_LO_TRG_HI (0x1 << 2)
+#define RT5640_JD1_IN4P_MASK (0x1 << 1)
+#define RT5640_JD1_IN4P_SFT 1
+#define RT5640_JD1_IN4P_DIS (0x0 << 1)
+#define RT5640_JD1_IN4P_EN (0x1 << 1)
+#define RT5640_JD2_IN4N_MASK (0x1)
+#define RT5640_JD2_IN4N_SFT 0
+#define RT5640_JD2_IN4N_DIS (0x0)
+#define RT5640_JD2_IN4N_EN (0x1)
+
+/* Jack detect for ANC (0xbc) */
+#define RT5640_ANC_DET_MASK (0x3 << 4)
+#define RT5640_ANC_DET_SFT 4
+#define RT5640_ANC_DET_DIS (0x0 << 4)
+#define RT5640_ANC_DET_MB1 (0x1 << 4)
+#define RT5640_ANC_DET_MB2 (0x2 << 4)
+#define RT5640_ANC_DET_JD (0x3 << 4)
+#define RT5640_AD_TRG_MASK (0x1 << 3)
+#define RT5640_AD_TRG_SFT 3
+#define RT5640_AD_TRG_LO (0x0 << 3)
+#define RT5640_AD_TRG_HI (0x1 << 3)
+#define RT5640_ANCM_DET_MASK (0x3 << 4)
+#define RT5640_ANCM_DET_SFT 4
+#define RT5640_ANCM_DET_DIS (0x0 << 4)
+#define RT5640_ANCM_DET_MB1 (0x1 << 4)
+#define RT5640_ANCM_DET_MB2 (0x2 << 4)
+#define RT5640_ANCM_DET_JD (0x3 << 4)
+#define RT5640_AMD_TRG_MASK (0x1 << 3)
+#define RT5640_AMD_TRG_SFT 3
+#define RT5640_AMD_TRG_LO (0x0 << 3)
+#define RT5640_AMD_TRG_HI (0x1 << 3)
+
+/* IRQ Control 1 (0xbd) */
+#define RT5640_IRQ_JD_MASK (0x1 << 15)
+#define RT5640_IRQ_JD_SFT 15
+#define RT5640_IRQ_JD_BP (0x0 << 15)
+#define RT5640_IRQ_JD_NOR (0x1 << 15)
+#define RT5640_IRQ_OT_MASK (0x1 << 14)
+#define RT5640_IRQ_OT_SFT 14
+#define RT5640_IRQ_OT_BP (0x0 << 14)
+#define RT5640_IRQ_OT_NOR (0x1 << 14)
+#define RT5640_JD_STKY_MASK (0x1 << 13)
+#define RT5640_JD_STKY_SFT 13
+#define RT5640_JD_STKY_DIS (0x0 << 13)
+#define RT5640_JD_STKY_EN (0x1 << 13)
+#define RT5640_OT_STKY_MASK (0x1 << 12)
+#define RT5640_OT_STKY_SFT 12
+#define RT5640_OT_STKY_DIS (0x0 << 12)
+#define RT5640_OT_STKY_EN (0x1 << 12)
+#define RT5640_JD_P_MASK (0x1 << 11)
+#define RT5640_JD_P_SFT 11
+#define RT5640_JD_P_NOR (0x0 << 11)
+#define RT5640_JD_P_INV (0x1 << 11)
+#define RT5640_OT_P_MASK (0x1 << 10)
+#define RT5640_OT_P_SFT 10
+#define RT5640_OT_P_NOR (0x0 << 10)
+#define RT5640_OT_P_INV (0x1 << 10)
+
+/* IRQ Control 2 (0xbe) */
+#define RT5640_IRQ_MB1_OC_MASK (0x1 << 15)
+#define RT5640_IRQ_MB1_OC_SFT 15
+#define RT5640_IRQ_MB1_OC_BP (0x0 << 15)
+#define RT5640_IRQ_MB1_OC_NOR (0x1 << 15)
+#define RT5640_IRQ_MB2_OC_MASK (0x1 << 14)
+#define RT5640_IRQ_MB2_OC_SFT 14
+#define RT5640_IRQ_MB2_OC_BP (0x0 << 14)
+#define RT5640_IRQ_MB2_OC_NOR (0x1 << 14)
+#define RT5640_MB1_OC_STKY_MASK (0x1 << 11)
+#define RT5640_MB1_OC_STKY_SFT 11
+#define RT5640_MB1_OC_STKY_DIS (0x0 << 11)
+#define RT5640_MB1_OC_STKY_EN (0x1 << 11)
+#define RT5640_MB2_OC_STKY_MASK (0x1 << 10)
+#define RT5640_MB2_OC_STKY_SFT 10
+#define RT5640_MB2_OC_STKY_DIS (0x0 << 10)
+#define RT5640_MB2_OC_STKY_EN (0x1 << 10)
+#define RT5640_MB1_OC_P_MASK (0x1 << 7)
+#define RT5640_MB1_OC_P_SFT 7
+#define RT5640_MB1_OC_P_NOR (0x0 << 7)
+#define RT5640_MB1_OC_P_INV (0x1 << 7)
+#define RT5640_MB2_OC_P_MASK (0x1 << 6)
+#define RT5640_MB2_OC_P_SFT 6
+#define RT5640_MB2_OC_P_NOR (0x0 << 6)
+#define RT5640_MB2_OC_P_INV (0x1 << 6)
+#define RT5640_MB1_OC_CLR (0x1 << 3)
+#define RT5640_MB1_OC_CLR_SFT 3
+#define RT5640_MB2_OC_CLR (0x1 << 2)
+#define RT5640_MB2_OC_CLR_SFT 2
+
+/* GPIO Control 1 (0xc0) */
+#define RT5640_GP1_PIN_MASK (0x1 << 15)
+#define RT5640_GP1_PIN_SFT 15
+#define RT5640_GP1_PIN_GPIO1 (0x0 << 15)
+#define RT5640_GP1_PIN_IRQ (0x1 << 15)
+#define RT5640_GP2_PIN_MASK (0x1 << 14)
+#define RT5640_GP2_PIN_SFT 14
+#define RT5640_GP2_PIN_GPIO2 (0x0 << 14)
+#define RT5640_GP2_PIN_DMIC1_SCL (0x1 << 14)
+#define RT5640_GP3_PIN_MASK (0x3 << 12)
+#define RT5640_GP3_PIN_SFT 12
+#define RT5640_GP3_PIN_GPIO3 (0x0 << 12)
+#define RT5640_GP3_PIN_DMIC1_SDA (0x1 << 12)
+#define RT5640_GP3_PIN_IRQ (0x2 << 12)
+#define RT5640_GP4_PIN_MASK (0x1 << 11)
+#define RT5640_GP4_PIN_SFT 11
+#define RT5640_GP4_PIN_GPIO4 (0x0 << 11)
+#define RT5640_GP4_PIN_DMIC2_SDA (0x1 << 11)
+#define RT5640_DP_SIG_MASK (0x1 << 10)
+#define RT5640_DP_SIG_SFT 10
+#define RT5640_DP_SIG_TEST (0x0 << 10)
+#define RT5640_DP_SIG_AP (0x1 << 10)
+#define RT5640_GPIO_M_MASK (0x1 << 9)
+#define RT5640_GPIO_M_SFT 9
+#define RT5640_GPIO_M_FLT (0x0 << 9)
+#define RT5640_GPIO_M_PH (0x1 << 9)
+
+/* GPIO Control 3 (0xc2) */
+#define RT5640_GP4_PF_MASK (0x1 << 11)
+#define RT5640_GP4_PF_SFT 11
+#define RT5640_GP4_PF_IN (0x0 << 11)
+#define RT5640_GP4_PF_OUT (0x1 << 11)
+#define RT5640_GP4_OUT_MASK (0x1 << 10)
+#define RT5640_GP4_OUT_SFT 10
+#define RT5640_GP4_OUT_LO (0x0 << 10)
+#define RT5640_GP4_OUT_HI (0x1 << 10)
+#define RT5640_GP4_P_MASK (0x1 << 9)
+#define RT5640_GP4_P_SFT 9
+#define RT5640_GP4_P_NOR (0x0 << 9)
+#define RT5640_GP4_P_INV (0x1 << 9)
+#define RT5640_GP3_PF_MASK (0x1 << 8)
+#define RT5640_GP3_PF_SFT 8
+#define RT5640_GP3_PF_IN (0x0 << 8)
+#define RT5640_GP3_PF_OUT (0x1 << 8)
+#define RT5640_GP3_OUT_MASK (0x1 << 7)
+#define RT5640_GP3_OUT_SFT 7
+#define RT5640_GP3_OUT_LO (0x0 << 7)
+#define RT5640_GP3_OUT_HI (0x1 << 7)
+#define RT5640_GP3_P_MASK (0x1 << 6)
+#define RT5640_GP3_P_SFT 6
+#define RT5640_GP3_P_NOR (0x0 << 6)
+#define RT5640_GP3_P_INV (0x1 << 6)
+#define RT5640_GP2_PF_MASK (0x1 << 5)
+#define RT5640_GP2_PF_SFT 5
+#define RT5640_GP2_PF_IN (0x0 << 5)
+#define RT5640_GP2_PF_OUT (0x1 << 5)
+#define RT5640_GP2_OUT_MASK (0x1 << 4)
+#define RT5640_GP2_OUT_SFT 4
+#define RT5640_GP2_OUT_LO (0x0 << 4)
+#define RT5640_GP2_OUT_HI (0x1 << 4)
+#define RT5640_GP2_P_MASK (0x1 << 3)
+#define RT5640_GP2_P_SFT 3
+#define RT5640_GP2_P_NOR (0x0 << 3)
+#define RT5640_GP2_P_INV (0x1 << 3)
+#define RT5640_GP1_PF_MASK (0x1 << 2)
+#define RT5640_GP1_PF_SFT 2
+#define RT5640_GP1_PF_IN (0x0 << 2)
+#define RT5640_GP1_PF_OUT (0x1 << 2)
+#define RT5640_GP1_OUT_MASK (0x1 << 1)
+#define RT5640_GP1_OUT_SFT 1
+#define RT5640_GP1_OUT_LO (0x0 << 1)
+#define RT5640_GP1_OUT_HI (0x1 << 1)
+#define RT5640_GP1_P_MASK (0x1)
+#define RT5640_GP1_P_SFT 0
+#define RT5640_GP1_P_NOR (0x0)
+#define RT5640_GP1_P_INV (0x1)
+
+/* FM34-500 Register Control 1 (0xc4) */
+#define RT5640_DSP_ADD_SFT 0
+
+/* FM34-500 Register Control 2 (0xc5) */
+#define RT5640_DSP_DAT_SFT 0
+
+/* FM34-500 Register Control 3 (0xc6) */
+#define RT5640_DSP_BUSY_MASK (0x1 << 15)
+#define RT5640_DSP_BUSY_BIT 15
+#define RT5640_DSP_DS_MASK (0x1 << 14)
+#define RT5640_DSP_DS_SFT 14
+#define RT5640_DSP_DS_FM3010 (0x1 << 14)
+#define RT5640_DSP_DS_TEMP (0x1 << 14)
+#define RT5640_DSP_CLK_MASK (0x3 << 12)
+#define RT5640_DSP_CLK_SFT 12
+#define RT5640_DSP_CLK_384K (0x0 << 12)
+#define RT5640_DSP_CLK_192K (0x1 << 12)
+#define RT5640_DSP_CLK_96K (0x2 << 12)
+#define RT5640_DSP_CLK_64K (0x3 << 12)
+#define RT5640_DSP_PD_PIN_MASK (0x1 << 11)
+#define RT5640_DSP_PD_PIN_SFT 11
+#define RT5640_DSP_PD_PIN_LO (0x0 << 11)
+#define RT5640_DSP_PD_PIN_HI (0x1 << 11)
+#define RT5640_DSP_RST_PIN_MASK (0x1 << 10)
+#define RT5640_DSP_RST_PIN_SFT 10
+#define RT5640_DSP_RST_PIN_LO (0x0 << 10)
+#define RT5640_DSP_RST_PIN_HI (0x1 << 10)
+#define RT5640_DSP_R_EN (0x1 << 9)
+#define RT5640_DSP_R_EN_BIT 9
+#define RT5640_DSP_W_EN (0x1 << 8)
+#define RT5640_DSP_W_EN_BIT 8
+#define RT5640_DSP_CMD_MASK (0xff)
+#define RT5640_DSP_CMD_SFT 0
+#define RT5640_DSP_CMD_MW (0x3B) /* Memory Write */
+#define RT5640_DSP_CMD_MR (0x37) /* Memory Read */
+#define RT5640_DSP_CMD_RR (0x60) /* Register Read */
+#define RT5640_DSP_CMD_RW (0x68) /* Register Write */
+
+/* Programmable Register Array Control 1 (0xc8) */
+#define RT5640_REG_SEQ_MASK (0xf << 12)
+#define RT5640_REG_SEQ_SFT 12
+#define RT5640_SEQ1_ST_MASK (0x1 << 11) /*RO*/
+#define RT5640_SEQ1_ST_SFT 11
+#define RT5640_SEQ1_ST_RUN (0x0 << 11)
+#define RT5640_SEQ1_ST_FIN (0x1 << 11)
+#define RT5640_SEQ2_ST_MASK (0x1 << 10) /*RO*/
+#define RT5640_SEQ2_ST_SFT 10
+#define RT5640_SEQ2_ST_RUN (0x0 << 10)
+#define RT5640_SEQ2_ST_FIN (0x1 << 10)
+#define RT5640_REG_LV_MASK (0x1 << 9)
+#define RT5640_REG_LV_SFT 9
+#define RT5640_REG_LV_MX (0x0 << 9)
+#define RT5640_REG_LV_PR (0x1 << 9)
+#define RT5640_SEQ_2_PT_MASK (0x1 << 8)
+#define RT5640_SEQ_2_PT_BIT 8
+#define RT5640_REG_IDX_MASK (0xff)
+#define RT5640_REG_IDX_SFT 0
+
+/* Programmable Register Array Control 2 (0xc9) */
+#define RT5640_REG_DAT_MASK (0xffff)
+#define RT5640_REG_DAT_SFT 0
+
+/* Programmable Register Array Control 3 (0xca) */
+#define RT5640_SEQ_DLY_MASK (0xff << 8)
+#define RT5640_SEQ_DLY_SFT 8
+#define RT5640_PROG_MASK (0x1 << 7)
+#define RT5640_PROG_SFT 7
+#define RT5640_PROG_DIS (0x0 << 7)
+#define RT5640_PROG_EN (0x1 << 7)
+#define RT5640_SEQ1_PT_RUN (0x1 << 6)
+#define RT5640_SEQ1_PT_RUN_BIT 6
+#define RT5640_SEQ2_PT_RUN (0x1 << 5)
+#define RT5640_SEQ2_PT_RUN_BIT 5
+
+/* Programmable Register Array Control 4 (0xcb) */
+#define RT5640_SEQ1_START_MASK (0xf << 8)
+#define RT5640_SEQ1_START_SFT 8
+#define RT5640_SEQ1_END_MASK (0xf)
+#define RT5640_SEQ1_END_SFT 0
+
+/* Programmable Register Array Control 5 (0xcc) */
+#define RT5640_SEQ2_START_MASK (0xf << 8)
+#define RT5640_SEQ2_START_SFT 8
+#define RT5640_SEQ2_END_MASK (0xf)
+#define RT5640_SEQ2_END_SFT 0
+
+/* Scramble Function (0xcd) */
+#define RT5640_SCB_KEY_MASK (0xff)
+#define RT5640_SCB_KEY_SFT 0
+
+/* Scramble Control (0xce) */
+#define RT5640_SCB_SWAP_MASK (0x1 << 15)
+#define RT5640_SCB_SWAP_SFT 15
+#define RT5640_SCB_SWAP_DIS (0x0 << 15)
+#define RT5640_SCB_SWAP_EN (0x1 << 15)
+#define RT5640_SCB_MASK (0x1 << 14)
+#define RT5640_SCB_SFT 14
+#define RT5640_SCB_DIS (0x0 << 14)
+#define RT5640_SCB_EN (0x1 << 14)
+
+/* Baseback Control (0xcf) */
+#define RT5640_BB_MASK (0x1 << 15)
+#define RT5640_BB_SFT 15
+#define RT5640_BB_DIS (0x0 << 15)
+#define RT5640_BB_EN (0x1 << 15)
+#define RT5640_BB_CT_MASK (0x7 << 12)
+#define RT5640_BB_CT_SFT 12
+#define RT5640_BB_CT_A (0x0 << 12)
+#define RT5640_BB_CT_B (0x1 << 12)
+#define RT5640_BB_CT_C (0x2 << 12)
+#define RT5640_BB_CT_D (0x3 << 12)
+#define RT5640_M_BB_L_MASK (0x1 << 9)
+#define RT5640_M_BB_L_SFT 9
+#define RT5640_M_BB_R_MASK (0x1 << 8)
+#define RT5640_M_BB_R_SFT 8
+#define RT5640_M_BB_HPF_L_MASK (0x1 << 7)
+#define RT5640_M_BB_HPF_L_SFT 7
+#define RT5640_M_BB_HPF_R_MASK (0x1 << 6)
+#define RT5640_M_BB_HPF_R_SFT 6
+#define RT5640_G_BB_BST_MASK (0x3f)
+#define RT5640_G_BB_BST_SFT 0
+
+/* MP3 Plus Control 1 (0xd0) */
+#define RT5640_M_MP3_L_MASK (0x1 << 15)
+#define RT5640_M_MP3_L_SFT 15
+#define RT5640_M_MP3_R_MASK (0x1 << 14)
+#define RT5640_M_MP3_R_SFT 14
+#define RT5640_M_MP3_MASK (0x1 << 13)
+#define RT5640_M_MP3_SFT 13
+#define RT5640_M_MP3_DIS (0x0 << 13)
+#define RT5640_M_MP3_EN (0x1 << 13)
+#define RT5640_EG_MP3_MASK (0x1f << 8)
+#define RT5640_EG_MP3_SFT 8
+#define RT5640_MP3_HLP_MASK (0x1 << 7)
+#define RT5640_MP3_HLP_SFT 7
+#define RT5640_MP3_HLP_DIS (0x0 << 7)
+#define RT5640_MP3_HLP_EN (0x1 << 7)
+#define RT5640_M_MP3_ORG_L_MASK (0x1 << 6)
+#define RT5640_M_MP3_ORG_L_SFT 6
+#define RT5640_M_MP3_ORG_R_MASK (0x1 << 5)
+#define RT5640_M_MP3_ORG_R_SFT 5
+
+/* MP3 Plus Control 2 (0xd1) */
+#define RT5640_MP3_WT_MASK (0x1 << 13)
+#define RT5640_MP3_WT_SFT 13
+#define RT5640_MP3_WT_1_4 (0x0 << 13)
+#define RT5640_MP3_WT_1_2 (0x1 << 13)
+#define RT5640_OG_MP3_MASK (0x1f << 8)
+#define RT5640_OG_MP3_SFT 8
+#define RT5640_HG_MP3_MASK (0x3f)
+#define RT5640_HG_MP3_SFT 0
+
+/* 3D HP Control 1 (0xd2) */
+#define RT5640_3D_CF_MASK (0x1 << 15)
+#define RT5640_3D_CF_SFT 15
+#define RT5640_3D_CF_DIS (0x0 << 15)
+#define RT5640_3D_CF_EN (0x1 << 15)
+#define RT5640_3D_HP_MASK (0x1 << 14)
+#define RT5640_3D_HP_SFT 14
+#define RT5640_3D_HP_DIS (0x0 << 14)
+#define RT5640_3D_HP_EN (0x1 << 14)
+#define RT5640_3D_BT_MASK (0x1 << 13)
+#define RT5640_3D_BT_SFT 13
+#define RT5640_3D_BT_DIS (0x0 << 13)
+#define RT5640_3D_BT_EN (0x1 << 13)
+#define RT5640_3D_1F_MIX_MASK (0x3 << 11)
+#define RT5640_3D_1F_MIX_SFT 11
+#define RT5640_3D_HP_M_MASK (0x1 << 10)
+#define RT5640_3D_HP_M_SFT 10
+#define RT5640_3D_HP_M_SUR (0x0 << 10)
+#define RT5640_3D_HP_M_FRO (0x1 << 10)
+#define RT5640_M_3D_HRTF_MASK (0x1 << 9)
+#define RT5640_M_3D_HRTF_SFT 9
+#define RT5640_M_3D_D2H_MASK (0x1 << 8)
+#define RT5640_M_3D_D2H_SFT 8
+#define RT5640_M_3D_D2R_MASK (0x1 << 7)
+#define RT5640_M_3D_D2R_SFT 7
+#define RT5640_M_3D_REVB_MASK (0x1 << 6)
+#define RT5640_M_3D_REVB_SFT 6
+
+/* Adjustable high pass filter control 1 (0xd3) */
+#define RT5640_2ND_HPF_MASK (0x1 << 15)
+#define RT5640_2ND_HPF_SFT 15
+#define RT5640_2ND_HPF_DIS (0x0 << 15)
+#define RT5640_2ND_HPF_EN (0x1 << 15)
+#define RT5640_HPF_CF_L_MASK (0x7 << 12)
+#define RT5640_HPF_CF_L_SFT 12
+#define RT5640_1ST_HPF_MASK (0x1 << 11)
+#define RT5640_1ST_HPF_SFT 11
+#define RT5640_1ST_HPF_DIS (0x0 << 11)
+#define RT5640_1ST_HPF_EN (0x1 << 11)
+#define RT5640_HPF_CF_R_MASK (0x7 << 8)
+#define RT5640_HPF_CF_R_SFT 8
+#define RT5640_ZD_T_MASK (0x3 << 6)
+#define RT5640_ZD_T_SFT 6
+#define RT5640_ZD_F_MASK (0x3 << 4)
+#define RT5640_ZD_F_SFT 4
+#define RT5640_ZD_F_IM (0x0 << 4)
+#define RT5640_ZD_F_ZC_IM (0x1 << 4)
+#define RT5640_ZD_F_ZC_IOD (0x2 << 4)
+#define RT5640_ZD_F_UN (0x3 << 4)
+
+/* HP calibration control and Amp detection (0xd6) */
+#define RT5640_SI_DAC_MASK (0x1 << 11)
+#define RT5640_SI_DAC_SFT 11
+#define RT5640_SI_DAC_AUTO (0x0 << 11)
+#define RT5640_SI_DAC_TEST (0x1 << 11)
+#define RT5640_DC_CAL_M_MASK (0x1 << 10)
+#define RT5640_DC_CAL_M_SFT 10
+#define RT5640_DC_CAL_M_CAL (0x0 << 10)
+#define RT5640_DC_CAL_M_NOR (0x1 << 10)
+#define RT5640_DC_CAL_MASK (0x1 << 9)
+#define RT5640_DC_CAL_SFT 9
+#define RT5640_DC_CAL_DIS (0x0 << 9)
+#define RT5640_DC_CAL_EN (0x1 << 9)
+#define RT5640_HPD_RCV_MASK (0x7 << 6)
+#define RT5640_HPD_RCV_SFT 6
+#define RT5640_HPD_PS_MASK (0x1 << 5)
+#define RT5640_HPD_PS_SFT 5
+#define RT5640_HPD_PS_DIS (0x0 << 5)
+#define RT5640_HPD_PS_EN (0x1 << 5)
+#define RT5640_CAL_M_MASK (0x1 << 4)
+#define RT5640_CAL_M_SFT 4
+#define RT5640_CAL_M_DEP (0x0 << 4)
+#define RT5640_CAL_M_CAL (0x1 << 4)
+#define RT5640_CAL_MASK (0x1 << 3)
+#define RT5640_CAL_SFT 3
+#define RT5640_CAL_DIS (0x0 << 3)
+#define RT5640_CAL_EN (0x1 << 3)
+#define RT5640_CAL_TEST_MASK (0x1 << 2)
+#define RT5640_CAL_TEST_SFT 2
+#define RT5640_CAL_TEST_DIS (0x0 << 2)
+#define RT5640_CAL_TEST_EN (0x1 << 2)
+#define RT5640_CAL_P_MASK (0x3)
+#define RT5640_CAL_P_SFT 0
+#define RT5640_CAL_P_NONE (0x0)
+#define RT5640_CAL_P_CAL (0x1)
+#define RT5640_CAL_P_DAC_CAL (0x2)
+
+/* Soft volume and zero cross control 1 (0xd9) */
+#define RT5640_SV_MASK (0x1 << 15)
+#define RT5640_SV_SFT 15
+#define RT5640_SV_DIS (0x0 << 15)
+#define RT5640_SV_EN (0x1 << 15)
+#define RT5640_SPO_SV_MASK (0x1 << 14)
+#define RT5640_SPO_SV_SFT 14
+#define RT5640_SPO_SV_DIS (0x0 << 14)
+#define RT5640_SPO_SV_EN (0x1 << 14)
+#define RT5640_OUT_SV_MASK (0x1 << 13)
+#define RT5640_OUT_SV_SFT 13
+#define RT5640_OUT_SV_DIS (0x0 << 13)
+#define RT5640_OUT_SV_EN (0x1 << 13)
+#define RT5640_HP_SV_MASK (0x1 << 12)
+#define RT5640_HP_SV_SFT 12
+#define RT5640_HP_SV_DIS (0x0 << 12)
+#define RT5640_HP_SV_EN (0x1 << 12)
+#define RT5640_ZCD_DIG_MASK (0x1 << 11)
+#define RT5640_ZCD_DIG_SFT 11
+#define RT5640_ZCD_DIG_DIS (0x0 << 11)
+#define RT5640_ZCD_DIG_EN (0x1 << 11)
+#define RT5640_ZCD_MASK (0x1 << 10)
+#define RT5640_ZCD_SFT 10
+#define RT5640_ZCD_PD (0x0 << 10)
+#define RT5640_ZCD_PU (0x1 << 10)
+#define RT5640_M_ZCD_MASK (0x3f << 4)
+#define RT5640_M_ZCD_SFT 4
+#define RT5640_M_ZCD_RM_L (0x1 << 9)
+#define RT5640_M_ZCD_RM_R (0x1 << 8)
+#define RT5640_M_ZCD_SM_L (0x1 << 7)
+#define RT5640_M_ZCD_SM_R (0x1 << 6)
+#define RT5640_M_ZCD_OM_L (0x1 << 5)
+#define RT5640_M_ZCD_OM_R (0x1 << 4)
+#define RT5640_SV_DLY_MASK (0xf)
+#define RT5640_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0xda) */
+#define RT5640_ZCD_HP_MASK (0x1 << 15)
+#define RT5640_ZCD_HP_SFT 15
+#define RT5640_ZCD_HP_DIS (0x0 << 15)
+#define RT5640_ZCD_HP_EN (0x1 << 15)
+
+
+/* Codec Private Register definition */
+/* 3D Speaker Control (0x63) */
+#define RT5640_3D_SPK_MASK (0x1 << 15)
+#define RT5640_3D_SPK_SFT 15
+#define RT5640_3D_SPK_DIS (0x0 << 15)
+#define RT5640_3D_SPK_EN (0x1 << 15)
+#define RT5640_3D_SPK_M_MASK (0x3 << 13)
+#define RT5640_3D_SPK_M_SFT 13
+#define RT5640_3D_SPK_CG_MASK (0x1f << 8)
+#define RT5640_3D_SPK_CG_SFT 8
+#define RT5640_3D_SPK_SG_MASK (0x1f)
+#define RT5640_3D_SPK_SG_SFT 0
+
+/* Wind Noise Detection Control 1 (0x6c) */
+#define RT5640_WND_MASK (0x1 << 15)
+#define RT5640_WND_SFT 15
+#define RT5640_WND_DIS (0x0 << 15)
+#define RT5640_WND_EN (0x1 << 15)
+
+/* Wind Noise Detection Control 2 (0x6d) */
+#define RT5640_WND_FC_NW_MASK (0x3f << 10)
+#define RT5640_WND_FC_NW_SFT 10
+#define RT5640_WND_FC_WK_MASK (0x3f << 4)
+#define RT5640_WND_FC_WK_SFT 4
+
+/* Wind Noise Detection Control 3 (0x6e) */
+#define RT5640_HPF_FC_MASK (0x3f << 6)
+#define RT5640_HPF_FC_SFT 6
+#define RT5640_WND_FC_ST_MASK (0x3f)
+#define RT5640_WND_FC_ST_SFT 0
+
+/* Wind Noise Detection Control 4 (0x6f) */
+#define RT5640_WND_TH_LO_MASK (0x3ff)
+#define RT5640_WND_TH_LO_SFT 0
+
+/* Wind Noise Detection Control 5 (0x70) */
+#define RT5640_WND_TH_HI_MASK (0x3ff)
+#define RT5640_WND_TH_HI_SFT 0
+
+/* Wind Noise Detection Control 8 (0x73) */
+#define RT5640_WND_WIND_MASK (0x1 << 13) /* Read-Only */
+#define RT5640_WND_WIND_SFT 13
+#define RT5640_WND_STRONG_MASK (0x1 << 12) /* Read-Only */
+#define RT5640_WND_STRONG_SFT 12
+enum {
+ RT5640_NO_WIND,
+ RT5640_BREEZE,
+ RT5640_STORM,
+};
+
+/* Dipole Speaker Interface (0x75) */
+#define RT5640_DP_ATT_MASK (0x3 << 14)
+#define RT5640_DP_ATT_SFT 14
+#define RT5640_DP_SPK_MASK (0x1 << 10)
+#define RT5640_DP_SPK_SFT 10
+#define RT5640_DP_SPK_DIS (0x0 << 10)
+#define RT5640_DP_SPK_EN (0x1 << 10)
+
+/* EQ Pre Volume Control (0xb3) */
+#define RT5640_EQ_PRE_VOL_MASK (0xffff)
+#define RT5640_EQ_PRE_VOL_SFT 0
+
+/* EQ Post Volume Control (0xb4) */
+#define RT5640_EQ_PST_VOL_MASK (0xffff)
+#define RT5640_EQ_PST_VOL_SFT 0
+
+#define RT5640_NO_JACK BIT(0)
+#define RT5640_HEADSET_DET BIT(1)
+#define RT5640_HEADPHO_DET BIT(2)
+
+/* System Clock Source */
+#define RT5640_SCLK_S_MCLK 0
+#define RT5640_SCLK_S_PLL1 1
+#define RT5640_SCLK_S_PLL1_TK 2
+#define RT5640_SCLK_S_RCCLK 3
+
+/* PLL1 Source */
+#define RT5640_PLL1_S_MCLK 0
+#define RT5640_PLL1_S_BCLK1 1
+#define RT5640_PLL1_S_BCLK2 2
+#define RT5640_PLL1_S_BCLK3 3
+
+
+enum {
+ RT5640_AIF1,
+ RT5640_AIF2,
+ RT5640_AIF3,
+ RT5640_AIFS,
+};
+
+enum {
+ RT5640_U_IF1 = 0x1,
+ RT5640_U_IF2 = 0x2,
+ RT5640_U_IF3 = 0x4,
+};
+
+enum {
+ RT5640_IF_123,
+ RT5640_IF_132,
+ RT5640_IF_312,
+ RT5640_IF_321,
+ RT5640_IF_231,
+ RT5640_IF_213,
+ RT5640_IF_113,
+ RT5640_IF_223,
+ RT5640_IF_ALL,
+};
+
+enum {
+ RT5640_DMIC_DIS,
+ RT5640_DMIC1,
+ RT5640_DMIC2,
+};
+
+struct rt5640_priv {
+ struct snd_soc_codec *codec;
+ struct rt5640_platform_data pdata;
+ struct regmap *regmap;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5640_AIFS];
+ int bclk[RT5640_AIFS];
+ int master[RT5640_AIFS];
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ bool hp_mute;
+};
+
+int rt5640_dmic_enable(struct snd_soc_codec *codec,
+ bool dmic1_data_pin, bool dmic2_data_pin);
+
+#endif
diff --git a/kernel/sound/soc/codecs/rt5645.c b/kernel/sound/soc/codecs/rt5645.c
new file mode 100644
index 000000000..be4d741c4
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5645.c
@@ -0,0 +1,2895 @@
+/*
+ * 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>
+#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/gpio.h>
+#include <linux/acpi.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 "rl6231.h"
+#include "rt5645.h"
+
+#define RT5645_DEVICE_ID 0x6308
+#define RT5650_DEVICE_ID 0x6419
+
+#define RT5645_PR_RANGE_BASE (0xff + 1)
+#define RT5645_PR_SPACING 0x100
+
+#define RT5645_PR_BASE (RT5645_PR_RANGE_BASE + (0 * RT5645_PR_SPACING))
+
+static const struct regmap_range_cfg rt5645_ranges[] = {
+ {
+ .name = "PR",
+ .range_min = RT5645_PR_BASE,
+ .range_max = RT5645_PR_BASE + 0xf8,
+ .selector_reg = RT5645_PRIV_INDEX,
+ .selector_mask = 0xff,
+ .selector_shift = 0x0,
+ .window_start = RT5645_PRIV_DATA,
+ .window_len = 0x1,
+ },
+};
+
+static const struct reg_default init_list[] = {
+ {RT5645_PR_BASE + 0x3d, 0x3600},
+ {RT5645_PR_BASE + 0x1c, 0xfd20},
+ {RT5645_PR_BASE + 0x20, 0x611f},
+ {RT5645_PR_BASE + 0x21, 0x4040},
+ {RT5645_PR_BASE + 0x23, 0x0004},
+};
+#define RT5645_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+static const struct reg_default rt5650_init_list[] = {
+ {0xf6, 0x0100},
+};
+
+static const struct reg_default rt5645_reg[] = {
+ { 0x00, 0x0000 },
+ { 0x01, 0xc8c8 },
+ { 0x02, 0xc8c8 },
+ { 0x03, 0xc8c8 },
+ { 0x0a, 0x0002 },
+ { 0x0b, 0x2827 },
+ { 0x0c, 0xe000 },
+ { 0x0d, 0x0000 },
+ { 0x0e, 0x0000 },
+ { 0x0f, 0x0808 },
+ { 0x14, 0x3333 },
+ { 0x16, 0x4b00 },
+ { 0x18, 0x018b },
+ { 0x19, 0xafaf },
+ { 0x1a, 0xafaf },
+ { 0x1b, 0x0001 },
+ { 0x1c, 0x2f2f },
+ { 0x1d, 0x2f2f },
+ { 0x1e, 0x0000 },
+ { 0x20, 0x0000 },
+ { 0x27, 0x7060 },
+ { 0x28, 0x7070 },
+ { 0x29, 0x8080 },
+ { 0x2a, 0x5656 },
+ { 0x2b, 0x5454 },
+ { 0x2c, 0xaaa0 },
+ { 0x2d, 0x0000 },
+ { 0x2f, 0x1002 },
+ { 0x31, 0x5000 },
+ { 0x32, 0x0000 },
+ { 0x33, 0x0000 },
+ { 0x34, 0x0000 },
+ { 0x35, 0x0000 },
+ { 0x3b, 0x0000 },
+ { 0x3c, 0x007f },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x007f },
+ { 0x3f, 0x0000 },
+ { 0x40, 0x001f },
+ { 0x41, 0x0000 },
+ { 0x42, 0x001f },
+ { 0x45, 0x6000 },
+ { 0x46, 0x003e },
+ { 0x47, 0x003e },
+ { 0x48, 0xf807 },
+ { 0x4a, 0x0004 },
+ { 0x4d, 0x0000 },
+ { 0x4e, 0x0000 },
+ { 0x4f, 0x01ff },
+ { 0x50, 0x0000 },
+ { 0x51, 0x0000 },
+ { 0x52, 0x01ff },
+ { 0x53, 0xf000 },
+ { 0x56, 0x0111 },
+ { 0x57, 0x0064 },
+ { 0x58, 0xef0e },
+ { 0x59, 0xf0f0 },
+ { 0x5a, 0xef0e },
+ { 0x5b, 0xf0f0 },
+ { 0x5c, 0xef0e },
+ { 0x5d, 0xf0f0 },
+ { 0x5e, 0xf000 },
+ { 0x5f, 0x0000 },
+ { 0x61, 0x0300 },
+ { 0x62, 0x0000 },
+ { 0x63, 0x00c2 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x0000 },
+ { 0x66, 0x0000 },
+ { 0x6a, 0x0000 },
+ { 0x6c, 0x0aaa },
+ { 0x70, 0x8000 },
+ { 0x71, 0x8000 },
+ { 0x72, 0x8000 },
+ { 0x73, 0x7770 },
+ { 0x74, 0x3e00 },
+ { 0x75, 0x2409 },
+ { 0x76, 0x000a },
+ { 0x77, 0x0c00 },
+ { 0x78, 0x0000 },
+ { 0x79, 0x0123 },
+ { 0x80, 0x0000 },
+ { 0x81, 0x0000 },
+ { 0x82, 0x0000 },
+ { 0x83, 0x0000 },
+ { 0x84, 0x0000 },
+ { 0x85, 0x0000 },
+ { 0x8a, 0x0000 },
+ { 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 int rt5645_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, RT5645_RESET, 0);
+}
+
+static bool rt5645_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5645_ranges); i++) {
+ if (reg >= rt5645_ranges[i].range_min &&
+ reg <= rt5645_ranges[i].range_max) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT5645_RESET:
+ case RT5645_PRIV_DATA:
+ case RT5645_IN1_CTRL1:
+ case RT5645_IN1_CTRL2:
+ case RT5645_IN1_CTRL3:
+ case RT5645_A_JD_CTRL1:
+ case RT5645_ADC_EQ_CTRL1:
+ case RT5645_EQ_CTRL1:
+ case RT5645_ALC_CTRL_1:
+ case RT5645_IRQ_CTRL2:
+ case RT5645_IRQ_CTRL3:
+ case RT5645_INT_IRQ_ST:
+ case RT5645_IL_CMD:
+ case RT5650_4BTN_IL_CMD1:
+ case RT5645_VENDOR_ID:
+ case RT5645_VENDOR_ID1:
+ case RT5645_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5645_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5645_ranges); i++) {
+ if (reg >= rt5645_ranges[i].range_min &&
+ reg <= rt5645_ranges[i].range_max) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT5645_RESET:
+ case RT5645_SPK_VOL:
+ case RT5645_HP_VOL:
+ case RT5645_LOUT1:
+ case RT5645_IN1_CTRL1:
+ case RT5645_IN1_CTRL2:
+ case RT5645_IN1_CTRL3:
+ case RT5645_IN2_CTRL:
+ case RT5645_INL1_INR1_VOL:
+ case RT5645_SPK_FUNC_LIM:
+ case RT5645_ADJ_HPF_CTRL:
+ case RT5645_DAC1_DIG_VOL:
+ case RT5645_DAC2_DIG_VOL:
+ case RT5645_DAC_CTRL:
+ case RT5645_STO1_ADC_DIG_VOL:
+ case RT5645_MONO_ADC_DIG_VOL:
+ case RT5645_ADC_BST_VOL1:
+ case RT5645_ADC_BST_VOL2:
+ case RT5645_STO1_ADC_MIXER:
+ case RT5645_MONO_ADC_MIXER:
+ case RT5645_AD_DA_MIXER:
+ case RT5645_STO_DAC_MIXER:
+ case RT5645_MONO_DAC_MIXER:
+ case RT5645_DIG_MIXER:
+ case RT5650_A_DAC_SOUR:
+ case RT5645_DIG_INF1_DATA:
+ case RT5645_PDM_OUT_CTRL:
+ case RT5645_REC_L1_MIXER:
+ case RT5645_REC_L2_MIXER:
+ case RT5645_REC_R1_MIXER:
+ case RT5645_REC_R2_MIXER:
+ case RT5645_HPMIXL_CTRL:
+ case RT5645_HPOMIXL_CTRL:
+ case RT5645_HPMIXR_CTRL:
+ case RT5645_HPOMIXR_CTRL:
+ case RT5645_HPO_MIXER:
+ case RT5645_SPK_L_MIXER:
+ case RT5645_SPK_R_MIXER:
+ case RT5645_SPO_MIXER:
+ case RT5645_SPO_CLSD_RATIO:
+ case RT5645_OUT_L1_MIXER:
+ case RT5645_OUT_R1_MIXER:
+ case RT5645_OUT_L_GAIN1:
+ case RT5645_OUT_L_GAIN2:
+ case RT5645_OUT_R_GAIN1:
+ case RT5645_OUT_R_GAIN2:
+ case RT5645_LOUT_MIXER:
+ case RT5645_HAPTIC_CTRL1:
+ case RT5645_HAPTIC_CTRL2:
+ case RT5645_HAPTIC_CTRL3:
+ case RT5645_HAPTIC_CTRL4:
+ case RT5645_HAPTIC_CTRL5:
+ case RT5645_HAPTIC_CTRL6:
+ case RT5645_HAPTIC_CTRL7:
+ case RT5645_HAPTIC_CTRL8:
+ case RT5645_HAPTIC_CTRL9:
+ case RT5645_HAPTIC_CTRL10:
+ case RT5645_PWR_DIG1:
+ case RT5645_PWR_DIG2:
+ case RT5645_PWR_ANLG1:
+ case RT5645_PWR_ANLG2:
+ case RT5645_PWR_MIXER:
+ case RT5645_PWR_VOL:
+ case RT5645_PRIV_INDEX:
+ case RT5645_PRIV_DATA:
+ case RT5645_I2S1_SDP:
+ case RT5645_I2S2_SDP:
+ case RT5645_ADDA_CLK1:
+ case RT5645_ADDA_CLK2:
+ case RT5645_DMIC_CTRL1:
+ case RT5645_DMIC_CTRL2:
+ case RT5645_TDM_CTRL_1:
+ case RT5645_TDM_CTRL_2:
+ case RT5645_TDM_CTRL_3:
+ case RT5645_GLB_CLK:
+ case RT5645_PLL_CTRL1:
+ case RT5645_PLL_CTRL2:
+ case RT5645_ASRC_1:
+ case RT5645_ASRC_2:
+ case RT5645_ASRC_3:
+ case RT5645_ASRC_4:
+ case RT5645_DEPOP_M1:
+ case RT5645_DEPOP_M2:
+ case RT5645_DEPOP_M3:
+ case RT5645_MICBIAS:
+ case RT5645_A_JD_CTRL1:
+ case RT5645_VAD_CTRL4:
+ case RT5645_CLSD_OUT_CTRL:
+ case RT5645_ADC_EQ_CTRL1:
+ case RT5645_ADC_EQ_CTRL2:
+ case RT5645_EQ_CTRL1:
+ case RT5645_EQ_CTRL2:
+ case RT5645_ALC_CTRL_1:
+ case RT5645_ALC_CTRL_2:
+ case RT5645_ALC_CTRL_3:
+ case RT5645_ALC_CTRL_4:
+ case RT5645_ALC_CTRL_5:
+ case RT5645_JD_CTRL:
+ case RT5645_IRQ_CTRL1:
+ case RT5645_IRQ_CTRL2:
+ case RT5645_IRQ_CTRL3:
+ case RT5645_INT_IRQ_ST:
+ case RT5645_GPIO_CTRL1:
+ case RT5645_GPIO_CTRL2:
+ case RT5645_GPIO_CTRL3:
+ case RT5645_BASS_BACK:
+ case RT5645_MP3_PLUS1:
+ case RT5645_MP3_PLUS2:
+ case RT5645_ADJ_HPF1:
+ case RT5645_ADJ_HPF2:
+ case RT5645_HP_CALIB_AMP_DET:
+ case RT5645_SV_ZCD1:
+ case RT5645_SV_ZCD2:
+ case RT5645_IL_CMD:
+ case RT5645_IL_CMD2:
+ case RT5645_IL_CMD3:
+ case RT5650_4BTN_IL_CMD1:
+ case RT5650_4BTN_IL_CMD2:
+ case RT5645_DRC1_HL_CTRL1:
+ case RT5645_DRC2_HL_CTRL1:
+ case RT5645_ADC_MONO_HP_CTRL1:
+ case RT5645_ADC_MONO_HP_CTRL2:
+ case RT5645_DRC2_CTRL1:
+ case RT5645_DRC2_CTRL2:
+ case RT5645_DRC2_CTRL3:
+ case RT5645_DRC2_CTRL4:
+ case RT5645_DRC2_CTRL5:
+ case RT5645_JD_CTRL3:
+ case RT5645_JD_CTRL4:
+ case RT5645_GEN_CTRL1:
+ case RT5645_GEN_CTRL2:
+ case RT5645_GEN_CTRL3:
+ case RT5645_VENDOR_ID:
+ case RT5645_VENDOR_ID1:
+ case RT5645_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -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 unsigned int bst_tlv[] = {
+ TLV_DB_RANGE_HEAD(7),
+ 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 char * const rt5645_tdm_data_swap_select[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot0_1_enum,
+ RT5645_TDM_CTRL_1, 6, rt5645_tdm_data_swap_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot2_3_enum,
+ RT5645_TDM_CTRL_1, 4, rt5645_tdm_data_swap_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot4_5_enum,
+ RT5645_TDM_CTRL_1, 2, rt5645_tdm_data_swap_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot6_7_enum,
+ RT5645_TDM_CTRL_1, 0, rt5645_tdm_data_swap_select);
+
+static const char * const rt5645_tdm_adc_data_select[] = {
+ "1/2/R", "2/1/R", "R/1/2", "R/2/1"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_sel_enum,
+ RT5645_TDM_CTRL_1, 8,
+ rt5645_tdm_adc_data_select);
+
+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),
+
+ /* Headphone Output Volume */
+ SOC_DOUBLE("HP Channel Switch", RT5645_HP_VOL,
+ RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("HP Playback Volume", RT5645_HP_VOL,
+ RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
+
+ /* OUTPUT Control */
+ SOC_DOUBLE("OUT Playback Switch", RT5645_LOUT1,
+ RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("OUT Channel Switch", RT5645_LOUT1,
+ RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("OUT Playback Volume", RT5645_LOUT1,
+ RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
+
+ /* DAC Digital Volume */
+ SOC_DOUBLE("DAC2 Playback Switch", RT5645_DAC_CTRL,
+ RT5645_M_DAC_L2_VOL_SFT, RT5645_M_DAC_R2_VOL_SFT, 1, 1),
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5645_DAC1_DIG_VOL,
+ RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 175, 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),
+
+ /* IN1/IN2 Control */
+ SOC_SINGLE_TLV("IN1 Boost", RT5645_IN1_CTRL1,
+ RT5645_BST_SFT1, 8, 0, bst_tlv),
+ SOC_SINGLE_TLV("IN2 Boost", RT5645_IN2_CTRL,
+ RT5645_BST_SFT2, 8, 0, bst_tlv),
+
+ /* INL/INR Volume Control */
+ SOC_DOUBLE_TLV("IN Capture Volume", RT5645_INL1_INR1_VOL,
+ RT5645_INL_VOL_SFT, RT5645_INR_VOL_SFT, 31, 1, in_vol_tlv),
+
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("ADC Capture Switch", RT5645_STO1_ADC_DIG_VOL,
+ RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("ADC Capture Volume", RT5645_STO1_ADC_DIG_VOL,
+ RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 127, 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),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("STO1 ADC Boost Gain", 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,
+ adc_bst_tlv),
+
+ /* I2S2 function select */
+ SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT,
+ 1, 1),
+
+ /* 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),
+};
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_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);
+
+ if (idx < 0)
+ dev_err(codec->dev, "Failed to set DMIC clock\n");
+ else
+ snd_soc_update_bits(codec, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_CLK_MASK, idx << RT5645_DMIC_CLK_SFT);
+ return idx;
+}
+
+static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ unsigned int val;
+
+ val = snd_soc_read(codec, RT5645_GLB_CLK);
+ val &= RT5645_SCLK_SRC_MASK;
+ if (val == RT5645_SCLK_SRC_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int is_using_asrc(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ unsigned int reg, shift, val;
+
+ switch (source->shift) {
+ case 0:
+ reg = RT5645_ASRC_3;
+ shift = 0;
+ break;
+ case 1:
+ reg = RT5645_ASRC_3;
+ shift = 4;
+ break;
+ case 3:
+ reg = RT5645_ASRC_2;
+ shift = 0;
+ break;
+ case 8:
+ reg = RT5645_ASRC_2;
+ shift = 4;
+ break;
+ case 9:
+ reg = RT5645_ASRC_2;
+ shift = 8;
+ break;
+ case 10:
+ reg = RT5645_ASRC_2;
+ shift = 12;
+ break;
+ default:
+ return 0;
+ }
+
+ val = (snd_soc_read(codec, reg) >> shift) & 0xf;
+ switch (val) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ return 1;
+ default:
+ return 0;
+ }
+
+}
+
+/**
+ * rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters
+ * @codec: SoC audio codec device.
+ * @filter_mask: mask of filters.
+ * @clk_src: clock source
+ *
+ * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5645 can
+ * only support standard 32fs or 64fs i2s format, ASRC should be enabled to
+ * support special i2s clock format such as Intel's 100fs(100 * sampling rate).
+ * ASRC function will track i2s clock and generate a corresponding system clock
+ * for codec. This function provides an API to select the clock source for a
+ * set of filters specified by the mask. And the codec driver will turn on ASRC
+ * for these filters if ASRC is selected as their clock source.
+ */
+int rt5645_sel_asrc_clk_src(struct snd_soc_codec *codec,
+ unsigned int filter_mask, unsigned int clk_src)
+{
+ unsigned int asrc2_mask = 0;
+ unsigned int asrc2_value = 0;
+ unsigned int asrc3_mask = 0;
+ unsigned int asrc3_value = 0;
+
+ switch (clk_src) {
+ case RT5645_CLK_SEL_SYS:
+ case RT5645_CLK_SEL_I2S1_ASRC:
+ case RT5645_CLK_SEL_I2S2_ASRC:
+ case RT5645_CLK_SEL_SYS2:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (filter_mask & RT5645_DA_STEREO_FILTER) {
+ asrc2_mask |= RT5645_DA_STO_CLK_SEL_MASK;
+ asrc2_value = (asrc2_value & ~RT5645_DA_STO_CLK_SEL_MASK)
+ | (clk_src << RT5645_DA_STO_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5645_DA_MONO_L_FILTER) {
+ asrc2_mask |= RT5645_DA_MONOL_CLK_SEL_MASK;
+ asrc2_value = (asrc2_value & ~RT5645_DA_MONOL_CLK_SEL_MASK)
+ | (clk_src << RT5645_DA_MONOL_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5645_DA_MONO_R_FILTER) {
+ asrc2_mask |= RT5645_DA_MONOR_CLK_SEL_MASK;
+ asrc2_value = (asrc2_value & ~RT5645_DA_MONOR_CLK_SEL_MASK)
+ | (clk_src << RT5645_DA_MONOR_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5645_AD_STEREO_FILTER) {
+ asrc2_mask |= RT5645_AD_STO1_CLK_SEL_MASK;
+ asrc2_value = (asrc2_value & ~RT5645_AD_STO1_CLK_SEL_MASK)
+ | (clk_src << RT5645_AD_STO1_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5645_AD_MONO_L_FILTER) {
+ asrc3_mask |= RT5645_AD_MONOL_CLK_SEL_MASK;
+ asrc3_value = (asrc3_value & ~RT5645_AD_MONOL_CLK_SEL_MASK)
+ | (clk_src << RT5645_AD_MONOL_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5645_AD_MONO_R_FILTER) {
+ asrc3_mask |= RT5645_AD_MONOR_CLK_SEL_MASK;
+ asrc3_value = (asrc3_value & ~RT5645_AD_MONOR_CLK_SEL_MASK)
+ | (clk_src << RT5645_AD_MONOR_CLK_SEL_SFT);
+ }
+
+ if (asrc2_mask)
+ snd_soc_update_bits(codec, RT5645_ASRC_2,
+ asrc2_mask, asrc2_value);
+
+ if (asrc3_mask)
+ snd_soc_update_bits(codec, RT5645_ASRC_3,
+ asrc3_mask, asrc3_value);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5645_sel_asrc_clk_src);
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5645_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER,
+ RT5645_M_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5645_STO1_ADC_MIXER,
+ RT5645_M_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER,
+ RT5645_M_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5645_STO1_ADC_MIXER,
+ RT5645_M_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_mono_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5645_MONO_ADC_MIXER,
+ RT5645_M_MONO_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5645_MONO_ADC_MIXER,
+ RT5645_M_MONO_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_mono_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5645_MONO_ADC_MIXER,
+ RT5645_M_MONO_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5645_MONO_ADC_MIXER,
+ RT5645_M_MONO_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER,
+ RT5645_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5645_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_sto_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_STO_DAC_MIXER,
+ RT5645_M_DAC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_STO_DAC_MIXER,
+ RT5645_M_DAC_L2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_STO_DAC_MIXER,
+ RT5645_M_DAC_R1_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_sto_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_STO_DAC_MIXER,
+ RT5645_M_DAC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_STO_DAC_MIXER,
+ RT5645_M_DAC_R2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_STO_DAC_MIXER,
+ RT5645_M_DAC_L1_STO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_mono_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_MONO_DAC_MIXER,
+ RT5645_M_DAC_L1_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_MONO_DAC_MIXER,
+ RT5645_M_DAC_L2_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_MONO_DAC_MIXER,
+ RT5645_M_DAC_R2_MONO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_mono_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_MONO_DAC_MIXER,
+ RT5645_M_DAC_R1_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_MONO_DAC_MIXER,
+ RT5645_M_DAC_R2_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_MONO_DAC_MIXER,
+ RT5645_M_DAC_L2_MONO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_dig_l_mix[] = {
+ SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5645_DIG_MIXER,
+ RT5645_M_STO_L_DAC_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_DIG_MIXER,
+ RT5645_M_DAC_L2_DAC_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_DIG_MIXER,
+ RT5645_M_DAC_R2_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_dig_r_mix[] = {
+ SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5645_DIG_MIXER,
+ RT5645_M_STO_R_DAC_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_DIG_MIXER,
+ RT5645_M_DAC_R2_DAC_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_DIG_MIXER,
+ RT5645_M_DAC_L2_DAC_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5645_rec_l_mix[] = {
+ SOC_DAPM_SINGLE("HPOL Switch", RT5645_REC_L2_MIXER,
+ RT5645_M_HP_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5645_REC_L2_MIXER,
+ RT5645_M_IN_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5645_REC_L2_MIXER,
+ RT5645_M_BST2_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5645_REC_L2_MIXER,
+ RT5645_M_BST1_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXL Switch", RT5645_REC_L2_MIXER,
+ RT5645_M_OM_L_RM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_rec_r_mix[] = {
+ SOC_DAPM_SINGLE("HPOR Switch", RT5645_REC_R2_MIXER,
+ RT5645_M_HP_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5645_REC_R2_MIXER,
+ RT5645_M_IN_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5645_REC_R2_MIXER,
+ RT5645_M_BST2_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5645_REC_R2_MIXER,
+ RT5645_M_BST1_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXR Switch", RT5645_REC_R2_MIXER,
+ RT5645_M_OM_R_RM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_spk_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_SPK_L_MIXER,
+ RT5645_M_DAC_L1_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_SPK_L_MIXER,
+ RT5645_M_DAC_L2_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5645_SPK_L_MIXER,
+ RT5645_M_IN_L_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5645_SPK_L_MIXER,
+ RT5645_M_BST1_L_SM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_spk_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPK_R_MIXER,
+ RT5645_M_DAC_R1_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_SPK_R_MIXER,
+ RT5645_M_DAC_R2_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5645_SPK_R_MIXER,
+ RT5645_M_IN_R_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5645_SPK_R_MIXER,
+ RT5645_M_BST2_R_SM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_out_l_mix[] = {
+ SOC_DAPM_SINGLE("BST1 Switch", RT5645_OUT_L1_MIXER,
+ RT5645_M_BST1_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5645_OUT_L1_MIXER,
+ RT5645_M_IN_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_OUT_L1_MIXER,
+ RT5645_M_DAC_L2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_OUT_L1_MIXER,
+ RT5645_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_out_r_mix[] = {
+ SOC_DAPM_SINGLE("BST2 Switch", RT5645_OUT_R1_MIXER,
+ RT5645_M_BST2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5645_OUT_R1_MIXER,
+ RT5645_M_IN_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_OUT_R1_MIXER,
+ RT5645_M_DAC_R2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_OUT_R1_MIXER,
+ RT5645_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_spo_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPO_MIXER,
+ RT5645_M_DAC_R1_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_SPO_MIXER,
+ RT5645_M_DAC_L1_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL R Switch", RT5645_SPO_MIXER,
+ RT5645_M_SV_R_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL L Switch", RT5645_SPO_MIXER,
+ RT5645_M_SV_L_SPM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_spo_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPO_MIXER,
+ RT5645_M_DAC_R1_SPM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL R Switch", RT5645_SPO_MIXER,
+ RT5645_M_SV_R_SPM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_hpo_mix[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPO_MIXER,
+ RT5645_M_DAC1_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPVOL Switch", RT5645_HPO_MIXER,
+ RT5645_M_HPVOL_HM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_hpvoll_mix[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPOMIXL_CTRL,
+ RT5645_M_DAC1_HV_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC2 Switch", RT5645_HPOMIXL_CTRL,
+ RT5645_M_DAC2_HV_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5645_HPOMIXL_CTRL,
+ RT5645_M_IN_HV_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5645_HPOMIXL_CTRL,
+ RT5645_M_BST1_HV_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_hpvolr_mix[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPOMIXR_CTRL,
+ RT5645_M_DAC1_HV_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC2 Switch", RT5645_HPOMIXR_CTRL,
+ RT5645_M_DAC2_HV_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5645_HPOMIXR_CTRL,
+ RT5645_M_IN_HV_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5645_HPOMIXR_CTRL,
+ RT5645_M_BST2_HV_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_lout_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_LOUT_MIXER,
+ RT5645_M_DAC_L1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_LOUT_MIXER,
+ RT5645_M_DAC_R1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTMIX L Switch", RT5645_LOUT_MIXER,
+ RT5645_M_OV_L_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTMIX R Switch", RT5645_LOUT_MIXER,
+ RT5645_M_OV_R_LM_SFT, 1, 1),
+};
+
+/*DAC1 L/R source*/ /* MX-29 [9:8] [11:10] */
+static const char * const rt5645_dac1_src[] = {
+ "IF1 DAC", "IF2 DAC", "IF3 DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_dac1l_enum, RT5645_AD_DA_MIXER,
+ RT5645_DAC1_L_SEL_SFT, rt5645_dac1_src);
+
+static const struct snd_kcontrol_new rt5645_dac1l_mux =
+ SOC_DAPM_ENUM("DAC1 L source", rt5645_dac1l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_dac1r_enum, RT5645_AD_DA_MIXER,
+ RT5645_DAC1_R_SEL_SFT, rt5645_dac1_src);
+
+static const struct snd_kcontrol_new rt5645_dac1r_mux =
+ SOC_DAPM_ENUM("DAC1 R source", rt5645_dac1r_enum);
+
+/*DAC2 L/R source*/ /* MX-1B [6:4] [2:0] */
+static const char * const rt5645_dac12_src[] = {
+ "IF1 DAC", "IF2 DAC", "IF3 DAC", "Mono ADC", "VAD_ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_dac2l_enum, RT5645_DAC_CTRL,
+ RT5645_DAC2_L_SEL_SFT, rt5645_dac12_src);
+
+static const struct snd_kcontrol_new rt5645_dac_l2_mux =
+ SOC_DAPM_ENUM("DAC2 L source", rt5645_dac2l_enum);
+
+static const char * const rt5645_dacr2_src[] = {
+ "IF1 DAC", "IF2 DAC", "IF3 DAC", "Mono ADC", "Haptic"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_dac2r_enum, RT5645_DAC_CTRL,
+ RT5645_DAC2_R_SEL_SFT, rt5645_dacr2_src);
+
+static const struct snd_kcontrol_new rt5645_dac_r2_mux =
+ SOC_DAPM_ENUM("DAC2 R source", rt5645_dac2r_enum);
+
+
+/* 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[] = {
+ "DAC MIX", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_stereo1_adc1_enum, RT5645_STO1_ADC_MIXER,
+ RT5645_ADC_1_SRC_SFT, rt5645_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5645_sto_adc1_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1 Mux", rt5645_stereo1_adc1_enum);
+
+/* MX-27 [11] */
+static const char * const rt5645_stereo_adc2_src[] = {
+ "DAC MIX", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_stereo1_adc2_enum, RT5645_STO1_ADC_MIXER,
+ RT5645_ADC_2_SRC_SFT, rt5645_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5645_sto_adc2_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2 Mux", rt5645_stereo1_adc2_enum);
+
+/* MX-27 [8] */
+static const char * const rt5645_stereo_dmic_src[] = {
+ "DMIC1", "DMIC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_stereo1_dmic_enum, RT5645_STO1_ADC_MIXER,
+ RT5645_DMIC_SRC_SFT, rt5645_stereo_dmic_src);
+
+static const struct snd_kcontrol_new rt5645_sto1_dmic_mux =
+ SOC_DAPM_ENUM("Stereo1 DMIC source", rt5645_stereo1_dmic_enum);
+
+/* Mono ADC source */
+/* MX-28 [12] */
+static const char * const rt5645_mono_adc_l1_src[] = {
+ "Mono DAC MIXL", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_mono_adc_l1_enum, RT5645_MONO_ADC_MIXER,
+ RT5645_MONO_ADC_L1_SRC_SFT, rt5645_mono_adc_l1_src);
+
+static const struct snd_kcontrol_new rt5645_mono_adc_l1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 left source", rt5645_mono_adc_l1_enum);
+/* MX-28 [11] */
+static const char * const rt5645_mono_adc_l2_src[] = {
+ "Mono DAC MIXL", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_mono_adc_l2_enum, RT5645_MONO_ADC_MIXER,
+ RT5645_MONO_ADC_L2_SRC_SFT, rt5645_mono_adc_l2_src);
+
+static const struct snd_kcontrol_new rt5645_mono_adc_l2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 left source", rt5645_mono_adc_l2_enum);
+
+/* MX-28 [8] */
+static const char * const rt5645_mono_dmic_src[] = {
+ "DMIC1", "DMIC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_mono_dmic_l_enum, RT5645_MONO_ADC_MIXER,
+ RT5645_MONO_DMIC_L_SRC_SFT, rt5645_mono_dmic_src);
+
+static const struct snd_kcontrol_new rt5645_mono_dmic_l_mux =
+ SOC_DAPM_ENUM("Mono DMIC left source", rt5645_mono_dmic_l_enum);
+/* MX-28 [1:0] */
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_mono_dmic_r_enum, RT5645_MONO_ADC_MIXER,
+ RT5645_MONO_DMIC_R_SRC_SFT, rt5645_mono_dmic_src);
+
+static const struct snd_kcontrol_new rt5645_mono_dmic_r_mux =
+ SOC_DAPM_ENUM("Mono DMIC Right source", rt5645_mono_dmic_r_enum);
+/* MX-28 [4] */
+static const char * const rt5645_mono_adc_r1_src[] = {
+ "Mono DAC MIXR", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_mono_adc_r1_enum, RT5645_MONO_ADC_MIXER,
+ RT5645_MONO_ADC_R1_SRC_SFT, rt5645_mono_adc_r1_src);
+
+static const struct snd_kcontrol_new rt5645_mono_adc_r1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 right source", rt5645_mono_adc_r1_enum);
+/* MX-28 [3] */
+static const char * const rt5645_mono_adc_r2_src[] = {
+ "Mono DAC MIXR", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_mono_adc_r2_enum, RT5645_MONO_ADC_MIXER,
+ RT5645_MONO_ADC_R2_SRC_SFT, rt5645_mono_adc_r2_src);
+
+static const struct snd_kcontrol_new rt5645_mono_adc_r2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 right source", rt5645_mono_adc_r2_enum);
+
+/* MX-77 [9:8] */
+static const char * const rt5645_if1_adc_in_src[] = {
+ "IF_ADC1", "IF_ADC2", "VAD_ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_if1_adc_in_enum, RT5645_TDM_CTRL_1,
+ RT5645_IF1_ADC_IN_SFT, rt5645_if1_adc_in_src);
+
+static const struct snd_kcontrol_new rt5645_if1_adc_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC IN source", rt5645_if1_adc_in_enum);
+
+/* MX-2d [3] [2] */
+static const char * const rt5650_a_dac1_src[] = {
+ "DAC1", "Stereo DAC Mixer"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5650_a_dac1_l_enum, RT5650_A_DAC_SOUR,
+ RT5650_A_DAC1_L_IN_SFT, rt5650_a_dac1_src);
+
+static const struct snd_kcontrol_new rt5650_a_dac1_l_mux =
+ SOC_DAPM_ENUM("A DAC1 L source", rt5650_a_dac1_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5650_a_dac1_r_enum, RT5650_A_DAC_SOUR,
+ RT5650_A_DAC1_R_IN_SFT, rt5650_a_dac1_src);
+
+static const struct snd_kcontrol_new rt5650_a_dac1_r_mux =
+ SOC_DAPM_ENUM("A DAC1 R source", rt5650_a_dac1_r_enum);
+
+/* MX-2d [1] [0] */
+static const char * const rt5650_a_dac2_src[] = {
+ "Stereo DAC Mixer", "Mono DAC Mixer"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5650_a_dac2_l_enum, RT5650_A_DAC_SOUR,
+ RT5650_A_DAC2_L_IN_SFT, rt5650_a_dac2_src);
+
+static const struct snd_kcontrol_new rt5650_a_dac2_l_mux =
+ SOC_DAPM_ENUM("A DAC2 L source", rt5650_a_dac2_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5650_a_dac2_r_enum, RT5650_A_DAC_SOUR,
+ RT5650_A_DAC2_R_IN_SFT, rt5650_a_dac2_src);
+
+static const struct snd_kcontrol_new rt5650_a_dac2_r_mux =
+ SOC_DAPM_ENUM("A DAC2 R source", rt5650_a_dac2_r_enum);
+
+/* MX-2F [13:12] */
+static const char * const rt5645_if2_adc_in_src[] = {
+ "IF_ADC1", "IF_ADC2", "VAD_ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_if2_adc_in_enum, RT5645_DIG_INF1_DATA,
+ RT5645_IF2_ADC_IN_SFT, rt5645_if2_adc_in_src);
+
+static const struct snd_kcontrol_new rt5645_if2_adc_in_mux =
+ SOC_DAPM_ENUM("IF2 ADC IN source", rt5645_if2_adc_in_enum);
+
+/* MX-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"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_pdm1_l_enum, RT5645_PDM_OUT_CTRL,
+ RT5645_PDM1_L_SFT, rt5645_pdm_src);
+
+static const struct snd_kcontrol_new rt5645_pdm1_l_mux =
+ SOC_DAPM_ENUM("PDM1 L source", rt5645_pdm1_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_pdm1_r_enum, RT5645_PDM_OUT_CTRL,
+ RT5645_PDM1_R_SFT, rt5645_pdm_src);
+
+static const struct snd_kcontrol_new rt5645_pdm1_r_mux =
+ SOC_DAPM_ENUM("PDM1 R source", rt5645_pdm1_r_enum);
+
+/* MX-9D [9:8] */
+static const char * const rt5645_vad_adc_src[] = {
+ "Sto1 ADC L", "Mono ADC L", "Mono ADC R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_vad_adc_enum, RT5645_VAD_CTRL4,
+ RT5645_VAD_SEL_SFT, rt5645_vad_adc_src);
+
+static const struct snd_kcontrol_new rt5645_vad_adc_mux =
+ SOC_DAPM_ENUM("VAD ADC source", rt5645_vad_adc_enum);
+
+static const struct snd_kcontrol_new spk_l_vol_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_SPK_VOL,
+ RT5645_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new spk_r_vol_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_SPK_VOL,
+ RT5645_R_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hp_l_vol_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_HP_VOL,
+ RT5645_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hp_r_vol_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_HP_VOL,
+ RT5645_R_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new pdm1_l_vol_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_PDM_OUT_CTRL,
+ RT5645_M_PDM1_L, 1, 1);
+
+static const struct snd_kcontrol_new pdm1_r_vol_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_PDM_OUT_CTRL,
+ RT5645_M_PDM1_R, 1, 1);
+
+static void hp_amp_power(struct snd_soc_codec *codec, int on)
+{
+ static int hp_amp_power_count;
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
+ 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);
+ }
+ 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);
+ snd_soc_update_bits(codec, RT5645_DEPOP_M2,
+ RT5645_DEPOP_MASK, 0);
+ }
+ }
+}
+
+static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ hp_amp_power(codec, 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,
+ 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_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,
+ 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_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);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, 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);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, RT5645_PWR_DIG1,
+ RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
+ RT5645_PWR_CLS_D_L, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5645_lout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ hp_amp_power(codec, 1);
+ snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ RT5645_PWR_LM, RT5645_PWR_LM);
+ snd_soc_update_bits(codec, RT5645_LOUT1,
+ RT5645_L_MUTE | RT5645_R_MUTE, 0);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, RT5645_LOUT1,
+ RT5645_L_MUTE | RT5645_R_MUTE,
+ RT5645_L_MUTE | RT5645_R_MUTE);
+ snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ RT5645_PWR_LM, 0);
+ hp_amp_power(codec, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5645_bst2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, 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,
+ RT5645_PWR_BST2_P, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5645_PWR_MIXER,
+ RT5645_PWR_LDO2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5645_PWR_ANLG2,
+ RT5645_PWR_PLL_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("JD Power", RT5645_PWR_ANLG2,
+ RT5645_PWR_JD1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5645_PWR_VOL,
+ RT5645_PWR_MIC_DET_BIT, 0, NULL, 0),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5645_ASRC_1,
+ 11, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5645_ASRC_1,
+ 12, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5645_ASRC_1,
+ 10, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC MONO L ASRC", 1, RT5645_ASRC_1,
+ 9, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5645_ASRC_1,
+ 8, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5645_ASRC_1,
+ 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5645_ASRC_1,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5645_ASRC_1,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5645_ASRC_1,
+ 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5645_ASRC_1,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5645_ASRC_1,
+ 0, 0, NULL, 0),
+
+ /* Input Side */
+ /* micbias */
+ SND_SOC_DAPM_MICBIAS("micbias1", RT5645_PWR_ANLG2,
+ RT5645_PWR_MB1_BIT, 0),
+ SND_SOC_DAPM_MICBIAS("micbias2", RT5645_PWR_ANLG2,
+ RT5645_PWR_MB2_BIT, 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("Haptic Generator"),
+
+ SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5645_DMIC_CTRL1,
+ RT5645_DMIC_1_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5645_DMIC_CTRL1,
+ RT5645_DMIC_2_EN_SFT, 0, NULL, 0),
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1", RT5645_PWR_ANLG2,
+ RT5645_PWR_BST1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("BST2", RT5645_PWR_ANLG2,
+ RT5645_PWR_BST2_BIT, 0, NULL, 0, rt5645_bst2_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ /* Input Volume */
+ SND_SOC_DAPM_PGA("INL VOL", RT5645_PWR_VOL,
+ RT5645_PWR_IN_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR VOL", RT5645_PWR_VOL,
+ RT5645_PWR_IN_R_BIT, 0, NULL, 0),
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIXL", RT5645_PWR_MIXER, RT5645_PWR_RM_L_BIT,
+ 0, rt5645_rec_l_mix, ARRAY_SIZE(rt5645_rec_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIXR", RT5645_PWR_MIXER, RT5645_PWR_RM_R_BIT,
+ 0, rt5645_rec_r_mix, ARRAY_SIZE(rt5645_rec_r_mix)),
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC L power", RT5645_PWR_DIG1,
+ RT5645_PWR_ADC_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC R power", RT5645_PWR_DIG1,
+ RT5645_PWR_ADC_R_BIT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_sto1_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_sto_adc2_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_sto_adc2_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_sto_adc1_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_sto_adc1_mux),
+ SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_mono_dmic_l_mux),
+ SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_mono_dmic_r_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_mono_adc_l2_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_mono_adc_l1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_mono_adc_r1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5645_mono_adc_r2_mux),
+ /* ADC Mixer */
+
+ SND_SOC_DAPM_SUPPLY_S("adc stereo1 filter", 1, RT5645_PWR_DIG2,
+ RT5645_PWR_ADC_S1F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5645_sto1_adc_l_mix, ARRAY_SIZE(rt5645_sto1_adc_l_mix),
+ NULL, 0),
+ SND_SOC_DAPM_MIXER_E("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5645_sto1_adc_r_mix, ARRAY_SIZE(rt5645_sto1_adc_r_mix),
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("adc mono left filter", 1, RT5645_PWR_DIG2,
+ RT5645_PWR_ADC_MF_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("Mono ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5645_mono_adc_l_mix, ARRAY_SIZE(rt5645_mono_adc_l_mix),
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("adc mono right filter", 1, RT5645_PWR_DIG2,
+ RT5645_PWR_ADC_MF_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("Mono ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5645_mono_adc_r_mix, ARRAY_SIZE(rt5645_mono_adc_r_mix),
+ NULL, 0),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("VAD_ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* IF1 2 Mux */
+ SND_SOC_DAPM_MUX("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 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", RT5645_PWR_DIG1,
+ RT5645_PWR_I2S2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM,
+ 0, 0, &rt5645_vad_adc_mux),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
+ rt5645_dac_l_mix, ARRAY_SIZE(rt5645_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5645_dac_r_mix, ARRAY_SIZE(rt5645_dac_r_mix)),
+
+ /* DAC2 channel Mux */
+ SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac_l2_mux),
+ SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac_r2_mux),
+ SND_SOC_DAPM_PGA("DAC L2 Volume", RT5645_PWR_DIG1,
+ RT5645_PWR_DAC_L2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC R2 Volume", RT5645_PWR_DIG1,
+ RT5645_PWR_DAC_R2_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DAC1 L Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac1l_mux),
+ SND_SOC_DAPM_MUX("DAC1 R Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac1r_mux),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY_S("dac stereo1 filter", 1, RT5645_PWR_DIG2,
+ RT5645_PWR_DAC_S1F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("dac mono left filter", 1, RT5645_PWR_DIG2,
+ RT5645_PWR_DAC_MF_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("dac mono right filter", 1, RT5645_PWR_DIG2,
+ RT5645_PWR_DAC_MF_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5645_sto_dac_l_mix, ARRAY_SIZE(rt5645_sto_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5645_sto_dac_r_mix, ARRAY_SIZE(rt5645_sto_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5645_mono_dac_l_mix, ARRAY_SIZE(rt5645_mono_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5645_mono_dac_r_mix, ARRAY_SIZE(rt5645_mono_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5645_dig_l_mix, ARRAY_SIZE(rt5645_dig_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5645_dig_r_mix, ARRAY_SIZE(rt5645_dig_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_L1_BIT,
+ 0),
+ SND_SOC_DAPM_DAC("DAC L2", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_L2_BIT,
+ 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_R1_BIT,
+ 0),
+ SND_SOC_DAPM_DAC("DAC R2", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_R2_BIT,
+ 0),
+ /* OUT Mixer */
+ SND_SOC_DAPM_MIXER("SPK MIXL", RT5645_PWR_MIXER, RT5645_PWR_SM_L_BIT,
+ 0, rt5645_spk_l_mix, ARRAY_SIZE(rt5645_spk_l_mix)),
+ SND_SOC_DAPM_MIXER("SPK MIXR", RT5645_PWR_MIXER, RT5645_PWR_SM_R_BIT,
+ 0, rt5645_spk_r_mix, ARRAY_SIZE(rt5645_spk_r_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXL", RT5645_PWR_MIXER, RT5645_PWR_OM_L_BIT,
+ 0, rt5645_out_l_mix, ARRAY_SIZE(rt5645_out_l_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXR", RT5645_PWR_MIXER, RT5645_PWR_OM_R_BIT,
+ 0, rt5645_out_r_mix, ARRAY_SIZE(rt5645_out_r_mix)),
+ /* Ouput Volume */
+ SND_SOC_DAPM_SWITCH("SPKVOL L", RT5645_PWR_VOL, RT5645_PWR_SV_L_BIT, 0,
+ &spk_l_vol_control),
+ SND_SOC_DAPM_SWITCH("SPKVOL R", RT5645_PWR_VOL, RT5645_PWR_SV_R_BIT, 0,
+ &spk_r_vol_control),
+ SND_SOC_DAPM_MIXER("HPOVOL MIXL", RT5645_PWR_VOL, RT5645_PWR_HV_L_BIT,
+ 0, rt5645_hpvoll_mix, ARRAY_SIZE(rt5645_hpvoll_mix)),
+ SND_SOC_DAPM_MIXER("HPOVOL MIXR", RT5645_PWR_VOL, RT5645_PWR_HV_R_BIT,
+ 0, rt5645_hpvolr_mix, ARRAY_SIZE(rt5645_hpvolr_mix)),
+ SND_SOC_DAPM_SUPPLY("HPOVOL MIXL Power", RT5645_PWR_MIXER,
+ RT5645_PWR_HM_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HPOVOL MIXR Power", RT5645_PWR_MIXER,
+ RT5645_PWR_HM_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("HPOVOL L", SND_SOC_NOPM, 0, 0, &hp_l_vol_control),
+ SND_SOC_DAPM_SWITCH("HPOVOL R", SND_SOC_NOPM, 0, 0, &hp_r_vol_control),
+
+ /* HPO/LOUT/Mono Mixer */
+ SND_SOC_DAPM_MIXER("SPOL MIX", SND_SOC_NOPM, 0, 0, rt5645_spo_l_mix,
+ ARRAY_SIZE(rt5645_spo_l_mix)),
+ SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0, 0, rt5645_spo_r_mix,
+ ARRAY_SIZE(rt5645_spo_r_mix)),
+ SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0, rt5645_hpo_mix,
+ ARRAY_SIZE(rt5645_hpo_mix)),
+ SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, rt5645_lout_mix,
+ ARRAY_SIZE(rt5645_lout_mix)),
+
+ SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0, rt5645_hp_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0, rt5645_lout_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("SPK amp", 2, SND_SOC_NOPM, 0, 0, rt5645_spk_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ /* PDM */
+ SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5645_PWR_DIG2, RT5645_PWR_PDM1_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_MUX("PDM1 L Mux", SND_SOC_NOPM, 0, 0, &rt5645_pdm1_l_mux),
+ SND_SOC_DAPM_MUX("PDM1 R Mux", SND_SOC_NOPM, 0, 0, &rt5645_pdm1_r_mux),
+
+ SND_SOC_DAPM_SWITCH("PDM1 L", SND_SOC_NOPM, 0, 0, &pdm1_l_vol_control),
+ SND_SOC_DAPM_SWITCH("PDM1 R", SND_SOC_NOPM, 0, 0, &pdm1_r_vol_control),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("LOUTL"),
+ SND_SOC_DAPM_OUTPUT("LOUTR"),
+ SND_SOC_DAPM_OUTPUT("PDM1L"),
+ SND_SOC_DAPM_OUTPUT("PDM1R"),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_widget rt5650_specific_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("A DAC1 L Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_a_dac1_l_mux),
+ SND_SOC_DAPM_MUX("A DAC1 R Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_a_dac1_r_mux),
+ SND_SOC_DAPM_MUX("A DAC2 L Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_a_dac2_l_mux),
+ SND_SOC_DAPM_MUX("A DAC2 R Mux", SND_SOC_NOPM,
+ 0, 0, &rt5650_a_dac2_r_mux),
+};
+
+static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
+ { "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc },
+ { "adc mono left filter", NULL, "ADC MONO L ASRC", is_using_asrc },
+ { "adc mono right filter", NULL, "ADC MONO R ASRC", is_using_asrc },
+ { "dac mono left filter", NULL, "DAC MONO L ASRC", is_using_asrc },
+ { "dac mono right filter", NULL, "DAC MONO R ASRC", is_using_asrc },
+ { "dac stereo1 filter", NULL, "DAC STO ASRC", is_using_asrc },
+
+ { "I2S1", NULL, "I2S1 ASRC" },
+ { "I2S2", NULL, "I2S2 ASRC" },
+
+ { "IN1P", NULL, "LDO2" },
+ { "IN2P", NULL, "LDO2" },
+
+ { "DMIC1", NULL, "DMIC L1" },
+ { "DMIC1", NULL, "DMIC R1" },
+ { "DMIC2", NULL, "DMIC L2" },
+ { "DMIC2", NULL, "DMIC R2" },
+
+ { "BST1", NULL, "IN1P" },
+ { "BST1", NULL, "IN1N" },
+ { "BST1", NULL, "JD Power" },
+ { "BST1", NULL, "Mic Det Power" },
+ { "BST2", NULL, "IN2P" },
+ { "BST2", NULL, "IN2N" },
+
+ { "INL VOL", NULL, "IN2P" },
+ { "INR VOL", NULL, "IN2N" },
+
+ { "RECMIXL", "HPOL Switch", "HPOL" },
+ { "RECMIXL", "INL Switch", "INL VOL" },
+ { "RECMIXL", "BST2 Switch", "BST2" },
+ { "RECMIXL", "BST1 Switch", "BST1" },
+ { "RECMIXL", "OUT MIXL Switch", "OUT MIXL" },
+
+ { "RECMIXR", "HPOR Switch", "HPOR" },
+ { "RECMIXR", "INR Switch", "INR VOL" },
+ { "RECMIXR", "BST2 Switch", "BST2" },
+ { "RECMIXR", "BST1 Switch", "BST1" },
+ { "RECMIXR", "OUT MIXR Switch", "OUT MIXR" },
+
+ { "ADC L", NULL, "RECMIXL" },
+ { "ADC L", NULL, "ADC L power" },
+ { "ADC R", NULL, "RECMIXR" },
+ { "ADC R", NULL, "ADC R power" },
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC L2", NULL, "DMIC CLK"},
+ {"DMIC L2", NULL, "DMIC2 Power"},
+ {"DMIC R2", NULL, "DMIC CLK"},
+ {"DMIC R2", NULL, "DMIC2 Power"},
+
+ { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
+ { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC" },
+
+ { "Mono DMIC L Mux", "DMIC1", "DMIC L1" },
+ { "Mono DMIC L Mux", "DMIC2", "DMIC L2" },
+ { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC" },
+
+ { "Mono DMIC R Mux", "DMIC1", "DMIC R1" },
+ { "Mono DMIC R Mux", "DMIC2", "DMIC R2" },
+ { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC" },
+
+ { "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC Mux" },
+ { "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" },
+ { "Stereo1 ADC L1 Mux", "ADC", "ADC L" },
+ { "Stereo1 ADC L1 Mux", "DAC MIX", "DAC MIXL" },
+
+ { "Stereo1 ADC R1 Mux", "ADC", "ADC R" },
+ { "Stereo1 ADC R1 Mux", "DAC MIX", "DAC MIXR" },
+ { "Stereo1 ADC R2 Mux", "DMIC", "Stereo1 DMIC Mux" },
+ { "Stereo1 ADC R2 Mux", "DAC MIX", "DAC MIXR" },
+
+ { "Mono ADC L2 Mux", "DMIC", "Mono DMIC L Mux" },
+ { "Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL" },
+ { "Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL" },
+ { "Mono ADC L1 Mux", "ADC", "ADC L" },
+
+ { "Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR" },
+ { "Mono ADC R1 Mux", "ADC", "ADC R" },
+ { "Mono ADC R2 Mux", "DMIC", "Mono DMIC R Mux" },
+ { "Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR" },
+
+ { "Sto1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux" },
+ { "Sto1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux" },
+ { "Sto1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux" },
+ { "Sto1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux" },
+
+ { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
+ { "Stereo1 ADC MIXL", NULL, "adc stereo1 filter" },
+ { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
+ { "Stereo1 ADC MIXR", NULL, "adc stereo1 filter" },
+ { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux" },
+ { "Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux" },
+ { "Mono ADC MIXL", NULL, "adc mono left filter" },
+ { "adc mono left filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux" },
+ { "Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux" },
+ { "Mono ADC MIXR", NULL, "adc mono right filter" },
+ { "adc mono right filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "VAD ADC Mux", "Sto1 ADC L", "Stereo1 ADC MIXL" },
+ { "VAD ADC Mux", "Mono ADC L", "Mono ADC MIXL" },
+ { "VAD ADC Mux", "Mono ADC R", "Mono ADC MIXR" },
+
+ { "IF_ADC1", NULL, "Stereo1 ADC MIXL" },
+ { "IF_ADC1", NULL, "Stereo1 ADC MIXR" },
+ { "IF_ADC2", NULL, "Mono ADC MIXL" },
+ { "IF_ADC2", NULL, "Mono ADC MIXR" },
+ { "VAD_ADC", NULL, "VAD ADC Mux" },
+
+ { "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 DAC1", NULL, "AIF1RX" },
+ { "IF1 DAC2", NULL, "AIF1RX" },
+ { "IF2 DAC", NULL, "AIF2RX" },
+
+ { "IF1 DAC1", NULL, "I2S1" },
+ { "IF1 DAC2", NULL, "I2S1" },
+ { "IF2 DAC", NULL, "I2S2" },
+
+ { "IF1 DAC2 L", NULL, "IF1 DAC2" },
+ { "IF1 DAC2 R", NULL, "IF1 DAC2" },
+ { "IF1 DAC1 L", NULL, "IF1 DAC1" },
+ { "IF1 DAC1 R", NULL, "IF1 DAC1" },
+ { "IF2 DAC L", NULL, "IF2 DAC" },
+ { "IF2 DAC R", NULL, "IF2 DAC" },
+
+ { "DAC1 L Mux", "IF1 DAC", "IF1 DAC1 L" },
+ { "DAC1 L Mux", "IF2 DAC", "IF2 DAC L" },
+
+ { "DAC1 R Mux", "IF1 DAC", "IF1 DAC1 R" },
+ { "DAC1 R Mux", "IF2 DAC", "IF2 DAC R" },
+
+ { "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" },
+ { "DAC1 MIXL", "DAC1 Switch", "DAC1 L Mux" },
+ { "DAC1 MIXL", NULL, "dac stereo1 filter" },
+ { "DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR" },
+ { "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" },
+ { "DAC1 MIXR", NULL, "dac stereo1 filter" },
+
+ { "DAC 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" },
+ { "DAC R2 Volume", NULL, "DAC R2 Mux" },
+ { "DAC R2 Volume", NULL, "dac mono right filter" },
+
+ { "Stereo DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" },
+ { "Stereo DAC MIXL", "DAC R1 Switch", "DAC1 MIXR" },
+ { "Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" },
+ { "Stereo DAC MIXL", NULL, "dac stereo1 filter" },
+ { "Stereo DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" },
+ { "Stereo DAC MIXR", "DAC L1 Switch", "DAC1 MIXL" },
+ { "Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" },
+ { "Stereo DAC MIXR", NULL, "dac stereo1 filter" },
+
+ { "Mono DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" },
+ { "Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" },
+ { "Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" },
+ { "Mono DAC MIXL", NULL, "dac mono left filter" },
+ { "Mono DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" },
+ { "Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" },
+ { "Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" },
+ { "Mono DAC MIXR", NULL, "dac mono right filter" },
+
+ { "DAC MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" },
+ { "DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" },
+ { "DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" },
+ { "DAC MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" },
+ { "DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" },
+ { "DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" },
+
+ { "DAC L1", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC R1", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC L2", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC R2", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "SPK MIXL", "BST1 Switch", "BST1" },
+ { "SPK MIXL", "INL Switch", "INL VOL" },
+ { "SPK MIXL", "DAC L1 Switch", "DAC L1" },
+ { "SPK MIXL", "DAC L2 Switch", "DAC L2" },
+ { "SPK MIXR", "BST2 Switch", "BST2" },
+ { "SPK MIXR", "INR Switch", "INR VOL" },
+ { "SPK MIXR", "DAC R1 Switch", "DAC R1" },
+ { "SPK MIXR", "DAC R2 Switch", "DAC R2" },
+
+ { "OUT MIXL", "BST1 Switch", "BST1" },
+ { "OUT MIXL", "INL Switch", "INL VOL" },
+ { "OUT MIXL", "DAC L2 Switch", "DAC L2" },
+ { "OUT MIXL", "DAC L1 Switch", "DAC L1" },
+
+ { "OUT MIXR", "BST2 Switch", "BST2" },
+ { "OUT MIXR", "INR Switch", "INR VOL" },
+ { "OUT MIXR", "DAC R2 Switch", "DAC R2" },
+ { "OUT MIXR", "DAC R1 Switch", "DAC R1" },
+
+ { "HPOVOL MIXL", "DAC1 Switch", "DAC L1" },
+ { "HPOVOL MIXL", "DAC2 Switch", "DAC L2" },
+ { "HPOVOL MIXL", "INL Switch", "INL VOL" },
+ { "HPOVOL MIXL", "BST1 Switch", "BST1" },
+ { "HPOVOL MIXL", NULL, "HPOVOL MIXL Power" },
+ { "HPOVOL MIXR", "DAC1 Switch", "DAC R1" },
+ { "HPOVOL MIXR", "DAC2 Switch", "DAC R2" },
+ { "HPOVOL MIXR", "INR Switch", "INR VOL" },
+ { "HPOVOL MIXR", "BST2 Switch", "BST2" },
+ { "HPOVOL MIXR", NULL, "HPOVOL MIXR Power" },
+
+ { "DAC 2", NULL, "DAC L2" },
+ { "DAC 2", NULL, "DAC R2" },
+ { "DAC 1", NULL, "DAC L1" },
+ { "DAC 1", NULL, "DAC R1" },
+ { "HPOVOL L", "Switch", "HPOVOL MIXL" },
+ { "HPOVOL R", "Switch", "HPOVOL MIXR" },
+ { "HPOVOL", NULL, "HPOVOL L" },
+ { "HPOVOL", NULL, "HPOVOL R" },
+ { "HPO MIX", "DAC1 Switch", "DAC 1" },
+ { "HPO MIX", "HPVOL Switch", "HPOVOL" },
+
+ { "SPKVOL L", "Switch", "SPK MIXL" },
+ { "SPKVOL R", "Switch", "SPK MIXR" },
+
+ { "SPOL MIX", "DAC 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" },
+
+ { "LOUT MIX", "DAC L1 Switch", "DAC L1" },
+ { "LOUT MIX", "DAC R1 Switch", "DAC R1" },
+ { "LOUT MIX", "OUTMIX L Switch", "OUT MIXL" },
+ { "LOUT MIX", "OUTMIX R Switch", "OUT MIXR" },
+
+ { "PDM1 L Mux", "Stereo DAC", "Stereo DAC MIXL" },
+ { "PDM1 L Mux", "Mono DAC", "Mono DAC MIXL" },
+ { "PDM1 L Mux", NULL, "PDM1 Power" },
+ { "PDM1 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
+ { "PDM1 R Mux", "Mono DAC", "Mono DAC MIXR" },
+ { "PDM1 R Mux", NULL, "PDM1 Power" },
+
+ { "HP amp", NULL, "HPO MIX" },
+ { "HP amp", NULL, "JD Power" },
+ { "HP amp", NULL, "Mic Det Power" },
+ { "HP amp", NULL, "LDO2" },
+ { "HPOL", NULL, "HP amp" },
+ { "HPOR", NULL, "HP amp" },
+
+ { "LOUT amp", NULL, "LOUT MIX" },
+ { "LOUTL", NULL, "LOUT amp" },
+ { "LOUTR", NULL, "LOUT amp" },
+
+ { "PDM1 L", "Switch", "PDM1 L Mux" },
+ { "PDM1 R", "Switch", "PDM1 R Mux" },
+
+ { "PDM1L", NULL, "PDM1 L" },
+ { "PDM1R", NULL, "PDM1 R" },
+
+ { "SPK amp", NULL, "SPOL MIX" },
+ { "SPK amp", NULL, "SPOR MIX" },
+ { "SPOL", NULL, "SPK amp" },
+ { "SPOR", NULL, "SPK amp" },
+};
+
+static const struct snd_soc_dapm_route rt5650_specific_dapm_routes[] = {
+ { "A DAC1 L Mux", "DAC1", "DAC1 MIXL"},
+ { "A DAC1 L Mux", "Stereo DAC Mixer", "Stereo DAC MIXL"},
+ { "A DAC1 R Mux", "DAC1", "DAC1 MIXR"},
+ { "A DAC1 R Mux", "Stereo DAC Mixer", "Stereo DAC MIXR"},
+
+ { "A DAC2 L Mux", "Stereo DAC Mixer", "Stereo DAC MIXL"},
+ { "A DAC2 L Mux", "Mono DAC Mixer", "Mono DAC MIXL"},
+ { "A DAC2 R Mux", "Stereo DAC Mixer", "Stereo DAC MIXR"},
+ { "A DAC2 R Mux", "Mono DAC Mixer", "Mono DAC MIXR"},
+
+ { "DAC L1", NULL, "A DAC1 L Mux" },
+ { "DAC R1", NULL, "A DAC1 R Mux" },
+ { "DAC L2", NULL, "A DAC2 L Mux" },
+ { "DAC R2", NULL, "A DAC2 R Mux" },
+};
+
+static const struct snd_soc_dapm_route rt5645_specific_dapm_routes[] = {
+ { "DAC L1", NULL, "Stereo DAC MIXL" },
+ { "DAC R1", NULL, "Stereo DAC MIXR" },
+ { "DAC L2", NULL, "Mono DAC MIXL" },
+ { "DAC R2", NULL, "Mono DAC MIXR" },
+};
+
+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, 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");
+ 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);
+ return -EINVAL;
+ }
+
+ switch (rt5645->codec_type) {
+ case CODEC_TYPE_RT5650:
+ dl_sft = 4;
+ break;
+ default:
+ dl_sft = 2;
+ break;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt5645->bclk[dai->id] = rt5645->lrck[dai->id] * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt5645->bclk[dai->id], rt5645->lrck[dai->id]);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ val_len = 0x1;
+ break;
+ case 24:
+ val_len = 0x2;
+ break;
+ case 8:
+ val_len = 0x3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5645_AIF1:
+ mask_clk = RT5645_I2S_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,
+ (0x3 << dl_sft), (val_len << dl_sft));
+ snd_soc_update_bits(codec, 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,
+ (0x3 << dl_sft), (val_len << dl_sft));
+ snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0, pol_sft;
+
+ switch (rt5645->codec_type) {
+ case CODEC_TYPE_RT5650:
+ pol_sft = 8;
+ break;
+ default:
+ pol_sft = 7;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rt5645->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT5645_I2S_MS_S;
+ rt5645->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= (1 << pol_sft);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5645_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5645_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5645_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (dai->id) {
+ case RT5645_AIF1:
+ snd_soc_update_bits(codec, 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 | (1 << pol_sft) |
+ RT5645_I2S_DF_MASK, reg_val);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+
+ if (freq == rt5645->sysclk && clk_id == rt5645->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5645_SCLK_S_MCLK:
+ reg_val |= RT5645_SCLK_SRC_MCLK;
+ break;
+ case RT5645_SCLK_S_PLL1:
+ reg_val |= RT5645_SCLK_SRC_PLL1;
+ break;
+ case RT5645_SCLK_S_RCCLK:
+ reg_val |= RT5645_SCLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ RT5645_SCLK_SRC_MASK, reg_val);
+ rt5645->sysclk = freq;
+ rt5645->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+ return 0;
+}
+
+static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt5645->pll_src && freq_in == rt5645->pll_in &&
+ freq_out == rt5645->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(codec->dev, "PLL disabled\n");
+
+ rt5645->pll_in = 0;
+ rt5645->pll_out = 0;
+ snd_soc_update_bits(codec, 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,
+ 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,
+ RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK1);
+ break;
+ case RT5645_AIF2:
+ snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK2);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec->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);
+ return ret;
+ }
+
+ dev_dbg(codec->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,
+ 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);
+
+ rt5645->pll_in = freq_in;
+ rt5645->pll_out = freq_out;
+ rt5645->pll_src = source;
+
+ return 0;
+}
+
+static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ unsigned int i_slot_sft, o_slot_sft, i_width_sht, o_width_sht, en_sft;
+ unsigned int mask, val = 0;
+
+ switch (rt5645->codec_type) {
+ case CODEC_TYPE_RT5650:
+ en_sft = 15;
+ i_slot_sft = 10;
+ o_slot_sft = 8;
+ i_width_sht = 6;
+ o_width_sht = 4;
+ mask = 0x8ff0;
+ break;
+ default:
+ en_sft = 14;
+ i_slot_sft = o_slot_sft = 12;
+ i_width_sht = o_width_sht = 10;
+ mask = 0x7c00;
+ break;
+ }
+ if (rx_mask || tx_mask) {
+ val |= (1 << en_sft);
+ if (rt5645->codec_type == CODEC_TYPE_RT5645)
+ snd_soc_update_bits(codec, RT5645_BASS_BACK,
+ RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB);
+ }
+
+ switch (slots) {
+ case 4:
+ val |= (1 << i_slot_sft) | (1 << o_slot_sft);
+ break;
+ case 6:
+ val |= (2 << i_slot_sft) | (2 << o_slot_sft);
+ break;
+ case 8:
+ val |= (3 << i_slot_sft) | (3 << o_slot_sft);
+ break;
+ case 2:
+ default:
+ break;
+ }
+
+ switch (slot_width) {
+ case 20:
+ val |= (1 << i_width_sht) | (1 << o_width_sht);
+ break;
+ case 24:
+ val |= (2 << i_width_sht) | (2 << o_width_sht);
+ break;
+ case 32:
+ val |= (3 << i_width_sht) | (3 << o_width_sht);
+ break;
+ case 16:
+ default:
+ break;
+ }
+
+ snd_soc_update_bits(codec, RT5645_TDM_CTRL_1, mask, val);
+
+ return 0;
+}
+
+static int rt5645_set_bias_level(struct snd_soc_codec *codec,
+ 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, 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,
+ RT5645_PWR_FV1 | RT5645_PWR_FV2,
+ RT5645_PWR_FV1 | RT5645_PWR_FV2);
+ snd_soc_update_bits(codec, 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,
+ 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,
+ RT5645_PWR_FV1 | RT5645_PWR_FV2,
+ RT5645_PWR_FV1 | RT5645_PWR_FV2);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100);
+ snd_soc_update_bits(codec, RT5645_GEN_CTRL1,
+ RT5645_DIG_GATE_CTRL, 0);
+ snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ RT5645_PWR_VREF1 | RT5645_PWR_MB |
+ RT5645_PWR_BG | RT5645_PWR_VREF2 |
+ RT5645_PWR_FV1 | RT5645_PWR_FV2, 0x0);
+ break;
+
+ default:
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int rt5645_jack_detect(struct snd_soc_codec *codec)
+{
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ int gpio_state, jack_type = 0;
+ unsigned int val;
+
+ 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);
+
+ dev_dbg(codec->dev, "gpio = %d(%d)\n", rt5645->pdata.hp_det_gpio,
+ gpio_state);
+
+ 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);
+
+ snd_soc_write(codec, RT5645_IN1_CTRL1, 0x0006);
+ snd_soc_write(codec, RT5645_JD_CTRL3, 0x00b0);
+
+ snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
+ RT5645_CBJ_MN_JD, 0);
+ snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
+ RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD);
+
+ msleep(400);
+ val = snd_soc_read(codec, RT5645_IN1_CTRL3) & 0x7;
+ dev_dbg(codec->dev, "val = %d\n", val);
+
+ if (val == 1 || val == 2)
+ jack_type = SND_JACK_HEADSET;
+ else
+ jack_type = SND_JACK_HEADPHONE;
+
+ 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_jack_report(rt5645->hp_jack, jack_type, SND_JACK_HEADPHONE);
+ snd_soc_jack_report(rt5645->mic_jack, jack_type, SND_JACK_MICROPHONE);
+ return 0;
+}
+
+int rt5645_set_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack)
+{
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
+ rt5645->hp_jack = hp_jack;
+ rt5645->mic_jack = mic_jack;
+ rt5645_jack_detect(codec);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5645_set_jack_detect);
+
+static void rt5645_jack_detect_work(struct work_struct *work)
+{
+ struct rt5645_priv *rt5645 =
+ container_of(work, struct rt5645_priv, jack_detect_work.work);
+
+ rt5645_jack_detect(rt5645->codec);
+}
+
+static irqreturn_t rt5645_irq(int irq, void *data)
+{
+ struct rt5645_priv *rt5645 = data;
+
+ queue_delayed_work(system_power_efficient_wq,
+ &rt5645->jack_detect_work, msecs_to_jiffies(250));
+
+ return IRQ_HANDLED;
+}
+
+static int rt5645_probe(struct snd_soc_codec *codec)
+{
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
+ rt5645->codec = codec;
+
+ switch (rt5645->codec_type) {
+ case CODEC_TYPE_RT5645:
+ snd_soc_dapm_add_routes(&codec->dapm,
+ rt5645_specific_dapm_routes,
+ ARRAY_SIZE(rt5645_specific_dapm_routes));
+ break;
+ case CODEC_TYPE_RT5650:
+ snd_soc_dapm_new_controls(&codec->dapm,
+ rt5650_specific_dapm_widgets,
+ ARRAY_SIZE(rt5650_specific_dapm_widgets));
+ snd_soc_dapm_add_routes(&codec->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);
+
+ /* 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);
+ }
+
+ return 0;
+}
+
+static int rt5645_remove(struct snd_soc_codec *codec)
+{
+ rt5645_reset(codec);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt5645_suspend(struct snd_soc_codec *codec)
+{
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt5645->regmap, true);
+ regcache_mark_dirty(rt5645->regmap);
+
+ return 0;
+}
+
+static int rt5645_resume(struct snd_soc_codec *codec)
+{
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt5645->regmap, false);
+ regcache_sync(rt5645->regmap);
+
+ return 0;
+}
+#else
+#define rt5645_suspend NULL
+#define rt5645_resume NULL
+#endif
+
+#define RT5645_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5645_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static struct snd_soc_dai_ops rt5645_aif_dai_ops = {
+ .hw_params = rt5645_hw_params,
+ .set_fmt = rt5645_set_dai_fmt,
+ .set_sysclk = rt5645_set_dai_sysclk,
+ .set_tdm_slot = rt5645_set_tdm_slot,
+ .set_pll = rt5645_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver rt5645_dai[] = {
+ {
+ .name = "rt5645-aif1",
+ .id = RT5645_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5645_STEREO_RATES,
+ .formats = RT5645_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5645_STEREO_RATES,
+ .formats = RT5645_FORMATS,
+ },
+ .ops = &rt5645_aif_dai_ops,
+ },
+ {
+ .name = "rt5645-aif2",
+ .id = RT5645_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5645_STEREO_RATES,
+ .formats = RT5645_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5645_STEREO_RATES,
+ .formats = RT5645_FORMATS,
+ },
+ .ops = &rt5645_aif_dai_ops,
+ },
+};
+
+static 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 regmap_config rt5645_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .use_single_rw = 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,
+ .reg_defaults = rt5645_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5645_reg),
+ .ranges = rt5645_ranges,
+ .num_ranges = ARRAY_SIZE(rt5645_ranges),
+};
+
+static const struct i2c_device_id rt5645_i2c_id[] = {
+ { "rt5645", 0 },
+ { "rt5650", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
+
+#ifdef CONFIG_ACPI
+static struct acpi_device_id rt5645_acpi_match[] = {
+ { "10EC5645", 0 },
+ { "10EC5650", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
+#endif
+
+static int rt5645_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt5645_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5645_priv *rt5645;
+ int ret;
+ unsigned int val;
+
+ rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv),
+ GFP_KERNEL);
+ if (rt5645 == NULL)
+ return -ENOMEM;
+
+ rt5645->i2c = i2c;
+ i2c_set_clientdata(i2c, rt5645);
+
+ if (pdata)
+ rt5645->pdata = *pdata;
+
+ 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);
+ return ret;
+ }
+
+ regmap_read(rt5645->regmap, RT5645_VENDOR_ID2, &val);
+
+ switch (val) {
+ case RT5645_DEVICE_ID:
+ rt5645->codec_type = CODEC_TYPE_RT5645;
+ break;
+ case RT5650_DEVICE_ID:
+ rt5645->codec_type = CODEC_TYPE_RT5650;
+ break;
+ default:
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt5645 or rt5650\n",
+ val);
+ return -ENODEV;
+ }
+
+ regmap_write(rt5645->regmap, RT5645_RESET, 0);
+
+ ret = regmap_register_patch(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,
+ ARRAY_SIZE(rt5650_init_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Apply rt5650 patch failed: %d\n",
+ ret);
+ }
+
+ if (rt5645->pdata.in2_diff)
+ regmap_update_bits(rt5645->regmap, RT5645_IN2_CTRL,
+ RT5645_IN_DF2, RT5645_IN_DF2);
+
+ if (rt5645->pdata.dmic_en) {
+ regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+ RT5645_GP2_PIN_MASK, RT5645_GP2_PIN_DMIC1_SCL);
+
+ switch (rt5645->pdata.dmic1_data_pin) {
+ case RT5645_DMIC_DATA_IN2N:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_IN2N);
+ break;
+
+ case RT5645_DMIC_DATA_GPIO5:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO5);
+ regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+ RT5645_GP5_PIN_MASK, RT5645_GP5_PIN_DMIC1_SDA);
+ break;
+
+ case RT5645_DMIC_DATA_GPIO11:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO11);
+ regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+ RT5645_GP11_PIN_MASK,
+ RT5645_GP11_PIN_DMIC1_SDA);
+ break;
+
+ default:
+ break;
+ }
+
+ switch (rt5645->pdata.dmic2_data_pin) {
+ case RT5645_DMIC_DATA_IN2P:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_IN2P);
+ break;
+
+ case RT5645_DMIC_DATA_GPIO6:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO6);
+ regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+ RT5645_GP6_PIN_MASK, RT5645_GP6_PIN_DMIC2_SDA);
+ break;
+
+ case RT5645_DMIC_DATA_GPIO10:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO10);
+ regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+ RT5645_GP10_PIN_MASK,
+ RT5645_GP10_PIN_DMIC2_SDA);
+ break;
+
+ case RT5645_DMIC_DATA_GPIO12:
+ regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+ RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO12);
+ regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+ RT5645_GP12_PIN_MASK,
+ RT5645_GP12_PIN_DMIC2_SDA);
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ if (rt5645->pdata.en_jd_func) {
+ 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);
+ regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+ RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT);
+ }
+
+ if (rt5645->pdata.jd_mode) {
+ regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+ RT5645_IRQ_JD_1_1_EN, RT5645_IRQ_JD_1_1_EN);
+ regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
+ RT5645_JD_PSV_MODE, RT5645_JD_PSV_MODE);
+ regmap_update_bits(rt5645->regmap, RT5645_HPO_MIXER,
+ RT5645_IRQ_PSV_MODE, RT5645_IRQ_PSV_MODE);
+ regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+ RT5645_MIC2_OVCD_EN, RT5645_MIC2_OVCD_EN);
+ regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+ RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ);
+ switch (rt5645->pdata.jd_mode) {
+ case 1:
+ regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
+ RT5645_JD1_MODE_MASK,
+ RT5645_JD1_MODE_0);
+ break;
+ case 2:
+ regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
+ RT5645_JD1_MODE_MASK,
+ RT5645_JD1_MODE_1);
+ break;
+ case 3:
+ regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
+ RT5645_JD1_MODE_MASK,
+ RT5645_JD1_MODE_2);
+ break;
+ default:
+ break;
+ }
+ }
+
+ 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 (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");
+ }
+
+ INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
+
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
+ rt5645_dai, ARRAY_SIZE(rt5645_dai));
+}
+
+static int rt5645_i2c_remove(struct i2c_client *i2c)
+{
+ struct rt5645_priv *rt5645 = i2c_get_clientdata(i2c);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, rt5645);
+
+ cancel_delayed_work_sync(&rt5645->jack_detect_work);
+
+ if (gpio_is_valid(rt5645->pdata.hp_det_gpio))
+ gpio_free(rt5645->pdata.hp_det_gpio);
+
+ snd_soc_unregister_codec(&i2c->dev);
+
+ return 0;
+}
+
+static struct i2c_driver rt5645_i2c_driver = {
+ .driver = {
+ .name = "rt5645",
+ .owner = THIS_MODULE,
+ .acpi_match_table = ACPI_PTR(rt5645_acpi_match),
+ },
+ .probe = rt5645_i2c_probe,
+ .remove = rt5645_i2c_remove,
+ .id_table = rt5645_i2c_id,
+};
+module_i2c_driver(rt5645_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5645 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/rt5645.h b/kernel/sound/soc/codecs/rt5645.h
new file mode 100644
index 000000000..db78e9462
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5645.h
@@ -0,0 +1,2204 @@
+/*
+ * 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
+#define RT5645_VENDOR_ID1 0xfe
+#define RT5645_VENDOR_ID2 0xff
+/* I/O - Output */
+#define RT5645_SPK_VOL 0x01
+#define RT5645_HP_VOL 0x02
+#define RT5645_LOUT1 0x03
+#define RT5645_LOUT_CTRL 0x05
+/* I/O - Input */
+#define RT5645_IN1_CTRL1 0x0a
+#define RT5645_IN1_CTRL2 0x0b
+#define RT5645_IN1_CTRL3 0x0c
+#define RT5645_IN2_CTRL 0x0d
+#define RT5645_INL1_INR1_VOL 0x0f
+#define RT5645_SPK_FUNC_LIM 0x14
+#define RT5645_ADJ_HPF_CTRL 0x16
+/* I/O - ADC/DAC/DMIC */
+#define RT5645_DAC1_DIG_VOL 0x19
+#define RT5645_DAC2_DIG_VOL 0x1a
+#define RT5645_DAC_CTRL 0x1b
+#define RT5645_STO1_ADC_DIG_VOL 0x1c
+#define RT5645_MONO_ADC_DIG_VOL 0x1d
+#define RT5645_ADC_BST_VOL1 0x1e
+/* Mixer - D-D */
+#define RT5645_ADC_BST_VOL2 0x20
+#define RT5645_STO1_ADC_MIXER 0x27
+#define RT5645_MONO_ADC_MIXER 0x28
+#define RT5645_AD_DA_MIXER 0x29
+#define RT5645_STO_DAC_MIXER 0x2a
+#define RT5645_MONO_DAC_MIXER 0x2b
+#define RT5645_DIG_MIXER 0x2c
+#define RT5650_A_DAC_SOUR 0x2d
+#define RT5645_DIG_INF1_DATA 0x2f
+/* Mixer - PDM */
+#define RT5645_PDM_OUT_CTRL 0x31
+/* Mixer - ADC */
+#define RT5645_REC_L1_MIXER 0x3b
+#define RT5645_REC_L2_MIXER 0x3c
+#define RT5645_REC_R1_MIXER 0x3d
+#define RT5645_REC_R2_MIXER 0x3e
+/* Mixer - DAC */
+#define RT5645_HPMIXL_CTRL 0x3f
+#define RT5645_HPOMIXL_CTRL 0x40
+#define RT5645_HPMIXR_CTRL 0x41
+#define RT5645_HPOMIXR_CTRL 0x42
+#define RT5645_HPO_MIXER 0x45
+#define RT5645_SPK_L_MIXER 0x46
+#define RT5645_SPK_R_MIXER 0x47
+#define RT5645_SPO_MIXER 0x48
+#define RT5645_SPO_CLSD_RATIO 0x4a
+#define RT5645_OUT_L_GAIN1 0x4d
+#define RT5645_OUT_L_GAIN2 0x4e
+#define RT5645_OUT_L1_MIXER 0x4f
+#define RT5645_OUT_R_GAIN1 0x50
+#define RT5645_OUT_R_GAIN2 0x51
+#define RT5645_OUT_R1_MIXER 0x52
+#define RT5645_LOUT_MIXER 0x53
+/* Haptic */
+#define RT5645_HAPTIC_CTRL1 0x56
+#define RT5645_HAPTIC_CTRL2 0x57
+#define RT5645_HAPTIC_CTRL3 0x58
+#define RT5645_HAPTIC_CTRL4 0x59
+#define RT5645_HAPTIC_CTRL5 0x5a
+#define RT5645_HAPTIC_CTRL6 0x5b
+#define RT5645_HAPTIC_CTRL7 0x5c
+#define RT5645_HAPTIC_CTRL8 0x5d
+#define RT5645_HAPTIC_CTRL9 0x5e
+#define RT5645_HAPTIC_CTRL10 0x5f
+/* Power */
+#define RT5645_PWR_DIG1 0x61
+#define RT5645_PWR_DIG2 0x62
+#define RT5645_PWR_ANLG1 0x63
+#define RT5645_PWR_ANLG2 0x64
+#define RT5645_PWR_MIXER 0x65
+#define RT5645_PWR_VOL 0x66
+/* Private Register Control */
+#define RT5645_PRIV_INDEX 0x6a
+#define RT5645_PRIV_DATA 0x6c
+/* Format - ADC/DAC */
+#define RT5645_I2S1_SDP 0x70
+#define RT5645_I2S2_SDP 0x71
+#define RT5645_ADDA_CLK1 0x73
+#define RT5645_ADDA_CLK2 0x74
+#define RT5645_DMIC_CTRL1 0x75
+#define RT5645_DMIC_CTRL2 0x76
+/* Format - TDM Control */
+#define RT5645_TDM_CTRL_1 0x77
+#define RT5645_TDM_CTRL_2 0x78
+#define RT5645_TDM_CTRL_3 0x79
+
+/* Function - Analog */
+#define RT5645_GLB_CLK 0x80
+#define RT5645_PLL_CTRL1 0x81
+#define RT5645_PLL_CTRL2 0x82
+#define RT5645_ASRC_1 0x83
+#define RT5645_ASRC_2 0x84
+#define RT5645_ASRC_3 0x85
+#define RT5645_ASRC_4 0x8a
+#define RT5645_DEPOP_M1 0x8e
+#define RT5645_DEPOP_M2 0x8f
+#define RT5645_DEPOP_M3 0x90
+#define RT5645_CHARGE_PUMP 0x91
+#define RT5645_MICBIAS 0x93
+#define RT5645_A_JD_CTRL1 0x94
+#define RT5645_VAD_CTRL4 0x9d
+#define RT5645_CLSD_OUT_CTRL 0xa0
+/* Function - Digital */
+#define RT5645_ADC_EQ_CTRL1 0xae
+#define RT5645_ADC_EQ_CTRL2 0xaf
+#define RT5645_EQ_CTRL1 0xb0
+#define RT5645_EQ_CTRL2 0xb1
+#define RT5645_ALC_CTRL_1 0xb3
+#define RT5645_ALC_CTRL_2 0xb4
+#define RT5645_ALC_CTRL_3 0xb5
+#define RT5645_ALC_CTRL_4 0xb6
+#define RT5645_ALC_CTRL_5 0xb7
+#define RT5645_JD_CTRL 0xbb
+#define RT5645_IRQ_CTRL1 0xbc
+#define RT5645_IRQ_CTRL2 0xbd
+#define RT5645_IRQ_CTRL3 0xbe
+#define RT5645_INT_IRQ_ST 0xbf
+#define RT5645_GPIO_CTRL1 0xc0
+#define RT5645_GPIO_CTRL2 0xc1
+#define RT5645_GPIO_CTRL3 0xc2
+#define RT5645_BASS_BACK 0xcf
+#define RT5645_MP3_PLUS1 0xd0
+#define RT5645_MP3_PLUS2 0xd1
+#define RT5645_ADJ_HPF1 0xd3
+#define RT5645_ADJ_HPF2 0xd4
+#define RT5645_HP_CALIB_AMP_DET 0xd6
+#define RT5645_SV_ZCD1 0xd9
+#define RT5645_SV_ZCD2 0xda
+#define RT5645_IL_CMD 0xdb
+#define RT5645_IL_CMD2 0xdc
+#define RT5645_IL_CMD3 0xdd
+#define RT5650_4BTN_IL_CMD1 0xdf
+#define RT5650_4BTN_IL_CMD2 0xe0
+#define RT5645_DRC1_HL_CTRL1 0xe7
+#define RT5645_DRC2_HL_CTRL1 0xe9
+#define RT5645_MUTI_DRC_CTRL1 0xea
+#define RT5645_ADC_MONO_HP_CTRL1 0xec
+#define RT5645_ADC_MONO_HP_CTRL2 0xed
+#define RT5645_DRC2_CTRL1 0xf0
+#define RT5645_DRC2_CTRL2 0xf1
+#define RT5645_DRC2_CTRL3 0xf2
+#define RT5645_DRC2_CTRL4 0xf3
+#define RT5645_DRC2_CTRL5 0xf4
+#define RT5645_JD_CTRL3 0xf8
+#define RT5645_JD_CTRL4 0xf9
+/* General Control */
+#define RT5645_GEN_CTRL1 0xfa
+#define RT5645_GEN_CTRL2 0xfb
+#define RT5645_GEN_CTRL3 0xfc
+
+
+/* Index of Codec Private Register definition */
+#define RT5645_DIG_VOL 0x00
+#define RT5645_PR_ALC_CTRL_1 0x01
+#define RT5645_PR_ALC_CTRL_2 0x02
+#define RT5645_PR_ALC_CTRL_3 0x03
+#define RT5645_PR_ALC_CTRL_4 0x04
+#define RT5645_PR_ALC_CTRL_5 0x05
+#define RT5645_PR_ALC_CTRL_6 0x06
+#define RT5645_BIAS_CUR1 0x12
+#define RT5645_BIAS_CUR3 0x14
+#define RT5645_CLSD_INT_REG1 0x1c
+#define RT5645_MAMP_INT_REG2 0x37
+#define RT5645_CHOP_DAC_ADC 0x3d
+#define RT5645_MIXER_INT_REG 0x3f
+#define RT5645_3D_SPK 0x63
+#define RT5645_WND_1 0x6c
+#define RT5645_WND_2 0x6d
+#define RT5645_WND_3 0x6e
+#define RT5645_WND_4 0x6f
+#define RT5645_WND_5 0x70
+#define RT5645_WND_8 0x73
+#define RT5645_DIP_SPK_INF 0x75
+#define RT5645_HP_DCC_INT1 0x77
+#define RT5645_EQ_BW_LOP 0xa0
+#define RT5645_EQ_GN_LOP 0xa1
+#define RT5645_EQ_FC_BP1 0xa2
+#define RT5645_EQ_BW_BP1 0xa3
+#define RT5645_EQ_GN_BP1 0xa4
+#define RT5645_EQ_FC_BP2 0xa5
+#define RT5645_EQ_BW_BP2 0xa6
+#define RT5645_EQ_GN_BP2 0xa7
+#define RT5645_EQ_FC_BP3 0xa8
+#define RT5645_EQ_BW_BP3 0xa9
+#define RT5645_EQ_GN_BP3 0xaa
+#define RT5645_EQ_FC_BP4 0xab
+#define RT5645_EQ_BW_BP4 0xac
+#define RT5645_EQ_GN_BP4 0xad
+#define RT5645_EQ_FC_HIP1 0xae
+#define RT5645_EQ_GN_HIP1 0xaf
+#define RT5645_EQ_FC_HIP2 0xb0
+#define RT5645_EQ_BW_HIP2 0xb1
+#define RT5645_EQ_GN_HIP2 0xb2
+#define RT5645_EQ_PRE_VOL 0xb3
+#define RT5645_EQ_PST_VOL 0xb4
+
+
+/* global definition */
+#define RT5645_L_MUTE (0x1 << 15)
+#define RT5645_L_MUTE_SFT 15
+#define RT5645_VOL_L_MUTE (0x1 << 14)
+#define RT5645_VOL_L_SFT 14
+#define RT5645_R_MUTE (0x1 << 7)
+#define RT5645_R_MUTE_SFT 7
+#define RT5645_VOL_R_MUTE (0x1 << 6)
+#define RT5645_VOL_R_SFT 6
+#define RT5645_L_VOL_MASK (0x3f << 8)
+#define RT5645_L_VOL_SFT 8
+#define RT5645_R_VOL_MASK (0x3f)
+#define RT5645_R_VOL_SFT 0
+
+/* IN1 Control 1 (0x0a) */
+#define RT5645_CBJ_BST1_MASK (0xf << 12)
+#define RT5645_CBJ_BST1_SFT (12)
+#define RT5645_CBJ_JD_HP_EN (0x1 << 9)
+#define RT5645_CBJ_JD_MIC_EN (0x1 << 8)
+#define RT5645_CBJ_JD_MIC_SW_EN (0x1 << 7)
+#define RT5645_CBJ_MIC_SEL_R (0x1 << 6)
+#define RT5645_CBJ_MIC_SEL_L (0x1 << 5)
+#define RT5645_CBJ_MIC_SW (0x1 << 4)
+#define RT5645_CBJ_BST1_EN (0x1 << 2)
+
+/* IN1 Control 2 (0x0b) */
+#define RT5645_CBJ_MN_JD (0x1 << 12)
+#define RT5645_CAPLESS_EN (0x1 << 11)
+#define RT5645_CBJ_DET_MODE (0x1 << 7)
+
+/* IN1 Control 3 (0x0c) */
+#define RT5645_CBJ_TIE_G_L (0x1 << 15)
+#define RT5645_CBJ_TIE_G_R (0x1 << 14)
+
+/* IN2 Control (0x0d) */
+#define RT5645_BST_MASK1 (0xf<<12)
+#define RT5645_BST_SFT1 12
+#define RT5645_BST_MASK2 (0xf<<8)
+#define RT5645_BST_SFT2 8
+#define RT5645_IN_DF2 (0x1 << 6)
+#define RT5645_IN_SFT2 6
+
+/* INL and INR Volume Control (0x0f) */
+#define RT5645_INL_SEL_MASK (0x1 << 15)
+#define RT5645_INL_SEL_SFT 15
+#define RT5645_INL_SEL_IN4P (0x0 << 15)
+#define RT5645_INL_SEL_MONOP (0x1 << 15)
+#define RT5645_INL_VOL_MASK (0x1f << 8)
+#define RT5645_INL_VOL_SFT 8
+#define RT5645_INR_SEL_MASK (0x1 << 7)
+#define RT5645_INR_SEL_SFT 7
+#define RT5645_INR_SEL_IN4N (0x0 << 7)
+#define RT5645_INR_SEL_MONON (0x1 << 7)
+#define RT5645_INR_VOL_MASK (0x1f)
+#define RT5645_INR_VOL_SFT 0
+
+/* DAC1 Digital Volume (0x19) */
+#define RT5645_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5645_DAC_L1_VOL_SFT 8
+#define RT5645_DAC_R1_VOL_MASK (0xff)
+#define RT5645_DAC_R1_VOL_SFT 0
+
+/* DAC2 Digital Volume (0x1a) */
+#define RT5645_DAC_L2_VOL_MASK (0xff << 8)
+#define RT5645_DAC_L2_VOL_SFT 8
+#define RT5645_DAC_R2_VOL_MASK (0xff)
+#define RT5645_DAC_R2_VOL_SFT 0
+
+/* DAC2 Control (0x1b) */
+#define RT5645_M_DAC_L2_VOL (0x1 << 13)
+#define RT5645_M_DAC_L2_VOL_SFT 13
+#define RT5645_M_DAC_R2_VOL (0x1 << 12)
+#define RT5645_M_DAC_R2_VOL_SFT 12
+#define RT5645_DAC2_L_SEL_MASK (0x7 << 4)
+#define RT5645_DAC2_L_SEL_SFT 4
+#define RT5645_DAC2_R_SEL_MASK (0x7 << 0)
+#define RT5645_DAC2_R_SEL_SFT 0
+
+/* ADC Digital Volume Control (0x1c) */
+#define RT5645_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5645_ADC_L_VOL_SFT 8
+#define RT5645_ADC_R_VOL_MASK (0x7f)
+#define RT5645_ADC_R_VOL_SFT 0
+
+/* Mono ADC Digital Volume Control (0x1d) */
+#define RT5645_MONO_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5645_MONO_ADC_L_VOL_SFT 8
+#define RT5645_MONO_ADC_R_VOL_MASK (0x7f)
+#define RT5645_MONO_ADC_R_VOL_SFT 0
+
+/* ADC Boost Volume Control (0x1e) */
+#define RT5645_STO1_ADC_L_BST_MASK (0x3 << 14)
+#define RT5645_STO1_ADC_L_BST_SFT 14
+#define RT5645_STO1_ADC_R_BST_MASK (0x3 << 12)
+#define RT5645_STO1_ADC_R_BST_SFT 12
+#define RT5645_STO1_ADC_COMP_MASK (0x3 << 10)
+#define RT5645_STO1_ADC_COMP_SFT 10
+#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
+
+/* Stereo2 ADC Mixer Control (0x26) */
+#define RT5645_STO2_ADC_SRC_MASK (0x1 << 15)
+#define RT5645_STO2_ADC_SRC_SFT 15
+
+/* Stereo ADC Mixer Control (0x27) */
+#define RT5645_M_ADC_L1 (0x1 << 14)
+#define RT5645_M_ADC_L1_SFT 14
+#define RT5645_M_ADC_L2 (0x1 << 13)
+#define RT5645_M_ADC_L2_SFT 13
+#define RT5645_ADC_1_SRC_MASK (0x1 << 12)
+#define RT5645_ADC_1_SRC_SFT 12
+#define RT5645_ADC_1_SRC_ADC (0x1 << 12)
+#define RT5645_ADC_1_SRC_DACMIX (0x0 << 12)
+#define RT5645_ADC_2_SRC_MASK (0x1 << 11)
+#define RT5645_ADC_2_SRC_SFT 11
+#define RT5645_DMIC_SRC_MASK (0x1 << 8)
+#define RT5645_DMIC_SRC_SFT 8
+#define RT5645_M_ADC_R1 (0x1 << 6)
+#define RT5645_M_ADC_R1_SFT 6
+#define RT5645_M_ADC_R2 (0x1 << 5)
+#define RT5645_M_ADC_R2_SFT 5
+#define RT5645_DMIC3_SRC_MASK (0x1 << 1)
+#define RT5645_DMIC3_SRC_SFT 0
+
+/* Mono ADC Mixer Control (0x28) */
+#define RT5645_M_MONO_ADC_L1 (0x1 << 14)
+#define RT5645_M_MONO_ADC_L1_SFT 14
+#define RT5645_M_MONO_ADC_L2 (0x1 << 13)
+#define RT5645_M_MONO_ADC_L2_SFT 13
+#define RT5645_MONO_ADC_L1_SRC_MASK (0x1 << 12)
+#define RT5645_MONO_ADC_L1_SRC_SFT 12
+#define RT5645_MONO_ADC_L1_SRC_DACMIXL (0x0 << 12)
+#define RT5645_MONO_ADC_L1_SRC_ADCL (0x1 << 12)
+#define RT5645_MONO_ADC_L2_SRC_MASK (0x1 << 11)
+#define RT5645_MONO_ADC_L2_SRC_SFT 11
+#define RT5645_MONO_DMIC_L_SRC_MASK (0x1 << 8)
+#define RT5645_MONO_DMIC_L_SRC_SFT 8
+#define RT5645_M_MONO_ADC_R1 (0x1 << 6)
+#define RT5645_M_MONO_ADC_R1_SFT 6
+#define RT5645_M_MONO_ADC_R2 (0x1 << 5)
+#define RT5645_M_MONO_ADC_R2_SFT 5
+#define RT5645_MONO_ADC_R1_SRC_MASK (0x1 << 4)
+#define RT5645_MONO_ADC_R1_SRC_SFT 4
+#define RT5645_MONO_ADC_R1_SRC_ADCR (0x1 << 4)
+#define RT5645_MONO_ADC_R1_SRC_DACMIXR (0x0 << 4)
+#define RT5645_MONO_ADC_R2_SRC_MASK (0x1 << 3)
+#define RT5645_MONO_ADC_R2_SRC_SFT 3
+#define RT5645_MONO_DMIC_R_SRC_MASK (0x3)
+#define RT5645_MONO_DMIC_R_SRC_SFT 0
+
+/* ADC Mixer to DAC Mixer Control (0x29) */
+#define RT5645_M_ADCMIX_L (0x1 << 15)
+#define RT5645_M_ADCMIX_L_SFT 15
+#define RT5645_M_DAC1_L (0x1 << 14)
+#define RT5645_M_DAC1_L_SFT 14
+#define RT5645_DAC1_R_SEL_MASK (0x3 << 10)
+#define RT5645_DAC1_R_SEL_SFT 10
+#define RT5645_DAC1_R_SEL_IF1 (0x0 << 10)
+#define RT5645_DAC1_R_SEL_IF2 (0x1 << 10)
+#define RT5645_DAC1_R_SEL_IF3 (0x2 << 10)
+#define RT5645_DAC1_R_SEL_IF4 (0x3 << 10)
+#define RT5645_DAC1_L_SEL_MASK (0x3 << 8)
+#define RT5645_DAC1_L_SEL_SFT 8
+#define RT5645_DAC1_L_SEL_IF1 (0x0 << 8)
+#define RT5645_DAC1_L_SEL_IF2 (0x1 << 8)
+#define RT5645_DAC1_L_SEL_IF3 (0x2 << 8)
+#define RT5645_DAC1_L_SEL_IF4 (0x3 << 8)
+#define RT5645_M_ADCMIX_R (0x1 << 7)
+#define RT5645_M_ADCMIX_R_SFT 7
+#define RT5645_M_DAC1_R (0x1 << 6)
+#define RT5645_M_DAC1_R_SFT 6
+
+/* Stereo DAC Mixer Control (0x2a) */
+#define RT5645_M_DAC_L1 (0x1 << 14)
+#define RT5645_M_DAC_L1_SFT 14
+#define RT5645_DAC_L1_STO_L_VOL_MASK (0x1 << 13)
+#define RT5645_DAC_L1_STO_L_VOL_SFT 13
+#define RT5645_M_DAC_L2 (0x1 << 12)
+#define RT5645_M_DAC_L2_SFT 12
+#define RT5645_DAC_L2_STO_L_VOL_MASK (0x1 << 11)
+#define RT5645_DAC_L2_STO_L_VOL_SFT 11
+#define RT5645_M_ANC_DAC_L (0x1 << 10)
+#define RT5645_M_ANC_DAC_L_SFT 10
+#define RT5645_M_DAC_R1_STO_L (0x1 << 9)
+#define RT5645_M_DAC_R1_STO_L_SFT 9
+#define RT5645_DAC_R1_STO_L_VOL_MASK (0x1 << 8)
+#define RT5645_DAC_R1_STO_L_VOL_SFT 8
+#define RT5645_M_DAC_R1 (0x1 << 6)
+#define RT5645_M_DAC_R1_SFT 6
+#define RT5645_DAC_R1_STO_R_VOL_MASK (0x1 << 5)
+#define RT5645_DAC_R1_STO_R_VOL_SFT 5
+#define RT5645_M_DAC_R2 (0x1 << 4)
+#define RT5645_M_DAC_R2_SFT 4
+#define RT5645_DAC_R2_STO_R_VOL_MASK (0x1 << 3)
+#define RT5645_DAC_R2_STO_R_VOL_SFT 3
+#define RT5645_M_ANC_DAC_R (0x1 << 2)
+#define RT5645_M_ANC_DAC_R_SFT 2
+#define RT5645_M_DAC_L1_STO_R (0x1 << 1)
+#define RT5645_M_DAC_L1_STO_R_SFT 1
+#define RT5645_DAC_L1_STO_R_VOL_MASK (0x1)
+#define RT5645_DAC_L1_STO_R_VOL_SFT 0
+
+/* Mono DAC Mixer Control (0x2b) */
+#define RT5645_M_DAC_L1_MONO_L (0x1 << 14)
+#define RT5645_M_DAC_L1_MONO_L_SFT 14
+#define RT5645_DAC_L1_MONO_L_VOL_MASK (0x1 << 13)
+#define RT5645_DAC_L1_MONO_L_VOL_SFT 13
+#define RT5645_M_DAC_L2_MONO_L (0x1 << 12)
+#define RT5645_M_DAC_L2_MONO_L_SFT 12
+#define RT5645_DAC_L2_MONO_L_VOL_MASK (0x1 << 11)
+#define RT5645_DAC_L2_MONO_L_VOL_SFT 11
+#define RT5645_M_DAC_R2_MONO_L (0x1 << 10)
+#define RT5645_M_DAC_R2_MONO_L_SFT 10
+#define RT5645_DAC_R2_MONO_L_VOL_MASK (0x1 << 9)
+#define RT5645_DAC_R2_MONO_L_VOL_SFT 9
+#define RT5645_M_DAC_R1_MONO_R (0x1 << 6)
+#define RT5645_M_DAC_R1_MONO_R_SFT 6
+#define RT5645_DAC_R1_MONO_R_VOL_MASK (0x1 << 5)
+#define RT5645_DAC_R1_MONO_R_VOL_SFT 5
+#define RT5645_M_DAC_R2_MONO_R (0x1 << 4)
+#define RT5645_M_DAC_R2_MONO_R_SFT 4
+#define RT5645_DAC_R2_MONO_R_VOL_MASK (0x1 << 3)
+#define RT5645_DAC_R2_MONO_R_VOL_SFT 3
+#define RT5645_M_DAC_L2_MONO_R (0x1 << 2)
+#define RT5645_M_DAC_L2_MONO_R_SFT 2
+#define RT5645_DAC_L2_MONO_R_VOL_MASK (0x1 << 1)
+#define RT5645_DAC_L2_MONO_R_VOL_SFT 1
+
+/* Digital Mixer Control (0x2c) */
+#define RT5645_M_STO_L_DAC_L (0x1 << 15)
+#define RT5645_M_STO_L_DAC_L_SFT 15
+#define RT5645_STO_L_DAC_L_VOL_MASK (0x1 << 14)
+#define RT5645_STO_L_DAC_L_VOL_SFT 14
+#define RT5645_M_DAC_L2_DAC_L (0x1 << 13)
+#define RT5645_M_DAC_L2_DAC_L_SFT 13
+#define RT5645_DAC_L2_DAC_L_VOL_MASK (0x1 << 12)
+#define RT5645_DAC_L2_DAC_L_VOL_SFT 12
+#define RT5645_M_STO_R_DAC_R (0x1 << 11)
+#define RT5645_M_STO_R_DAC_R_SFT 11
+#define RT5645_STO_R_DAC_R_VOL_MASK (0x1 << 10)
+#define RT5645_STO_R_DAC_R_VOL_SFT 10
+#define RT5645_M_DAC_R2_DAC_R (0x1 << 9)
+#define RT5645_M_DAC_R2_DAC_R_SFT 9
+#define RT5645_DAC_R2_DAC_R_VOL_MASK (0x1 << 8)
+#define RT5645_DAC_R2_DAC_R_VOL_SFT 8
+#define RT5645_M_DAC_R2_DAC_L (0x1 << 7)
+#define RT5645_M_DAC_R2_DAC_L_SFT 7
+#define RT5645_DAC_R2_DAC_L_VOL_MASK (0x1 << 6)
+#define RT5645_DAC_R2_DAC_L_VOL_SFT 6
+#define RT5645_M_DAC_L2_DAC_R (0x1 << 5)
+#define RT5645_M_DAC_L2_DAC_R_SFT 5
+#define RT5645_DAC_L2_DAC_R_VOL_MASK (0x1 << 4)
+#define RT5645_DAC_L2_DAC_R_VOL_SFT 4
+
+/* Analog DAC1/2 Input Source Control (0x2d) */
+#define RT5650_A_DAC1_L_IN_SFT 3
+#define RT5650_A_DAC1_R_IN_SFT 2
+#define RT5650_A_DAC2_L_IN_SFT 1
+#define RT5650_A_DAC2_R_IN_SFT 0
+
+/* Digital Interface Data Control (0x2f) */
+#define RT5645_IF1_ADC2_IN_SEL (0x1 << 15)
+#define RT5645_IF1_ADC2_IN_SFT 15
+#define RT5645_IF2_ADC_IN_MASK (0x7 << 12)
+#define RT5645_IF2_ADC_IN_SFT 12
+#define RT5645_IF2_DAC_SEL_MASK (0x3 << 10)
+#define RT5645_IF2_DAC_SEL_SFT 10
+#define RT5645_IF2_ADC_SEL_MASK (0x3 << 8)
+#define RT5645_IF2_ADC_SEL_SFT 8
+#define RT5645_IF3_DAC_SEL_MASK (0x3 << 6)
+#define RT5645_IF3_DAC_SEL_SFT 6
+#define RT5645_IF3_ADC_SEL_MASK (0x3 << 4)
+#define RT5645_IF3_ADC_SEL_SFT 4
+#define RT5645_IF3_ADC_IN_MASK (0x7)
+#define RT5645_IF3_ADC_IN_SFT 0
+
+/* PDM Output Control (0x31) */
+#define RT5645_PDM1_L_MASK (0x1 << 15)
+#define RT5645_PDM1_L_SFT 15
+#define RT5645_M_PDM1_L (0x1 << 14)
+#define RT5645_M_PDM1_L_SFT 14
+#define RT5645_PDM1_R_MASK (0x1 << 13)
+#define RT5645_PDM1_R_SFT 13
+#define RT5645_M_PDM1_R (0x1 << 12)
+#define RT5645_M_PDM1_R_SFT 12
+#define RT5645_PDM2_L_MASK (0x1 << 11)
+#define RT5645_PDM2_L_SFT 11
+#define RT5645_M_PDM2_L (0x1 << 10)
+#define RT5645_M_PDM2_L_SFT 10
+#define RT5645_PDM2_R_MASK (0x1 << 9)
+#define RT5645_PDM2_R_SFT 9
+#define RT5645_M_PDM2_R (0x1 << 8)
+#define RT5645_M_PDM2_R_SFT 8
+#define RT5645_PDM2_BUSY (0x1 << 7)
+#define RT5645_PDM1_BUSY (0x1 << 6)
+#define RT5645_PDM_PATTERN (0x1 << 5)
+#define RT5645_PDM_GAIN (0x1 << 4)
+#define RT5645_PDM_DIV_MASK (0x3)
+
+/* REC Left Mixer Control 1 (0x3b) */
+#define RT5645_G_HP_L_RM_L_MASK (0x7 << 13)
+#define RT5645_G_HP_L_RM_L_SFT 13
+#define RT5645_G_IN_L_RM_L_MASK (0x7 << 10)
+#define RT5645_G_IN_L_RM_L_SFT 10
+#define RT5645_G_BST4_RM_L_MASK (0x7 << 7)
+#define RT5645_G_BST4_RM_L_SFT 7
+#define RT5645_G_BST3_RM_L_MASK (0x7 << 4)
+#define RT5645_G_BST3_RM_L_SFT 4
+#define RT5645_G_BST2_RM_L_MASK (0x7 << 1)
+#define RT5645_G_BST2_RM_L_SFT 1
+
+/* REC Left Mixer Control 2 (0x3c) */
+#define RT5645_G_BST1_RM_L_MASK (0x7 << 13)
+#define RT5645_G_BST1_RM_L_SFT 13
+#define RT5645_G_OM_L_RM_L_MASK (0x7 << 10)
+#define RT5645_G_OM_L_RM_L_SFT 10
+#define RT5645_M_MM_L_RM_L (0x1 << 6)
+#define RT5645_M_MM_L_RM_L_SFT 6
+#define RT5645_M_IN_L_RM_L (0x1 << 5)
+#define RT5645_M_IN_L_RM_L_SFT 5
+#define RT5645_M_HP_L_RM_L (0x1 << 4)
+#define RT5645_M_HP_L_RM_L_SFT 4
+#define RT5645_M_BST3_RM_L (0x1 << 3)
+#define RT5645_M_BST3_RM_L_SFT 3
+#define RT5645_M_BST2_RM_L (0x1 << 2)
+#define RT5645_M_BST2_RM_L_SFT 2
+#define RT5645_M_BST1_RM_L (0x1 << 1)
+#define RT5645_M_BST1_RM_L_SFT 1
+#define RT5645_M_OM_L_RM_L (0x1)
+#define RT5645_M_OM_L_RM_L_SFT 0
+
+/* REC Right Mixer Control 1 (0x3d) */
+#define RT5645_G_HP_R_RM_R_MASK (0x7 << 13)
+#define RT5645_G_HP_R_RM_R_SFT 13
+#define RT5645_G_IN_R_RM_R_MASK (0x7 << 10)
+#define RT5645_G_IN_R_RM_R_SFT 10
+#define RT5645_G_BST4_RM_R_MASK (0x7 << 7)
+#define RT5645_G_BST4_RM_R_SFT 7
+#define RT5645_G_BST3_RM_R_MASK (0x7 << 4)
+#define RT5645_G_BST3_RM_R_SFT 4
+#define RT5645_G_BST2_RM_R_MASK (0x7 << 1)
+#define RT5645_G_BST2_RM_R_SFT 1
+
+/* REC Right Mixer Control 2 (0x3e) */
+#define RT5645_G_BST1_RM_R_MASK (0x7 << 13)
+#define RT5645_G_BST1_RM_R_SFT 13
+#define RT5645_G_OM_R_RM_R_MASK (0x7 << 10)
+#define RT5645_G_OM_R_RM_R_SFT 10
+#define RT5645_M_MM_R_RM_R (0x1 << 6)
+#define RT5645_M_MM_R_RM_R_SFT 6
+#define RT5645_M_IN_R_RM_R (0x1 << 5)
+#define RT5645_M_IN_R_RM_R_SFT 5
+#define RT5645_M_HP_R_RM_R (0x1 << 4)
+#define RT5645_M_HP_R_RM_R_SFT 4
+#define RT5645_M_BST3_RM_R (0x1 << 3)
+#define RT5645_M_BST3_RM_R_SFT 3
+#define RT5645_M_BST2_RM_R (0x1 << 2)
+#define RT5645_M_BST2_RM_R_SFT 2
+#define RT5645_M_BST1_RM_R (0x1 << 1)
+#define RT5645_M_BST1_RM_R_SFT 1
+#define RT5645_M_OM_R_RM_R (0x1)
+#define RT5645_M_OM_R_RM_R_SFT 0
+
+/* HPOMIX Control (0x40) (0x42) */
+#define RT5645_M_BST1_HV (0x1 << 4)
+#define RT5645_M_BST1_HV_SFT 4
+#define RT5645_M_BST2_HV (0x1 << 4)
+#define RT5645_M_BST2_HV_SFT 4
+#define RT5645_M_BST3_HV (0x1 << 3)
+#define RT5645_M_BST3_HV_SFT 3
+#define RT5645_M_IN_HV (0x1 << 2)
+#define RT5645_M_IN_HV_SFT 2
+#define RT5645_M_DAC2_HV (0x1 << 1)
+#define RT5645_M_DAC2_HV_SFT 1
+#define RT5645_M_DAC1_HV (0x1 << 0)
+#define RT5645_M_DAC1_HV_SFT 0
+
+/* HPMIX Control (0x45) */
+#define RT5645_M_DAC1_HM (0x1 << 14)
+#define RT5645_M_DAC1_HM_SFT 14
+#define RT5645_M_HPVOL_HM (0x1 << 13)
+#define RT5645_M_HPVOL_HM_SFT 13
+#define RT5645_IRQ_PSV_MODE (0x1 << 12)
+
+/* SPK Left Mixer Control (0x46) */
+#define RT5645_G_RM_L_SM_L_MASK (0x3 << 14)
+#define RT5645_G_RM_L_SM_L_SFT 14
+#define RT5645_G_IN_L_SM_L_MASK (0x3 << 12)
+#define RT5645_G_IN_L_SM_L_SFT 12
+#define RT5645_G_DAC_L1_SM_L_MASK (0x3 << 10)
+#define RT5645_G_DAC_L1_SM_L_SFT 10
+#define RT5645_G_DAC_L2_SM_L_MASK (0x3 << 8)
+#define RT5645_G_DAC_L2_SM_L_SFT 8
+#define RT5645_G_OM_L_SM_L_MASK (0x3 << 6)
+#define RT5645_G_OM_L_SM_L_SFT 6
+#define RT5645_M_BST1_L_SM_L (0x1 << 5)
+#define RT5645_M_BST1_L_SM_L_SFT 5
+#define RT5645_M_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
+
+/* SPK Right Mixer Control (0x47) */
+#define RT5645_G_RM_R_SM_R_MASK (0x3 << 14)
+#define RT5645_G_RM_R_SM_R_SFT 14
+#define RT5645_G_IN_R_SM_R_MASK (0x3 << 12)
+#define RT5645_G_IN_R_SM_R_SFT 12
+#define RT5645_G_DAC_R1_SM_R_MASK (0x3 << 10)
+#define RT5645_G_DAC_R1_SM_R_SFT 10
+#define RT5645_G_DAC_R2_SM_R_MASK (0x3 << 8)
+#define RT5645_G_DAC_R2_SM_R_SFT 8
+#define RT5645_G_OM_R_SM_R_MASK (0x3 << 6)
+#define RT5645_G_OM_R_SM_R_SFT 6
+#define RT5645_M_BST2_R_SM_R (0x1 << 5)
+#define RT5645_M_BST2_R_SM_R_SFT 5
+#define RT5645_M_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
+
+/* SPOLMIX Control (0x48) */
+#define RT5645_M_DAC_L1_SPM_L (0x1 << 15)
+#define RT5645_M_DAC_L1_SPM_L_SFT 15
+#define RT5645_M_DAC_R1_SPM_L (0x1 << 14)
+#define RT5645_M_DAC_R1_SPM_L_SFT 14
+#define RT5645_M_SV_L_SPM_L (0x1 << 13)
+#define RT5645_M_SV_L_SPM_L_SFT 13
+#define RT5645_M_SV_R_SPM_L (0x1 << 12)
+#define RT5645_M_SV_R_SPM_L_SFT 12
+#define RT5645_M_BST3_SPM_L (0x1 << 11)
+#define RT5645_M_BST3_SPM_L_SFT 11
+#define RT5645_M_DAC_R1_SPM_R (0x1 << 2)
+#define RT5645_M_DAC_R1_SPM_R_SFT 2
+#define RT5645_M_BST3_SPM_R (0x1 << 1)
+#define RT5645_M_BST3_SPM_R_SFT 1
+#define RT5645_M_SV_R_SPM_R (0x1 << 0)
+#define RT5645_M_SV_R_SPM_R_SFT 0
+
+/* Mono Output Mixer Control (0x4c) */
+#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)
+#define RT5645_M_DAC_R1_MM_SFT 3
+#define RT5645_M_DAC_R2_MM (0x1 << 2)
+#define RT5645_M_DAC_R2_MM_SFT 2
+#define RT5645_M_DAC_L2_MM (0x1 << 1)
+#define RT5645_M_DAC_L2_MM_SFT 1
+#define RT5645_M_BST3_MM (0x1 << 0)
+#define RT5645_M_BST3_MM_SFT 0
+
+/* Output Left Mixer Control 1 (0x4d) */
+#define RT5645_G_BST3_OM_L_MASK (0x7 << 13)
+#define RT5645_G_BST3_OM_L_SFT 13
+#define RT5645_G_BST2_OM_L_MASK (0x7 << 10)
+#define RT5645_G_BST2_OM_L_SFT 10
+#define RT5645_G_BST1_OM_L_MASK (0x7 << 7)
+#define RT5645_G_BST1_OM_L_SFT 7
+#define RT5645_G_IN_L_OM_L_MASK (0x7 << 4)
+#define RT5645_G_IN_L_OM_L_SFT 4
+#define RT5645_G_RM_L_OM_L_MASK (0x7 << 1)
+#define RT5645_G_RM_L_OM_L_SFT 1
+
+/* Output Left Mixer Control 2 (0x4e) */
+#define RT5645_G_DAC_R2_OM_L_MASK (0x7 << 13)
+#define RT5645_G_DAC_R2_OM_L_SFT 13
+#define RT5645_G_DAC_L2_OM_L_MASK (0x7 << 10)
+#define RT5645_G_DAC_L2_OM_L_SFT 10
+#define RT5645_G_DAC_L1_OM_L_MASK (0x7 << 7)
+#define RT5645_G_DAC_L1_OM_L_SFT 7
+
+/* Output Left Mixer Control 3 (0x4f) */
+#define RT5645_M_BST3_OM_L (0x1 << 4)
+#define RT5645_M_BST3_OM_L_SFT 4
+#define RT5645_M_BST1_OM_L (0x1 << 3)
+#define RT5645_M_BST1_OM_L_SFT 3
+#define RT5645_M_IN_L_OM_L (0x1 << 2)
+#define RT5645_M_IN_L_OM_L_SFT 2
+#define RT5645_M_DAC_L2_OM_L (0x1 << 1)
+#define RT5645_M_DAC_L2_OM_L_SFT 1
+#define RT5645_M_DAC_L1_OM_L (0x1)
+#define RT5645_M_DAC_L1_OM_L_SFT 0
+
+/* Output Right Mixer Control 1 (0x50) */
+#define RT5645_G_BST4_OM_R_MASK (0x7 << 13)
+#define RT5645_G_BST4_OM_R_SFT 13
+#define RT5645_G_BST2_OM_R_MASK (0x7 << 10)
+#define RT5645_G_BST2_OM_R_SFT 10
+#define RT5645_G_BST1_OM_R_MASK (0x7 << 7)
+#define RT5645_G_BST1_OM_R_SFT 7
+#define RT5645_G_IN_R_OM_R_MASK (0x7 << 4)
+#define RT5645_G_IN_R_OM_R_SFT 4
+#define RT5645_G_RM_R_OM_R_MASK (0x7 << 1)
+#define RT5645_G_RM_R_OM_R_SFT 1
+
+/* Output Right Mixer Control 2 (0x51) */
+#define RT5645_G_DAC_L2_OM_R_MASK (0x7 << 13)
+#define RT5645_G_DAC_L2_OM_R_SFT 13
+#define RT5645_G_DAC_R2_OM_R_MASK (0x7 << 10)
+#define RT5645_G_DAC_R2_OM_R_SFT 10
+#define RT5645_G_DAC_R1_OM_R_MASK (0x7 << 7)
+#define RT5645_G_DAC_R1_OM_R_SFT 7
+
+/* Output Right Mixer Control 3 (0x52) */
+#define RT5645_M_BST3_OM_R (0x1 << 4)
+#define RT5645_M_BST3_OM_R_SFT 4
+#define RT5645_M_BST2_OM_R (0x1 << 3)
+#define RT5645_M_BST2_OM_R_SFT 3
+#define RT5645_M_IN_R_OM_R (0x1 << 2)
+#define RT5645_M_IN_R_OM_R_SFT 2
+#define RT5645_M_DAC_R2_OM_R (0x1 << 1)
+#define RT5645_M_DAC_R2_OM_R_SFT 1
+#define RT5645_M_DAC_R1_OM_R (0x1)
+#define RT5645_M_DAC_R1_OM_R_SFT 0
+
+/* LOUT Mixer Control (0x53) */
+#define RT5645_M_DAC_L1_LM (0x1 << 15)
+#define RT5645_M_DAC_L1_LM_SFT 15
+#define RT5645_M_DAC_R1_LM (0x1 << 14)
+#define RT5645_M_DAC_R1_LM_SFT 14
+#define RT5645_M_OV_L_LM (0x1 << 13)
+#define RT5645_M_OV_L_LM_SFT 13
+#define RT5645_M_OV_R_LM (0x1 << 12)
+#define RT5645_M_OV_R_LM_SFT 12
+#define RT5645_G_LOUTMIX_MASK (0x1 << 11)
+#define RT5645_G_LOUTMIX_SFT 11
+
+/* Power Management for Digital 1 (0x61) */
+#define RT5645_PWR_I2S1 (0x1 << 15)
+#define RT5645_PWR_I2S1_BIT 15
+#define RT5645_PWR_I2S2 (0x1 << 14)
+#define RT5645_PWR_I2S2_BIT 14
+#define RT5645_PWR_I2S3 (0x1 << 13)
+#define RT5645_PWR_I2S3_BIT 13
+#define RT5645_PWR_DAC_L1 (0x1 << 12)
+#define RT5645_PWR_DAC_L1_BIT 12
+#define RT5645_PWR_DAC_R1 (0x1 << 11)
+#define RT5645_PWR_DAC_R1_BIT 11
+#define RT5645_PWR_CLS_D_R (0x1 << 9)
+#define RT5645_PWR_CLS_D_R_BIT 9
+#define RT5645_PWR_CLS_D_L (0x1 << 8)
+#define RT5645_PWR_CLS_D_L_BIT 8
+#define RT5645_PWR_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)
+#define RT5645_PWR_DAC_R2_BIT 6
+#define RT5645_PWR_ADC_L (0x1 << 2)
+#define RT5645_PWR_ADC_L_BIT 2
+#define RT5645_PWR_ADC_R (0x1 << 1)
+#define RT5645_PWR_ADC_R_BIT 1
+#define RT5645_PWR_CLS_D (0x1)
+#define RT5645_PWR_CLS_D_BIT 0
+
+/* Power Management for Digital 2 (0x62) */
+#define RT5645_PWR_ADC_S1F (0x1 << 15)
+#define RT5645_PWR_ADC_S1F_BIT 15
+#define RT5645_PWR_ADC_MF_L (0x1 << 14)
+#define RT5645_PWR_ADC_MF_L_BIT 14
+#define RT5645_PWR_ADC_MF_R (0x1 << 13)
+#define RT5645_PWR_ADC_MF_R_BIT 13
+#define RT5645_PWR_I2S_DSP (0x1 << 12)
+#define RT5645_PWR_I2S_DSP_BIT 12
+#define RT5645_PWR_DAC_S1F (0x1 << 11)
+#define RT5645_PWR_DAC_S1F_BIT 11
+#define RT5645_PWR_DAC_MF_L (0x1 << 10)
+#define RT5645_PWR_DAC_MF_L_BIT 10
+#define RT5645_PWR_DAC_MF_R (0x1 << 9)
+#define RT5645_PWR_DAC_MF_R_BIT 9
+#define RT5645_PWR_PDM1 (0x1 << 7)
+#define RT5645_PWR_PDM1_BIT 7
+#define RT5645_PWR_PDM2 (0x1 << 6)
+#define RT5645_PWR_PDM2_BIT 6
+#define RT5645_PWR_IPTV (0x1 << 1)
+#define RT5645_PWR_IPTV_BIT 1
+#define RT5645_PWR_PAD (0x1)
+#define RT5645_PWR_PAD_BIT 0
+
+/* Power Management for Analog 1 (0x63) */
+#define RT5645_PWR_VREF1 (0x1 << 15)
+#define RT5645_PWR_VREF1_BIT 15
+#define RT5645_PWR_FV1 (0x1 << 14)
+#define RT5645_PWR_FV1_BIT 14
+#define RT5645_PWR_MB (0x1 << 13)
+#define RT5645_PWR_MB_BIT 13
+#define RT5645_PWR_LM (0x1 << 12)
+#define RT5645_PWR_LM_BIT 12
+#define RT5645_PWR_BG (0x1 << 11)
+#define RT5645_PWR_BG_BIT 11
+#define RT5645_PWR_MA (0x1 << 10)
+#define RT5645_PWR_MA_BIT 10
+#define RT5645_PWR_HP_L (0x1 << 7)
+#define RT5645_PWR_HP_L_BIT 7
+#define RT5645_PWR_HP_R (0x1 << 6)
+#define RT5645_PWR_HP_R_BIT 6
+#define RT5645_PWR_HA (0x1 << 5)
+#define RT5645_PWR_HA_BIT 5
+#define RT5645_PWR_VREF2 (0x1 << 4)
+#define RT5645_PWR_VREF2_BIT 4
+#define RT5645_PWR_FV2 (0x1 << 3)
+#define RT5645_PWR_FV2_BIT 3
+#define RT5645_LDO_SEL_MASK (0x3)
+#define RT5645_LDO_SEL_SFT 0
+
+/* Power Management for Analog 2 (0x64) */
+#define RT5645_PWR_BST1 (0x1 << 15)
+#define RT5645_PWR_BST1_BIT 15
+#define RT5645_PWR_BST2 (0x1 << 14)
+#define RT5645_PWR_BST2_BIT 14
+#define RT5645_PWR_BST3 (0x1 << 13)
+#define RT5645_PWR_BST3_BIT 13
+#define RT5645_PWR_BST4 (0x1 << 12)
+#define RT5645_PWR_BST4_BIT 12
+#define RT5645_PWR_MB1 (0x1 << 11)
+#define RT5645_PWR_MB1_BIT 11
+#define RT5645_PWR_MB2 (0x1 << 10)
+#define RT5645_PWR_MB2_BIT 10
+#define RT5645_PWR_PLL (0x1 << 9)
+#define RT5645_PWR_PLL_BIT 9
+#define RT5645_PWR_BST2_P (0x1 << 5)
+#define RT5645_PWR_BST2_P_BIT 5
+#define RT5645_PWR_BST3_P (0x1 << 4)
+#define RT5645_PWR_BST3_P_BIT 4
+#define RT5645_PWR_BST4_P (0x1 << 3)
+#define RT5645_PWR_BST4_P_BIT 3
+#define RT5645_PWR_JD1 (0x1 << 2)
+#define RT5645_PWR_JD1_BIT 2
+#define RT5645_PWR_JD (0x1 << 1)
+#define RT5645_PWR_JD_BIT 1
+
+/* Power Management for Mixer (0x65) */
+#define RT5645_PWR_OM_L (0x1 << 15)
+#define RT5645_PWR_OM_L_BIT 15
+#define RT5645_PWR_OM_R (0x1 << 14)
+#define RT5645_PWR_OM_R_BIT 14
+#define RT5645_PWR_SM_L (0x1 << 13)
+#define RT5645_PWR_SM_L_BIT 13
+#define RT5645_PWR_SM_R (0x1 << 12)
+#define RT5645_PWR_SM_R_BIT 12
+#define RT5645_PWR_RM_L (0x1 << 11)
+#define RT5645_PWR_RM_L_BIT 11
+#define RT5645_PWR_RM_R (0x1 << 10)
+#define RT5645_PWR_RM_R_BIT 10
+#define RT5645_PWR_MM (0x1 << 8)
+#define RT5645_PWR_MM_BIT 8
+#define RT5645_PWR_HM_L (0x1 << 7)
+#define RT5645_PWR_HM_L_BIT 7
+#define RT5645_PWR_HM_R (0x1 << 6)
+#define RT5645_PWR_HM_R_BIT 6
+#define RT5645_PWR_LDO2 (0x1 << 1)
+#define RT5645_PWR_LDO2_BIT 1
+
+/* Power Management for Volume (0x66) */
+#define RT5645_PWR_SV_L (0x1 << 15)
+#define RT5645_PWR_SV_L_BIT 15
+#define RT5645_PWR_SV_R (0x1 << 14)
+#define RT5645_PWR_SV_R_BIT 14
+#define RT5645_PWR_HV_L (0x1 << 11)
+#define RT5645_PWR_HV_L_BIT 11
+#define RT5645_PWR_HV_R (0x1 << 10)
+#define RT5645_PWR_HV_R_BIT 10
+#define RT5645_PWR_IN_L (0x1 << 9)
+#define RT5645_PWR_IN_L_BIT 9
+#define RT5645_PWR_IN_R (0x1 << 8)
+#define RT5645_PWR_IN_R_BIT 8
+#define RT5645_PWR_MIC_DET (0x1 << 5)
+#define RT5645_PWR_MIC_DET_BIT 5
+
+/* I2S1/2 Audio Serial Data Port Control (0x70 0x71) */
+#define RT5645_I2S_MS_MASK (0x1 << 15)
+#define RT5645_I2S_MS_SFT 15
+#define RT5645_I2S_MS_M (0x0 << 15)
+#define RT5645_I2S_MS_S (0x1 << 15)
+#define RT5645_I2S_O_CP_MASK (0x3 << 10)
+#define RT5645_I2S_O_CP_SFT 10
+#define RT5645_I2S_O_CP_OFF (0x0 << 10)
+#define RT5645_I2S_O_CP_U_LAW (0x1 << 10)
+#define RT5645_I2S_O_CP_A_LAW (0x2 << 10)
+#define RT5645_I2S_I_CP_MASK (0x3 << 8)
+#define RT5645_I2S_I_CP_SFT 8
+#define RT5645_I2S_I_CP_OFF (0x0 << 8)
+#define RT5645_I2S_I_CP_U_LAW (0x1 << 8)
+#define RT5645_I2S_I_CP_A_LAW (0x2 << 8)
+#define RT5645_I2S_BP_MASK (0x1 << 7)
+#define RT5645_I2S_BP_SFT 7
+#define RT5645_I2S_BP_NOR (0x0 << 7)
+#define RT5645_I2S_BP_INV (0x1 << 7)
+#define RT5645_I2S_DL_MASK (0x3 << 2)
+#define RT5645_I2S_DL_SFT 2
+#define RT5645_I2S_DL_16 (0x0 << 2)
+#define RT5645_I2S_DL_20 (0x1 << 2)
+#define RT5645_I2S_DL_24 (0x2 << 2)
+#define RT5645_I2S_DL_8 (0x3 << 2)
+#define RT5645_I2S_DF_MASK (0x3)
+#define RT5645_I2S_DF_SFT 0
+#define RT5645_I2S_DF_I2S (0x0)
+#define RT5645_I2S_DF_LEFT (0x1)
+#define RT5645_I2S_DF_PCM_A (0x2)
+#define RT5645_I2S_DF_PCM_B (0x3)
+
+/* I2S2 Audio Serial Data Port Control (0x71) */
+#define RT5645_I2S2_SDI_MASK (0x1 << 6)
+#define RT5645_I2S2_SDI_SFT 6
+#define RT5645_I2S2_SDI_I2S1 (0x0 << 6)
+#define RT5645_I2S2_SDI_I2S2 (0x1 << 6)
+
+/* ADC/DAC Clock Control 1 (0x73) */
+#define RT5645_I2S_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)
+#define RT5645_I2S_PD1_2 (0x1 << 12)
+#define RT5645_I2S_PD1_3 (0x2 << 12)
+#define RT5645_I2S_PD1_4 (0x3 << 12)
+#define RT5645_I2S_PD1_6 (0x4 << 12)
+#define RT5645_I2S_PD1_8 (0x5 << 12)
+#define RT5645_I2S_PD1_12 (0x6 << 12)
+#define RT5645_I2S_PD1_16 (0x7 << 12)
+#define RT5645_I2S_BCLK_MS2_MASK (0x1 << 11)
+#define RT5645_I2S_BCLK_MS2_SFT 11
+#define RT5645_I2S_BCLK_MS2_32 (0x0 << 11)
+#define RT5645_I2S_BCLK_MS2_64 (0x1 << 11)
+#define RT5645_I2S_PD2_MASK (0x7 << 8)
+#define RT5645_I2S_PD2_SFT 8
+#define RT5645_I2S_PD2_1 (0x0 << 8)
+#define RT5645_I2S_PD2_2 (0x1 << 8)
+#define RT5645_I2S_PD2_3 (0x2 << 8)
+#define RT5645_I2S_PD2_4 (0x3 << 8)
+#define RT5645_I2S_PD2_6 (0x4 << 8)
+#define RT5645_I2S_PD2_8 (0x5 << 8)
+#define RT5645_I2S_PD2_12 (0x6 << 8)
+#define RT5645_I2S_PD2_16 (0x7 << 8)
+#define RT5645_I2S_BCLK_MS3_MASK (0x1 << 7)
+#define RT5645_I2S_BCLK_MS3_SFT 7
+#define RT5645_I2S_BCLK_MS3_32 (0x0 << 7)
+#define RT5645_I2S_BCLK_MS3_64 (0x1 << 7)
+#define RT5645_I2S_PD3_MASK (0x7 << 4)
+#define RT5645_I2S_PD3_SFT 4
+#define RT5645_I2S_PD3_1 (0x0 << 4)
+#define RT5645_I2S_PD3_2 (0x1 << 4)
+#define RT5645_I2S_PD3_3 (0x2 << 4)
+#define RT5645_I2S_PD3_4 (0x3 << 4)
+#define RT5645_I2S_PD3_6 (0x4 << 4)
+#define RT5645_I2S_PD3_8 (0x5 << 4)
+#define RT5645_I2S_PD3_12 (0x6 << 4)
+#define RT5645_I2S_PD3_16 (0x7 << 4)
+#define RT5645_DAC_OSR_MASK (0x3 << 2)
+#define RT5645_DAC_OSR_SFT 2
+#define RT5645_DAC_OSR_128 (0x0 << 2)
+#define RT5645_DAC_OSR_64 (0x1 << 2)
+#define RT5645_DAC_OSR_32 (0x2 << 2)
+#define RT5645_DAC_OSR_16 (0x3 << 2)
+#define RT5645_ADC_OSR_MASK (0x3)
+#define RT5645_ADC_OSR_SFT 0
+#define RT5645_ADC_OSR_128 (0x0)
+#define RT5645_ADC_OSR_64 (0x1)
+#define RT5645_ADC_OSR_32 (0x2)
+#define RT5645_ADC_OSR_16 (0x3)
+
+/* ADC/DAC Clock Control 2 (0x74) */
+#define RT5645_DAC_L_OSR_MASK (0x3 << 14)
+#define RT5645_DAC_L_OSR_SFT 14
+#define RT5645_DAC_L_OSR_128 (0x0 << 14)
+#define RT5645_DAC_L_OSR_64 (0x1 << 14)
+#define RT5645_DAC_L_OSR_32 (0x2 << 14)
+#define RT5645_DAC_L_OSR_16 (0x3 << 14)
+#define RT5645_ADC_R_OSR_MASK (0x3 << 12)
+#define RT5645_ADC_R_OSR_SFT 12
+#define RT5645_ADC_R_OSR_128 (0x0 << 12)
+#define RT5645_ADC_R_OSR_64 (0x1 << 12)
+#define RT5645_ADC_R_OSR_32 (0x2 << 12)
+#define RT5645_ADC_R_OSR_16 (0x3 << 12)
+#define RT5645_DAHPF_EN (0x1 << 11)
+#define RT5645_DAHPF_EN_SFT 11
+#define RT5645_ADHPF_EN (0x1 << 10)
+#define RT5645_ADHPF_EN_SFT 10
+
+/* Digital Microphone Control (0x75) */
+#define RT5645_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5645_DMIC_1_EN_SFT 15
+#define RT5645_DMIC_1_DIS (0x0 << 15)
+#define RT5645_DMIC_1_EN (0x1 << 15)
+#define RT5645_DMIC_2_EN_MASK (0x1 << 14)
+#define RT5645_DMIC_2_EN_SFT 14
+#define RT5645_DMIC_2_DIS (0x0 << 14)
+#define RT5645_DMIC_2_EN (0x1 << 14)
+#define RT5645_DMIC_1L_LH_MASK (0x1 << 13)
+#define RT5645_DMIC_1L_LH_SFT 13
+#define RT5645_DMIC_1L_LH_FALLING (0x0 << 13)
+#define RT5645_DMIC_1L_LH_RISING (0x1 << 13)
+#define RT5645_DMIC_1R_LH_MASK (0x1 << 12)
+#define RT5645_DMIC_1R_LH_SFT 12
+#define RT5645_DMIC_1R_LH_FALLING (0x0 << 12)
+#define RT5645_DMIC_1R_LH_RISING (0x1 << 12)
+#define RT5645_DMIC_2_DP_MASK (0x3 << 10)
+#define RT5645_DMIC_2_DP_SFT 10
+#define RT5645_DMIC_2_DP_GPIO6 (0x0 << 10)
+#define RT5645_DMIC_2_DP_GPIO10 (0x1 << 10)
+#define RT5645_DMIC_2_DP_GPIO12 (0x2 << 10)
+#define RT5645_DMIC_2_DP_IN2P (0x3 << 10)
+#define RT5645_DMIC_2L_LH_MASK (0x1 << 9)
+#define RT5645_DMIC_2L_LH_SFT 9
+#define RT5645_DMIC_2L_LH_FALLING (0x0 << 9)
+#define RT5645_DMIC_2L_LH_RISING (0x1 << 9)
+#define RT5645_DMIC_2R_LH_MASK (0x1 << 8)
+#define RT5645_DMIC_2R_LH_SFT 8
+#define RT5645_DMIC_2R_LH_FALLING (0x0 << 8)
+#define RT5645_DMIC_2R_LH_RISING (0x1 << 8)
+#define RT5645_DMIC_CLK_MASK (0x7 << 5)
+#define RT5645_DMIC_CLK_SFT 5
+#define RT5645_DMIC_3_EN_MASK (0x1 << 4)
+#define RT5645_DMIC_3_EN_SFT 4
+#define RT5645_DMIC_3_DIS (0x0 << 4)
+#define RT5645_DMIC_3_EN (0x1 << 4)
+#define RT5645_DMIC_1_DP_MASK (0x3 << 0)
+#define RT5645_DMIC_1_DP_SFT 0
+#define RT5645_DMIC_1_DP_GPIO5 (0x0 << 0)
+#define RT5645_DMIC_1_DP_IN2N (0x1 << 0)
+#define RT5645_DMIC_1_DP_GPIO11 (0x2 << 0)
+
+/* TDM Control 1 (0x77) */
+#define RT5645_IF1_ADC_IN_MASK (0x3 << 8)
+#define RT5645_IF1_ADC_IN_SFT 8
+
+/* Global Clock Control (0x80) */
+#define RT5645_SCLK_SRC_MASK (0x3 << 14)
+#define RT5645_SCLK_SRC_SFT 14
+#define RT5645_SCLK_SRC_MCLK (0x0 << 14)
+#define RT5645_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5645_SCLK_SRC_RCCLK (0x2 << 14) /* 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_PLL1_PD_MASK (0x1 << 3)
+#define RT5645_PLL1_PD_SFT 3
+#define RT5645_PLL1_PD_1 (0x0 << 3)
+#define RT5645_PLL1_PD_2 (0x1 << 3)
+
+#define RT5645_PLL_INP_MAX 40000000
+#define RT5645_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x81) */
+#define RT5645_PLL_N_MAX 0x1ff
+#define RT5645_PLL_N_MASK (RT5645_PLL_N_MAX << 7)
+#define RT5645_PLL_N_SFT 7
+#define RT5645_PLL_K_MAX 0x1f
+#define RT5645_PLL_K_MASK (RT5645_PLL_K_MAX)
+#define RT5645_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x82) */
+#define RT5645_PLL_M_MAX 0xf
+#define RT5645_PLL_M_MASK (RT5645_PLL_M_MAX << 12)
+#define RT5645_PLL_M_SFT 12
+#define RT5645_PLL_M_BP (0x1 << 11)
+#define RT5645_PLL_M_BP_SFT 11
+
+/* ASRC Control 1 (0x83) */
+#define RT5645_STO_T_MASK (0x1 << 15)
+#define RT5645_STO_T_SFT 15
+#define RT5645_STO_T_SCLK (0x0 << 15)
+#define RT5645_STO_T_LRCK1 (0x1 << 15)
+#define RT5645_M1_T_MASK (0x1 << 14)
+#define RT5645_M1_T_SFT 14
+#define RT5645_M1_T_I2S2 (0x0 << 14)
+#define RT5645_M1_T_I2S2_D3 (0x1 << 14)
+#define RT5645_I2S2_F_MASK (0x1 << 12)
+#define RT5645_I2S2_F_SFT 12
+#define RT5645_I2S2_F_I2S2_D2 (0x0 << 12)
+#define RT5645_I2S2_F_I2S1_TCLK (0x1 << 12)
+#define RT5645_DMIC_1_M_MASK (0x1 << 9)
+#define RT5645_DMIC_1_M_SFT 9
+#define RT5645_DMIC_1_M_NOR (0x0 << 9)
+#define RT5645_DMIC_1_M_ASYN (0x1 << 9)
+#define RT5645_DMIC_2_M_MASK (0x1 << 8)
+#define RT5645_DMIC_2_M_SFT 8
+#define RT5645_DMIC_2_M_NOR (0x0 << 8)
+#define RT5645_DMIC_2_M_ASYN (0x1 << 8)
+
+/* ASRC clock source selection (0x84, 0x85) */
+#define RT5645_CLK_SEL_SYS (0x0)
+#define RT5645_CLK_SEL_I2S1_ASRC (0x1)
+#define RT5645_CLK_SEL_I2S2_ASRC (0x2)
+#define RT5645_CLK_SEL_SYS2 (0x5)
+
+/* ASRC Control 2 (0x84) */
+#define RT5645_DA_STO_CLK_SEL_MASK (0xf << 12)
+#define RT5645_DA_STO_CLK_SEL_SFT 12
+#define RT5645_DA_MONOL_CLK_SEL_MASK (0xf << 8)
+#define RT5645_DA_MONOL_CLK_SEL_SFT 8
+#define RT5645_DA_MONOR_CLK_SEL_MASK (0xf << 4)
+#define RT5645_DA_MONOR_CLK_SEL_SFT 4
+#define RT5645_AD_STO1_CLK_SEL_MASK (0xf << 0)
+#define RT5645_AD_STO1_CLK_SEL_SFT 0
+
+/* ASRC Control 3 (0x85) */
+#define RT5645_AD_MONOL_CLK_SEL_MASK (0xf << 4)
+#define RT5645_AD_MONOL_CLK_SEL_SFT 4
+#define RT5645_AD_MONOR_CLK_SEL_MASK (0xf << 0)
+#define RT5645_AD_MONOR_CLK_SEL_SFT 0
+
+/* ASRC Control 4 (0x89) */
+#define RT5645_I2S1_PD_MASK (0x7 << 12)
+#define RT5645_I2S1_PD_SFT 12
+#define RT5645_I2S2_PD_MASK (0x7 << 8)
+#define RT5645_I2S2_PD_SFT 8
+
+/* HPOUT Over Current Detection (0x8b) */
+#define RT5645_HP_OVCD_MASK (0x1 << 10)
+#define RT5645_HP_OVCD_SFT 10
+#define RT5645_HP_OVCD_DIS (0x0 << 10)
+#define RT5645_HP_OVCD_EN (0x1 << 10)
+#define RT5645_HP_OC_TH_MASK (0x3 << 8)
+#define RT5645_HP_OC_TH_SFT 8
+#define RT5645_HP_OC_TH_90 (0x0 << 8)
+#define RT5645_HP_OC_TH_105 (0x1 << 8)
+#define RT5645_HP_OC_TH_120 (0x2 << 8)
+#define RT5645_HP_OC_TH_135 (0x3 << 8)
+
+/* Class D Over Current Control (0x8c) */
+#define RT5645_CLSD_OC_MASK (0x1 << 9)
+#define RT5645_CLSD_OC_SFT 9
+#define RT5645_CLSD_OC_PU (0x0 << 9)
+#define RT5645_CLSD_OC_PD (0x1 << 9)
+#define RT5645_AUTO_PD_MASK (0x1 << 8)
+#define RT5645_AUTO_PD_SFT 8
+#define RT5645_AUTO_PD_DIS (0x0 << 8)
+#define RT5645_AUTO_PD_EN (0x1 << 8)
+#define RT5645_CLSD_OC_TH_MASK (0x3f)
+#define RT5645_CLSD_OC_TH_SFT 0
+
+/* Class D Output Control (0x8d) */
+#define RT5645_CLSD_RATIO_MASK (0xf << 12)
+#define RT5645_CLSD_RATIO_SFT 12
+#define RT5645_CLSD_OM_MASK (0x1 << 11)
+#define RT5645_CLSD_OM_SFT 11
+#define RT5645_CLSD_OM_MONO (0x0 << 11)
+#define RT5645_CLSD_OM_STO (0x1 << 11)
+#define RT5645_CLSD_SCH_MASK (0x1 << 10)
+#define RT5645_CLSD_SCH_SFT 10
+#define RT5645_CLSD_SCH_L (0x0 << 10)
+#define RT5645_CLSD_SCH_S (0x1 << 10)
+
+/* Depop Mode Control 1 (0x8e) */
+#define RT5645_SMT_TRIG_MASK (0x1 << 15)
+#define RT5645_SMT_TRIG_SFT 15
+#define RT5645_SMT_TRIG_DIS (0x0 << 15)
+#define RT5645_SMT_TRIG_EN (0x1 << 15)
+#define RT5645_HP_L_SMT_MASK (0x1 << 9)
+#define RT5645_HP_L_SMT_SFT 9
+#define RT5645_HP_L_SMT_DIS (0x0 << 9)
+#define RT5645_HP_L_SMT_EN (0x1 << 9)
+#define RT5645_HP_R_SMT_MASK (0x1 << 8)
+#define RT5645_HP_R_SMT_SFT 8
+#define RT5645_HP_R_SMT_DIS (0x0 << 8)
+#define RT5645_HP_R_SMT_EN (0x1 << 8)
+#define RT5645_HP_CD_PD_MASK (0x1 << 7)
+#define RT5645_HP_CD_PD_SFT 7
+#define RT5645_HP_CD_PD_DIS (0x0 << 7)
+#define RT5645_HP_CD_PD_EN (0x1 << 7)
+#define RT5645_RSTN_MASK (0x1 << 6)
+#define RT5645_RSTN_SFT 6
+#define RT5645_RSTN_DIS (0x0 << 6)
+#define RT5645_RSTN_EN (0x1 << 6)
+#define RT5645_RSTP_MASK (0x1 << 5)
+#define RT5645_RSTP_SFT 5
+#define RT5645_RSTP_DIS (0x0 << 5)
+#define RT5645_RSTP_EN (0x1 << 5)
+#define RT5645_HP_CO_MASK (0x1 << 4)
+#define RT5645_HP_CO_SFT 4
+#define RT5645_HP_CO_DIS (0x0 << 4)
+#define RT5645_HP_CO_EN (0x1 << 4)
+#define RT5645_HP_CP_MASK (0x1 << 3)
+#define RT5645_HP_CP_SFT 3
+#define RT5645_HP_CP_PD (0x0 << 3)
+#define RT5645_HP_CP_PU (0x1 << 3)
+#define RT5645_HP_SG_MASK (0x1 << 2)
+#define RT5645_HP_SG_SFT 2
+#define RT5645_HP_SG_DIS (0x0 << 2)
+#define RT5645_HP_SG_EN (0x1 << 2)
+#define RT5645_HP_DP_MASK (0x1 << 1)
+#define RT5645_HP_DP_SFT 1
+#define RT5645_HP_DP_PD (0x0 << 1)
+#define RT5645_HP_DP_PU (0x1 << 1)
+#define RT5645_HP_CB_MASK (0x1)
+#define RT5645_HP_CB_SFT 0
+#define RT5645_HP_CB_PD (0x0)
+#define RT5645_HP_CB_PU (0x1)
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5645_DEPOP_MASK (0x1 << 13)
+#define RT5645_DEPOP_SFT 13
+#define RT5645_DEPOP_AUTO (0x0 << 13)
+#define RT5645_DEPOP_MAN (0x1 << 13)
+#define RT5645_RAMP_MASK (0x1 << 12)
+#define RT5645_RAMP_SFT 12
+#define RT5645_RAMP_DIS (0x0 << 12)
+#define RT5645_RAMP_EN (0x1 << 12)
+#define RT5645_BPS_MASK (0x1 << 11)
+#define RT5645_BPS_SFT 11
+#define RT5645_BPS_DIS (0x0 << 11)
+#define RT5645_BPS_EN (0x1 << 11)
+#define RT5645_FAST_UPDN_MASK (0x1 << 10)
+#define RT5645_FAST_UPDN_SFT 10
+#define RT5645_FAST_UPDN_DIS (0x0 << 10)
+#define RT5645_FAST_UPDN_EN (0x1 << 10)
+#define RT5645_MRES_MASK (0x3 << 8)
+#define RT5645_MRES_SFT 8
+#define RT5645_MRES_15MO (0x0 << 8)
+#define RT5645_MRES_25MO (0x1 << 8)
+#define RT5645_MRES_35MO (0x2 << 8)
+#define RT5645_MRES_45MO (0x3 << 8)
+#define RT5645_VLO_MASK (0x1 << 7)
+#define RT5645_VLO_SFT 7
+#define RT5645_VLO_3V (0x0 << 7)
+#define RT5645_VLO_32V (0x1 << 7)
+#define RT5645_DIG_DP_MASK (0x1 << 6)
+#define RT5645_DIG_DP_SFT 6
+#define RT5645_DIG_DP_DIS (0x0 << 6)
+#define RT5645_DIG_DP_EN (0x1 << 6)
+#define RT5645_DP_TH_MASK (0x3 << 4)
+#define RT5645_DP_TH_SFT 4
+
+/* Depop Mode Control 3 (0x90) */
+#define RT5645_CP_SYS_MASK (0x7 << 12)
+#define RT5645_CP_SYS_SFT 12
+#define RT5645_CP_FQ1_MASK (0x7 << 8)
+#define RT5645_CP_FQ1_SFT 8
+#define RT5645_CP_FQ2_MASK (0x7 << 4)
+#define RT5645_CP_FQ2_SFT 4
+#define RT5645_CP_FQ3_MASK (0x7)
+#define RT5645_CP_FQ3_SFT 0
+#define RT5645_CP_FQ_1_5_KHZ 0
+#define RT5645_CP_FQ_3_KHZ 1
+#define RT5645_CP_FQ_6_KHZ 2
+#define RT5645_CP_FQ_12_KHZ 3
+#define RT5645_CP_FQ_24_KHZ 4
+#define RT5645_CP_FQ_48_KHZ 5
+#define RT5645_CP_FQ_96_KHZ 6
+#define RT5645_CP_FQ_192_KHZ 7
+
+/* PV detection and SPK gain control (0x92) */
+#define RT5645_PVDD_DET_MASK (0x1 << 15)
+#define RT5645_PVDD_DET_SFT 15
+#define RT5645_PVDD_DET_DIS (0x0 << 15)
+#define RT5645_PVDD_DET_EN (0x1 << 15)
+#define RT5645_SPK_AG_MASK (0x1 << 14)
+#define RT5645_SPK_AG_SFT 14
+#define RT5645_SPK_AG_DIS (0x0 << 14)
+#define RT5645_SPK_AG_EN (0x1 << 14)
+
+/* Micbias Control (0x93) */
+#define RT5645_MIC1_BS_MASK (0x1 << 15)
+#define RT5645_MIC1_BS_SFT 15
+#define RT5645_MIC1_BS_9AV (0x0 << 15)
+#define RT5645_MIC1_BS_75AV (0x1 << 15)
+#define RT5645_MIC2_BS_MASK (0x1 << 14)
+#define RT5645_MIC2_BS_SFT 14
+#define RT5645_MIC2_BS_9AV (0x0 << 14)
+#define RT5645_MIC2_BS_75AV (0x1 << 14)
+#define RT5645_MIC1_CLK_MASK (0x1 << 13)
+#define RT5645_MIC1_CLK_SFT 13
+#define RT5645_MIC1_CLK_DIS (0x0 << 13)
+#define RT5645_MIC1_CLK_EN (0x1 << 13)
+#define RT5645_MIC2_CLK_MASK (0x1 << 12)
+#define RT5645_MIC2_CLK_SFT 12
+#define RT5645_MIC2_CLK_DIS (0x0 << 12)
+#define RT5645_MIC2_CLK_EN (0x1 << 12)
+#define RT5645_MIC1_OVCD_MASK (0x1 << 11)
+#define RT5645_MIC1_OVCD_SFT 11
+#define RT5645_MIC1_OVCD_DIS (0x0 << 11)
+#define RT5645_MIC1_OVCD_EN (0x1 << 11)
+#define RT5645_MIC1_OVTH_MASK (0x3 << 9)
+#define RT5645_MIC1_OVTH_SFT 9
+#define RT5645_MIC1_OVTH_600UA (0x0 << 9)
+#define RT5645_MIC1_OVTH_1500UA (0x1 << 9)
+#define RT5645_MIC1_OVTH_2000UA (0x2 << 9)
+#define RT5645_MIC2_OVCD_MASK (0x1 << 8)
+#define RT5645_MIC2_OVCD_SFT 8
+#define RT5645_MIC2_OVCD_DIS (0x0 << 8)
+#define RT5645_MIC2_OVCD_EN (0x1 << 8)
+#define RT5645_MIC2_OVTH_MASK (0x3 << 6)
+#define RT5645_MIC2_OVTH_SFT 6
+#define RT5645_MIC2_OVTH_600UA (0x0 << 6)
+#define RT5645_MIC2_OVTH_1500UA (0x1 << 6)
+#define RT5645_MIC2_OVTH_2000UA (0x2 << 6)
+#define RT5645_PWR_MB_MASK (0x1 << 5)
+#define RT5645_PWR_MB_SFT 5
+#define RT5645_PWR_MB_PD (0x0 << 5)
+#define RT5645_PWR_MB_PU (0x1 << 5)
+#define RT5645_PWR_CLK25M_MASK (0x1 << 4)
+#define RT5645_PWR_CLK25M_SFT 4
+#define RT5645_PWR_CLK25M_PD (0x0 << 4)
+#define RT5645_PWR_CLK25M_PU (0x1 << 4)
+#define RT5645_IRQ_CLK_MCLK (0x0 << 3)
+#define RT5645_IRQ_CLK_INT (0x1 << 3)
+#define RT5645_JD1_MODE_MASK (0x3 << 0)
+#define RT5645_JD1_MODE_0 (0x0 << 0)
+#define RT5645_JD1_MODE_1 (0x1 << 0)
+#define RT5645_JD1_MODE_2 (0x2 << 0)
+
+/* VAD Control 4 (0x9d) */
+#define RT5645_VAD_SEL_MASK (0x3 << 8)
+#define RT5645_VAD_SEL_SFT 8
+
+/* EQ Control 1 (0xb0) */
+#define RT5645_EQ_SRC_MASK (0x1 << 15)
+#define RT5645_EQ_SRC_SFT 15
+#define RT5645_EQ_SRC_DAC (0x0 << 15)
+#define RT5645_EQ_SRC_ADC (0x1 << 15)
+#define RT5645_EQ_UPD (0x1 << 14)
+#define RT5645_EQ_UPD_BIT 14
+#define RT5645_EQ_CD_MASK (0x1 << 13)
+#define RT5645_EQ_CD_SFT 13
+#define RT5645_EQ_CD_DIS (0x0 << 13)
+#define RT5645_EQ_CD_EN (0x1 << 13)
+#define RT5645_EQ_DITH_MASK (0x3 << 8)
+#define RT5645_EQ_DITH_SFT 8
+#define RT5645_EQ_DITH_NOR (0x0 << 8)
+#define RT5645_EQ_DITH_LSB (0x1 << 8)
+#define RT5645_EQ_DITH_LSB_1 (0x2 << 8)
+#define RT5645_EQ_DITH_LSB_2 (0x3 << 8)
+
+/* EQ Control 2 (0xb1) */
+#define RT5645_EQ_HPF1_M_MASK (0x1 << 8)
+#define RT5645_EQ_HPF1_M_SFT 8
+#define RT5645_EQ_HPF1_M_HI (0x0 << 8)
+#define RT5645_EQ_HPF1_M_1ST (0x1 << 8)
+#define RT5645_EQ_LPF1_M_MASK (0x1 << 7)
+#define RT5645_EQ_LPF1_M_SFT 7
+#define RT5645_EQ_LPF1_M_LO (0x0 << 7)
+#define RT5645_EQ_LPF1_M_1ST (0x1 << 7)
+#define RT5645_EQ_HPF2_MASK (0x1 << 6)
+#define RT5645_EQ_HPF2_SFT 6
+#define RT5645_EQ_HPF2_DIS (0x0 << 6)
+#define RT5645_EQ_HPF2_EN (0x1 << 6)
+#define RT5645_EQ_HPF1_MASK (0x1 << 5)
+#define RT5645_EQ_HPF1_SFT 5
+#define RT5645_EQ_HPF1_DIS (0x0 << 5)
+#define RT5645_EQ_HPF1_EN (0x1 << 5)
+#define RT5645_EQ_BPF4_MASK (0x1 << 4)
+#define RT5645_EQ_BPF4_SFT 4
+#define RT5645_EQ_BPF4_DIS (0x0 << 4)
+#define RT5645_EQ_BPF4_EN (0x1 << 4)
+#define RT5645_EQ_BPF3_MASK (0x1 << 3)
+#define RT5645_EQ_BPF3_SFT 3
+#define RT5645_EQ_BPF3_DIS (0x0 << 3)
+#define RT5645_EQ_BPF3_EN (0x1 << 3)
+#define RT5645_EQ_BPF2_MASK (0x1 << 2)
+#define RT5645_EQ_BPF2_SFT 2
+#define RT5645_EQ_BPF2_DIS (0x0 << 2)
+#define RT5645_EQ_BPF2_EN (0x1 << 2)
+#define RT5645_EQ_BPF1_MASK (0x1 << 1)
+#define RT5645_EQ_BPF1_SFT 1
+#define RT5645_EQ_BPF1_DIS (0x0 << 1)
+#define RT5645_EQ_BPF1_EN (0x1 << 1)
+#define RT5645_EQ_LPF_MASK (0x1)
+#define RT5645_EQ_LPF_SFT 0
+#define RT5645_EQ_LPF_DIS (0x0)
+#define RT5645_EQ_LPF_EN (0x1)
+#define RT5645_EQ_CTRL_MASK (0x7f)
+
+/* Memory Test (0xb2) */
+#define RT5645_MT_MASK (0x1 << 15)
+#define RT5645_MT_SFT 15
+#define RT5645_MT_DIS (0x0 << 15)
+#define RT5645_MT_EN (0x1 << 15)
+
+/* DRC/AGC Control 1 (0xb4) */
+#define RT5645_DRC_AGC_P_MASK (0x1 << 15)
+#define RT5645_DRC_AGC_P_SFT 15
+#define RT5645_DRC_AGC_P_DAC (0x0 << 15)
+#define RT5645_DRC_AGC_P_ADC (0x1 << 15)
+#define RT5645_DRC_AGC_MASK (0x1 << 14)
+#define RT5645_DRC_AGC_SFT 14
+#define RT5645_DRC_AGC_DIS (0x0 << 14)
+#define RT5645_DRC_AGC_EN (0x1 << 14)
+#define RT5645_DRC_AGC_UPD (0x1 << 13)
+#define RT5645_DRC_AGC_UPD_BIT 13
+#define RT5645_DRC_AGC_AR_MASK (0x1f << 8)
+#define RT5645_DRC_AGC_AR_SFT 8
+#define RT5645_DRC_AGC_R_MASK (0x7 << 5)
+#define RT5645_DRC_AGC_R_SFT 5
+#define RT5645_DRC_AGC_R_48K (0x1 << 5)
+#define RT5645_DRC_AGC_R_96K (0x2 << 5)
+#define RT5645_DRC_AGC_R_192K (0x3 << 5)
+#define RT5645_DRC_AGC_R_441K (0x5 << 5)
+#define RT5645_DRC_AGC_R_882K (0x6 << 5)
+#define RT5645_DRC_AGC_R_1764K (0x7 << 5)
+#define RT5645_DRC_AGC_RC_MASK (0x1f)
+#define RT5645_DRC_AGC_RC_SFT 0
+
+/* DRC/AGC Control 2 (0xb5) */
+#define RT5645_DRC_AGC_POB_MASK (0x3f << 8)
+#define RT5645_DRC_AGC_POB_SFT 8
+#define RT5645_DRC_AGC_CP_MASK (0x1 << 7)
+#define RT5645_DRC_AGC_CP_SFT 7
+#define RT5645_DRC_AGC_CP_DIS (0x0 << 7)
+#define RT5645_DRC_AGC_CP_EN (0x1 << 7)
+#define RT5645_DRC_AGC_CPR_MASK (0x3 << 5)
+#define RT5645_DRC_AGC_CPR_SFT 5
+#define RT5645_DRC_AGC_CPR_1_1 (0x0 << 5)
+#define RT5645_DRC_AGC_CPR_1_2 (0x1 << 5)
+#define RT5645_DRC_AGC_CPR_1_3 (0x2 << 5)
+#define RT5645_DRC_AGC_CPR_1_4 (0x3 << 5)
+#define RT5645_DRC_AGC_PRB_MASK (0x1f)
+#define RT5645_DRC_AGC_PRB_SFT 0
+
+/* DRC/AGC Control 3 (0xb6) */
+#define RT5645_DRC_AGC_NGB_MASK (0xf << 12)
+#define RT5645_DRC_AGC_NGB_SFT 12
+#define RT5645_DRC_AGC_TAR_MASK (0x1f << 7)
+#define RT5645_DRC_AGC_TAR_SFT 7
+#define RT5645_DRC_AGC_NG_MASK (0x1 << 6)
+#define RT5645_DRC_AGC_NG_SFT 6
+#define RT5645_DRC_AGC_NG_DIS (0x0 << 6)
+#define RT5645_DRC_AGC_NG_EN (0x1 << 6)
+#define RT5645_DRC_AGC_NGH_MASK (0x1 << 5)
+#define RT5645_DRC_AGC_NGH_SFT 5
+#define RT5645_DRC_AGC_NGH_DIS (0x0 << 5)
+#define RT5645_DRC_AGC_NGH_EN (0x1 << 5)
+#define RT5645_DRC_AGC_NGT_MASK (0x1f)
+#define RT5645_DRC_AGC_NGT_SFT 0
+
+/* ANC Control 1 (0xb8) */
+#define RT5645_ANC_M_MASK (0x1 << 15)
+#define RT5645_ANC_M_SFT 15
+#define RT5645_ANC_M_NOR (0x0 << 15)
+#define RT5645_ANC_M_REV (0x1 << 15)
+#define RT5645_ANC_MASK (0x1 << 14)
+#define RT5645_ANC_SFT 14
+#define RT5645_ANC_DIS (0x0 << 14)
+#define RT5645_ANC_EN (0x1 << 14)
+#define RT5645_ANC_MD_MASK (0x3 << 12)
+#define RT5645_ANC_MD_SFT 12
+#define RT5645_ANC_MD_DIS (0x0 << 12)
+#define RT5645_ANC_MD_67MS (0x1 << 12)
+#define RT5645_ANC_MD_267MS (0x2 << 12)
+#define RT5645_ANC_MD_1067MS (0x3 << 12)
+#define RT5645_ANC_SN_MASK (0x1 << 11)
+#define RT5645_ANC_SN_SFT 11
+#define RT5645_ANC_SN_DIS (0x0 << 11)
+#define RT5645_ANC_SN_EN (0x1 << 11)
+#define RT5645_ANC_CLK_MASK (0x1 << 10)
+#define RT5645_ANC_CLK_SFT 10
+#define RT5645_ANC_CLK_ANC (0x0 << 10)
+#define RT5645_ANC_CLK_REG (0x1 << 10)
+#define RT5645_ANC_ZCD_MASK (0x3 << 8)
+#define RT5645_ANC_ZCD_SFT 8
+#define RT5645_ANC_ZCD_DIS (0x0 << 8)
+#define RT5645_ANC_ZCD_T1 (0x1 << 8)
+#define RT5645_ANC_ZCD_T2 (0x2 << 8)
+#define RT5645_ANC_ZCD_WT (0x3 << 8)
+#define RT5645_ANC_CS_MASK (0x1 << 7)
+#define RT5645_ANC_CS_SFT 7
+#define RT5645_ANC_CS_DIS (0x0 << 7)
+#define RT5645_ANC_CS_EN (0x1 << 7)
+#define RT5645_ANC_SW_MASK (0x1 << 6)
+#define RT5645_ANC_SW_SFT 6
+#define RT5645_ANC_SW_NOR (0x0 << 6)
+#define RT5645_ANC_SW_AUTO (0x1 << 6)
+#define RT5645_ANC_CO_L_MASK (0x3f)
+#define RT5645_ANC_CO_L_SFT 0
+
+/* ANC Control 2 (0xb6) */
+#define RT5645_ANC_FG_R_MASK (0xf << 12)
+#define RT5645_ANC_FG_R_SFT 12
+#define RT5645_ANC_FG_L_MASK (0xf << 8)
+#define RT5645_ANC_FG_L_SFT 8
+#define RT5645_ANC_CG_R_MASK (0xf << 4)
+#define RT5645_ANC_CG_R_SFT 4
+#define RT5645_ANC_CG_L_MASK (0xf)
+#define RT5645_ANC_CG_L_SFT 0
+
+/* ANC Control 3 (0xb6) */
+#define RT5645_ANC_CD_MASK (0x1 << 6)
+#define RT5645_ANC_CD_SFT 6
+#define RT5645_ANC_CD_BOTH (0x0 << 6)
+#define RT5645_ANC_CD_IND (0x1 << 6)
+#define RT5645_ANC_CO_R_MASK (0x3f)
+#define RT5645_ANC_CO_R_SFT 0
+
+/* Jack Detect Control (0xbb) */
+#define RT5645_JD_MASK (0x7 << 13)
+#define RT5645_JD_SFT 13
+#define RT5645_JD_DIS (0x0 << 13)
+#define RT5645_JD_GPIO1 (0x1 << 13)
+#define RT5645_JD_JD1_IN4P (0x2 << 13)
+#define RT5645_JD_JD2_IN4N (0x3 << 13)
+#define RT5645_JD_GPIO2 (0x4 << 13)
+#define RT5645_JD_GPIO3 (0x5 << 13)
+#define RT5645_JD_GPIO4 (0x6 << 13)
+#define RT5645_JD_HP_MASK (0x1 << 11)
+#define RT5645_JD_HP_SFT 11
+#define RT5645_JD_HP_DIS (0x0 << 11)
+#define RT5645_JD_HP_EN (0x1 << 11)
+#define RT5645_JD_HP_TRG_MASK (0x1 << 10)
+#define RT5645_JD_HP_TRG_SFT 10
+#define RT5645_JD_HP_TRG_LO (0x0 << 10)
+#define RT5645_JD_HP_TRG_HI (0x1 << 10)
+#define RT5645_JD_SPL_MASK (0x1 << 9)
+#define RT5645_JD_SPL_SFT 9
+#define RT5645_JD_SPL_DIS (0x0 << 9)
+#define RT5645_JD_SPL_EN (0x1 << 9)
+#define RT5645_JD_SPL_TRG_MASK (0x1 << 8)
+#define RT5645_JD_SPL_TRG_SFT 8
+#define RT5645_JD_SPL_TRG_LO (0x0 << 8)
+#define RT5645_JD_SPL_TRG_HI (0x1 << 8)
+#define RT5645_JD_SPR_MASK (0x1 << 7)
+#define RT5645_JD_SPR_SFT 7
+#define RT5645_JD_SPR_DIS (0x0 << 7)
+#define RT5645_JD_SPR_EN (0x1 << 7)
+#define RT5645_JD_SPR_TRG_MASK (0x1 << 6)
+#define RT5645_JD_SPR_TRG_SFT 6
+#define RT5645_JD_SPR_TRG_LO (0x0 << 6)
+#define RT5645_JD_SPR_TRG_HI (0x1 << 6)
+#define RT5645_JD_MO_MASK (0x1 << 5)
+#define RT5645_JD_MO_SFT 5
+#define RT5645_JD_MO_DIS (0x0 << 5)
+#define RT5645_JD_MO_EN (0x1 << 5)
+#define RT5645_JD_MO_TRG_MASK (0x1 << 4)
+#define RT5645_JD_MO_TRG_SFT 4
+#define RT5645_JD_MO_TRG_LO (0x0 << 4)
+#define RT5645_JD_MO_TRG_HI (0x1 << 4)
+#define RT5645_JD_LO_MASK (0x1 << 3)
+#define RT5645_JD_LO_SFT 3
+#define RT5645_JD_LO_DIS (0x0 << 3)
+#define RT5645_JD_LO_EN (0x1 << 3)
+#define RT5645_JD_LO_TRG_MASK (0x1 << 2)
+#define RT5645_JD_LO_TRG_SFT 2
+#define RT5645_JD_LO_TRG_LO (0x0 << 2)
+#define RT5645_JD_LO_TRG_HI (0x1 << 2)
+#define RT5645_JD1_IN4P_MASK (0x1 << 1)
+#define RT5645_JD1_IN4P_SFT 1
+#define RT5645_JD1_IN4P_DIS (0x0 << 1)
+#define RT5645_JD1_IN4P_EN (0x1 << 1)
+#define RT5645_JD2_IN4N_MASK (0x1)
+#define RT5645_JD2_IN4N_SFT 0
+#define RT5645_JD2_IN4N_DIS (0x0)
+#define RT5645_JD2_IN4N_EN (0x1)
+
+/* Jack detect for ANC (0xbc) */
+#define RT5645_ANC_DET_MASK (0x3 << 4)
+#define RT5645_ANC_DET_SFT 4
+#define RT5645_ANC_DET_DIS (0x0 << 4)
+#define RT5645_ANC_DET_MB1 (0x1 << 4)
+#define RT5645_ANC_DET_MB2 (0x2 << 4)
+#define RT5645_ANC_DET_JD (0x3 << 4)
+#define RT5645_AD_TRG_MASK (0x1 << 3)
+#define RT5645_AD_TRG_SFT 3
+#define RT5645_AD_TRG_LO (0x0 << 3)
+#define RT5645_AD_TRG_HI (0x1 << 3)
+#define RT5645_ANCM_DET_MASK (0x3 << 4)
+#define RT5645_ANCM_DET_SFT 4
+#define RT5645_ANCM_DET_DIS (0x0 << 4)
+#define RT5645_ANCM_DET_MB1 (0x1 << 4)
+#define RT5645_ANCM_DET_MB2 (0x2 << 4)
+#define RT5645_ANCM_DET_JD (0x3 << 4)
+#define RT5645_AMD_TRG_MASK (0x1 << 3)
+#define RT5645_AMD_TRG_SFT 3
+#define RT5645_AMD_TRG_LO (0x0 << 3)
+#define RT5645_AMD_TRG_HI (0x1 << 3)
+
+/* IRQ Control 1 (0xbd) */
+#define RT5645_IRQ_JD_MASK (0x1 << 15)
+#define RT5645_IRQ_JD_SFT 15
+#define RT5645_IRQ_JD_BP (0x0 << 15)
+#define RT5645_IRQ_JD_NOR (0x1 << 15)
+#define RT5645_IRQ_OT_MASK (0x1 << 14)
+#define RT5645_IRQ_OT_SFT 14
+#define RT5645_IRQ_OT_BP (0x0 << 14)
+#define RT5645_IRQ_OT_NOR (0x1 << 14)
+#define RT5645_JD_STKY_MASK (0x1 << 13)
+#define RT5645_JD_STKY_SFT 13
+#define RT5645_JD_STKY_DIS (0x0 << 13)
+#define RT5645_JD_STKY_EN (0x1 << 13)
+#define RT5645_OT_STKY_MASK (0x1 << 12)
+#define RT5645_OT_STKY_SFT 12
+#define RT5645_OT_STKY_DIS (0x0 << 12)
+#define RT5645_OT_STKY_EN (0x1 << 12)
+#define RT5645_JD_P_MASK (0x1 << 11)
+#define RT5645_JD_P_SFT 11
+#define RT5645_JD_P_NOR (0x0 << 11)
+#define RT5645_JD_P_INV (0x1 << 11)
+#define RT5645_OT_P_MASK (0x1 << 10)
+#define RT5645_OT_P_SFT 10
+#define RT5645_OT_P_NOR (0x0 << 10)
+#define RT5645_OT_P_INV (0x1 << 10)
+#define RT5645_IRQ_JD_1_1_EN (0x1 << 9)
+
+/* IRQ Control 2 (0xbe) */
+#define RT5645_IRQ_MB1_OC_MASK (0x1 << 15)
+#define RT5645_IRQ_MB1_OC_SFT 15
+#define RT5645_IRQ_MB1_OC_BP (0x0 << 15)
+#define RT5645_IRQ_MB1_OC_NOR (0x1 << 15)
+#define RT5645_IRQ_MB2_OC_MASK (0x1 << 14)
+#define RT5645_IRQ_MB2_OC_SFT 14
+#define RT5645_IRQ_MB2_OC_BP (0x0 << 14)
+#define RT5645_IRQ_MB2_OC_NOR (0x1 << 14)
+#define RT5645_MB1_OC_STKY_MASK (0x1 << 13)
+#define RT5645_MB1_OC_STKY_SFT 13
+#define RT5645_MB1_OC_STKY_DIS (0x0 << 13)
+#define RT5645_MB1_OC_STKY_EN (0x1 << 13)
+#define RT5645_MB2_OC_STKY_MASK (0x1 << 12)
+#define RT5645_MB2_OC_STKY_SFT 12
+#define RT5645_MB2_OC_STKY_DIS (0x0 << 12)
+#define RT5645_MB2_OC_STKY_EN (0x1 << 12)
+#define RT5645_MB1_OC_P_MASK (0x1 << 7)
+#define RT5645_MB1_OC_P_SFT 7
+#define RT5645_MB1_OC_P_NOR (0x0 << 7)
+#define RT5645_MB1_OC_P_INV (0x1 << 7)
+#define RT5645_MB2_OC_P_MASK (0x1 << 6)
+#define RT5645_MB2_OC_P_SFT 6
+#define RT5645_MB2_OC_P_NOR (0x0 << 6)
+#define RT5645_MB2_OC_P_INV (0x1 << 6)
+#define RT5645_MB1_OC_CLR (0x1 << 3)
+#define RT5645_MB1_OC_CLR_SFT 3
+#define RT5645_MB2_OC_CLR (0x1 << 2)
+#define RT5645_MB2_OC_CLR_SFT 2
+
+/* GPIO Control 1 (0xc0) */
+#define RT5645_GP1_PIN_MASK (0x1 << 15)
+#define RT5645_GP1_PIN_SFT 15
+#define RT5645_GP1_PIN_GPIO1 (0x0 << 15)
+#define RT5645_GP1_PIN_IRQ (0x1 << 15)
+#define RT5645_GP2_PIN_MASK (0x1 << 14)
+#define RT5645_GP2_PIN_SFT 14
+#define RT5645_GP2_PIN_GPIO2 (0x0 << 14)
+#define RT5645_GP2_PIN_DMIC1_SCL (0x1 << 14)
+#define RT5645_GP3_PIN_MASK (0x3 << 12)
+#define RT5645_GP3_PIN_SFT 12
+#define RT5645_GP3_PIN_GPIO3 (0x0 << 12)
+#define RT5645_GP3_PIN_DMIC1_SDA (0x1 << 12)
+#define RT5645_GP3_PIN_IRQ (0x2 << 12)
+#define RT5645_GP4_PIN_MASK (0x1 << 11)
+#define RT5645_GP4_PIN_SFT 11
+#define RT5645_GP4_PIN_GPIO4 (0x0 << 11)
+#define RT5645_GP4_PIN_DMIC2_SDA (0x1 << 11)
+#define RT5645_DP_SIG_MASK (0x1 << 10)
+#define RT5645_DP_SIG_SFT 10
+#define RT5645_DP_SIG_TEST (0x0 << 10)
+#define RT5645_DP_SIG_AP (0x1 << 10)
+#define RT5645_GPIO_M_MASK (0x1 << 9)
+#define RT5645_GPIO_M_SFT 9
+#define RT5645_GPIO_M_FLT (0x0 << 9)
+#define RT5645_GPIO_M_PH (0x1 << 9)
+#define RT5645_I2S2_SEL (0x1 << 8)
+#define RT5645_I2S2_SEL_SFT 8
+#define RT5645_GP5_PIN_MASK (0x1 << 7)
+#define RT5645_GP5_PIN_SFT 7
+#define RT5645_GP5_PIN_GPIO5 (0x0 << 7)
+#define RT5645_GP5_PIN_DMIC1_SDA (0x1 << 7)
+#define RT5645_GP6_PIN_MASK (0x1 << 6)
+#define RT5645_GP6_PIN_SFT 6
+#define RT5645_GP6_PIN_GPIO6 (0x0 << 6)
+#define RT5645_GP6_PIN_DMIC2_SDA (0x1 << 6)
+#define RT5645_GP8_PIN_MASK (0x1 << 3)
+#define RT5645_GP8_PIN_SFT 3
+#define RT5645_GP8_PIN_GPIO8 (0x0 << 3)
+#define RT5645_GP8_PIN_DMIC2_SDA (0x1 << 3)
+#define RT5645_GP12_PIN_MASK (0x1 << 2)
+#define RT5645_GP12_PIN_SFT 2
+#define RT5645_GP12_PIN_GPIO12 (0x0 << 2)
+#define RT5645_GP12_PIN_DMIC2_SDA (0x1 << 2)
+#define RT5645_GP11_PIN_MASK (0x1 << 1)
+#define RT5645_GP11_PIN_SFT 1
+#define RT5645_GP11_PIN_GPIO11 (0x0 << 1)
+#define RT5645_GP11_PIN_DMIC1_SDA (0x1 << 1)
+#define RT5645_GP10_PIN_MASK (0x1)
+#define RT5645_GP10_PIN_SFT 0
+#define RT5645_GP10_PIN_GPIO10 (0x0)
+#define RT5645_GP10_PIN_DMIC2_SDA (0x1)
+
+/* GPIO Control 3 (0xc2) */
+#define RT5645_GP4_PF_MASK (0x1 << 11)
+#define RT5645_GP4_PF_SFT 11
+#define RT5645_GP4_PF_IN (0x0 << 11)
+#define RT5645_GP4_PF_OUT (0x1 << 11)
+#define RT5645_GP4_OUT_MASK (0x1 << 10)
+#define RT5645_GP4_OUT_SFT 10
+#define RT5645_GP4_OUT_LO (0x0 << 10)
+#define RT5645_GP4_OUT_HI (0x1 << 10)
+#define RT5645_GP4_P_MASK (0x1 << 9)
+#define RT5645_GP4_P_SFT 9
+#define RT5645_GP4_P_NOR (0x0 << 9)
+#define RT5645_GP4_P_INV (0x1 << 9)
+#define RT5645_GP3_PF_MASK (0x1 << 8)
+#define RT5645_GP3_PF_SFT 8
+#define RT5645_GP3_PF_IN (0x0 << 8)
+#define RT5645_GP3_PF_OUT (0x1 << 8)
+#define RT5645_GP3_OUT_MASK (0x1 << 7)
+#define RT5645_GP3_OUT_SFT 7
+#define RT5645_GP3_OUT_LO (0x0 << 7)
+#define RT5645_GP3_OUT_HI (0x1 << 7)
+#define RT5645_GP3_P_MASK (0x1 << 6)
+#define RT5645_GP3_P_SFT 6
+#define RT5645_GP3_P_NOR (0x0 << 6)
+#define RT5645_GP3_P_INV (0x1 << 6)
+#define RT5645_GP2_PF_MASK (0x1 << 5)
+#define RT5645_GP2_PF_SFT 5
+#define RT5645_GP2_PF_IN (0x0 << 5)
+#define RT5645_GP2_PF_OUT (0x1 << 5)
+#define RT5645_GP2_OUT_MASK (0x1 << 4)
+#define RT5645_GP2_OUT_SFT 4
+#define RT5645_GP2_OUT_LO (0x0 << 4)
+#define RT5645_GP2_OUT_HI (0x1 << 4)
+#define RT5645_GP2_P_MASK (0x1 << 3)
+#define RT5645_GP2_P_SFT 3
+#define RT5645_GP2_P_NOR (0x0 << 3)
+#define RT5645_GP2_P_INV (0x1 << 3)
+#define RT5645_GP1_PF_MASK (0x1 << 2)
+#define RT5645_GP1_PF_SFT 2
+#define RT5645_GP1_PF_IN (0x0 << 2)
+#define RT5645_GP1_PF_OUT (0x1 << 2)
+#define RT5645_GP1_OUT_MASK (0x1 << 1)
+#define RT5645_GP1_OUT_SFT 1
+#define RT5645_GP1_OUT_LO (0x0 << 1)
+#define RT5645_GP1_OUT_HI (0x1 << 1)
+#define RT5645_GP1_P_MASK (0x1)
+#define RT5645_GP1_P_SFT 0
+#define RT5645_GP1_P_NOR (0x0)
+#define RT5645_GP1_P_INV (0x1)
+
+/* Programmable Register Array Control 1 (0xc8) */
+#define RT5645_REG_SEQ_MASK (0xf << 12)
+#define RT5645_REG_SEQ_SFT 12
+#define RT5645_SEQ1_ST_MASK (0x1 << 11) /*RO*/
+#define RT5645_SEQ1_ST_SFT 11
+#define RT5645_SEQ1_ST_RUN (0x0 << 11)
+#define RT5645_SEQ1_ST_FIN (0x1 << 11)
+#define RT5645_SEQ2_ST_MASK (0x1 << 10) /*RO*/
+#define RT5645_SEQ2_ST_SFT 10
+#define RT5645_SEQ2_ST_RUN (0x0 << 10)
+#define RT5645_SEQ2_ST_FIN (0x1 << 10)
+#define RT5645_REG_LV_MASK (0x1 << 9)
+#define RT5645_REG_LV_SFT 9
+#define RT5645_REG_LV_MX (0x0 << 9)
+#define RT5645_REG_LV_PR (0x1 << 9)
+#define RT5645_SEQ_2_PT_MASK (0x1 << 8)
+#define RT5645_SEQ_2_PT_BIT 8
+#define RT5645_REG_IDX_MASK (0xff)
+#define RT5645_REG_IDX_SFT 0
+
+/* Programmable Register Array Control 2 (0xc9) */
+#define RT5645_REG_DAT_MASK (0xffff)
+#define RT5645_REG_DAT_SFT 0
+
+/* Programmable Register Array Control 3 (0xca) */
+#define RT5645_SEQ_DLY_MASK (0xff << 8)
+#define RT5645_SEQ_DLY_SFT 8
+#define RT5645_PROG_MASK (0x1 << 7)
+#define RT5645_PROG_SFT 7
+#define RT5645_PROG_DIS (0x0 << 7)
+#define RT5645_PROG_EN (0x1 << 7)
+#define RT5645_SEQ1_PT_RUN (0x1 << 6)
+#define RT5645_SEQ1_PT_RUN_BIT 6
+#define RT5645_SEQ2_PT_RUN (0x1 << 5)
+#define RT5645_SEQ2_PT_RUN_BIT 5
+
+/* Programmable Register Array Control 4 (0xcb) */
+#define RT5645_SEQ1_START_MASK (0xf << 8)
+#define RT5645_SEQ1_START_SFT 8
+#define RT5645_SEQ1_END_MASK (0xf)
+#define RT5645_SEQ1_END_SFT 0
+
+/* Programmable Register Array Control 5 (0xcc) */
+#define RT5645_SEQ2_START_MASK (0xf << 8)
+#define RT5645_SEQ2_START_SFT 8
+#define RT5645_SEQ2_END_MASK (0xf)
+#define RT5645_SEQ2_END_SFT 0
+
+/* Scramble Function (0xcd) */
+#define RT5645_SCB_KEY_MASK (0xff)
+#define RT5645_SCB_KEY_SFT 0
+
+/* Scramble Control (0xce) */
+#define RT5645_SCB_SWAP_MASK (0x1 << 15)
+#define RT5645_SCB_SWAP_SFT 15
+#define RT5645_SCB_SWAP_DIS (0x0 << 15)
+#define RT5645_SCB_SWAP_EN (0x1 << 15)
+#define RT5645_SCB_MASK (0x1 << 14)
+#define RT5645_SCB_SFT 14
+#define RT5645_SCB_DIS (0x0 << 14)
+#define RT5645_SCB_EN (0x1 << 14)
+
+/* Baseback Control (0xcf) */
+#define RT5645_BB_MASK (0x1 << 15)
+#define RT5645_BB_SFT 15
+#define RT5645_BB_DIS (0x0 << 15)
+#define RT5645_BB_EN (0x1 << 15)
+#define RT5645_BB_CT_MASK (0x7 << 12)
+#define RT5645_BB_CT_SFT 12
+#define RT5645_BB_CT_A (0x0 << 12)
+#define RT5645_BB_CT_B (0x1 << 12)
+#define RT5645_BB_CT_C (0x2 << 12)
+#define RT5645_BB_CT_D (0x3 << 12)
+#define RT5645_M_BB_L_MASK (0x1 << 9)
+#define RT5645_M_BB_L_SFT 9
+#define RT5645_M_BB_R_MASK (0x1 << 8)
+#define RT5645_M_BB_R_SFT 8
+#define RT5645_M_BB_HPF_L_MASK (0x1 << 7)
+#define RT5645_M_BB_HPF_L_SFT 7
+#define RT5645_M_BB_HPF_R_MASK (0x1 << 6)
+#define RT5645_M_BB_HPF_R_SFT 6
+#define RT5645_G_BB_BST_MASK (0x3f)
+#define RT5645_G_BB_BST_SFT 0
+#define RT5645_G_BB_BST_25DB 0x14
+
+/* MP3 Plus Control 1 (0xd0) */
+#define RT5645_M_MP3_L_MASK (0x1 << 15)
+#define RT5645_M_MP3_L_SFT 15
+#define RT5645_M_MP3_R_MASK (0x1 << 14)
+#define RT5645_M_MP3_R_SFT 14
+#define RT5645_M_MP3_MASK (0x1 << 13)
+#define RT5645_M_MP3_SFT 13
+#define RT5645_M_MP3_DIS (0x0 << 13)
+#define RT5645_M_MP3_EN (0x1 << 13)
+#define RT5645_EG_MP3_MASK (0x1f << 8)
+#define RT5645_EG_MP3_SFT 8
+#define RT5645_MP3_HLP_MASK (0x1 << 7)
+#define RT5645_MP3_HLP_SFT 7
+#define RT5645_MP3_HLP_DIS (0x0 << 7)
+#define RT5645_MP3_HLP_EN (0x1 << 7)
+#define RT5645_M_MP3_ORG_L_MASK (0x1 << 6)
+#define RT5645_M_MP3_ORG_L_SFT 6
+#define RT5645_M_MP3_ORG_R_MASK (0x1 << 5)
+#define RT5645_M_MP3_ORG_R_SFT 5
+
+/* MP3 Plus Control 2 (0xd1) */
+#define RT5645_MP3_WT_MASK (0x1 << 13)
+#define RT5645_MP3_WT_SFT 13
+#define RT5645_MP3_WT_1_4 (0x0 << 13)
+#define RT5645_MP3_WT_1_2 (0x1 << 13)
+#define RT5645_OG_MP3_MASK (0x1f << 8)
+#define RT5645_OG_MP3_SFT 8
+#define RT5645_HG_MP3_MASK (0x3f)
+#define RT5645_HG_MP3_SFT 0
+
+/* 3D HP Control 1 (0xd2) */
+#define RT5645_3D_CF_MASK (0x1 << 15)
+#define RT5645_3D_CF_SFT 15
+#define RT5645_3D_CF_DIS (0x0 << 15)
+#define RT5645_3D_CF_EN (0x1 << 15)
+#define RT5645_3D_HP_MASK (0x1 << 14)
+#define RT5645_3D_HP_SFT 14
+#define RT5645_3D_HP_DIS (0x0 << 14)
+#define RT5645_3D_HP_EN (0x1 << 14)
+#define RT5645_3D_BT_MASK (0x1 << 13)
+#define RT5645_3D_BT_SFT 13
+#define RT5645_3D_BT_DIS (0x0 << 13)
+#define RT5645_3D_BT_EN (0x1 << 13)
+#define RT5645_3D_1F_MIX_MASK (0x3 << 11)
+#define RT5645_3D_1F_MIX_SFT 11
+#define RT5645_3D_HP_M_MASK (0x1 << 10)
+#define RT5645_3D_HP_M_SFT 10
+#define RT5645_3D_HP_M_SUR (0x0 << 10)
+#define RT5645_3D_HP_M_FRO (0x1 << 10)
+#define RT5645_M_3D_HRTF_MASK (0x1 << 9)
+#define RT5645_M_3D_HRTF_SFT 9
+#define RT5645_M_3D_D2H_MASK (0x1 << 8)
+#define RT5645_M_3D_D2H_SFT 8
+#define RT5645_M_3D_D2R_MASK (0x1 << 7)
+#define RT5645_M_3D_D2R_SFT 7
+#define RT5645_M_3D_REVB_MASK (0x1 << 6)
+#define RT5645_M_3D_REVB_SFT 6
+
+/* Adjustable high pass filter control 1 (0xd3) */
+#define RT5645_2ND_HPF_MASK (0x1 << 15)
+#define RT5645_2ND_HPF_SFT 15
+#define RT5645_2ND_HPF_DIS (0x0 << 15)
+#define RT5645_2ND_HPF_EN (0x1 << 15)
+#define RT5645_HPF_CF_L_MASK (0x7 << 12)
+#define RT5645_HPF_CF_L_SFT 12
+#define RT5645_1ST_HPF_MASK (0x1 << 11)
+#define RT5645_1ST_HPF_SFT 11
+#define RT5645_1ST_HPF_DIS (0x0 << 11)
+#define RT5645_1ST_HPF_EN (0x1 << 11)
+#define RT5645_HPF_CF_R_MASK (0x7 << 8)
+#define RT5645_HPF_CF_R_SFT 8
+#define RT5645_ZD_T_MASK (0x3 << 6)
+#define RT5645_ZD_T_SFT 6
+#define RT5645_ZD_F_MASK (0x3 << 4)
+#define RT5645_ZD_F_SFT 4
+#define RT5645_ZD_F_IM (0x0 << 4)
+#define RT5645_ZD_F_ZC_IM (0x1 << 4)
+#define RT5645_ZD_F_ZC_IOD (0x2 << 4)
+#define RT5645_ZD_F_UN (0x3 << 4)
+
+/* HP calibration control and Amp detection (0xd6) */
+#define RT5645_SI_DAC_MASK (0x1 << 11)
+#define RT5645_SI_DAC_SFT 11
+#define RT5645_SI_DAC_AUTO (0x0 << 11)
+#define RT5645_SI_DAC_TEST (0x1 << 11)
+#define RT5645_DC_CAL_M_MASK (0x1 << 10)
+#define RT5645_DC_CAL_M_SFT 10
+#define RT5645_DC_CAL_M_CAL (0x0 << 10)
+#define RT5645_DC_CAL_M_NOR (0x1 << 10)
+#define RT5645_DC_CAL_MASK (0x1 << 9)
+#define RT5645_DC_CAL_SFT 9
+#define RT5645_DC_CAL_DIS (0x0 << 9)
+#define RT5645_DC_CAL_EN (0x1 << 9)
+#define RT5645_HPD_RCV_MASK (0x7 << 6)
+#define RT5645_HPD_RCV_SFT 6
+#define RT5645_HPD_PS_MASK (0x1 << 5)
+#define RT5645_HPD_PS_SFT 5
+#define RT5645_HPD_PS_DIS (0x0 << 5)
+#define RT5645_HPD_PS_EN (0x1 << 5)
+#define RT5645_CAL_M_MASK (0x1 << 4)
+#define RT5645_CAL_M_SFT 4
+#define RT5645_CAL_M_DEP (0x0 << 4)
+#define RT5645_CAL_M_CAL (0x1 << 4)
+#define RT5645_CAL_MASK (0x1 << 3)
+#define RT5645_CAL_SFT 3
+#define RT5645_CAL_DIS (0x0 << 3)
+#define RT5645_CAL_EN (0x1 << 3)
+#define RT5645_CAL_TEST_MASK (0x1 << 2)
+#define RT5645_CAL_TEST_SFT 2
+#define RT5645_CAL_TEST_DIS (0x0 << 2)
+#define RT5645_CAL_TEST_EN (0x1 << 2)
+#define RT5645_CAL_P_MASK (0x3)
+#define RT5645_CAL_P_SFT 0
+#define RT5645_CAL_P_NONE (0x0)
+#define RT5645_CAL_P_CAL (0x1)
+#define RT5645_CAL_P_DAC_CAL (0x2)
+
+/* Soft volume and zero cross control 1 (0xd9) */
+#define RT5645_SV_MASK (0x1 << 15)
+#define RT5645_SV_SFT 15
+#define RT5645_SV_DIS (0x0 << 15)
+#define RT5645_SV_EN (0x1 << 15)
+#define RT5645_SPO_SV_MASK (0x1 << 14)
+#define RT5645_SPO_SV_SFT 14
+#define RT5645_SPO_SV_DIS (0x0 << 14)
+#define RT5645_SPO_SV_EN (0x1 << 14)
+#define RT5645_OUT_SV_MASK (0x1 << 13)
+#define RT5645_OUT_SV_SFT 13
+#define RT5645_OUT_SV_DIS (0x0 << 13)
+#define RT5645_OUT_SV_EN (0x1 << 13)
+#define RT5645_HP_SV_MASK (0x1 << 12)
+#define RT5645_HP_SV_SFT 12
+#define RT5645_HP_SV_DIS (0x0 << 12)
+#define RT5645_HP_SV_EN (0x1 << 12)
+#define RT5645_ZCD_DIG_MASK (0x1 << 11)
+#define RT5645_ZCD_DIG_SFT 11
+#define RT5645_ZCD_DIG_DIS (0x0 << 11)
+#define RT5645_ZCD_DIG_EN (0x1 << 11)
+#define RT5645_ZCD_MASK (0x1 << 10)
+#define RT5645_ZCD_SFT 10
+#define RT5645_ZCD_PD (0x0 << 10)
+#define RT5645_ZCD_PU (0x1 << 10)
+#define RT5645_M_ZCD_MASK (0x3f << 4)
+#define RT5645_M_ZCD_SFT 4
+#define RT5645_M_ZCD_RM_L (0x1 << 9)
+#define RT5645_M_ZCD_RM_R (0x1 << 8)
+#define RT5645_M_ZCD_SM_L (0x1 << 7)
+#define RT5645_M_ZCD_SM_R (0x1 << 6)
+#define RT5645_M_ZCD_OM_L (0x1 << 5)
+#define RT5645_M_ZCD_OM_R (0x1 << 4)
+#define RT5645_SV_DLY_MASK (0xf)
+#define RT5645_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0xda) */
+#define RT5645_ZCD_HP_MASK (0x1 << 15)
+#define RT5645_ZCD_HP_SFT 15
+#define RT5645_ZCD_HP_DIS (0x0 << 15)
+#define RT5645_ZCD_HP_EN (0x1 << 15)
+
+
+/* Codec Private Register definition */
+/* 3D Speaker Control (0x63) */
+#define RT5645_3D_SPK_MASK (0x1 << 15)
+#define RT5645_3D_SPK_SFT 15
+#define RT5645_3D_SPK_DIS (0x0 << 15)
+#define RT5645_3D_SPK_EN (0x1 << 15)
+#define RT5645_3D_SPK_M_MASK (0x3 << 13)
+#define RT5645_3D_SPK_M_SFT 13
+#define RT5645_3D_SPK_CG_MASK (0x1f << 8)
+#define RT5645_3D_SPK_CG_SFT 8
+#define RT5645_3D_SPK_SG_MASK (0x1f)
+#define RT5645_3D_SPK_SG_SFT 0
+
+/* Wind Noise Detection Control 1 (0x6c) */
+#define RT5645_WND_MASK (0x1 << 15)
+#define RT5645_WND_SFT 15
+#define RT5645_WND_DIS (0x0 << 15)
+#define RT5645_WND_EN (0x1 << 15)
+
+/* Wind Noise Detection Control 2 (0x6d) */
+#define RT5645_WND_FC_NW_MASK (0x3f << 10)
+#define RT5645_WND_FC_NW_SFT 10
+#define RT5645_WND_FC_WK_MASK (0x3f << 4)
+#define RT5645_WND_FC_WK_SFT 4
+
+/* Wind Noise Detection Control 3 (0x6e) */
+#define RT5645_HPF_FC_MASK (0x3f << 6)
+#define RT5645_HPF_FC_SFT 6
+#define RT5645_WND_FC_ST_MASK (0x3f)
+#define RT5645_WND_FC_ST_SFT 0
+
+/* Wind Noise Detection Control 4 (0x6f) */
+#define RT5645_WND_TH_LO_MASK (0x3ff)
+#define RT5645_WND_TH_LO_SFT 0
+
+/* Wind Noise Detection Control 5 (0x70) */
+#define RT5645_WND_TH_HI_MASK (0x3ff)
+#define RT5645_WND_TH_HI_SFT 0
+
+/* Wind Noise Detection Control 8 (0x73) */
+#define RT5645_WND_WIND_MASK (0x1 << 13) /* Read-Only */
+#define RT5645_WND_WIND_SFT 13
+#define RT5645_WND_STRONG_MASK (0x1 << 12) /* Read-Only */
+#define RT5645_WND_STRONG_SFT 12
+enum {
+ RT5645_NO_WIND,
+ RT5645_BREEZE,
+ RT5645_STORM,
+};
+
+/* Dipole Speaker Interface (0x75) */
+#define RT5645_DP_ATT_MASK (0x3 << 14)
+#define RT5645_DP_ATT_SFT 14
+#define RT5645_DP_SPK_MASK (0x1 << 10)
+#define RT5645_DP_SPK_SFT 10
+#define RT5645_DP_SPK_DIS (0x0 << 10)
+#define RT5645_DP_SPK_EN (0x1 << 10)
+
+/* EQ Pre Volume Control (0xb3) */
+#define RT5645_EQ_PRE_VOL_MASK (0xffff)
+#define RT5645_EQ_PRE_VOL_SFT 0
+
+/* EQ Post Volume Control (0xb4) */
+#define RT5645_EQ_PST_VOL_MASK (0xffff)
+#define RT5645_EQ_PST_VOL_SFT 0
+
+/* Jack Detect Control 3 (0xf8) */
+#define RT5645_CMP_MIC_IN_DET_MASK (0x7 << 12)
+#define RT5645_JD_CBJ_EN (0x1 << 7)
+#define RT5645_JD_CBJ_POL (0x1 << 6)
+#define RT5645_JD_TRI_CBJ_SEL_MASK (0x7 << 3)
+#define RT5645_JD_TRI_CBJ_SEL_SFT (3)
+#define RT5645_JD_TRI_HPO_SEL_MASK (0x7)
+#define RT5645_JD_TRI_HPO_SEL_SFT (0)
+#define RT5645_JD_F_GPIO_JD1 (0x0)
+#define RT5645_JD_F_JD1_1 (0x1)
+#define RT5645_JD_F_JD1_2 (0x2)
+#define RT5645_JD_F_JD2 (0x3)
+#define RT5645_JD_F_JD3 (0x4)
+#define RT5645_JD_F_GPIO_JD2 (0x5)
+#define RT5645_JD_F_MX0B_12 (0x6)
+
+/* Digital Misc Control (0xfa) */
+#define RT5645_RST_DSP (0x1 << 13)
+#define RT5645_IF1_ADC1_IN1_SEL (0x1 << 12)
+#define RT5645_IF1_ADC1_IN1_SFT 12
+#define RT5645_IF1_ADC1_IN2_SEL (0x1 << 11)
+#define RT5645_IF1_ADC1_IN2_SFT 11
+#define RT5645_IF1_ADC2_IN1_SEL (0x1 << 10)
+#define RT5645_IF1_ADC2_IN1_SFT 10
+#define RT5645_DIG_GATE_CTRL 0x1
+
+/* General Control2 (0xfb) */
+#define RT5645_RXDC_SRC_MASK (0x1 << 7)
+#define RT5645_RXDC_SRC_STO (0x0 << 7)
+#define RT5645_RXDC_SRC_MONO (0x1 << 7)
+#define RT5645_RXDC_SRC_SFT (7)
+#define RT5645_RXDP2_SEL_MASK (0x1 << 3)
+#define RT5645_RXDP2_SEL_IF2 (0x0 << 3)
+#define RT5645_RXDP2_SEL_ADC (0x1 << 3)
+#define RT5645_RXDP2_SEL_SFT (3)
+
+/* General Control3 (0xfc) */
+#define RT5645_JD_PSV_MODE (0x1 << 12)
+#define RT5645_IRQ_CLK_GATE_CTRL (0x1 << 11)
+#define RT5645_MICINDET_MANU (0x1 << 7)
+
+/* Vendor ID (0xfd) */
+#define RT5645_VER_C 0x2
+#define RT5645_VER_D 0x3
+
+
+/* Volume Rescale */
+#define RT5645_VOL_RSCL_MAX 0x27
+#define RT5645_VOL_RSCL_RANGE 0x1F
+/* Debug String Length */
+#define RT5645_REG_DISP_LEN 23
+
+
+/* System Clock Source */
+enum {
+ RT5645_SCLK_S_MCLK,
+ RT5645_SCLK_S_PLL1,
+ RT5645_SCLK_S_RCCLK,
+};
+
+/* PLL1 Source */
+enum {
+ RT5645_PLL1_S_MCLK,
+ RT5645_PLL1_S_BCLK1,
+ RT5645_PLL1_S_BCLK2,
+};
+
+enum {
+ RT5645_AIF1,
+ RT5645_AIF2,
+ RT5645_AIFS,
+};
+
+enum {
+ RT5645_DMIC_DATA_IN2P,
+ RT5645_DMIC_DATA_GPIO6,
+ RT5645_DMIC_DATA_GPIO10,
+ RT5645_DMIC_DATA_GPIO12,
+};
+
+enum {
+ RT5645_DMIC_DATA_IN2N,
+ RT5645_DMIC_DATA_GPIO5,
+ RT5645_DMIC_DATA_GPIO11,
+};
+
+enum {
+ CODEC_TYPE_RT5645,
+ CODEC_TYPE_RT5650,
+};
+
+/* filter mask */
+enum {
+ RT5645_DA_STEREO_FILTER = 0x1,
+ RT5645_DA_MONO_L_FILTER = (0x1 << 1),
+ RT5645_DA_MONO_R_FILTER = (0x1 << 2),
+ RT5645_AD_STEREO_FILTER = (0x1 << 3),
+ RT5645_AD_MONO_L_FILTER = (0x1 << 4),
+ RT5645_AD_MONO_R_FILTER = (0x1 << 5),
+};
+
+int rt5645_sel_asrc_clk_src(struct snd_soc_codec *codec,
+ 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_codec *codec,
+ struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack);
+
+#endif /* __RT5645_H__ */
diff --git a/kernel/sound/soc/codecs/rt5651.c b/kernel/sound/soc/codecs/rt5651.c
new file mode 100644
index 000000000..9f4c7be6d
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5651.c
@@ -0,0 +1,1820 @@
+/*
+ * 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/i2c.h>
+#include <linux/regmap.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 "rt5651.h"
+
+#define RT5651_DEVICE_ID_VALUE 0x6281
+
+#define RT5651_PR_RANGE_BASE (0xff + 1)
+#define RT5651_PR_SPACING 0x100
+
+#define RT5651_PR_BASE (RT5651_PR_RANGE_BASE + (0 * RT5651_PR_SPACING))
+
+static const struct regmap_range_cfg rt5651_ranges[] = {
+ { .name = "PR", .range_min = RT5651_PR_BASE,
+ .range_max = RT5651_PR_BASE + 0xb4,
+ .selector_reg = RT5651_PRIV_INDEX,
+ .selector_mask = 0xff,
+ .selector_shift = 0x0,
+ .window_start = RT5651_PRIV_DATA,
+ .window_len = 0x1, },
+};
+
+static struct reg_default init_list[] = {
+ {RT5651_PR_BASE + 0x3d, 0x3e00},
+};
+
+static const struct reg_default rt5651_reg[] = {
+ { 0x00, 0x0000 },
+ { 0x02, 0xc8c8 },
+ { 0x03, 0xc8c8 },
+ { 0x05, 0x0000 },
+ { 0x0d, 0x0000 },
+ { 0x0e, 0x0000 },
+ { 0x0f, 0x0808 },
+ { 0x10, 0x0808 },
+ { 0x19, 0xafaf },
+ { 0x1a, 0xafaf },
+ { 0x1b, 0x0c00 },
+ { 0x1c, 0x2f2f },
+ { 0x1d, 0x2f2f },
+ { 0x1e, 0x0000 },
+ { 0x27, 0x7860 },
+ { 0x28, 0x7070 },
+ { 0x29, 0x8080 },
+ { 0x2a, 0x5252 },
+ { 0x2b, 0x5454 },
+ { 0x2f, 0x0000 },
+ { 0x30, 0x5000 },
+ { 0x3b, 0x0000 },
+ { 0x3c, 0x006f },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x006f },
+ { 0x45, 0x6000 },
+ { 0x4d, 0x0000 },
+ { 0x4e, 0x0000 },
+ { 0x4f, 0x0279 },
+ { 0x50, 0x0000 },
+ { 0x51, 0x0000 },
+ { 0x52, 0x0279 },
+ { 0x53, 0xf000 },
+ { 0x61, 0x0000 },
+ { 0x62, 0x0000 },
+ { 0x63, 0x00c0 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x0000 },
+ { 0x66, 0x0000 },
+ { 0x70, 0x8000 },
+ { 0x71, 0x8000 },
+ { 0x73, 0x1104 },
+ { 0x74, 0x0c00 },
+ { 0x75, 0x1400 },
+ { 0x77, 0x0c00 },
+ { 0x78, 0x4000 },
+ { 0x79, 0x0123 },
+ { 0x80, 0x0000 },
+ { 0x81, 0x0000 },
+ { 0x82, 0x0000 },
+ { 0x83, 0x0800 },
+ { 0x84, 0x0000 },
+ { 0x85, 0x0008 },
+ { 0x89, 0x0000 },
+ { 0x8e, 0x0004 },
+ { 0x8f, 0x1100 },
+ { 0x90, 0x0000 },
+ { 0x93, 0x2000 },
+ { 0x94, 0x0200 },
+ { 0xb0, 0x2080 },
+ { 0xb1, 0x0000 },
+ { 0xb4, 0x2206 },
+ { 0xb5, 0x1f00 },
+ { 0xb6, 0x0000 },
+ { 0xbb, 0x0000 },
+ { 0xbc, 0x0000 },
+ { 0xbd, 0x0000 },
+ { 0xbe, 0x0000 },
+ { 0xbf, 0x0000 },
+ { 0xc0, 0x0400 },
+ { 0xc1, 0x0000 },
+ { 0xc2, 0x0000 },
+ { 0xcf, 0x0013 },
+ { 0xd0, 0x0680 },
+ { 0xd1, 0x1c17 },
+ { 0xd3, 0xb320 },
+ { 0xd9, 0x0809 },
+ { 0xfa, 0x0010 },
+ { 0xfe, 0x10ec },
+ { 0xff, 0x6281 },
+};
+
+static bool rt5651_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5651_ranges); i++) {
+ if ((reg >= rt5651_ranges[i].window_start &&
+ reg <= rt5651_ranges[i].window_start +
+ rt5651_ranges[i].window_len) ||
+ (reg >= rt5651_ranges[i].range_min &&
+ reg <= rt5651_ranges[i].range_max)) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT5651_RESET:
+ case RT5651_PRIV_DATA:
+ case RT5651_EQ_CTRL1:
+ case RT5651_ALC_1:
+ case RT5651_IRQ_CTRL2:
+ case RT5651_INT_IRQ_ST:
+ case RT5651_PGM_REG_ARR1:
+ case RT5651_PGM_REG_ARR3:
+ case RT5651_VENDOR_ID:
+ case RT5651_DEVICE_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5651_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5651_ranges); i++) {
+ if ((reg >= rt5651_ranges[i].window_start &&
+ reg <= rt5651_ranges[i].window_start +
+ rt5651_ranges[i].window_len) ||
+ (reg >= rt5651_ranges[i].range_min &&
+ reg <= rt5651_ranges[i].range_max)) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT5651_RESET:
+ case RT5651_VERSION_ID:
+ case RT5651_VENDOR_ID:
+ case RT5651_DEVICE_ID:
+ case RT5651_HP_VOL:
+ case RT5651_LOUT_CTRL1:
+ case RT5651_LOUT_CTRL2:
+ case RT5651_IN1_IN2:
+ case RT5651_IN3:
+ case RT5651_INL1_INR1_VOL:
+ case RT5651_INL2_INR2_VOL:
+ case RT5651_DAC1_DIG_VOL:
+ case RT5651_DAC2_DIG_VOL:
+ case RT5651_DAC2_CTRL:
+ case RT5651_ADC_DIG_VOL:
+ case RT5651_ADC_DATA:
+ case RT5651_ADC_BST_VOL:
+ case RT5651_STO1_ADC_MIXER:
+ case RT5651_STO2_ADC_MIXER:
+ case RT5651_AD_DA_MIXER:
+ case RT5651_STO_DAC_MIXER:
+ case RT5651_DD_MIXER:
+ case RT5651_DIG_INF_DATA:
+ case RT5651_PDM_CTL:
+ case RT5651_REC_L1_MIXER:
+ case RT5651_REC_L2_MIXER:
+ case RT5651_REC_R1_MIXER:
+ case RT5651_REC_R2_MIXER:
+ case RT5651_HPO_MIXER:
+ case RT5651_OUT_L1_MIXER:
+ case RT5651_OUT_L2_MIXER:
+ case RT5651_OUT_L3_MIXER:
+ case RT5651_OUT_R1_MIXER:
+ case RT5651_OUT_R2_MIXER:
+ case RT5651_OUT_R3_MIXER:
+ case RT5651_LOUT_MIXER:
+ case RT5651_PWR_DIG1:
+ case RT5651_PWR_DIG2:
+ case RT5651_PWR_ANLG1:
+ case RT5651_PWR_ANLG2:
+ case RT5651_PWR_MIXER:
+ case RT5651_PWR_VOL:
+ case RT5651_PRIV_INDEX:
+ case RT5651_PRIV_DATA:
+ case RT5651_I2S1_SDP:
+ case RT5651_I2S2_SDP:
+ case RT5651_ADDA_CLK1:
+ case RT5651_ADDA_CLK2:
+ case RT5651_DMIC:
+ case RT5651_TDM_CTL_1:
+ case RT5651_TDM_CTL_2:
+ case RT5651_TDM_CTL_3:
+ case RT5651_GLB_CLK:
+ case RT5651_PLL_CTRL1:
+ case RT5651_PLL_CTRL2:
+ case RT5651_PLL_MODE_1:
+ case RT5651_PLL_MODE_2:
+ case RT5651_PLL_MODE_3:
+ case RT5651_PLL_MODE_4:
+ case RT5651_PLL_MODE_5:
+ case RT5651_PLL_MODE_6:
+ case RT5651_PLL_MODE_7:
+ case RT5651_DEPOP_M1:
+ case RT5651_DEPOP_M2:
+ case RT5651_DEPOP_M3:
+ case RT5651_CHARGE_PUMP:
+ case RT5651_MICBIAS:
+ case RT5651_A_JD_CTL1:
+ case RT5651_EQ_CTRL1:
+ case RT5651_EQ_CTRL2:
+ case RT5651_ALC_1:
+ case RT5651_ALC_2:
+ case RT5651_ALC_3:
+ case RT5651_JD_CTRL1:
+ case RT5651_JD_CTRL2:
+ case RT5651_IRQ_CTRL1:
+ case RT5651_IRQ_CTRL2:
+ case RT5651_INT_IRQ_ST:
+ case RT5651_GPIO_CTRL1:
+ case RT5651_GPIO_CTRL2:
+ case RT5651_GPIO_CTRL3:
+ case RT5651_PGM_REG_ARR1:
+ case RT5651_PGM_REG_ARR2:
+ case RT5651_PGM_REG_ARR3:
+ case RT5651_PGM_REG_ARR4:
+ case RT5651_PGM_REG_ARR5:
+ case RT5651_SCB_FUNC:
+ case RT5651_SCB_CTRL:
+ case RT5651_BASE_BACK:
+ case RT5651_MP3_PLUS1:
+ case RT5651_MP3_PLUS2:
+ case RT5651_ADJ_HPF_CTRL1:
+ case RT5651_ADJ_HPF_CTRL2:
+ case RT5651_HP_CALIB_AMP_DET:
+ case RT5651_HP_CALIB2:
+ case RT5651_SV_ZCD1:
+ case RT5651_SV_ZCD2:
+ case RT5651_D_MISC:
+ case RT5651_DUMMY2:
+ case RT5651_DUMMY3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_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 unsigned int bst_tlv[] = {
+ TLV_DB_RANGE_HEAD(7),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
+};
+
+/* Interface data select */
+static const char * const rt5651_data_select[] = {
+ "Normal", "Swap", "left copy to right", "right copy to left"};
+
+static SOC_ENUM_SINGLE_DECL(rt5651_if2_dac_enum, RT5651_DIG_INF_DATA,
+ RT5651_IF2_DAC_SEL_SFT, rt5651_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5651_if2_adc_enum, RT5651_DIG_INF_DATA,
+ RT5651_IF2_ADC_SEL_SFT, rt5651_data_select);
+
+static const struct snd_kcontrol_new rt5651_snd_controls[] = {
+ /* Headphone Output Volume */
+ SOC_DOUBLE_TLV("HP Playback Volume", RT5651_HP_VOL,
+ RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* OUTPUT Control */
+ SOC_DOUBLE_TLV("OUT Playback Volume", RT5651_LOUT_CTRL1,
+ RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 39, 1, out_vol_tlv),
+
+ /* DAC Digital Volume */
+ SOC_DOUBLE("DAC2 Playback Switch", RT5651_DAC2_CTRL,
+ RT5651_M_DAC_L2_VOL_SFT, RT5651_M_DAC_R2_VOL_SFT, 1, 1),
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5651_DAC1_DIG_VOL,
+ RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
+ SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5651_DAC2_DIG_VOL,
+ RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
+ /* IN1/IN2 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),
+ /* INL/INR Volume Control */
+ SOC_DOUBLE_TLV("IN Capture Volume", RT5651_INL1_INR1_VOL,
+ RT5651_INL_VOL_SFT, RT5651_INR_VOL_SFT,
+ 31, 1, in_vol_tlv),
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("ADC Capture Switch", RT5651_ADC_DIG_VOL,
+ RT5651_L_MUTE_SFT, RT5651_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("ADC Capture Volume", RT5651_ADC_DIG_VOL,
+ RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
+ SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5651_ADC_DATA,
+ RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("ADC Boost Gain", RT5651_ADC_BST_VOL,
+ RT5651_ADC_L_BST_SFT, RT5651_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+
+ /* ASRC */
+ SOC_SINGLE("IF1 ASRC Switch", RT5651_PLL_MODE_1,
+ RT5651_STO1_T_SFT, 1, 0),
+ SOC_SINGLE("IF2 ASRC Switch", RT5651_PLL_MODE_1,
+ RT5651_STO2_T_SFT, 1, 0),
+ SOC_SINGLE("DMIC ASRC Switch", RT5651_PLL_MODE_1,
+ RT5651_DMIC_1_M_SFT, 1, 0),
+
+ SOC_ENUM("ADC IF2 Data Switch", rt5651_if2_adc_enum),
+ SOC_ENUM("DAC IF2 Data Switch", rt5651_if2_dac_enum),
+};
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_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);
+
+ if (idx < 0)
+ dev_err(codec->dev, "Failed to set DMIC clock\n");
+ else
+ snd_soc_update_bits(codec, 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,
+ RT5651_M_STO1_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO1_ADC_MIXER,
+ RT5651_M_STO1_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO1_ADC_MIXER,
+ RT5651_M_STO1_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO1_ADC_MIXER,
+ RT5651_M_STO1_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_sto2_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO2_ADC_MIXER,
+ RT5651_M_STO2_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO2_ADC_MIXER,
+ RT5651_M_STO2_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_sto2_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO2_ADC_MIXER,
+ RT5651_M_STO2_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO2_ADC_MIXER,
+ RT5651_M_STO2_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5651_AD_DA_MIXER,
+ RT5651_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INF1 Switch", RT5651_AD_DA_MIXER,
+ RT5651_M_IF1_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5651_AD_DA_MIXER,
+ RT5651_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INF1 Switch", RT5651_AD_DA_MIXER,
+ RT5651_M_IF1_DAC_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_sto_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_STO_DAC_MIXER,
+ RT5651_M_DAC_L1_MIXL_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5651_STO_DAC_MIXER,
+ RT5651_M_DAC_L2_MIXL_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_STO_DAC_MIXER,
+ RT5651_M_DAC_R1_MIXL_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_sto_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_STO_DAC_MIXER,
+ RT5651_M_DAC_R1_MIXR_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5651_STO_DAC_MIXER,
+ RT5651_M_DAC_R2_MIXR_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_STO_DAC_MIXER,
+ RT5651_M_DAC_L1_MIXR_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_dd_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_DD_MIXER,
+ RT5651_M_STO_DD_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5651_DD_MIXER,
+ RT5651_M_STO_DD_L2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5651_DD_MIXER,
+ RT5651_M_STO_DD_R2_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_dd_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_DD_MIXER,
+ RT5651_M_STO_DD_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5651_DD_MIXER,
+ RT5651_M_STO_DD_R2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5651_DD_MIXER,
+ RT5651_M_STO_DD_L2_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5651_rec_l_mix[] = {
+ SOC_DAPM_SINGLE("INL1 Switch", RT5651_REC_L2_MIXER,
+ RT5651_M_IN1_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5651_REC_L2_MIXER,
+ RT5651_M_BST3_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5651_REC_L2_MIXER,
+ RT5651_M_BST2_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5651_REC_L2_MIXER,
+ RT5651_M_BST1_RM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_rec_r_mix[] = {
+ SOC_DAPM_SINGLE("INR1 Switch", RT5651_REC_R2_MIXER,
+ RT5651_M_IN1_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST3 Switch", RT5651_REC_R2_MIXER,
+ RT5651_M_BST3_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5651_REC_R2_MIXER,
+ RT5651_M_BST2_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5651_REC_R2_MIXER,
+ RT5651_M_BST1_RM_R_SFT, 1, 1),
+};
+
+/* Analog Output Mixer */
+
+static const struct snd_kcontrol_new rt5651_out_l_mix[] = {
+ SOC_DAPM_SINGLE("BST1 Switch", RT5651_OUT_L3_MIXER,
+ RT5651_M_BST1_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5651_OUT_L3_MIXER,
+ RT5651_M_BST2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL1 Switch", RT5651_OUT_L3_MIXER,
+ RT5651_M_IN1_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXL Switch", RT5651_OUT_L3_MIXER,
+ RT5651_M_RM_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_OUT_L3_MIXER,
+ RT5651_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_out_r_mix[] = {
+ SOC_DAPM_SINGLE("BST2 Switch", RT5651_OUT_R3_MIXER,
+ RT5651_M_BST2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5651_OUT_R3_MIXER,
+ RT5651_M_BST1_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR1 Switch", RT5651_OUT_R3_MIXER,
+ RT5651_M_IN1_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXR Switch", RT5651_OUT_R3_MIXER,
+ RT5651_M_RM_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_OUT_R3_MIXER,
+ RT5651_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_hpo_mix[] = {
+ SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5651_HPO_MIXER,
+ RT5651_M_DAC1_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5651_HPO_MIXER,
+ RT5651_M_HPVOL_HM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_lout_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_LOUT_MIXER,
+ RT5651_M_DAC_L1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_LOUT_MIXER,
+ RT5651_M_DAC_R1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL L Switch", RT5651_LOUT_MIXER,
+ RT5651_M_OV_L_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL R Switch", RT5651_LOUT_MIXER,
+ RT5651_M_OV_R_LM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new outvol_l_control =
+ SOC_DAPM_SINGLE("Switch", RT5651_LOUT_CTRL1,
+ RT5651_VOL_L_SFT, 1, 1);
+
+static const struct snd_kcontrol_new outvol_r_control =
+ SOC_DAPM_SINGLE("Switch", RT5651_LOUT_CTRL1,
+ RT5651_VOL_R_SFT, 1, 1);
+
+static const struct snd_kcontrol_new lout_l_mute_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_LOUT_CTRL1,
+ RT5651_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new lout_r_mute_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_LOUT_CTRL1,
+ RT5651_R_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpovol_l_control =
+ SOC_DAPM_SINGLE("Switch", RT5651_HP_VOL,
+ RT5651_VOL_L_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpovol_r_control =
+ SOC_DAPM_SINGLE("Switch", RT5651_HP_VOL,
+ RT5651_VOL_R_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpo_l_mute_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_HP_VOL,
+ RT5651_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpo_r_mute_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_HP_VOL,
+ RT5651_R_MUTE_SFT, 1, 1);
+
+/* 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"};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5651_stereo1_adc1_enum, RT5651_STO1_ADC_MIXER,
+ RT5651_STO1_ADC_1_SRC_SFT, rt5651_stereo1_adc1_src);
+
+static const struct snd_kcontrol_new rt5651_sto1_adc_l1_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC L1 source", rt5651_stereo1_adc1_enum);
+
+static const struct snd_kcontrol_new rt5651_sto1_adc_r1_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC R1 source", rt5651_stereo1_adc1_enum);
+
+static const char * const rt5651_stereo1_adc2_src[] = {"DMIC", "DD MIX"};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5651_stereo1_adc2_enum, RT5651_STO1_ADC_MIXER,
+ RT5651_STO1_ADC_2_SRC_SFT, rt5651_stereo1_adc2_src);
+
+static const struct snd_kcontrol_new rt5651_sto1_adc_l2_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC L2 source", rt5651_stereo1_adc2_enum);
+
+static const struct snd_kcontrol_new rt5651_sto1_adc_r2_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC R2 source", rt5651_stereo1_adc2_enum);
+
+/* Mono ADC source */
+static const char * const rt5651_sto2_adc_l1_src[] = {"DD MIXL", "ADCL"};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5651_sto2_adc_l1_enum, RT5651_STO1_ADC_MIXER,
+ RT5651_STO2_ADC_L1_SRC_SFT, rt5651_sto2_adc_l1_src);
+
+static const struct snd_kcontrol_new rt5651_sto2_adc_l1_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC1 left source", rt5651_sto2_adc_l1_enum);
+
+static const char * const rt5651_sto2_adc_l2_src[] = {"DMIC L", "DD MIXL"};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5651_sto2_adc_l2_enum, RT5651_STO1_ADC_MIXER,
+ RT5651_STO2_ADC_L2_SRC_SFT, rt5651_sto2_adc_l2_src);
+
+static const struct snd_kcontrol_new rt5651_sto2_adc_l2_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC2 left source", rt5651_sto2_adc_l2_enum);
+
+static const char * const rt5651_sto2_adc_r1_src[] = {"DD MIXR", "ADCR"};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5651_sto2_adc_r1_enum, RT5651_STO1_ADC_MIXER,
+ RT5651_STO2_ADC_R1_SRC_SFT, rt5651_sto2_adc_r1_src);
+
+static const struct snd_kcontrol_new rt5651_sto2_adc_r1_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC1 right source", rt5651_sto2_adc_r1_enum);
+
+static const char * const rt5651_sto2_adc_r2_src[] = {"DMIC R", "DD MIXR"};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5651_sto2_adc_r2_enum, RT5651_STO1_ADC_MIXER,
+ RT5651_STO2_ADC_R2_SRC_SFT, rt5651_sto2_adc_r2_src);
+
+static const struct snd_kcontrol_new rt5651_sto2_adc_r2_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC2 right source", rt5651_sto2_adc_r2_enum);
+
+/* DAC2 channel source */
+
+static const char * const rt5651_dac_src[] = {"IF1", "IF2"};
+
+static SOC_ENUM_SINGLE_DECL(rt5651_dac_l2_enum, RT5651_DAC2_CTRL,
+ RT5651_SEL_DAC_L2_SFT, rt5651_dac_src);
+
+static const struct snd_kcontrol_new rt5651_dac_l2_mux =
+ SOC_DAPM_ENUM("DAC2 left channel source", rt5651_dac_l2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5651_dac_r2_enum, RT5651_DAC2_CTRL,
+ RT5651_SEL_DAC_R2_SFT, rt5651_dac_src);
+
+static const struct snd_kcontrol_new rt5651_dac_r2_mux =
+ SOC_DAPM_ENUM("DAC2 right channel source", rt5651_dac_r2_enum);
+
+/* IF2_ADC channel source */
+
+static const char * const rt5651_adc_src[] = {"IF1 ADC1", "IF1 ADC2"};
+
+static SOC_ENUM_SINGLE_DECL(rt5651_if2_adc_src_enum, RT5651_DIG_INF_DATA,
+ RT5651_IF2_ADC_SRC_SFT, rt5651_adc_src);
+
+static const struct snd_kcontrol_new rt5651_if2_adc_src_mux =
+ SOC_DAPM_ENUM("IF2 ADC channel source", rt5651_if2_adc_src_enum);
+
+/* PDM select */
+static const char * const rt5651_pdm_sel[] = {"DD MIX", "Stereo DAC MIX"};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5651_pdm_l_sel_enum, RT5651_PDM_CTL,
+ RT5651_PDM_L_SEL_SFT, rt5651_pdm_sel);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5651_pdm_r_sel_enum, RT5651_PDM_CTL,
+ RT5651_PDM_R_SEL_SFT, rt5651_pdm_sel);
+
+static const struct snd_kcontrol_new rt5651_pdm_l_mux =
+ SOC_DAPM_ENUM("PDM L select", rt5651_pdm_l_sel_enum);
+
+static const struct snd_kcontrol_new rt5651_pdm_r_mux =
+ SOC_DAPM_ENUM("PDM R select", rt5651_pdm_r_sel_enum);
+
+static int rt5651_amp_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* depop parameters */
+ regmap_update_bits(rt5651->regmap, RT5651_PR_BASE +
+ RT5651_CHPUMP_INT_REG1, 0x0700, 0x0200);
+ regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M2,
+ RT5651_DEPOP_MASK, RT5651_DEPOP_MAN);
+ regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M1,
+ RT5651_HP_CP_MASK | RT5651_HP_SG_MASK |
+ RT5651_HP_CB_MASK, RT5651_HP_CP_PU |
+ RT5651_HP_SG_DIS | RT5651_HP_CB_PU);
+ regmap_write(rt5651->regmap, RT5651_PR_BASE +
+ RT5651_HP_DCC_INT1, 0x9f00);
+ /* headphone amp power on */
+ regmap_update_bits(rt5651->regmap, RT5651_PWR_ANLG1,
+ RT5651_PWR_FV1 | RT5651_PWR_FV2, 0);
+ regmap_update_bits(rt5651->regmap, RT5651_PWR_ANLG1,
+ RT5651_PWR_HA,
+ RT5651_PWR_HA);
+ usleep_range(10000, 15000);
+ regmap_update_bits(rt5651->regmap, RT5651_PWR_ANLG1,
+ RT5651_PWR_FV1 | RT5651_PWR_FV2 ,
+ RT5651_PWR_FV1 | RT5651_PWR_FV2);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5651_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* headphone unmute sequence */
+ regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M2,
+ RT5651_DEPOP_MASK | RT5651_DIG_DP_MASK,
+ RT5651_DEPOP_AUTO | RT5651_DIG_DP_EN);
+ regmap_update_bits(rt5651->regmap, RT5651_CHARGE_PUMP,
+ RT5651_PM_HP_MASK, RT5651_PM_HP_HV);
+
+ regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M3,
+ RT5651_CP_FQ1_MASK | RT5651_CP_FQ2_MASK |
+ RT5651_CP_FQ3_MASK,
+ (RT5651_CP_FQ_192_KHZ << RT5651_CP_FQ1_SFT) |
+ (RT5651_CP_FQ_12_KHZ << RT5651_CP_FQ2_SFT) |
+ (RT5651_CP_FQ_192_KHZ << RT5651_CP_FQ3_SFT));
+
+ regmap_write(rt5651->regmap, RT5651_PR_BASE +
+ RT5651_MAMP_INT_REG2, 0x1c00);
+ regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M1,
+ RT5651_HP_CP_MASK | RT5651_HP_SG_MASK,
+ RT5651_HP_CP_PD | RT5651_HP_SG_EN);
+ regmap_update_bits(rt5651->regmap, RT5651_PR_BASE +
+ RT5651_CHPUMP_INT_REG1, 0x0700, 0x0400);
+ rt5651->hp_mute = 0;
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ rt5651->hp_mute = 1;
+ usleep_range(70000, 75000);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5651_hp_post_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!rt5651->hp_mute)
+ usleep_range(80000, 85000);
+
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5651_bst1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, 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,
+ RT5651_PWR_BST1_OP2, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5651_bst2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, 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,
+ RT5651_PWR_BST2_OP2, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5651_bst3_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, 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,
+ RT5651_PWR_BST3_OP2, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = {
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5651_PLL_MODE_2,
+ 15, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5651_PLL_MODE_2,
+ 14, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("STO1 DAC ASRC", 1, RT5651_PLL_MODE_2,
+ 13, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("STO2 DAC ASRC", 1, RT5651_PLL_MODE_2,
+ 12, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC ASRC", 1, RT5651_PLL_MODE_2,
+ 11, 0, NULL, 0),
+
+ 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),
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("MIC3"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+ SND_SOC_DAPM_INPUT("IN2P"),
+ SND_SOC_DAPM_INPUT("IN2N"),
+ SND_SOC_DAPM_INPUT("IN3P"),
+ SND_SOC_DAPM_INPUT("DMIC L1"),
+ SND_SOC_DAPM_INPUT("DMIC R1"),
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", RT5651_DMIC, RT5651_DMIC_1_EN_SFT,
+ 0, set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ /* Boost */
+ SND_SOC_DAPM_PGA_E("BST1", RT5651_PWR_ANLG2,
+ RT5651_PWR_BST1_BIT, 0, NULL, 0, rt5651_bst1_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("BST2", RT5651_PWR_ANLG2,
+ RT5651_PWR_BST2_BIT, 0, NULL, 0, rt5651_bst2_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("BST3", RT5651_PWR_ANLG2,
+ RT5651_PWR_BST3_BIT, 0, NULL, 0, rt5651_bst3_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ /* Input Volume */
+ SND_SOC_DAPM_PGA("INL1 VOL", RT5651_PWR_VOL,
+ RT5651_PWR_IN1_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR1 VOL", RT5651_PWR_VOL,
+ RT5651_PWR_IN1_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INL2 VOL", RT5651_PWR_VOL,
+ RT5651_PWR_IN2_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR2 VOL", RT5651_PWR_VOL,
+ RT5651_PWR_IN2_R_BIT, 0, NULL, 0),
+ /* 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)),
+ SND_SOC_DAPM_MIXER("RECMIXR", RT5651_PWR_MIXER, RT5651_PWR_RM_R_BIT, 0,
+ rt5651_rec_r_mix, ARRAY_SIZE(rt5651_rec_r_mix)),
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("ADC L Power", RT5651_PWR_DIG1,
+ RT5651_PWR_ADC_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC R Power", RT5651_PWR_DIG1,
+ RT5651_PWR_ADC_R_BIT, 0, NULL, 0),
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5651_sto1_adc_l2_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5651_sto1_adc_r2_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5651_sto1_adc_l1_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5651_sto1_adc_r1_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5651_sto2_adc_l2_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5651_sto2_adc_l1_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5651_sto2_adc_r1_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5651_sto2_adc_r2_mux),
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("Stereo1 Filter", RT5651_PWR_DIG2,
+ RT5651_PWR_ADC_STO1_F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Stereo2 Filter", RT5651_PWR_DIG2,
+ RT5651_PWR_ADC_STO2_F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5651_sto1_adc_l_mix,
+ ARRAY_SIZE(rt5651_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5651_sto1_adc_r_mix,
+ ARRAY_SIZE(rt5651_sto1_adc_r_mix)),
+ SND_SOC_DAPM_MIXER("Stereo2 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5651_sto2_adc_l_mix,
+ ARRAY_SIZE(rt5651_sto2_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo2 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5651_sto2_adc_r_mix,
+ ARRAY_SIZE(rt5651_sto2_adc_r_mix)),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5651_PWR_DIG1,
+ RT5651_PWR_I2S1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S2", RT5651_PWR_DIG1,
+ RT5651_PWR_I2S2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("IF2 ADC", SND_SOC_NOPM, 0, 0,
+ &rt5651_if2_adc_src_mux),
+
+ /* Digital Interface Select */
+
+ SND_SOC_DAPM_MUX("PDM L Mux", RT5651_PDM_CTL,
+ RT5651_M_PDM_L_SFT, 1, &rt5651_pdm_l_mux),
+ SND_SOC_DAPM_MUX("PDM R Mux", RT5651_PDM_CTL,
+ RT5651_M_PDM_R_SFT, 1, &rt5651_pdm_r_mux),
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Audio DSP */
+ SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5651_dac_l_mix, ARRAY_SIZE(rt5651_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5651_dac_r_mix, ARRAY_SIZE(rt5651_dac_r_mix)),
+
+ /* DAC2 channel Mux */
+ SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_dac_l2_mux),
+ SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_dac_r2_mux),
+ SND_SOC_DAPM_PGA("DAC L2 Volume", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC R2 Volume", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5651_PWR_DIG2,
+ RT5651_PWR_DAC_STO1_F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Stero2 DAC Power", RT5651_PWR_DIG2,
+ RT5651_PWR_DAC_STO2_F_BIT, 0, NULL, 0),
+ /* DAC Mixer */
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5651_sto_dac_l_mix,
+ ARRAY_SIZE(rt5651_sto_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5651_sto_dac_r_mix,
+ ARRAY_SIZE(rt5651_sto_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("DD MIXL", SND_SOC_NOPM, 0, 0,
+ rt5651_dd_dac_l_mix,
+ ARRAY_SIZE(rt5651_dd_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DD MIXR", SND_SOC_NOPM, 0, 0,
+ rt5651_dd_dac_r_mix,
+ ARRAY_SIZE(rt5651_dd_dac_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("DAC L1 Power", RT5651_PWR_DIG1,
+ RT5651_PWR_DAC_L1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5651_PWR_DIG1,
+ RT5651_PWR_DAC_R1_BIT, 0, NULL, 0),
+ /* OUT Mixer */
+ SND_SOC_DAPM_MIXER("OUT MIXL", RT5651_PWR_MIXER, RT5651_PWR_OM_L_BIT,
+ 0, rt5651_out_l_mix, ARRAY_SIZE(rt5651_out_l_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXR", RT5651_PWR_MIXER, RT5651_PWR_OM_R_BIT,
+ 0, rt5651_out_r_mix, ARRAY_SIZE(rt5651_out_r_mix)),
+ /* Ouput Volume */
+ SND_SOC_DAPM_SWITCH("OUTVOL L", RT5651_PWR_VOL,
+ RT5651_PWR_OV_L_BIT, 0, &outvol_l_control),
+ SND_SOC_DAPM_SWITCH("OUTVOL R", RT5651_PWR_VOL,
+ RT5651_PWR_OV_R_BIT, 0, &outvol_r_control),
+ SND_SOC_DAPM_SWITCH("HPOVOL L", RT5651_PWR_VOL,
+ RT5651_PWR_HV_L_BIT, 0, &hpovol_l_control),
+ SND_SOC_DAPM_SWITCH("HPOVOL R", RT5651_PWR_VOL,
+ RT5651_PWR_HV_R_BIT, 0, &hpovol_r_control),
+ SND_SOC_DAPM_PGA("INL1", RT5651_PWR_VOL,
+ RT5651_PWR_IN1_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR1", RT5651_PWR_VOL,
+ RT5651_PWR_IN1_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INL2", RT5651_PWR_VOL,
+ RT5651_PWR_IN2_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR2", RT5651_PWR_VOL,
+ RT5651_PWR_IN2_R_BIT, 0, NULL, 0),
+ /* HPO/LOUT/Mono Mixer */
+ SND_SOC_DAPM_MIXER("HPOL MIX", SND_SOC_NOPM, 0, 0,
+ rt5651_hpo_mix, ARRAY_SIZE(rt5651_hpo_mix)),
+ SND_SOC_DAPM_MIXER("HPOR MIX", SND_SOC_NOPM, 0, 0,
+ rt5651_hpo_mix, ARRAY_SIZE(rt5651_hpo_mix)),
+ SND_SOC_DAPM_SUPPLY("HP L Amp", RT5651_PWR_ANLG1,
+ RT5651_PWR_HP_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HP R Amp", RT5651_PWR_ANLG1,
+ RT5651_PWR_HP_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("LOUT MIX", RT5651_PWR_ANLG1, RT5651_PWR_LM_BIT, 0,
+ rt5651_lout_mix, ARRAY_SIZE(rt5651_lout_mix)),
+
+ SND_SOC_DAPM_SUPPLY("Amp Power", RT5651_PWR_ANLG1,
+ RT5651_PWR_HA_BIT, 0, rt5651_amp_power_event,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5651_hp_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SWITCH("HPO L Playback", SND_SOC_NOPM, 0, 0,
+ &hpo_l_mute_control),
+ SND_SOC_DAPM_SWITCH("HPO R Playback", SND_SOC_NOPM, 0, 0,
+ &hpo_r_mute_control),
+ SND_SOC_DAPM_SWITCH("LOUT L Playback", SND_SOC_NOPM, 0, 0,
+ &lout_l_mute_control),
+ SND_SOC_DAPM_SWITCH("LOUT R Playback", SND_SOC_NOPM, 0, 0,
+ &lout_r_mute_control),
+ SND_SOC_DAPM_POST("HP Post", rt5651_hp_post_event),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("LOUTL"),
+ SND_SOC_DAPM_OUTPUT("LOUTR"),
+ SND_SOC_DAPM_OUTPUT("PDML"),
+ SND_SOC_DAPM_OUTPUT("PDMR"),
+};
+
+static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
+ {"Stero1 DAC Power", NULL, "STO1 DAC ASRC"},
+ {"Stero2 DAC Power", NULL, "STO2 DAC ASRC"},
+ {"I2S1", NULL, "I2S1 ASRC"},
+ {"I2S2", NULL, "I2S2 ASRC"},
+
+ {"IN1P", NULL, "LDO"},
+ {"IN2P", NULL, "LDO"},
+ {"IN3P", NULL, "LDO"},
+
+ {"IN1P", NULL, "MIC1"},
+ {"IN2P", NULL, "MIC2"},
+ {"IN2N", NULL, "MIC2"},
+ {"IN3P", NULL, "MIC3"},
+
+ {"BST1", NULL, "IN1P"},
+ {"BST2", NULL, "IN2P"},
+ {"BST2", NULL, "IN2N"},
+ {"BST3", NULL, "IN3P"},
+
+ {"INL1 VOL", NULL, "IN2P"},
+ {"INR1 VOL", NULL, "IN2N"},
+
+ {"RECMIXL", "INL1 Switch", "INL1 VOL"},
+ {"RECMIXL", "BST3 Switch", "BST3"},
+ {"RECMIXL", "BST2 Switch", "BST2"},
+ {"RECMIXL", "BST1 Switch", "BST1"},
+
+ {"RECMIXR", "INR1 Switch", "INR1 VOL"},
+ {"RECMIXR", "BST3 Switch", "BST3"},
+ {"RECMIXR", "BST2 Switch", "BST2"},
+ {"RECMIXR", "BST1 Switch", "BST1"},
+
+ {"ADC L", NULL, "RECMIXL"},
+ {"ADC L", NULL, "ADC L Power"},
+ {"ADC R", NULL, "RECMIXR"},
+ {"ADC R", NULL, "ADC R Power"},
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+
+ {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"},
+ {"Stereo1 ADC L2 Mux", "DD MIX", "DD MIXL"},
+ {"Stereo1 ADC L1 Mux", "ADC", "ADC L"},
+ {"Stereo1 ADC L1 Mux", "DD MIX", "DD MIXL"},
+
+ {"Stereo1 ADC R1 Mux", "ADC", "ADC R"},
+ {"Stereo1 ADC R1 Mux", "DD MIX", "DD MIXR"},
+ {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"},
+ {"Stereo1 ADC R2 Mux", "DD MIX", "DD MIXR"},
+
+ {"Stereo2 ADC L2 Mux", "DMIC L", "DMIC L1"},
+ {"Stereo2 ADC L2 Mux", "DD MIXL", "DD MIXL"},
+ {"Stereo2 ADC L1 Mux", "DD MIXL", "DD MIXL"},
+ {"Stereo2 ADC L1 Mux", "ADCL", "ADC L"},
+
+ {"Stereo2 ADC R1 Mux", "DD MIXR", "DD MIXR"},
+ {"Stereo2 ADC R1 Mux", "ADCR", "ADC R"},
+ {"Stereo2 ADC R2 Mux", "DMIC R", "DMIC R1"},
+ {"Stereo2 ADC R2 Mux", "DD MIXR", "DD MIXR"},
+
+ {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"},
+ {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"},
+ {"Stereo1 ADC MIXL", NULL, "Stereo1 Filter"},
+ {"Stereo1 Filter", NULL, "PLL1", is_sysclk_from_pll},
+ {"Stereo1 Filter", NULL, "ADC ASRC"},
+
+ {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"},
+ {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"},
+ {"Stereo1 ADC MIXR", NULL, "Stereo1 Filter"},
+
+ {"Stereo2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC L1 Mux"},
+ {"Stereo2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC L2 Mux"},
+ {"Stereo2 ADC MIXL", NULL, "Stereo2 Filter"},
+ {"Stereo2 Filter", NULL, "PLL1", is_sysclk_from_pll},
+ {"Stereo2 Filter", NULL, "ADC ASRC"},
+
+ {"Stereo2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC R1 Mux"},
+ {"Stereo2 ADC MIXR", "ADC2 Switch", "Stereo2 ADC R2 Mux"},
+ {"Stereo2 ADC MIXR", NULL, "Stereo2 Filter"},
+
+ {"IF1 ADC2", NULL, "Stereo2 ADC MIXL"},
+ {"IF1 ADC2", NULL, "Stereo2 ADC MIXR"},
+ {"IF1 ADC1", NULL, "Stereo1 ADC MIXL"},
+ {"IF1 ADC1", NULL, "Stereo1 ADC MIXR"},
+
+ {"IF1 ADC1", NULL, "I2S1"},
+
+ {"IF2 ADC", "IF1 ADC1", "IF1 ADC1"},
+ {"IF2 ADC", "IF1 ADC2", "IF1 ADC2"},
+ {"IF2 ADC", NULL, "I2S2"},
+
+ {"AIF1TX", NULL, "IF1 ADC1"},
+ {"AIF1TX", NULL, "IF1 ADC2"},
+ {"AIF2TX", NULL, "IF2 ADC"},
+
+ {"IF1 DAC", NULL, "AIF1RX"},
+ {"IF1 DAC", NULL, "I2S1"},
+ {"IF2 DAC", NULL, "AIF2RX"},
+ {"IF2 DAC", NULL, "I2S2"},
+
+ {"IF1 DAC1 L", NULL, "IF1 DAC"},
+ {"IF1 DAC1 R", NULL, "IF1 DAC"},
+ {"IF1 DAC2 L", NULL, "IF1 DAC"},
+ {"IF1 DAC2 R", NULL, "IF1 DAC"},
+ {"IF2 DAC L", NULL, "IF2 DAC"},
+ {"IF2 DAC R", NULL, "IF2 DAC"},
+
+ {"DAC MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"},
+ {"DAC MIXL", "INF1 Switch", "IF1 DAC1 L"},
+ {"DAC MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"},
+ {"DAC MIXR", "INF1 Switch", "IF1 DAC1 R"},
+
+ {"Audio DSP", NULL, "DAC MIXL"},
+ {"Audio DSP", NULL, "DAC MIXR"},
+
+ {"DAC L2 Mux", "IF1", "IF1 DAC2 L"},
+ {"DAC L2 Mux", "IF2", "IF2 DAC L"},
+ {"DAC L2 Volume", NULL, "DAC L2 Mux"},
+
+ {"DAC R2 Mux", "IF1", "IF1 DAC2 R"},
+ {"DAC R2 Mux", "IF2", "IF2 DAC R"},
+ {"DAC R2 Volume", NULL, "DAC R2 Mux"},
+
+ {"Stereo DAC MIXL", "DAC L1 Switch", "Audio DSP"},
+ {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Volume"},
+ {"Stereo DAC MIXL", "DAC R1 Switch", "DAC MIXR"},
+ {"Stereo DAC MIXL", NULL, "Stero1 DAC Power"},
+ {"Stereo DAC MIXL", NULL, "Stero2 DAC Power"},
+ {"Stereo DAC MIXR", "DAC R1 Switch", "Audio DSP"},
+ {"Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Volume"},
+ {"Stereo DAC MIXR", "DAC L1 Switch", "DAC MIXL"},
+ {"Stereo DAC MIXR", NULL, "Stero1 DAC Power"},
+ {"Stereo DAC MIXR", NULL, "Stero2 DAC Power"},
+
+ {"PDM L Mux", "Stereo DAC MIX", "Stereo DAC MIXL"},
+ {"PDM L Mux", "DD MIX", "DAC MIXL"},
+ {"PDM R Mux", "Stereo DAC MIX", "Stereo DAC MIXR"},
+ {"PDM R Mux", "DD MIX", "DAC MIXR"},
+
+ {"DAC L1", NULL, "Stereo DAC MIXL"},
+ {"DAC L1", NULL, "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"},
+ {"DD MIXL", "DAC L2 Switch", "DAC L2 Volume"},
+ {"DD MIXL", "DAC R2 Switch", "DAC R2 Volume"},
+ {"DD MIXL", NULL, "Stero2 DAC Power"},
+
+ {"DD MIXR", "DAC R1 Switch", "DAC MIXR"},
+ {"DD MIXR", "DAC R2 Switch", "DAC R2 Volume"},
+ {"DD MIXR", "DAC L2 Switch", "DAC L2 Volume"},
+ {"DD MIXR", NULL, "Stero2 DAC Power"},
+
+ {"OUT MIXL", "BST1 Switch", "BST1"},
+ {"OUT MIXL", "BST2 Switch", "BST2"},
+ {"OUT MIXL", "INL1 Switch", "INL1 VOL"},
+ {"OUT MIXL", "REC MIXL Switch", "RECMIXL"},
+ {"OUT MIXL", "DAC L1 Switch", "DAC L1"},
+
+ {"OUT MIXR", "BST2 Switch", "BST2"},
+ {"OUT MIXR", "BST1 Switch", "BST1"},
+ {"OUT MIXR", "INR1 Switch", "INR1 VOL"},
+ {"OUT MIXR", "REC MIXR Switch", "RECMIXR"},
+ {"OUT MIXR", "DAC R1 Switch", "DAC R1"},
+
+ {"HPOVOL L", "Switch", "OUT MIXL"},
+ {"HPOVOL R", "Switch", "OUT MIXR"},
+ {"OUTVOL L", "Switch", "OUT MIXL"},
+ {"OUTVOL R", "Switch", "OUT MIXR"},
+
+ {"HPOL MIX", "HPO MIX DAC1 Switch", "DAC L1"},
+ {"HPOL MIX", "HPO MIX HPVOL Switch", "HPOVOL L"},
+ {"HPOL MIX", NULL, "HP L Amp"},
+ {"HPOR MIX", "HPO MIX DAC1 Switch", "DAC R1"},
+ {"HPOR MIX", "HPO MIX HPVOL Switch", "HPOVOL R"},
+ {"HPOR MIX", NULL, "HP R Amp"},
+
+ {"LOUT MIX", "DAC L1 Switch", "DAC L1"},
+ {"LOUT MIX", "DAC R1 Switch", "DAC R1"},
+ {"LOUT MIX", "OUTVOL L Switch", "OUTVOL L"},
+ {"LOUT MIX", "OUTVOL R Switch", "OUTVOL R"},
+
+ {"HP Amp", NULL, "HPOL MIX"},
+ {"HP Amp", NULL, "HPOR MIX"},
+ {"HP Amp", NULL, "Amp Power"},
+ {"HPO L Playback", "Switch", "HP Amp"},
+ {"HPO R Playback", "Switch", "HP Amp"},
+ {"HPOL", NULL, "HPO L Playback"},
+ {"HPOR", NULL, "HPO R Playback"},
+
+ {"LOUT L Playback", "Switch", "LOUT MIX"},
+ {"LOUT R Playback", "Switch", "LOUT MIX"},
+ {"LOUTL", NULL, "LOUT L Playback"},
+ {"LOUTL", NULL, "Amp Power"},
+ {"LOUTR", NULL, "LOUT R Playback"},
+ {"LOUTR", NULL, "Amp Power"},
+
+ {"PDML", NULL, "PDM L Mux"},
+ {"PDMR", NULL, "PDM R Mux"},
+};
+
+static int rt5651_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val_len = 0, val_clk, mask_clk;
+ int pre_div, bclk_ms, frame_size;
+
+ rt5651->lrck[dai->id] = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt5651->sysclk, rt5651->lrck[dai->id]);
+
+ if (pre_div < 0) {
+ dev_err(codec->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);
+ return -EINVAL;
+ }
+ bclk_ms = frame_size > 32 ? 1 : 0;
+ rt5651->bclk[dai->id] = rt5651->lrck[dai->id] * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt5651->bclk[dai->id], rt5651->lrck[dai->id]);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ val_len |= RT5651_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= RT5651_I2S_DL_24;
+ break;
+ case 8:
+ val_len |= RT5651_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5651_AIF1:
+ mask_clk = RT5651_I2S_PD1_MASK;
+ val_clk = pre_div << RT5651_I2S_PD1_SFT;
+ snd_soc_update_bits(codec, RT5651_I2S1_SDP,
+ RT5651_I2S_DL_MASK, val_len);
+ snd_soc_update_bits(codec, 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,
+ RT5651_I2S_DL_MASK, val_len);
+ snd_soc_update_bits(codec, RT5651_ADDA_CLK1, mask_clk, val_clk);
+ break;
+ default:
+ dev_err(codec->dev, "Wrong dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rt5651->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT5651_I2S_MS_S;
+ rt5651->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5651_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5651_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5651_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5651_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5651_AIF1:
+ snd_soc_update_bits(codec, 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,
+ 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);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+
+ if (freq == rt5651->sysclk && clk_id == rt5651->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5651_SCLK_S_MCLK:
+ reg_val |= RT5651_SCLK_SRC_MCLK;
+ break;
+ case RT5651_SCLK_S_PLL1:
+ reg_val |= RT5651_SCLK_SRC_PLL1;
+ 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);
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, RT5651_GLB_CLK,
+ RT5651_SCLK_SRC_MASK, reg_val);
+ rt5651->sysclk = freq;
+ rt5651->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+ return 0;
+}
+
+static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt5651->pll_src && freq_in == rt5651->pll_in &&
+ freq_out == rt5651->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(codec->dev, "PLL disabled\n");
+
+ rt5651->pll_in = 0;
+ rt5651->pll_out = 0;
+ snd_soc_update_bits(codec, 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,
+ RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_MCLK);
+ break;
+ case RT5651_PLL1_S_BCLK1:
+ snd_soc_update_bits(codec, 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,
+ RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_BCLK2);
+ break;
+ default:
+ dev_err(codec->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);
+ return ret;
+ }
+
+ dev_dbg(codec->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,
+ 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);
+
+ rt5651->pll_in = freq_in;
+ rt5651->pll_out = freq_out;
+ rt5651->pll_src = source;
+
+ return 0;
+}
+
+static int rt5651_set_bias_level(struct snd_soc_codec *codec,
+ 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,
+ 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_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);
+ }
+ 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);
+ break;
+
+ default:
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int rt5651_probe(struct snd_soc_codec *codec)
+{
+ struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+ rt5651->codec = codec;
+
+ 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);
+
+ rt5651_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt5651_suspend(struct snd_soc_codec *codec)
+{
+ struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt5651->regmap, true);
+ regcache_mark_dirty(rt5651->regmap);
+ return 0;
+}
+
+static int rt5651_resume(struct snd_soc_codec *codec)
+{
+ struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt5651->regmap, false);
+ snd_soc_cache_sync(codec);
+
+ return 0;
+}
+#else
+#define rt5651_suspend NULL
+#define rt5651_resume NULL
+#endif
+
+#define RT5651_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5651_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5651_aif_dai_ops = {
+ .hw_params = rt5651_hw_params,
+ .set_fmt = rt5651_set_dai_fmt,
+ .set_sysclk = rt5651_set_dai_sysclk,
+ .set_pll = rt5651_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver rt5651_dai[] = {
+ {
+ .name = "rt5651-aif1",
+ .id = RT5651_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5651_STEREO_RATES,
+ .formats = RT5651_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5651_STEREO_RATES,
+ .formats = RT5651_FORMATS,
+ },
+ .ops = &rt5651_aif_dai_ops,
+ },
+ {
+ .name = "rt5651-aif2",
+ .id = RT5651_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5651_STEREO_RATES,
+ .formats = RT5651_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5651_STEREO_RATES,
+ .formats = RT5651_FORMATS,
+ },
+ .ops = &rt5651_aif_dai_ops,
+ },
+};
+
+static 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 regmap_config rt5651_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = RT5651_DEVICE_ID + 1 + (ARRAY_SIZE(rt5651_ranges) *
+ RT5651_PR_SPACING),
+ .volatile_reg = rt5651_volatile_register,
+ .readable_reg = rt5651_readable_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt5651_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5651_reg),
+ .ranges = rt5651_ranges,
+ .num_ranges = ARRAY_SIZE(rt5651_ranges),
+};
+
+static const struct i2c_device_id rt5651_i2c_id[] = {
+ { "rt5651", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id);
+
+static int rt5651_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt5651_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5651_priv *rt5651;
+ int ret;
+
+ rt5651 = devm_kzalloc(&i2c->dev, sizeof(*rt5651),
+ GFP_KERNEL);
+ if (NULL == rt5651)
+ return -ENOMEM;
+
+ 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);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt5651->regmap, RT5651_DEVICE_ID, &ret);
+ if (ret != RT5651_DEVICE_ID_VALUE) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt5651\n", ret);
+ return -ENODEV;
+ }
+
+ regmap_write(rt5651->regmap, RT5651_RESET, 0);
+
+ ret = regmap_register_patch(rt5651->regmap, init_list,
+ ARRAY_SIZE(init_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ if (rt5651->pdata.in2_diff)
+ regmap_update_bits(rt5651->regmap, RT5651_IN1_IN2,
+ RT5651_IN_DF2, RT5651_IN_DF2);
+
+ if (rt5651->pdata.dmic_en)
+ regmap_update_bits(rt5651->regmap, RT5651_GPIO_CTRL1,
+ RT5651_GP2_PIN_MASK, RT5651_GP2_PIN_DMIC1_SCL);
+
+ rt5651->hp_mute = 1;
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_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,
+ },
+ .probe = rt5651_i2c_probe,
+ .remove = rt5651_i2c_remove,
+ .id_table = rt5651_i2c_id,
+};
+module_i2c_driver(rt5651_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5651 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/rt5651.h b/kernel/sound/soc/codecs/rt5651.h
new file mode 100644
index 000000000..1bd33cfa6
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5651.h
@@ -0,0 +1,2080 @@
+/*
+ * 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>
+
+/* Info */
+#define RT5651_RESET 0x00
+#define RT5651_VERSION_ID 0xfd
+#define RT5651_VENDOR_ID 0xfe
+#define RT5651_DEVICE_ID 0xff
+/* I/O - Output */
+#define RT5651_HP_VOL 0x02
+#define RT5651_LOUT_CTRL1 0x03
+#define RT5651_LOUT_CTRL2 0x05
+/* I/O - Input */
+#define RT5651_IN1_IN2 0x0d
+#define RT5651_IN3 0x0e
+#define RT5651_INL1_INR1_VOL 0x0f
+#define RT5651_INL2_INR2_VOL 0x10
+/* I/O - ADC/DAC/DMIC */
+#define RT5651_DAC1_DIG_VOL 0x19
+#define RT5651_DAC2_DIG_VOL 0x1a
+#define RT5651_DAC2_CTRL 0x1b
+#define RT5651_ADC_DIG_VOL 0x1c
+#define RT5651_ADC_DATA 0x1d
+#define RT5651_ADC_BST_VOL 0x1e
+/* Mixer - D-D */
+#define RT5651_STO1_ADC_MIXER 0x27
+#define RT5651_STO2_ADC_MIXER 0x28
+#define RT5651_AD_DA_MIXER 0x29
+#define RT5651_STO_DAC_MIXER 0x2a
+#define RT5651_DD_MIXER 0x2b
+#define RT5651_DIG_INF_DATA 0x2f
+/* PDM */
+#define RT5651_PDM_CTL 0x30
+#define RT5651_PDM_I2C_CTL1 0x31
+#define RT5651_PDM_I2C_CTL2 0x32
+#define RT5651_PDM_I2C_DATA_W 0x33
+#define RT5651_PDM_I2C_DATA_R 0x34
+/* Mixer - ADC */
+#define RT5651_REC_L1_MIXER 0x3b
+#define RT5651_REC_L2_MIXER 0x3c
+#define RT5651_REC_R1_MIXER 0x3d
+#define RT5651_REC_R2_MIXER 0x3e
+/* Mixer - DAC */
+#define RT5651_HPO_MIXER 0x45
+#define RT5651_OUT_L1_MIXER 0x4d
+#define RT5651_OUT_L2_MIXER 0x4e
+#define RT5651_OUT_L3_MIXER 0x4f
+#define RT5651_OUT_R1_MIXER 0x50
+#define RT5651_OUT_R2_MIXER 0x51
+#define RT5651_OUT_R3_MIXER 0x52
+#define RT5651_LOUT_MIXER 0x53
+/* Power */
+#define RT5651_PWR_DIG1 0x61
+#define RT5651_PWR_DIG2 0x62
+#define RT5651_PWR_ANLG1 0x63
+#define RT5651_PWR_ANLG2 0x64
+#define RT5651_PWR_MIXER 0x65
+#define RT5651_PWR_VOL 0x66
+/* Private Register Control */
+#define RT5651_PRIV_INDEX 0x6a
+#define RT5651_PRIV_DATA 0x6c
+/* Format - ADC/DAC */
+#define RT5651_I2S1_SDP 0x70
+#define RT5651_I2S2_SDP 0x71
+#define RT5651_ADDA_CLK1 0x73
+#define RT5651_ADDA_CLK2 0x74
+#define RT5651_DMIC 0x75
+/* TDM Control */
+#define RT5651_TDM_CTL_1 0x77
+#define RT5651_TDM_CTL_2 0x78
+#define RT5651_TDM_CTL_3 0x79
+/* Function - Analog */
+#define RT5651_GLB_CLK 0x80
+#define RT5651_PLL_CTRL1 0x81
+#define RT5651_PLL_CTRL2 0x82
+#define RT5651_PLL_MODE_1 0x83
+#define RT5651_PLL_MODE_2 0x84
+#define RT5651_PLL_MODE_3 0x85
+#define RT5651_PLL_MODE_4 0x86
+#define RT5651_PLL_MODE_5 0x87
+#define RT5651_PLL_MODE_6 0x89
+#define RT5651_PLL_MODE_7 0x8a
+#define RT5651_DEPOP_M1 0x8e
+#define RT5651_DEPOP_M2 0x8f
+#define RT5651_DEPOP_M3 0x90
+#define RT5651_CHARGE_PUMP 0x91
+#define RT5651_MICBIAS 0x93
+#define RT5651_A_JD_CTL1 0x94
+/* Function - Digital */
+#define RT5651_EQ_CTRL1 0xb0
+#define RT5651_EQ_CTRL2 0xb1
+#define RT5651_ALC_1 0xb4
+#define RT5651_ALC_2 0xb5
+#define RT5651_ALC_3 0xb6
+#define RT5651_JD_CTRL1 0xbb
+#define RT5651_JD_CTRL2 0xbc
+#define RT5651_IRQ_CTRL1 0xbd
+#define RT5651_IRQ_CTRL2 0xbe
+#define RT5651_INT_IRQ_ST 0xbf
+#define RT5651_GPIO_CTRL1 0xc0
+#define RT5651_GPIO_CTRL2 0xc1
+#define RT5651_GPIO_CTRL3 0xc2
+#define RT5651_PGM_REG_ARR1 0xc8
+#define RT5651_PGM_REG_ARR2 0xc9
+#define RT5651_PGM_REG_ARR3 0xca
+#define RT5651_PGM_REG_ARR4 0xcb
+#define RT5651_PGM_REG_ARR5 0xcc
+#define RT5651_SCB_FUNC 0xcd
+#define RT5651_SCB_CTRL 0xce
+#define RT5651_BASE_BACK 0xcf
+#define RT5651_MP3_PLUS1 0xd0
+#define RT5651_MP3_PLUS2 0xd1
+#define RT5651_ADJ_HPF_CTRL1 0xd3
+#define RT5651_ADJ_HPF_CTRL2 0xd4
+#define RT5651_HP_CALIB_AMP_DET 0xd6
+#define RT5651_HP_CALIB2 0xd7
+#define RT5651_SV_ZCD1 0xd9
+#define RT5651_SV_ZCD2 0xda
+#define RT5651_D_MISC 0xfa
+/* Dummy Register */
+#define RT5651_DUMMY2 0xfb
+#define RT5651_DUMMY3 0xfc
+
+
+/* Index of Codec Private Register definition */
+#define RT5651_BIAS_CUR1 0x12
+#define RT5651_BIAS_CUR3 0x14
+#define RT5651_CLSD_INT_REG1 0x1c
+#define RT5651_CHPUMP_INT_REG1 0x24
+#define RT5651_MAMP_INT_REG2 0x37
+#define RT5651_CHOP_DAC_ADC 0x3d
+#define RT5651_3D_SPK 0x63
+#define RT5651_WND_1 0x6c
+#define RT5651_WND_2 0x6d
+#define RT5651_WND_3 0x6e
+#define RT5651_WND_4 0x6f
+#define RT5651_WND_5 0x70
+#define RT5651_WND_8 0x73
+#define RT5651_DIP_SPK_INF 0x75
+#define RT5651_HP_DCC_INT1 0x77
+#define RT5651_EQ_BW_LOP 0xa0
+#define RT5651_EQ_GN_LOP 0xa1
+#define RT5651_EQ_FC_BP1 0xa2
+#define RT5651_EQ_BW_BP1 0xa3
+#define RT5651_EQ_GN_BP1 0xa4
+#define RT5651_EQ_FC_BP2 0xa5
+#define RT5651_EQ_BW_BP2 0xa6
+#define RT5651_EQ_GN_BP2 0xa7
+#define RT5651_EQ_FC_BP3 0xa8
+#define RT5651_EQ_BW_BP3 0xa9
+#define RT5651_EQ_GN_BP3 0xaa
+#define RT5651_EQ_FC_BP4 0xab
+#define RT5651_EQ_BW_BP4 0xac
+#define RT5651_EQ_GN_BP4 0xad
+#define RT5651_EQ_FC_HIP1 0xae
+#define RT5651_EQ_GN_HIP1 0xaf
+#define RT5651_EQ_FC_HIP2 0xb0
+#define RT5651_EQ_BW_HIP2 0xb1
+#define RT5651_EQ_GN_HIP2 0xb2
+#define RT5651_EQ_PRE_VOL 0xb3
+#define RT5651_EQ_PST_VOL 0xb4
+
+
+/* global definition */
+#define RT5651_L_MUTE (0x1 << 15)
+#define RT5651_L_MUTE_SFT 15
+#define RT5651_VOL_L_MUTE (0x1 << 14)
+#define RT5651_VOL_L_SFT 14
+#define RT5651_R_MUTE (0x1 << 7)
+#define RT5651_R_MUTE_SFT 7
+#define RT5651_VOL_R_MUTE (0x1 << 6)
+#define RT5651_VOL_R_SFT 6
+#define RT5651_L_VOL_MASK (0x3f << 8)
+#define RT5651_L_VOL_SFT 8
+#define RT5651_R_VOL_MASK (0x3f)
+#define RT5651_R_VOL_SFT 0
+
+/* LOUT Control 2(0x05) */
+#define RT5651_EN_DFO (0x1 << 15)
+
+/* IN1 and IN2 Control (0x0d) */
+/* IN3 and IN4 Control (0x0e) */
+#define RT5651_BST_MASK1 (0xf<<12)
+#define RT5651_BST_SFT1 12
+#define RT5651_BST_MASK2 (0xf<<8)
+#define RT5651_BST_SFT2 8
+#define RT5651_IN_DF1 (0x1 << 7)
+#define RT5651_IN_SFT1 7
+#define RT5651_IN_DF2 (0x1 << 6)
+#define RT5651_IN_SFT2 6
+
+/* INL1 and INR1 Volume Control (0x0f) */
+/* INL2 and INR2 Volume Control (0x10) */
+#define RT5651_INL_SEL_MASK (0x1 << 15)
+#define RT5651_INL_SEL_SFT 15
+#define RT5651_INL_SEL_IN4P (0x0 << 15)
+#define RT5651_INL_SEL_MONOP (0x1 << 15)
+#define RT5651_INL_VOL_MASK (0x1f << 8)
+#define RT5651_INL_VOL_SFT 8
+#define RT5651_INR_SEL_MASK (0x1 << 7)
+#define RT5651_INR_SEL_SFT 7
+#define RT5651_INR_SEL_IN4N (0x0 << 7)
+#define RT5651_INR_SEL_MONON (0x1 << 7)
+#define RT5651_INR_VOL_MASK (0x1f)
+#define RT5651_INR_VOL_SFT 0
+
+/* DAC1 Digital Volume (0x19) */
+#define RT5651_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5651_DAC_L1_VOL_SFT 8
+#define RT5651_DAC_R1_VOL_MASK (0xff)
+#define RT5651_DAC_R1_VOL_SFT 0
+
+/* DAC2 Digital Volume (0x1a) */
+#define RT5651_DAC_L2_VOL_MASK (0xff << 8)
+#define RT5651_DAC_L2_VOL_SFT 8
+#define RT5651_DAC_R2_VOL_MASK (0xff)
+#define RT5651_DAC_R2_VOL_SFT 0
+
+/* DAC2 Control (0x1b) */
+#define RT5651_M_DAC_L2_VOL (0x1 << 13)
+#define RT5651_M_DAC_L2_VOL_SFT 13
+#define RT5651_M_DAC_R2_VOL (0x1 << 12)
+#define RT5651_M_DAC_R2_VOL_SFT 12
+#define RT5651_SEL_DAC_L2 (0x1 << 11)
+#define RT5651_IF2_DAC_L2 (0x1 << 11)
+#define RT5651_IF1_DAC_L2 (0x0 << 11)
+#define RT5651_SEL_DAC_L2_SFT 11
+#define RT5651_SEL_DAC_R2 (0x1 << 10)
+#define RT5651_IF2_DAC_R2 (0x1 << 11)
+#define RT5651_IF1_DAC_R2 (0x0 << 11)
+#define RT5651_SEL_DAC_R2_SFT 10
+
+/* ADC Digital Volume Control (0x1c) */
+#define RT5651_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5651_ADC_L_VOL_SFT 8
+#define RT5651_ADC_R_VOL_MASK (0x7f)
+#define RT5651_ADC_R_VOL_SFT 0
+
+/* Mono ADC Digital Volume Control (0x1d) */
+#define RT5651_M_MONO_ADC_L (0x1 << 15)
+#define RT5651_M_MONO_ADC_L_SFT 15
+#define RT5651_MONO_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5651_MONO_ADC_L_VOL_SFT 8
+#define RT5651_M_MONO_ADC_R (0x1 << 7)
+#define RT5651_M_MONO_ADC_R_SFT 7
+#define RT5651_MONO_ADC_R_VOL_MASK (0x7f)
+#define RT5651_MONO_ADC_R_VOL_SFT 0
+
+/* ADC Boost Volume Control (0x1e) */
+#define RT5651_ADC_L_BST_MASK (0x3 << 14)
+#define RT5651_ADC_L_BST_SFT 14
+#define RT5651_ADC_R_BST_MASK (0x3 << 12)
+#define RT5651_ADC_R_BST_SFT 12
+#define RT5651_ADC_COMP_MASK (0x3 << 10)
+#define RT5651_ADC_COMP_SFT 10
+
+/* Stereo ADC1 Mixer Control (0x27) */
+#define RT5651_M_STO1_ADC_L1 (0x1 << 14)
+#define RT5651_M_STO1_ADC_L1_SFT 14
+#define RT5651_M_STO1_ADC_L2 (0x1 << 13)
+#define RT5651_M_STO1_ADC_L2_SFT 13
+#define RT5651_STO1_ADC_1_SRC_MASK (0x1 << 12)
+#define RT5651_STO1_ADC_1_SRC_SFT 12
+#define RT5651_STO1_ADC_1_SRC_ADC (0x1 << 12)
+#define RT5651_STO1_ADC_1_SRC_DACMIX (0x0 << 12)
+#define RT5651_STO1_ADC_2_SRC_MASK (0x1 << 11)
+#define RT5651_STO1_ADC_2_SRC_SFT 11
+#define RT5651_STO1_ADC_2_SRC_DMIC (0x0 << 11)
+#define RT5651_STO1_ADC_2_SRC_DACMIXR (0x1 << 11)
+#define RT5651_M_STO1_ADC_R1 (0x1 << 6)
+#define RT5651_M_STO1_ADC_R1_SFT 6
+#define RT5651_M_STO1_ADC_R2 (0x1 << 5)
+#define RT5651_M_STO1_ADC_R2_SFT 5
+
+/* Stereo ADC2 Mixer Control (0x28) */
+#define RT5651_M_STO2_ADC_L1 (0x1 << 14)
+#define RT5651_M_STO2_ADC_L1_SFT 14
+#define RT5651_M_STO2_ADC_L2 (0x1 << 13)
+#define RT5651_M_STO2_ADC_L2_SFT 13
+#define RT5651_STO2_ADC_L1_SRC_MASK (0x1 << 12)
+#define RT5651_STO2_ADC_L1_SRC_SFT 12
+#define RT5651_STO2_ADC_L1_SRC_DACMIXL (0x0 << 12)
+#define RT5651_STO2_ADC_L1_SRC_ADCL (0x1 << 12)
+#define RT5651_STO2_ADC_L2_SRC_MASK (0x1 << 11)
+#define RT5651_STO2_ADC_L2_SRC_SFT 11
+#define RT5651_STO2_ADC_L2_SRC_DMIC (0x0 << 11)
+#define RT5651_STO2_ADC_L2_SRC_DACMIXR (0x1 << 11)
+#define RT5651_M_STO2_ADC_R1 (0x1 << 6)
+#define RT5651_M_STO2_ADC_R1_SFT 6
+#define RT5651_M_STO2_ADC_R2 (0x1 << 5)
+#define RT5651_M_STO2_ADC_R2_SFT 5
+#define RT5651_STO2_ADC_R1_SRC_MASK (0x1 << 4)
+#define RT5651_STO2_ADC_R1_SRC_SFT 4
+#define RT5651_STO2_ADC_R1_SRC_ADCR (0x1 << 4)
+#define RT5651_STO2_ADC_R1_SRC_DACMIXR (0x0 << 4)
+#define RT5651_STO2_ADC_R2_SRC_MASK (0x1 << 3)
+#define RT5651_STO2_ADC_R2_SRC_SFT 3
+#define RT5651_STO2_ADC_R2_SRC_DMIC (0x0 << 3)
+#define RT5651_STO2_ADC_R2_SRC_DACMIXR (0x1 << 3)
+
+/* ADC Mixer to DAC Mixer Control (0x29) */
+#define RT5651_M_ADCMIX_L (0x1 << 15)
+#define RT5651_M_ADCMIX_L_SFT 15
+#define RT5651_M_IF1_DAC_L (0x1 << 14)
+#define RT5651_M_IF1_DAC_L_SFT 14
+#define RT5651_M_ADCMIX_R (0x1 << 7)
+#define RT5651_M_ADCMIX_R_SFT 7
+#define RT5651_M_IF1_DAC_R (0x1 << 6)
+#define RT5651_M_IF1_DAC_R_SFT 6
+
+/* Stereo DAC Mixer Control (0x2a) */
+#define RT5651_M_DAC_L1_MIXL (0x1 << 14)
+#define RT5651_M_DAC_L1_MIXL_SFT 14
+#define RT5651_DAC_L1_STO_L_VOL_MASK (0x1 << 13)
+#define RT5651_DAC_L1_STO_L_VOL_SFT 13
+#define RT5651_M_DAC_L2_MIXL (0x1 << 12)
+#define RT5651_M_DAC_L2_MIXL_SFT 12
+#define RT5651_DAC_L2_STO_L_VOL_MASK (0x1 << 11)
+#define RT5651_DAC_L2_STO_L_VOL_SFT 11
+#define RT5651_M_DAC_R1_MIXL (0x1 << 9)
+#define RT5651_M_DAC_R1_MIXL_SFT 9
+#define RT5651_DAC_R1_STO_L_VOL_MASK (0x1 << 8)
+#define RT5651_DAC_R1_STO_L_VOL_SFT 8
+#define RT5651_M_DAC_R1_MIXR (0x1 << 6)
+#define RT5651_M_DAC_R1_MIXR_SFT 6
+#define RT5651_DAC_R1_STO_R_VOL_MASK (0x1 << 5)
+#define RT5651_DAC_R1_STO_R_VOL_SFT 5
+#define RT5651_M_DAC_R2_MIXR (0x1 << 4)
+#define RT5651_M_DAC_R2_MIXR_SFT 4
+#define RT5651_DAC_R2_STO_R_VOL_MASK (0x1 << 3)
+#define RT5651_DAC_R2_STO_R_VOL_SFT 3
+#define RT5651_M_DAC_L1_MIXR (0x1 << 1)
+#define RT5651_M_DAC_L1_MIXR_SFT 1
+#define RT5651_DAC_L1_STO_R_VOL_MASK (0x1)
+#define RT5651_DAC_L1_STO_R_VOL_SFT 0
+
+/* DD Mixer Control (0x2b) */
+#define RT5651_M_STO_DD_L1 (0x1 << 14)
+#define RT5651_M_STO_DD_L1_SFT 14
+#define RT5651_STO_DD_L1_VOL_MASK (0x1 << 13)
+#define RT5651_DAC_DD_L1_VOL_SFT 13
+#define RT5651_M_STO_DD_L2 (0x1 << 12)
+#define RT5651_M_STO_DD_L2_SFT 12
+#define RT5651_STO_DD_L2_VOL_MASK (0x1 << 11)
+#define RT5651_STO_DD_L2_VOL_SFT 11
+#define RT5651_M_STO_DD_R2_L (0x1 << 10)
+#define RT5651_M_STO_DD_R2_L_SFT 10
+#define RT5651_STO_DD_R2_L_VOL_MASK (0x1 << 9)
+#define RT5651_STO_DD_R2_L_VOL_SFT 9
+#define RT5651_M_STO_DD_R1 (0x1 << 6)
+#define RT5651_M_STO_DD_R1_SFT 6
+#define RT5651_STO_DD_R1_VOL_MASK (0x1 << 5)
+#define RT5651_STO_DD_R1_VOL_SFT 5
+#define RT5651_M_STO_DD_R2 (0x1 << 4)
+#define RT5651_M_STO_DD_R2_SFT 4
+#define RT5651_STO_DD_R2_VOL_MASK (0x1 << 3)
+#define RT5651_STO_DD_R2_VOL_SFT 3
+#define RT5651_M_STO_DD_L2_R (0x1 << 2)
+#define RT5651_M_STO_DD_L2_R_SFT 2
+#define RT5651_STO_DD_L2_R_VOL_MASK (0x1 << 1)
+#define RT5651_STO_DD_L2_R_VOL_SFT 1
+
+/* Digital Mixer Control (0x2c) */
+#define RT5651_M_STO_L_DAC_L (0x1 << 15)
+#define RT5651_M_STO_L_DAC_L_SFT 15
+#define RT5651_STO_L_DAC_L_VOL_MASK (0x1 << 14)
+#define RT5651_STO_L_DAC_L_VOL_SFT 14
+#define RT5651_M_DAC_L2_DAC_L (0x1 << 13)
+#define RT5651_M_DAC_L2_DAC_L_SFT 13
+#define RT5651_DAC_L2_DAC_L_VOL_MASK (0x1 << 12)
+#define RT5651_DAC_L2_DAC_L_VOL_SFT 12
+#define RT5651_M_STO_R_DAC_R (0x1 << 11)
+#define RT5651_M_STO_R_DAC_R_SFT 11
+#define RT5651_STO_R_DAC_R_VOL_MASK (0x1 << 10)
+#define RT5651_STO_R_DAC_R_VOL_SFT 10
+#define RT5651_M_DAC_R2_DAC_R (0x1 << 9)
+#define RT5651_M_DAC_R2_DAC_R_SFT 9
+#define RT5651_DAC_R2_DAC_R_VOL_MASK (0x1 << 8)
+#define RT5651_DAC_R2_DAC_R_VOL_SFT 8
+
+/* DSP Path Control 1 (0x2d) */
+#define RT5651_RXDP_SRC_MASK (0x1 << 15)
+#define RT5651_RXDP_SRC_SFT 15
+#define RT5651_RXDP_SRC_NOR (0x0 << 15)
+#define RT5651_RXDP_SRC_DIV3 (0x1 << 15)
+#define RT5651_TXDP_SRC_MASK (0x1 << 14)
+#define RT5651_TXDP_SRC_SFT 14
+#define RT5651_TXDP_SRC_NOR (0x0 << 14)
+#define RT5651_TXDP_SRC_DIV3 (0x1 << 14)
+
+/* DSP Path Control 2 (0x2e) */
+#define RT5651_DAC_L2_SEL_MASK (0x3 << 14)
+#define RT5651_DAC_L2_SEL_SFT 14
+#define RT5651_DAC_L2_SEL_IF2 (0x0 << 14)
+#define RT5651_DAC_L2_SEL_IF3 (0x1 << 14)
+#define RT5651_DAC_L2_SEL_TXDC (0x2 << 14)
+#define RT5651_DAC_L2_SEL_BASS (0x3 << 14)
+#define RT5651_DAC_R2_SEL_MASK (0x3 << 12)
+#define RT5651_DAC_R2_SEL_SFT 12
+#define RT5651_DAC_R2_SEL_IF2 (0x0 << 12)
+#define RT5651_DAC_R2_SEL_IF3 (0x1 << 12)
+#define RT5651_DAC_R2_SEL_TXDC (0x2 << 12)
+#define RT5651_IF2_ADC_L_SEL_MASK (0x1 << 11)
+#define RT5651_IF2_ADC_L_SEL_SFT 11
+#define RT5651_IF2_ADC_L_SEL_TXDP (0x0 << 11)
+#define RT5651_IF2_ADC_L_SEL_PASS (0x1 << 11)
+#define RT5651_IF2_ADC_R_SEL_MASK (0x1 << 10)
+#define RT5651_IF2_ADC_R_SEL_SFT 10
+#define RT5651_IF2_ADC_R_SEL_TXDP (0x0 << 10)
+#define RT5651_IF2_ADC_R_SEL_PASS (0x1 << 10)
+#define RT5651_RXDC_SEL_MASK (0x3 << 8)
+#define RT5651_RXDC_SEL_SFT 8
+#define RT5651_RXDC_SEL_NOR (0x0 << 8)
+#define RT5651_RXDC_SEL_L2R (0x1 << 8)
+#define RT5651_RXDC_SEL_R2L (0x2 << 8)
+#define RT5651_RXDC_SEL_SWAP (0x3 << 8)
+#define RT5651_RXDP_SEL_MASK (0x3 << 6)
+#define RT5651_RXDP_SEL_SFT 6
+#define RT5651_RXDP_SEL_NOR (0x0 << 6)
+#define RT5651_RXDP_SEL_L2R (0x1 << 6)
+#define RT5651_RXDP_SEL_R2L (0x2 << 6)
+#define RT5651_RXDP_SEL_SWAP (0x3 << 6)
+#define RT5651_TXDC_SEL_MASK (0x3 << 4)
+#define RT5651_TXDC_SEL_SFT 4
+#define RT5651_TXDC_SEL_NOR (0x0 << 4)
+#define RT5651_TXDC_SEL_L2R (0x1 << 4)
+#define RT5651_TXDC_SEL_R2L (0x2 << 4)
+#define RT5651_TXDC_SEL_SWAP (0x3 << 4)
+#define RT5651_TXDP_SEL_MASK (0x3 << 2)
+#define RT5651_TXDP_SEL_SFT 2
+#define RT5651_TXDP_SEL_NOR (0x0 << 2)
+#define RT5651_TXDP_SEL_L2R (0x1 << 2)
+#define RT5651_TXDP_SEL_R2L (0x2 << 2)
+#define RT5651_TRXDP_SEL_SWAP (0x3 << 2)
+
+/* Digital Interface Data Control (0x2f) */
+#define RT5651_IF2_DAC_SEL_MASK (0x3 << 10)
+#define RT5651_IF2_DAC_SEL_SFT 10
+#define RT5651_IF2_DAC_SEL_NOR (0x0 << 10)
+#define RT5651_IF2_DAC_SEL_SWAP (0x1 << 10)
+#define RT5651_IF2_DAC_SEL_L2R (0x2 << 10)
+#define RT5651_IF2_DAC_SEL_R2L (0x3 << 10)
+#define RT5651_IF2_ADC_SEL_MASK (0x3 << 8)
+#define RT5651_IF2_ADC_SEL_SFT 8
+#define RT5651_IF2_ADC_SEL_NOR (0x0 << 8)
+#define RT5651_IF2_ADC_SEL_SWAP (0x1 << 8)
+#define RT5651_IF2_ADC_SEL_L2R (0x2 << 8)
+#define RT5651_IF2_ADC_SEL_R2L (0x3 << 8)
+#define RT5651_IF2_ADC_SRC_MASK (0x1 << 7)
+#define RT5651_IF2_ADC_SRC_SFT 7
+#define RT5651_IF1_ADC1 (0x0 << 7)
+#define RT5651_IF1_ADC2 (0x1 << 7)
+
+/* PDM Output Control (0x30) */
+#define RT5651_PDM_L_SEL_MASK (0x1 << 15)
+#define RT5651_PDM_L_SEL_SFT 15
+#define RT5651_PDM_L_SEL_DD_L (0x0 << 15)
+#define RT5651_PDM_L_SEL_STO_L (0x1 << 15)
+#define RT5651_M_PDM_L (0x1 << 14)
+#define RT5651_M_PDM_L_SFT 14
+#define RT5651_PDM_R_SEL_MASK (0x1 << 13)
+#define RT5651_PDM_R_SEL_SFT 13
+#define RT5651_PDM_R_SEL_DD_L (0x0 << 13)
+#define RT5651_PDM_R_SEL_STO_L (0x1 << 13)
+#define RT5651_M_PDM_R (0x1 << 12)
+#define RT5651_M_PDM_R_SFT 12
+#define RT5651_PDM_BUSY (0x1 << 6)
+#define RT5651_PDM_BUSY_SFT 6
+#define RT5651_PDM_PATTERN_SEL_MASK (0x1 << 5)
+#define RT5651_PDM_PATTERN_SEL_64 (0x0 << 5)
+#define RT5651_PDM_PATTERN_SEL_128 (0x1 << 5)
+#define RT5651_PDM_VOL_MASK (0x1 << 4)
+#define RT5651_PDM_VOL_SFT 4
+#define RT5651_PDM_DIV_MASK (0x3)
+#define RT5651_PDM_DIV_SFT 0
+#define RT5651_PDM_DIV_1 0
+#define RT5651_PDM_DIV_2 1
+#define RT5651_PDM_DIV_3 2
+#define RT5651_PDM_DIV_4 3
+
+/* PDM I2C/Data Control 1 (0x31) */
+#define RT5651_PDM_I2C_ID_MASK (0xf << 12)
+#define PT5631_PDM_CMD_EXE (0x1 << 11)
+#define RT5651_PDM_I2C_CMD_MASK (0x1 << 10)
+#define RT5651_PDM_I2C_CMD_R (0x0 << 10)
+#define RT5651_PDM_I2C_CMD_W (0x1 << 10)
+#define RT5651_PDM_I2C_CMD_EXE (0x1 << 9)
+#define RT5651_PDM_I2C_NORMAL (0x0 << 8)
+#define RT5651_PDM_I2C_BUSY (0x1 << 8)
+
+/* PDM I2C/Data Control 2 (0x32) */
+#define RT5651_PDM_I2C_ADDR (0xff << 8)
+#define RT5651_PDM_I2C_CMD_PATTERN (0xff)
+
+
+/* REC Left Mixer Control 1 (0x3b) */
+#define RT5651_G_LN_L2_RM_L_MASK (0x7 << 13)
+#define RT5651_G_IN_L2_RM_L_SFT 13
+#define RT5651_G_LN_L1_RM_L_MASK (0x7 << 10)
+#define RT5651_G_IN_L1_RM_L_SFT 10
+#define RT5651_G_BST3_RM_L_MASK (0x7 << 4)
+#define RT5651_G_BST3_RM_L_SFT 4
+#define RT5651_G_BST2_RM_L_MASK (0x7 << 1)
+#define RT5651_G_BST2_RM_L_SFT 1
+
+/* REC Left Mixer Control 2 (0x3c) */
+#define RT5651_G_BST1_RM_L_MASK (0x7 << 13)
+#define RT5651_G_BST1_RM_L_SFT 13
+#define RT5651_G_OM_L_RM_L_MASK (0x7 << 10)
+#define RT5651_G_OM_L_RM_L_SFT 10
+#define RT5651_M_IN2_L_RM_L (0x1 << 6)
+#define RT5651_M_IN2_L_RM_L_SFT 6
+#define RT5651_M_IN1_L_RM_L (0x1 << 5)
+#define RT5651_M_IN1_L_RM_L_SFT 5
+#define RT5651_M_BST3_RM_L (0x1 << 3)
+#define RT5651_M_BST3_RM_L_SFT 3
+#define RT5651_M_BST2_RM_L (0x1 << 2)
+#define RT5651_M_BST2_RM_L_SFT 2
+#define RT5651_M_BST1_RM_L (0x1 << 1)
+#define RT5651_M_BST1_RM_L_SFT 1
+#define RT5651_M_OM_L_RM_L (0x1)
+#define RT5651_M_OM_L_RM_L_SFT 0
+
+/* REC Right Mixer Control 1 (0x3d) */
+#define RT5651_G_IN2_R_RM_R_MASK (0x7 << 13)
+#define RT5651_G_IN2_R_RM_R_SFT 13
+#define RT5651_G_IN1_R_RM_R_MASK (0x7 << 10)
+#define RT5651_G_IN1_R_RM_R_SFT 10
+#define RT5651_G_BST3_RM_R_MASK (0x7 << 4)
+#define RT5651_G_BST3_RM_R_SFT 4
+#define RT5651_G_BST2_RM_R_MASK (0x7 << 1)
+#define RT5651_G_BST2_RM_R_SFT 1
+
+/* REC Right Mixer Control 2 (0x3e) */
+#define RT5651_G_BST1_RM_R_MASK (0x7 << 13)
+#define RT5651_G_BST1_RM_R_SFT 13
+#define RT5651_G_OM_R_RM_R_MASK (0x7 << 10)
+#define RT5651_G_OM_R_RM_R_SFT 10
+#define RT5651_M_IN2_R_RM_R (0x1 << 6)
+#define RT5651_M_IN2_R_RM_R_SFT 6
+#define RT5651_M_IN1_R_RM_R (0x1 << 5)
+#define RT5651_M_IN1_R_RM_R_SFT 5
+#define RT5651_M_BST3_RM_R (0x1 << 3)
+#define RT5651_M_BST3_RM_R_SFT 3
+#define RT5651_M_BST2_RM_R (0x1 << 2)
+#define RT5651_M_BST2_RM_R_SFT 2
+#define RT5651_M_BST1_RM_R (0x1 << 1)
+#define RT5651_M_BST1_RM_R_SFT 1
+#define RT5651_M_OM_R_RM_R (0x1)
+#define RT5651_M_OM_R_RM_R_SFT 0
+
+/* HPMIX Control (0x45) */
+#define RT5651_M_DAC1_HM (0x1 << 14)
+#define RT5651_M_DAC1_HM_SFT 14
+#define RT5651_M_HPVOL_HM (0x1 << 13)
+#define RT5651_M_HPVOL_HM_SFT 13
+#define RT5651_G_HPOMIX_MASK (0x1 << 12)
+#define RT5651_G_HPOMIX_SFT 12
+
+/* SPK Left Mixer Control (0x46) */
+#define RT5651_G_RM_L_SM_L_MASK (0x3 << 14)
+#define RT5651_G_RM_L_SM_L_SFT 14
+#define RT5651_G_IN_L_SM_L_MASK (0x3 << 12)
+#define RT5651_G_IN_L_SM_L_SFT 12
+#define RT5651_G_DAC_L1_SM_L_MASK (0x3 << 10)
+#define RT5651_G_DAC_L1_SM_L_SFT 10
+#define RT5651_G_DAC_L2_SM_L_MASK (0x3 << 8)
+#define RT5651_G_DAC_L2_SM_L_SFT 8
+#define RT5651_G_OM_L_SM_L_MASK (0x3 << 6)
+#define RT5651_G_OM_L_SM_L_SFT 6
+#define RT5651_M_RM_L_SM_L (0x1 << 5)
+#define RT5651_M_RM_L_SM_L_SFT 5
+#define RT5651_M_IN_L_SM_L (0x1 << 4)
+#define RT5651_M_IN_L_SM_L_SFT 4
+#define RT5651_M_DAC_L1_SM_L (0x1 << 3)
+#define RT5651_M_DAC_L1_SM_L_SFT 3
+#define RT5651_M_DAC_L2_SM_L (0x1 << 2)
+#define RT5651_M_DAC_L2_SM_L_SFT 2
+#define RT5651_M_OM_L_SM_L (0x1 << 1)
+#define RT5651_M_OM_L_SM_L_SFT 1
+
+/* SPK Right Mixer Control (0x47) */
+#define RT5651_G_RM_R_SM_R_MASK (0x3 << 14)
+#define RT5651_G_RM_R_SM_R_SFT 14
+#define RT5651_G_IN_R_SM_R_MASK (0x3 << 12)
+#define RT5651_G_IN_R_SM_R_SFT 12
+#define RT5651_G_DAC_R1_SM_R_MASK (0x3 << 10)
+#define RT5651_G_DAC_R1_SM_R_SFT 10
+#define RT5651_G_DAC_R2_SM_R_MASK (0x3 << 8)
+#define RT5651_G_DAC_R2_SM_R_SFT 8
+#define RT5651_G_OM_R_SM_R_MASK (0x3 << 6)
+#define RT5651_G_OM_R_SM_R_SFT 6
+#define RT5651_M_RM_R_SM_R (0x1 << 5)
+#define RT5651_M_RM_R_SM_R_SFT 5
+#define RT5651_M_IN_R_SM_R (0x1 << 4)
+#define RT5651_M_IN_R_SM_R_SFT 4
+#define RT5651_M_DAC_R1_SM_R (0x1 << 3)
+#define RT5651_M_DAC_R1_SM_R_SFT 3
+#define RT5651_M_DAC_R2_SM_R (0x1 << 2)
+#define RT5651_M_DAC_R2_SM_R_SFT 2
+#define RT5651_M_OM_R_SM_R (0x1 << 1)
+#define RT5651_M_OM_R_SM_R_SFT 1
+
+/* SPOLMIX Control (0x48) */
+#define RT5651_M_DAC_R1_SPM_L (0x1 << 15)
+#define RT5651_M_DAC_R1_SPM_L_SFT 15
+#define RT5651_M_DAC_L1_SPM_L (0x1 << 14)
+#define RT5651_M_DAC_L1_SPM_L_SFT 14
+#define RT5651_M_SV_R_SPM_L (0x1 << 13)
+#define RT5651_M_SV_R_SPM_L_SFT 13
+#define RT5651_M_SV_L_SPM_L (0x1 << 12)
+#define RT5651_M_SV_L_SPM_L_SFT 12
+#define RT5651_M_BST1_SPM_L (0x1 << 11)
+#define RT5651_M_BST1_SPM_L_SFT 11
+
+/* SPORMIX Control (0x49) */
+#define RT5651_M_DAC_R1_SPM_R (0x1 << 13)
+#define RT5651_M_DAC_R1_SPM_R_SFT 13
+#define RT5651_M_SV_R_SPM_R (0x1 << 12)
+#define RT5651_M_SV_R_SPM_R_SFT 12
+#define RT5651_M_BST1_SPM_R (0x1 << 11)
+#define RT5651_M_BST1_SPM_R_SFT 11
+
+/* SPOLMIX / SPORMIX Ratio Control (0x4a) */
+#define RT5651_SPO_CLSD_RATIO_MASK (0x7)
+#define RT5651_SPO_CLSD_RATIO_SFT 0
+
+/* Mono Output Mixer Control (0x4c) */
+#define RT5651_M_DAC_R2_MM (0x1 << 15)
+#define RT5651_M_DAC_R2_MM_SFT 15
+#define RT5651_M_DAC_L2_MM (0x1 << 14)
+#define RT5651_M_DAC_L2_MM_SFT 14
+#define RT5651_M_OV_R_MM (0x1 << 13)
+#define RT5651_M_OV_R_MM_SFT 13
+#define RT5651_M_OV_L_MM (0x1 << 12)
+#define RT5651_M_OV_L_MM_SFT 12
+#define RT5651_M_BST1_MM (0x1 << 11)
+#define RT5651_M_BST1_MM_SFT 11
+#define RT5651_G_MONOMIX_MASK (0x1 << 10)
+#define RT5651_G_MONOMIX_SFT 10
+
+/* Output Left Mixer Control 1 (0x4d) */
+#define RT5651_G_BST2_OM_L_MASK (0x7 << 10)
+#define RT5651_G_BST2_OM_L_SFT 10
+#define RT5651_G_BST1_OM_L_MASK (0x7 << 7)
+#define RT5651_G_BST1_OM_L_SFT 7
+#define RT5651_G_IN1_L_OM_L_MASK (0x7 << 4)
+#define RT5651_G_IN1_L_OM_L_SFT 4
+#define RT5651_G_RM_L_OM_L_MASK (0x7 << 1)
+#define RT5651_G_RM_L_OM_L_SFT 1
+
+/* Output Left Mixer Control 2 (0x4e) */
+#define RT5651_G_DAC_L1_OM_L_MASK (0x7 << 7)
+#define RT5651_G_DAC_L1_OM_L_SFT 7
+#define RT5651_G_IN2_L_OM_L_MASK (0x7 << 4)
+#define RT5651_G_IN2_L_OM_L_SFT 4
+
+/* Output Left Mixer Control 3 (0x4f) */
+#define RT5651_M_IN2_L_OM_L (0x1 << 9)
+#define RT5651_M_IN2_L_OM_L_SFT 9
+#define RT5651_M_BST2_OM_L (0x1 << 6)
+#define RT5651_M_BST2_OM_L_SFT 6
+#define RT5651_M_BST1_OM_L (0x1 << 5)
+#define RT5651_M_BST1_OM_L_SFT 5
+#define RT5651_M_IN1_L_OM_L (0x1 << 4)
+#define RT5651_M_IN1_L_OM_L_SFT 4
+#define RT5651_M_RM_L_OM_L (0x1 << 3)
+#define RT5651_M_RM_L_OM_L_SFT 3
+#define RT5651_M_DAC_L1_OM_L (0x1)
+#define RT5651_M_DAC_L1_OM_L_SFT 0
+
+/* Output Right Mixer Control 1 (0x50) */
+#define RT5651_G_BST2_OM_R_MASK (0x7 << 10)
+#define RT5651_G_BST2_OM_R_SFT 10
+#define RT5651_G_BST1_OM_R_MASK (0x7 << 7)
+#define RT5651_G_BST1_OM_R_SFT 7
+#define RT5651_G_IN1_R_OM_R_MASK (0x7 << 4)
+#define RT5651_G_IN1_R_OM_R_SFT 4
+#define RT5651_G_RM_R_OM_R_MASK (0x7 << 1)
+#define RT5651_G_RM_R_OM_R_SFT 1
+
+/* Output Right Mixer Control 2 (0x51) */
+#define RT5651_G_DAC_R1_OM_R_MASK (0x7 << 7)
+#define RT5651_G_DAC_R1_OM_R_SFT 7
+#define RT5651_G_IN2_R_OM_R_MASK (0x7 << 4)
+#define RT5651_G_IN2_R_OM_R_SFT 4
+
+/* Output Right Mixer Control 3 (0x52) */
+#define RT5651_M_IN2_R_OM_R (0x1 << 9)
+#define RT5651_M_IN2_R_OM_R_SFT 9
+#define RT5651_M_BST2_OM_R (0x1 << 6)
+#define RT5651_M_BST2_OM_R_SFT 6
+#define RT5651_M_BST1_OM_R (0x1 << 5)
+#define RT5651_M_BST1_OM_R_SFT 5
+#define RT5651_M_IN1_R_OM_R (0x1 << 4)
+#define RT5651_M_IN1_R_OM_R_SFT 4
+#define RT5651_M_RM_R_OM_R (0x1 << 3)
+#define RT5651_M_RM_R_OM_R_SFT 3
+#define RT5651_M_DAC_R1_OM_R (0x1)
+#define RT5651_M_DAC_R1_OM_R_SFT 0
+
+/* LOUT Mixer Control (0x53) */
+#define RT5651_M_DAC_L1_LM (0x1 << 15)
+#define RT5651_M_DAC_L1_LM_SFT 15
+#define RT5651_M_DAC_R1_LM (0x1 << 14)
+#define RT5651_M_DAC_R1_LM_SFT 14
+#define RT5651_M_OV_L_LM (0x1 << 13)
+#define RT5651_M_OV_L_LM_SFT 13
+#define RT5651_M_OV_R_LM (0x1 << 12)
+#define RT5651_M_OV_R_LM_SFT 12
+#define RT5651_G_LOUTMIX_MASK (0x1 << 11)
+#define RT5651_G_LOUTMIX_SFT 11
+
+/* Power Management for Digital 1 (0x61) */
+#define RT5651_PWR_I2S1 (0x1 << 15)
+#define RT5651_PWR_I2S1_BIT 15
+#define RT5651_PWR_I2S2 (0x1 << 14)
+#define RT5651_PWR_I2S2_BIT 14
+#define RT5651_PWR_DAC_L1 (0x1 << 12)
+#define RT5651_PWR_DAC_L1_BIT 12
+#define RT5651_PWR_DAC_R1 (0x1 << 11)
+#define RT5651_PWR_DAC_R1_BIT 11
+#define RT5651_PWR_ADC_L (0x1 << 2)
+#define RT5651_PWR_ADC_L_BIT 2
+#define RT5651_PWR_ADC_R (0x1 << 1)
+#define RT5651_PWR_ADC_R_BIT 1
+
+/* Power Management for Digital 2 (0x62) */
+#define RT5651_PWR_ADC_STO1_F (0x1 << 15)
+#define RT5651_PWR_ADC_STO1_F_BIT 15
+#define RT5651_PWR_ADC_STO2_F (0x1 << 14)
+#define RT5651_PWR_ADC_STO2_F_BIT 14
+#define RT5651_PWR_DAC_STO1_F (0x1 << 11)
+#define RT5651_PWR_DAC_STO1_F_BIT 11
+#define RT5651_PWR_DAC_STO2_F (0x1 << 10)
+#define RT5651_PWR_DAC_STO2_F_BIT 10
+#define RT5651_PWR_PDM (0x1 << 9)
+#define RT5651_PWR_PDM_BIT 9
+
+/* Power Management for Analog 1 (0x63) */
+#define RT5651_PWR_VREF1 (0x1 << 15)
+#define RT5651_PWR_VREF1_BIT 15
+#define RT5651_PWR_FV1 (0x1 << 14)
+#define RT5651_PWR_FV1_BIT 14
+#define RT5651_PWR_MB (0x1 << 13)
+#define RT5651_PWR_MB_BIT 13
+#define RT5651_PWR_LM (0x1 << 12)
+#define RT5651_PWR_LM_BIT 12
+#define RT5651_PWR_BG (0x1 << 11)
+#define RT5651_PWR_BG_BIT 11
+#define RT5651_PWR_HP_L (0x1 << 7)
+#define RT5651_PWR_HP_L_BIT 7
+#define RT5651_PWR_HP_R (0x1 << 6)
+#define RT5651_PWR_HP_R_BIT 6
+#define RT5651_PWR_HA (0x1 << 5)
+#define RT5651_PWR_HA_BIT 5
+#define RT5651_PWR_VREF2 (0x1 << 4)
+#define RT5651_PWR_VREF2_BIT 4
+#define RT5651_PWR_FV2 (0x1 << 3)
+#define RT5651_PWR_FV2_BIT 3
+#define RT5651_PWR_LDO (0x1 << 2)
+#define RT5651_PWR_LDO_BIT 2
+#define RT5651_PWR_LDO_DVO_MASK (0x3)
+#define RT5651_PWR_LDO_DVO_1_0V 0
+#define RT5651_PWR_LDO_DVO_1_1V 1
+#define RT5651_PWR_LDO_DVO_1_2V 2
+#define RT5651_PWR_LDO_DVO_1_3V 3
+
+/* Power Management for Analog 2 (0x64) */
+#define RT5651_PWR_BST1 (0x1 << 15)
+#define RT5651_PWR_BST1_BIT 15
+#define RT5651_PWR_BST2 (0x1 << 14)
+#define RT5651_PWR_BST2_BIT 14
+#define RT5651_PWR_BST3 (0x1 << 13)
+#define RT5651_PWR_BST3_BIT 13
+#define RT5651_PWR_MB1 (0x1 << 11)
+#define RT5651_PWR_MB1_BIT 11
+#define RT5651_PWR_PLL (0x1 << 9)
+#define RT5651_PWR_PLL_BIT 9
+#define RT5651_PWR_BST1_OP2 (0x1 << 5)
+#define RT5651_PWR_BST1_OP2_BIT 5
+#define RT5651_PWR_BST2_OP2 (0x1 << 4)
+#define RT5651_PWR_BST2_OP2_BIT 4
+#define RT5651_PWR_BST3_OP2 (0x1 << 3)
+#define RT5651_PWR_BST3_OP2_BIT 3
+#define RT5651_PWR_JD_M (0x1 << 2)
+#define RT5651_PWM_JD_M_BIT 2
+#define RT5651_PWR_JD2 (0x1 << 1)
+#define RT5651_PWM_JD2_BIT 1
+#define RT5651_PWR_JD3 (0x1)
+#define RT5651_PWM_JD3_BIT 0
+
+/* Power Management for Mixer (0x65) */
+#define RT5651_PWR_OM_L (0x1 << 15)
+#define RT5651_PWR_OM_L_BIT 15
+#define RT5651_PWR_OM_R (0x1 << 14)
+#define RT5651_PWR_OM_R_BIT 14
+#define RT5651_PWR_RM_L (0x1 << 11)
+#define RT5651_PWR_RM_L_BIT 11
+#define RT5651_PWR_RM_R (0x1 << 10)
+#define RT5651_PWR_RM_R_BIT 10
+
+/* Power Management for Volume (0x66) */
+#define RT5651_PWR_OV_L (0x1 << 13)
+#define RT5651_PWR_OV_L_BIT 13
+#define RT5651_PWR_OV_R (0x1 << 12)
+#define RT5651_PWR_OV_R_BIT 12
+#define RT5651_PWR_HV_L (0x1 << 11)
+#define RT5651_PWR_HV_L_BIT 11
+#define RT5651_PWR_HV_R (0x1 << 10)
+#define RT5651_PWR_HV_R_BIT 10
+#define RT5651_PWR_IN1_L (0x1 << 9)
+#define RT5651_PWR_IN1_L_BIT 9
+#define RT5651_PWR_IN1_R (0x1 << 8)
+#define RT5651_PWR_IN1_R_BIT 8
+#define RT5651_PWR_IN2_L (0x1 << 7)
+#define RT5651_PWR_IN2_L_BIT 7
+#define RT5651_PWR_IN2_R (0x1 << 6)
+#define RT5651_PWR_IN2_R_BIT 6
+
+/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71) */
+#define RT5651_I2S_MS_MASK (0x1 << 15)
+#define RT5651_I2S_MS_SFT 15
+#define RT5651_I2S_MS_M (0x0 << 15)
+#define RT5651_I2S_MS_S (0x1 << 15)
+#define RT5651_I2S_O_CP_MASK (0x3 << 10)
+#define RT5651_I2S_O_CP_SFT 10
+#define RT5651_I2S_O_CP_OFF (0x0 << 10)
+#define RT5651_I2S_O_CP_U_LAW (0x1 << 10)
+#define RT5651_I2S_O_CP_A_LAW (0x2 << 10)
+#define RT5651_I2S_I_CP_MASK (0x3 << 8)
+#define RT5651_I2S_I_CP_SFT 8
+#define RT5651_I2S_I_CP_OFF (0x0 << 8)
+#define RT5651_I2S_I_CP_U_LAW (0x1 << 8)
+#define RT5651_I2S_I_CP_A_LAW (0x2 << 8)
+#define RT5651_I2S_BP_MASK (0x1 << 7)
+#define RT5651_I2S_BP_SFT 7
+#define RT5651_I2S_BP_NOR (0x0 << 7)
+#define RT5651_I2S_BP_INV (0x1 << 7)
+#define RT5651_I2S_DL_MASK (0x3 << 2)
+#define RT5651_I2S_DL_SFT 2
+#define RT5651_I2S_DL_16 (0x0 << 2)
+#define RT5651_I2S_DL_20 (0x1 << 2)
+#define RT5651_I2S_DL_24 (0x2 << 2)
+#define RT5651_I2S_DL_8 (0x3 << 2)
+#define RT5651_I2S_DF_MASK (0x3)
+#define RT5651_I2S_DF_SFT 0
+#define RT5651_I2S_DF_I2S (0x0)
+#define RT5651_I2S_DF_LEFT (0x1)
+#define RT5651_I2S_DF_PCM_A (0x2)
+#define RT5651_I2S_DF_PCM_B (0x3)
+
+/* ADC/DAC Clock Control 1 (0x73) */
+#define RT5651_I2S_PD1_MASK (0x7 << 12)
+#define RT5651_I2S_PD1_SFT 12
+#define RT5651_I2S_PD1_1 (0x0 << 12)
+#define RT5651_I2S_PD1_2 (0x1 << 12)
+#define RT5651_I2S_PD1_3 (0x2 << 12)
+#define RT5651_I2S_PD1_4 (0x3 << 12)
+#define RT5651_I2S_PD1_6 (0x4 << 12)
+#define RT5651_I2S_PD1_8 (0x5 << 12)
+#define RT5651_I2S_PD1_12 (0x6 << 12)
+#define RT5651_I2S_PD1_16 (0x7 << 12)
+#define RT5651_I2S_BCLK_MS2_MASK (0x1 << 11)
+#define RT5651_I2S_BCLK_MS2_SFT 11
+#define RT5651_I2S_BCLK_MS2_32 (0x0 << 11)
+#define RT5651_I2S_BCLK_MS2_64 (0x1 << 11)
+#define RT5651_I2S_PD2_MASK (0x7 << 8)
+#define RT5651_I2S_PD2_SFT 8
+#define RT5651_I2S_PD2_1 (0x0 << 8)
+#define RT5651_I2S_PD2_2 (0x1 << 8)
+#define RT5651_I2S_PD2_3 (0x2 << 8)
+#define RT5651_I2S_PD2_4 (0x3 << 8)
+#define RT5651_I2S_PD2_6 (0x4 << 8)
+#define RT5651_I2S_PD2_8 (0x5 << 8)
+#define RT5651_I2S_PD2_12 (0x6 << 8)
+#define RT5651_I2S_PD2_16 (0x7 << 8)
+#define RT5651_DAC_OSR_MASK (0x3 << 2)
+#define RT5651_DAC_OSR_SFT 2
+#define RT5651_DAC_OSR_128 (0x0 << 2)
+#define RT5651_DAC_OSR_64 (0x1 << 2)
+#define RT5651_DAC_OSR_32 (0x2 << 2)
+#define RT5651_DAC_OSR_128_3 (0x3 << 2)
+#define RT5651_ADC_OSR_MASK (0x3)
+#define RT5651_ADC_OSR_SFT 0
+#define RT5651_ADC_OSR_128 (0x0)
+#define RT5651_ADC_OSR_64 (0x1)
+#define RT5651_ADC_OSR_32 (0x2)
+#define RT5651_ADC_OSR_128_3 (0x3)
+
+/* ADC/DAC Clock Control 2 (0x74) */
+#define RT5651_DAHPF_EN (0x1 << 11)
+#define RT5651_DAHPF_EN_SFT 11
+#define RT5651_ADHPF_EN (0x1 << 10)
+#define RT5651_ADHPF_EN_SFT 10
+
+/* Digital Microphone Control (0x75) */
+#define RT5651_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5651_DMIC_1_EN_SFT 15
+#define RT5651_DMIC_1_DIS (0x0 << 15)
+#define RT5651_DMIC_1_EN (0x1 << 15)
+#define RT5651_DMIC_1L_LH_MASK (0x1 << 13)
+#define RT5651_DMIC_1L_LH_SFT 13
+#define RT5651_DMIC_1L_LH_FALLING (0x0 << 13)
+#define RT5651_DMIC_1L_LH_RISING (0x1 << 13)
+#define RT5651_DMIC_1R_LH_MASK (0x1 << 12)
+#define RT5651_DMIC_1R_LH_SFT 12
+#define RT5651_DMIC_1R_LH_FALLING (0x0 << 12)
+#define RT5651_DMIC_1R_LH_RISING (0x1 << 12)
+#define RT5651_DMIC_1_DP_MASK (0x3 << 10)
+#define RT5651_DMIC_1_DP_SFT 10
+#define RT5651_DMIC_1_DP_GPIO6 (0x0 << 10)
+#define RT5651_DMIC_1_DP_IN1P (0x1 << 10)
+#define RT5651_DMIC_2_DP_GPIO8 (0x2 << 10)
+#define RT5651_DMIC_CLK_MASK (0x7 << 5)
+#define RT5651_DMIC_CLK_SFT 5
+
+/* TDM Control 1 (0x77) */
+#define RT5651_TDM_INTEL_SEL_MASK (0x1 << 15)
+#define RT5651_TDM_INTEL_SEL_SFT 15
+#define RT5651_TDM_INTEL_SEL_64 (0x0 << 15)
+#define RT5651_TDM_INTEL_SEL_50 (0x1 << 15)
+#define RT5651_TDM_MODE_SEL_MASK (0x1 << 14)
+#define RT5651_TDM_MODE_SEL_SFT 14
+#define RT5651_TDM_MODE_SEL_NOR (0x0 << 14)
+#define RT5651_TDM_MODE_SEL_TDM (0x1 << 14)
+#define RT5651_TDM_CH_NUM_SEL_MASK (0x3 << 12)
+#define RT5651_TDM_CH_NUM_SEL_SFT 12
+#define RT5651_TDM_CH_NUM_SEL_2 (0x0 << 12)
+#define RT5651_TDM_CH_NUM_SEL_4 (0x1 << 12)
+#define RT5651_TDM_CH_NUM_SEL_6 (0x2 << 12)
+#define RT5651_TDM_CH_NUM_SEL_8 (0x3 << 12)
+#define RT5651_TDM_CH_LEN_SEL_MASK (0x3 << 10)
+#define RT5651_TDM_CH_LEN_SEL_SFT 10
+#define RT5651_TDM_CH_LEN_SEL_16 (0x0 << 10)
+#define RT5651_TDM_CH_LEN_SEL_20 (0x1 << 10)
+#define RT5651_TDM_CH_LEN_SEL_24 (0x2 << 10)
+#define RT5651_TDM_CH_LEN_SEL_32 (0x3 << 10)
+#define RT5651_TDM_ADC_SEL_MASK (0x1 << 9)
+#define RT5651_TDM_ADC_SEL_SFT 9
+#define RT5651_TDM_ADC_SEL_NOR (0x0 << 9)
+#define RT5651_TDM_ADC_SEL_SWAP (0x1 << 9)
+#define RT5651_TDM_ADC_START_SEL_MASK (0x1 << 8)
+#define RT5651_TDM_ADC_START_SEL_SFT 8
+#define RT5651_TDM_ADC_START_SEL_SL0 (0x0 << 8)
+#define RT5651_TDM_ADC_START_SEL_SL4 (0x1 << 8)
+#define RT5651_TDM_I2S_CH2_SEL_MASK (0x3 << 6)
+#define RT5651_TDM_I2S_CH2_SEL_SFT 6
+#define RT5651_TDM_I2S_CH2_SEL_LR (0x0 << 6)
+#define RT5651_TDM_I2S_CH2_SEL_RL (0x1 << 6)
+#define RT5651_TDM_I2S_CH2_SEL_LL (0x2 << 6)
+#define RT5651_TDM_I2S_CH2_SEL_RR (0x3 << 6)
+#define RT5651_TDM_I2S_CH4_SEL_MASK (0x3 << 4)
+#define RT5651_TDM_I2S_CH4_SEL_SFT 4
+#define RT5651_TDM_I2S_CH4_SEL_LR (0x0 << 4)
+#define RT5651_TDM_I2S_CH4_SEL_RL (0x1 << 4)
+#define RT5651_TDM_I2S_CH4_SEL_LL (0x2 << 4)
+#define RT5651_TDM_I2S_CH4_SEL_RR (0x3 << 4)
+#define RT5651_TDM_I2S_CH6_SEL_MASK (0x3 << 2)
+#define RT5651_TDM_I2S_CH6_SEL_SFT 2
+#define RT5651_TDM_I2S_CH6_SEL_LR (0x0 << 2)
+#define RT5651_TDM_I2S_CH6_SEL_RL (0x1 << 2)
+#define RT5651_TDM_I2S_CH6_SEL_LL (0x2 << 2)
+#define RT5651_TDM_I2S_CH6_SEL_RR (0x3 << 2)
+#define RT5651_TDM_I2S_CH8_SEL_MASK (0x3)
+#define RT5651_TDM_I2S_CH8_SEL_SFT 0
+#define RT5651_TDM_I2S_CH8_SEL_LR (0x0)
+#define RT5651_TDM_I2S_CH8_SEL_RL (0x1)
+#define RT5651_TDM_I2S_CH8_SEL_LL (0x2)
+#define RT5651_TDM_I2S_CH8_SEL_RR (0x3)
+
+/* TDM Control 2 (0x78) */
+#define RT5651_TDM_LRCK_POL_SEL_MASK (0x1 << 15)
+#define RT5651_TDM_LRCK_POL_SEL_SFT 15
+#define RT5651_TDM_LRCK_POL_SEL_NOR (0x0 << 15)
+#define RT5651_TDM_LRCK_POL_SEL_INV (0x1 << 15)
+#define RT5651_TDM_CH_VAL_SEL_MASK (0x1 << 14)
+#define RT5651_TDM_CH_VAL_SEL_SFT 14
+#define RT5651_TDM_CH_VAL_SEL_CH01 (0x0 << 14)
+#define RT5651_TDM_CH_VAL_SEL_CH0123 (0x1 << 14)
+#define RT5651_TDM_CH_VAL_EN (0x1 << 13)
+#define RT5651_TDM_CH_VAL_SFT 13
+#define RT5651_TDM_LPBK_EN (0x1 << 12)
+#define RT5651_TDM_LPBK_SFT 12
+#define RT5651_TDM_LRCK_PULSE_SEL_MASK (0x1 << 11)
+#define RT5651_TDM_LRCK_PULSE_SEL_SFT 11
+#define RT5651_TDM_LRCK_PULSE_SEL_BCLK (0x0 << 11)
+#define RT5651_TDM_LRCK_PULSE_SEL_CH (0x1 << 11)
+#define RT5651_TDM_END_EDGE_SEL_MASK (0x1 << 10)
+#define RT5651_TDM_END_EDGE_SEL_SFT 10
+#define RT5651_TDM_END_EDGE_SEL_POS (0x0 << 10)
+#define RT5651_TDM_END_EDGE_SEL_NEG (0x1 << 10)
+#define RT5651_TDM_END_EDGE_EN (0x1 << 9)
+#define RT5651_TDM_END_EDGE_EN_SFT 9
+#define RT5651_TDM_TRAN_EDGE_SEL_MASK (0x1 << 8)
+#define RT5651_TDM_TRAN_EDGE_SEL_SFT 8
+#define RT5651_TDM_TRAN_EDGE_SEL_POS (0x0 << 8)
+#define RT5651_TDM_TRAN_EDGE_SEL_NEG (0x1 << 8)
+#define RT5651_M_TDM2_L (0x1 << 7)
+#define RT5651_M_TDM2_L_SFT 7
+#define RT5651_M_TDM2_R (0x1 << 6)
+#define RT5651_M_TDM2_R_SFT 6
+#define RT5651_M_TDM4_L (0x1 << 5)
+#define RT5651_M_TDM4_L_SFT 5
+#define RT5651_M_TDM4_R (0x1 << 4)
+#define RT5651_M_TDM4_R_SFT 4
+
+/* TDM Control 3 (0x79) */
+#define RT5651_CH2_L_SEL_MASK (0x7 << 12)
+#define RT5651_CH2_L_SEL_SFT 12
+#define RT5651_CH2_L_SEL_SL0 (0x0 << 12)
+#define RT5651_CH2_L_SEL_SL1 (0x1 << 12)
+#define RT5651_CH2_L_SEL_SL2 (0x2 << 12)
+#define RT5651_CH2_L_SEL_SL3 (0x3 << 12)
+#define RT5651_CH2_L_SEL_SL4 (0x4 << 12)
+#define RT5651_CH2_L_SEL_SL5 (0x5 << 12)
+#define RT5651_CH2_L_SEL_SL6 (0x6 << 12)
+#define RT5651_CH2_L_SEL_SL7 (0x7 << 12)
+#define RT5651_CH2_R_SEL_MASK (0x7 << 8)
+#define RT5651_CH2_R_SEL_SFT 8
+#define RT5651_CH2_R_SEL_SL0 (0x0 << 8)
+#define RT5651_CH2_R_SEL_SL1 (0x1 << 8)
+#define RT5651_CH2_R_SEL_SL2 (0x2 << 8)
+#define RT5651_CH2_R_SEL_SL3 (0x3 << 8)
+#define RT5651_CH2_R_SEL_SL4 (0x4 << 8)
+#define RT5651_CH2_R_SEL_SL5 (0x5 << 8)
+#define RT5651_CH2_R_SEL_SL6 (0x6 << 8)
+#define RT5651_CH2_R_SEL_SL7 (0x7 << 8)
+#define RT5651_CH4_L_SEL_MASK (0x7 << 4)
+#define RT5651_CH4_L_SEL_SFT 4
+#define RT5651_CH4_L_SEL_SL0 (0x0 << 4)
+#define RT5651_CH4_L_SEL_SL1 (0x1 << 4)
+#define RT5651_CH4_L_SEL_SL2 (0x2 << 4)
+#define RT5651_CH4_L_SEL_SL3 (0x3 << 4)
+#define RT5651_CH4_L_SEL_SL4 (0x4 << 4)
+#define RT5651_CH4_L_SEL_SL5 (0x5 << 4)
+#define RT5651_CH4_L_SEL_SL6 (0x6 << 4)
+#define RT5651_CH4_L_SEL_SL7 (0x7 << 4)
+#define RT5651_CH4_R_SEL_MASK (0x7)
+#define RT5651_CH4_R_SEL_SFT 0
+#define RT5651_CH4_R_SEL_SL0 (0x0)
+#define RT5651_CH4_R_SEL_SL1 (0x1)
+#define RT5651_CH4_R_SEL_SL2 (0x2)
+#define RT5651_CH4_R_SEL_SL3 (0x3)
+#define RT5651_CH4_R_SEL_SL4 (0x4)
+#define RT5651_CH4_R_SEL_SL5 (0x5)
+#define RT5651_CH4_R_SEL_SL6 (0x6)
+#define RT5651_CH4_R_SEL_SL7 (0x7)
+
+/* Global Clock Control (0x80) */
+#define RT5651_SCLK_SRC_MASK (0x3 << 14)
+#define RT5651_SCLK_SRC_SFT 14
+#define RT5651_SCLK_SRC_MCLK (0x0 << 14)
+#define RT5651_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5651_SCLK_SRC_RCCLK (0x2 << 14)
+#define RT5651_PLL1_SRC_MASK (0x3 << 12)
+#define RT5651_PLL1_SRC_SFT 12
+#define RT5651_PLL1_SRC_MCLK (0x0 << 12)
+#define RT5651_PLL1_SRC_BCLK1 (0x1 << 12)
+#define RT5651_PLL1_SRC_BCLK2 (0x2 << 12)
+#define RT5651_PLL1_PD_MASK (0x1 << 3)
+#define RT5651_PLL1_PD_SFT 3
+#define RT5651_PLL1_PD_1 (0x0 << 3)
+#define RT5651_PLL1_PD_2 (0x1 << 3)
+
+#define RT5651_PLL_INP_MAX 40000000
+#define RT5651_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x81) */
+#define RT5651_PLL_N_MAX 0x1ff
+#define RT5651_PLL_N_MASK (RT5651_PLL_N_MAX << 7)
+#define RT5651_PLL_N_SFT 7
+#define RT5651_PLL_K_MAX 0x1f
+#define RT5651_PLL_K_MASK (RT5651_PLL_K_MAX)
+#define RT5651_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x82) */
+#define RT5651_PLL_M_MAX 0xf
+#define RT5651_PLL_M_MASK (RT5651_PLL_M_MAX << 12)
+#define RT5651_PLL_M_SFT 12
+#define RT5651_PLL_M_BP (0x1 << 11)
+#define RT5651_PLL_M_BP_SFT 11
+
+/* PLL tracking mode 1 (0x83) */
+#define RT5651_STO1_T_MASK (0x1 << 15)
+#define RT5651_STO1_T_SFT 15
+#define RT5651_STO1_T_SCLK (0x0 << 15)
+#define RT5651_STO1_T_LRCK1 (0x1 << 15)
+#define RT5651_STO2_T_MASK (0x1 << 12)
+#define RT5651_STO2_T_SFT 12
+#define RT5651_STO2_T_I2S2 (0x0 << 12)
+#define RT5651_STO2_T_LRCK2 (0x1 << 12)
+#define RT5651_ASRC2_REF_MASK (0x1 << 11)
+#define RT5651_ASRC2_REF_SFT 11
+#define RT5651_ASRC2_REF_LRCK2 (0x0 << 11)
+#define RT5651_ASRC2_REF_LRCK1 (0x1 << 11)
+#define RT5651_DMIC_1_M_MASK (0x1 << 9)
+#define RT5651_DMIC_1_M_SFT 9
+#define RT5651_DMIC_1_M_NOR (0x0 << 9)
+#define RT5651_DMIC_1_M_ASYN (0x1 << 9)
+
+/* PLL tracking mode 2 (0x84) */
+#define RT5651_STO1_ASRC_EN (0x1 << 15)
+#define RT5651_STO1_ASRC_EN_SFT 15
+#define RT5651_STO2_ASRC_EN (0x1 << 14)
+#define RT5651_STO2_ASRC_EN_SFT 14
+#define RT5651_STO1_DAC_M_MASK (0x1 << 13)
+#define RT5651_STO1_DAC_M_SFT 13
+#define RT5651_STO1_DAC_M_NOR (0x0 << 13)
+#define RT5651_STO1_DAC_M_ASRC (0x1 << 13)
+#define RT5651_STO2_DAC_M_MASK (0x1 << 12)
+#define RT5651_STO2_DAC_M_SFT 12
+#define RT5651_STO2_DAC_M_NOR (0x0 << 12)
+#define RT5651_STO2_DAC_M_ASRC (0x1 << 12)
+#define RT5651_ADC_M_MASK (0x1 << 11)
+#define RT5651_ADC_M_SFT 11
+#define RT5651_ADC_M_NOR (0x0 << 11)
+#define RT5651_ADC_M_ASRC (0x1 << 11)
+#define RT5651_I2S1_R_D_MASK (0x1 << 4)
+#define RT5651_I2S1_R_D_SFT 4
+#define RT5651_I2S1_R_D_DIS (0x0 << 4)
+#define RT5651_I2S1_R_D_EN (0x1 << 4)
+#define RT5651_I2S2_R_D_MASK (0x1 << 3)
+#define RT5651_I2S2_R_D_SFT 3
+#define RT5651_I2S2_R_D_DIS (0x0 << 3)
+#define RT5651_I2S2_R_D_EN (0x1 << 3)
+#define RT5651_PRE_SCLK_MASK (0x3)
+#define RT5651_PRE_SCLK_SFT 0
+#define RT5651_PRE_SCLK_512 (0x0)
+#define RT5651_PRE_SCLK_1024 (0x1)
+#define RT5651_PRE_SCLK_2048 (0x2)
+
+/* PLL tracking mode 3 (0x85) */
+#define RT5651_I2S1_RATE_MASK (0xf << 12)
+#define RT5651_I2S1_RATE_SFT 12
+#define RT5651_I2S2_RATE_MASK (0xf << 8)
+#define RT5651_I2S2_RATE_SFT 8
+#define RT5651_G_ASRC_LP_MASK (0x1 << 3)
+#define RT5651_G_ASRC_LP_SFT 3
+#define RT5651_ASRC_LP_F_M (0x1 << 2)
+#define RT5651_ASRC_LP_F_SFT 2
+#define RT5651_ASRC_LP_F_NOR (0x0 << 2)
+#define RT5651_ASRC_LP_F_SB (0x1 << 2)
+#define RT5651_FTK_PH_DET_MASK (0x3)
+#define RT5651_FTK_PH_DET_SFT 0
+#define RT5651_FTK_PH_DET_DIV1 (0x0)
+#define RT5651_FTK_PH_DET_DIV2 (0x1)
+#define RT5651_FTK_PH_DET_DIV4 (0x2)
+#define RT5651_FTK_PH_DET_DIV8 (0x3)
+
+/*PLL tracking mode 6 (0x89) */
+#define RT5651_I2S1_PD_MASK (0x7 << 12)
+#define RT5651_I2S1_PD_SFT 12
+#define RT5651_I2S2_PD_MASK (0x7 << 8)
+#define RT5651_I2S2_PD_SFT 8
+
+/*PLL tracking mode 7 (0x8a) */
+#define RT5651_FSI1_RATE_MASK (0xf << 12)
+#define RT5651_FSI1_RATE_SFT 12
+#define RT5651_FSI2_RATE_MASK (0xf << 8)
+#define RT5651_FSI2_RATE_SFT 8
+
+/* HPOUT Over Current Detection (0x8b) */
+#define RT5651_HP_OVCD_MASK (0x1 << 10)
+#define RT5651_HP_OVCD_SFT 10
+#define RT5651_HP_OVCD_DIS (0x0 << 10)
+#define RT5651_HP_OVCD_EN (0x1 << 10)
+#define RT5651_HP_OC_TH_MASK (0x3 << 8)
+#define RT5651_HP_OC_TH_SFT 8
+#define RT5651_HP_OC_TH_90 (0x0 << 8)
+#define RT5651_HP_OC_TH_105 (0x1 << 8)
+#define RT5651_HP_OC_TH_120 (0x2 << 8)
+#define RT5651_HP_OC_TH_135 (0x3 << 8)
+
+/* Depop Mode Control 1 (0x8e) */
+#define RT5651_SMT_TRIG_MASK (0x1 << 15)
+#define RT5651_SMT_TRIG_SFT 15
+#define RT5651_SMT_TRIG_DIS (0x0 << 15)
+#define RT5651_SMT_TRIG_EN (0x1 << 15)
+#define RT5651_HP_L_SMT_MASK (0x1 << 9)
+#define RT5651_HP_L_SMT_SFT 9
+#define RT5651_HP_L_SMT_DIS (0x0 << 9)
+#define RT5651_HP_L_SMT_EN (0x1 << 9)
+#define RT5651_HP_R_SMT_MASK (0x1 << 8)
+#define RT5651_HP_R_SMT_SFT 8
+#define RT5651_HP_R_SMT_DIS (0x0 << 8)
+#define RT5651_HP_R_SMT_EN (0x1 << 8)
+#define RT5651_HP_CD_PD_MASK (0x1 << 7)
+#define RT5651_HP_CD_PD_SFT 7
+#define RT5651_HP_CD_PD_DIS (0x0 << 7)
+#define RT5651_HP_CD_PD_EN (0x1 << 7)
+#define RT5651_RSTN_MASK (0x1 << 6)
+#define RT5651_RSTN_SFT 6
+#define RT5651_RSTN_DIS (0x0 << 6)
+#define RT5651_RSTN_EN (0x1 << 6)
+#define RT5651_RSTP_MASK (0x1 << 5)
+#define RT5651_RSTP_SFT 5
+#define RT5651_RSTP_DIS (0x0 << 5)
+#define RT5651_RSTP_EN (0x1 << 5)
+#define RT5651_HP_CO_MASK (0x1 << 4)
+#define RT5651_HP_CO_SFT 4
+#define RT5651_HP_CO_DIS (0x0 << 4)
+#define RT5651_HP_CO_EN (0x1 << 4)
+#define RT5651_HP_CP_MASK (0x1 << 3)
+#define RT5651_HP_CP_SFT 3
+#define RT5651_HP_CP_PD (0x0 << 3)
+#define RT5651_HP_CP_PU (0x1 << 3)
+#define RT5651_HP_SG_MASK (0x1 << 2)
+#define RT5651_HP_SG_SFT 2
+#define RT5651_HP_SG_DIS (0x0 << 2)
+#define RT5651_HP_SG_EN (0x1 << 2)
+#define RT5651_HP_DP_MASK (0x1 << 1)
+#define RT5651_HP_DP_SFT 1
+#define RT5651_HP_DP_PD (0x0 << 1)
+#define RT5651_HP_DP_PU (0x1 << 1)
+#define RT5651_HP_CB_MASK (0x1)
+#define RT5651_HP_CB_SFT 0
+#define RT5651_HP_CB_PD (0x0)
+#define RT5651_HP_CB_PU (0x1)
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5651_DEPOP_MASK (0x1 << 13)
+#define RT5651_DEPOP_SFT 13
+#define RT5651_DEPOP_AUTO (0x0 << 13)
+#define RT5651_DEPOP_MAN (0x1 << 13)
+#define RT5651_RAMP_MASK (0x1 << 12)
+#define RT5651_RAMP_SFT 12
+#define RT5651_RAMP_DIS (0x0 << 12)
+#define RT5651_RAMP_EN (0x1 << 12)
+#define RT5651_BPS_MASK (0x1 << 11)
+#define RT5651_BPS_SFT 11
+#define RT5651_BPS_DIS (0x0 << 11)
+#define RT5651_BPS_EN (0x1 << 11)
+#define RT5651_FAST_UPDN_MASK (0x1 << 10)
+#define RT5651_FAST_UPDN_SFT 10
+#define RT5651_FAST_UPDN_DIS (0x0 << 10)
+#define RT5651_FAST_UPDN_EN (0x1 << 10)
+#define RT5651_MRES_MASK (0x3 << 8)
+#define RT5651_MRES_SFT 8
+#define RT5651_MRES_15MO (0x0 << 8)
+#define RT5651_MRES_25MO (0x1 << 8)
+#define RT5651_MRES_35MO (0x2 << 8)
+#define RT5651_MRES_45MO (0x3 << 8)
+#define RT5651_VLO_MASK (0x1 << 7)
+#define RT5651_VLO_SFT 7
+#define RT5651_VLO_3V (0x0 << 7)
+#define RT5651_VLO_32V (0x1 << 7)
+#define RT5651_DIG_DP_MASK (0x1 << 6)
+#define RT5651_DIG_DP_SFT 6
+#define RT5651_DIG_DP_DIS (0x0 << 6)
+#define RT5651_DIG_DP_EN (0x1 << 6)
+#define RT5651_DP_TH_MASK (0x3 << 4)
+#define RT5651_DP_TH_SFT 4
+
+/* Depop Mode Control 3 (0x90) */
+#define RT5651_CP_SYS_MASK (0x7 << 12)
+#define RT5651_CP_SYS_SFT 12
+#define RT5651_CP_FQ1_MASK (0x7 << 8)
+#define RT5651_CP_FQ1_SFT 8
+#define RT5651_CP_FQ2_MASK (0x7 << 4)
+#define RT5651_CP_FQ2_SFT 4
+#define RT5651_CP_FQ3_MASK (0x7)
+#define RT5651_CP_FQ3_SFT 0
+#define RT5651_CP_FQ_1_5_KHZ 0
+#define RT5651_CP_FQ_3_KHZ 1
+#define RT5651_CP_FQ_6_KHZ 2
+#define RT5651_CP_FQ_12_KHZ 3
+#define RT5651_CP_FQ_24_KHZ 4
+#define RT5651_CP_FQ_48_KHZ 5
+#define RT5651_CP_FQ_96_KHZ 6
+#define RT5651_CP_FQ_192_KHZ 7
+
+/* HPOUT charge pump (0x91) */
+#define RT5651_OSW_L_MASK (0x1 << 11)
+#define RT5651_OSW_L_SFT 11
+#define RT5651_OSW_L_DIS (0x0 << 11)
+#define RT5651_OSW_L_EN (0x1 << 11)
+#define RT5651_OSW_R_MASK (0x1 << 10)
+#define RT5651_OSW_R_SFT 10
+#define RT5651_OSW_R_DIS (0x0 << 10)
+#define RT5651_OSW_R_EN (0x1 << 10)
+#define RT5651_PM_HP_MASK (0x3 << 8)
+#define RT5651_PM_HP_SFT 8
+#define RT5651_PM_HP_LV (0x0 << 8)
+#define RT5651_PM_HP_MV (0x1 << 8)
+#define RT5651_PM_HP_HV (0x2 << 8)
+#define RT5651_IB_HP_MASK (0x3 << 6)
+#define RT5651_IB_HP_SFT 6
+#define RT5651_IB_HP_125IL (0x0 << 6)
+#define RT5651_IB_HP_25IL (0x1 << 6)
+#define RT5651_IB_HP_5IL (0x2 << 6)
+#define RT5651_IB_HP_1IL (0x3 << 6)
+
+/* Micbias Control (0x93) */
+#define RT5651_MIC1_BS_MASK (0x1 << 15)
+#define RT5651_MIC1_BS_SFT 15
+#define RT5651_MIC1_BS_9AV (0x0 << 15)
+#define RT5651_MIC1_BS_75AV (0x1 << 15)
+#define RT5651_MIC1_CLK_MASK (0x1 << 13)
+#define RT5651_MIC1_CLK_SFT 13
+#define RT5651_MIC1_CLK_DIS (0x0 << 13)
+#define RT5651_MIC1_CLK_EN (0x1 << 13)
+#define RT5651_MIC1_OVCD_MASK (0x1 << 11)
+#define RT5651_MIC1_OVCD_SFT 11
+#define RT5651_MIC1_OVCD_DIS (0x0 << 11)
+#define RT5651_MIC1_OVCD_EN (0x1 << 11)
+#define RT5651_MIC1_OVTH_MASK (0x3 << 9)
+#define RT5651_MIC1_OVTH_SFT 9
+#define RT5651_MIC1_OVTH_600UA (0x0 << 9)
+#define RT5651_MIC1_OVTH_1500UA (0x1 << 9)
+#define RT5651_MIC1_OVTH_2000UA (0x2 << 9)
+#define RT5651_PWR_MB_MASK (0x1 << 5)
+#define RT5651_PWR_MB_SFT 5
+#define RT5651_PWR_MB_PD (0x0 << 5)
+#define RT5651_PWR_MB_PU (0x1 << 5)
+#define RT5651_PWR_CLK12M_MASK (0x1 << 4)
+#define RT5651_PWR_CLK12M_SFT 4
+#define RT5651_PWR_CLK12M_PD (0x0 << 4)
+#define RT5651_PWR_CLK12M_PU (0x1 << 4)
+
+/* Analog JD Control 1 (0x94) */
+#define RT5651_JD2_CMP_MASK (0x7 << 12)
+#define RT5651_JD2_CMP_SFT 12
+#define RT5651_JD_PU (0x1 << 11)
+#define RT5651_JD_PU_SFT 11
+#define RT5651_JD_PD (0x1 << 10)
+#define RT5651_JD_PD_SFT 10
+#define RT5651_JD_MODE_SEL_MASK (0x3 << 8)
+#define RT5651_JD_MODE_SEL_SFT 8
+#define RT5651_JD_MODE_SEL_M0 (0x0 << 8)
+#define RT5651_JD_MODE_SEL_M1 (0x1 << 8)
+#define RT5651_JD_MODE_SEL_M2 (0x2 << 8)
+#define RT5651_JD_M_CMP (0x7 << 4)
+#define RT5651_JD_M_CMP_SFT 4
+#define RT5651_JD_M_PU (0x1 << 3)
+#define RT5651_JD_M_PU_SFT 3
+#define RT5651_JD_M_PD (0x1 << 2)
+#define RT5651_JD_M_PD_SFT 2
+#define RT5651_JD_M_MODE_SEL_MASK (0x3)
+#define RT5651_JD_M_MODE_SEL_SFT 0
+#define RT5651_JD_M_MODE_SEL_M0 (0x0)
+#define RT5651_JD_M_MODE_SEL_M1 (0x1)
+#define RT5651_JD_M_MODE_SEL_M2 (0x2)
+
+/* Analog JD Control 2 (0x95) */
+#define RT5651_JD3_CMP_MASK (0x7 << 12)
+#define RT5651_JD3_CMP_SFT 12
+
+/* EQ Control 1 (0xb0) */
+#define RT5651_EQ_SRC_MASK (0x1 << 15)
+#define RT5651_EQ_SRC_SFT 15
+#define RT5651_EQ_SRC_DAC (0x0 << 15)
+#define RT5651_EQ_SRC_ADC (0x1 << 15)
+#define RT5651_EQ_UPD (0x1 << 14)
+#define RT5651_EQ_UPD_BIT 14
+#define RT5651_EQ_CD_MASK (0x1 << 13)
+#define RT5651_EQ_CD_SFT 13
+#define RT5651_EQ_CD_DIS (0x0 << 13)
+#define RT5651_EQ_CD_EN (0x1 << 13)
+#define RT5651_EQ_DITH_MASK (0x3 << 8)
+#define RT5651_EQ_DITH_SFT 8
+#define RT5651_EQ_DITH_NOR (0x0 << 8)
+#define RT5651_EQ_DITH_LSB (0x1 << 8)
+#define RT5651_EQ_DITH_LSB_1 (0x2 << 8)
+#define RT5651_EQ_DITH_LSB_2 (0x3 << 8)
+#define RT5651_EQ_CD_F (0x1 << 7)
+#define RT5651_EQ_CD_F_BIT 7
+#define RT5651_EQ_STA_HP2 (0x1 << 6)
+#define RT5651_EQ_STA_HP2_BIT 6
+#define RT5651_EQ_STA_HP1 (0x1 << 5)
+#define RT5651_EQ_STA_HP1_BIT 5
+#define RT5651_EQ_STA_BP4 (0x1 << 4)
+#define RT5651_EQ_STA_BP4_BIT 4
+#define RT5651_EQ_STA_BP3 (0x1 << 3)
+#define RT5651_EQ_STA_BP3_BIT 3
+#define RT5651_EQ_STA_BP2 (0x1 << 2)
+#define RT5651_EQ_STA_BP2_BIT 2
+#define RT5651_EQ_STA_BP1 (0x1 << 1)
+#define RT5651_EQ_STA_BP1_BIT 1
+#define RT5651_EQ_STA_LP (0x1)
+#define RT5651_EQ_STA_LP_BIT 0
+
+/* EQ Control 2 (0xb1) */
+#define RT5651_EQ_HPF1_M_MASK (0x1 << 8)
+#define RT5651_EQ_HPF1_M_SFT 8
+#define RT5651_EQ_HPF1_M_HI (0x0 << 8)
+#define RT5651_EQ_HPF1_M_1ST (0x1 << 8)
+#define RT5651_EQ_LPF1_M_MASK (0x1 << 7)
+#define RT5651_EQ_LPF1_M_SFT 7
+#define RT5651_EQ_LPF1_M_LO (0x0 << 7)
+#define RT5651_EQ_LPF1_M_1ST (0x1 << 7)
+#define RT5651_EQ_HPF2_MASK (0x1 << 6)
+#define RT5651_EQ_HPF2_SFT 6
+#define RT5651_EQ_HPF2_DIS (0x0 << 6)
+#define RT5651_EQ_HPF2_EN (0x1 << 6)
+#define RT5651_EQ_HPF1_MASK (0x1 << 5)
+#define RT5651_EQ_HPF1_SFT 5
+#define RT5651_EQ_HPF1_DIS (0x0 << 5)
+#define RT5651_EQ_HPF1_EN (0x1 << 5)
+#define RT5651_EQ_BPF4_MASK (0x1 << 4)
+#define RT5651_EQ_BPF4_SFT 4
+#define RT5651_EQ_BPF4_DIS (0x0 << 4)
+#define RT5651_EQ_BPF4_EN (0x1 << 4)
+#define RT5651_EQ_BPF3_MASK (0x1 << 3)
+#define RT5651_EQ_BPF3_SFT 3
+#define RT5651_EQ_BPF3_DIS (0x0 << 3)
+#define RT5651_EQ_BPF3_EN (0x1 << 3)
+#define RT5651_EQ_BPF2_MASK (0x1 << 2)
+#define RT5651_EQ_BPF2_SFT 2
+#define RT5651_EQ_BPF2_DIS (0x0 << 2)
+#define RT5651_EQ_BPF2_EN (0x1 << 2)
+#define RT5651_EQ_BPF1_MASK (0x1 << 1)
+#define RT5651_EQ_BPF1_SFT 1
+#define RT5651_EQ_BPF1_DIS (0x0 << 1)
+#define RT5651_EQ_BPF1_EN (0x1 << 1)
+#define RT5651_EQ_LPF_MASK (0x1)
+#define RT5651_EQ_LPF_SFT 0
+#define RT5651_EQ_LPF_DIS (0x0)
+#define RT5651_EQ_LPF_EN (0x1)
+#define RT5651_EQ_CTRL_MASK (0x7f)
+
+/* Memory Test (0xb2) */
+#define RT5651_MT_MASK (0x1 << 15)
+#define RT5651_MT_SFT 15
+#define RT5651_MT_DIS (0x0 << 15)
+#define RT5651_MT_EN (0x1 << 15)
+
+/* ALC Control 1 (0xb4) */
+#define RT5651_ALC_P_MASK (0x1 << 15)
+#define RT5651_ALC_P_SFT 15
+#define RT5651_ALC_P_DAC (0x0 << 15)
+#define RT5651_ALC_P_ADC (0x1 << 15)
+#define RT5651_ALC_MASK (0x1 << 14)
+#define RT5651_ALC_SFT 14
+#define RT5651_ALC_DIS (0x0 << 14)
+#define RT5651_ALC_EN (0x1 << 14)
+#define RT5651_ALC_UPD (0x1 << 13)
+#define RT5651_ALC_UPD_BIT 13
+#define RT5651_ALC_AR_MASK (0x1f << 8)
+#define RT5651_ALC_AR_SFT 8
+#define RT5651_ALC_R_MASK (0x7 << 5)
+#define RT5651_ALC_R_SFT 5
+#define RT5651_ALC_R_48K (0x1 << 5)
+#define RT5651_ALC_R_96K (0x2 << 5)
+#define RT5651_ALC_R_192K (0x3 << 5)
+#define RT5651_ALC_R_441K (0x5 << 5)
+#define RT5651_ALC_R_882K (0x6 << 5)
+#define RT5651_ALC_R_1764K (0x7 << 5)
+#define RT5651_ALC_RC_MASK (0x1f)
+#define RT5651_ALC_RC_SFT 0
+
+/* ALC Control 2 (0xb5) */
+#define RT5651_ALC_POB_MASK (0x3f << 8)
+#define RT5651_ALC_POB_SFT 8
+#define RT5651_ALC_DRC_MASK (0x1 << 7)
+#define RT5651_ALC_DRC_SFT 7
+#define RT5651_ALC_DRC_DIS (0x0 << 7)
+#define RT5651_ALC_DRC_EN (0x1 << 7)
+#define RT5651_ALC_CPR_MASK (0x3 << 5)
+#define RT5651_ALC_CPR_SFT 5
+#define RT5651_ALC_CPR_1_1 (0x0 << 5)
+#define RT5651_ALC_CPR_1_2 (0x1 << 5)
+#define RT5651_ALC_CPR_1_4 (0x2 << 5)
+#define RT5651_ALC_CPR_1_8 (0x3 << 5)
+#define RT5651_ALC_PRB_MASK (0x1f)
+#define RT5651_ALC_PRB_SFT 0
+
+/* ALC Control 3 (0xb6) */
+#define RT5651_ALC_NGB_MASK (0xf << 12)
+#define RT5651_ALC_NGB_SFT 12
+#define RT5651_ALC_TAR_MASK (0x1f << 7)
+#define RT5651_ALC_TAR_SFT 7
+#define RT5651_ALC_NG_MASK (0x1 << 6)
+#define RT5651_ALC_NG_SFT 6
+#define RT5651_ALC_NG_DIS (0x0 << 6)
+#define RT5651_ALC_NG_EN (0x1 << 6)
+#define RT5651_ALC_NGH_MASK (0x1 << 5)
+#define RT5651_ALC_NGH_SFT 5
+#define RT5651_ALC_NGH_DIS (0x0 << 5)
+#define RT5651_ALC_NGH_EN (0x1 << 5)
+#define RT5651_ALC_NGT_MASK (0x1f)
+#define RT5651_ALC_NGT_SFT 0
+
+/* Jack Detect Control 1 (0xbb) */
+#define RT5651_JD_MASK (0x7 << 13)
+#define RT5651_JD_SFT 13
+#define RT5651_JD_DIS (0x0 << 13)
+#define RT5651_JD_GPIO1 (0x1 << 13)
+#define RT5651_JD_GPIO2 (0x2 << 13)
+#define RT5651_JD_GPIO3 (0x3 << 13)
+#define RT5651_JD_GPIO4 (0x4 << 13)
+#define RT5651_JD_GPIO5 (0x5 << 13)
+#define RT5651_JD_GPIO6 (0x6 << 13)
+#define RT5651_JD_HP_MASK (0x1 << 11)
+#define RT5651_JD_HP_SFT 11
+#define RT5651_JD_HP_DIS (0x0 << 11)
+#define RT5651_JD_HP_EN (0x1 << 11)
+#define RT5651_JD_HP_TRG_MASK (0x1 << 10)
+#define RT5651_JD_HP_TRG_SFT 10
+#define RT5651_JD_HP_TRG_LO (0x0 << 10)
+#define RT5651_JD_HP_TRG_HI (0x1 << 10)
+#define RT5651_JD_SPL_MASK (0x1 << 9)
+#define RT5651_JD_SPL_SFT 9
+#define RT5651_JD_SPL_DIS (0x0 << 9)
+#define RT5651_JD_SPL_EN (0x1 << 9)
+#define RT5651_JD_SPL_TRG_MASK (0x1 << 8)
+#define RT5651_JD_SPL_TRG_SFT 8
+#define RT5651_JD_SPL_TRG_LO (0x0 << 8)
+#define RT5651_JD_SPL_TRG_HI (0x1 << 8)
+#define RT5651_JD_SPR_MASK (0x1 << 7)
+#define RT5651_JD_SPR_SFT 7
+#define RT5651_JD_SPR_DIS (0x0 << 7)
+#define RT5651_JD_SPR_EN (0x1 << 7)
+#define RT5651_JD_SPR_TRG_MASK (0x1 << 6)
+#define RT5651_JD_SPR_TRG_SFT 6
+#define RT5651_JD_SPR_TRG_LO (0x0 << 6)
+#define RT5651_JD_SPR_TRG_HI (0x1 << 6)
+#define RT5651_JD_LO_MASK (0x1 << 3)
+#define RT5651_JD_LO_SFT 3
+#define RT5651_JD_LO_DIS (0x0 << 3)
+#define RT5651_JD_LO_EN (0x1 << 3)
+#define RT5651_JD_LO_TRG_MASK (0x1 << 2)
+#define RT5651_JD_LO_TRG_SFT 2
+#define RT5651_JD_LO_TRG_LO (0x0 << 2)
+#define RT5651_JD_LO_TRG_HI (0x1 << 2)
+
+/* Jack Detect Control 2 (0xbc) */
+#define RT5651_JD_TRG_SEL_MASK (0x7 << 9)
+#define RT5651_JD_TRG_SEL_SFT 9
+#define RT5651_JD_TRG_SEL_GPIO (0x0 << 9)
+#define RT5651_JD_TRG_SEL_JD1_1 (0x1 << 9)
+#define RT5651_JD_TRG_SEL_JD1_2 (0x2 << 9)
+#define RT5651_JD_TRG_SEL_JD2 (0x3 << 9)
+#define RT5651_JD_TRG_SEL_JD3 (0x4 << 9)
+#define RT5651_JD3_IRQ_EN (0x1 << 8)
+#define RT5651_JD3_IRQ_EN_SFT 8
+#define RT5651_JD3_EN_STKY (0x1 << 7)
+#define RT5651_JD3_EN_STKY_SFT 7
+#define RT5651_JD3_INV (0x1 << 6)
+#define RT5651_JD3_INV_SFT 6
+
+/* IRQ Control 1 (0xbd) */
+#define RT5651_IRQ_JD_MASK (0x1 << 15)
+#define RT5651_IRQ_JD_SFT 15
+#define RT5651_IRQ_JD_BP (0x0 << 15)
+#define RT5651_IRQ_JD_NOR (0x1 << 15)
+#define RT5651_JD_STKY_MASK (0x1 << 13)
+#define RT5651_JD_STKY_SFT 13
+#define RT5651_JD_STKY_DIS (0x0 << 13)
+#define RT5651_JD_STKY_EN (0x1 << 13)
+#define RT5651_JD_P_MASK (0x1 << 11)
+#define RT5651_JD_P_SFT 11
+#define RT5651_JD_P_NOR (0x0 << 11)
+#define RT5651_JD_P_INV (0x1 << 11)
+#define RT5651_JD1_1_IRQ_EN (0x1 << 9)
+#define RT5651_JD1_1_IRQ_EN_SFT 9
+#define RT5651_JD1_1_EN_STKY (0x1 << 8)
+#define RT5651_JD1_1_EN_STKY_SFT 8
+#define RT5651_JD1_1_INV (0x1 << 7)
+#define RT5651_JD1_1_INV_SFT 7
+#define RT5651_JD1_2_IRQ_EN (0x1 << 6)
+#define RT5651_JD1_2_IRQ_EN_SFT 6
+#define RT5651_JD1_2_EN_STKY (0x1 << 5)
+#define RT5651_JD1_2_EN_STKY_SFT 5
+#define RT5651_JD1_2_INV (0x1 << 4)
+#define RT5651_JD1_2_INV_SFT 4
+#define RT5651_JD2_IRQ_EN (0x1 << 3)
+#define RT5651_JD2_IRQ_EN_SFT 3
+#define RT5651_JD2_EN_STKY (0x1 << 2)
+#define RT5651_JD2_EN_STKY_SFT 2
+#define RT5651_JD2_INV (0x1 << 1)
+#define RT5651_JD2_INV_SFT 1
+
+/* IRQ Control 2 (0xbe) */
+#define RT5651_IRQ_MB1_OC_MASK (0x1 << 15)
+#define RT5651_IRQ_MB1_OC_SFT 15
+#define RT5651_IRQ_MB1_OC_BP (0x0 << 15)
+#define RT5651_IRQ_MB1_OC_NOR (0x1 << 15)
+#define RT5651_MB1_OC_STKY_MASK (0x1 << 11)
+#define RT5651_MB1_OC_STKY_SFT 11
+#define RT5651_MB1_OC_STKY_DIS (0x0 << 11)
+#define RT5651_MB1_OC_STKY_EN (0x1 << 11)
+#define RT5651_MB1_OC_P_MASK (0x1 << 7)
+#define RT5651_MB1_OC_P_SFT 7
+#define RT5651_MB1_OC_P_NOR (0x0 << 7)
+#define RT5651_MB1_OC_P_INV (0x1 << 7)
+#define RT5651_MB2_OC_P_MASK (0x1 << 6)
+#define RT5651_MB1_OC_CLR (0x1 << 3)
+#define RT5651_MB1_OC_CLR_SFT 3
+#define RT5651_STA_GPIO8 (0x1)
+#define RT5651_STA_GPIO8_BIT 0
+
+/* Internal Status and GPIO status (0xbf) */
+#define RT5651_STA_JD3 (0x1 << 15)
+#define RT5651_STA_JD3_BIT 15
+#define RT5651_STA_JD2 (0x1 << 14)
+#define RT5651_STA_JD2_BIT 14
+#define RT5651_STA_JD1_2 (0x1 << 13)
+#define RT5651_STA_JD1_2_BIT 13
+#define RT5651_STA_JD1_1 (0x1 << 12)
+#define RT5651_STA_JD1_1_BIT 12
+#define RT5651_STA_GP7 (0x1 << 11)
+#define RT5651_STA_GP7_BIT 11
+#define RT5651_STA_GP6 (0x1 << 10)
+#define RT5651_STA_GP6_BIT 10
+#define RT5651_STA_GP5 (0x1 << 9)
+#define RT5651_STA_GP5_BIT 9
+#define RT5651_STA_GP1 (0x1 << 8)
+#define RT5651_STA_GP1_BIT 8
+#define RT5651_STA_GP2 (0x1 << 7)
+#define RT5651_STA_GP2_BIT 7
+#define RT5651_STA_GP3 (0x1 << 6)
+#define RT5651_STA_GP3_BIT 6
+#define RT5651_STA_GP4 (0x1 << 5)
+#define RT5651_STA_GP4_BIT 5
+#define RT5651_STA_GP_JD (0x1 << 4)
+#define RT5651_STA_GP_JD_BIT 4
+
+/* GPIO Control 1 (0xc0) */
+#define RT5651_GP1_PIN_MASK (0x1 << 15)
+#define RT5651_GP1_PIN_SFT 15
+#define RT5651_GP1_PIN_GPIO1 (0x0 << 15)
+#define RT5651_GP1_PIN_IRQ (0x1 << 15)
+#define RT5651_GP2_PIN_MASK (0x1 << 14)
+#define RT5651_GP2_PIN_SFT 14
+#define RT5651_GP2_PIN_GPIO2 (0x0 << 14)
+#define RT5651_GP2_PIN_DMIC1_SCL (0x1 << 14)
+#define RT5651_GPIO_M_MASK (0x1 << 9)
+#define RT5651_GPIO_M_SFT 9
+#define RT5651_GPIO_M_FLT (0x0 << 9)
+#define RT5651_GPIO_M_PH (0x1 << 9)
+#define RT5651_I2S2_SEL_MASK (0x1 << 8)
+#define RT5651_I2S2_SEL_SFT 8
+#define RT5651_I2S2_SEL_I2S (0x0 << 8)
+#define RT5651_I2S2_SEL_GPIO (0x1 << 8)
+#define RT5651_GP5_PIN_MASK (0x1 << 7)
+#define RT5651_GP5_PIN_SFT 7
+#define RT5651_GP5_PIN_GPIO5 (0x0 << 7)
+#define RT5651_GP5_PIN_IRQ (0x1 << 7)
+#define RT5651_GP6_PIN_MASK (0x1 << 6)
+#define RT5651_GP6_PIN_SFT 6
+#define RT5651_GP6_PIN_GPIO6 (0x0 << 6)
+#define RT5651_GP6_PIN_DMIC_SDA (0x1 << 6)
+#define RT5651_GP7_PIN_MASK (0x1 << 5)
+#define RT5651_GP7_PIN_SFT 5
+#define RT5651_GP7_PIN_GPIO7 (0x0 << 5)
+#define RT5651_GP7_PIN_IRQ (0x1 << 5)
+#define RT5651_GP8_PIN_MASK (0x1 << 4)
+#define RT5651_GP8_PIN_SFT 4
+#define RT5651_GP8_PIN_GPIO8 (0x0 << 4)
+#define RT5651_GP8_PIN_DMIC_SDA (0x1 << 4)
+#define RT5651_GPIO_PDM_SEL_MASK (0x1 << 3)
+#define RT5651_GPIO_PDM_SEL_SFT 3
+#define RT5651_GPIO_PDM_SEL_GPIO (0x0 << 3)
+#define RT5651_GPIO_PDM_SEL_PDM (0x1 << 3)
+
+/* GPIO Control 2 (0xc1) */
+#define RT5651_GP5_DR_MASK (0x1 << 14)
+#define RT5651_GP5_DR_SFT 14
+#define RT5651_GP5_DR_IN (0x0 << 14)
+#define RT5651_GP5_DR_OUT (0x1 << 14)
+#define RT5651_GP5_OUT_MASK (0x1 << 13)
+#define RT5651_GP5_OUT_SFT 13
+#define RT5651_GP5_OUT_LO (0x0 << 13)
+#define RT5651_GP5_OUT_HI (0x1 << 13)
+#define RT5651_GP5_P_MASK (0x1 << 12)
+#define RT5651_GP5_P_SFT 12
+#define RT5651_GP5_P_NOR (0x0 << 12)
+#define RT5651_GP5_P_INV (0x1 << 12)
+#define RT5651_GP4_DR_MASK (0x1 << 11)
+#define RT5651_GP4_DR_SFT 11
+#define RT5651_GP4_DR_IN (0x0 << 11)
+#define RT5651_GP4_DR_OUT (0x1 << 11)
+#define RT5651_GP4_OUT_MASK (0x1 << 10)
+#define RT5651_GP4_OUT_SFT 10
+#define RT5651_GP4_OUT_LO (0x0 << 10)
+#define RT5651_GP4_OUT_HI (0x1 << 10)
+#define RT5651_GP4_P_MASK (0x1 << 9)
+#define RT5651_GP4_P_SFT 9
+#define RT5651_GP4_P_NOR (0x0 << 9)
+#define RT5651_GP4_P_INV (0x1 << 9)
+#define RT5651_GP3_DR_MASK (0x1 << 8)
+#define RT5651_GP3_DR_SFT 8
+#define RT5651_GP3_DR_IN (0x0 << 8)
+#define RT5651_GP3_DR_OUT (0x1 << 8)
+#define RT5651_GP3_OUT_MASK (0x1 << 7)
+#define RT5651_GP3_OUT_SFT 7
+#define RT5651_GP3_OUT_LO (0x0 << 7)
+#define RT5651_GP3_OUT_HI (0x1 << 7)
+#define RT5651_GP3_P_MASK (0x1 << 6)
+#define RT5651_GP3_P_SFT 6
+#define RT5651_GP3_P_NOR (0x0 << 6)
+#define RT5651_GP3_P_INV (0x1 << 6)
+#define RT5651_GP2_DR_MASK (0x1 << 5)
+#define RT5651_GP2_DR_SFT 5
+#define RT5651_GP2_DR_IN (0x0 << 5)
+#define RT5651_GP2_DR_OUT (0x1 << 5)
+#define RT5651_GP2_OUT_MASK (0x1 << 4)
+#define RT5651_GP2_OUT_SFT 4
+#define RT5651_GP2_OUT_LO (0x0 << 4)
+#define RT5651_GP2_OUT_HI (0x1 << 4)
+#define RT5651_GP2_P_MASK (0x1 << 3)
+#define RT5651_GP2_P_SFT 3
+#define RT5651_GP2_P_NOR (0x0 << 3)
+#define RT5651_GP2_P_INV (0x1 << 3)
+#define RT5651_GP1_DR_MASK (0x1 << 2)
+#define RT5651_GP1_DR_SFT 2
+#define RT5651_GP1_DR_IN (0x0 << 2)
+#define RT5651_GP1_DR_OUT (0x1 << 2)
+#define RT5651_GP1_OUT_MASK (0x1 << 1)
+#define RT5651_GP1_OUT_SFT 1
+#define RT5651_GP1_OUT_LO (0x0 << 1)
+#define RT5651_GP1_OUT_HI (0x1 << 1)
+#define RT5651_GP1_P_MASK (0x1)
+#define RT5651_GP1_P_SFT 0
+#define RT5651_GP1_P_NOR (0x0)
+#define RT5651_GP1_P_INV (0x1)
+
+/* GPIO Control 3 (0xc2) */
+#define RT5651_GP8_DR_MASK (0x1 << 8)
+#define RT5651_GP8_DR_SFT 8
+#define RT5651_GP8_DR_IN (0x0 << 8)
+#define RT5651_GP8_DR_OUT (0x1 << 8)
+#define RT5651_GP8_OUT_MASK (0x1 << 7)
+#define RT5651_GP8_OUT_SFT 7
+#define RT5651_GP8_OUT_LO (0x0 << 7)
+#define RT5651_GP8_OUT_HI (0x1 << 7)
+#define RT5651_GP8_P_MASK (0x1 << 6)
+#define RT5651_GP8_P_SFT 6
+#define RT5651_GP8_P_NOR (0x0 << 6)
+#define RT5651_GP8_P_INV (0x1 << 6)
+#define RT5651_GP7_DR_MASK (0x1 << 5)
+#define RT5651_GP7_DR_SFT 5
+#define RT5651_GP7_DR_IN (0x0 << 5)
+#define RT5651_GP7_DR_OUT (0x1 << 5)
+#define RT5651_GP7_OUT_MASK (0x1 << 4)
+#define RT5651_GP7_OUT_SFT 4
+#define RT5651_GP7_OUT_LO (0x0 << 4)
+#define RT5651_GP7_OUT_HI (0x1 << 4)
+#define RT5651_GP7_P_MASK (0x1 << 3)
+#define RT5651_GP7_P_SFT 3
+#define RT5651_GP7_P_NOR (0x0 << 3)
+#define RT5651_GP7_P_INV (0x1 << 3)
+#define RT5651_GP6_DR_MASK (0x1 << 2)
+#define RT5651_GP6_DR_SFT 2
+#define RT5651_GP6_DR_IN (0x0 << 2)
+#define RT5651_GP6_DR_OUT (0x1 << 2)
+#define RT5651_GP6_OUT_MASK (0x1 << 1)
+#define RT5651_GP6_OUT_SFT 1
+#define RT5651_GP6_OUT_LO (0x0 << 1)
+#define RT5651_GP6_OUT_HI (0x1 << 1)
+#define RT5651_GP6_P_MASK (0x1)
+#define RT5651_GP6_P_SFT 0
+#define RT5651_GP6_P_NOR (0x0)
+#define RT5651_GP6_P_INV (0x1)
+
+/* Scramble Control (0xce) */
+#define RT5651_SCB_SWAP_MASK (0x1 << 15)
+#define RT5651_SCB_SWAP_SFT 15
+#define RT5651_SCB_SWAP_DIS (0x0 << 15)
+#define RT5651_SCB_SWAP_EN (0x1 << 15)
+#define RT5651_SCB_MASK (0x1 << 14)
+#define RT5651_SCB_SFT 14
+#define RT5651_SCB_DIS (0x0 << 14)
+#define RT5651_SCB_EN (0x1 << 14)
+
+/* Baseback Control (0xcf) */
+#define RT5651_BB_MASK (0x1 << 15)
+#define RT5651_BB_SFT 15
+#define RT5651_BB_DIS (0x0 << 15)
+#define RT5651_BB_EN (0x1 << 15)
+#define RT5651_BB_CT_MASK (0x7 << 12)
+#define RT5651_BB_CT_SFT 12
+#define RT5651_BB_CT_A (0x0 << 12)
+#define RT5651_BB_CT_B (0x1 << 12)
+#define RT5651_BB_CT_C (0x2 << 12)
+#define RT5651_BB_CT_D (0x3 << 12)
+#define RT5651_M_BB_L_MASK (0x1 << 9)
+#define RT5651_M_BB_L_SFT 9
+#define RT5651_M_BB_R_MASK (0x1 << 8)
+#define RT5651_M_BB_R_SFT 8
+#define RT5651_M_BB_HPF_L_MASK (0x1 << 7)
+#define RT5651_M_BB_HPF_L_SFT 7
+#define RT5651_M_BB_HPF_R_MASK (0x1 << 6)
+#define RT5651_M_BB_HPF_R_SFT 6
+#define RT5651_G_BB_BST_MASK (0x3f)
+#define RT5651_G_BB_BST_SFT 0
+
+/* MP3 Plus Control 1 (0xd0) */
+#define RT5651_M_MP3_L_MASK (0x1 << 15)
+#define RT5651_M_MP3_L_SFT 15
+#define RT5651_M_MP3_R_MASK (0x1 << 14)
+#define RT5651_M_MP3_R_SFT 14
+#define RT5651_M_MP3_MASK (0x1 << 13)
+#define RT5651_M_MP3_SFT 13
+#define RT5651_M_MP3_DIS (0x0 << 13)
+#define RT5651_M_MP3_EN (0x1 << 13)
+#define RT5651_EG_MP3_MASK (0x1f << 8)
+#define RT5651_EG_MP3_SFT 8
+#define RT5651_MP3_HLP_MASK (0x1 << 7)
+#define RT5651_MP3_HLP_SFT 7
+#define RT5651_MP3_HLP_DIS (0x0 << 7)
+#define RT5651_MP3_HLP_EN (0x1 << 7)
+#define RT5651_M_MP3_ORG_L_MASK (0x1 << 6)
+#define RT5651_M_MP3_ORG_L_SFT 6
+#define RT5651_M_MP3_ORG_R_MASK (0x1 << 5)
+#define RT5651_M_MP3_ORG_R_SFT 5
+
+/* MP3 Plus Control 2 (0xd1) */
+#define RT5651_MP3_WT_MASK (0x1 << 13)
+#define RT5651_MP3_WT_SFT 13
+#define RT5651_MP3_WT_1_4 (0x0 << 13)
+#define RT5651_MP3_WT_1_2 (0x1 << 13)
+#define RT5651_OG_MP3_MASK (0x1f << 8)
+#define RT5651_OG_MP3_SFT 8
+#define RT5651_HG_MP3_MASK (0x3f)
+#define RT5651_HG_MP3_SFT 0
+
+/* 3D HP Control 1 (0xd2) */
+#define RT5651_3D_CF_MASK (0x1 << 15)
+#define RT5651_3D_CF_SFT 15
+#define RT5651_3D_CF_DIS (0x0 << 15)
+#define RT5651_3D_CF_EN (0x1 << 15)
+#define RT5651_3D_HP_MASK (0x1 << 14)
+#define RT5651_3D_HP_SFT 14
+#define RT5651_3D_HP_DIS (0x0 << 14)
+#define RT5651_3D_HP_EN (0x1 << 14)
+#define RT5651_3D_BT_MASK (0x1 << 13)
+#define RT5651_3D_BT_SFT 13
+#define RT5651_3D_BT_DIS (0x0 << 13)
+#define RT5651_3D_BT_EN (0x1 << 13)
+#define RT5651_3D_1F_MIX_MASK (0x3 << 11)
+#define RT5651_3D_1F_MIX_SFT 11
+#define RT5651_3D_HP_M_MASK (0x1 << 10)
+#define RT5651_3D_HP_M_SFT 10
+#define RT5651_3D_HP_M_SUR (0x0 << 10)
+#define RT5651_3D_HP_M_FRO (0x1 << 10)
+#define RT5651_M_3D_HRTF_MASK (0x1 << 9)
+#define RT5651_M_3D_HRTF_SFT 9
+#define RT5651_M_3D_D2H_MASK (0x1 << 8)
+#define RT5651_M_3D_D2H_SFT 8
+#define RT5651_M_3D_D2R_MASK (0x1 << 7)
+#define RT5651_M_3D_D2R_SFT 7
+#define RT5651_M_3D_REVB_MASK (0x1 << 6)
+#define RT5651_M_3D_REVB_SFT 6
+
+/* Adjustable high pass filter control 1 (0xd3) */
+#define RT5651_2ND_HPF_MASK (0x1 << 15)
+#define RT5651_2ND_HPF_SFT 15
+#define RT5651_2ND_HPF_DIS (0x0 << 15)
+#define RT5651_2ND_HPF_EN (0x1 << 15)
+#define RT5651_HPF_CF_L_MASK (0x7 << 12)
+#define RT5651_HPF_CF_L_SFT 12
+#define RT5651_HPF_CF_R_MASK (0x7 << 8)
+#define RT5651_HPF_CF_R_SFT 8
+#define RT5651_ZD_T_MASK (0x3 << 6)
+#define RT5651_ZD_T_SFT 6
+#define RT5651_ZD_F_MASK (0x3 << 4)
+#define RT5651_ZD_F_SFT 4
+#define RT5651_ZD_F_IM (0x0 << 4)
+#define RT5651_ZD_F_ZC_IM (0x1 << 4)
+#define RT5651_ZD_F_ZC_IOD (0x2 << 4)
+#define RT5651_ZD_F_UN (0x3 << 4)
+
+/* Adjustable high pass filter control 2 (0xd4) */
+#define RT5651_HPF_CF_L_NUM_MASK (0x3f << 8)
+#define RT5651_HPF_CF_L_NUM_SFT 8
+#define RT5651_HPF_CF_R_NUM_MASK (0x3f)
+#define RT5651_HPF_CF_R_NUM_SFT 0
+
+/* HP calibration control and Amp detection (0xd6) */
+#define RT5651_SI_DAC_MASK (0x1 << 11)
+#define RT5651_SI_DAC_SFT 11
+#define RT5651_SI_DAC_AUTO (0x0 << 11)
+#define RT5651_SI_DAC_TEST (0x1 << 11)
+#define RT5651_DC_CAL_M_MASK (0x1 << 10)
+#define RT5651_DC_CAL_M_SFT 10
+#define RT5651_DC_CAL_M_NOR (0x0 << 10)
+#define RT5651_DC_CAL_M_CAL (0x1 << 10)
+#define RT5651_DC_CAL_MASK (0x1 << 9)
+#define RT5651_DC_CAL_SFT 9
+#define RT5651_DC_CAL_DIS (0x0 << 9)
+#define RT5651_DC_CAL_EN (0x1 << 9)
+#define RT5651_HPD_RCV_MASK (0x7 << 6)
+#define RT5651_HPD_RCV_SFT 6
+#define RT5651_HPD_PS_MASK (0x1 << 5)
+#define RT5651_HPD_PS_SFT 5
+#define RT5651_HPD_PS_DIS (0x0 << 5)
+#define RT5651_HPD_PS_EN (0x1 << 5)
+#define RT5651_CAL_M_MASK (0x1 << 4)
+#define RT5651_CAL_M_SFT 4
+#define RT5651_CAL_M_DEP (0x0 << 4)
+#define RT5651_CAL_M_CAL (0x1 << 4)
+#define RT5651_CAL_MASK (0x1 << 3)
+#define RT5651_CAL_SFT 3
+#define RT5651_CAL_DIS (0x0 << 3)
+#define RT5651_CAL_EN (0x1 << 3)
+#define RT5651_CAL_TEST_MASK (0x1 << 2)
+#define RT5651_CAL_TEST_SFT 2
+#define RT5651_CAL_TEST_DIS (0x0 << 2)
+#define RT5651_CAL_TEST_EN (0x1 << 2)
+#define RT5651_CAL_P_MASK (0x3)
+#define RT5651_CAL_P_SFT 0
+#define RT5651_CAL_P_NONE (0x0)
+#define RT5651_CAL_P_CAL (0x1)
+#define RT5651_CAL_P_DAC_CAL (0x2)
+
+/* Soft volume and zero cross control 1 (0xd9) */
+#define RT5651_SV_MASK (0x1 << 15)
+#define RT5651_SV_SFT 15
+#define RT5651_SV_DIS (0x0 << 15)
+#define RT5651_SV_EN (0x1 << 15)
+#define RT5651_OUT_SV_MASK (0x1 << 13)
+#define RT5651_OUT_SV_SFT 13
+#define RT5651_OUT_SV_DIS (0x0 << 13)
+#define RT5651_OUT_SV_EN (0x1 << 13)
+#define RT5651_HP_SV_MASK (0x1 << 12)
+#define RT5651_HP_SV_SFT 12
+#define RT5651_HP_SV_DIS (0x0 << 12)
+#define RT5651_HP_SV_EN (0x1 << 12)
+#define RT5651_ZCD_DIG_MASK (0x1 << 11)
+#define RT5651_ZCD_DIG_SFT 11
+#define RT5651_ZCD_DIG_DIS (0x0 << 11)
+#define RT5651_ZCD_DIG_EN (0x1 << 11)
+#define RT5651_ZCD_MASK (0x1 << 10)
+#define RT5651_ZCD_SFT 10
+#define RT5651_ZCD_PD (0x0 << 10)
+#define RT5651_ZCD_PU (0x1 << 10)
+#define RT5651_M_ZCD_MASK (0x3f << 4)
+#define RT5651_M_ZCD_SFT 4
+#define RT5651_M_ZCD_OM_L (0x1 << 7)
+#define RT5651_M_ZCD_OM_R (0x1 << 6)
+#define RT5651_M_ZCD_RM_L (0x1 << 5)
+#define RT5651_M_ZCD_RM_R (0x1 << 4)
+#define RT5651_SV_DLY_MASK (0xf)
+#define RT5651_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0xda) */
+#define RT5651_ZCD_HP_MASK (0x1 << 15)
+#define RT5651_ZCD_HP_SFT 15
+#define RT5651_ZCD_HP_DIS (0x0 << 15)
+#define RT5651_ZCD_HP_EN (0x1 << 15)
+
+/* Digital Misc Control (0xfa) */
+#define RT5651_I2S2_MS_SP_MASK (0x1 << 8)
+#define RT5651_I2S2_MS_SP_SEL 8
+#define RT5651_I2S2_MS_SP_64 (0x0 << 8)
+#define RT5651_I2S2_MS_SP_50 (0x1 << 8)
+#define RT5651_CLK_DET_EN (0x1 << 3)
+#define RT5651_CLK_DET_EN_SFT 3
+#define RT5651_AMP_DET_EN (0x1 << 1)
+#define RT5651_AMP_DET_EN_SFT 1
+#define RT5651_D_GATE_EN (0x1)
+#define RT5651_D_GATE_EN_SFT 0
+
+/* Codec Private Register definition */
+/* 3D Speaker Control (0x63) */
+#define RT5651_3D_SPK_MASK (0x1 << 15)
+#define RT5651_3D_SPK_SFT 15
+#define RT5651_3D_SPK_DIS (0x0 << 15)
+#define RT5651_3D_SPK_EN (0x1 << 15)
+#define RT5651_3D_SPK_M_MASK (0x3 << 13)
+#define RT5651_3D_SPK_M_SFT 13
+#define RT5651_3D_SPK_CG_MASK (0x1f << 8)
+#define RT5651_3D_SPK_CG_SFT 8
+#define RT5651_3D_SPK_SG_MASK (0x1f)
+#define RT5651_3D_SPK_SG_SFT 0
+
+/* Wind Noise Detection Control 1 (0x6c) */
+#define RT5651_WND_MASK (0x1 << 15)
+#define RT5651_WND_SFT 15
+#define RT5651_WND_DIS (0x0 << 15)
+#define RT5651_WND_EN (0x1 << 15)
+
+/* Wind Noise Detection Control 2 (0x6d) */
+#define RT5651_WND_FC_NW_MASK (0x3f << 10)
+#define RT5651_WND_FC_NW_SFT 10
+#define RT5651_WND_FC_WK_MASK (0x3f << 4)
+#define RT5651_WND_FC_WK_SFT 4
+
+/* Wind Noise Detection Control 3 (0x6e) */
+#define RT5651_HPF_FC_MASK (0x3f << 6)
+#define RT5651_HPF_FC_SFT 6
+#define RT5651_WND_FC_ST_MASK (0x3f)
+#define RT5651_WND_FC_ST_SFT 0
+
+/* Wind Noise Detection Control 4 (0x6f) */
+#define RT5651_WND_TH_LO_MASK (0x3ff)
+#define RT5651_WND_TH_LO_SFT 0
+
+/* Wind Noise Detection Control 5 (0x70) */
+#define RT5651_WND_TH_HI_MASK (0x3ff)
+#define RT5651_WND_TH_HI_SFT 0
+
+/* Wind Noise Detection Control 8 (0x73) */
+#define RT5651_WND_WIND_MASK (0x1 << 13) /* Read-Only */
+#define RT5651_WND_WIND_SFT 13
+#define RT5651_WND_STRONG_MASK (0x1 << 12) /* Read-Only */
+#define RT5651_WND_STRONG_SFT 12
+enum {
+ RT5651_NO_WIND,
+ RT5651_BREEZE,
+ RT5651_STORM,
+};
+
+/* Dipole Speaker Interface (0x75) */
+#define RT5651_DP_ATT_MASK (0x3 << 14)
+#define RT5651_DP_ATT_SFT 14
+#define RT5651_DP_SPK_MASK (0x1 << 10)
+#define RT5651_DP_SPK_SFT 10
+#define RT5651_DP_SPK_DIS (0x0 << 10)
+#define RT5651_DP_SPK_EN (0x1 << 10)
+
+/* EQ Pre Volume Control (0xb3) */
+#define RT5651_EQ_PRE_VOL_MASK (0xffff)
+#define RT5651_EQ_PRE_VOL_SFT 0
+
+/* EQ Post Volume Control (0xb4) */
+#define RT5651_EQ_PST_VOL_MASK (0xffff)
+#define RT5651_EQ_PST_VOL_SFT 0
+
+/* System Clock Source */
+enum {
+ RT5651_SCLK_S_MCLK,
+ RT5651_SCLK_S_PLL1,
+ RT5651_SCLK_S_RCCLK,
+};
+
+/* PLL1 Source */
+enum {
+ RT5651_PLL1_S_MCLK,
+ RT5651_PLL1_S_BCLK1,
+ RT5651_PLL1_S_BCLK2,
+};
+
+enum {
+ RT5651_AIF1,
+ RT5651_AIF2,
+ RT5651_AIFS,
+};
+
+struct rt5651_pll_code {
+ bool m_bp; /* Indicates bypass m code or not. */
+ int m_code;
+ int n_code;
+ int k_code;
+};
+
+struct rt5651_priv {
+ struct snd_soc_codec *codec;
+ struct rt5651_platform_data pdata;
+ struct regmap *regmap;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5651_AIFS];
+ int bclk[RT5651_AIFS];
+ int master[RT5651_AIFS];
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ int dmic_en;
+ bool hp_mute;
+};
+
+#endif /* __RT5651_H__ */
diff --git a/kernel/sound/soc/codecs/rt5670-dsp.h b/kernel/sound/soc/codecs/rt5670-dsp.h
new file mode 100644
index 000000000..a34d0cdb8
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5670-dsp.h
@@ -0,0 +1,54 @@
+/*
+ * 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__
+#define __RT5670_DSP_H__
+
+#define RT5670_DSP_CTRL1 0xe0
+#define RT5670_DSP_CTRL2 0xe1
+#define RT5670_DSP_CTRL3 0xe2
+#define RT5670_DSP_CTRL4 0xe3
+#define RT5670_DSP_CTRL5 0xe4
+
+/* DSP Control 1 (0xe0) */
+#define RT5670_DSP_CMD_MASK (0xff << 8)
+#define RT5670_DSP_CMD_PE (0x0d << 8) /* Patch Entry */
+#define RT5670_DSP_CMD_MW (0x3b << 8) /* Memory Write */
+#define RT5670_DSP_CMD_MR (0x37 << 8) /* Memory Read */
+#define RT5670_DSP_CMD_RR (0x60 << 8) /* Register Read */
+#define RT5670_DSP_CMD_RW (0x68 << 8) /* Register Write */
+#define RT5670_DSP_REG_DATHI (0x26 << 8) /* High Data Addr */
+#define RT5670_DSP_REG_DATLO (0x25 << 8) /* Low Data Addr */
+#define RT5670_DSP_CLK_MASK (0x3 << 6)
+#define RT5670_DSP_CLK_SFT 6
+#define RT5670_DSP_CLK_768K (0x0 << 6)
+#define RT5670_DSP_CLK_384K (0x1 << 6)
+#define RT5670_DSP_CLK_192K (0x2 << 6)
+#define RT5670_DSP_CLK_96K (0x3 << 6)
+#define RT5670_DSP_BUSY_MASK (0x1 << 5)
+#define RT5670_DSP_RW_MASK (0x1 << 4)
+#define RT5670_DSP_DL_MASK (0x3 << 2)
+#define RT5670_DSP_DL_0 (0x0 << 2)
+#define RT5670_DSP_DL_1 (0x1 << 2)
+#define RT5670_DSP_DL_2 (0x2 << 2)
+#define RT5670_DSP_DL_3 (0x3 << 2)
+#define RT5670_DSP_I2C_AL_16 (0x1 << 1)
+#define RT5670_DSP_CMD_EN (0x1)
+
+struct rt5670_dsp_param {
+ u16 cmd_fmt;
+ u16 addr;
+ u16 data;
+ u8 cmd;
+};
+
+#endif /* __RT5670_DSP_H__ */
+
diff --git a/kernel/sound/soc/codecs/rt5670.c b/kernel/sound/soc/codecs/rt5670.c
new file mode 100644
index 000000000..cc7f84a15
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5670.c
@@ -0,0 +1,3059 @@
+/*
+ * 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>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/spi/spi.h>
+#include <linux/dmi.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/rt5670.h>
+
+#include "rl6231.h"
+#include "rt5670.h"
+#include "rt5670-dsp.h"
+
+#define RT5670_DEVICE_ID 0x6271
+
+#define RT5670_PR_RANGE_BASE (0xff + 1)
+#define RT5670_PR_SPACING 0x100
+
+#define RT5670_PR_BASE (RT5670_PR_RANGE_BASE + (0 * RT5670_PR_SPACING))
+
+static const struct regmap_range_cfg rt5670_ranges[] = {
+ { .name = "PR", .range_min = RT5670_PR_BASE,
+ .range_max = RT5670_PR_BASE + 0xf8,
+ .selector_reg = RT5670_PRIV_INDEX,
+ .selector_mask = 0xff,
+ .selector_shift = 0x0,
+ .window_start = RT5670_PRIV_DATA,
+ .window_len = 0x1, },
+};
+
+static struct reg_default init_list[] = {
+ { RT5670_PR_BASE + 0x14, 0x9a8a },
+ { RT5670_PR_BASE + 0x38, 0x3ba1 },
+ { RT5670_PR_BASE + 0x3d, 0x3640 },
+};
+#define RT5670_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+static const struct reg_default rt5670_reg[] = {
+ { 0x00, 0x0000 },
+ { 0x02, 0x8888 },
+ { 0x03, 0x8888 },
+ { 0x0a, 0x0001 },
+ { 0x0b, 0x0827 },
+ { 0x0c, 0x0000 },
+ { 0x0d, 0x0008 },
+ { 0x0e, 0x0000 },
+ { 0x0f, 0x0808 },
+ { 0x19, 0xafaf },
+ { 0x1a, 0xafaf },
+ { 0x1b, 0x0011 },
+ { 0x1c, 0x2f2f },
+ { 0x1d, 0x2f2f },
+ { 0x1e, 0x0000 },
+ { 0x1f, 0x2f2f },
+ { 0x20, 0x0000 },
+ { 0x26, 0x7860 },
+ { 0x27, 0x7860 },
+ { 0x28, 0x7871 },
+ { 0x29, 0x8080 },
+ { 0x2a, 0x5656 },
+ { 0x2b, 0x5454 },
+ { 0x2c, 0xaaa0 },
+ { 0x2d, 0x0000 },
+ { 0x2e, 0x2f2f },
+ { 0x2f, 0x1002 },
+ { 0x30, 0x0000 },
+ { 0x31, 0x5f00 },
+ { 0x32, 0x0000 },
+ { 0x33, 0x0000 },
+ { 0x34, 0x0000 },
+ { 0x35, 0x0000 },
+ { 0x36, 0x0000 },
+ { 0x37, 0x0000 },
+ { 0x38, 0x0000 },
+ { 0x3b, 0x0000 },
+ { 0x3c, 0x007f },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x007f },
+ { 0x45, 0xe00f },
+ { 0x4c, 0x5380 },
+ { 0x4f, 0x0073 },
+ { 0x52, 0x00d3 },
+ { 0x53, 0xf000 },
+ { 0x61, 0x0000 },
+ { 0x62, 0x0001 },
+ { 0x63, 0x00c3 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x0001 },
+ { 0x66, 0x0000 },
+ { 0x6f, 0x8000 },
+ { 0x70, 0x8000 },
+ { 0x71, 0x8000 },
+ { 0x72, 0x8000 },
+ { 0x73, 0x7770 },
+ { 0x74, 0x0e00 },
+ { 0x75, 0x1505 },
+ { 0x76, 0x0015 },
+ { 0x77, 0x0c00 },
+ { 0x78, 0x4000 },
+ { 0x79, 0x0123 },
+ { 0x7f, 0x1100 },
+ { 0x80, 0x0000 },
+ { 0x81, 0x0000 },
+ { 0x82, 0x0000 },
+ { 0x83, 0x0000 },
+ { 0x84, 0x0000 },
+ { 0x85, 0x0000 },
+ { 0x86, 0x0004 },
+ { 0x87, 0x0000 },
+ { 0x88, 0x0000 },
+ { 0x89, 0x0000 },
+ { 0x8a, 0x0000 },
+ { 0x8b, 0x0000 },
+ { 0x8c, 0x0003 },
+ { 0x8d, 0x0000 },
+ { 0x8e, 0x0004 },
+ { 0x8f, 0x1100 },
+ { 0x90, 0x0646 },
+ { 0x91, 0x0c06 },
+ { 0x93, 0x0000 },
+ { 0x94, 0x1270 },
+ { 0x95, 0x1000 },
+ { 0x97, 0x0000 },
+ { 0x98, 0x0000 },
+ { 0x99, 0x0000 },
+ { 0x9a, 0x2184 },
+ { 0x9b, 0x010a },
+ { 0x9c, 0x0aea },
+ { 0x9d, 0x000c },
+ { 0x9e, 0x0400 },
+ { 0xae, 0x7000 },
+ { 0xaf, 0x0000 },
+ { 0xb0, 0x7000 },
+ { 0xb1, 0x0000 },
+ { 0xb2, 0x0000 },
+ { 0xb3, 0x001f },
+ { 0xb4, 0x220c },
+ { 0xb5, 0x1f00 },
+ { 0xb6, 0x0000 },
+ { 0xb7, 0x0000 },
+ { 0xbb, 0x0000 },
+ { 0xbc, 0x0000 },
+ { 0xbd, 0x0000 },
+ { 0xbe, 0x0000 },
+ { 0xbf, 0x0000 },
+ { 0xc0, 0x0000 },
+ { 0xc1, 0x0000 },
+ { 0xc2, 0x0000 },
+ { 0xcd, 0x0000 },
+ { 0xce, 0x0000 },
+ { 0xcf, 0x1813 },
+ { 0xd0, 0x0690 },
+ { 0xd1, 0x1c17 },
+ { 0xd3, 0xa220 },
+ { 0xd4, 0x0000 },
+ { 0xd6, 0x0400 },
+ { 0xd9, 0x0809 },
+ { 0xda, 0x0000 },
+ { 0xdb, 0x0001 },
+ { 0xdc, 0x0049 },
+ { 0xdd, 0x0024 },
+ { 0xe6, 0x8000 },
+ { 0xe7, 0x0000 },
+ { 0xec, 0xa200 },
+ { 0xed, 0x0000 },
+ { 0xee, 0xa200 },
+ { 0xef, 0x0000 },
+ { 0xf8, 0x0000 },
+ { 0xf9, 0x0000 },
+ { 0xfa, 0x8010 },
+ { 0xfb, 0x0033 },
+ { 0xfc, 0x0100 },
+};
+
+static bool rt5670_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5670_ranges); i++) {
+ if ((reg >= rt5670_ranges[i].window_start &&
+ reg <= rt5670_ranges[i].window_start +
+ rt5670_ranges[i].window_len) ||
+ (reg >= rt5670_ranges[i].range_min &&
+ reg <= rt5670_ranges[i].range_max)) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT5670_RESET:
+ case RT5670_PDM_DATA_CTRL1:
+ case RT5670_PDM1_DATA_CTRL4:
+ case RT5670_PDM2_DATA_CTRL4:
+ case RT5670_PRIV_DATA:
+ case RT5670_ASRC_5:
+ case RT5670_CJ_CTRL1:
+ case RT5670_CJ_CTRL2:
+ case RT5670_CJ_CTRL3:
+ case RT5670_A_JD_CTRL1:
+ case RT5670_A_JD_CTRL2:
+ case RT5670_VAD_CTRL5:
+ case RT5670_ADC_EQ_CTRL1:
+ case RT5670_EQ_CTRL1:
+ case RT5670_ALC_CTRL_1:
+ case RT5670_IRQ_CTRL2:
+ case RT5670_INT_IRQ_ST:
+ case RT5670_IL_CMD:
+ case RT5670_DSP_CTRL1:
+ case RT5670_DSP_CTRL2:
+ case RT5670_DSP_CTRL3:
+ case RT5670_DSP_CTRL4:
+ case RT5670_DSP_CTRL5:
+ case RT5670_VENDOR_ID:
+ case RT5670_VENDOR_ID1:
+ case RT5670_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5670_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5670_ranges); i++) {
+ if ((reg >= rt5670_ranges[i].window_start &&
+ reg <= rt5670_ranges[i].window_start +
+ rt5670_ranges[i].window_len) ||
+ (reg >= rt5670_ranges[i].range_min &&
+ reg <= rt5670_ranges[i].range_max)) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT5670_RESET:
+ case RT5670_HP_VOL:
+ case RT5670_LOUT1:
+ case RT5670_CJ_CTRL1:
+ case RT5670_CJ_CTRL2:
+ case RT5670_CJ_CTRL3:
+ case RT5670_IN2:
+ case RT5670_INL1_INR1_VOL:
+ case RT5670_DAC1_DIG_VOL:
+ case RT5670_DAC2_DIG_VOL:
+ case RT5670_DAC_CTRL:
+ case RT5670_STO1_ADC_DIG_VOL:
+ case RT5670_MONO_ADC_DIG_VOL:
+ case RT5670_STO2_ADC_DIG_VOL:
+ case RT5670_ADC_BST_VOL1:
+ case RT5670_ADC_BST_VOL2:
+ case RT5670_STO2_ADC_MIXER:
+ case RT5670_STO1_ADC_MIXER:
+ case RT5670_MONO_ADC_MIXER:
+ case RT5670_AD_DA_MIXER:
+ case RT5670_STO_DAC_MIXER:
+ case RT5670_DD_MIXER:
+ case RT5670_DIG_MIXER:
+ case RT5670_DSP_PATH1:
+ case RT5670_DSP_PATH2:
+ case RT5670_DIG_INF1_DATA:
+ case RT5670_DIG_INF2_DATA:
+ case RT5670_PDM_OUT_CTRL:
+ case RT5670_PDM_DATA_CTRL1:
+ case RT5670_PDM1_DATA_CTRL2:
+ case RT5670_PDM1_DATA_CTRL3:
+ case RT5670_PDM1_DATA_CTRL4:
+ case RT5670_PDM2_DATA_CTRL2:
+ case RT5670_PDM2_DATA_CTRL3:
+ case RT5670_PDM2_DATA_CTRL4:
+ case RT5670_REC_L1_MIXER:
+ case RT5670_REC_L2_MIXER:
+ case RT5670_REC_R1_MIXER:
+ case RT5670_REC_R2_MIXER:
+ case RT5670_HPO_MIXER:
+ case RT5670_MONO_MIXER:
+ case RT5670_OUT_L1_MIXER:
+ case RT5670_OUT_R1_MIXER:
+ case RT5670_LOUT_MIXER:
+ case RT5670_PWR_DIG1:
+ case RT5670_PWR_DIG2:
+ case RT5670_PWR_ANLG1:
+ case RT5670_PWR_ANLG2:
+ case RT5670_PWR_MIXER:
+ case RT5670_PWR_VOL:
+ case RT5670_PRIV_INDEX:
+ case RT5670_PRIV_DATA:
+ case RT5670_I2S4_SDP:
+ case RT5670_I2S1_SDP:
+ case RT5670_I2S2_SDP:
+ case RT5670_I2S3_SDP:
+ case RT5670_ADDA_CLK1:
+ case RT5670_ADDA_CLK2:
+ case RT5670_DMIC_CTRL1:
+ case RT5670_DMIC_CTRL2:
+ case RT5670_TDM_CTRL_1:
+ case RT5670_TDM_CTRL_2:
+ case RT5670_TDM_CTRL_3:
+ case RT5670_DSP_CLK:
+ case RT5670_GLB_CLK:
+ case RT5670_PLL_CTRL1:
+ case RT5670_PLL_CTRL2:
+ case RT5670_ASRC_1:
+ case RT5670_ASRC_2:
+ case RT5670_ASRC_3:
+ case RT5670_ASRC_4:
+ case RT5670_ASRC_5:
+ case RT5670_ASRC_7:
+ case RT5670_ASRC_8:
+ case RT5670_ASRC_9:
+ case RT5670_ASRC_10:
+ case RT5670_ASRC_11:
+ case RT5670_ASRC_12:
+ case RT5670_ASRC_13:
+ case RT5670_ASRC_14:
+ case RT5670_DEPOP_M1:
+ case RT5670_DEPOP_M2:
+ case RT5670_DEPOP_M3:
+ case RT5670_CHARGE_PUMP:
+ case RT5670_MICBIAS:
+ case RT5670_A_JD_CTRL1:
+ case RT5670_A_JD_CTRL2:
+ case RT5670_VAD_CTRL1:
+ case RT5670_VAD_CTRL2:
+ case RT5670_VAD_CTRL3:
+ case RT5670_VAD_CTRL4:
+ case RT5670_VAD_CTRL5:
+ case RT5670_ADC_EQ_CTRL1:
+ case RT5670_ADC_EQ_CTRL2:
+ case RT5670_EQ_CTRL1:
+ case RT5670_EQ_CTRL2:
+ case RT5670_ALC_DRC_CTRL1:
+ case RT5670_ALC_DRC_CTRL2:
+ case RT5670_ALC_CTRL_1:
+ case RT5670_ALC_CTRL_2:
+ case RT5670_ALC_CTRL_3:
+ case RT5670_JD_CTRL:
+ case RT5670_IRQ_CTRL1:
+ case RT5670_IRQ_CTRL2:
+ case RT5670_INT_IRQ_ST:
+ case RT5670_GPIO_CTRL1:
+ case RT5670_GPIO_CTRL2:
+ case RT5670_GPIO_CTRL3:
+ case RT5670_SCRABBLE_FUN:
+ case RT5670_SCRABBLE_CTRL:
+ case RT5670_BASE_BACK:
+ case RT5670_MP3_PLUS1:
+ case RT5670_MP3_PLUS2:
+ case RT5670_ADJ_HPF1:
+ case RT5670_ADJ_HPF2:
+ case RT5670_HP_CALIB_AMP_DET:
+ case RT5670_SV_ZCD1:
+ case RT5670_SV_ZCD2:
+ case RT5670_IL_CMD:
+ case RT5670_IL_CMD2:
+ case RT5670_IL_CMD3:
+ case RT5670_DRC_HL_CTRL1:
+ case RT5670_DRC_HL_CTRL2:
+ case RT5670_ADC_MONO_HP_CTRL1:
+ case RT5670_ADC_MONO_HP_CTRL2:
+ case RT5670_ADC_STO2_HP_CTRL1:
+ case RT5670_ADC_STO2_HP_CTRL2:
+ case RT5670_JD_CTRL3:
+ case RT5670_JD_CTRL4:
+ case RT5670_DIG_MISC:
+ case RT5670_DSP_CTRL1:
+ case RT5670_DSP_CTRL2:
+ case RT5670_DSP_CTRL3:
+ case RT5670_DSP_CTRL4:
+ case RT5670_DSP_CTRL5:
+ case RT5670_GEN_CTRL2:
+ case RT5670_GEN_CTRL3:
+ case RT5670_VENDOR_ID:
+ case RT5670_VENDOR_ID1:
+ case RT5670_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * rt5670_headset_detect - Detect headset.
+ * @codec: SoC audio codec 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_codec *codec, int jack_insert)
+{
+ int val;
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ if (jack_insert) {
+ snd_soc_dapm_force_enable_pin(&codec->dapm,
+ "Mic Det Power");
+ snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0);
+ snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+ RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD,
+ RT5670_CBJ_MN_JD);
+ snd_soc_write(codec, RT5670_GPIO_CTRL2, 0x0004);
+ snd_soc_update_bits(codec, RT5670_GPIO_CTRL1,
+ RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ);
+ snd_soc_update_bits(codec, RT5670_CJ_CTRL1,
+ RT5670_CBJ_BST1_EN, RT5670_CBJ_BST1_EN);
+ snd_soc_write(codec, RT5670_JD_CTRL3, 0x00f0);
+ snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+ RT5670_CBJ_MN_JD, RT5670_CBJ_MN_JD);
+ snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+ RT5670_CBJ_MN_JD, 0);
+ msleep(300);
+ val = snd_soc_read(codec, RT5670_CJ_CTRL3) & 0x7;
+ if (val == 0x1 || val == 0x2) {
+ rt5670->jack_type = SND_JACK_HEADSET;
+ /* for push button */
+ snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x8);
+ snd_soc_update_bits(codec, RT5670_IL_CMD, 0x40, 0x40);
+ snd_soc_read(codec, RT5670_IL_CMD);
+ } else {
+ snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
+ rt5670->jack_type = SND_JACK_HEADPHONE;
+ snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
+ snd_soc_dapm_sync(&codec->dapm);
+ }
+ } else {
+ snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x0);
+ snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
+ rt5670->jack_type = 0;
+ snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
+ snd_soc_dapm_sync(&codec->dapm);
+ }
+
+ return rt5670->jack_type;
+}
+
+void rt5670_jack_suspend(struct snd_soc_codec *codec)
+{
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ rt5670->jack_type_saved = rt5670->jack_type;
+ rt5670_headset_detect(codec, 0);
+}
+EXPORT_SYMBOL_GPL(rt5670_jack_suspend);
+
+void rt5670_jack_resume(struct snd_soc_codec *codec)
+{
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ if (rt5670->jack_type_saved)
+ rt5670_headset_detect(codec, 1);
+}
+EXPORT_SYMBOL_GPL(rt5670_jack_resume);
+
+static int rt5670_button_detect(struct snd_soc_codec *codec)
+{
+ int btn_type, val;
+
+ val = snd_soc_read(codec, RT5670_IL_CMD);
+ btn_type = val & 0xff80;
+ snd_soc_write(codec, RT5670_IL_CMD, val);
+ if (btn_type != 0) {
+ msleep(20);
+ val = snd_soc_read(codec, RT5670_IL_CMD);
+ snd_soc_write(codec, 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->pdata.jd_mode == 1) /* 2 port */
+ val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0070;
+ else
+ val = snd_soc_read(rt5670->codec, 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->codec, 1);
+ /* for push button and jack out */
+ gpio->debounce_time = 25;
+ break;
+ }
+ btn_type = 0;
+ if (snd_soc_read(rt5670->codec, RT5670_INT_IRQ_ST) & 0x4) {
+ /* button pressed */
+ report = SND_JACK_HEADSET;
+ btn_type = rt5670_button_detect(rt5670->codec);
+ 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->codec->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_update_bits(rt5670->codec, RT5670_INT_IRQ_ST, 0x1, 0x0);
+ rt5670_headset_detect(rt5670->codec, 0);
+ gpio->debounce_time = 150; /* for jack in */
+ break;
+ default:
+ break;
+ }
+
+ return report;
+}
+
+int rt5670_set_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack)
+{
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ rt5670->jack = jack;
+ rt5670->hp_gpio.gpiod_dev = codec->dev;
+ rt5670->hp_gpio.name = "headphone detect";
+ 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(codec->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_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 unsigned int bst_tlv[] = {
+ TLV_DB_RANGE_HEAD(7),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
+};
+
+/* Interface data select */
+static const char * const rt5670_data_select[] = {
+ "Normal", "Swap", "left copy to right", "right copy to left"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_if2_dac_enum, RT5670_DIG_INF1_DATA,
+ RT5670_IF2_DAC_SEL_SFT, rt5670_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5670_if2_adc_enum, RT5670_DIG_INF1_DATA,
+ RT5670_IF2_ADC_SEL_SFT, rt5670_data_select);
+
+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),
+ /* 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_TLV("DAC1 Playback Volume", RT5670_DAC1_DIG_VOL,
+ RT5670_L_VOL_SFT, RT5670_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
+ SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5670_DAC2_DIG_VOL,
+ RT5670_L_VOL_SFT, RT5670_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
+ /* IN1/IN2 Control */
+ SOC_SINGLE_TLV("IN1 Boost Volume", RT5670_CJ_CTRL1,
+ RT5670_BST_SFT1, 8, 0, bst_tlv),
+ SOC_SINGLE_TLV("IN2 Boost Volume", RT5670_IN2,
+ RT5670_BST_SFT1, 8, 0, bst_tlv),
+ /* INL/INR Volume Control */
+ SOC_DOUBLE_TLV("IN Capture Volume", RT5670_INL1_INR1_VOL,
+ RT5670_INL_VOL_SFT, RT5670_INR_VOL_SFT,
+ 31, 1, in_vol_tlv),
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("ADC Capture Switch", RT5670_STO1_ADC_DIG_VOL,
+ RT5670_L_MUTE_SFT, RT5670_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("ADC Capture Volume", RT5670_STO1_ADC_DIG_VOL,
+ RT5670_L_VOL_SFT, RT5670_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
+
+ SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5670_MONO_ADC_DIG_VOL,
+ RT5670_L_VOL_SFT, RT5670_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5670_ADC_BST_VOL1,
+ RT5670_STO1_ADC_L_BST_SFT, RT5670_STO1_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+
+ SOC_DOUBLE_TLV("STO2 ADC Boost Gain Volume", RT5670_ADC_BST_VOL1,
+ RT5670_STO2_ADC_L_BST_SFT, RT5670_STO2_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+
+ SOC_ENUM("ADC IF2 Data Switch", rt5670_if2_adc_enum),
+ SOC_ENUM("DAC IF2 Data Switch", rt5670_if2_dac_enum),
+};
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_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);
+
+ if (idx < 0)
+ dev_err(codec->dev, "Failed to set DMIC clock\n");
+ else
+ snd_soc_update_bits(codec, RT5670_DMIC_CTRL1,
+ RT5670_DMIC_CLK_MASK, idx << RT5670_DMIC_CLK_SFT);
+ return idx;
+}
+
+static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ if (rt5670->sysclk_src == RT5670_SCLK_S_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int is_using_asrc(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ unsigned int reg, shift, val;
+
+ switch (source->shift) {
+ case 0:
+ reg = RT5670_ASRC_3;
+ shift = 0;
+ break;
+ case 1:
+ reg = RT5670_ASRC_3;
+ shift = 4;
+ break;
+ case 2:
+ reg = RT5670_ASRC_5;
+ shift = 12;
+ break;
+ case 3:
+ reg = RT5670_ASRC_2;
+ shift = 0;
+ break;
+ case 8:
+ reg = RT5670_ASRC_2;
+ shift = 4;
+ break;
+ case 9:
+ reg = RT5670_ASRC_2;
+ shift = 8;
+ break;
+ case 10:
+ reg = RT5670_ASRC_2;
+ shift = 12;
+ break;
+ default:
+ return 0;
+ }
+
+ val = (snd_soc_read(codec, reg) >> shift) & 0xf;
+ switch (val) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ return 1;
+ default:
+ return 0;
+ }
+
+}
+
+static int can_use_asrc(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ if (rt5670->sysclk > rt5670->lrck[RT5670_AIF1] * 384)
+ return 1;
+
+ return 0;
+}
+
+
+/**
+ * rt5670_sel_asrc_clk_src - select ASRC clock source for a set of filters
+ * @codec: SoC audio codec device.
+ * @filter_mask: mask of filters.
+ * @clk_src: clock source
+ *
+ * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5670 can
+ * only support standard 32fs or 64fs i2s format, ASRC should be enabled to
+ * support special i2s clock format such as Intel's 100fs(100 * sampling rate).
+ * ASRC function will track i2s clock and generate a corresponding system clock
+ * for codec. This function provides an API to select the clock source for a
+ * set of filters specified by the mask. And the codec driver will turn on ASRC
+ * for these filters if ASRC is selected as their clock source.
+ */
+int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
+ unsigned int filter_mask, unsigned int clk_src)
+{
+ unsigned int asrc2_mask = 0, asrc2_value = 0;
+ unsigned int asrc3_mask = 0, asrc3_value = 0;
+
+ if (clk_src > RT5670_CLK_SEL_SYS3)
+ return -EINVAL;
+
+ if (filter_mask & RT5670_DA_STEREO_FILTER) {
+ asrc2_mask |= RT5670_DA_STO_CLK_SEL_MASK;
+ asrc2_value = (asrc2_value & ~RT5670_DA_STO_CLK_SEL_MASK)
+ | (clk_src << RT5670_DA_STO_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5670_DA_MONO_L_FILTER) {
+ asrc2_mask |= RT5670_DA_MONOL_CLK_SEL_MASK;
+ asrc2_value = (asrc2_value & ~RT5670_DA_MONOL_CLK_SEL_MASK)
+ | (clk_src << RT5670_DA_MONOL_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5670_DA_MONO_R_FILTER) {
+ asrc2_mask |= RT5670_DA_MONOR_CLK_SEL_MASK;
+ asrc2_value = (asrc2_value & ~RT5670_DA_MONOR_CLK_SEL_MASK)
+ | (clk_src << RT5670_DA_MONOR_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5670_AD_STEREO_FILTER) {
+ asrc2_mask |= RT5670_AD_STO1_CLK_SEL_MASK;
+ asrc2_value = (asrc2_value & ~RT5670_AD_STO1_CLK_SEL_MASK)
+ | (clk_src << RT5670_AD_STO1_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5670_AD_MONO_L_FILTER) {
+ asrc3_mask |= RT5670_AD_MONOL_CLK_SEL_MASK;
+ asrc3_value = (asrc3_value & ~RT5670_AD_MONOL_CLK_SEL_MASK)
+ | (clk_src << RT5670_AD_MONOL_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5670_AD_MONO_R_FILTER) {
+ asrc3_mask |= RT5670_AD_MONOR_CLK_SEL_MASK;
+ asrc3_value = (asrc3_value & ~RT5670_AD_MONOR_CLK_SEL_MASK)
+ | (clk_src << RT5670_AD_MONOR_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5670_UP_RATE_FILTER) {
+ asrc3_mask |= RT5670_UP_CLK_SEL_MASK;
+ asrc3_value = (asrc3_value & ~RT5670_UP_CLK_SEL_MASK)
+ | (clk_src << RT5670_UP_CLK_SEL_SFT);
+ }
+
+ if (filter_mask & RT5670_DOWN_RATE_FILTER) {
+ asrc3_mask |= RT5670_DOWN_CLK_SEL_MASK;
+ asrc3_value = (asrc3_value & ~RT5670_DOWN_CLK_SEL_MASK)
+ | (clk_src << RT5670_DOWN_CLK_SEL_SFT);
+ }
+
+ if (asrc2_mask)
+ snd_soc_update_bits(codec, RT5670_ASRC_2,
+ asrc2_mask, asrc2_value);
+
+ if (asrc3_mask)
+ snd_soc_update_bits(codec, RT5670_ASRC_3,
+ asrc3_mask, asrc3_value);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5670_sel_asrc_clk_src);
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5670_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER,
+ RT5670_M_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5670_STO1_ADC_MIXER,
+ RT5670_M_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER,
+ RT5670_M_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5670_STO1_ADC_MIXER,
+ RT5670_M_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_sto2_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO2_ADC_MIXER,
+ RT5670_M_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5670_STO2_ADC_MIXER,
+ RT5670_M_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_sto2_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO2_ADC_MIXER,
+ RT5670_M_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5670_STO2_ADC_MIXER,
+ RT5670_M_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_mono_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5670_MONO_ADC_MIXER,
+ RT5670_M_MONO_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5670_MONO_ADC_MIXER,
+ RT5670_M_MONO_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_mono_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5670_MONO_ADC_MIXER,
+ RT5670_M_MONO_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5670_MONO_ADC_MIXER,
+ RT5670_M_MONO_ADC_R2_SFT, 1, 1),
+};
+
+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),
+};
+
+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),
+};
+
+static const struct snd_kcontrol_new rt5670_sto_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_STO_DAC_MIXER,
+ RT5670_M_DAC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_STO_DAC_MIXER,
+ RT5670_M_DAC_L2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_STO_DAC_MIXER,
+ RT5670_M_DAC_R1_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_sto_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_STO_DAC_MIXER,
+ RT5670_M_DAC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_STO_DAC_MIXER,
+ RT5670_M_DAC_R2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_STO_DAC_MIXER,
+ RT5670_M_DAC_L1_STO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_mono_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_DD_MIXER,
+ RT5670_M_DAC_L1_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_DD_MIXER,
+ RT5670_M_DAC_L2_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_DD_MIXER,
+ RT5670_M_DAC_R2_MONO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_mono_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_DD_MIXER,
+ RT5670_M_DAC_R1_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_DD_MIXER,
+ RT5670_M_DAC_R2_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_DD_MIXER,
+ RT5670_M_DAC_L2_MONO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_dig_l_mix[] = {
+ SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5670_DIG_MIXER,
+ RT5670_M_STO_L_DAC_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_DIG_MIXER,
+ RT5670_M_DAC_L2_DAC_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_DIG_MIXER,
+ RT5670_M_DAC_R2_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_dig_r_mix[] = {
+ SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5670_DIG_MIXER,
+ RT5670_M_STO_R_DAC_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_DIG_MIXER,
+ RT5670_M_DAC_R2_DAC_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_DIG_MIXER,
+ RT5670_M_DAC_L2_DAC_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5670_rec_l_mix[] = {
+ SOC_DAPM_SINGLE("INL Switch", RT5670_REC_L2_MIXER,
+ RT5670_M_IN_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5670_REC_L2_MIXER,
+ RT5670_M_BST2_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5670_REC_L2_MIXER,
+ RT5670_M_BST1_RM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_rec_r_mix[] = {
+ SOC_DAPM_SINGLE("INR Switch", RT5670_REC_R2_MIXER,
+ RT5670_M_IN_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5670_REC_R2_MIXER,
+ RT5670_M_BST2_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5670_REC_R2_MIXER,
+ RT5670_M_BST1_RM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_out_l_mix[] = {
+ SOC_DAPM_SINGLE("BST1 Switch", RT5670_OUT_L1_MIXER,
+ RT5670_M_BST1_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5670_OUT_L1_MIXER,
+ RT5670_M_IN_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_OUT_L1_MIXER,
+ RT5670_M_DAC_L2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_OUT_L1_MIXER,
+ RT5670_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_out_r_mix[] = {
+ SOC_DAPM_SINGLE("BST2 Switch", RT5670_OUT_R1_MIXER,
+ RT5670_M_BST2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5670_OUT_R1_MIXER,
+ RT5670_M_IN_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_OUT_R1_MIXER,
+ RT5670_M_DAC_R2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_OUT_R1_MIXER,
+ RT5670_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_hpo_mix[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5670_HPO_MIXER,
+ RT5670_M_DAC1_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPVOL Switch", RT5670_HPO_MIXER,
+ RT5670_M_HPVOL_HM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_hpvoll_mix[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5670_HPO_MIXER,
+ RT5670_M_DACL1_HML_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5670_HPO_MIXER,
+ RT5670_M_INL1_HML_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_hpvolr_mix[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5670_HPO_MIXER,
+ RT5670_M_DACR1_HMR_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5670_HPO_MIXER,
+ RT5670_M_INR1_HMR_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5670_lout_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_LOUT_MIXER,
+ RT5670_M_DAC_L1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_LOUT_MIXER,
+ RT5670_M_DAC_R1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTMIX L Switch", RT5670_LOUT_MIXER,
+ RT5670_M_OV_L_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTMIX R Switch", RT5670_LOUT_MIXER,
+ RT5670_M_OV_R_LM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new 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);
+
+static const struct snd_kcontrol_new lout_r_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5670_LOUT1,
+ RT5670_R_MUTE_SFT, 1, 1);
+
+/* DAC1 L/R source */ /* MX-29 [9:8] [11:10] */
+static const char * const rt5670_dac1_src[] = {
+ "IF1 DAC", "IF2 DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_dac1l_enum, RT5670_AD_DA_MIXER,
+ RT5670_DAC1_L_SEL_SFT, rt5670_dac1_src);
+
+static const struct snd_kcontrol_new rt5670_dac1l_mux =
+ SOC_DAPM_ENUM("DAC1 L source", rt5670_dac1l_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5670_dac1r_enum, RT5670_AD_DA_MIXER,
+ RT5670_DAC1_R_SEL_SFT, rt5670_dac1_src);
+
+static const struct snd_kcontrol_new rt5670_dac1r_mux =
+ SOC_DAPM_ENUM("DAC1 R source", rt5670_dac1r_enum);
+
+/*DAC2 L/R source*/ /* MX-1B [6:4] [2:0] */
+/* TODO Use SOC_VALUE_ENUM_SINGLE_DECL */
+static const char * const rt5670_dac12_src[] = {
+ "IF1 DAC", "IF2 DAC", "IF3 DAC", "TxDC DAC",
+ "Bass", "VAD_ADC", "IF4 DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_dac2l_enum, RT5670_DAC_CTRL,
+ RT5670_DAC2_L_SEL_SFT, rt5670_dac12_src);
+
+static const struct snd_kcontrol_new rt5670_dac_l2_mux =
+ SOC_DAPM_ENUM("DAC2 L source", rt5670_dac2l_enum);
+
+static const char * const rt5670_dacr2_src[] = {
+ "IF1 DAC", "IF2 DAC", "IF3 DAC", "TxDC DAC", "TxDP ADC", "IF4 DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_dac2r_enum, RT5670_DAC_CTRL,
+ RT5670_DAC2_R_SEL_SFT, rt5670_dacr2_src);
+
+static const struct snd_kcontrol_new rt5670_dac_r2_mux =
+ SOC_DAPM_ENUM("DAC2 R source", rt5670_dac2r_enum);
+
+/*RxDP source*/ /* MX-2D [15:13] */
+static const char * const rt5670_rxdp_src[] = {
+ "IF2 DAC", "IF1 DAC", "STO1 ADC Mixer", "STO2 ADC Mixer",
+ "Mono ADC Mixer L", "Mono ADC Mixer R", "DAC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_rxdp_enum, RT5670_DSP_PATH1,
+ RT5670_RXDP_SEL_SFT, rt5670_rxdp_src);
+
+static const struct snd_kcontrol_new rt5670_rxdp_mux =
+ SOC_DAPM_ENUM("DAC2 L source", rt5670_rxdp_enum);
+
+/* MX-2D [1] [0] */
+static const char * const rt5670_dsp_bypass_src[] = {
+ "DSP", "Bypass"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_dsp_ul_enum, RT5670_DSP_PATH1,
+ RT5670_DSP_UL_SFT, rt5670_dsp_bypass_src);
+
+static const struct snd_kcontrol_new rt5670_dsp_ul_mux =
+ SOC_DAPM_ENUM("DSP UL source", rt5670_dsp_ul_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5670_dsp_dl_enum, RT5670_DSP_PATH1,
+ RT5670_DSP_DL_SFT, rt5670_dsp_bypass_src);
+
+static const struct snd_kcontrol_new rt5670_dsp_dl_mux =
+ SOC_DAPM_ENUM("DSP DL source", rt5670_dsp_dl_enum);
+
+/* Stereo2 ADC source */
+/* MX-26 [15] */
+static const char * const rt5670_stereo2_adc_lr_src[] = {
+ "L", "LR"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_adc_lr_enum, RT5670_STO2_ADC_MIXER,
+ RT5670_STO2_ADC_SRC_SFT, rt5670_stereo2_adc_lr_src);
+
+static const struct snd_kcontrol_new rt5670_sto2_adc_lr_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC LR source", rt5670_stereo2_adc_lr_enum);
+
+/* Stereo1 ADC source */
+/* MX-27 MX-26 [12] */
+static const char * const rt5670_stereo_adc1_src[] = {
+ "DAC MIX", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_stereo1_adc1_enum, RT5670_STO1_ADC_MIXER,
+ RT5670_ADC_1_SRC_SFT, rt5670_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5670_sto_adc_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 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_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[] = {
+ "DAC MIX", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_stereo1_adc2_enum, RT5670_STO1_ADC_MIXER,
+ RT5670_ADC_2_SRC_SFT, rt5670_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5670_sto_adc_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 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);
+
+/* MX-27 MX-26 [9:8] */
+static const char * const rt5670_stereo_dmic_src[] = {
+ "DMIC1", "DMIC2", "DMIC3"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_stereo1_dmic_enum, RT5670_STO1_ADC_MIXER,
+ RT5670_DMIC_SRC_SFT, rt5670_stereo_dmic_src);
+
+static const struct snd_kcontrol_new rt5670_sto1_dmic_mux =
+ SOC_DAPM_ENUM("Stereo1 DMIC source", rt5670_stereo1_dmic_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_dmic_enum, RT5670_STO2_ADC_MIXER,
+ RT5670_DMIC_SRC_SFT, rt5670_stereo_dmic_src);
+
+static const struct snd_kcontrol_new rt5670_sto2_dmic_mux =
+ SOC_DAPM_ENUM("Stereo2 DMIC source", rt5670_stereo2_dmic_enum);
+
+/* 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[] = {
+ "Mono DAC MIXL", "ADC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_mono_adc_l1_enum, RT5670_MONO_ADC_MIXER,
+ RT5670_MONO_ADC_L1_SRC_SFT, rt5670_mono_adc_l1_src);
+
+static const struct snd_kcontrol_new rt5670_mono_adc_l1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 left source", rt5670_mono_adc_l1_enum);
+/* MX-28 [11] */
+static const char * const rt5670_mono_adc_l2_src[] = {
+ "Mono DAC MIXL", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_mono_adc_l2_enum, RT5670_MONO_ADC_MIXER,
+ RT5670_MONO_ADC_L2_SRC_SFT, rt5670_mono_adc_l2_src);
+
+static const struct snd_kcontrol_new rt5670_mono_adc_l2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 left source", rt5670_mono_adc_l2_enum);
+
+/* MX-28 [9:8] */
+static const char * const rt5670_mono_dmic_src[] = {
+ "DMIC1", "DMIC2", "DMIC3"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_mono_dmic_l_enum, RT5670_MONO_ADC_MIXER,
+ RT5670_MONO_DMIC_L_SRC_SFT, rt5670_mono_dmic_src);
+
+static const struct snd_kcontrol_new rt5670_mono_dmic_l_mux =
+ SOC_DAPM_ENUM("Mono DMIC left source", rt5670_mono_dmic_l_enum);
+/* MX-28 [1:0] */
+static SOC_ENUM_SINGLE_DECL(rt5670_mono_dmic_r_enum, RT5670_MONO_ADC_MIXER,
+ RT5670_MONO_DMIC_R_SRC_SFT, rt5670_mono_dmic_src);
+
+static const struct snd_kcontrol_new rt5670_mono_dmic_r_mux =
+ SOC_DAPM_ENUM("Mono DMIC Right source", rt5670_mono_dmic_r_enum);
+/* MX-28 [4] */
+static const char * const rt5670_mono_adc_r1_src[] = {
+ "Mono DAC MIXR", "ADC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_mono_adc_r1_enum, RT5670_MONO_ADC_MIXER,
+ RT5670_MONO_ADC_R1_SRC_SFT, rt5670_mono_adc_r1_src);
+
+static const struct snd_kcontrol_new rt5670_mono_adc_r1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 right source", rt5670_mono_adc_r1_enum);
+/* MX-28 [3] */
+static const char * const rt5670_mono_adc_r2_src[] = {
+ "Mono DAC MIXR", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_mono_adc_r2_enum, RT5670_MONO_ADC_MIXER,
+ RT5670_MONO_ADC_R2_SRC_SFT, rt5670_mono_adc_r2_src);
+
+static const struct snd_kcontrol_new rt5670_mono_adc_r2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 right source", rt5670_mono_adc_r2_enum);
+
+/* MX-2D [3:2] */
+static const char * const rt5670_txdp_slot_src[] = {
+ "Slot 0-1", "Slot 2-3", "Slot 4-5", "Slot 6-7"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_txdp_slot_enum, RT5670_DSP_PATH1,
+ RT5670_TXDP_SLOT_SEL_SFT, rt5670_txdp_slot_src);
+
+static const struct snd_kcontrol_new rt5670_txdp_slot_mux =
+ SOC_DAPM_ENUM("TxDP Slot source", rt5670_txdp_slot_enum);
+
+/* MX-2F [15] */
+static const char * const rt5670_if1_adc2_in_src[] = {
+ "IF_ADC2", "VAD_ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_if1_adc2_in_enum, RT5670_DIG_INF1_DATA,
+ RT5670_IF1_ADC2_IN_SFT, rt5670_if1_adc2_in_src);
+
+static const struct snd_kcontrol_new rt5670_if1_adc2_in_mux =
+ SOC_DAPM_ENUM("IF1 ADC2 IN source", rt5670_if1_adc2_in_enum);
+
+/* MX-2F [14:12] */
+static const char * const rt5670_if2_adc_in_src[] = {
+ "IF_ADC1", "IF_ADC2", "IF_ADC3", "TxDC_DAC", "TxDP_ADC", "VAD_ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_if2_adc_in_enum, RT5670_DIG_INF1_DATA,
+ RT5670_IF2_ADC_IN_SFT, rt5670_if2_adc_in_src);
+
+static const struct snd_kcontrol_new rt5670_if2_adc_in_mux =
+ SOC_DAPM_ENUM("IF2 ADC IN source", rt5670_if2_adc_in_enum);
+
+/* MX-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"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_pdm1_l_enum, RT5670_PDM_OUT_CTRL,
+ RT5670_PDM1_L_SFT, rt5670_pdm_src);
+
+static const struct snd_kcontrol_new rt5670_pdm1_l_mux =
+ SOC_DAPM_ENUM("PDM1 L source", rt5670_pdm1_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5670_pdm1_r_enum, RT5670_PDM_OUT_CTRL,
+ RT5670_PDM1_R_SFT, rt5670_pdm_src);
+
+static const struct snd_kcontrol_new rt5670_pdm1_r_mux =
+ SOC_DAPM_ENUM("PDM1 R source", rt5670_pdm1_r_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5670_pdm2_l_enum, RT5670_PDM_OUT_CTRL,
+ RT5670_PDM2_L_SFT, rt5670_pdm_src);
+
+static const struct snd_kcontrol_new rt5670_pdm2_l_mux =
+ SOC_DAPM_ENUM("PDM2 L source", rt5670_pdm2_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5670_pdm2_r_enum, RT5670_PDM_OUT_CTRL,
+ RT5670_PDM2_R_SFT, rt5670_pdm_src);
+
+static const struct snd_kcontrol_new rt5670_pdm2_r_mux =
+ SOC_DAPM_ENUM("PDM2 R source", rt5670_pdm2_r_enum);
+
+/* MX-FA [12] */
+static const char * const rt5670_if1_adc1_in1_src[] = {
+ "IF_ADC1", "IF1_ADC3"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_if1_adc1_in1_enum, RT5670_DIG_MISC,
+ RT5670_IF1_ADC1_IN1_SFT, rt5670_if1_adc1_in1_src);
+
+static const struct snd_kcontrol_new rt5670_if1_adc1_in1_mux =
+ SOC_DAPM_ENUM("IF1 ADC1 IN1 source", rt5670_if1_adc1_in1_enum);
+
+/* MX-FA [11] */
+static const char * const rt5670_if1_adc1_in2_src[] = {
+ "IF1_ADC1_IN1", "IF1_ADC4"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_if1_adc1_in2_enum, RT5670_DIG_MISC,
+ RT5670_IF1_ADC1_IN2_SFT, rt5670_if1_adc1_in2_src);
+
+static const struct snd_kcontrol_new rt5670_if1_adc1_in2_mux =
+ SOC_DAPM_ENUM("IF1 ADC1 IN2 source", rt5670_if1_adc1_in2_enum);
+
+/* MX-FA [10] */
+static const char * const rt5670_if1_adc2_in1_src[] = {
+ "IF1_ADC2_IN", "IF1_ADC4"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_if1_adc2_in1_enum, RT5670_DIG_MISC,
+ RT5670_IF1_ADC2_IN1_SFT, rt5670_if1_adc2_in1_src);
+
+static const struct snd_kcontrol_new rt5670_if1_adc2_in1_mux =
+ SOC_DAPM_ENUM("IF1 ADC2 IN1 source", rt5670_if1_adc2_in1_enum);
+
+/* MX-9D [9:8] */
+static const char * const rt5670_vad_adc_src[] = {
+ "Sto1 ADC L", "Mono ADC L", "Mono ADC R", "Sto2 ADC L"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5670_vad_adc_enum, RT5670_VAD_CTRL4,
+ RT5670_VAD_SEL_SFT, rt5670_vad_adc_src);
+
+static const struct snd_kcontrol_new rt5670_vad_adc_mux =
+ SOC_DAPM_ENUM("VAD ADC source", rt5670_vad_adc_enum);
+
+static int rt5670_hp_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(rt5670->regmap, RT5670_CHARGE_PUMP,
+ RT5670_PM_HP_MASK, RT5670_PM_HP_HV);
+ regmap_update_bits(rt5670->regmap, RT5670_GEN_CTRL2,
+ 0x0400, 0x0400);
+ /* headphone amp power on */
+ regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG1,
+ RT5670_PWR_HA | RT5670_PWR_FV1 |
+ RT5670_PWR_FV2, RT5670_PWR_HA |
+ RT5670_PWR_FV1 | RT5670_PWR_FV2);
+ /* depop parameters */
+ regmap_write(rt5670->regmap, RT5670_DEPOP_M2, 0x3100);
+ regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x8009);
+ regmap_write(rt5670->regmap, RT5670_PR_BASE +
+ RT5670_HP_DCC_INT1, 0x9f00);
+ mdelay(20);
+ regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x8019);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x0004);
+ msleep(30);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5670_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* headphone unmute sequence */
+ regmap_write(rt5670->regmap, RT5670_PR_BASE +
+ RT5670_MAMP_INT_REG2, 0xb400);
+ regmap_write(rt5670->regmap, RT5670_DEPOP_M3, 0x0772);
+ regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x805d);
+ regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x831d);
+ regmap_update_bits(rt5670->regmap, RT5670_GEN_CTRL2,
+ 0x0300, 0x0300);
+ regmap_update_bits(rt5670->regmap, RT5670_HP_VOL,
+ RT5670_L_MUTE | RT5670_R_MUTE, 0);
+ msleep(80);
+ regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x8019);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ /* headphone mute sequence */
+ regmap_write(rt5670->regmap, RT5670_PR_BASE +
+ RT5670_MAMP_INT_REG2, 0xb400);
+ regmap_write(rt5670->regmap, RT5670_DEPOP_M3, 0x0772);
+ regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x803d);
+ mdelay(10);
+ regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x831d);
+ mdelay(10);
+ regmap_update_bits(rt5670->regmap, RT5670_HP_VOL,
+ RT5670_L_MUTE | RT5670_R_MUTE,
+ RT5670_L_MUTE | RT5670_R_MUTE);
+ msleep(20);
+ regmap_update_bits(rt5670->regmap,
+ RT5670_GEN_CTRL2, 0x0300, 0x0);
+ regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x8019);
+ regmap_write(rt5670->regmap, RT5670_DEPOP_M3, 0x0707);
+ regmap_write(rt5670->regmap, RT5670_PR_BASE +
+ RT5670_MAMP_INT_REG2, 0xfc00);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5670_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);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, 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,
+ RT5670_PWR_BST1_P, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5670_bst2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, 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,
+ RT5670_PWR_BST2_P, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5670_PWR_ANLG2,
+ RT5670_PWR_PLL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S DSP", RT5670_PWR_DIG2,
+ RT5670_PWR_I2S_DSP_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5670_PWR_VOL,
+ RT5670_PWR_MIC_DET_BIT, 0, NULL, 0),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5670_ASRC_1,
+ 11, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5670_ASRC_1,
+ 12, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5670_ASRC_1,
+ 10, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC MONO L ASRC", 1, RT5670_ASRC_1,
+ 9, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5670_ASRC_1,
+ 8, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5670_ASRC_1,
+ 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC STO2 ASRC", 1, RT5670_ASRC_1,
+ 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5670_ASRC_1,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5670_ASRC_1,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5670_ASRC_1,
+ 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5670_ASRC_1,
+ 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5670_ASRC_1,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5670_ASRC_1,
+ 0, 0, NULL, 0),
+
+ /* Input Side */
+ /* micbias */
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5670_PWR_ANLG2,
+ RT5670_PWR_MB1_BIT, 0, NULL, 0),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC L1"),
+ SND_SOC_DAPM_INPUT("DMIC R1"),
+ SND_SOC_DAPM_INPUT("DMIC L2"),
+ SND_SOC_DAPM_INPUT("DMIC R2"),
+ SND_SOC_DAPM_INPUT("DMIC L3"),
+ SND_SOC_DAPM_INPUT("DMIC R3"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+ SND_SOC_DAPM_INPUT("IN1N"),
+ SND_SOC_DAPM_INPUT("IN2P"),
+ SND_SOC_DAPM_INPUT("IN2N"),
+
+ SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5670_DMIC_CTRL1,
+ RT5670_DMIC_1_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5670_DMIC_CTRL1,
+ RT5670_DMIC_2_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC3 Power", RT5670_DMIC_CTRL1,
+ RT5670_DMIC_3_EN_SFT, 0, NULL, 0),
+ /* Boost */
+ SND_SOC_DAPM_PGA_E("BST1", RT5670_PWR_ANLG2, RT5670_PWR_BST1_BIT,
+ 0, NULL, 0, rt5670_bst1_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("BST2", RT5670_PWR_ANLG2, RT5670_PWR_BST2_BIT,
+ 0, NULL, 0, rt5670_bst2_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ /* Input Volume */
+ SND_SOC_DAPM_PGA("INL VOL", RT5670_PWR_VOL,
+ RT5670_PWR_IN_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR VOL", RT5670_PWR_VOL,
+ RT5670_PWR_IN_R_BIT, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIXL", RT5670_PWR_MIXER, RT5670_PWR_RM_L_BIT, 0,
+ rt5670_rec_l_mix, ARRAY_SIZE(rt5670_rec_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIXR", RT5670_PWR_MIXER, RT5670_PWR_RM_R_BIT, 0,
+ rt5670_rec_r_mix, ARRAY_SIZE(rt5670_rec_r_mix)),
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC 2", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_PGA("ADC 1_2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC 1 power", RT5670_PWR_DIG1,
+ RT5670_PWR_ADC_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC 2 power", RT5670_PWR_DIG1,
+ RT5670_PWR_ADC_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC clock", RT5670_PR_BASE +
+ RT5670_CHOP_DAC_ADC, 12, 0, NULL, 0),
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_sto1_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_sto_adc_l2_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_sto_adc_r2_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_sto_adc_l1_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_sto_adc_r1_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),
+ SND_SOC_DAPM_MUX("Stereo2 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_sto2_adc_r2_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_sto2_adc_l1_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_sto2_adc_r1_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC LR Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_sto2_adc_lr_mux),
+ SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_mono_dmic_l_mux),
+ SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_mono_dmic_r_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_mono_adc_l2_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_mono_adc_l1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_mono_adc_r1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_mono_adc_r2_mux),
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5670_PWR_DIG2,
+ RT5670_PWR_ADC_S1F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Stereo2 Filter", RT5670_PWR_DIG2,
+ RT5670_PWR_ADC_S2F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", 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("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5670_sto2_adc_l_mix,
+ ARRAY_SIZE(rt5670_sto2_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5670_sto2_adc_r_mix,
+ ARRAY_SIZE(rt5670_sto2_adc_r_mix)),
+ SND_SOC_DAPM_SUPPLY("ADC Mono Left Filter", RT5670_PWR_DIG2,
+ RT5670_PWR_ADC_MF_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXL", RT5670_MONO_ADC_DIG_VOL,
+ RT5670_L_MUTE_SFT, 1, rt5670_mono_adc_l_mix,
+ ARRAY_SIZE(rt5670_mono_adc_l_mix)),
+ SND_SOC_DAPM_SUPPLY("ADC Mono Right Filter", RT5670_PWR_DIG2,
+ RT5670_PWR_ADC_MF_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXR", RT5670_MONO_ADC_DIG_VOL,
+ RT5670_R_MUTE_SFT, 1, rt5670_mono_adc_r_mix,
+ ARRAY_SIZE(rt5670_mono_adc_r_mix)),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo2 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo2 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("VAD_ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ 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),
+ SND_SOC_DAPM_PGA("TxDP_ADC_L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("TxDP_ADC_R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("TxDC_DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("TDM Data Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_txdp_slot_mux),
+
+ SND_SOC_DAPM_MUX("DSP UL Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_dsp_ul_mux),
+ SND_SOC_DAPM_MUX("DSP DL Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_dsp_dl_mux),
+
+ SND_SOC_DAPM_MUX("RxDP Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_rxdp_mux),
+
+ /* IF2 Mux */
+ SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_if2_adc_in_mux),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5670_PWR_DIG1,
+ RT5670_PWR_I2S1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S2", RT5670_PWR_DIG1,
+ RT5670_PWR_I2S2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("IF1 ADC1 IN1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_if1_adc1_in1_mux),
+ SND_SOC_DAPM_MUX("IF1 ADC1 IN2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_if1_adc1_in2_mux),
+ SND_SOC_DAPM_MUX("IF1 ADC2 IN Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_if1_adc2_in_mux),
+ SND_SOC_DAPM_MUX("IF1 ADC2 IN1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_if1_adc2_in1_mux),
+ SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_vad_adc_mux),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0,
+ RT5670_GPIO_CTRL1, RT5670_I2S2_PIN_SFT, 1),
+
+ /* Audio DSP */
+ SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
+ rt5670_dac_l_mix, ARRAY_SIZE(rt5670_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5670_dac_r_mix, ARRAY_SIZE(rt5670_dac_r_mix)),
+ SND_SOC_DAPM_PGA("DAC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DAC2 channel Mux */
+ SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_dac_l2_mux),
+ SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5670_dac_r2_mux),
+ SND_SOC_DAPM_PGA("DAC L2 Volume", RT5670_PWR_DIG1,
+ RT5670_PWR_DAC_L2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC R2 Volume", RT5670_PWR_DIG1,
+ RT5670_PWR_DAC_R2_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DAC1 L Mux", SND_SOC_NOPM, 0, 0, &rt5670_dac1l_mux),
+ SND_SOC_DAPM_MUX("DAC1 R Mux", SND_SOC_NOPM, 0, 0, &rt5670_dac1r_mux),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5670_PWR_DIG2,
+ RT5670_PWR_DAC_S1F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Mono Left Filter", RT5670_PWR_DIG2,
+ RT5670_PWR_DAC_MF_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Mono Right Filter", RT5670_PWR_DIG2,
+ RT5670_PWR_DAC_MF_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5670_sto_dac_l_mix,
+ ARRAY_SIZE(rt5670_sto_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5670_sto_dac_r_mix,
+ ARRAY_SIZE(rt5670_sto_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5670_mono_dac_l_mix,
+ ARRAY_SIZE(rt5670_mono_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5670_mono_dac_r_mix,
+ ARRAY_SIZE(rt5670_mono_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5670_dig_l_mix,
+ ARRAY_SIZE(rt5670_dig_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5670_dig_r_mix,
+ ARRAY_SIZE(rt5670_dig_r_mix)),
+
+ /* DACs */
+ SND_SOC_DAPM_SUPPLY("DAC L1 Power", RT5670_PWR_DIG1,
+ RT5670_PWR_DAC_L1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5670_PWR_DIG1,
+ RT5670_PWR_DAC_R1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC L2", NULL, RT5670_PWR_DIG1,
+ RT5670_PWR_DAC_L2_BIT, 0),
+
+ SND_SOC_DAPM_DAC("DAC R2", NULL, RT5670_PWR_DIG1,
+ RT5670_PWR_DAC_R2_BIT, 0),
+ /* OUT Mixer */
+
+ SND_SOC_DAPM_MIXER("OUT MIXL", RT5670_PWR_MIXER, RT5670_PWR_OM_L_BIT,
+ 0, rt5670_out_l_mix, ARRAY_SIZE(rt5670_out_l_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXR", RT5670_PWR_MIXER, RT5670_PWR_OM_R_BIT,
+ 0, rt5670_out_r_mix, ARRAY_SIZE(rt5670_out_r_mix)),
+ /* Ouput Volume */
+ SND_SOC_DAPM_MIXER("HPOVOL MIXL", RT5670_PWR_VOL,
+ RT5670_PWR_HV_L_BIT, 0,
+ rt5670_hpvoll_mix, ARRAY_SIZE(rt5670_hpvoll_mix)),
+ SND_SOC_DAPM_MIXER("HPOVOL MIXR", RT5670_PWR_VOL,
+ RT5670_PWR_HV_R_BIT, 0,
+ rt5670_hpvolr_mix, ARRAY_SIZE(rt5670_hpvolr_mix)),
+ SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* HPO/LOUT/Mono Mixer */
+ SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0,
+ rt5670_hpo_mix, ARRAY_SIZE(rt5670_hpo_mix)),
+ SND_SOC_DAPM_MIXER("LOUT MIX", RT5670_PWR_ANLG1, RT5670_PWR_LM_BIT,
+ 0, rt5670_lout_mix, ARRAY_SIZE(rt5670_lout_mix)),
+ SND_SOC_DAPM_SUPPLY_S("Improve HP Amp Drv", 1, SND_SOC_NOPM, 0, 0,
+ rt5670_hp_power_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("HP L Amp", RT5670_PWR_ANLG1,
+ RT5670_PWR_HP_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HP R Amp", RT5670_PWR_ANLG1,
+ RT5670_PWR_HP_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0,
+ rt5670_hp_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SWITCH("LOUT L Playback", SND_SOC_NOPM, 0, 0,
+ &lout_l_enable_control),
+ SND_SOC_DAPM_SWITCH("LOUT R Playback", SND_SOC_NOPM, 0, 0,
+ &lout_r_enable_control),
+ SND_SOC_DAPM_PGA("LOUT Amp", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* PDM */
+ SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5670_PWR_DIG2,
+ RT5670_PWR_PDM1_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("PDM1 L Mux", RT5670_PDM_OUT_CTRL,
+ RT5670_M_PDM1_L_SFT, 1, &rt5670_pdm1_l_mux),
+ SND_SOC_DAPM_MUX("PDM1 R Mux", RT5670_PDM_OUT_CTRL,
+ RT5670_M_PDM1_R_SFT, 1, &rt5670_pdm1_r_mux),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("LOUTL"),
+ SND_SOC_DAPM_OUTPUT("LOUTR"),
+};
+
+static const struct snd_soc_dapm_widget rt5670_specific_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5670_PWR_DIG2,
+ RT5670_PWR_PDM2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MUX("PDM2 L Mux", RT5670_PDM_OUT_CTRL,
+ RT5670_M_PDM2_L_SFT, 1, &rt5670_pdm2_l_mux),
+ SND_SOC_DAPM_MUX("PDM2 R Mux", RT5670_PDM_OUT_CTRL,
+ RT5670_M_PDM2_R_SFT, 1, &rt5670_pdm2_r_mux),
+ SND_SOC_DAPM_OUTPUT("PDM1L"),
+ SND_SOC_DAPM_OUTPUT("PDM1R"),
+ SND_SOC_DAPM_OUTPUT("PDM2L"),
+ SND_SOC_DAPM_OUTPUT("PDM2R"),
+};
+
+static const struct snd_soc_dapm_widget rt5672_specific_dapm_widgets[] = {
+ SND_SOC_DAPM_PGA("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPOLP"),
+ SND_SOC_DAPM_OUTPUT("SPOLN"),
+ SND_SOC_DAPM_OUTPUT("SPORP"),
+ SND_SOC_DAPM_OUTPUT("SPORN"),
+};
+
+static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
+ { "ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc },
+ { "ADC Stereo2 Filter", NULL, "ADC STO2 ASRC", is_using_asrc },
+ { "ADC Mono Left Filter", NULL, "ADC MONO L ASRC", is_using_asrc },
+ { "ADC Mono Right Filter", NULL, "ADC MONO R ASRC", is_using_asrc },
+ { "DAC Mono Left Filter", NULL, "DAC MONO L ASRC", is_using_asrc },
+ { "DAC Mono Right Filter", NULL, "DAC MONO R ASRC", is_using_asrc },
+ { "DAC Stereo1 Filter", NULL, "DAC STO ASRC", is_using_asrc },
+ { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", can_use_asrc },
+ { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", can_use_asrc },
+ { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", can_use_asrc },
+ { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", can_use_asrc },
+
+ { "I2S1", NULL, "I2S1 ASRC", can_use_asrc},
+ { "I2S2", NULL, "I2S2 ASRC", can_use_asrc},
+
+ { "DMIC1", NULL, "DMIC L1" },
+ { "DMIC1", NULL, "DMIC R1" },
+ { "DMIC2", NULL, "DMIC L2" },
+ { "DMIC2", NULL, "DMIC R2" },
+ { "DMIC3", NULL, "DMIC L3" },
+ { "DMIC3", NULL, "DMIC R3" },
+
+ { "BST1", NULL, "IN1P" },
+ { "BST1", NULL, "IN1N" },
+ { "BST1", NULL, "Mic Det Power" },
+ { "BST2", NULL, "IN2P" },
+ { "BST2", NULL, "IN2N" },
+
+ { "INL VOL", NULL, "IN2P" },
+ { "INR VOL", NULL, "IN2N" },
+
+ { "RECMIXL", "INL Switch", "INL VOL" },
+ { "RECMIXL", "BST2 Switch", "BST2" },
+ { "RECMIXL", "BST1 Switch", "BST1" },
+
+ { "RECMIXR", "INR Switch", "INR VOL" },
+ { "RECMIXR", "BST2 Switch", "BST2" },
+ { "RECMIXR", "BST1 Switch", "BST1" },
+
+ { "ADC 1", NULL, "RECMIXL" },
+ { "ADC 1", NULL, "ADC 1 power" },
+ { "ADC 1", NULL, "ADC clock" },
+ { "ADC 2", NULL, "RECMIXR" },
+ { "ADC 2", NULL, "ADC 2 power" },
+ { "ADC 2", NULL, "ADC clock" },
+
+ { "DMIC L1", NULL, "DMIC CLK" },
+ { "DMIC L1", NULL, "DMIC1 Power" },
+ { "DMIC R1", NULL, "DMIC CLK" },
+ { "DMIC R1", NULL, "DMIC1 Power" },
+ { "DMIC L2", NULL, "DMIC CLK" },
+ { "DMIC L2", NULL, "DMIC2 Power" },
+ { "DMIC R2", NULL, "DMIC CLK" },
+ { "DMIC R2", NULL, "DMIC2 Power" },
+ { "DMIC L3", NULL, "DMIC CLK" },
+ { "DMIC L3", NULL, "DMIC3 Power" },
+ { "DMIC R3", NULL, "DMIC CLK" },
+ { "DMIC R3", NULL, "DMIC3 Power" },
+
+ { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
+ { "Stereo1 DMIC Mux", "DMIC3", "DMIC3" },
+
+ { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" },
+ { "Stereo2 DMIC Mux", "DMIC3", "DMIC3" },
+
+ { "Mono DMIC L Mux", "DMIC1", "DMIC L1" },
+ { "Mono DMIC L Mux", "DMIC2", "DMIC L2" },
+ { "Mono DMIC L Mux", "DMIC3", "DMIC L3" },
+
+ { "Mono DMIC R Mux", "DMIC1", "DMIC R1" },
+ { "Mono DMIC R Mux", "DMIC2", "DMIC R2" },
+ { "Mono DMIC R Mux", "DMIC3", "DMIC R3" },
+
+ { "ADC 1_2", NULL, "ADC 1" },
+ { "ADC 1_2", NULL, "ADC 2" },
+
+ { "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC Mux" },
+ { "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" },
+ { "Stereo1 ADC L1 Mux", "ADC", "ADC 1_2" },
+ { "Stereo1 ADC L1 Mux", "DAC MIX", "DAC MIXL" },
+
+ { "Stereo1 ADC R1 Mux", "ADC", "ADC 1_2" },
+ { "Stereo1 ADC R1 Mux", "DAC MIX", "DAC MIXR" },
+ { "Stereo1 ADC R2 Mux", "DMIC", "Stereo1 DMIC Mux" },
+ { "Stereo1 ADC R2 Mux", "DAC MIX", "DAC MIXR" },
+
+ { "Mono ADC L2 Mux", "DMIC", "Mono DMIC L Mux" },
+ { "Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL" },
+ { "Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL" },
+ { "Mono ADC L1 Mux", "ADC1", "ADC 1" },
+
+ { "Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR" },
+ { "Mono ADC R1 Mux", "ADC2", "ADC 2" },
+ { "Mono ADC R2 Mux", "DMIC", "Mono DMIC R Mux" },
+ { "Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR" },
+
+ { "Sto1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux" },
+ { "Sto1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux" },
+ { "Sto1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux" },
+ { "Sto1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux" },
+
+ { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
+ { "Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter" },
+ { "ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
+ { "Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter" },
+ { "ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux" },
+ { "Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux" },
+ { "Mono ADC MIXL", NULL, "ADC Mono Left Filter" },
+ { "ADC Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux" },
+ { "Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux" },
+ { "Mono ADC MIXR", NULL, "ADC Mono Right Filter" },
+ { "ADC Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Stereo2 ADC L2 Mux", "DMIC", "Stereo2 DMIC Mux" },
+ { "Stereo2 ADC L2 Mux", "DAC MIX", "DAC MIXL" },
+ { "Stereo2 ADC L1 Mux", "ADC", "ADC 1_2" },
+ { "Stereo2 ADC L1 Mux", "DAC MIX", "DAC MIXL" },
+
+ { "Stereo2 ADC R1 Mux", "ADC", "ADC 1_2" },
+ { "Stereo2 ADC R1 Mux", "DAC MIX", "DAC MIXR" },
+ { "Stereo2 ADC R2 Mux", "DMIC", "Stereo2 DMIC Mux" },
+ { "Stereo2 ADC R2 Mux", "DAC MIX", "DAC MIXR" },
+
+ { "Sto2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC L1 Mux" },
+ { "Sto2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC L2 Mux" },
+ { "Sto2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC R1 Mux" },
+ { "Sto2 ADC MIXR", "ADC2 Switch", "Stereo2 ADC R2 Mux" },
+
+ { "Sto2 ADC LR MIX", NULL, "Sto2 ADC MIXL" },
+ { "Sto2 ADC LR MIX", NULL, "Sto2 ADC MIXR" },
+
+ { "Stereo2 ADC LR Mux", "L", "Sto2 ADC MIXL" },
+ { "Stereo2 ADC LR Mux", "LR", "Sto2 ADC LR MIX" },
+
+ { "Stereo2 ADC MIXL", NULL, "Stereo2 ADC LR Mux" },
+ { "Stereo2 ADC MIXL", NULL, "ADC Stereo2 Filter" },
+ { "ADC Stereo2 Filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" },
+ { "Stereo2 ADC MIXR", NULL, "ADC Stereo2 Filter" },
+ { "ADC Stereo2 Filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "VAD ADC Mux", "Sto1 ADC L", "Stereo1 ADC MIXL" },
+ { "VAD ADC Mux", "Mono ADC L", "Mono ADC MIXL" },
+ { "VAD ADC Mux", "Mono ADC R", "Mono ADC MIXR" },
+ { "VAD ADC Mux", "Sto2 ADC L", "Sto2 ADC MIXL" },
+
+ { "VAD_ADC", NULL, "VAD ADC Mux" },
+
+ { "IF_ADC1", NULL, "Stereo1 ADC MIXL" },
+ { "IF_ADC1", NULL, "Stereo1 ADC MIXR" },
+ { "IF_ADC2", NULL, "Mono ADC MIXL" },
+ { "IF_ADC2", NULL, "Mono ADC MIXR" },
+ { "IF_ADC3", NULL, "Stereo2 ADC MIXL" },
+ { "IF_ADC3", NULL, "Stereo2 ADC MIXR" },
+
+ { "IF1 ADC1 IN1 Mux", "IF_ADC1", "IF_ADC1" },
+ { "IF1 ADC1 IN1 Mux", "IF1_ADC3", "IF1_ADC3" },
+
+ { "IF1 ADC1 IN2 Mux", "IF1_ADC1_IN1", "IF1 ADC1 IN1 Mux" },
+ { "IF1 ADC1 IN2 Mux", "IF1_ADC4", "IF1_ADC4" },
+
+ { "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_ADC1" , NULL, "IF1 ADC1 IN2 Mux" },
+ { "IF1_ADC2" , NULL, "IF1 ADC2 IN1 Mux" },
+
+ { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" },
+ { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" },
+ { "Stereo2 ADC MIX", NULL, "Sto2 ADC MIXL" },
+ { "Stereo2 ADC MIX", NULL, "Sto2 ADC MIXR" },
+ { "Mono ADC MIX", NULL, "Mono ADC MIXL" },
+ { "Mono ADC MIX", NULL, "Mono ADC MIXR" },
+
+ { "RxDP Mux", "IF2 DAC", "IF2 DAC" },
+ { "RxDP Mux", "IF1 DAC", "IF1 DAC2" },
+ { "RxDP Mux", "STO1 ADC Mixer", "Stereo1 ADC MIX" },
+ { "RxDP Mux", "STO2 ADC Mixer", "Stereo2 ADC MIX" },
+ { "RxDP Mux", "Mono ADC Mixer L", "Mono ADC MIXL" },
+ { "RxDP Mux", "Mono ADC Mixer R", "Mono ADC MIXR" },
+ { "RxDP Mux", "DAC1", "DAC MIX" },
+
+ { "TDM Data Mux", "Slot 0-1", "Stereo1 ADC MIX" },
+ { "TDM Data Mux", "Slot 2-3", "Mono ADC MIX" },
+ { "TDM Data Mux", "Slot 4-5", "Stereo2 ADC MIX" },
+ { "TDM Data Mux", "Slot 6-7", "IF2 DAC" },
+
+ { "DSP UL Mux", "Bypass", "TDM Data Mux" },
+ { "DSP UL Mux", NULL, "I2S DSP" },
+ { "DSP DL Mux", "Bypass", "RxDP Mux" },
+ { "DSP DL Mux", NULL, "I2S DSP" },
+
+ { "TxDP_ADC_L", NULL, "DSP UL Mux" },
+ { "TxDP_ADC_R", NULL, "DSP UL Mux" },
+ { "TxDC_DAC", NULL, "DSP DL Mux" },
+
+ { "TxDP_ADC", NULL, "TxDP_ADC_L" },
+ { "TxDP_ADC", NULL, "TxDP_ADC_R" },
+
+ { "IF1 ADC", NULL, "I2S1" },
+ { "IF1 ADC", NULL, "IF1_ADC1" },
+ { "IF1 ADC", NULL, "IF1_ADC2" },
+ { "IF1 ADC", NULL, "IF_ADC3" },
+ { "IF1 ADC", NULL, "TxDP_ADC" },
+
+ { "IF2 ADC Mux", "IF_ADC1", "IF_ADC1" },
+ { "IF2 ADC Mux", "IF_ADC2", "IF_ADC2" },
+ { "IF2 ADC Mux", "IF_ADC3", "IF_ADC3" },
+ { "IF2 ADC Mux", "TxDC_DAC", "TxDC_DAC" },
+ { "IF2 ADC Mux", "TxDP_ADC", "TxDP_ADC" },
+ { "IF2 ADC Mux", "VAD_ADC", "VAD_ADC" },
+
+ { "IF2 ADC L", NULL, "IF2 ADC Mux" },
+ { "IF2 ADC R", NULL, "IF2 ADC Mux" },
+
+ { "IF2 ADC", NULL, "I2S2" },
+ { "IF2 ADC", NULL, "IF2 ADC L" },
+ { "IF2 ADC", NULL, "IF2 ADC R" },
+
+ { "AIF1TX", NULL, "IF1 ADC" },
+ { "AIF2TX", NULL, "IF2 ADC" },
+
+ { "IF1 DAC1", NULL, "AIF1RX" },
+ { "IF1 DAC2", NULL, "AIF1RX" },
+ { "IF2 DAC", NULL, "AIF2RX" },
+
+ { "IF1 DAC1", NULL, "I2S1" },
+ { "IF1 DAC2", NULL, "I2S1" },
+ { "IF2 DAC", NULL, "I2S2" },
+
+ { "IF1 DAC2 L", NULL, "IF1 DAC2" },
+ { "IF1 DAC2 R", NULL, "IF1 DAC2" },
+ { "IF1 DAC1 L", NULL, "IF1 DAC1" },
+ { "IF1 DAC1 R", NULL, "IF1 DAC1" },
+ { "IF2 DAC L", NULL, "IF2 DAC" },
+ { "IF2 DAC R", NULL, "IF2 DAC" },
+
+ { "DAC1 L Mux", "IF1 DAC", "IF1 DAC1 L" },
+ { "DAC1 L Mux", "IF2 DAC", "IF2 DAC L" },
+
+ { "DAC1 R Mux", "IF1 DAC", "IF1 DAC1 R" },
+ { "DAC1 R Mux", "IF2 DAC", "IF2 DAC R" },
+
+ { "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" },
+ { "DAC1 MIXL", "DAC1 Switch", "DAC1 L Mux" },
+ { "DAC1 MIXL", NULL, "DAC Stereo1 Filter" },
+ { "DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR" },
+ { "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" },
+ { "DAC1 MIXR", NULL, "DAC Stereo1 Filter" },
+
+ { "DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "DAC MIX", NULL, "DAC1 MIXL" },
+ { "DAC MIX", NULL, "DAC1 MIXR" },
+
+ { "Audio DSP", NULL, "DAC1 MIXL" },
+ { "Audio DSP", NULL, "DAC1 MIXR" },
+
+ { "DAC L2 Mux", "IF1 DAC", "IF1 DAC2 L" },
+ { "DAC L2 Mux", "IF2 DAC", "IF2 DAC L" },
+ { "DAC L2 Mux", "TxDC DAC", "TxDC_DAC" },
+ { "DAC L2 Mux", "VAD_ADC", "VAD_ADC" },
+ { "DAC L2 Volume", NULL, "DAC L2 Mux" },
+ { "DAC L2 Volume", NULL, "DAC Mono Left Filter" },
+
+ { "DAC R2 Mux", "IF1 DAC", "IF1 DAC2 R" },
+ { "DAC R2 Mux", "IF2 DAC", "IF2 DAC R" },
+ { "DAC R2 Mux", "TxDC DAC", "TxDC_DAC" },
+ { "DAC R2 Mux", "TxDP ADC", "TxDP_ADC" },
+ { "DAC R2 Volume", NULL, "DAC R2 Mux" },
+ { "DAC R2 Volume", NULL, "DAC Mono Right Filter" },
+
+ { "Stereo DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" },
+ { "Stereo DAC MIXL", "DAC R1 Switch", "DAC1 MIXR" },
+ { "Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" },
+ { "Stereo DAC MIXL", NULL, "DAC Stereo1 Filter" },
+ { "Stereo DAC MIXL", NULL, "DAC L1 Power" },
+ { "Stereo DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" },
+ { "Stereo DAC MIXR", "DAC L1 Switch", "DAC1 MIXL" },
+ { "Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" },
+ { "Stereo DAC MIXR", NULL, "DAC Stereo1 Filter" },
+ { "Stereo DAC MIXR", NULL, "DAC R1 Power" },
+
+ { "Mono DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" },
+ { "Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" },
+ { "Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" },
+ { "Mono DAC MIXL", NULL, "DAC Mono Left Filter" },
+ { "Mono DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" },
+ { "Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" },
+ { "Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" },
+ { "Mono DAC MIXR", NULL, "DAC Mono Right Filter" },
+
+ { "DAC MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" },
+ { "DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" },
+ { "DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" },
+ { "DAC MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" },
+ { "DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" },
+ { "DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" },
+
+ { "DAC L1", NULL, "DAC L1 Power" },
+ { "DAC L1", NULL, "Stereo DAC MIXL" },
+ { "DAC R1", NULL, "DAC R1 Power" },
+ { "DAC R1", NULL, "Stereo DAC MIXR" },
+ { "DAC L2", NULL, "Mono DAC MIXL" },
+ { "DAC R2", NULL, "Mono DAC MIXR" },
+
+ { "OUT MIXL", "BST1 Switch", "BST1" },
+ { "OUT MIXL", "INL Switch", "INL VOL" },
+ { "OUT MIXL", "DAC L2 Switch", "DAC L2" },
+ { "OUT MIXL", "DAC L1 Switch", "DAC L1" },
+
+ { "OUT MIXR", "BST2 Switch", "BST2" },
+ { "OUT MIXR", "INR Switch", "INR VOL" },
+ { "OUT MIXR", "DAC R2 Switch", "DAC R2" },
+ { "OUT MIXR", "DAC R1 Switch", "DAC R1" },
+
+ { "HPOVOL MIXL", "DAC1 Switch", "DAC L1" },
+ { "HPOVOL MIXL", "INL Switch", "INL VOL" },
+ { "HPOVOL MIXR", "DAC1 Switch", "DAC R1" },
+ { "HPOVOL MIXR", "INR Switch", "INR VOL" },
+
+ { "DAC 2", NULL, "DAC L2" },
+ { "DAC 2", NULL, "DAC R2" },
+ { "DAC 1", NULL, "DAC L1" },
+ { "DAC 1", NULL, "DAC R1" },
+ { "HPOVOL", NULL, "HPOVOL MIXL" },
+ { "HPOVOL", NULL, "HPOVOL MIXR" },
+ { "HPO MIX", "DAC1 Switch", "DAC 1" },
+ { "HPO MIX", "HPVOL Switch", "HPOVOL" },
+
+ { "LOUT MIX", "DAC L1 Switch", "DAC L1" },
+ { "LOUT MIX", "DAC R1 Switch", "DAC R1" },
+ { "LOUT MIX", "OUTMIX L Switch", "OUT MIXL" },
+ { "LOUT MIX", "OUTMIX R Switch", "OUT MIXR" },
+
+ { "PDM1 L Mux", "Stereo DAC", "Stereo DAC MIXL" },
+ { "PDM1 L Mux", "Mono DAC", "Mono DAC MIXL" },
+ { "PDM1 L Mux", NULL, "PDM1 Power" },
+ { "PDM1 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
+ { "PDM1 R Mux", "Mono DAC", "Mono DAC MIXR" },
+ { "PDM1 R Mux", NULL, "PDM1 Power" },
+
+ { "HP Amp", NULL, "HPO MIX" },
+ { "HP Amp", NULL, "Mic Det Power" },
+ { "HPOL", NULL, "HP Amp" },
+ { "HPOL", NULL, "HP L Amp" },
+ { "HPOL", NULL, "Improve HP Amp Drv" },
+ { "HPOR", NULL, "HP Amp" },
+ { "HPOR", NULL, "HP R Amp" },
+ { "HPOR", NULL, "Improve HP Amp Drv" },
+
+ { "LOUT Amp", NULL, "LOUT MIX" },
+ { "LOUT L Playback", "Switch", "LOUT Amp" },
+ { "LOUT R Playback", "Switch", "LOUT Amp" },
+ { "LOUTL", NULL, "LOUT L Playback" },
+ { "LOUTR", NULL, "LOUT R Playback" },
+ { "LOUTL", NULL, "Improve HP Amp Drv" },
+ { "LOUTR", NULL, "Improve HP Amp Drv" },
+};
+
+static const struct snd_soc_dapm_route rt5670_specific_dapm_routes[] = {
+ { "PDM2 L Mux", "Stereo DAC", "Stereo DAC MIXL" },
+ { "PDM2 L Mux", "Mono DAC", "Mono DAC MIXL" },
+ { "PDM2 L Mux", NULL, "PDM2 Power" },
+ { "PDM2 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
+ { "PDM2 R Mux", "Mono DAC", "Mono DAC MIXR" },
+ { "PDM2 R Mux", NULL, "PDM2 Power" },
+ { "PDM1L", NULL, "PDM1 L Mux" },
+ { "PDM1R", NULL, "PDM1 R Mux" },
+ { "PDM2L", NULL, "PDM2 L Mux" },
+ { "PDM2R", NULL, "PDM2 R Mux" },
+};
+
+static const struct snd_soc_dapm_route rt5672_specific_dapm_routes[] = {
+ { "SPO Amp", NULL, "PDM1 L Mux" },
+ { "SPO Amp", NULL, "PDM1 R Mux" },
+ { "SPOLP", NULL, "SPO Amp" },
+ { "SPOLN", NULL, "SPO Amp" },
+ { "SPORP", NULL, "SPO Amp" },
+ { "SPORN", NULL, "SPO Amp" },
+};
+
+static int rt5670_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ 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",
+ 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);
+ return -EINVAL;
+ }
+ bclk_ms = frame_size > 32;
+ rt5670->bclk[dai->id] = rt5670->lrck[dai->id] * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt5670->bclk[dai->id], rt5670->lrck[dai->id]);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ val_len |= RT5670_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= RT5670_I2S_DL_24;
+ break;
+ case 8:
+ val_len |= RT5670_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5670_AIF1:
+ mask_clk = RT5670_I2S_BCLK_MS1_MASK | RT5670_I2S_PD1_MASK;
+ val_clk = bclk_ms << RT5670_I2S_BCLK_MS1_SFT |
+ pre_div << RT5670_I2S_PD1_SFT;
+ snd_soc_update_bits(codec, RT5670_I2S1_SDP,
+ RT5670_I2S_DL_MASK, val_len);
+ snd_soc_update_bits(codec, 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,
+ RT5670_I2S_DL_MASK, val_len);
+ snd_soc_update_bits(codec, RT5670_ADDA_CLK1, mask_clk, val_clk);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5670_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rt5670->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT5670_I2S_MS_S;
+ rt5670->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5670_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5670_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5670_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5670_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5670_AIF1:
+ snd_soc_update_bits(codec, 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,
+ 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);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+
+ switch (clk_id) {
+ case RT5670_SCLK_S_MCLK:
+ reg_val |= RT5670_SCLK_SRC_MCLK;
+ break;
+ case RT5670_SCLK_S_PLL1:
+ reg_val |= RT5670_SCLK_SRC_PLL1;
+ break;
+ case RT5670_SCLK_S_RCCLK:
+ reg_val |= RT5670_SCLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ RT5670_SCLK_SRC_MASK, reg_val);
+ rt5670->sysclk = freq;
+ if (clk_id != RT5670_SCLK_S_RCCLK)
+ rt5670->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+ return 0;
+}
+
+static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt5670->pll_src && freq_in == rt5670->pll_in &&
+ freq_out == rt5670->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(codec->dev, "PLL disabled\n");
+
+ rt5670->pll_in = 0;
+ rt5670->pll_out = 0;
+ snd_soc_update_bits(codec, 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,
+ RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_MCLK);
+ break;
+ case RT5670_PLL1_S_BCLK1:
+ case RT5670_PLL1_S_BCLK2:
+ case RT5670_PLL1_S_BCLK3:
+ case RT5670_PLL1_S_BCLK4:
+ switch (dai->id) {
+ case RT5670_AIF1:
+ snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_BCLK1);
+ break;
+ case RT5670_AIF2:
+ snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_BCLK2);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec->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);
+ return ret;
+ }
+
+ dev_dbg(codec->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,
+ 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);
+
+ rt5670->pll_in = freq_in;
+ rt5670->pll_out = freq_out;
+ rt5670->pll_src = source;
+
+ return 0;
+}
+
+static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int val = 0;
+
+ if (rx_mask || tx_mask)
+ val |= (1 << 14);
+
+ switch (slots) {
+ case 4:
+ val |= (1 << 12);
+ break;
+ case 6:
+ val |= (2 << 12);
+ break;
+ case 8:
+ val |= (3 << 12);
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width) {
+ case 20:
+ val |= (1 << 10);
+ break;
+ case 24:
+ val |= (2 << 10);
+ break;
+ case 32:
+ val |= (3 << 10);
+ break;
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, RT5670_TDM_CTRL_1, 0x7c00, val);
+
+ return 0;
+}
+
+static int rt5670_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
+ snd_soc_update_bits(codec, 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,
+ RT5670_PWR_FV1 | RT5670_PWR_FV2,
+ RT5670_PWR_FV1 | RT5670_PWR_FV2);
+ snd_soc_update_bits(codec, 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);
+ }
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_update_bits(codec, 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);
+ break;
+ case SND_SOC_BIAS_OFF:
+ if (rt5670->pdata.jd_mode)
+ snd_soc_update_bits(codec, 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,
+ 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);
+ break;
+
+ default:
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int rt5670_probe(struct snd_soc_codec *codec)
+{
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ switch (snd_soc_read(codec, RT5670_RESET) & RT5670_ID_MASK) {
+ case RT5670_ID_5670:
+ case RT5670_ID_5671:
+ snd_soc_dapm_new_controls(&codec->dapm,
+ rt5670_specific_dapm_widgets,
+ ARRAY_SIZE(rt5670_specific_dapm_widgets));
+ snd_soc_dapm_add_routes(&codec->dapm,
+ rt5670_specific_dapm_routes,
+ ARRAY_SIZE(rt5670_specific_dapm_routes));
+ break;
+ case RT5670_ID_5672:
+ snd_soc_dapm_new_controls(&codec->dapm,
+ rt5672_specific_dapm_widgets,
+ ARRAY_SIZE(rt5672_specific_dapm_widgets));
+ snd_soc_dapm_add_routes(&codec->dapm,
+ rt5672_specific_dapm_routes,
+ ARRAY_SIZE(rt5672_specific_dapm_routes));
+ break;
+ default:
+ dev_err(codec->dev,
+ "The driver is for RT5670 RT5671 or RT5672 only\n");
+ return -ENODEV;
+ }
+ rt5670->codec = codec;
+
+ return 0;
+}
+
+static int rt5670_remove(struct snd_soc_codec *codec)
+{
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ regmap_write(rt5670->regmap, RT5670_RESET, 0);
+ snd_soc_jack_free_gpios(rt5670->jack, 1, &rt5670->hp_gpio);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt5670_suspend(struct snd_soc_codec *codec)
+{
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt5670->regmap, true);
+ regcache_mark_dirty(rt5670->regmap);
+ return 0;
+}
+
+static int rt5670_resume(struct snd_soc_codec *codec)
+{
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt5670->regmap, false);
+ regcache_sync(rt5670->regmap);
+
+ return 0;
+}
+#else
+#define rt5670_suspend NULL
+#define rt5670_resume NULL
+#endif
+
+#define RT5670_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5670_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static 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,
+};
+
+static struct snd_soc_dai_driver rt5670_dai[] = {
+ {
+ .name = "rt5670-aif1",
+ .id = RT5670_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5670_STEREO_RATES,
+ .formats = RT5670_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5670_STEREO_RATES,
+ .formats = RT5670_FORMATS,
+ },
+ .ops = &rt5670_aif_dai_ops,
+ },
+ {
+ .name = "rt5670-aif2",
+ .id = RT5670_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5670_STEREO_RATES,
+ .formats = RT5670_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5670_STEREO_RATES,
+ .formats = RT5670_FORMATS,
+ },
+ .ops = &rt5670_aif_dai_ops,
+ },
+};
+
+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 regmap_config rt5670_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .use_single_rw = 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,
+ .reg_defaults = rt5670_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5670_reg),
+ .ranges = rt5670_ranges,
+ .num_ranges = ARRAY_SIZE(rt5670_ranges),
+};
+
+static const struct i2c_device_id rt5670_i2c_id[] = {
+ { "rt5670", 0 },
+ { "rt5671", 0 },
+ { "rt5672", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
+
+#ifdef CONFIG_ACPI
+static struct acpi_device_id rt5670_acpi_match[] = {
+ { "10EC5670", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match);
+#endif
+
+static const struct dmi_system_id dmi_platform_intel_braswell[] = {
+ {
+ .ident = "Intel Braswell",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Braswell CRB"),
+ },
+ },
+ {}
+};
+
+static int rt5670_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt5670_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5670_priv *rt5670;
+ int ret;
+ unsigned int val;
+
+ rt5670 = devm_kzalloc(&i2c->dev,
+ sizeof(struct rt5670_priv),
+ GFP_KERNEL);
+ if (NULL == rt5670)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5670);
+
+ if (pdata)
+ rt5670->pdata = *pdata;
+
+ if (dmi_check_system(dmi_platform_intel_braswell)) {
+ rt5670->pdata.dmic_en = true;
+ rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P;
+ rt5670->pdata.dev_gpio = true;
+ rt5670->pdata.jd_mode = 1;
+ }
+
+ rt5670->regmap = devm_regmap_init_i2c(i2c, &rt5670_regmap);
+ if (IS_ERR(rt5670->regmap)) {
+ ret = PTR_ERR(rt5670->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt5670->regmap, RT5670_VENDOR_ID2, &val);
+ if (val != RT5670_DEVICE_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt5670/72\n", val);
+ return -ENODEV;
+ }
+
+ regmap_write(rt5670->regmap, RT5670_RESET, 0);
+ regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG1,
+ RT5670_PWR_HP_L | RT5670_PWR_HP_R |
+ RT5670_PWR_VREF2, RT5670_PWR_VREF2);
+ msleep(100);
+
+ regmap_write(rt5670->regmap, RT5670_RESET, 0);
+
+ regmap_read(rt5670->regmap, RT5670_VENDOR_ID, &val);
+ if (val >= 4)
+ regmap_write(rt5670->regmap, RT5670_GPIO_CTRL3, 0x0980);
+ else
+ regmap_write(rt5670->regmap, RT5670_GPIO_CTRL3, 0x0d00);
+
+ ret = regmap_register_patch(rt5670->regmap, init_list,
+ ARRAY_SIZE(init_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ if (rt5670->pdata.in2_diff)
+ regmap_update_bits(rt5670->regmap, RT5670_IN2,
+ RT5670_IN_DF2, RT5670_IN_DF2);
+
+ if (rt5670->pdata.dev_gpio) {
+ /* 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);
+ regmap_update_bits(rt5670->regmap, RT5670_DIG_MISC, 0x8, 0x8);
+ }
+
+ if (rt5670->pdata.jd_mode) {
+ regmap_update_bits(rt5670->regmap, RT5670_GLB_CLK,
+ RT5670_SCLK_SRC_MASK, RT5670_SCLK_SRC_RCCLK);
+ rt5670->sysclk = 0;
+ rt5670->sysclk_src = RT5670_SCLK_S_RCCLK;
+ regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG1,
+ RT5670_PWR_MB, RT5670_PWR_MB);
+ regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG2,
+ RT5670_PWR_JD1, RT5670_PWR_JD1);
+ regmap_update_bits(rt5670->regmap, RT5670_IRQ_CTRL1,
+ RT5670_JD1_1_EN_MASK, RT5670_JD1_1_EN);
+ regmap_update_bits(rt5670->regmap, RT5670_JD_CTRL3,
+ RT5670_JD_TRI_CBJ_SEL_MASK |
+ RT5670_JD_TRI_HPO_SEL_MASK,
+ RT5670_JD_CBJ_JD1_1 | RT5670_JD_HPO_JD1_1);
+ switch (rt5670->pdata.jd_mode) {
+ case 1:
+ regmap_update_bits(rt5670->regmap, RT5670_A_JD_CTRL1,
+ RT5670_JD1_MODE_MASK,
+ RT5670_JD1_MODE_0);
+ break;
+ case 2:
+ regmap_update_bits(rt5670->regmap, RT5670_A_JD_CTRL1,
+ RT5670_JD1_MODE_MASK,
+ RT5670_JD1_MODE_1);
+ break;
+ case 3:
+ regmap_update_bits(rt5670->regmap, RT5670_A_JD_CTRL1,
+ RT5670_JD1_MODE_MASK,
+ RT5670_JD1_MODE_2);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (rt5670->pdata.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) {
+ case RT5670_DMIC_DATA_IN2P:
+ regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1,
+ RT5670_DMIC_1_DP_MASK,
+ RT5670_DMIC_1_DP_IN2P);
+ break;
+
+ case RT5670_DMIC_DATA_GPIO6:
+ regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1,
+ RT5670_DMIC_1_DP_MASK,
+ RT5670_DMIC_1_DP_GPIO6);
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1,
+ RT5670_GP6_PIN_MASK,
+ RT5670_GP6_PIN_DMIC1_SDA);
+ break;
+
+ case RT5670_DMIC_DATA_GPIO7:
+ regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1,
+ RT5670_DMIC_1_DP_MASK,
+ RT5670_DMIC_1_DP_GPIO7);
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1,
+ RT5670_GP7_PIN_MASK,
+ RT5670_GP7_PIN_DMIC1_SDA);
+ break;
+
+ default:
+ break;
+ }
+
+ switch (rt5670->pdata.dmic2_data_pin) {
+ case RT5670_DMIC_DATA_IN3N:
+ regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1,
+ RT5670_DMIC_2_DP_MASK,
+ RT5670_DMIC_2_DP_IN3N);
+ break;
+
+ case RT5670_DMIC_DATA_GPIO8:
+ regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1,
+ RT5670_DMIC_2_DP_MASK,
+ RT5670_DMIC_2_DP_GPIO8);
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1,
+ RT5670_GP8_PIN_MASK,
+ RT5670_GP8_PIN_DMIC2_SDA);
+ break;
+
+ default:
+ break;
+ }
+
+ switch (rt5670->pdata.dmic3_data_pin) {
+ case RT5670_DMIC_DATA_GPIO5:
+ regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL2,
+ RT5670_DMIC_3_DP_MASK,
+ RT5670_DMIC_3_DP_GPIO5);
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1,
+ RT5670_GP5_PIN_MASK,
+ RT5670_GP5_PIN_DMIC3_SDA);
+ break;
+
+ case RT5670_DMIC_DATA_GPIO9:
+ case RT5670_DMIC_DATA_GPIO10:
+ dev_err(&i2c->dev,
+ "Always use GPIO5 as DMIC3 data pin\n");
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ pm_runtime_enable(&i2c->dev);
+ pm_request_idle(&i2c->dev);
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_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);
+
+ return ret;
+}
+
+static int 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,
+ .remove = rt5670_i2c_remove,
+ .id_table = rt5670_i2c_id,
+};
+
+module_i2c_driver(rt5670_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5670 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/rt5670.h b/kernel/sound/soc/codecs/rt5670.h
new file mode 100644
index 000000000..dc2b46236
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5670.h
@@ -0,0 +1,2014 @@
+/*
+ * 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
+#define RT5670_VENDOR_ID1 0xfe
+#define RT5670_VENDOR_ID2 0xff
+/* I/O - Output */
+#define RT5670_HP_VOL 0x02
+#define RT5670_LOUT1 0x03
+/* I/O - Input */
+#define RT5670_CJ_CTRL1 0x0a
+#define RT5670_CJ_CTRL2 0x0b
+#define RT5670_CJ_CTRL3 0x0c
+#define RT5670_IN2 0x0e
+#define RT5670_INL1_INR1_VOL 0x0f
+/* I/O - ADC/DAC/DMIC */
+#define RT5670_DAC1_DIG_VOL 0x19
+#define RT5670_DAC2_DIG_VOL 0x1a
+#define RT5670_DAC_CTRL 0x1b
+#define RT5670_STO1_ADC_DIG_VOL 0x1c
+#define RT5670_MONO_ADC_DIG_VOL 0x1d
+#define RT5670_ADC_BST_VOL1 0x1e
+#define RT5670_STO2_ADC_DIG_VOL 0x1f
+/* Mixer - D-D */
+#define RT5670_ADC_BST_VOL2 0x20
+#define RT5670_STO2_ADC_MIXER 0x26
+#define RT5670_STO1_ADC_MIXER 0x27
+#define RT5670_MONO_ADC_MIXER 0x28
+#define RT5670_AD_DA_MIXER 0x29
+#define RT5670_STO_DAC_MIXER 0x2a
+#define RT5670_DD_MIXER 0x2b
+#define RT5670_DIG_MIXER 0x2c
+#define RT5670_DSP_PATH1 0x2d
+#define RT5670_DSP_PATH2 0x2e
+#define RT5670_DIG_INF1_DATA 0x2f
+#define RT5670_DIG_INF2_DATA 0x30
+/* Mixer - PDM */
+#define RT5670_PDM_OUT_CTRL 0x31
+#define RT5670_PDM_DATA_CTRL1 0x32
+#define RT5670_PDM1_DATA_CTRL2 0x33
+#define RT5670_PDM1_DATA_CTRL3 0x34
+#define RT5670_PDM1_DATA_CTRL4 0x35
+#define RT5670_PDM2_DATA_CTRL2 0x36
+#define RT5670_PDM2_DATA_CTRL3 0x37
+#define RT5670_PDM2_DATA_CTRL4 0x38
+/* Mixer - ADC */
+#define RT5670_REC_L1_MIXER 0x3b
+#define RT5670_REC_L2_MIXER 0x3c
+#define RT5670_REC_R1_MIXER 0x3d
+#define RT5670_REC_R2_MIXER 0x3e
+/* Mixer - DAC */
+#define RT5670_HPO_MIXER 0x45
+#define RT5670_MONO_MIXER 0x4c
+#define RT5670_OUT_L1_MIXER 0x4f
+#define RT5670_OUT_R1_MIXER 0x52
+#define RT5670_LOUT_MIXER 0x53
+/* Power */
+#define RT5670_PWR_DIG1 0x61
+#define RT5670_PWR_DIG2 0x62
+#define RT5670_PWR_ANLG1 0x63
+#define RT5670_PWR_ANLG2 0x64
+#define RT5670_PWR_MIXER 0x65
+#define RT5670_PWR_VOL 0x66
+/* Private Register Control */
+#define RT5670_PRIV_INDEX 0x6a
+#define RT5670_PRIV_DATA 0x6c
+/* Format - ADC/DAC */
+#define RT5670_I2S4_SDP 0x6f
+#define RT5670_I2S1_SDP 0x70
+#define RT5670_I2S2_SDP 0x71
+#define RT5670_I2S3_SDP 0x72
+#define RT5670_ADDA_CLK1 0x73
+#define RT5670_ADDA_CLK2 0x74
+#define RT5670_DMIC_CTRL1 0x75
+#define RT5670_DMIC_CTRL2 0x76
+/* Format - TDM Control */
+#define RT5670_TDM_CTRL_1 0x77
+#define RT5670_TDM_CTRL_2 0x78
+#define RT5670_TDM_CTRL_3 0x79
+
+/* Function - Analog */
+#define RT5670_DSP_CLK 0x7f
+#define RT5670_GLB_CLK 0x80
+#define RT5670_PLL_CTRL1 0x81
+#define RT5670_PLL_CTRL2 0x82
+#define RT5670_ASRC_1 0x83
+#define RT5670_ASRC_2 0x84
+#define RT5670_ASRC_3 0x85
+#define RT5670_ASRC_4 0x86
+#define RT5670_ASRC_5 0x87
+#define RT5670_ASRC_7 0x89
+#define RT5670_ASRC_8 0x8a
+#define RT5670_ASRC_9 0x8b
+#define RT5670_ASRC_10 0x8c
+#define RT5670_ASRC_11 0x8d
+#define RT5670_DEPOP_M1 0x8e
+#define RT5670_DEPOP_M2 0x8f
+#define RT5670_DEPOP_M3 0x90
+#define RT5670_CHARGE_PUMP 0x91
+#define RT5670_MICBIAS 0x93
+#define RT5670_A_JD_CTRL1 0x94
+#define RT5670_A_JD_CTRL2 0x95
+#define RT5670_ASRC_12 0x97
+#define RT5670_ASRC_13 0x98
+#define RT5670_ASRC_14 0x99
+#define RT5670_VAD_CTRL1 0x9a
+#define RT5670_VAD_CTRL2 0x9b
+#define RT5670_VAD_CTRL3 0x9c
+#define RT5670_VAD_CTRL4 0x9d
+#define RT5670_VAD_CTRL5 0x9e
+/* Function - Digital */
+#define RT5670_ADC_EQ_CTRL1 0xae
+#define RT5670_ADC_EQ_CTRL2 0xaf
+#define RT5670_EQ_CTRL1 0xb0
+#define RT5670_EQ_CTRL2 0xb1
+#define RT5670_ALC_DRC_CTRL1 0xb2
+#define RT5670_ALC_DRC_CTRL2 0xb3
+#define RT5670_ALC_CTRL_1 0xb4
+#define RT5670_ALC_CTRL_2 0xb5
+#define RT5670_ALC_CTRL_3 0xb6
+#define RT5670_ALC_CTRL_4 0xb7
+#define RT5670_JD_CTRL 0xbb
+#define RT5670_IRQ_CTRL1 0xbd
+#define RT5670_IRQ_CTRL2 0xbe
+#define RT5670_INT_IRQ_ST 0xbf
+#define RT5670_GPIO_CTRL1 0xc0
+#define RT5670_GPIO_CTRL2 0xc1
+#define RT5670_GPIO_CTRL3 0xc2
+#define RT5670_SCRABBLE_FUN 0xcd
+#define RT5670_SCRABBLE_CTRL 0xce
+#define RT5670_BASE_BACK 0xcf
+#define RT5670_MP3_PLUS1 0xd0
+#define RT5670_MP3_PLUS2 0xd1
+#define RT5670_ADJ_HPF1 0xd3
+#define RT5670_ADJ_HPF2 0xd4
+#define RT5670_HP_CALIB_AMP_DET 0xd6
+#define RT5670_SV_ZCD1 0xd9
+#define RT5670_SV_ZCD2 0xda
+#define RT5670_IL_CMD 0xdb
+#define RT5670_IL_CMD2 0xdc
+#define RT5670_IL_CMD3 0xdd
+#define RT5670_DRC_HL_CTRL1 0xe6
+#define RT5670_DRC_HL_CTRL2 0xe7
+#define RT5670_ADC_MONO_HP_CTRL1 0xec
+#define RT5670_ADC_MONO_HP_CTRL2 0xed
+#define RT5670_ADC_STO2_HP_CTRL1 0xee
+#define RT5670_ADC_STO2_HP_CTRL2 0xef
+#define RT5670_JD_CTRL3 0xf8
+#define RT5670_JD_CTRL4 0xf9
+/* General Control */
+#define RT5670_DIG_MISC 0xfa
+#define RT5670_GEN_CTRL2 0xfb
+#define RT5670_GEN_CTRL3 0xfc
+
+
+/* Index of Codec Private Register definition */
+#define RT5670_DIG_VOL 0x00
+#define RT5670_PR_ALC_CTRL_1 0x01
+#define RT5670_PR_ALC_CTRL_2 0x02
+#define RT5670_PR_ALC_CTRL_3 0x03
+#define RT5670_PR_ALC_CTRL_4 0x04
+#define RT5670_PR_ALC_CTRL_5 0x05
+#define RT5670_PR_ALC_CTRL_6 0x06
+#define RT5670_BIAS_CUR1 0x12
+#define RT5670_BIAS_CUR3 0x14
+#define RT5670_CLSD_INT_REG1 0x1c
+#define RT5670_MAMP_INT_REG2 0x37
+#define RT5670_CHOP_DAC_ADC 0x3d
+#define RT5670_MIXER_INT_REG 0x3f
+#define RT5670_3D_SPK 0x63
+#define RT5670_WND_1 0x6c
+#define RT5670_WND_2 0x6d
+#define RT5670_WND_3 0x6e
+#define RT5670_WND_4 0x6f
+#define RT5670_WND_5 0x70
+#define RT5670_WND_8 0x73
+#define RT5670_DIP_SPK_INF 0x75
+#define RT5670_HP_DCC_INT1 0x77
+#define RT5670_EQ_BW_LOP 0xa0
+#define RT5670_EQ_GN_LOP 0xa1
+#define RT5670_EQ_FC_BP1 0xa2
+#define RT5670_EQ_BW_BP1 0xa3
+#define RT5670_EQ_GN_BP1 0xa4
+#define RT5670_EQ_FC_BP2 0xa5
+#define RT5670_EQ_BW_BP2 0xa6
+#define RT5670_EQ_GN_BP2 0xa7
+#define RT5670_EQ_FC_BP3 0xa8
+#define RT5670_EQ_BW_BP3 0xa9
+#define RT5670_EQ_GN_BP3 0xaa
+#define RT5670_EQ_FC_BP4 0xab
+#define RT5670_EQ_BW_BP4 0xac
+#define RT5670_EQ_GN_BP4 0xad
+#define RT5670_EQ_FC_HIP1 0xae
+#define RT5670_EQ_GN_HIP1 0xaf
+#define RT5670_EQ_FC_HIP2 0xb0
+#define RT5670_EQ_BW_HIP2 0xb1
+#define RT5670_EQ_GN_HIP2 0xb2
+#define RT5670_EQ_PRE_VOL 0xb3
+#define RT5670_EQ_PST_VOL 0xb4
+
+
+/* global definition */
+#define RT5670_L_MUTE (0x1 << 15)
+#define RT5670_L_MUTE_SFT 15
+#define RT5670_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)
+#define RT5670_R_VOL_SFT 0
+
+/* SW Reset & Device ID (0x00) */
+#define RT5670_ID_MASK (0x3 << 1)
+#define RT5670_ID_5670 (0x0 << 1)
+#define RT5670_ID_5672 (0x1 << 1)
+#define RT5670_ID_5671 (0x2 << 1)
+
+/* Combo Jack Control 1 (0x0a) */
+#define RT5670_CBJ_BST1_MASK (0xf << 12)
+#define RT5670_CBJ_BST1_SFT (12)
+#define RT5670_CBJ_JD_HP_EN (0x1 << 9)
+#define RT5670_CBJ_JD_MIC_EN (0x1 << 8)
+#define RT5670_CBJ_BST1_EN (0x1 << 2)
+
+/* Combo Jack Control 1 (0x0b) */
+#define RT5670_CBJ_MN_JD (0x1 << 12)
+#define RT5670_CAPLESS_EN (0x1 << 11)
+#define RT5670_CBJ_DET_MODE (0x1 << 7)
+
+/* IN2 Control (0x0e) */
+#define RT5670_BST_MASK1 (0xf<<12)
+#define RT5670_BST_SFT1 12
+#define RT5670_BST_MASK2 (0xf<<8)
+#define RT5670_BST_SFT2 8
+#define RT5670_IN_DF1 (0x1 << 7)
+#define RT5670_IN_SFT1 7
+#define RT5670_IN_DF2 (0x1 << 6)
+#define RT5670_IN_SFT2 6
+
+/* INL and INR Volume Control (0x0f) */
+#define RT5670_INL_SEL_MASK (0x1 << 15)
+#define RT5670_INL_SEL_SFT 15
+#define RT5670_INL_SEL_IN4P (0x0 << 15)
+#define RT5670_INL_SEL_MONOP (0x1 << 15)
+#define RT5670_INL_VOL_MASK (0x1f << 8)
+#define RT5670_INL_VOL_SFT 8
+#define RT5670_INR_SEL_MASK (0x1 << 7)
+#define RT5670_INR_SEL_SFT 7
+#define RT5670_INR_SEL_IN4N (0x0 << 7)
+#define RT5670_INR_SEL_MONON (0x1 << 7)
+#define RT5670_INR_VOL_MASK (0x1f)
+#define RT5670_INR_VOL_SFT 0
+
+/* Sidetone Control (0x18) */
+#define RT5670_ST_SEL_MASK (0x7 << 9)
+#define RT5670_ST_SEL_SFT 9
+#define RT5670_M_ST_DACR2 (0x1 << 8)
+#define RT5670_M_ST_DACR2_SFT 8
+#define RT5670_M_ST_DACL2 (0x1 << 7)
+#define RT5670_M_ST_DACL2_SFT 7
+#define RT5670_ST_EN (0x1 << 6)
+#define RT5670_ST_EN_SFT 6
+
+/* DAC1 Digital Volume (0x19) */
+#define RT5670_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5670_DAC_L1_VOL_SFT 8
+#define RT5670_DAC_R1_VOL_MASK (0xff)
+#define RT5670_DAC_R1_VOL_SFT 0
+
+/* DAC2 Digital Volume (0x1a) */
+#define RT5670_DAC_L2_VOL_MASK (0xff << 8)
+#define RT5670_DAC_L2_VOL_SFT 8
+#define RT5670_DAC_R2_VOL_MASK (0xff)
+#define RT5670_DAC_R2_VOL_SFT 0
+
+/* DAC2 Control (0x1b) */
+#define RT5670_M_DAC_L2_VOL (0x1 << 13)
+#define RT5670_M_DAC_L2_VOL_SFT 13
+#define RT5670_M_DAC_R2_VOL (0x1 << 12)
+#define RT5670_M_DAC_R2_VOL_SFT 12
+#define RT5670_DAC2_L_SEL_MASK (0x7 << 4)
+#define RT5670_DAC2_L_SEL_SFT 4
+#define RT5670_DAC2_R_SEL_MASK (0x7 << 0)
+#define RT5670_DAC2_R_SEL_SFT 0
+
+/* ADC Digital Volume Control (0x1c) */
+#define RT5670_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5670_ADC_L_VOL_SFT 8
+#define RT5670_ADC_R_VOL_MASK (0x7f)
+#define RT5670_ADC_R_VOL_SFT 0
+
+/* Mono ADC Digital Volume Control (0x1d) */
+#define RT5670_MONO_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5670_MONO_ADC_L_VOL_SFT 8
+#define RT5670_MONO_ADC_R_VOL_MASK (0x7f)
+#define RT5670_MONO_ADC_R_VOL_SFT 0
+
+/* ADC Boost Volume Control (0x1e) */
+#define RT5670_STO1_ADC_L_BST_MASK (0x3 << 14)
+#define RT5670_STO1_ADC_L_BST_SFT 14
+#define RT5670_STO1_ADC_R_BST_MASK (0x3 << 12)
+#define RT5670_STO1_ADC_R_BST_SFT 12
+#define RT5670_STO1_ADC_COMP_MASK (0x3 << 10)
+#define RT5670_STO1_ADC_COMP_SFT 10
+#define RT5670_STO2_ADC_L_BST_MASK (0x3 << 8)
+#define RT5670_STO2_ADC_L_BST_SFT 8
+#define RT5670_STO2_ADC_R_BST_MASK (0x3 << 6)
+#define RT5670_STO2_ADC_R_BST_SFT 6
+#define RT5670_STO2_ADC_COMP_MASK (0x3 << 4)
+#define RT5670_STO2_ADC_COMP_SFT 4
+
+/* Stereo2 ADC Mixer Control (0x26) */
+#define RT5670_STO2_ADC_SRC_MASK (0x1 << 15)
+#define RT5670_STO2_ADC_SRC_SFT 15
+
+/* Stereo ADC Mixer Control (0x26 0x27) */
+#define RT5670_M_ADC_L1 (0x1 << 14)
+#define RT5670_M_ADC_L1_SFT 14
+#define RT5670_M_ADC_L2 (0x1 << 13)
+#define RT5670_M_ADC_L2_SFT 13
+#define RT5670_ADC_1_SRC_MASK (0x1 << 12)
+#define RT5670_ADC_1_SRC_SFT 12
+#define RT5670_ADC_1_SRC_ADC (0x1 << 12)
+#define RT5670_ADC_1_SRC_DACMIX (0x0 << 12)
+#define RT5670_ADC_2_SRC_MASK (0x1 << 11)
+#define RT5670_ADC_2_SRC_SFT 11
+#define RT5670_ADC_SRC_MASK (0x1 << 10)
+#define RT5670_ADC_SRC_SFT 10
+#define RT5670_DMIC_SRC_MASK (0x3 << 8)
+#define RT5670_DMIC_SRC_SFT 8
+#define RT5670_M_ADC_R1 (0x1 << 6)
+#define RT5670_M_ADC_R1_SFT 6
+#define RT5670_M_ADC_R2 (0x1 << 5)
+#define RT5670_M_ADC_R2_SFT 5
+#define RT5670_DMIC3_SRC_MASK (0x1 << 1)
+#define RT5670_DMIC3_SRC_SFT 0
+
+/* Mono ADC Mixer Control (0x28) */
+#define RT5670_M_MONO_ADC_L1 (0x1 << 14)
+#define RT5670_M_MONO_ADC_L1_SFT 14
+#define RT5670_M_MONO_ADC_L2 (0x1 << 13)
+#define RT5670_M_MONO_ADC_L2_SFT 13
+#define RT5670_MONO_ADC_L1_SRC_MASK (0x1 << 12)
+#define RT5670_MONO_ADC_L1_SRC_SFT 12
+#define RT5670_MONO_ADC_L1_SRC_DACMIXL (0x0 << 12)
+#define RT5670_MONO_ADC_L1_SRC_ADCL (0x1 << 12)
+#define RT5670_MONO_ADC_L2_SRC_MASK (0x1 << 11)
+#define RT5670_MONO_ADC_L2_SRC_SFT 11
+#define RT5670_MONO_ADC_L_SRC_MASK (0x1 << 10)
+#define RT5670_MONO_ADC_L_SRC_SFT 10
+#define RT5670_MONO_DMIC_L_SRC_MASK (0x3 << 8)
+#define RT5670_MONO_DMIC_L_SRC_SFT 8
+#define RT5670_M_MONO_ADC_R1 (0x1 << 6)
+#define RT5670_M_MONO_ADC_R1_SFT 6
+#define RT5670_M_MONO_ADC_R2 (0x1 << 5)
+#define RT5670_M_MONO_ADC_R2_SFT 5
+#define RT5670_MONO_ADC_R1_SRC_MASK (0x1 << 4)
+#define RT5670_MONO_ADC_R1_SRC_SFT 4
+#define RT5670_MONO_ADC_R1_SRC_ADCR (0x1 << 4)
+#define RT5670_MONO_ADC_R1_SRC_DACMIXR (0x0 << 4)
+#define RT5670_MONO_ADC_R2_SRC_MASK (0x1 << 3)
+#define RT5670_MONO_ADC_R2_SRC_SFT 3
+#define RT5670_MONO_DMIC_R_SRC_MASK (0x3)
+#define RT5670_MONO_DMIC_R_SRC_SFT 0
+
+/* ADC Mixer to DAC Mixer Control (0x29) */
+#define RT5670_M_ADCMIX_L (0x1 << 15)
+#define RT5670_M_ADCMIX_L_SFT 15
+#define RT5670_M_DAC1_L (0x1 << 14)
+#define RT5670_M_DAC1_L_SFT 14
+#define RT5670_DAC1_R_SEL_MASK (0x3 << 10)
+#define RT5670_DAC1_R_SEL_SFT 10
+#define RT5670_DAC1_R_SEL_IF1 (0x0 << 10)
+#define RT5670_DAC1_R_SEL_IF2 (0x1 << 10)
+#define RT5670_DAC1_R_SEL_IF3 (0x2 << 10)
+#define RT5670_DAC1_R_SEL_IF4 (0x3 << 10)
+#define RT5670_DAC1_L_SEL_MASK (0x3 << 8)
+#define RT5670_DAC1_L_SEL_SFT 8
+#define RT5670_DAC1_L_SEL_IF1 (0x0 << 8)
+#define RT5670_DAC1_L_SEL_IF2 (0x1 << 8)
+#define RT5670_DAC1_L_SEL_IF3 (0x2 << 8)
+#define RT5670_DAC1_L_SEL_IF4 (0x3 << 8)
+#define RT5670_M_ADCMIX_R (0x1 << 7)
+#define RT5670_M_ADCMIX_R_SFT 7
+#define RT5670_M_DAC1_R (0x1 << 6)
+#define RT5670_M_DAC1_R_SFT 6
+
+/* Stereo DAC Mixer Control (0x2a) */
+#define RT5670_M_DAC_L1 (0x1 << 14)
+#define RT5670_M_DAC_L1_SFT 14
+#define RT5670_DAC_L1_STO_L_VOL_MASK (0x1 << 13)
+#define RT5670_DAC_L1_STO_L_VOL_SFT 13
+#define RT5670_M_DAC_L2 (0x1 << 12)
+#define RT5670_M_DAC_L2_SFT 12
+#define RT5670_DAC_L2_STO_L_VOL_MASK (0x1 << 11)
+#define RT5670_DAC_L2_STO_L_VOL_SFT 11
+#define RT5670_M_DAC_R1_STO_L (0x1 << 9)
+#define RT5670_M_DAC_R1_STO_L_SFT 9
+#define RT5670_DAC_R1_STO_L_VOL_MASK (0x1 << 8)
+#define RT5670_DAC_R1_STO_L_VOL_SFT 8
+#define RT5670_M_DAC_R1 (0x1 << 6)
+#define RT5670_M_DAC_R1_SFT 6
+#define RT5670_DAC_R1_STO_R_VOL_MASK (0x1 << 5)
+#define RT5670_DAC_R1_STO_R_VOL_SFT 5
+#define RT5670_M_DAC_R2 (0x1 << 4)
+#define RT5670_M_DAC_R2_SFT 4
+#define RT5670_DAC_R2_STO_R_VOL_MASK (0x1 << 3)
+#define RT5670_DAC_R2_STO_R_VOL_SFT 3
+#define RT5670_M_DAC_L1_STO_R (0x1 << 1)
+#define RT5670_M_DAC_L1_STO_R_SFT 1
+#define RT5670_DAC_L1_STO_R_VOL_MASK (0x1)
+#define RT5670_DAC_L1_STO_R_VOL_SFT 0
+
+/* Mono DAC Mixer Control (0x2b) */
+#define RT5670_M_DAC_L1_MONO_L (0x1 << 14)
+#define RT5670_M_DAC_L1_MONO_L_SFT 14
+#define RT5670_DAC_L1_MONO_L_VOL_MASK (0x1 << 13)
+#define RT5670_DAC_L1_MONO_L_VOL_SFT 13
+#define RT5670_M_DAC_L2_MONO_L (0x1 << 12)
+#define RT5670_M_DAC_L2_MONO_L_SFT 12
+#define RT5670_DAC_L2_MONO_L_VOL_MASK (0x1 << 11)
+#define RT5670_DAC_L2_MONO_L_VOL_SFT 11
+#define RT5670_M_DAC_R2_MONO_L (0x1 << 10)
+#define RT5670_M_DAC_R2_MONO_L_SFT 10
+#define RT5670_DAC_R2_MONO_L_VOL_MASK (0x1 << 9)
+#define RT5670_DAC_R2_MONO_L_VOL_SFT 9
+#define RT5670_M_DAC_R1_MONO_R (0x1 << 6)
+#define RT5670_M_DAC_R1_MONO_R_SFT 6
+#define RT5670_DAC_R1_MONO_R_VOL_MASK (0x1 << 5)
+#define RT5670_DAC_R1_MONO_R_VOL_SFT 5
+#define RT5670_M_DAC_R2_MONO_R (0x1 << 4)
+#define RT5670_M_DAC_R2_MONO_R_SFT 4
+#define RT5670_DAC_R2_MONO_R_VOL_MASK (0x1 << 3)
+#define RT5670_DAC_R2_MONO_R_VOL_SFT 3
+#define RT5670_M_DAC_L2_MONO_R (0x1 << 2)
+#define RT5670_M_DAC_L2_MONO_R_SFT 2
+#define RT5670_DAC_L2_MONO_R_VOL_MASK (0x1 << 1)
+#define RT5670_DAC_L2_MONO_R_VOL_SFT 1
+
+/* Digital Mixer Control (0x2c) */
+#define RT5670_M_STO_L_DAC_L (0x1 << 15)
+#define RT5670_M_STO_L_DAC_L_SFT 15
+#define RT5670_STO_L_DAC_L_VOL_MASK (0x1 << 14)
+#define RT5670_STO_L_DAC_L_VOL_SFT 14
+#define RT5670_M_DAC_L2_DAC_L (0x1 << 13)
+#define RT5670_M_DAC_L2_DAC_L_SFT 13
+#define RT5670_DAC_L2_DAC_L_VOL_MASK (0x1 << 12)
+#define RT5670_DAC_L2_DAC_L_VOL_SFT 12
+#define RT5670_M_STO_R_DAC_R (0x1 << 11)
+#define RT5670_M_STO_R_DAC_R_SFT 11
+#define RT5670_STO_R_DAC_R_VOL_MASK (0x1 << 10)
+#define RT5670_STO_R_DAC_R_VOL_SFT 10
+#define RT5670_M_DAC_R2_DAC_R (0x1 << 9)
+#define RT5670_M_DAC_R2_DAC_R_SFT 9
+#define RT5670_DAC_R2_DAC_R_VOL_MASK (0x1 << 8)
+#define RT5670_DAC_R2_DAC_R_VOL_SFT 8
+#define RT5670_M_DAC_R2_DAC_L (0x1 << 7)
+#define RT5670_M_DAC_R2_DAC_L_SFT 7
+#define RT5670_DAC_R2_DAC_L_VOL_MASK (0x1 << 6)
+#define RT5670_DAC_R2_DAC_L_VOL_SFT 6
+#define RT5670_M_DAC_L2_DAC_R (0x1 << 5)
+#define RT5670_M_DAC_L2_DAC_R_SFT 5
+#define RT5670_DAC_L2_DAC_R_VOL_MASK (0x1 << 4)
+#define RT5670_DAC_L2_DAC_R_VOL_SFT 4
+
+/* DSP Path Control 1 (0x2d) */
+#define RT5670_RXDP_SEL_MASK (0x7 << 13)
+#define RT5670_RXDP_SEL_SFT 13
+#define RT5670_RXDP_SRC_MASK (0x3 << 11)
+#define RT5670_RXDP_SRC_SFT 11
+#define RT5670_RXDP_SRC_NOR (0x0 << 11)
+#define RT5670_RXDP_SRC_DIV2 (0x1 << 11)
+#define RT5670_RXDP_SRC_DIV3 (0x2 << 11)
+#define RT5670_TXDP_SRC_MASK (0x3 << 4)
+#define RT5670_TXDP_SRC_SFT 4
+#define RT5670_TXDP_SRC_NOR (0x0 << 4)
+#define RT5670_TXDP_SRC_DIV2 (0x1 << 4)
+#define RT5670_TXDP_SRC_DIV3 (0x2 << 4)
+#define RT5670_TXDP_SLOT_SEL_MASK (0x3 << 2)
+#define RT5670_TXDP_SLOT_SEL_SFT 2
+#define RT5670_DSP_UL_SEL (0x1 << 1)
+#define RT5670_DSP_UL_SFT 1
+#define RT5670_DSP_DL_SEL 0x1
+#define RT5670_DSP_DL_SFT 0
+
+/* DSP Path Control 2 (0x2e) */
+#define RT5670_TXDP_L_VOL_MASK (0x7f << 8)
+#define RT5670_TXDP_L_VOL_SFT 8
+#define RT5670_TXDP_R_VOL_MASK (0x7f)
+#define RT5670_TXDP_R_VOL_SFT 0
+
+/* Digital Interface Data Control (0x2f) */
+#define RT5670_IF1_ADC2_IN_SEL (0x1 << 15)
+#define RT5670_IF1_ADC2_IN_SFT 15
+#define RT5670_IF2_ADC_IN_MASK (0x7 << 12)
+#define RT5670_IF2_ADC_IN_SFT 12
+#define RT5670_IF2_DAC_SEL_MASK (0x3 << 10)
+#define RT5670_IF2_DAC_SEL_SFT 10
+#define RT5670_IF2_ADC_SEL_MASK (0x3 << 8)
+#define RT5670_IF2_ADC_SEL_SFT 8
+
+/* Digital Interface Data Control (0x30) */
+#define RT5670_IF4_ADC_IN_MASK (0x3 << 4)
+#define RT5670_IF4_ADC_IN_SFT 4
+
+/* PDM Output Control (0x31) */
+#define RT5670_PDM1_L_MASK (0x1 << 15)
+#define RT5670_PDM1_L_SFT 15
+#define RT5670_M_PDM1_L (0x1 << 14)
+#define RT5670_M_PDM1_L_SFT 14
+#define RT5670_PDM1_R_MASK (0x1 << 13)
+#define RT5670_PDM1_R_SFT 13
+#define RT5670_M_PDM1_R (0x1 << 12)
+#define RT5670_M_PDM1_R_SFT 12
+#define RT5670_PDM2_L_MASK (0x1 << 11)
+#define RT5670_PDM2_L_SFT 11
+#define RT5670_M_PDM2_L (0x1 << 10)
+#define RT5670_M_PDM2_L_SFT 10
+#define RT5670_PDM2_R_MASK (0x1 << 9)
+#define RT5670_PDM2_R_SFT 9
+#define RT5670_M_PDM2_R (0x1 << 8)
+#define RT5670_M_PDM2_R_SFT 8
+#define RT5670_PDM2_BUSY (0x1 << 7)
+#define RT5670_PDM1_BUSY (0x1 << 6)
+#define RT5670_PDM_PATTERN (0x1 << 5)
+#define RT5670_PDM_GAIN (0x1 << 4)
+#define RT5670_PDM_DIV_MASK (0x3)
+
+/* REC Left Mixer Control 1 (0x3b) */
+#define RT5670_G_HP_L_RM_L_MASK (0x7 << 13)
+#define RT5670_G_HP_L_RM_L_SFT 13
+#define RT5670_G_IN_L_RM_L_MASK (0x7 << 10)
+#define RT5670_G_IN_L_RM_L_SFT 10
+#define RT5670_G_BST4_RM_L_MASK (0x7 << 7)
+#define RT5670_G_BST4_RM_L_SFT 7
+#define RT5670_G_BST3_RM_L_MASK (0x7 << 4)
+#define RT5670_G_BST3_RM_L_SFT 4
+#define RT5670_G_BST2_RM_L_MASK (0x7 << 1)
+#define RT5670_G_BST2_RM_L_SFT 1
+
+/* REC Left Mixer Control 2 (0x3c) */
+#define RT5670_G_BST1_RM_L_MASK (0x7 << 13)
+#define RT5670_G_BST1_RM_L_SFT 13
+#define RT5670_M_IN_L_RM_L (0x1 << 5)
+#define RT5670_M_IN_L_RM_L_SFT 5
+#define RT5670_M_BST2_RM_L (0x1 << 3)
+#define RT5670_M_BST2_RM_L_SFT 3
+#define RT5670_M_BST1_RM_L (0x1 << 1)
+#define RT5670_M_BST1_RM_L_SFT 1
+
+/* REC Right Mixer Control 1 (0x3d) */
+#define RT5670_G_HP_R_RM_R_MASK (0x7 << 13)
+#define RT5670_G_HP_R_RM_R_SFT 13
+#define RT5670_G_IN_R_RM_R_MASK (0x7 << 10)
+#define RT5670_G_IN_R_RM_R_SFT 10
+#define RT5670_G_BST4_RM_R_MASK (0x7 << 7)
+#define RT5670_G_BST4_RM_R_SFT 7
+#define RT5670_G_BST3_RM_R_MASK (0x7 << 4)
+#define RT5670_G_BST3_RM_R_SFT 4
+#define RT5670_G_BST2_RM_R_MASK (0x7 << 1)
+#define RT5670_G_BST2_RM_R_SFT 1
+
+/* REC Right Mixer Control 2 (0x3e) */
+#define RT5670_G_BST1_RM_R_MASK (0x7 << 13)
+#define RT5670_G_BST1_RM_R_SFT 13
+#define RT5670_M_IN_R_RM_R (0x1 << 5)
+#define RT5670_M_IN_R_RM_R_SFT 5
+#define RT5670_M_BST2_RM_R (0x1 << 3)
+#define RT5670_M_BST2_RM_R_SFT 3
+#define RT5670_M_BST1_RM_R (0x1 << 1)
+#define RT5670_M_BST1_RM_R_SFT 1
+
+/* HPMIX Control (0x45) */
+#define RT5670_M_DAC2_HM (0x1 << 15)
+#define RT5670_M_DAC2_HM_SFT 15
+#define RT5670_M_HPVOL_HM (0x1 << 14)
+#define RT5670_M_HPVOL_HM_SFT 14
+#define RT5670_M_DAC1_HM (0x1 << 13)
+#define RT5670_M_DAC1_HM_SFT 13
+#define RT5670_G_HPOMIX_MASK (0x1 << 12)
+#define RT5670_G_HPOMIX_SFT 12
+#define RT5670_M_INR1_HMR (0x1 << 3)
+#define RT5670_M_INR1_HMR_SFT 3
+#define RT5670_M_DACR1_HMR (0x1 << 2)
+#define RT5670_M_DACR1_HMR_SFT 2
+#define RT5670_M_INL1_HML (0x1 << 1)
+#define RT5670_M_INL1_HML_SFT 1
+#define RT5670_M_DACL1_HML (0x1)
+#define RT5670_M_DACL1_HML_SFT 0
+
+/* Mono Output Mixer Control (0x4c) */
+#define RT5670_M_DAC_R2_MA (0x1 << 15)
+#define RT5670_M_DAC_R2_MA_SFT 15
+#define RT5670_M_DAC_L2_MA (0x1 << 14)
+#define RT5670_M_DAC_L2_MA_SFT 14
+#define RT5670_M_OV_R_MM (0x1 << 13)
+#define RT5670_M_OV_R_MM_SFT 13
+#define RT5670_M_OV_L_MM (0x1 << 12)
+#define RT5670_M_OV_L_MM_SFT 12
+#define RT5670_G_MONOMIX_MASK (0x1 << 10)
+#define RT5670_G_MONOMIX_SFT 10
+#define RT5670_M_DAC_R2_MM (0x1 << 9)
+#define RT5670_M_DAC_R2_MM_SFT 9
+#define RT5670_M_DAC_L2_MM (0x1 << 8)
+#define RT5670_M_DAC_L2_MM_SFT 8
+#define RT5670_M_BST4_MM (0x1 << 7)
+#define RT5670_M_BST4_MM_SFT 7
+
+/* Output Left Mixer Control 1 (0x4d) */
+#define RT5670_G_BST3_OM_L_MASK (0x7 << 13)
+#define RT5670_G_BST3_OM_L_SFT 13
+#define RT5670_G_BST2_OM_L_MASK (0x7 << 10)
+#define RT5670_G_BST2_OM_L_SFT 10
+#define RT5670_G_BST1_OM_L_MASK (0x7 << 7)
+#define RT5670_G_BST1_OM_L_SFT 7
+#define RT5670_G_IN_L_OM_L_MASK (0x7 << 4)
+#define RT5670_G_IN_L_OM_L_SFT 4
+#define RT5670_G_RM_L_OM_L_MASK (0x7 << 1)
+#define RT5670_G_RM_L_OM_L_SFT 1
+
+/* Output Left Mixer Control 2 (0x4e) */
+#define RT5670_G_DAC_R2_OM_L_MASK (0x7 << 13)
+#define RT5670_G_DAC_R2_OM_L_SFT 13
+#define RT5670_G_DAC_L2_OM_L_MASK (0x7 << 10)
+#define RT5670_G_DAC_L2_OM_L_SFT 10
+#define RT5670_G_DAC_L1_OM_L_MASK (0x7 << 7)
+#define RT5670_G_DAC_L1_OM_L_SFT 7
+
+/* Output Left Mixer Control 3 (0x4f) */
+#define RT5670_M_BST1_OM_L (0x1 << 5)
+#define RT5670_M_BST1_OM_L_SFT 5
+#define RT5670_M_IN_L_OM_L (0x1 << 4)
+#define RT5670_M_IN_L_OM_L_SFT 4
+#define RT5670_M_DAC_L2_OM_L (0x1 << 1)
+#define RT5670_M_DAC_L2_OM_L_SFT 1
+#define RT5670_M_DAC_L1_OM_L (0x1)
+#define RT5670_M_DAC_L1_OM_L_SFT 0
+
+/* Output Right Mixer Control 1 (0x50) */
+#define RT5670_G_BST4_OM_R_MASK (0x7 << 13)
+#define RT5670_G_BST4_OM_R_SFT 13
+#define RT5670_G_BST2_OM_R_MASK (0x7 << 10)
+#define RT5670_G_BST2_OM_R_SFT 10
+#define RT5670_G_BST1_OM_R_MASK (0x7 << 7)
+#define RT5670_G_BST1_OM_R_SFT 7
+#define RT5670_G_IN_R_OM_R_MASK (0x7 << 4)
+#define RT5670_G_IN_R_OM_R_SFT 4
+#define RT5670_G_RM_R_OM_R_MASK (0x7 << 1)
+#define RT5670_G_RM_R_OM_R_SFT 1
+
+/* Output Right Mixer Control 2 (0x51) */
+#define RT5670_G_DAC_L2_OM_R_MASK (0x7 << 13)
+#define RT5670_G_DAC_L2_OM_R_SFT 13
+#define RT5670_G_DAC_R2_OM_R_MASK (0x7 << 10)
+#define RT5670_G_DAC_R2_OM_R_SFT 10
+#define RT5670_G_DAC_R1_OM_R_MASK (0x7 << 7)
+#define RT5670_G_DAC_R1_OM_R_SFT 7
+
+/* Output Right Mixer Control 3 (0x52) */
+#define RT5670_M_BST2_OM_R (0x1 << 6)
+#define RT5670_M_BST2_OM_R_SFT 6
+#define RT5670_M_IN_R_OM_R (0x1 << 4)
+#define RT5670_M_IN_R_OM_R_SFT 4
+#define RT5670_M_DAC_R2_OM_R (0x1 << 1)
+#define RT5670_M_DAC_R2_OM_R_SFT 1
+#define RT5670_M_DAC_R1_OM_R (0x1)
+#define RT5670_M_DAC_R1_OM_R_SFT 0
+
+/* LOUT Mixer Control (0x53) */
+#define RT5670_M_DAC_L1_LM (0x1 << 15)
+#define RT5670_M_DAC_L1_LM_SFT 15
+#define RT5670_M_DAC_R1_LM (0x1 << 14)
+#define RT5670_M_DAC_R1_LM_SFT 14
+#define RT5670_M_OV_L_LM (0x1 << 13)
+#define RT5670_M_OV_L_LM_SFT 13
+#define RT5670_M_OV_R_LM (0x1 << 12)
+#define RT5670_M_OV_R_LM_SFT 12
+#define RT5670_G_LOUTMIX_MASK (0x1 << 11)
+#define RT5670_G_LOUTMIX_SFT 11
+
+/* Power Management for Digital 1 (0x61) */
+#define RT5670_PWR_I2S1 (0x1 << 15)
+#define RT5670_PWR_I2S1_BIT 15
+#define RT5670_PWR_I2S2 (0x1 << 14)
+#define RT5670_PWR_I2S2_BIT 14
+#define RT5670_PWR_DAC_L1 (0x1 << 12)
+#define RT5670_PWR_DAC_L1_BIT 12
+#define RT5670_PWR_DAC_R1 (0x1 << 11)
+#define RT5670_PWR_DAC_R1_BIT 11
+#define RT5670_PWR_DAC_L2 (0x1 << 7)
+#define RT5670_PWR_DAC_L2_BIT 7
+#define RT5670_PWR_DAC_R2 (0x1 << 6)
+#define RT5670_PWR_DAC_R2_BIT 6
+#define RT5670_PWR_ADC_L (0x1 << 2)
+#define RT5670_PWR_ADC_L_BIT 2
+#define RT5670_PWR_ADC_R (0x1 << 1)
+#define RT5670_PWR_ADC_R_BIT 1
+#define RT5670_PWR_CLS_D (0x1)
+#define RT5670_PWR_CLS_D_BIT 0
+
+/* Power Management for Digital 2 (0x62) */
+#define RT5670_PWR_ADC_S1F (0x1 << 15)
+#define RT5670_PWR_ADC_S1F_BIT 15
+#define RT5670_PWR_ADC_MF_L (0x1 << 14)
+#define RT5670_PWR_ADC_MF_L_BIT 14
+#define RT5670_PWR_ADC_MF_R (0x1 << 13)
+#define RT5670_PWR_ADC_MF_R_BIT 13
+#define RT5670_PWR_I2S_DSP (0x1 << 12)
+#define RT5670_PWR_I2S_DSP_BIT 12
+#define RT5670_PWR_DAC_S1F (0x1 << 11)
+#define RT5670_PWR_DAC_S1F_BIT 11
+#define RT5670_PWR_DAC_MF_L (0x1 << 10)
+#define RT5670_PWR_DAC_MF_L_BIT 10
+#define RT5670_PWR_DAC_MF_R (0x1 << 9)
+#define RT5670_PWR_DAC_MF_R_BIT 9
+#define RT5670_PWR_ADC_S2F (0x1 << 8)
+#define RT5670_PWR_ADC_S2F_BIT 8
+#define RT5670_PWR_PDM1 (0x1 << 7)
+#define RT5670_PWR_PDM1_BIT 7
+#define RT5670_PWR_PDM2 (0x1 << 6)
+#define RT5670_PWR_PDM2_BIT 6
+
+/* Power Management for Analog 1 (0x63) */
+#define RT5670_PWR_VREF1 (0x1 << 15)
+#define RT5670_PWR_VREF1_BIT 15
+#define RT5670_PWR_FV1 (0x1 << 14)
+#define RT5670_PWR_FV1_BIT 14
+#define RT5670_PWR_MB (0x1 << 13)
+#define RT5670_PWR_MB_BIT 13
+#define RT5670_PWR_LM (0x1 << 12)
+#define RT5670_PWR_LM_BIT 12
+#define RT5670_PWR_BG (0x1 << 11)
+#define RT5670_PWR_BG_BIT 11
+#define RT5670_PWR_HP_L (0x1 << 7)
+#define RT5670_PWR_HP_L_BIT 7
+#define RT5670_PWR_HP_R (0x1 << 6)
+#define RT5670_PWR_HP_R_BIT 6
+#define RT5670_PWR_HA (0x1 << 5)
+#define RT5670_PWR_HA_BIT 5
+#define RT5670_PWR_VREF2 (0x1 << 4)
+#define RT5670_PWR_VREF2_BIT 4
+#define RT5670_PWR_FV2 (0x1 << 3)
+#define RT5670_PWR_FV2_BIT 3
+#define RT5670_LDO_SEL_MASK (0x3)
+#define RT5670_LDO_SEL_SFT 0
+
+/* Power Management for Analog 2 (0x64) */
+#define RT5670_PWR_BST1 (0x1 << 15)
+#define RT5670_PWR_BST1_BIT 15
+#define RT5670_PWR_BST2 (0x1 << 13)
+#define RT5670_PWR_BST2_BIT 13
+#define RT5670_PWR_MB1 (0x1 << 11)
+#define RT5670_PWR_MB1_BIT 11
+#define RT5670_PWR_MB2 (0x1 << 10)
+#define RT5670_PWR_MB2_BIT 10
+#define RT5670_PWR_PLL (0x1 << 9)
+#define RT5670_PWR_PLL_BIT 9
+#define RT5670_PWR_BST1_P (0x1 << 6)
+#define RT5670_PWR_BST1_P_BIT 6
+#define RT5670_PWR_BST2_P (0x1 << 4)
+#define RT5670_PWR_BST2_P_BIT 4
+#define RT5670_PWR_JD1 (0x1 << 2)
+#define RT5670_PWR_JD1_BIT 2
+#define RT5670_PWR_JD (0x1 << 1)
+#define RT5670_PWR_JD_BIT 1
+
+/* Power Management for Mixer (0x65) */
+#define RT5670_PWR_OM_L (0x1 << 15)
+#define RT5670_PWR_OM_L_BIT 15
+#define RT5670_PWR_OM_R (0x1 << 14)
+#define RT5670_PWR_OM_R_BIT 14
+#define RT5670_PWR_RM_L (0x1 << 11)
+#define RT5670_PWR_RM_L_BIT 11
+#define RT5670_PWR_RM_R (0x1 << 10)
+#define RT5670_PWR_RM_R_BIT 10
+
+/* Power Management for Volume (0x66) */
+#define RT5670_PWR_HV_L (0x1 << 11)
+#define RT5670_PWR_HV_L_BIT 11
+#define RT5670_PWR_HV_R (0x1 << 10)
+#define RT5670_PWR_HV_R_BIT 10
+#define RT5670_PWR_IN_L (0x1 << 9)
+#define RT5670_PWR_IN_L_BIT 9
+#define RT5670_PWR_IN_R (0x1 << 8)
+#define RT5670_PWR_IN_R_BIT 8
+#define RT5670_PWR_MIC_DET (0x1 << 5)
+#define RT5670_PWR_MIC_DET_BIT 5
+
+/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71 0x72) */
+#define RT5670_I2S_MS_MASK (0x1 << 15)
+#define RT5670_I2S_MS_SFT 15
+#define RT5670_I2S_MS_M (0x0 << 15)
+#define RT5670_I2S_MS_S (0x1 << 15)
+#define RT5670_I2S_IF_MASK (0x7 << 12)
+#define RT5670_I2S_IF_SFT 12
+#define RT5670_I2S_O_CP_MASK (0x3 << 10)
+#define RT5670_I2S_O_CP_SFT 10
+#define RT5670_I2S_O_CP_OFF (0x0 << 10)
+#define RT5670_I2S_O_CP_U_LAW (0x1 << 10)
+#define RT5670_I2S_O_CP_A_LAW (0x2 << 10)
+#define RT5670_I2S_I_CP_MASK (0x3 << 8)
+#define RT5670_I2S_I_CP_SFT 8
+#define RT5670_I2S_I_CP_OFF (0x0 << 8)
+#define RT5670_I2S_I_CP_U_LAW (0x1 << 8)
+#define RT5670_I2S_I_CP_A_LAW (0x2 << 8)
+#define RT5670_I2S_BP_MASK (0x1 << 7)
+#define RT5670_I2S_BP_SFT 7
+#define RT5670_I2S_BP_NOR (0x0 << 7)
+#define RT5670_I2S_BP_INV (0x1 << 7)
+#define RT5670_I2S_DL_MASK (0x3 << 2)
+#define RT5670_I2S_DL_SFT 2
+#define RT5670_I2S_DL_16 (0x0 << 2)
+#define RT5670_I2S_DL_20 (0x1 << 2)
+#define RT5670_I2S_DL_24 (0x2 << 2)
+#define RT5670_I2S_DL_8 (0x3 << 2)
+#define RT5670_I2S_DF_MASK (0x3)
+#define RT5670_I2S_DF_SFT 0
+#define RT5670_I2S_DF_I2S (0x0)
+#define RT5670_I2S_DF_LEFT (0x1)
+#define RT5670_I2S_DF_PCM_A (0x2)
+#define RT5670_I2S_DF_PCM_B (0x3)
+
+/* I2S2 Audio Serial Data Port Control (0x71) */
+#define RT5670_I2S2_SDI_MASK (0x1 << 6)
+#define RT5670_I2S2_SDI_SFT 6
+#define RT5670_I2S2_SDI_I2S1 (0x0 << 6)
+#define RT5670_I2S2_SDI_I2S2 (0x1 << 6)
+
+/* ADC/DAC Clock Control 1 (0x73) */
+#define RT5670_I2S_BCLK_MS1_MASK (0x1 << 15)
+#define RT5670_I2S_BCLK_MS1_SFT 15
+#define RT5670_I2S_BCLK_MS1_32 (0x0 << 15)
+#define RT5670_I2S_BCLK_MS1_64 (0x1 << 15)
+#define RT5670_I2S_PD1_MASK (0x7 << 12)
+#define RT5670_I2S_PD1_SFT 12
+#define RT5670_I2S_PD1_1 (0x0 << 12)
+#define RT5670_I2S_PD1_2 (0x1 << 12)
+#define RT5670_I2S_PD1_3 (0x2 << 12)
+#define RT5670_I2S_PD1_4 (0x3 << 12)
+#define RT5670_I2S_PD1_6 (0x4 << 12)
+#define RT5670_I2S_PD1_8 (0x5 << 12)
+#define RT5670_I2S_PD1_12 (0x6 << 12)
+#define RT5670_I2S_PD1_16 (0x7 << 12)
+#define RT5670_I2S_BCLK_MS2_MASK (0x1 << 11)
+#define RT5670_I2S_BCLK_MS2_SFT 11
+#define RT5670_I2S_BCLK_MS2_32 (0x0 << 11)
+#define RT5670_I2S_BCLK_MS2_64 (0x1 << 11)
+#define RT5670_I2S_PD2_MASK (0x7 << 8)
+#define RT5670_I2S_PD2_SFT 8
+#define RT5670_I2S_PD2_1 (0x0 << 8)
+#define RT5670_I2S_PD2_2 (0x1 << 8)
+#define RT5670_I2S_PD2_3 (0x2 << 8)
+#define RT5670_I2S_PD2_4 (0x3 << 8)
+#define RT5670_I2S_PD2_6 (0x4 << 8)
+#define RT5670_I2S_PD2_8 (0x5 << 8)
+#define RT5670_I2S_PD2_12 (0x6 << 8)
+#define RT5670_I2S_PD2_16 (0x7 << 8)
+#define RT5670_I2S_BCLK_MS3_MASK (0x1 << 7)
+#define RT5670_I2S_BCLK_MS3_SFT 7
+#define RT5670_I2S_BCLK_MS3_32 (0x0 << 7)
+#define RT5670_I2S_BCLK_MS3_64 (0x1 << 7)
+#define RT5670_I2S_PD3_MASK (0x7 << 4)
+#define RT5670_I2S_PD3_SFT 4
+#define RT5670_I2S_PD3_1 (0x0 << 4)
+#define RT5670_I2S_PD3_2 (0x1 << 4)
+#define RT5670_I2S_PD3_3 (0x2 << 4)
+#define RT5670_I2S_PD3_4 (0x3 << 4)
+#define RT5670_I2S_PD3_6 (0x4 << 4)
+#define RT5670_I2S_PD3_8 (0x5 << 4)
+#define RT5670_I2S_PD3_12 (0x6 << 4)
+#define RT5670_I2S_PD3_16 (0x7 << 4)
+#define RT5670_DAC_OSR_MASK (0x3 << 2)
+#define RT5670_DAC_OSR_SFT 2
+#define RT5670_DAC_OSR_128 (0x0 << 2)
+#define RT5670_DAC_OSR_64 (0x1 << 2)
+#define RT5670_DAC_OSR_32 (0x2 << 2)
+#define RT5670_DAC_OSR_16 (0x3 << 2)
+#define RT5670_ADC_OSR_MASK (0x3)
+#define RT5670_ADC_OSR_SFT 0
+#define RT5670_ADC_OSR_128 (0x0)
+#define RT5670_ADC_OSR_64 (0x1)
+#define RT5670_ADC_OSR_32 (0x2)
+#define RT5670_ADC_OSR_16 (0x3)
+
+/* ADC/DAC Clock Control 2 (0x74) */
+#define RT5670_DAC_L_OSR_MASK (0x3 << 14)
+#define RT5670_DAC_L_OSR_SFT 14
+#define RT5670_DAC_L_OSR_128 (0x0 << 14)
+#define RT5670_DAC_L_OSR_64 (0x1 << 14)
+#define RT5670_DAC_L_OSR_32 (0x2 << 14)
+#define RT5670_DAC_L_OSR_16 (0x3 << 14)
+#define RT5670_ADC_R_OSR_MASK (0x3 << 12)
+#define RT5670_ADC_R_OSR_SFT 12
+#define RT5670_ADC_R_OSR_128 (0x0 << 12)
+#define RT5670_ADC_R_OSR_64 (0x1 << 12)
+#define RT5670_ADC_R_OSR_32 (0x2 << 12)
+#define RT5670_ADC_R_OSR_16 (0x3 << 12)
+#define RT5670_DAHPF_EN (0x1 << 11)
+#define RT5670_DAHPF_EN_SFT 11
+#define RT5670_ADHPF_EN (0x1 << 10)
+#define RT5670_ADHPF_EN_SFT 10
+
+/* Digital Microphone Control (0x75) */
+#define RT5670_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5670_DMIC_1_EN_SFT 15
+#define RT5670_DMIC_1_DIS (0x0 << 15)
+#define RT5670_DMIC_1_EN (0x1 << 15)
+#define RT5670_DMIC_2_EN_MASK (0x1 << 14)
+#define RT5670_DMIC_2_EN_SFT 14
+#define RT5670_DMIC_2_DIS (0x0 << 14)
+#define RT5670_DMIC_2_EN (0x1 << 14)
+#define RT5670_DMIC_1L_LH_MASK (0x1 << 13)
+#define RT5670_DMIC_1L_LH_SFT 13
+#define RT5670_DMIC_1L_LH_FALLING (0x0 << 13)
+#define RT5670_DMIC_1L_LH_RISING (0x1 << 13)
+#define RT5670_DMIC_1R_LH_MASK (0x1 << 12)
+#define RT5670_DMIC_1R_LH_SFT 12
+#define RT5670_DMIC_1R_LH_FALLING (0x0 << 12)
+#define RT5670_DMIC_1R_LH_RISING (0x1 << 12)
+#define RT5670_DMIC_2_DP_MASK (0x1 << 10)
+#define RT5670_DMIC_2_DP_SFT 10
+#define RT5670_DMIC_2_DP_GPIO8 (0x0 << 10)
+#define RT5670_DMIC_2_DP_IN3N (0x1 << 10)
+#define RT5670_DMIC_2L_LH_MASK (0x1 << 9)
+#define RT5670_DMIC_2L_LH_SFT 9
+#define RT5670_DMIC_2L_LH_FALLING (0x0 << 9)
+#define RT5670_DMIC_2L_LH_RISING (0x1 << 9)
+#define RT5670_DMIC_2R_LH_MASK (0x1 << 8)
+#define RT5670_DMIC_2R_LH_SFT 8
+#define RT5670_DMIC_2R_LH_FALLING (0x0 << 8)
+#define RT5670_DMIC_2R_LH_RISING (0x1 << 8)
+#define RT5670_DMIC_CLK_MASK (0x7 << 5)
+#define RT5670_DMIC_CLK_SFT 5
+#define RT5670_DMIC_3_EN_MASK (0x1 << 4)
+#define RT5670_DMIC_3_EN_SFT 4
+#define RT5670_DMIC_3_DIS (0x0 << 4)
+#define RT5670_DMIC_3_EN (0x1 << 4)
+#define RT5670_DMIC_1_DP_MASK (0x3 << 0)
+#define RT5670_DMIC_1_DP_SFT 0
+#define RT5670_DMIC_1_DP_GPIO6 (0x0 << 0)
+#define RT5670_DMIC_1_DP_IN2P (0x1 << 0)
+#define RT5670_DMIC_1_DP_GPIO7 (0x2 << 0)
+
+/* Digital Microphone Control2 (0x76) */
+#define RT5670_DMIC_3_DP_MASK (0x3 << 6)
+#define RT5670_DMIC_3_DP_SFT 6
+#define RT5670_DMIC_3_DP_GPIO9 (0x0 << 6)
+#define RT5670_DMIC_3_DP_GPIO10 (0x1 << 6)
+#define RT5670_DMIC_3_DP_GPIO5 (0x2 << 6)
+
+/* Global Clock Control (0x80) */
+#define RT5670_SCLK_SRC_MASK (0x3 << 14)
+#define RT5670_SCLK_SRC_SFT 14
+#define RT5670_SCLK_SRC_MCLK (0x0 << 14)
+#define RT5670_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5670_SCLK_SRC_RCCLK (0x2 << 14) /* 15MHz */
+#define RT5670_PLL1_SRC_MASK (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_PD_MASK (0x1 << 3)
+#define RT5670_PLL1_PD_SFT 3
+#define RT5670_PLL1_PD_1 (0x0 << 3)
+#define RT5670_PLL1_PD_2 (0x1 << 3)
+
+#define RT5670_PLL_INP_MAX 40000000
+#define RT5670_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x81) */
+#define RT5670_PLL_N_MAX 0x1ff
+#define RT5670_PLL_N_MASK (RT5670_PLL_N_MAX << 7)
+#define RT5670_PLL_N_SFT 7
+#define RT5670_PLL_K_MAX 0x1f
+#define RT5670_PLL_K_MASK (RT5670_PLL_K_MAX)
+#define RT5670_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x82) */
+#define RT5670_PLL_M_MAX 0xf
+#define RT5670_PLL_M_MASK (RT5670_PLL_M_MAX << 12)
+#define RT5670_PLL_M_SFT 12
+#define RT5670_PLL_M_BP (0x1 << 11)
+#define RT5670_PLL_M_BP_SFT 11
+
+/* ASRC Control 1 (0x83) */
+#define RT5670_STO_T_MASK (0x1 << 15)
+#define RT5670_STO_T_SFT 15
+#define RT5670_STO_T_SCLK (0x0 << 15)
+#define RT5670_STO_T_LRCK1 (0x1 << 15)
+#define RT5670_M1_T_MASK (0x1 << 14)
+#define RT5670_M1_T_SFT 14
+#define RT5670_M1_T_I2S2 (0x0 << 14)
+#define RT5670_M1_T_I2S2_D3 (0x1 << 14)
+#define RT5670_I2S2_F_MASK (0x1 << 12)
+#define RT5670_I2S2_F_SFT 12
+#define RT5670_I2S2_F_I2S2_D2 (0x0 << 12)
+#define RT5670_I2S2_F_I2S1_TCLK (0x1 << 12)
+#define RT5670_DMIC_1_M_MASK (0x1 << 9)
+#define RT5670_DMIC_1_M_SFT 9
+#define RT5670_DMIC_1_M_NOR (0x0 << 9)
+#define RT5670_DMIC_1_M_ASYN (0x1 << 9)
+#define RT5670_DMIC_2_M_MASK (0x1 << 8)
+#define RT5670_DMIC_2_M_SFT 8
+#define RT5670_DMIC_2_M_NOR (0x0 << 8)
+#define RT5670_DMIC_2_M_ASYN (0x1 << 8)
+
+/* ASRC clock source selection (0x84, 0x85) */
+#define RT5670_CLK_SEL_SYS (0x0)
+#define RT5670_CLK_SEL_I2S1_ASRC (0x1)
+#define RT5670_CLK_SEL_I2S2_ASRC (0x2)
+#define RT5670_CLK_SEL_I2S3_ASRC (0x3)
+#define RT5670_CLK_SEL_SYS2 (0x5)
+#define RT5670_CLK_SEL_SYS3 (0x6)
+
+/* ASRC Control 2 (0x84) */
+#define RT5670_DA_STO_CLK_SEL_MASK (0xf << 12)
+#define RT5670_DA_STO_CLK_SEL_SFT 12
+#define RT5670_DA_MONOL_CLK_SEL_MASK (0xf << 8)
+#define RT5670_DA_MONOL_CLK_SEL_SFT 8
+#define RT5670_DA_MONOR_CLK_SEL_MASK (0xf << 4)
+#define RT5670_DA_MONOR_CLK_SEL_SFT 4
+#define RT5670_AD_STO1_CLK_SEL_MASK (0xf << 0)
+#define RT5670_AD_STO1_CLK_SEL_SFT 0
+
+/* ASRC Control 3 (0x85) */
+#define RT5670_UP_CLK_SEL_MASK (0xf << 12)
+#define RT5670_UP_CLK_SEL_SFT 12
+#define RT5670_DOWN_CLK_SEL_MASK (0xf << 8)
+#define RT5670_DOWN_CLK_SEL_SFT 8
+#define RT5670_AD_MONOL_CLK_SEL_MASK (0xf << 4)
+#define RT5670_AD_MONOL_CLK_SEL_SFT 4
+#define RT5670_AD_MONOR_CLK_SEL_MASK (0xf << 0)
+#define RT5670_AD_MONOR_CLK_SEL_SFT 0
+
+/* ASRC Control 4 (0x89) */
+#define RT5670_I2S1_PD_MASK (0x7 << 12)
+#define RT5670_I2S1_PD_SFT 12
+#define RT5670_I2S2_PD_MASK (0x7 << 8)
+#define RT5670_I2S2_PD_SFT 8
+
+/* HPOUT Over Current Detection (0x8b) */
+#define RT5670_HP_OVCD_MASK (0x1 << 10)
+#define RT5670_HP_OVCD_SFT 10
+#define RT5670_HP_OVCD_DIS (0x0 << 10)
+#define RT5670_HP_OVCD_EN (0x1 << 10)
+#define RT5670_HP_OC_TH_MASK (0x3 << 8)
+#define RT5670_HP_OC_TH_SFT 8
+#define RT5670_HP_OC_TH_90 (0x0 << 8)
+#define RT5670_HP_OC_TH_105 (0x1 << 8)
+#define RT5670_HP_OC_TH_120 (0x2 << 8)
+#define RT5670_HP_OC_TH_135 (0x3 << 8)
+
+/* Class D Over Current Control (0x8c) */
+#define RT5670_CLSD_OC_MASK (0x1 << 9)
+#define RT5670_CLSD_OC_SFT 9
+#define RT5670_CLSD_OC_PU (0x0 << 9)
+#define RT5670_CLSD_OC_PD (0x1 << 9)
+#define RT5670_AUTO_PD_MASK (0x1 << 8)
+#define RT5670_AUTO_PD_SFT 8
+#define RT5670_AUTO_PD_DIS (0x0 << 8)
+#define RT5670_AUTO_PD_EN (0x1 << 8)
+#define RT5670_CLSD_OC_TH_MASK (0x3f)
+#define RT5670_CLSD_OC_TH_SFT 0
+
+/* Class D Output Control (0x8d) */
+#define RT5670_CLSD_RATIO_MASK (0xf << 12)
+#define RT5670_CLSD_RATIO_SFT 12
+#define RT5670_CLSD_OM_MASK (0x1 << 11)
+#define RT5670_CLSD_OM_SFT 11
+#define RT5670_CLSD_OM_MONO (0x0 << 11)
+#define RT5670_CLSD_OM_STO (0x1 << 11)
+#define RT5670_CLSD_SCH_MASK (0x1 << 10)
+#define RT5670_CLSD_SCH_SFT 10
+#define RT5670_CLSD_SCH_L (0x0 << 10)
+#define RT5670_CLSD_SCH_S (0x1 << 10)
+
+/* Depop Mode Control 1 (0x8e) */
+#define RT5670_SMT_TRIG_MASK (0x1 << 15)
+#define RT5670_SMT_TRIG_SFT 15
+#define RT5670_SMT_TRIG_DIS (0x0 << 15)
+#define RT5670_SMT_TRIG_EN (0x1 << 15)
+#define RT5670_HP_L_SMT_MASK (0x1 << 9)
+#define RT5670_HP_L_SMT_SFT 9
+#define RT5670_HP_L_SMT_DIS (0x0 << 9)
+#define RT5670_HP_L_SMT_EN (0x1 << 9)
+#define RT5670_HP_R_SMT_MASK (0x1 << 8)
+#define RT5670_HP_R_SMT_SFT 8
+#define RT5670_HP_R_SMT_DIS (0x0 << 8)
+#define RT5670_HP_R_SMT_EN (0x1 << 8)
+#define RT5670_HP_CD_PD_MASK (0x1 << 7)
+#define RT5670_HP_CD_PD_SFT 7
+#define RT5670_HP_CD_PD_DIS (0x0 << 7)
+#define RT5670_HP_CD_PD_EN (0x1 << 7)
+#define RT5670_RSTN_MASK (0x1 << 6)
+#define RT5670_RSTN_SFT 6
+#define RT5670_RSTN_DIS (0x0 << 6)
+#define RT5670_RSTN_EN (0x1 << 6)
+#define RT5670_RSTP_MASK (0x1 << 5)
+#define RT5670_RSTP_SFT 5
+#define RT5670_RSTP_DIS (0x0 << 5)
+#define RT5670_RSTP_EN (0x1 << 5)
+#define RT5670_HP_CO_MASK (0x1 << 4)
+#define RT5670_HP_CO_SFT 4
+#define RT5670_HP_CO_DIS (0x0 << 4)
+#define RT5670_HP_CO_EN (0x1 << 4)
+#define RT5670_HP_CP_MASK (0x1 << 3)
+#define RT5670_HP_CP_SFT 3
+#define RT5670_HP_CP_PD (0x0 << 3)
+#define RT5670_HP_CP_PU (0x1 << 3)
+#define RT5670_HP_SG_MASK (0x1 << 2)
+#define RT5670_HP_SG_SFT 2
+#define RT5670_HP_SG_DIS (0x0 << 2)
+#define RT5670_HP_SG_EN (0x1 << 2)
+#define RT5670_HP_DP_MASK (0x1 << 1)
+#define RT5670_HP_DP_SFT 1
+#define RT5670_HP_DP_PD (0x0 << 1)
+#define RT5670_HP_DP_PU (0x1 << 1)
+#define RT5670_HP_CB_MASK (0x1)
+#define RT5670_HP_CB_SFT 0
+#define RT5670_HP_CB_PD (0x0)
+#define RT5670_HP_CB_PU (0x1)
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5670_DEPOP_MASK (0x1 << 13)
+#define RT5670_DEPOP_SFT 13
+#define RT5670_DEPOP_AUTO (0x0 << 13)
+#define RT5670_DEPOP_MAN (0x1 << 13)
+#define RT5670_RAMP_MASK (0x1 << 12)
+#define RT5670_RAMP_SFT 12
+#define RT5670_RAMP_DIS (0x0 << 12)
+#define RT5670_RAMP_EN (0x1 << 12)
+#define RT5670_BPS_MASK (0x1 << 11)
+#define RT5670_BPS_SFT 11
+#define RT5670_BPS_DIS (0x0 << 11)
+#define RT5670_BPS_EN (0x1 << 11)
+#define RT5670_FAST_UPDN_MASK (0x1 << 10)
+#define RT5670_FAST_UPDN_SFT 10
+#define RT5670_FAST_UPDN_DIS (0x0 << 10)
+#define RT5670_FAST_UPDN_EN (0x1 << 10)
+#define RT5670_MRES_MASK (0x3 << 8)
+#define RT5670_MRES_SFT 8
+#define RT5670_MRES_15MO (0x0 << 8)
+#define RT5670_MRES_25MO (0x1 << 8)
+#define RT5670_MRES_35MO (0x2 << 8)
+#define RT5670_MRES_45MO (0x3 << 8)
+#define RT5670_VLO_MASK (0x1 << 7)
+#define RT5670_VLO_SFT 7
+#define RT5670_VLO_3V (0x0 << 7)
+#define RT5670_VLO_32V (0x1 << 7)
+#define RT5670_DIG_DP_MASK (0x1 << 6)
+#define RT5670_DIG_DP_SFT 6
+#define RT5670_DIG_DP_DIS (0x0 << 6)
+#define RT5670_DIG_DP_EN (0x1 << 6)
+#define RT5670_DP_TH_MASK (0x3 << 4)
+#define RT5670_DP_TH_SFT 4
+
+/* Depop Mode Control 3 (0x90) */
+#define RT5670_CP_SYS_MASK (0x7 << 12)
+#define RT5670_CP_SYS_SFT 12
+#define RT5670_CP_FQ1_MASK (0x7 << 8)
+#define RT5670_CP_FQ1_SFT 8
+#define RT5670_CP_FQ2_MASK (0x7 << 4)
+#define RT5670_CP_FQ2_SFT 4
+#define RT5670_CP_FQ3_MASK (0x7)
+#define RT5670_CP_FQ3_SFT 0
+#define RT5670_CP_FQ_1_5_KHZ 0
+#define RT5670_CP_FQ_3_KHZ 1
+#define RT5670_CP_FQ_6_KHZ 2
+#define RT5670_CP_FQ_12_KHZ 3
+#define RT5670_CP_FQ_24_KHZ 4
+#define RT5670_CP_FQ_48_KHZ 5
+#define RT5670_CP_FQ_96_KHZ 6
+#define RT5670_CP_FQ_192_KHZ 7
+
+/* HPOUT charge pump (0x91) */
+#define RT5670_OSW_L_MASK (0x1 << 11)
+#define RT5670_OSW_L_SFT 11
+#define RT5670_OSW_L_DIS (0x0 << 11)
+#define RT5670_OSW_L_EN (0x1 << 11)
+#define RT5670_OSW_R_MASK (0x1 << 10)
+#define RT5670_OSW_R_SFT 10
+#define RT5670_OSW_R_DIS (0x0 << 10)
+#define RT5670_OSW_R_EN (0x1 << 10)
+#define RT5670_PM_HP_MASK (0x3 << 8)
+#define RT5670_PM_HP_SFT 8
+#define RT5670_PM_HP_LV (0x0 << 8)
+#define RT5670_PM_HP_MV (0x1 << 8)
+#define RT5670_PM_HP_HV (0x2 << 8)
+#define RT5670_IB_HP_MASK (0x3 << 6)
+#define RT5670_IB_HP_SFT 6
+#define RT5670_IB_HP_125IL (0x0 << 6)
+#define RT5670_IB_HP_25IL (0x1 << 6)
+#define RT5670_IB_HP_5IL (0x2 << 6)
+#define RT5670_IB_HP_1IL (0x3 << 6)
+
+/* PV detection and SPK gain control (0x92) */
+#define RT5670_PVDD_DET_MASK (0x1 << 15)
+#define RT5670_PVDD_DET_SFT 15
+#define RT5670_PVDD_DET_DIS (0x0 << 15)
+#define RT5670_PVDD_DET_EN (0x1 << 15)
+#define RT5670_SPK_AG_MASK (0x1 << 14)
+#define RT5670_SPK_AG_SFT 14
+#define RT5670_SPK_AG_DIS (0x0 << 14)
+#define RT5670_SPK_AG_EN (0x1 << 14)
+
+/* Micbias Control (0x93) */
+#define RT5670_MIC1_BS_MASK (0x1 << 15)
+#define RT5670_MIC1_BS_SFT 15
+#define RT5670_MIC1_BS_9AV (0x0 << 15)
+#define RT5670_MIC1_BS_75AV (0x1 << 15)
+#define RT5670_MIC2_BS_MASK (0x1 << 14)
+#define RT5670_MIC2_BS_SFT 14
+#define RT5670_MIC2_BS_9AV (0x0 << 14)
+#define RT5670_MIC2_BS_75AV (0x1 << 14)
+#define RT5670_MIC1_CLK_MASK (0x1 << 13)
+#define RT5670_MIC1_CLK_SFT 13
+#define RT5670_MIC1_CLK_DIS (0x0 << 13)
+#define RT5670_MIC1_CLK_EN (0x1 << 13)
+#define RT5670_MIC2_CLK_MASK (0x1 << 12)
+#define RT5670_MIC2_CLK_SFT 12
+#define RT5670_MIC2_CLK_DIS (0x0 << 12)
+#define RT5670_MIC2_CLK_EN (0x1 << 12)
+#define RT5670_MIC1_OVCD_MASK (0x1 << 11)
+#define RT5670_MIC1_OVCD_SFT 11
+#define RT5670_MIC1_OVCD_DIS (0x0 << 11)
+#define RT5670_MIC1_OVCD_EN (0x1 << 11)
+#define RT5670_MIC1_OVTH_MASK (0x3 << 9)
+#define RT5670_MIC1_OVTH_SFT 9
+#define RT5670_MIC1_OVTH_600UA (0x0 << 9)
+#define RT5670_MIC1_OVTH_1500UA (0x1 << 9)
+#define RT5670_MIC1_OVTH_2000UA (0x2 << 9)
+#define RT5670_MIC2_OVCD_MASK (0x1 << 8)
+#define RT5670_MIC2_OVCD_SFT 8
+#define RT5670_MIC2_OVCD_DIS (0x0 << 8)
+#define RT5670_MIC2_OVCD_EN (0x1 << 8)
+#define RT5670_MIC2_OVTH_MASK (0x3 << 6)
+#define RT5670_MIC2_OVTH_SFT 6
+#define RT5670_MIC2_OVTH_600UA (0x0 << 6)
+#define RT5670_MIC2_OVTH_1500UA (0x1 << 6)
+#define RT5670_MIC2_OVTH_2000UA (0x2 << 6)
+#define RT5670_PWR_MB_MASK (0x1 << 5)
+#define RT5670_PWR_MB_SFT 5
+#define RT5670_PWR_MB_PD (0x0 << 5)
+#define RT5670_PWR_MB_PU (0x1 << 5)
+#define RT5670_PWR_CLK25M_MASK (0x1 << 4)
+#define RT5670_PWR_CLK25M_SFT 4
+#define RT5670_PWR_CLK25M_PD (0x0 << 4)
+#define RT5670_PWR_CLK25M_PU (0x1 << 4)
+
+/* Analog JD Control 1 (0x94) */
+#define RT5670_JD1_MODE_MASK (0x3 << 0)
+#define RT5670_JD1_MODE_0 (0x0 << 0)
+#define RT5670_JD1_MODE_1 (0x1 << 0)
+#define RT5670_JD1_MODE_2 (0x2 << 0)
+
+/* VAD Control 4 (0x9d) */
+#define RT5670_VAD_SEL_MASK (0x3 << 8)
+#define RT5670_VAD_SEL_SFT 8
+
+/* EQ Control 1 (0xb0) */
+#define RT5670_EQ_SRC_MASK (0x1 << 15)
+#define RT5670_EQ_SRC_SFT 15
+#define RT5670_EQ_SRC_DAC (0x0 << 15)
+#define RT5670_EQ_SRC_ADC (0x1 << 15)
+#define RT5670_EQ_UPD (0x1 << 14)
+#define RT5670_EQ_UPD_BIT 14
+#define RT5670_EQ_CD_MASK (0x1 << 13)
+#define RT5670_EQ_CD_SFT 13
+#define RT5670_EQ_CD_DIS (0x0 << 13)
+#define RT5670_EQ_CD_EN (0x1 << 13)
+#define RT5670_EQ_DITH_MASK (0x3 << 8)
+#define RT5670_EQ_DITH_SFT 8
+#define RT5670_EQ_DITH_NOR (0x0 << 8)
+#define RT5670_EQ_DITH_LSB (0x1 << 8)
+#define RT5670_EQ_DITH_LSB_1 (0x2 << 8)
+#define RT5670_EQ_DITH_LSB_2 (0x3 << 8)
+
+/* EQ Control 2 (0xb1) */
+#define RT5670_EQ_HPF1_M_MASK (0x1 << 8)
+#define RT5670_EQ_HPF1_M_SFT 8
+#define RT5670_EQ_HPF1_M_HI (0x0 << 8)
+#define RT5670_EQ_HPF1_M_1ST (0x1 << 8)
+#define RT5670_EQ_LPF1_M_MASK (0x1 << 7)
+#define RT5670_EQ_LPF1_M_SFT 7
+#define RT5670_EQ_LPF1_M_LO (0x0 << 7)
+#define RT5670_EQ_LPF1_M_1ST (0x1 << 7)
+#define RT5670_EQ_HPF2_MASK (0x1 << 6)
+#define RT5670_EQ_HPF2_SFT 6
+#define RT5670_EQ_HPF2_DIS (0x0 << 6)
+#define RT5670_EQ_HPF2_EN (0x1 << 6)
+#define RT5670_EQ_HPF1_MASK (0x1 << 5)
+#define RT5670_EQ_HPF1_SFT 5
+#define RT5670_EQ_HPF1_DIS (0x0 << 5)
+#define RT5670_EQ_HPF1_EN (0x1 << 5)
+#define RT5670_EQ_BPF4_MASK (0x1 << 4)
+#define RT5670_EQ_BPF4_SFT 4
+#define RT5670_EQ_BPF4_DIS (0x0 << 4)
+#define RT5670_EQ_BPF4_EN (0x1 << 4)
+#define RT5670_EQ_BPF3_MASK (0x1 << 3)
+#define RT5670_EQ_BPF3_SFT 3
+#define RT5670_EQ_BPF3_DIS (0x0 << 3)
+#define RT5670_EQ_BPF3_EN (0x1 << 3)
+#define RT5670_EQ_BPF2_MASK (0x1 << 2)
+#define RT5670_EQ_BPF2_SFT 2
+#define RT5670_EQ_BPF2_DIS (0x0 << 2)
+#define RT5670_EQ_BPF2_EN (0x1 << 2)
+#define RT5670_EQ_BPF1_MASK (0x1 << 1)
+#define RT5670_EQ_BPF1_SFT 1
+#define RT5670_EQ_BPF1_DIS (0x0 << 1)
+#define RT5670_EQ_BPF1_EN (0x1 << 1)
+#define RT5670_EQ_LPF_MASK (0x1)
+#define RT5670_EQ_LPF_SFT 0
+#define RT5670_EQ_LPF_DIS (0x0)
+#define RT5670_EQ_LPF_EN (0x1)
+#define RT5670_EQ_CTRL_MASK (0x7f)
+
+/* Memory Test (0xb2) */
+#define RT5670_MT_MASK (0x1 << 15)
+#define RT5670_MT_SFT 15
+#define RT5670_MT_DIS (0x0 << 15)
+#define RT5670_MT_EN (0x1 << 15)
+
+/* DRC/AGC Control 1 (0xb4) */
+#define RT5670_DRC_AGC_P_MASK (0x1 << 15)
+#define RT5670_DRC_AGC_P_SFT 15
+#define RT5670_DRC_AGC_P_DAC (0x0 << 15)
+#define RT5670_DRC_AGC_P_ADC (0x1 << 15)
+#define RT5670_DRC_AGC_MASK (0x1 << 14)
+#define RT5670_DRC_AGC_SFT 14
+#define RT5670_DRC_AGC_DIS (0x0 << 14)
+#define RT5670_DRC_AGC_EN (0x1 << 14)
+#define RT5670_DRC_AGC_UPD (0x1 << 13)
+#define RT5670_DRC_AGC_UPD_BIT 13
+#define RT5670_DRC_AGC_AR_MASK (0x1f << 8)
+#define RT5670_DRC_AGC_AR_SFT 8
+#define RT5670_DRC_AGC_R_MASK (0x7 << 5)
+#define RT5670_DRC_AGC_R_SFT 5
+#define RT5670_DRC_AGC_R_48K (0x1 << 5)
+#define RT5670_DRC_AGC_R_96K (0x2 << 5)
+#define RT5670_DRC_AGC_R_192K (0x3 << 5)
+#define RT5670_DRC_AGC_R_441K (0x5 << 5)
+#define RT5670_DRC_AGC_R_882K (0x6 << 5)
+#define RT5670_DRC_AGC_R_1764K (0x7 << 5)
+#define RT5670_DRC_AGC_RC_MASK (0x1f)
+#define RT5670_DRC_AGC_RC_SFT 0
+
+/* DRC/AGC Control 2 (0xb5) */
+#define RT5670_DRC_AGC_POB_MASK (0x3f << 8)
+#define RT5670_DRC_AGC_POB_SFT 8
+#define RT5670_DRC_AGC_CP_MASK (0x1 << 7)
+#define RT5670_DRC_AGC_CP_SFT 7
+#define RT5670_DRC_AGC_CP_DIS (0x0 << 7)
+#define RT5670_DRC_AGC_CP_EN (0x1 << 7)
+#define RT5670_DRC_AGC_CPR_MASK (0x3 << 5)
+#define RT5670_DRC_AGC_CPR_SFT 5
+#define RT5670_DRC_AGC_CPR_1_1 (0x0 << 5)
+#define RT5670_DRC_AGC_CPR_1_2 (0x1 << 5)
+#define RT5670_DRC_AGC_CPR_1_3 (0x2 << 5)
+#define RT5670_DRC_AGC_CPR_1_4 (0x3 << 5)
+#define RT5670_DRC_AGC_PRB_MASK (0x1f)
+#define RT5670_DRC_AGC_PRB_SFT 0
+
+/* DRC/AGC Control 3 (0xb6) */
+#define RT5670_DRC_AGC_NGB_MASK (0xf << 12)
+#define RT5670_DRC_AGC_NGB_SFT 12
+#define RT5670_DRC_AGC_TAR_MASK (0x1f << 7)
+#define RT5670_DRC_AGC_TAR_SFT 7
+#define RT5670_DRC_AGC_NG_MASK (0x1 << 6)
+#define RT5670_DRC_AGC_NG_SFT 6
+#define RT5670_DRC_AGC_NG_DIS (0x0 << 6)
+#define RT5670_DRC_AGC_NG_EN (0x1 << 6)
+#define RT5670_DRC_AGC_NGH_MASK (0x1 << 5)
+#define RT5670_DRC_AGC_NGH_SFT 5
+#define RT5670_DRC_AGC_NGH_DIS (0x0 << 5)
+#define RT5670_DRC_AGC_NGH_EN (0x1 << 5)
+#define RT5670_DRC_AGC_NGT_MASK (0x1f)
+#define RT5670_DRC_AGC_NGT_SFT 0
+
+/* Jack Detect Control (0xbb) */
+#define RT5670_JD_MASK (0x7 << 13)
+#define RT5670_JD_SFT 13
+#define RT5670_JD_DIS (0x0 << 13)
+#define RT5670_JD_GPIO1 (0x1 << 13)
+#define RT5670_JD_JD1_IN4P (0x2 << 13)
+#define RT5670_JD_JD2_IN4N (0x3 << 13)
+#define RT5670_JD_GPIO2 (0x4 << 13)
+#define RT5670_JD_GPIO3 (0x5 << 13)
+#define RT5670_JD_GPIO4 (0x6 << 13)
+#define RT5670_JD_HP_MASK (0x1 << 11)
+#define RT5670_JD_HP_SFT 11
+#define RT5670_JD_HP_DIS (0x0 << 11)
+#define RT5670_JD_HP_EN (0x1 << 11)
+#define RT5670_JD_HP_TRG_MASK (0x1 << 10)
+#define RT5670_JD_HP_TRG_SFT 10
+#define RT5670_JD_HP_TRG_LO (0x0 << 10)
+#define RT5670_JD_HP_TRG_HI (0x1 << 10)
+#define RT5670_JD_SPL_MASK (0x1 << 9)
+#define RT5670_JD_SPL_SFT 9
+#define RT5670_JD_SPL_DIS (0x0 << 9)
+#define RT5670_JD_SPL_EN (0x1 << 9)
+#define RT5670_JD_SPL_TRG_MASK (0x1 << 8)
+#define RT5670_JD_SPL_TRG_SFT 8
+#define RT5670_JD_SPL_TRG_LO (0x0 << 8)
+#define RT5670_JD_SPL_TRG_HI (0x1 << 8)
+#define RT5670_JD_SPR_MASK (0x1 << 7)
+#define RT5670_JD_SPR_SFT 7
+#define RT5670_JD_SPR_DIS (0x0 << 7)
+#define RT5670_JD_SPR_EN (0x1 << 7)
+#define RT5670_JD_SPR_TRG_MASK (0x1 << 6)
+#define RT5670_JD_SPR_TRG_SFT 6
+#define RT5670_JD_SPR_TRG_LO (0x0 << 6)
+#define RT5670_JD_SPR_TRG_HI (0x1 << 6)
+#define RT5670_JD_MO_MASK (0x1 << 5)
+#define RT5670_JD_MO_SFT 5
+#define RT5670_JD_MO_DIS (0x0 << 5)
+#define RT5670_JD_MO_EN (0x1 << 5)
+#define RT5670_JD_MO_TRG_MASK (0x1 << 4)
+#define RT5670_JD_MO_TRG_SFT 4
+#define RT5670_JD_MO_TRG_LO (0x0 << 4)
+#define RT5670_JD_MO_TRG_HI (0x1 << 4)
+#define RT5670_JD_LO_MASK (0x1 << 3)
+#define RT5670_JD_LO_SFT 3
+#define RT5670_JD_LO_DIS (0x0 << 3)
+#define RT5670_JD_LO_EN (0x1 << 3)
+#define RT5670_JD_LO_TRG_MASK (0x1 << 2)
+#define RT5670_JD_LO_TRG_SFT 2
+#define RT5670_JD_LO_TRG_LO (0x0 << 2)
+#define RT5670_JD_LO_TRG_HI (0x1 << 2)
+#define RT5670_JD1_IN4P_MASK (0x1 << 1)
+#define RT5670_JD1_IN4P_SFT 1
+#define RT5670_JD1_IN4P_DIS (0x0 << 1)
+#define RT5670_JD1_IN4P_EN (0x1 << 1)
+#define RT5670_JD2_IN4N_MASK (0x1)
+#define RT5670_JD2_IN4N_SFT 0
+#define RT5670_JD2_IN4N_DIS (0x0)
+#define RT5670_JD2_IN4N_EN (0x1)
+
+/* IRQ Control 1 (0xbd) */
+#define RT5670_IRQ_JD_MASK (0x1 << 15)
+#define RT5670_IRQ_JD_SFT 15
+#define RT5670_IRQ_JD_BP (0x0 << 15)
+#define RT5670_IRQ_JD_NOR (0x1 << 15)
+#define RT5670_IRQ_OT_MASK (0x1 << 14)
+#define RT5670_IRQ_OT_SFT 14
+#define RT5670_IRQ_OT_BP (0x0 << 14)
+#define RT5670_IRQ_OT_NOR (0x1 << 14)
+#define RT5670_JD_STKY_MASK (0x1 << 13)
+#define RT5670_JD_STKY_SFT 13
+#define RT5670_JD_STKY_DIS (0x0 << 13)
+#define RT5670_JD_STKY_EN (0x1 << 13)
+#define RT5670_OT_STKY_MASK (0x1 << 12)
+#define RT5670_OT_STKY_SFT 12
+#define RT5670_OT_STKY_DIS (0x0 << 12)
+#define RT5670_OT_STKY_EN (0x1 << 12)
+#define RT5670_JD_P_MASK (0x1 << 11)
+#define RT5670_JD_P_SFT 11
+#define RT5670_JD_P_NOR (0x0 << 11)
+#define RT5670_JD_P_INV (0x1 << 11)
+#define RT5670_OT_P_MASK (0x1 << 10)
+#define RT5670_OT_P_SFT 10
+#define RT5670_OT_P_NOR (0x0 << 10)
+#define RT5670_OT_P_INV (0x1 << 10)
+#define RT5670_JD1_1_EN_MASK (0x1 << 9)
+#define RT5670_JD1_1_EN_SFT 9
+#define RT5670_JD1_1_DIS (0x0 << 9)
+#define RT5670_JD1_1_EN (0x1 << 9)
+
+/* IRQ Control 2 (0xbe) */
+#define RT5670_IRQ_MB1_OC_MASK (0x1 << 15)
+#define RT5670_IRQ_MB1_OC_SFT 15
+#define RT5670_IRQ_MB1_OC_BP (0x0 << 15)
+#define RT5670_IRQ_MB1_OC_NOR (0x1 << 15)
+#define RT5670_IRQ_MB2_OC_MASK (0x1 << 14)
+#define RT5670_IRQ_MB2_OC_SFT 14
+#define RT5670_IRQ_MB2_OC_BP (0x0 << 14)
+#define RT5670_IRQ_MB2_OC_NOR (0x1 << 14)
+#define RT5670_MB1_OC_STKY_MASK (0x1 << 11)
+#define RT5670_MB1_OC_STKY_SFT 11
+#define RT5670_MB1_OC_STKY_DIS (0x0 << 11)
+#define RT5670_MB1_OC_STKY_EN (0x1 << 11)
+#define RT5670_MB2_OC_STKY_MASK (0x1 << 10)
+#define RT5670_MB2_OC_STKY_SFT 10
+#define RT5670_MB2_OC_STKY_DIS (0x0 << 10)
+#define RT5670_MB2_OC_STKY_EN (0x1 << 10)
+#define RT5670_MB1_OC_P_MASK (0x1 << 7)
+#define RT5670_MB1_OC_P_SFT 7
+#define RT5670_MB1_OC_P_NOR (0x0 << 7)
+#define RT5670_MB1_OC_P_INV (0x1 << 7)
+#define RT5670_MB2_OC_P_MASK (0x1 << 6)
+#define RT5670_MB2_OC_P_SFT 6
+#define RT5670_MB2_OC_P_NOR (0x0 << 6)
+#define RT5670_MB2_OC_P_INV (0x1 << 6)
+#define RT5670_MB1_OC_CLR (0x1 << 3)
+#define RT5670_MB1_OC_CLR_SFT 3
+#define RT5670_MB2_OC_CLR (0x1 << 2)
+#define RT5670_MB2_OC_CLR_SFT 2
+
+/* GPIO Control 1 (0xc0) */
+#define RT5670_GP1_PIN_MASK (0x1 << 15)
+#define RT5670_GP1_PIN_SFT 15
+#define RT5670_GP1_PIN_GPIO1 (0x0 << 15)
+#define RT5670_GP1_PIN_IRQ (0x1 << 15)
+#define RT5670_GP2_PIN_MASK (0x1 << 14)
+#define RT5670_GP2_PIN_SFT 14
+#define RT5670_GP2_PIN_GPIO2 (0x0 << 14)
+#define RT5670_GP2_PIN_DMIC1_SCL (0x1 << 14)
+#define RT5670_GP3_PIN_MASK (0x3 << 12)
+#define RT5670_GP3_PIN_SFT 12
+#define RT5670_GP3_PIN_GPIO3 (0x0 << 12)
+#define RT5670_GP3_PIN_DMIC1_SDA (0x1 << 12)
+#define RT5670_GP3_PIN_IRQ (0x2 << 12)
+#define RT5670_GP4_PIN_MASK (0x1 << 11)
+#define RT5670_GP4_PIN_SFT 11
+#define RT5670_GP4_PIN_GPIO4 (0x0 << 11)
+#define RT5670_GP4_PIN_DMIC2_SDA (0x1 << 11)
+#define RT5670_DP_SIG_MASK (0x1 << 10)
+#define RT5670_DP_SIG_SFT 10
+#define RT5670_DP_SIG_TEST (0x0 << 10)
+#define RT5670_DP_SIG_AP (0x1 << 10)
+#define RT5670_GPIO_M_MASK (0x1 << 9)
+#define RT5670_GPIO_M_SFT 9
+#define RT5670_GPIO_M_FLT (0x0 << 9)
+#define RT5670_GPIO_M_PH (0x1 << 9)
+#define RT5670_I2S2_PIN_MASK (0x1 << 8)
+#define RT5670_I2S2_PIN_SFT 8
+#define RT5670_I2S2_PIN_I2S (0x0 << 8)
+#define RT5670_I2S2_PIN_GPIO (0x1 << 8)
+#define RT5670_GP5_PIN_MASK (0x1 << 7)
+#define RT5670_GP5_PIN_SFT 7
+#define RT5670_GP5_PIN_GPIO5 (0x0 << 7)
+#define RT5670_GP5_PIN_DMIC3_SDA (0x1 << 7)
+#define RT5670_GP6_PIN_MASK (0x1 << 6)
+#define RT5670_GP6_PIN_SFT 6
+#define RT5670_GP6_PIN_GPIO6 (0x0 << 6)
+#define RT5670_GP6_PIN_DMIC1_SDA (0x1 << 6)
+#define RT5670_GP7_PIN_MASK (0x3 << 4)
+#define RT5670_GP7_PIN_SFT 4
+#define RT5670_GP7_PIN_GPIO7 (0x0 << 4)
+#define RT5670_GP7_PIN_DMIC1_SDA (0x1 << 4)
+#define RT5670_GP7_PIN_PDM_SCL2 (0x2 << 4)
+#define RT5670_GP8_PIN_MASK (0x1 << 3)
+#define RT5670_GP8_PIN_SFT 3
+#define RT5670_GP8_PIN_GPIO8 (0x0 << 3)
+#define RT5670_GP8_PIN_DMIC2_SDA (0x1 << 3)
+#define RT5670_GP9_PIN_MASK (0x1 << 2)
+#define RT5670_GP9_PIN_SFT 2
+#define RT5670_GP9_PIN_GPIO9 (0x0 << 2)
+#define RT5670_GP9_PIN_DMIC3_SDA (0x1 << 2)
+#define RT5670_GP10_PIN_MASK (0x3)
+#define RT5670_GP10_PIN_SFT 0
+#define RT5670_GP10_PIN_GPIO9 (0x0)
+#define RT5670_GP10_PIN_DMIC3_SDA (0x1)
+#define RT5670_GP10_PIN_PDM_ADT2 (0x2)
+
+/* GPIO Control 2 (0xc1) */
+#define RT5670_GP4_PF_MASK (0x1 << 11)
+#define RT5670_GP4_PF_SFT 11
+#define RT5670_GP4_PF_IN (0x0 << 11)
+#define RT5670_GP4_PF_OUT (0x1 << 11)
+#define RT5670_GP4_OUT_MASK (0x1 << 10)
+#define RT5670_GP4_OUT_SFT 10
+#define RT5670_GP4_OUT_LO (0x0 << 10)
+#define RT5670_GP4_OUT_HI (0x1 << 10)
+#define RT5670_GP4_P_MASK (0x1 << 9)
+#define RT5670_GP4_P_SFT 9
+#define RT5670_GP4_P_NOR (0x0 << 9)
+#define RT5670_GP4_P_INV (0x1 << 9)
+#define RT5670_GP3_PF_MASK (0x1 << 8)
+#define RT5670_GP3_PF_SFT 8
+#define RT5670_GP3_PF_IN (0x0 << 8)
+#define RT5670_GP3_PF_OUT (0x1 << 8)
+#define RT5670_GP3_OUT_MASK (0x1 << 7)
+#define RT5670_GP3_OUT_SFT 7
+#define RT5670_GP3_OUT_LO (0x0 << 7)
+#define RT5670_GP3_OUT_HI (0x1 << 7)
+#define RT5670_GP3_P_MASK (0x1 << 6)
+#define RT5670_GP3_P_SFT 6
+#define RT5670_GP3_P_NOR (0x0 << 6)
+#define RT5670_GP3_P_INV (0x1 << 6)
+#define RT5670_GP2_PF_MASK (0x1 << 5)
+#define RT5670_GP2_PF_SFT 5
+#define RT5670_GP2_PF_IN (0x0 << 5)
+#define RT5670_GP2_PF_OUT (0x1 << 5)
+#define RT5670_GP2_OUT_MASK (0x1 << 4)
+#define RT5670_GP2_OUT_SFT 4
+#define RT5670_GP2_OUT_LO (0x0 << 4)
+#define RT5670_GP2_OUT_HI (0x1 << 4)
+#define RT5670_GP2_P_MASK (0x1 << 3)
+#define RT5670_GP2_P_SFT 3
+#define RT5670_GP2_P_NOR (0x0 << 3)
+#define RT5670_GP2_P_INV (0x1 << 3)
+#define RT5670_GP1_PF_MASK (0x1 << 2)
+#define RT5670_GP1_PF_SFT 2
+#define RT5670_GP1_PF_IN (0x0 << 2)
+#define RT5670_GP1_PF_OUT (0x1 << 2)
+#define RT5670_GP1_OUT_MASK (0x1 << 1)
+#define RT5670_GP1_OUT_SFT 1
+#define RT5670_GP1_OUT_LO (0x0 << 1)
+#define RT5670_GP1_OUT_HI (0x1 << 1)
+#define RT5670_GP1_P_MASK (0x1)
+#define RT5670_GP1_P_SFT 0
+#define RT5670_GP1_P_NOR (0x0)
+#define RT5670_GP1_P_INV (0x1)
+
+/* Scramble Function (0xcd) */
+#define RT5670_SCB_KEY_MASK (0xff)
+#define RT5670_SCB_KEY_SFT 0
+
+/* Scramble Control (0xce) */
+#define RT5670_SCB_SWAP_MASK (0x1 << 15)
+#define RT5670_SCB_SWAP_SFT 15
+#define RT5670_SCB_SWAP_DIS (0x0 << 15)
+#define RT5670_SCB_SWAP_EN (0x1 << 15)
+#define RT5670_SCB_MASK (0x1 << 14)
+#define RT5670_SCB_SFT 14
+#define RT5670_SCB_DIS (0x0 << 14)
+#define RT5670_SCB_EN (0x1 << 14)
+
+/* Baseback Control (0xcf) */
+#define RT5670_BB_MASK (0x1 << 15)
+#define RT5670_BB_SFT 15
+#define RT5670_BB_DIS (0x0 << 15)
+#define RT5670_BB_EN (0x1 << 15)
+#define RT5670_BB_CT_MASK (0x7 << 12)
+#define RT5670_BB_CT_SFT 12
+#define RT5670_BB_CT_A (0x0 << 12)
+#define RT5670_BB_CT_B (0x1 << 12)
+#define RT5670_BB_CT_C (0x2 << 12)
+#define RT5670_BB_CT_D (0x3 << 12)
+#define RT5670_M_BB_L_MASK (0x1 << 9)
+#define RT5670_M_BB_L_SFT 9
+#define RT5670_M_BB_R_MASK (0x1 << 8)
+#define RT5670_M_BB_R_SFT 8
+#define RT5670_M_BB_HPF_L_MASK (0x1 << 7)
+#define RT5670_M_BB_HPF_L_SFT 7
+#define RT5670_M_BB_HPF_R_MASK (0x1 << 6)
+#define RT5670_M_BB_HPF_R_SFT 6
+#define RT5670_G_BB_BST_MASK (0x3f)
+#define RT5670_G_BB_BST_SFT 0
+
+/* MP3 Plus Control 1 (0xd0) */
+#define RT5670_M_MP3_L_MASK (0x1 << 15)
+#define RT5670_M_MP3_L_SFT 15
+#define RT5670_M_MP3_R_MASK (0x1 << 14)
+#define RT5670_M_MP3_R_SFT 14
+#define RT5670_M_MP3_MASK (0x1 << 13)
+#define RT5670_M_MP3_SFT 13
+#define RT5670_M_MP3_DIS (0x0 << 13)
+#define RT5670_M_MP3_EN (0x1 << 13)
+#define RT5670_EG_MP3_MASK (0x1f << 8)
+#define RT5670_EG_MP3_SFT 8
+#define RT5670_MP3_HLP_MASK (0x1 << 7)
+#define RT5670_MP3_HLP_SFT 7
+#define RT5670_MP3_HLP_DIS (0x0 << 7)
+#define RT5670_MP3_HLP_EN (0x1 << 7)
+#define RT5670_M_MP3_ORG_L_MASK (0x1 << 6)
+#define RT5670_M_MP3_ORG_L_SFT 6
+#define RT5670_M_MP3_ORG_R_MASK (0x1 << 5)
+#define RT5670_M_MP3_ORG_R_SFT 5
+
+/* MP3 Plus Control 2 (0xd1) */
+#define RT5670_MP3_WT_MASK (0x1 << 13)
+#define RT5670_MP3_WT_SFT 13
+#define RT5670_MP3_WT_1_4 (0x0 << 13)
+#define RT5670_MP3_WT_1_2 (0x1 << 13)
+#define RT5670_OG_MP3_MASK (0x1f << 8)
+#define RT5670_OG_MP3_SFT 8
+#define RT5670_HG_MP3_MASK (0x3f)
+#define RT5670_HG_MP3_SFT 0
+
+/* 3D HP Control 1 (0xd2) */
+#define RT5670_3D_CF_MASK (0x1 << 15)
+#define RT5670_3D_CF_SFT 15
+#define RT5670_3D_CF_DIS (0x0 << 15)
+#define RT5670_3D_CF_EN (0x1 << 15)
+#define RT5670_3D_HP_MASK (0x1 << 14)
+#define RT5670_3D_HP_SFT 14
+#define RT5670_3D_HP_DIS (0x0 << 14)
+#define RT5670_3D_HP_EN (0x1 << 14)
+#define RT5670_3D_BT_MASK (0x1 << 13)
+#define RT5670_3D_BT_SFT 13
+#define RT5670_3D_BT_DIS (0x0 << 13)
+#define RT5670_3D_BT_EN (0x1 << 13)
+#define RT5670_3D_1F_MIX_MASK (0x3 << 11)
+#define RT5670_3D_1F_MIX_SFT 11
+#define RT5670_3D_HP_M_MASK (0x1 << 10)
+#define RT5670_3D_HP_M_SFT 10
+#define RT5670_3D_HP_M_SUR (0x0 << 10)
+#define RT5670_3D_HP_M_FRO (0x1 << 10)
+#define RT5670_M_3D_HRTF_MASK (0x1 << 9)
+#define RT5670_M_3D_HRTF_SFT 9
+#define RT5670_M_3D_D2H_MASK (0x1 << 8)
+#define RT5670_M_3D_D2H_SFT 8
+#define RT5670_M_3D_D2R_MASK (0x1 << 7)
+#define RT5670_M_3D_D2R_SFT 7
+#define RT5670_M_3D_REVB_MASK (0x1 << 6)
+#define RT5670_M_3D_REVB_SFT 6
+
+/* Adjustable high pass filter control 1 (0xd3) */
+#define RT5670_2ND_HPF_MASK (0x1 << 15)
+#define RT5670_2ND_HPF_SFT 15
+#define RT5670_2ND_HPF_DIS (0x0 << 15)
+#define RT5670_2ND_HPF_EN (0x1 << 15)
+#define RT5670_HPF_CF_L_MASK (0x7 << 12)
+#define RT5670_HPF_CF_L_SFT 12
+#define RT5670_1ST_HPF_MASK (0x1 << 11)
+#define RT5670_1ST_HPF_SFT 11
+#define RT5670_1ST_HPF_DIS (0x0 << 11)
+#define RT5670_1ST_HPF_EN (0x1 << 11)
+#define RT5670_HPF_CF_R_MASK (0x7 << 8)
+#define RT5670_HPF_CF_R_SFT 8
+#define RT5670_ZD_T_MASK (0x3 << 6)
+#define RT5670_ZD_T_SFT 6
+#define RT5670_ZD_F_MASK (0x3 << 4)
+#define RT5670_ZD_F_SFT 4
+#define RT5670_ZD_F_IM (0x0 << 4)
+#define RT5670_ZD_F_ZC_IM (0x1 << 4)
+#define RT5670_ZD_F_ZC_IOD (0x2 << 4)
+#define RT5670_ZD_F_UN (0x3 << 4)
+
+/* HP calibration control and Amp detection (0xd6) */
+#define RT5670_SI_DAC_MASK (0x1 << 11)
+#define RT5670_SI_DAC_SFT 11
+#define RT5670_SI_DAC_AUTO (0x0 << 11)
+#define RT5670_SI_DAC_TEST (0x1 << 11)
+#define RT5670_DC_CAL_M_MASK (0x1 << 10)
+#define RT5670_DC_CAL_M_SFT 10
+#define RT5670_DC_CAL_M_CAL (0x0 << 10)
+#define RT5670_DC_CAL_M_NOR (0x1 << 10)
+#define RT5670_DC_CAL_MASK (0x1 << 9)
+#define RT5670_DC_CAL_SFT 9
+#define RT5670_DC_CAL_DIS (0x0 << 9)
+#define RT5670_DC_CAL_EN (0x1 << 9)
+#define RT5670_HPD_RCV_MASK (0x7 << 6)
+#define RT5670_HPD_RCV_SFT 6
+#define RT5670_HPD_PS_MASK (0x1 << 5)
+#define RT5670_HPD_PS_SFT 5
+#define RT5670_HPD_PS_DIS (0x0 << 5)
+#define RT5670_HPD_PS_EN (0x1 << 5)
+#define RT5670_CAL_M_MASK (0x1 << 4)
+#define RT5670_CAL_M_SFT 4
+#define RT5670_CAL_M_DEP (0x0 << 4)
+#define RT5670_CAL_M_CAL (0x1 << 4)
+#define RT5670_CAL_MASK (0x1 << 3)
+#define RT5670_CAL_SFT 3
+#define RT5670_CAL_DIS (0x0 << 3)
+#define RT5670_CAL_EN (0x1 << 3)
+#define RT5670_CAL_TEST_MASK (0x1 << 2)
+#define RT5670_CAL_TEST_SFT 2
+#define RT5670_CAL_TEST_DIS (0x0 << 2)
+#define RT5670_CAL_TEST_EN (0x1 << 2)
+#define RT5670_CAL_P_MASK (0x3)
+#define RT5670_CAL_P_SFT 0
+#define RT5670_CAL_P_NONE (0x0)
+#define RT5670_CAL_P_CAL (0x1)
+#define RT5670_CAL_P_DAC_CAL (0x2)
+
+/* Soft volume and zero cross control 1 (0xd9) */
+#define RT5670_SV_MASK (0x1 << 15)
+#define RT5670_SV_SFT 15
+#define RT5670_SV_DIS (0x0 << 15)
+#define RT5670_SV_EN (0x1 << 15)
+#define RT5670_SPO_SV_MASK (0x1 << 14)
+#define RT5670_SPO_SV_SFT 14
+#define RT5670_SPO_SV_DIS (0x0 << 14)
+#define RT5670_SPO_SV_EN (0x1 << 14)
+#define RT5670_OUT_SV_MASK (0x1 << 13)
+#define RT5670_OUT_SV_SFT 13
+#define RT5670_OUT_SV_DIS (0x0 << 13)
+#define RT5670_OUT_SV_EN (0x1 << 13)
+#define RT5670_HP_SV_MASK (0x1 << 12)
+#define RT5670_HP_SV_SFT 12
+#define RT5670_HP_SV_DIS (0x0 << 12)
+#define RT5670_HP_SV_EN (0x1 << 12)
+#define RT5670_ZCD_DIG_MASK (0x1 << 11)
+#define RT5670_ZCD_DIG_SFT 11
+#define RT5670_ZCD_DIG_DIS (0x0 << 11)
+#define RT5670_ZCD_DIG_EN (0x1 << 11)
+#define RT5670_ZCD_MASK (0x1 << 10)
+#define RT5670_ZCD_SFT 10
+#define RT5670_ZCD_PD (0x0 << 10)
+#define RT5670_ZCD_PU (0x1 << 10)
+#define RT5670_M_ZCD_MASK (0x3f << 4)
+#define RT5670_M_ZCD_SFT 4
+#define RT5670_M_ZCD_RM_L (0x1 << 9)
+#define RT5670_M_ZCD_RM_R (0x1 << 8)
+#define RT5670_M_ZCD_SM_L (0x1 << 7)
+#define RT5670_M_ZCD_SM_R (0x1 << 6)
+#define RT5670_M_ZCD_OM_L (0x1 << 5)
+#define RT5670_M_ZCD_OM_R (0x1 << 4)
+#define RT5670_SV_DLY_MASK (0xf)
+#define RT5670_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0xda) */
+#define RT5670_ZCD_HP_MASK (0x1 << 15)
+#define RT5670_ZCD_HP_SFT 15
+#define RT5670_ZCD_HP_DIS (0x0 << 15)
+#define RT5670_ZCD_HP_EN (0x1 << 15)
+
+
+/* Codec Private Register definition */
+/* 3D Speaker Control (0x63) */
+#define RT5670_3D_SPK_MASK (0x1 << 15)
+#define RT5670_3D_SPK_SFT 15
+#define RT5670_3D_SPK_DIS (0x0 << 15)
+#define RT5670_3D_SPK_EN (0x1 << 15)
+#define RT5670_3D_SPK_M_MASK (0x3 << 13)
+#define RT5670_3D_SPK_M_SFT 13
+#define RT5670_3D_SPK_CG_MASK (0x1f << 8)
+#define RT5670_3D_SPK_CG_SFT 8
+#define RT5670_3D_SPK_SG_MASK (0x1f)
+#define RT5670_3D_SPK_SG_SFT 0
+
+/* Wind Noise Detection Control 1 (0x6c) */
+#define RT5670_WND_MASK (0x1 << 15)
+#define RT5670_WND_SFT 15
+#define RT5670_WND_DIS (0x0 << 15)
+#define RT5670_WND_EN (0x1 << 15)
+
+/* Wind Noise Detection Control 2 (0x6d) */
+#define RT5670_WND_FC_NW_MASK (0x3f << 10)
+#define RT5670_WND_FC_NW_SFT 10
+#define RT5670_WND_FC_WK_MASK (0x3f << 4)
+#define RT5670_WND_FC_WK_SFT 4
+
+/* Wind Noise Detection Control 3 (0x6e) */
+#define RT5670_HPF_FC_MASK (0x3f << 6)
+#define RT5670_HPF_FC_SFT 6
+#define RT5670_WND_FC_ST_MASK (0x3f)
+#define RT5670_WND_FC_ST_SFT 0
+
+/* Wind Noise Detection Control 4 (0x6f) */
+#define RT5670_WND_TH_LO_MASK (0x3ff)
+#define RT5670_WND_TH_LO_SFT 0
+
+/* Wind Noise Detection Control 5 (0x70) */
+#define RT5670_WND_TH_HI_MASK (0x3ff)
+#define RT5670_WND_TH_HI_SFT 0
+
+/* Wind Noise Detection Control 8 (0x73) */
+#define RT5670_WND_WIND_MASK (0x1 << 13) /* Read-Only */
+#define RT5670_WND_WIND_SFT 13
+#define RT5670_WND_STRONG_MASK (0x1 << 12) /* Read-Only */
+#define RT5670_WND_STRONG_SFT 12
+enum {
+ RT5670_NO_WIND,
+ RT5670_BREEZE,
+ RT5670_STORM,
+};
+
+/* Dipole Speaker Interface (0x75) */
+#define RT5670_DP_ATT_MASK (0x3 << 14)
+#define RT5670_DP_ATT_SFT 14
+#define RT5670_DP_SPK_MASK (0x1 << 10)
+#define RT5670_DP_SPK_SFT 10
+#define RT5670_DP_SPK_DIS (0x0 << 10)
+#define RT5670_DP_SPK_EN (0x1 << 10)
+
+/* EQ Pre Volume Control (0xb3) */
+#define RT5670_EQ_PRE_VOL_MASK (0xffff)
+#define RT5670_EQ_PRE_VOL_SFT 0
+
+/* EQ Post Volume Control (0xb4) */
+#define RT5670_EQ_PST_VOL_MASK (0xffff)
+#define RT5670_EQ_PST_VOL_SFT 0
+
+/* Jack Detect Control 3 (0xf8) */
+#define RT5670_CMP_MIC_IN_DET_MASK (0x7 << 12)
+#define RT5670_JD_CBJ_EN (0x1 << 7)
+#define RT5670_JD_CBJ_POL (0x1 << 6)
+#define RT5670_JD_TRI_CBJ_SEL_MASK (0x7 << 3)
+#define RT5670_JD_TRI_CBJ_SEL_SFT (3)
+#define RT5670_JD_CBJ_GPIO_JD1 (0x0 << 3)
+#define RT5670_JD_CBJ_JD1_1 (0x1 << 3)
+#define RT5670_JD_CBJ_JD1_2 (0x2 << 3)
+#define RT5670_JD_CBJ_JD2 (0x3 << 3)
+#define RT5670_JD_CBJ_JD3 (0x4 << 3)
+#define RT5670_JD_CBJ_GPIO_JD2 (0x5 << 3)
+#define RT5670_JD_CBJ_MX0B_12 (0x6 << 3)
+#define RT5670_JD_TRI_HPO_SEL_MASK (0x7 << 3)
+#define RT5670_JD_TRI_HPO_SEL_SFT (0)
+#define RT5670_JD_HPO_GPIO_JD1 (0x0)
+#define RT5670_JD_HPO_JD1_1 (0x1)
+#define RT5670_JD_HPO_JD1_2 (0x2)
+#define RT5670_JD_HPO_JD2 (0x3)
+#define RT5670_JD_HPO_JD3 (0x4)
+#define RT5670_JD_HPO_GPIO_JD2 (0x5)
+#define RT5670_JD_HPO_MX0B_12 (0x6)
+
+/* Digital Misc Control (0xfa) */
+#define RT5670_RST_DSP (0x1 << 13)
+#define RT5670_IF1_ADC1_IN1_SEL (0x1 << 12)
+#define RT5670_IF1_ADC1_IN1_SFT 12
+#define RT5670_IF1_ADC1_IN2_SEL (0x1 << 11)
+#define RT5670_IF1_ADC1_IN2_SFT 11
+#define RT5670_IF1_ADC2_IN1_SEL (0x1 << 10)
+#define RT5670_IF1_ADC2_IN1_SFT 10
+
+/* General Control2 (0xfb) */
+#define RT5670_RXDC_SRC_MASK (0x1 << 7)
+#define RT5670_RXDC_SRC_STO (0x0 << 7)
+#define RT5670_RXDC_SRC_MONO (0x1 << 7)
+#define RT5670_RXDC_SRC_SFT (7)
+#define RT5670_RXDP2_SEL_MASK (0x1 << 3)
+#define RT5670_RXDP2_SEL_IF2 (0x0 << 3)
+#define RT5670_RXDP2_SEL_ADC (0x1 << 3)
+#define RT5670_RXDP2_SEL_SFT (3)
+
+/* System Clock Source */
+enum {
+ RT5670_SCLK_S_MCLK,
+ RT5670_SCLK_S_PLL1,
+ RT5670_SCLK_S_RCCLK,
+};
+
+/* PLL1 Source */
+enum {
+ RT5670_PLL1_S_MCLK,
+ RT5670_PLL1_S_BCLK1,
+ RT5670_PLL1_S_BCLK2,
+ RT5670_PLL1_S_BCLK3,
+ RT5670_PLL1_S_BCLK4,
+};
+
+enum {
+ RT5670_AIF1,
+ RT5670_AIF2,
+ RT5670_AIF3,
+ RT5670_AIF4,
+ RT5670_AIFS,
+};
+
+enum {
+ RT5670_DMIC1_DISABLED,
+ RT5670_DMIC_DATA_GPIO6,
+ RT5670_DMIC_DATA_IN2P,
+ RT5670_DMIC_DATA_GPIO7,
+};
+
+enum {
+ RT5670_DMIC2_DISABLED,
+ RT5670_DMIC_DATA_GPIO8,
+ RT5670_DMIC_DATA_IN3N,
+};
+
+enum {
+ RT5670_DMIC3_DISABLED,
+ RT5670_DMIC_DATA_GPIO9,
+ RT5670_DMIC_DATA_GPIO10,
+ RT5670_DMIC_DATA_GPIO5,
+};
+
+/* filter mask */
+enum {
+ RT5670_DA_STEREO_FILTER = 0x1,
+ RT5670_DA_MONO_L_FILTER = (0x1 << 1),
+ RT5670_DA_MONO_R_FILTER = (0x1 << 2),
+ RT5670_AD_STEREO_FILTER = (0x1 << 3),
+ RT5670_AD_MONO_L_FILTER = (0x1 << 4),
+ RT5670_AD_MONO_R_FILTER = (0x1 << 5),
+ RT5670_UP_RATE_FILTER = (0x1 << 6),
+ RT5670_DOWN_RATE_FILTER = (0x1 << 7),
+};
+
+int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
+ unsigned int filter_mask, unsigned int clk_src);
+
+struct rt5670_priv {
+ struct snd_soc_codec *codec;
+ struct rt5670_platform_data pdata;
+ struct regmap *regmap;
+ struct snd_soc_jack *jack;
+ struct snd_soc_jack_gpio hp_gpio;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5670_AIFS];
+ int bclk[RT5670_AIFS];
+ int master[RT5670_AIFS];
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ int dsp_sw; /* expected parameter setting */
+ int dsp_rate;
+ int jack_type;
+ int jack_type_saved;
+};
+
+void rt5670_jack_suspend(struct snd_soc_codec *codec);
+void rt5670_jack_resume(struct snd_soc_codec *codec);
+int rt5670_set_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack);
+#endif /* __RT5670_H__ */
diff --git a/kernel/sound/soc/codecs/rt5677-spi.c b/kernel/sound/soc/codecs/rt5677-spi.c
new file mode 100644
index 000000000..ef6348cb9
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5677-spi.c
@@ -0,0 +1,130 @@
+/*
+ * 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>
+#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/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 "rt5677-spi.h"
+
+static struct spi_device *g_spi;
+
+/**
+ * rt5677_spi_write - Write data to SPI.
+ * @txbuf: Data Buffer for writing.
+ * @len: Data length.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5677_spi_write(u8 *txbuf, size_t len)
+{
+ int status;
+
+ status = spi_write(g_spi, txbuf, len);
+
+ if (status)
+ dev_err(&g_spi->dev, "rt5677_spi_write error %d\n", status);
+
+ return status;
+}
+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.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5677_spi_burst_write(u32 addr, const struct firmware *fw)
+{
+ u8 spi_cmd = RT5677_SPI_CMD_BURST_WRITE;
+ u8 *write_buf;
+ unsigned int i, end, offset = 0;
+
+ write_buf = kmalloc(RT5677_SPI_BUF_LEN + 6, GFP_KERNEL);
+
+ if (write_buf == NULL)
+ return -ENOMEM;
+
+ 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];
+ }
+
+ write_buf[end + 5] = spi_cmd;
+
+ rt5677_spi_write(write_buf, end + 6);
+
+ offset += RT5677_SPI_BUF_LEN;
+ }
+
+ kfree(write_buf);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5677_spi_burst_write);
+
+static int rt5677_spi_probe(struct spi_device *spi)
+{
+ g_spi = spi;
+ return 0;
+}
+
+static struct spi_driver rt5677_spi_driver = {
+ .driver = {
+ .name = "rt5677",
+ .owner = THIS_MODULE,
+ },
+ .probe = rt5677_spi_probe,
+};
+module_spi_driver(rt5677_spi_driver);
+
+MODULE_DESCRIPTION("ASoC RT5677 SPI driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/rt5677-spi.h b/kernel/sound/soc/codecs/rt5677-spi.h
new file mode 100644
index 000000000..ec41b2b3b
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5677-spi.h
@@ -0,0 +1,21 @@
+/*
+ * 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);
+
+#endif /* __RT5677_SPI_H__ */
diff --git a/kernel/sound/soc/codecs/rt5677.c b/kernel/sound/soc/codecs/rt5677.c
new file mode 100644
index 000000000..169aa471f
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5677.c
@@ -0,0 +1,5159 @@
+/*
+ * 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/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/of_gpio.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 <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 "rt5677.h"
+#include "rt5677-spi.h"
+
+#define RT5677_DEVICE_ID 0x6327
+
+#define RT5677_PR_RANGE_BASE (0xff + 1)
+#define RT5677_PR_SPACING 0x100
+
+#define RT5677_PR_BASE (RT5677_PR_RANGE_BASE + (0 * RT5677_PR_SPACING))
+
+static const struct regmap_range_cfg rt5677_ranges[] = {
+ {
+ .name = "PR",
+ .range_min = RT5677_PR_BASE,
+ .range_max = RT5677_PR_BASE + 0xfd,
+ .selector_reg = RT5677_PRIV_INDEX,
+ .selector_mask = 0xff,
+ .selector_shift = 0x0,
+ .window_start = RT5677_PRIV_DATA,
+ .window_len = 0x1,
+ },
+};
+
+static const struct reg_default init_list[] = {
+ {RT5677_ASRC_12, 0x0018},
+ {RT5677_PR_BASE + 0x3d, 0x364d},
+ {RT5677_PR_BASE + 0x17, 0x4fc0},
+ {RT5677_PR_BASE + 0x13, 0x0312},
+ {RT5677_PR_BASE + 0x1e, 0x0000},
+ {RT5677_PR_BASE + 0x12, 0x0eaa},
+ {RT5677_PR_BASE + 0x14, 0x018a},
+ {RT5677_PR_BASE + 0x15, 0x0490},
+ {RT5677_PR_BASE + 0x38, 0x0f71},
+ {RT5677_PR_BASE + 0x39, 0x0f71},
+};
+#define RT5677_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+static const struct reg_default rt5677_reg[] = {
+ {RT5677_RESET , 0x0000},
+ {RT5677_LOUT1 , 0xa800},
+ {RT5677_IN1 , 0x0000},
+ {RT5677_MICBIAS , 0x0000},
+ {RT5677_SLIMBUS_PARAM , 0x0000},
+ {RT5677_SLIMBUS_RX , 0x0000},
+ {RT5677_SLIMBUS_CTRL , 0x0000},
+ {RT5677_SIDETONE_CTRL , 0x000b},
+ {RT5677_ANA_DAC1_2_3_SRC , 0x0000},
+ {RT5677_IF_DSP_DAC3_4_MIXER , 0x1111},
+ {RT5677_DAC4_DIG_VOL , 0xafaf},
+ {RT5677_DAC3_DIG_VOL , 0xafaf},
+ {RT5677_DAC1_DIG_VOL , 0xafaf},
+ {RT5677_DAC2_DIG_VOL , 0xafaf},
+ {RT5677_IF_DSP_DAC2_MIXER , 0x0011},
+ {RT5677_STO1_ADC_DIG_VOL , 0x2f2f},
+ {RT5677_MONO_ADC_DIG_VOL , 0x2f2f},
+ {RT5677_STO1_2_ADC_BST , 0x0000},
+ {RT5677_STO2_ADC_DIG_VOL , 0x2f2f},
+ {RT5677_ADC_BST_CTRL2 , 0x0000},
+ {RT5677_STO3_4_ADC_BST , 0x0000},
+ {RT5677_STO3_ADC_DIG_VOL , 0x2f2f},
+ {RT5677_STO4_ADC_DIG_VOL , 0x2f2f},
+ {RT5677_STO4_ADC_MIXER , 0xd4c0},
+ {RT5677_STO3_ADC_MIXER , 0xd4c0},
+ {RT5677_STO2_ADC_MIXER , 0xd4c0},
+ {RT5677_STO1_ADC_MIXER , 0xd4c0},
+ {RT5677_MONO_ADC_MIXER , 0xd4d1},
+ {RT5677_ADC_IF_DSP_DAC1_MIXER , 0x8080},
+ {RT5677_STO1_DAC_MIXER , 0xaaaa},
+ {RT5677_MONO_DAC_MIXER , 0xaaaa},
+ {RT5677_DD1_MIXER , 0xaaaa},
+ {RT5677_DD2_MIXER , 0xaaaa},
+ {RT5677_IF3_DATA , 0x0000},
+ {RT5677_IF4_DATA , 0x0000},
+ {RT5677_PDM_OUT_CTRL , 0x8888},
+ {RT5677_PDM_DATA_CTRL1 , 0x0000},
+ {RT5677_PDM_DATA_CTRL2 , 0x0000},
+ {RT5677_PDM1_DATA_CTRL2 , 0x0000},
+ {RT5677_PDM1_DATA_CTRL3 , 0x0000},
+ {RT5677_PDM1_DATA_CTRL4 , 0x0000},
+ {RT5677_PDM2_DATA_CTRL2 , 0x0000},
+ {RT5677_PDM2_DATA_CTRL3 , 0x0000},
+ {RT5677_PDM2_DATA_CTRL4 , 0x0000},
+ {RT5677_TDM1_CTRL1 , 0x0300},
+ {RT5677_TDM1_CTRL2 , 0x0000},
+ {RT5677_TDM1_CTRL3 , 0x4000},
+ {RT5677_TDM1_CTRL4 , 0x0123},
+ {RT5677_TDM1_CTRL5 , 0x4567},
+ {RT5677_TDM2_CTRL1 , 0x0300},
+ {RT5677_TDM2_CTRL2 , 0x0000},
+ {RT5677_TDM2_CTRL3 , 0x4000},
+ {RT5677_TDM2_CTRL4 , 0x0123},
+ {RT5677_TDM2_CTRL5 , 0x4567},
+ {RT5677_I2C_MASTER_CTRL1 , 0x0001},
+ {RT5677_I2C_MASTER_CTRL2 , 0x0000},
+ {RT5677_I2C_MASTER_CTRL3 , 0x0000},
+ {RT5677_I2C_MASTER_CTRL4 , 0x0000},
+ {RT5677_I2C_MASTER_CTRL5 , 0x0000},
+ {RT5677_I2C_MASTER_CTRL6 , 0x0000},
+ {RT5677_I2C_MASTER_CTRL7 , 0x0000},
+ {RT5677_I2C_MASTER_CTRL8 , 0x0000},
+ {RT5677_DMIC_CTRL1 , 0x1505},
+ {RT5677_DMIC_CTRL2 , 0x0055},
+ {RT5677_HAP_GENE_CTRL1 , 0x0111},
+ {RT5677_HAP_GENE_CTRL2 , 0x0064},
+ {RT5677_HAP_GENE_CTRL3 , 0xef0e},
+ {RT5677_HAP_GENE_CTRL4 , 0xf0f0},
+ {RT5677_HAP_GENE_CTRL5 , 0xef0e},
+ {RT5677_HAP_GENE_CTRL6 , 0xf0f0},
+ {RT5677_HAP_GENE_CTRL7 , 0xef0e},
+ {RT5677_HAP_GENE_CTRL8 , 0xf0f0},
+ {RT5677_HAP_GENE_CTRL9 , 0xf000},
+ {RT5677_HAP_GENE_CTRL10 , 0x0000},
+ {RT5677_PWR_DIG1 , 0x0000},
+ {RT5677_PWR_DIG2 , 0x0000},
+ {RT5677_PWR_ANLG1 , 0x0055},
+ {RT5677_PWR_ANLG2 , 0x0000},
+ {RT5677_PWR_DSP1 , 0x0001},
+ {RT5677_PWR_DSP_ST , 0x0000},
+ {RT5677_PWR_DSP2 , 0x0000},
+ {RT5677_ADC_DAC_HPF_CTRL1 , 0x0e00},
+ {RT5677_PRIV_INDEX , 0x0000},
+ {RT5677_PRIV_DATA , 0x0000},
+ {RT5677_I2S4_SDP , 0x8000},
+ {RT5677_I2S1_SDP , 0x8000},
+ {RT5677_I2S2_SDP , 0x8000},
+ {RT5677_I2S3_SDP , 0x8000},
+ {RT5677_CLK_TREE_CTRL1 , 0x1111},
+ {RT5677_CLK_TREE_CTRL2 , 0x1111},
+ {RT5677_CLK_TREE_CTRL3 , 0x0000},
+ {RT5677_PLL1_CTRL1 , 0x0000},
+ {RT5677_PLL1_CTRL2 , 0x0000},
+ {RT5677_PLL2_CTRL1 , 0x0c60},
+ {RT5677_PLL2_CTRL2 , 0x2000},
+ {RT5677_GLB_CLK1 , 0x0000},
+ {RT5677_GLB_CLK2 , 0x0000},
+ {RT5677_ASRC_1 , 0x0000},
+ {RT5677_ASRC_2 , 0x0000},
+ {RT5677_ASRC_3 , 0x0000},
+ {RT5677_ASRC_4 , 0x0000},
+ {RT5677_ASRC_5 , 0x0000},
+ {RT5677_ASRC_6 , 0x0000},
+ {RT5677_ASRC_7 , 0x0000},
+ {RT5677_ASRC_8 , 0x0000},
+ {RT5677_ASRC_9 , 0x0000},
+ {RT5677_ASRC_10 , 0x0000},
+ {RT5677_ASRC_11 , 0x0000},
+ {RT5677_ASRC_12 , 0x0018},
+ {RT5677_ASRC_13 , 0x0000},
+ {RT5677_ASRC_14 , 0x0000},
+ {RT5677_ASRC_15 , 0x0000},
+ {RT5677_ASRC_16 , 0x0000},
+ {RT5677_ASRC_17 , 0x0000},
+ {RT5677_ASRC_18 , 0x0000},
+ {RT5677_ASRC_19 , 0x0000},
+ {RT5677_ASRC_20 , 0x0000},
+ {RT5677_ASRC_21 , 0x000c},
+ {RT5677_ASRC_22 , 0x0000},
+ {RT5677_ASRC_23 , 0x0000},
+ {RT5677_VAD_CTRL1 , 0x2184},
+ {RT5677_VAD_CTRL2 , 0x010a},
+ {RT5677_VAD_CTRL3 , 0x0aea},
+ {RT5677_VAD_CTRL4 , 0x000c},
+ {RT5677_VAD_CTRL5 , 0x0000},
+ {RT5677_DSP_INB_CTRL1 , 0x0000},
+ {RT5677_DSP_INB_CTRL2 , 0x0000},
+ {RT5677_DSP_IN_OUTB_CTRL , 0x0000},
+ {RT5677_DSP_OUTB0_1_DIG_VOL , 0x2f2f},
+ {RT5677_DSP_OUTB2_3_DIG_VOL , 0x2f2f},
+ {RT5677_DSP_OUTB4_5_DIG_VOL , 0x2f2f},
+ {RT5677_DSP_OUTB6_7_DIG_VOL , 0x2f2f},
+ {RT5677_ADC_EQ_CTRL1 , 0x6000},
+ {RT5677_ADC_EQ_CTRL2 , 0x0000},
+ {RT5677_EQ_CTRL1 , 0xc000},
+ {RT5677_EQ_CTRL2 , 0x0000},
+ {RT5677_EQ_CTRL3 , 0x0000},
+ {RT5677_SOFT_VOL_ZERO_CROSS1 , 0x0009},
+ {RT5677_JD_CTRL1 , 0x0000},
+ {RT5677_JD_CTRL2 , 0x0000},
+ {RT5677_JD_CTRL3 , 0x0000},
+ {RT5677_IRQ_CTRL1 , 0x0000},
+ {RT5677_IRQ_CTRL2 , 0x0000},
+ {RT5677_GPIO_ST , 0x0000},
+ {RT5677_GPIO_CTRL1 , 0x0000},
+ {RT5677_GPIO_CTRL2 , 0x0000},
+ {RT5677_GPIO_CTRL3 , 0x0000},
+ {RT5677_STO1_ADC_HI_FILTER1 , 0xb320},
+ {RT5677_STO1_ADC_HI_FILTER2 , 0x0000},
+ {RT5677_MONO_ADC_HI_FILTER1 , 0xb300},
+ {RT5677_MONO_ADC_HI_FILTER2 , 0x0000},
+ {RT5677_STO2_ADC_HI_FILTER1 , 0xb300},
+ {RT5677_STO2_ADC_HI_FILTER2 , 0x0000},
+ {RT5677_STO3_ADC_HI_FILTER1 , 0xb300},
+ {RT5677_STO3_ADC_HI_FILTER2 , 0x0000},
+ {RT5677_STO4_ADC_HI_FILTER1 , 0xb300},
+ {RT5677_STO4_ADC_HI_FILTER2 , 0x0000},
+ {RT5677_MB_DRC_CTRL1 , 0x0f20},
+ {RT5677_DRC1_CTRL1 , 0x001f},
+ {RT5677_DRC1_CTRL2 , 0x020c},
+ {RT5677_DRC1_CTRL3 , 0x1f00},
+ {RT5677_DRC1_CTRL4 , 0x0000},
+ {RT5677_DRC1_CTRL5 , 0x0000},
+ {RT5677_DRC1_CTRL6 , 0x0029},
+ {RT5677_DRC2_CTRL1 , 0x001f},
+ {RT5677_DRC2_CTRL2 , 0x020c},
+ {RT5677_DRC2_CTRL3 , 0x1f00},
+ {RT5677_DRC2_CTRL4 , 0x0000},
+ {RT5677_DRC2_CTRL5 , 0x0000},
+ {RT5677_DRC2_CTRL6 , 0x0029},
+ {RT5677_DRC1_HL_CTRL1 , 0x8000},
+ {RT5677_DRC1_HL_CTRL2 , 0x0200},
+ {RT5677_DRC2_HL_CTRL1 , 0x8000},
+ {RT5677_DRC2_HL_CTRL2 , 0x0200},
+ {RT5677_DSP_INB1_SRC_CTRL1 , 0x5800},
+ {RT5677_DSP_INB1_SRC_CTRL2 , 0x0000},
+ {RT5677_DSP_INB1_SRC_CTRL3 , 0x0000},
+ {RT5677_DSP_INB1_SRC_CTRL4 , 0x0800},
+ {RT5677_DSP_INB2_SRC_CTRL1 , 0x5800},
+ {RT5677_DSP_INB2_SRC_CTRL2 , 0x0000},
+ {RT5677_DSP_INB2_SRC_CTRL3 , 0x0000},
+ {RT5677_DSP_INB2_SRC_CTRL4 , 0x0800},
+ {RT5677_DSP_INB3_SRC_CTRL1 , 0x5800},
+ {RT5677_DSP_INB3_SRC_CTRL2 , 0x0000},
+ {RT5677_DSP_INB3_SRC_CTRL3 , 0x0000},
+ {RT5677_DSP_INB3_SRC_CTRL4 , 0x0800},
+ {RT5677_DSP_OUTB1_SRC_CTRL1 , 0x5800},
+ {RT5677_DSP_OUTB1_SRC_CTRL2 , 0x0000},
+ {RT5677_DSP_OUTB1_SRC_CTRL3 , 0x0000},
+ {RT5677_DSP_OUTB1_SRC_CTRL4 , 0x0800},
+ {RT5677_DSP_OUTB2_SRC_CTRL1 , 0x5800},
+ {RT5677_DSP_OUTB2_SRC_CTRL2 , 0x0000},
+ {RT5677_DSP_OUTB2_SRC_CTRL3 , 0x0000},
+ {RT5677_DSP_OUTB2_SRC_CTRL4 , 0x0800},
+ {RT5677_DSP_OUTB_0123_MIXER_CTRL, 0xfefe},
+ {RT5677_DSP_OUTB_45_MIXER_CTRL , 0xfefe},
+ {RT5677_DSP_OUTB_67_MIXER_CTRL , 0xfefe},
+ {RT5677_DIG_MISC , 0x0000},
+ {RT5677_GEN_CTRL1 , 0x0000},
+ {RT5677_GEN_CTRL2 , 0x0000},
+ {RT5677_VENDOR_ID , 0x0000},
+ {RT5677_VENDOR_ID1 , 0x10ec},
+ {RT5677_VENDOR_ID2 , 0x6327},
+};
+
+static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5677_ranges); i++) {
+ if (reg >= rt5677_ranges[i].range_min &&
+ reg <= rt5677_ranges[i].range_max) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT5677_RESET:
+ case RT5677_SLIMBUS_PARAM:
+ case RT5677_PDM_DATA_CTRL1:
+ case RT5677_PDM_DATA_CTRL2:
+ case RT5677_PDM1_DATA_CTRL4:
+ case RT5677_PDM2_DATA_CTRL4:
+ case RT5677_I2C_MASTER_CTRL1:
+ case RT5677_I2C_MASTER_CTRL7:
+ case RT5677_I2C_MASTER_CTRL8:
+ case RT5677_HAP_GENE_CTRL2:
+ case RT5677_PWR_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:
+ case RT5677_ADC_EQ_CTRL1:
+ case RT5677_EQ_CTRL1:
+ case RT5677_IRQ_CTRL1:
+ case RT5677_IRQ_CTRL2:
+ case RT5677_GPIO_ST:
+ case RT5677_DSP_INB1_SRC_CTRL4:
+ case RT5677_DSP_INB2_SRC_CTRL4:
+ case RT5677_DSP_INB3_SRC_CTRL4:
+ case RT5677_DSP_OUTB1_SRC_CTRL4:
+ case RT5677_DSP_OUTB2_SRC_CTRL4:
+ case RT5677_VENDOR_ID:
+ case RT5677_VENDOR_ID1:
+ case RT5677_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5677_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5677_ranges); i++) {
+ if (reg >= rt5677_ranges[i].range_min &&
+ reg <= rt5677_ranges[i].range_max) {
+ return true;
+ }
+ }
+
+ switch (reg) {
+ case RT5677_RESET:
+ case RT5677_LOUT1:
+ case RT5677_IN1:
+ case RT5677_MICBIAS:
+ case RT5677_SLIMBUS_PARAM:
+ case RT5677_SLIMBUS_RX:
+ case RT5677_SLIMBUS_CTRL:
+ case RT5677_SIDETONE_CTRL:
+ case RT5677_ANA_DAC1_2_3_SRC:
+ case RT5677_IF_DSP_DAC3_4_MIXER:
+ case RT5677_DAC4_DIG_VOL:
+ case RT5677_DAC3_DIG_VOL:
+ case RT5677_DAC1_DIG_VOL:
+ case RT5677_DAC2_DIG_VOL:
+ case RT5677_IF_DSP_DAC2_MIXER:
+ case RT5677_STO1_ADC_DIG_VOL:
+ case RT5677_MONO_ADC_DIG_VOL:
+ case RT5677_STO1_2_ADC_BST:
+ case RT5677_STO2_ADC_DIG_VOL:
+ case RT5677_ADC_BST_CTRL2:
+ case RT5677_STO3_4_ADC_BST:
+ case RT5677_STO3_ADC_DIG_VOL:
+ case RT5677_STO4_ADC_DIG_VOL:
+ case RT5677_STO4_ADC_MIXER:
+ case RT5677_STO3_ADC_MIXER:
+ case RT5677_STO2_ADC_MIXER:
+ case RT5677_STO1_ADC_MIXER:
+ case RT5677_MONO_ADC_MIXER:
+ case RT5677_ADC_IF_DSP_DAC1_MIXER:
+ case RT5677_STO1_DAC_MIXER:
+ case RT5677_MONO_DAC_MIXER:
+ case RT5677_DD1_MIXER:
+ case RT5677_DD2_MIXER:
+ case RT5677_IF3_DATA:
+ case RT5677_IF4_DATA:
+ case RT5677_PDM_OUT_CTRL:
+ case RT5677_PDM_DATA_CTRL1:
+ case RT5677_PDM_DATA_CTRL2:
+ case RT5677_PDM1_DATA_CTRL2:
+ case RT5677_PDM1_DATA_CTRL3:
+ case RT5677_PDM1_DATA_CTRL4:
+ case RT5677_PDM2_DATA_CTRL2:
+ case RT5677_PDM2_DATA_CTRL3:
+ case RT5677_PDM2_DATA_CTRL4:
+ case RT5677_TDM1_CTRL1:
+ case RT5677_TDM1_CTRL2:
+ case RT5677_TDM1_CTRL3:
+ case RT5677_TDM1_CTRL4:
+ case RT5677_TDM1_CTRL5:
+ case RT5677_TDM2_CTRL1:
+ case RT5677_TDM2_CTRL2:
+ case RT5677_TDM2_CTRL3:
+ case RT5677_TDM2_CTRL4:
+ case RT5677_TDM2_CTRL5:
+ case RT5677_I2C_MASTER_CTRL1:
+ case RT5677_I2C_MASTER_CTRL2:
+ case RT5677_I2C_MASTER_CTRL3:
+ case RT5677_I2C_MASTER_CTRL4:
+ case RT5677_I2C_MASTER_CTRL5:
+ case RT5677_I2C_MASTER_CTRL6:
+ case RT5677_I2C_MASTER_CTRL7:
+ case RT5677_I2C_MASTER_CTRL8:
+ case RT5677_DMIC_CTRL1:
+ case RT5677_DMIC_CTRL2:
+ case RT5677_HAP_GENE_CTRL1:
+ case RT5677_HAP_GENE_CTRL2:
+ case RT5677_HAP_GENE_CTRL3:
+ case RT5677_HAP_GENE_CTRL4:
+ case RT5677_HAP_GENE_CTRL5:
+ case RT5677_HAP_GENE_CTRL6:
+ case RT5677_HAP_GENE_CTRL7:
+ case RT5677_HAP_GENE_CTRL8:
+ case RT5677_HAP_GENE_CTRL9:
+ case RT5677_HAP_GENE_CTRL10:
+ case RT5677_PWR_DIG1:
+ case RT5677_PWR_DIG2:
+ case RT5677_PWR_ANLG1:
+ case RT5677_PWR_ANLG2:
+ case RT5677_PWR_DSP1:
+ case RT5677_PWR_DSP_ST:
+ case RT5677_PWR_DSP2:
+ case RT5677_ADC_DAC_HPF_CTRL1:
+ case RT5677_PRIV_INDEX:
+ case RT5677_PRIV_DATA:
+ case RT5677_I2S4_SDP:
+ case RT5677_I2S1_SDP:
+ case RT5677_I2S2_SDP:
+ case RT5677_I2S3_SDP:
+ case RT5677_CLK_TREE_CTRL1:
+ case RT5677_CLK_TREE_CTRL2:
+ case RT5677_CLK_TREE_CTRL3:
+ case RT5677_PLL1_CTRL1:
+ case RT5677_PLL1_CTRL2:
+ case RT5677_PLL2_CTRL1:
+ case RT5677_PLL2_CTRL2:
+ case RT5677_GLB_CLK1:
+ case RT5677_GLB_CLK2:
+ case RT5677_ASRC_1:
+ case RT5677_ASRC_2:
+ case RT5677_ASRC_3:
+ case RT5677_ASRC_4:
+ case RT5677_ASRC_5:
+ case RT5677_ASRC_6:
+ case RT5677_ASRC_7:
+ case RT5677_ASRC_8:
+ case RT5677_ASRC_9:
+ case RT5677_ASRC_10:
+ case RT5677_ASRC_11:
+ case RT5677_ASRC_12:
+ case RT5677_ASRC_13:
+ case RT5677_ASRC_14:
+ case RT5677_ASRC_15:
+ case RT5677_ASRC_16:
+ case RT5677_ASRC_17:
+ case RT5677_ASRC_18:
+ case RT5677_ASRC_19:
+ case RT5677_ASRC_20:
+ case RT5677_ASRC_21:
+ case RT5677_ASRC_22:
+ case RT5677_ASRC_23:
+ case RT5677_VAD_CTRL1:
+ case RT5677_VAD_CTRL2:
+ case RT5677_VAD_CTRL3:
+ case RT5677_VAD_CTRL4:
+ case RT5677_VAD_CTRL5:
+ case RT5677_DSP_INB_CTRL1:
+ case RT5677_DSP_INB_CTRL2:
+ case RT5677_DSP_IN_OUTB_CTRL:
+ case RT5677_DSP_OUTB0_1_DIG_VOL:
+ case RT5677_DSP_OUTB2_3_DIG_VOL:
+ case RT5677_DSP_OUTB4_5_DIG_VOL:
+ case RT5677_DSP_OUTB6_7_DIG_VOL:
+ case RT5677_ADC_EQ_CTRL1:
+ case RT5677_ADC_EQ_CTRL2:
+ case RT5677_EQ_CTRL1:
+ case RT5677_EQ_CTRL2:
+ case RT5677_EQ_CTRL3:
+ case RT5677_SOFT_VOL_ZERO_CROSS1:
+ case RT5677_JD_CTRL1:
+ case RT5677_JD_CTRL2:
+ case RT5677_JD_CTRL3:
+ case RT5677_IRQ_CTRL1:
+ case RT5677_IRQ_CTRL2:
+ case RT5677_GPIO_ST:
+ case RT5677_GPIO_CTRL1:
+ case RT5677_GPIO_CTRL2:
+ case RT5677_GPIO_CTRL3:
+ case RT5677_STO1_ADC_HI_FILTER1:
+ case RT5677_STO1_ADC_HI_FILTER2:
+ case RT5677_MONO_ADC_HI_FILTER1:
+ case RT5677_MONO_ADC_HI_FILTER2:
+ case RT5677_STO2_ADC_HI_FILTER1:
+ case RT5677_STO2_ADC_HI_FILTER2:
+ case RT5677_STO3_ADC_HI_FILTER1:
+ case RT5677_STO3_ADC_HI_FILTER2:
+ case RT5677_STO4_ADC_HI_FILTER1:
+ case RT5677_STO4_ADC_HI_FILTER2:
+ case RT5677_MB_DRC_CTRL1:
+ case RT5677_DRC1_CTRL1:
+ case RT5677_DRC1_CTRL2:
+ case RT5677_DRC1_CTRL3:
+ case RT5677_DRC1_CTRL4:
+ case RT5677_DRC1_CTRL5:
+ case RT5677_DRC1_CTRL6:
+ case RT5677_DRC2_CTRL1:
+ case RT5677_DRC2_CTRL2:
+ case RT5677_DRC2_CTRL3:
+ case RT5677_DRC2_CTRL4:
+ case RT5677_DRC2_CTRL5:
+ case RT5677_DRC2_CTRL6:
+ case RT5677_DRC1_HL_CTRL1:
+ case RT5677_DRC1_HL_CTRL2:
+ case RT5677_DRC2_HL_CTRL1:
+ case RT5677_DRC2_HL_CTRL2:
+ case RT5677_DSP_INB1_SRC_CTRL1:
+ case RT5677_DSP_INB1_SRC_CTRL2:
+ case RT5677_DSP_INB1_SRC_CTRL3:
+ case RT5677_DSP_INB1_SRC_CTRL4:
+ case RT5677_DSP_INB2_SRC_CTRL1:
+ case RT5677_DSP_INB2_SRC_CTRL2:
+ case RT5677_DSP_INB2_SRC_CTRL3:
+ case RT5677_DSP_INB2_SRC_CTRL4:
+ case RT5677_DSP_INB3_SRC_CTRL1:
+ case RT5677_DSP_INB3_SRC_CTRL2:
+ case RT5677_DSP_INB3_SRC_CTRL3:
+ case RT5677_DSP_INB3_SRC_CTRL4:
+ case RT5677_DSP_OUTB1_SRC_CTRL1:
+ case RT5677_DSP_OUTB1_SRC_CTRL2:
+ case RT5677_DSP_OUTB1_SRC_CTRL3:
+ case RT5677_DSP_OUTB1_SRC_CTRL4:
+ case RT5677_DSP_OUTB2_SRC_CTRL1:
+ case RT5677_DSP_OUTB2_SRC_CTRL2:
+ case RT5677_DSP_OUTB2_SRC_CTRL3:
+ case RT5677_DSP_OUTB2_SRC_CTRL4:
+ case RT5677_DSP_OUTB_0123_MIXER_CTRL:
+ case RT5677_DSP_OUTB_45_MIXER_CTRL:
+ case RT5677_DSP_OUTB_67_MIXER_CTRL:
+ case RT5677_DIG_MISC:
+ case RT5677_GEN_CTRL1:
+ case RT5677_GEN_CTRL2:
+ case RT5677_VENDOR_ID:
+ case RT5677_VENDOR_ID1:
+ case RT5677_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * rt5677_dsp_mode_i2c_write_addr - Write value to address on DSP mode.
+ * @rt5677: Private Data.
+ * @addr: Address index.
+ * @value: Address data.
+ *
+ *
+ * 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;
+ int ret;
+
+ mutex_lock(&rt5677->dsp_cmd_lock);
+
+ ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
+ addr >> 16);
+ if (ret < 0) {
+ dev_err(codec->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);
+ 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);
+ 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);
+ 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);
+ goto err;
+ }
+
+err:
+ mutex_unlock(&rt5677->dsp_cmd_lock);
+
+ return ret;
+}
+
+/**
+ * rt5677_dsp_mode_i2c_read_addr - Read value from address on DSP mode.
+ * rt5677: Private Data.
+ * @addr: Address index.
+ * @value: Address data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_read_addr(
+ struct rt5677_priv *rt5677, unsigned int addr, unsigned int *value)
+{
+ struct snd_soc_codec *codec = rt5677->codec;
+ int ret;
+ unsigned int msb, lsb;
+
+ mutex_lock(&rt5677->dsp_cmd_lock);
+
+ ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
+ addr >> 16);
+ if (ret < 0) {
+ dev_err(codec->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);
+ 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);
+ goto err;
+ }
+
+ regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, &msb);
+ regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, &lsb);
+ *value = (msb << 16) | lsb;
+
+err:
+ mutex_unlock(&rt5677->dsp_cmd_lock);
+
+ return ret;
+}
+
+/**
+ * rt5677_dsp_mode_i2c_write - Write register on DSP mode.
+ * rt5677: Private Data.
+ * @reg: Register index.
+ * @value: Register data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_write(struct rt5677_priv *rt5677,
+ unsigned int reg, unsigned int value)
+{
+ return rt5677_dsp_mode_i2c_write_addr(rt5677, 0x18020000 + reg * 2,
+ value, 0x0001);
+}
+
+/**
+ * rt5677_dsp_mode_i2c_read - Read register on DSP mode.
+ * @codec: SoC audio codec device.
+ * @reg: Register index.
+ * @value: Register data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_read(
+ struct rt5677_priv *rt5677, unsigned int reg, unsigned int *value)
+{
+ int ret = rt5677_dsp_mode_i2c_read_addr(rt5677, 0x18020000 + reg * 2,
+ value);
+
+ *value &= 0xffff;
+
+ return ret;
+}
+
+static void rt5677_set_dsp_mode(struct snd_soc_codec *codec, bool on)
+{
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+ if (on) {
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x2);
+ rt5677->is_dsp_mode = true;
+ } else {
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x0);
+ rt5677->is_dsp_mode = false;
+ }
+}
+
+static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on)
+{
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ static bool activity;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI))
+ return -ENXIO;
+
+ if (on && !activity) {
+ activity = true;
+
+ regcache_cache_only(rt5677->regmap, false);
+ regcache_cache_bypass(rt5677->regmap, true);
+
+ 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);
+ switch (rt5677->type) {
+ case RT5677:
+ 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);
+ break;
+ case RT5676:
+ regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
+ RT5677_DSP_CLK_SRC_MASK,
+ RT5677_DSP_CLK_SRC_BYPASS);
+ break;
+ default:
+ break;
+ }
+ 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);
+ }
+
+ ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2,
+ codec->dev);
+ if (ret == 0) {
+ rt5677_spi_burst_write(0x60000000, rt5677->fw2);
+ release_firmware(rt5677->fw2);
+ }
+
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x0);
+
+ regcache_cache_bypass(rt5677->regmap, false);
+ regcache_cache_only(rt5677->regmap, true);
+ } else if (!on && activity) {
+ activity = false;
+
+ regcache_cache_only(rt5677->regmap, false);
+ regcache_cache_bypass(rt5677->regmap, true);
+
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x1);
+ rt5677_set_dsp_mode(codec, false);
+ regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x0001);
+
+ regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+
+ regcache_cache_bypass(rt5677->regmap, false);
+ regcache_mark_dirty(rt5677->regmap);
+ regcache_sync(rt5677->regmap);
+ }
+
+ return 0;
+}
+
+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),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
+};
+
+static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt5677->dsp_vad_en;
+
+ return 0;
+}
+
+static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ 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);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt5677_snd_controls[] = {
+ /* OUTPUT Control */
+ SOC_SINGLE("OUT1 Playback Switch", RT5677_LOUT1,
+ RT5677_LOUT1_L_MUTE_SFT, 1, 1),
+ SOC_SINGLE("OUT2 Playback Switch", RT5677_LOUT1,
+ RT5677_LOUT2_L_MUTE_SFT, 1, 1),
+ SOC_SINGLE("OUT3 Playback Switch", RT5677_LOUT1,
+ RT5677_LOUT3_L_MUTE_SFT, 1, 1),
+
+ /* DAC Digital Volume */
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5677_DAC1_DIG_VOL,
+ RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 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),
+ SOC_DOUBLE_TLV("DAC3 Playback Volume", RT5677_DAC3_DIG_VOL,
+ RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 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),
+
+ /* IN1/IN2 Control */
+ SOC_SINGLE_TLV("IN1 Boost", RT5677_IN1, RT5677_BST_SFT1, 8, 0, bst_tlv),
+ SOC_SINGLE_TLV("IN2 Boost", RT5677_IN1, RT5677_BST_SFT2, 8, 0, bst_tlv),
+
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("ADC1 Capture Switch", RT5677_STO1_ADC_DIG_VOL,
+ RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("ADC2 Capture Switch", RT5677_STO2_ADC_DIG_VOL,
+ RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("ADC3 Capture Switch", RT5677_STO3_ADC_DIG_VOL,
+ RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("ADC4 Capture Switch", RT5677_STO4_ADC_DIG_VOL,
+ RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("Mono ADC Capture Switch", RT5677_MONO_ADC_DIG_VOL,
+ RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1),
+
+ SOC_DOUBLE_TLV("ADC1 Capture Volume", RT5677_STO1_ADC_DIG_VOL,
+ RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
+ adc_vol_tlv),
+ SOC_DOUBLE_TLV("ADC2 Capture Volume", RT5677_STO2_ADC_DIG_VOL,
+ RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
+ adc_vol_tlv),
+ SOC_DOUBLE_TLV("ADC3 Capture Volume", RT5677_STO3_ADC_DIG_VOL,
+ RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
+ adc_vol_tlv),
+ SOC_DOUBLE_TLV("ADC4 Capture Volume", RT5677_STO4_ADC_DIG_VOL,
+ RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
+ adc_vol_tlv),
+ SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5677_MONO_ADC_DIG_VOL,
+ RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 63, 0,
+ adc_vol_tlv),
+
+ /* Sidetone Control */
+ SOC_SINGLE_TLV("Sidetone Volume", RT5677_SIDETONE_CTRL,
+ RT5677_ST_VOL_SFT, 31, 0, st_vol_tlv),
+
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("STO1 ADC Boost Volume", RT5677_STO1_2_ADC_BST,
+ RT5677_STO1_ADC_L_BST_SFT, RT5677_STO1_ADC_R_BST_SFT, 3, 0,
+ adc_bst_tlv),
+ SOC_DOUBLE_TLV("STO2 ADC Boost Volume", RT5677_STO1_2_ADC_BST,
+ RT5677_STO2_ADC_L_BST_SFT, RT5677_STO2_ADC_R_BST_SFT, 3, 0,
+ adc_bst_tlv),
+ SOC_DOUBLE_TLV("STO3 ADC Boost Volume", RT5677_STO3_4_ADC_BST,
+ RT5677_STO3_ADC_L_BST_SFT, RT5677_STO3_ADC_R_BST_SFT, 3, 0,
+ adc_bst_tlv),
+ SOC_DOUBLE_TLV("STO4 ADC Boost Volume", RT5677_STO3_4_ADC_BST,
+ RT5677_STO4_ADC_L_BST_SFT, RT5677_STO4_ADC_R_BST_SFT, 3, 0,
+ adc_bst_tlv),
+ SOC_DOUBLE_TLV("Mono ADC Boost Volume", RT5677_ADC_BST_CTRL2,
+ RT5677_MONO_ADC_L_BST_SFT, RT5677_MONO_ADC_R_BST_SFT, 3, 0,
+ adc_bst_tlv),
+
+ SOC_SINGLE_EXT("DSP VAD Switch", SND_SOC_NOPM, 0, 1, 0,
+ rt5677_dsp_vad_get, rt5677_dsp_vad_put),
+};
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_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->lrck[RT5677_AIF1] << 8);
+
+ if (idx < 0)
+ dev_err(codec->dev, "Failed to set DMIC clock\n");
+ else
+ regmap_update_bits(rt5677->regmap, RT5677_DMIC_CTRL1,
+ RT5677_DMIC_CLK_MASK, idx << RT5677_DMIC_CLK_SFT);
+ return idx;
+}
+
+static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ regmap_read(rt5677->regmap, RT5677_GLB_CLK1, &val);
+ val &= RT5677_SCLK_SRC_MASK;
+ if (val == RT5677_SCLK_SRC_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static int is_using_asrc(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg, shift, val;
+
+ if (source->reg == RT5677_ASRC_1) {
+ switch (source->shift) {
+ case 12:
+ reg = RT5677_ASRC_4;
+ shift = 0;
+ break;
+ case 13:
+ reg = RT5677_ASRC_4;
+ shift = 4;
+ break;
+ case 14:
+ reg = RT5677_ASRC_4;
+ shift = 8;
+ break;
+ case 15:
+ reg = RT5677_ASRC_4;
+ shift = 12;
+ break;
+ default:
+ return 0;
+ }
+ } else {
+ switch (source->shift) {
+ case 0:
+ reg = RT5677_ASRC_6;
+ shift = 8;
+ break;
+ case 1:
+ reg = RT5677_ASRC_6;
+ shift = 12;
+ break;
+ case 2:
+ reg = RT5677_ASRC_5;
+ shift = 0;
+ break;
+ case 3:
+ reg = RT5677_ASRC_5;
+ shift = 4;
+ break;
+ case 4:
+ reg = RT5677_ASRC_5;
+ shift = 8;
+ break;
+ case 5:
+ reg = RT5677_ASRC_5;
+ shift = 12;
+ break;
+ case 12:
+ reg = RT5677_ASRC_3;
+ shift = 0;
+ break;
+ case 13:
+ reg = RT5677_ASRC_3;
+ shift = 4;
+ break;
+ case 14:
+ reg = RT5677_ASRC_3;
+ shift = 12;
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ regmap_read(rt5677->regmap, reg, &val);
+ val = (val >> shift) & 0xf;
+
+ switch (val) {
+ case 1 ... 6:
+ return 1;
+ default:
+ return 0;
+ }
+
+}
+
+static int can_use_asrc(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+ if (rt5677->sysclk > rt5677->lrck[RT5677_AIF1] * 384)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * rt5677_sel_asrc_clk_src - select ASRC clock source for a set of filters
+ * @codec: SoC audio codec 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_codec *codec,
+ unsigned int filter_mask, unsigned int clk_src)
+{
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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;
+
+ 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);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5677_sel_asrc_clk_src);
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5677_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO1_ADC_MIXER,
+ RT5677_M_STO1_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO1_ADC_MIXER,
+ RT5677_M_STO1_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO1_ADC_MIXER,
+ RT5677_M_STO1_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO1_ADC_MIXER,
+ RT5677_M_STO1_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto2_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO2_ADC_MIXER,
+ RT5677_M_STO2_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO2_ADC_MIXER,
+ RT5677_M_STO2_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto2_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO2_ADC_MIXER,
+ RT5677_M_STO2_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO2_ADC_MIXER,
+ RT5677_M_STO2_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto3_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO3_ADC_MIXER,
+ RT5677_M_STO3_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO3_ADC_MIXER,
+ RT5677_M_STO3_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto3_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO3_ADC_MIXER,
+ RT5677_M_STO3_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO3_ADC_MIXER,
+ RT5677_M_STO3_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto4_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO4_ADC_MIXER,
+ RT5677_M_STO4_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO4_ADC_MIXER,
+ RT5677_M_STO4_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto4_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO4_ADC_MIXER,
+ RT5677_M_STO4_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO4_ADC_MIXER,
+ RT5677_M_STO4_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_mono_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5677_MONO_ADC_MIXER,
+ RT5677_M_MONO_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5677_MONO_ADC_MIXER,
+ RT5677_M_MONO_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_mono_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5677_MONO_ADC_MIXER,
+ RT5677_M_MONO_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5677_MONO_ADC_MIXER,
+ RT5677_M_MONO_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5677_ADC_IF_DSP_DAC1_MIXER,
+ RT5677_M_ADDA_MIXER1_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5677_ADC_IF_DSP_DAC1_MIXER,
+ RT5677_M_DAC1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5677_ADC_IF_DSP_DAC1_MIXER,
+ RT5677_M_ADDA_MIXER1_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC1 Switch", RT5677_ADC_IF_DSP_DAC1_MIXER,
+ RT5677_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto1_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_DAC1_L_STO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ 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,
+ RT5677_M_ST_DAC1_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_DAC2_R_STO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_ST_DAC2_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_DAC2_L_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_ST_DAC2_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_DAC2_R_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_STO_L_DD1_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_DAC3_L_DD1_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_STO_R_DD1_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_DAC3_R_DD1_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_STO_L_DD2_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_DAC4_L_DD2_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_STO_R_DD2_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("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,
+ RT5677_M_DAC4_R_DD2_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER,
+ RT5677_M_DAC4_L_DD2_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_ob_01_mix[] = {
+ SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_01_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_23_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_45_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_6_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_7_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_8_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_9_H_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_ob_23_mix[] = {
+ SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_01_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_23_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_45_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_6_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_7_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_8_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+ RT5677_DSP_IB_9_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_ob_4_mix[] = {
+ SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_01_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_23_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_45_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_6_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_7_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_8_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_9_H_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_ob_5_mix[] = {
+ SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_01_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_23_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_45_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_6_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_7_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_8_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+ RT5677_DSP_IB_9_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_ob_6_mix[] = {
+ SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_01_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_23_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_45_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_6_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_7_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_8_H_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_9_H_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_ob_7_mix[] = {
+ SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_01_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_23_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_45_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_6_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_7_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_8_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+ RT5677_DSP_IB_9_L_SFT, 1, 1),
+};
+
+
+/* Mux */
+/* DAC1 L/R Source */ /* MX-29 [10:8] */
+static const char * const rt5677_dac1_src[] = {
+ "IF1 DAC 01", "IF2 DAC 01", "IF3 DAC LR", "IF4 DAC LR", "SLB DAC 01",
+ "OB 01"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_dac1_enum, RT5677_ADC_IF_DSP_DAC1_MIXER,
+ RT5677_DAC1_L_SEL_SFT, rt5677_dac1_src);
+
+static const struct snd_kcontrol_new rt5677_dac1_mux =
+ SOC_DAPM_ENUM("DAC1 Source", rt5677_dac1_enum);
+
+/* ADDA1 L/R Source */ /* MX-29 [1:0] */
+static const char * const rt5677_adda1_src[] = {
+ "STO1 ADC MIX", "STO2 ADC MIX", "OB 67",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_adda1_enum, RT5677_ADC_IF_DSP_DAC1_MIXER,
+ RT5677_ADDA1_SEL_SFT, rt5677_adda1_src);
+
+static const struct snd_kcontrol_new rt5677_adda1_mux =
+ SOC_DAPM_ENUM("ADDA1 Source", rt5677_adda1_enum);
+
+
+/*DAC2 L/R Source*/ /* MX-1B [6:4] [2:0] */
+static const char * const rt5677_dac2l_src[] = {
+ "IF1 DAC 2", "IF2 DAC 2", "IF3 DAC L", "IF4 DAC L", "SLB DAC 2",
+ "OB 2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_dac2l_enum, RT5677_IF_DSP_DAC2_MIXER,
+ RT5677_SEL_DAC2_L_SRC_SFT, rt5677_dac2l_src);
+
+static const struct snd_kcontrol_new rt5677_dac2_l_mux =
+ SOC_DAPM_ENUM("DAC2 L Source", rt5677_dac2l_enum);
+
+static const char * const rt5677_dac2r_src[] = {
+ "IF1 DAC 3", "IF2 DAC 3", "IF3 DAC R", "IF4 DAC R", "SLB DAC 3",
+ "OB 3", "Haptic Generator", "VAD ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_dac2r_enum, RT5677_IF_DSP_DAC2_MIXER,
+ RT5677_SEL_DAC2_R_SRC_SFT, rt5677_dac2r_src);
+
+static const struct snd_kcontrol_new rt5677_dac2_r_mux =
+ SOC_DAPM_ENUM("DAC2 R Source", rt5677_dac2r_enum);
+
+/*DAC3 L/R Source*/ /* MX-16 [6:4] [2:0] */
+static const char * const rt5677_dac3l_src[] = {
+ "IF1 DAC 4", "IF2 DAC 4", "IF3 DAC L", "IF4 DAC L",
+ "SLB DAC 4", "OB 4"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_dac3l_enum, RT5677_IF_DSP_DAC3_4_MIXER,
+ RT5677_SEL_DAC3_L_SRC_SFT, rt5677_dac3l_src);
+
+static const struct snd_kcontrol_new rt5677_dac3_l_mux =
+ SOC_DAPM_ENUM("DAC3 L Source", rt5677_dac3l_enum);
+
+static const char * const rt5677_dac3r_src[] = {
+ "IF1 DAC 5", "IF2 DAC 5", "IF3 DAC R", "IF4 DAC R",
+ "SLB DAC 5", "OB 5"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_dac3r_enum, RT5677_IF_DSP_DAC3_4_MIXER,
+ RT5677_SEL_DAC3_R_SRC_SFT, rt5677_dac3r_src);
+
+static const struct snd_kcontrol_new rt5677_dac3_r_mux =
+ SOC_DAPM_ENUM("DAC3 R Source", rt5677_dac3r_enum);
+
+/*DAC4 L/R Source*/ /* MX-16 [14:12] [10:8] */
+static const char * const rt5677_dac4l_src[] = {
+ "IF1 DAC 6", "IF2 DAC 6", "IF3 DAC L", "IF4 DAC L",
+ "SLB DAC 6", "OB 6"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_dac4l_enum, RT5677_IF_DSP_DAC3_4_MIXER,
+ RT5677_SEL_DAC4_L_SRC_SFT, rt5677_dac4l_src);
+
+static const struct snd_kcontrol_new rt5677_dac4_l_mux =
+ SOC_DAPM_ENUM("DAC4 L Source", rt5677_dac4l_enum);
+
+static const char * const rt5677_dac4r_src[] = {
+ "IF1 DAC 7", "IF2 DAC 7", "IF3 DAC R", "IF4 DAC R",
+ "SLB DAC 7", "OB 7"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_dac4r_enum, RT5677_IF_DSP_DAC3_4_MIXER,
+ RT5677_SEL_DAC4_R_SRC_SFT, rt5677_dac4r_src);
+
+static const struct snd_kcontrol_new rt5677_dac4_r_mux =
+ SOC_DAPM_ENUM("DAC4 R Source", rt5677_dac4r_enum);
+
+/* In/OutBound Source Pass SRC */ /* MX-A5 [3] [4] [0] [1] [2] */
+static const char * const rt5677_iob_bypass_src[] = {
+ "Bypass", "Pass SRC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_ob01_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL,
+ RT5677_SEL_SRC_OB01_SFT, rt5677_iob_bypass_src);
+
+static const struct snd_kcontrol_new rt5677_ob01_bypass_src_mux =
+ SOC_DAPM_ENUM("OB01 Bypass Source", rt5677_ob01_bypass_src_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_ob23_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL,
+ RT5677_SEL_SRC_OB23_SFT, rt5677_iob_bypass_src);
+
+static const struct snd_kcontrol_new rt5677_ob23_bypass_src_mux =
+ SOC_DAPM_ENUM("OB23 Bypass Source", rt5677_ob23_bypass_src_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_ib01_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL,
+ RT5677_SEL_SRC_IB01_SFT, rt5677_iob_bypass_src);
+
+static const struct snd_kcontrol_new rt5677_ib01_bypass_src_mux =
+ SOC_DAPM_ENUM("IB01 Bypass Source", rt5677_ib01_bypass_src_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_ib23_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL,
+ RT5677_SEL_SRC_IB23_SFT, rt5677_iob_bypass_src);
+
+static const struct snd_kcontrol_new rt5677_ib23_bypass_src_mux =
+ SOC_DAPM_ENUM("IB23 Bypass Source", rt5677_ib23_bypass_src_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_ib45_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL,
+ RT5677_SEL_SRC_IB45_SFT, rt5677_iob_bypass_src);
+
+static const struct snd_kcontrol_new rt5677_ib45_bypass_src_mux =
+ SOC_DAPM_ENUM("IB45 Bypass Source", rt5677_ib45_bypass_src_enum);
+
+/* Stereo ADC Source 2 */ /* MX-27 MX26 MX25 [11:10] */
+static const char * const rt5677_stereo_adc2_src[] = {
+ "DD MIX1", "DMIC", "Stereo DAC MIX"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_stereo1_adc2_enum, RT5677_STO1_ADC_MIXER,
+ RT5677_SEL_STO1_ADC2_SFT, rt5677_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_sto1_adc2_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC2 Source", rt5677_stereo1_adc2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_stereo2_adc2_enum, RT5677_STO2_ADC_MIXER,
+ RT5677_SEL_STO2_ADC2_SFT, rt5677_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_sto2_adc2_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC2 Source", rt5677_stereo2_adc2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_stereo3_adc2_enum, RT5677_STO3_ADC_MIXER,
+ RT5677_SEL_STO3_ADC2_SFT, rt5677_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_sto3_adc2_mux =
+ SOC_DAPM_ENUM("Stereo3 ADC2 Source", rt5677_stereo3_adc2_enum);
+
+/* DMIC Source */ /* MX-28 [9:8][1:0] MX-27 MX-26 MX-25 MX-24 [9:8] */
+static const char * const rt5677_dmic_src[] = {
+ "DMIC1", "DMIC2", "DMIC3", "DMIC4"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_mono_dmic_l_enum, RT5677_MONO_ADC_MIXER,
+ RT5677_SEL_MONO_DMIC_L_SFT, rt5677_dmic_src);
+
+static const struct snd_kcontrol_new rt5677_mono_dmic_l_mux =
+ SOC_DAPM_ENUM("Mono DMIC L Source", rt5677_mono_dmic_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_mono_dmic_r_enum, RT5677_MONO_ADC_MIXER,
+ RT5677_SEL_MONO_DMIC_R_SFT, rt5677_dmic_src);
+
+static const struct snd_kcontrol_new rt5677_mono_dmic_r_mux =
+ SOC_DAPM_ENUM("Mono DMIC R Source", rt5677_mono_dmic_r_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_stereo1_dmic_enum, RT5677_STO1_ADC_MIXER,
+ RT5677_SEL_STO1_DMIC_SFT, rt5677_dmic_src);
+
+static const struct snd_kcontrol_new rt5677_sto1_dmic_mux =
+ SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5677_stereo1_dmic_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_stereo2_dmic_enum, RT5677_STO2_ADC_MIXER,
+ RT5677_SEL_STO2_DMIC_SFT, rt5677_dmic_src);
+
+static const struct snd_kcontrol_new rt5677_sto2_dmic_mux =
+ SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5677_stereo2_dmic_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_stereo3_dmic_enum, RT5677_STO3_ADC_MIXER,
+ RT5677_SEL_STO3_DMIC_SFT, rt5677_dmic_src);
+
+static const struct snd_kcontrol_new rt5677_sto3_dmic_mux =
+ SOC_DAPM_ENUM("Stereo3 DMIC Source", rt5677_stereo3_dmic_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_stereo4_dmic_enum, RT5677_STO4_ADC_MIXER,
+ RT5677_SEL_STO4_DMIC_SFT, rt5677_dmic_src);
+
+static const struct snd_kcontrol_new rt5677_sto4_dmic_mux =
+ SOC_DAPM_ENUM("Stereo4 DMIC Source", rt5677_stereo4_dmic_enum);
+
+/* Stereo2 ADC Source */ /* MX-26 [0] */
+static const char * const rt5677_stereo2_adc_lr_src[] = {
+ "L", "LR"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_stereo2_adc_lr_enum, RT5677_STO2_ADC_MIXER,
+ RT5677_SEL_STO2_LR_MIX_SFT, rt5677_stereo2_adc_lr_src);
+
+static const struct snd_kcontrol_new rt5677_sto2_adc_lr_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC LR Source", rt5677_stereo2_adc_lr_enum);
+
+/* Stereo1 ADC Source 1 */ /* MX-27 MX26 MX25 [13:12] */
+static const char * const rt5677_stereo_adc1_src[] = {
+ "DD MIX1", "ADC1/2", "Stereo DAC MIX"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_stereo1_adc1_enum, RT5677_STO1_ADC_MIXER,
+ RT5677_SEL_STO1_ADC1_SFT, rt5677_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_sto1_adc1_mux =
+ SOC_DAPM_ENUM("Stereo1 ADC1 Source", rt5677_stereo1_adc1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_stereo2_adc1_enum, RT5677_STO2_ADC_MIXER,
+ RT5677_SEL_STO2_ADC1_SFT, rt5677_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_sto2_adc1_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC1 Source", rt5677_stereo2_adc1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_stereo3_adc1_enum, RT5677_STO3_ADC_MIXER,
+ RT5677_SEL_STO3_ADC1_SFT, rt5677_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_sto3_adc1_mux =
+ SOC_DAPM_ENUM("Stereo3 ADC1 Source", rt5677_stereo3_adc1_enum);
+
+/* Mono ADC Left Source 2 */ /* MX-28 [11:10] */
+static const char * const rt5677_mono_adc2_l_src[] = {
+ "DD MIX1L", "DMIC", "MONO DAC MIXL"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_mono_adc2_l_enum, RT5677_MONO_ADC_MIXER,
+ RT5677_SEL_MONO_ADC_L2_SFT, rt5677_mono_adc2_l_src);
+
+static const struct snd_kcontrol_new rt5677_mono_adc2_l_mux =
+ SOC_DAPM_ENUM("Mono ADC2 L Source", rt5677_mono_adc2_l_enum);
+
+/* Mono ADC Left Source 1 */ /* MX-28 [13:12] */
+static const char * const rt5677_mono_adc1_l_src[] = {
+ "DD MIX1L", "ADC1", "MONO DAC MIXL"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_mono_adc1_l_enum, RT5677_MONO_ADC_MIXER,
+ RT5677_SEL_MONO_ADC_L1_SFT, rt5677_mono_adc1_l_src);
+
+static const struct snd_kcontrol_new rt5677_mono_adc1_l_mux =
+ SOC_DAPM_ENUM("Mono ADC1 L Source", rt5677_mono_adc1_l_enum);
+
+/* Mono ADC Right Source 2 */ /* MX-28 [3:2] */
+static const char * const rt5677_mono_adc2_r_src[] = {
+ "DD MIX1R", "DMIC", "MONO DAC MIXR"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_mono_adc2_r_enum, RT5677_MONO_ADC_MIXER,
+ RT5677_SEL_MONO_ADC_R2_SFT, rt5677_mono_adc2_r_src);
+
+static const struct snd_kcontrol_new rt5677_mono_adc2_r_mux =
+ SOC_DAPM_ENUM("Mono ADC2 R Source", rt5677_mono_adc2_r_enum);
+
+/* Mono ADC Right Source 1 */ /* MX-28 [5:4] */
+static const char * const rt5677_mono_adc1_r_src[] = {
+ "DD MIX1R", "ADC2", "MONO DAC MIXR"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_mono_adc1_r_enum, RT5677_MONO_ADC_MIXER,
+ RT5677_SEL_MONO_ADC_R1_SFT, rt5677_mono_adc1_r_src);
+
+static const struct snd_kcontrol_new rt5677_mono_adc1_r_mux =
+ SOC_DAPM_ENUM("Mono ADC1 R Source", rt5677_mono_adc1_r_enum);
+
+/* Stereo4 ADC Source 2 */ /* MX-24 [11:10] */
+static const char * const rt5677_stereo4_adc2_src[] = {
+ "DD MIX1", "DMIC", "DD MIX2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_stereo4_adc2_enum, RT5677_STO4_ADC_MIXER,
+ RT5677_SEL_STO4_ADC2_SFT, rt5677_stereo4_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_sto4_adc2_mux =
+ SOC_DAPM_ENUM("Stereo4 ADC2 Source", rt5677_stereo4_adc2_enum);
+
+
+/* Stereo4 ADC Source 1 */ /* MX-24 [13:12] */
+static const char * const rt5677_stereo4_adc1_src[] = {
+ "DD MIX1", "ADC1/2", "DD MIX2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_stereo4_adc1_enum, RT5677_STO4_ADC_MIXER,
+ RT5677_SEL_STO4_ADC1_SFT, rt5677_stereo4_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_sto4_adc1_mux =
+ SOC_DAPM_ENUM("Stereo4 ADC1 Source", rt5677_stereo4_adc1_enum);
+
+/* InBound0/1 Source */ /* MX-A3 [14:12] */
+static const char * const rt5677_inbound01_src[] = {
+ "IF1 DAC 01", "IF2 DAC 01", "SLB DAC 01", "STO1 ADC MIX",
+ "VAD ADC/DAC1 FS"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_inbound01_enum, RT5677_DSP_INB_CTRL1,
+ RT5677_IB01_SRC_SFT, rt5677_inbound01_src);
+
+static const struct snd_kcontrol_new rt5677_ib01_src_mux =
+ SOC_DAPM_ENUM("InBound0/1 Source", rt5677_inbound01_enum);
+
+/* InBound2/3 Source */ /* MX-A3 [10:8] */
+static const char * const rt5677_inbound23_src[] = {
+ "IF1 DAC 23", "IF2 DAC 23", "SLB DAC 23", "STO2 ADC MIX",
+ "DAC1 FS", "IF4 DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_inbound23_enum, RT5677_DSP_INB_CTRL1,
+ RT5677_IB23_SRC_SFT, rt5677_inbound23_src);
+
+static const struct snd_kcontrol_new rt5677_ib23_src_mux =
+ SOC_DAPM_ENUM("InBound2/3 Source", rt5677_inbound23_enum);
+
+/* InBound4/5 Source */ /* MX-A3 [6:4] */
+static const char * const rt5677_inbound45_src[] = {
+ "IF1 DAC 45", "IF2 DAC 45", "SLB DAC 45", "STO3 ADC MIX",
+ "IF3 DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_inbound45_enum, RT5677_DSP_INB_CTRL1,
+ RT5677_IB45_SRC_SFT, rt5677_inbound45_src);
+
+static const struct snd_kcontrol_new rt5677_ib45_src_mux =
+ SOC_DAPM_ENUM("InBound4/5 Source", rt5677_inbound45_enum);
+
+/* InBound6 Source */ /* MX-A3 [2:0] */
+static const char * const rt5677_inbound6_src[] = {
+ "IF1 DAC 6", "IF2 DAC 6", "SLB DAC 6", "STO4 ADC MIX L",
+ "IF4 DAC L", "STO1 ADC MIX L", "STO2 ADC MIX L", "STO3 ADC MIX L"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_inbound6_enum, RT5677_DSP_INB_CTRL1,
+ RT5677_IB6_SRC_SFT, rt5677_inbound6_src);
+
+static const struct snd_kcontrol_new rt5677_ib6_src_mux =
+ SOC_DAPM_ENUM("InBound6 Source", rt5677_inbound6_enum);
+
+/* InBound7 Source */ /* MX-A4 [14:12] */
+static const char * const rt5677_inbound7_src[] = {
+ "IF1 DAC 7", "IF2 DAC 7", "SLB DAC 7", "STO4 ADC MIX R",
+ "IF4 DAC R", "STO1 ADC MIX R", "STO2 ADC MIX R", "STO3 ADC MIX R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_inbound7_enum, RT5677_DSP_INB_CTRL2,
+ RT5677_IB7_SRC_SFT, rt5677_inbound7_src);
+
+static const struct snd_kcontrol_new rt5677_ib7_src_mux =
+ SOC_DAPM_ENUM("InBound7 Source", rt5677_inbound7_enum);
+
+/* InBound8 Source */ /* MX-A4 [10:8] */
+static const char * const rt5677_inbound8_src[] = {
+ "STO1 ADC MIX L", "STO2 ADC MIX L", "STO3 ADC MIX L", "STO4 ADC MIX L",
+ "MONO ADC MIX L", "DACL1 FS"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_inbound8_enum, RT5677_DSP_INB_CTRL2,
+ RT5677_IB8_SRC_SFT, rt5677_inbound8_src);
+
+static const struct snd_kcontrol_new rt5677_ib8_src_mux =
+ SOC_DAPM_ENUM("InBound8 Source", rt5677_inbound8_enum);
+
+/* InBound9 Source */ /* MX-A4 [6:4] */
+static const char * const rt5677_inbound9_src[] = {
+ "STO1 ADC MIX R", "STO2 ADC MIX R", "STO3 ADC MIX R", "STO4 ADC MIX R",
+ "MONO ADC MIX R", "DACR1 FS", "DAC1 FS"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_inbound9_enum, RT5677_DSP_INB_CTRL2,
+ RT5677_IB9_SRC_SFT, rt5677_inbound9_src);
+
+static const struct snd_kcontrol_new rt5677_ib9_src_mux =
+ SOC_DAPM_ENUM("InBound9 Source", rt5677_inbound9_enum);
+
+/* VAD Source */ /* MX-9F [6:4] */
+static const char * const rt5677_vad_src[] = {
+ "STO1 ADC MIX L", "MONO ADC MIX L", "MONO ADC MIX R", "STO2 ADC MIX L",
+ "STO3 ADC MIX L"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_vad_enum, RT5677_VAD_CTRL4,
+ RT5677_VAD_SRC_SFT, rt5677_vad_src);
+
+static const struct snd_kcontrol_new rt5677_vad_src_mux =
+ SOC_DAPM_ENUM("VAD Source", rt5677_vad_enum);
+
+/* Sidetone Source */ /* MX-13 [11:9] */
+static const char * const rt5677_sidetone_src[] = {
+ "DMIC1 L", "DMIC2 L", "DMIC3 L", "DMIC4 L", "ADC1", "ADC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_sidetone_enum, RT5677_SIDETONE_CTRL,
+ RT5677_ST_SEL_SFT, rt5677_sidetone_src);
+
+static const struct snd_kcontrol_new rt5677_sidetone_mux =
+ SOC_DAPM_ENUM("Sidetone Source", rt5677_sidetone_enum);
+
+/* DAC1/2 Source */ /* MX-15 [1:0] */
+static const char * const rt5677_dac12_src[] = {
+ "STO1 DAC MIX", "MONO DAC MIX", "DD MIX1", "DD MIX2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_dac12_enum, RT5677_ANA_DAC1_2_3_SRC,
+ RT5677_ANA_DAC1_2_SRC_SEL_SFT, rt5677_dac12_src);
+
+static const struct snd_kcontrol_new rt5677_dac12_mux =
+ SOC_DAPM_ENUM("Analog DAC1/2 Source", rt5677_dac12_enum);
+
+/* DAC3 Source */ /* MX-15 [5:4] */
+static const char * const rt5677_dac3_src[] = {
+ "MONO DAC MIXL", "MONO DAC MIXR", "DD MIX1L", "DD MIX2L"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_dac3_enum, RT5677_ANA_DAC1_2_3_SRC,
+ RT5677_ANA_DAC3_SRC_SEL_SFT, rt5677_dac3_src);
+
+static const struct snd_kcontrol_new rt5677_dac3_mux =
+ SOC_DAPM_ENUM("Analog DAC3 Source", rt5677_dac3_enum);
+
+/* PDM channel Source */ /* MX-31 [13:12][9:8][5:4][1:0] */
+static const char * const rt5677_pdm_src[] = {
+ "STO1 DAC MIX", "MONO DAC MIX", "DD MIX1", "DD MIX2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_pdm1_l_enum, RT5677_PDM_OUT_CTRL,
+ RT5677_SEL_PDM1_L_SFT, rt5677_pdm_src);
+
+static const struct snd_kcontrol_new rt5677_pdm1_l_mux =
+ SOC_DAPM_ENUM("PDM1 Source", rt5677_pdm1_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_pdm2_l_enum, RT5677_PDM_OUT_CTRL,
+ RT5677_SEL_PDM2_L_SFT, rt5677_pdm_src);
+
+static const struct snd_kcontrol_new rt5677_pdm2_l_mux =
+ SOC_DAPM_ENUM("PDM2 Source", rt5677_pdm2_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_pdm1_r_enum, RT5677_PDM_OUT_CTRL,
+ RT5677_SEL_PDM1_R_SFT, rt5677_pdm_src);
+
+static const struct snd_kcontrol_new rt5677_pdm1_r_mux =
+ SOC_DAPM_ENUM("PDM1 Source", rt5677_pdm1_r_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_pdm2_r_enum, RT5677_PDM_OUT_CTRL,
+ RT5677_SEL_PDM2_R_SFT, rt5677_pdm_src);
+
+static const struct snd_kcontrol_new rt5677_pdm2_r_mux =
+ SOC_DAPM_ENUM("PDM2 Source", rt5677_pdm2_r_enum);
+
+/* TDM IF1/2 SLB ADC1 Data Selection */ /* MX-3C MX-41 [5:4] MX-08 [1:0] */
+static const char * const rt5677_if12_adc1_src[] = {
+ "STO1 ADC MIX", "OB01", "VAD ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_adc1_enum, RT5677_TDM1_CTRL2,
+ RT5677_IF1_ADC1_SFT, rt5677_if12_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc1_mux =
+ SOC_DAPM_ENUM("IF1 ADC1 Source", rt5677_if1_adc1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_adc1_enum, RT5677_TDM2_CTRL2,
+ RT5677_IF2_ADC1_SFT, rt5677_if12_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc1_mux =
+ SOC_DAPM_ENUM("IF2 ADC1 Source", rt5677_if2_adc1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_slb_adc1_enum, RT5677_SLIMBUS_RX,
+ RT5677_SLB_ADC1_SFT, rt5677_if12_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_slb_adc1_mux =
+ SOC_DAPM_ENUM("SLB ADC1 Source", rt5677_slb_adc1_enum);
+
+/* TDM IF1/2 SLB ADC2 Data Selection */ /* MX-3C MX-41 [7:6] MX-08 [3:2] */
+static const char * const rt5677_if12_adc2_src[] = {
+ "STO2 ADC MIX", "OB23"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_adc2_enum, RT5677_TDM1_CTRL2,
+ RT5677_IF1_ADC2_SFT, rt5677_if12_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc2_mux =
+ SOC_DAPM_ENUM("IF1 ADC2 Source", rt5677_if1_adc2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_adc2_enum, RT5677_TDM2_CTRL2,
+ RT5677_IF2_ADC2_SFT, rt5677_if12_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc2_mux =
+ SOC_DAPM_ENUM("IF2 ADC2 Source", rt5677_if2_adc2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_slb_adc2_enum, RT5677_SLIMBUS_RX,
+ RT5677_SLB_ADC2_SFT, rt5677_if12_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_slb_adc2_mux =
+ SOC_DAPM_ENUM("SLB ADC2 Source", rt5677_slb_adc2_enum);
+
+/* TDM IF1/2 SLB ADC3 Data Selection */ /* MX-3C MX-41 [9:8] MX-08 [5:4] */
+static const char * const rt5677_if12_adc3_src[] = {
+ "STO3 ADC MIX", "MONO ADC MIX", "OB45"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_adc3_enum, RT5677_TDM1_CTRL2,
+ RT5677_IF1_ADC3_SFT, rt5677_if12_adc3_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc3_mux =
+ SOC_DAPM_ENUM("IF1 ADC3 Source", rt5677_if1_adc3_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_adc3_enum, RT5677_TDM2_CTRL2,
+ RT5677_IF2_ADC3_SFT, rt5677_if12_adc3_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc3_mux =
+ SOC_DAPM_ENUM("IF2 ADC3 Source", rt5677_if2_adc3_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_slb_adc3_enum, RT5677_SLIMBUS_RX,
+ RT5677_SLB_ADC3_SFT, rt5677_if12_adc3_src);
+
+static const struct snd_kcontrol_new rt5677_slb_adc3_mux =
+ SOC_DAPM_ENUM("SLB ADC3 Source", rt5677_slb_adc3_enum);
+
+/* TDM IF1/2 SLB ADC4 Data Selection */ /* MX-3C MX-41 [11:10] MX-08 [7:6] */
+static const char * const rt5677_if12_adc4_src[] = {
+ "STO4 ADC MIX", "OB67", "OB01"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_adc4_enum, RT5677_TDM1_CTRL2,
+ RT5677_IF1_ADC4_SFT, rt5677_if12_adc4_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc4_mux =
+ SOC_DAPM_ENUM("IF1 ADC4 Source", rt5677_if1_adc4_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_adc4_enum, RT5677_TDM2_CTRL2,
+ RT5677_IF2_ADC4_SFT, rt5677_if12_adc4_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc4_mux =
+ SOC_DAPM_ENUM("IF2 ADC4 Source", rt5677_if2_adc4_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_slb_adc4_enum, RT5677_SLIMBUS_RX,
+ RT5677_SLB_ADC4_SFT, rt5677_if12_adc4_src);
+
+static const struct snd_kcontrol_new rt5677_slb_adc4_mux =
+ SOC_DAPM_ENUM("SLB ADC4 Source", rt5677_slb_adc4_enum);
+
+/* Interface3/4 ADC Data Input */ /* MX-2F [3:0] MX-30 [7:4] */
+static const char * const rt5677_if34_adc_src[] = {
+ "STO1 ADC MIX", "STO2 ADC MIX", "STO3 ADC MIX", "STO4 ADC MIX",
+ "MONO ADC MIX", "OB01", "OB23", "VAD ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if3_adc_enum, RT5677_IF3_DATA,
+ RT5677_IF3_ADC_IN_SFT, rt5677_if34_adc_src);
+
+static const struct snd_kcontrol_new rt5677_if3_adc_mux =
+ SOC_DAPM_ENUM("IF3 ADC Source", rt5677_if3_adc_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if4_adc_enum, RT5677_IF4_DATA,
+ RT5677_IF4_ADC_IN_SFT, rt5677_if34_adc_src);
+
+static const struct snd_kcontrol_new rt5677_if4_adc_mux =
+ SOC_DAPM_ENUM("IF4 ADC Source", rt5677_if4_adc_enum);
+
+/* TDM IF1/2 ADC Data Selection */ /* MX-3B MX-40 [7:6][5:4][3:2][1:0] */
+static const char * const rt5677_if12_adc_swap_src[] = {
+ "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_adc1_swap_enum, RT5677_TDM1_CTRL1,
+ RT5677_IF1_ADC1_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc1_swap_mux =
+ SOC_DAPM_ENUM("IF1 ADC1 Swap Source", rt5677_if1_adc1_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_adc2_swap_enum, RT5677_TDM1_CTRL1,
+ RT5677_IF1_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc2_swap_mux =
+ SOC_DAPM_ENUM("IF1 ADC2 Swap Source", rt5677_if1_adc2_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_adc3_swap_enum, RT5677_TDM1_CTRL1,
+ RT5677_IF1_ADC3_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc3_swap_mux =
+ SOC_DAPM_ENUM("IF1 ADC3 Swap Source", rt5677_if1_adc3_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_adc4_swap_enum, RT5677_TDM1_CTRL1,
+ RT5677_IF1_ADC4_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc4_swap_mux =
+ SOC_DAPM_ENUM("IF1 ADC4 Swap Source", rt5677_if1_adc4_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_adc1_swap_enum, RT5677_TDM2_CTRL1,
+ RT5677_IF1_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc1_swap_mux =
+ SOC_DAPM_ENUM("IF1 ADC2 Swap Source", rt5677_if2_adc1_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_adc2_swap_enum, RT5677_TDM2_CTRL1,
+ RT5677_IF2_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc2_swap_mux =
+ SOC_DAPM_ENUM("IF2 ADC2 Swap Source", rt5677_if2_adc2_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_adc3_swap_enum, RT5677_TDM2_CTRL1,
+ RT5677_IF2_ADC3_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc3_swap_mux =
+ SOC_DAPM_ENUM("IF2 ADC3 Swap Source", rt5677_if2_adc3_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_adc4_swap_enum, RT5677_TDM2_CTRL1,
+ RT5677_IF2_ADC4_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc4_swap_mux =
+ SOC_DAPM_ENUM("IF2 ADC4 Swap Source", rt5677_if2_adc4_swap_enum);
+
+/* TDM IF1 ADC Data Selection */ /* MX-3C [2:0] */
+static const char * const rt5677_if1_adc_tdm_swap_src[] = {
+ "1/2/3/4", "2/1/3/4", "2/3/1/4", "4/1/2/3", "1/3/2/4", "1/4/2/3",
+ "3/1/2/4", "3/4/1/2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_adc_tdm_swap_enum, RT5677_TDM1_CTRL2,
+ RT5677_IF1_ADC_CTRL_SFT, rt5677_if1_adc_tdm_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc_tdm_swap_mux =
+ SOC_DAPM_ENUM("IF1 ADC TDM Swap Source", rt5677_if1_adc_tdm_swap_enum);
+
+/* TDM IF2 ADC Data Selection */ /* MX-41[2:0] */
+static const char * const rt5677_if2_adc_tdm_swap_src[] = {
+ "1/2/3/4", "2/1/3/4", "3/1/2/4", "4/1/2/3", "1/3/2/4", "1/4/2/3",
+ "2/3/1/4", "3/4/1/2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_adc_tdm_swap_enum, RT5677_TDM2_CTRL2,
+ RT5677_IF2_ADC_CTRL_SFT, rt5677_if2_adc_tdm_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc_tdm_swap_mux =
+ SOC_DAPM_ENUM("IF2 ADC TDM Swap Source", rt5677_if2_adc_tdm_swap_enum);
+
+/* TDM IF1/2 DAC Data Selection */ /* MX-3E[14:12][10:8][6:4][2:0]
+ MX-3F[14:12][10:8][6:4][2:0]
+ MX-43[14:12][10:8][6:4][2:0]
+ MX-44[14:12][10:8][6:4][2:0] */
+static const char * const rt5677_if12_dac_tdm_sel_src[] = {
+ "Slot0", "Slot1", "Slot2", "Slot3", "Slot4", "Slot5", "Slot6", "Slot7"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_dac0_tdm_sel_enum, RT5677_TDM1_CTRL4,
+ RT5677_IF1_DAC0_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac0_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC0 TDM Source", rt5677_if1_dac0_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_dac1_tdm_sel_enum, RT5677_TDM1_CTRL4,
+ RT5677_IF1_DAC1_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac1_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC1 TDM Source", rt5677_if1_dac1_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_dac2_tdm_sel_enum, RT5677_TDM1_CTRL4,
+ RT5677_IF1_DAC2_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac2_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC2 TDM Source", rt5677_if1_dac2_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_dac3_tdm_sel_enum, RT5677_TDM1_CTRL4,
+ RT5677_IF1_DAC3_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac3_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC3 TDM Source", rt5677_if1_dac3_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_dac4_tdm_sel_enum, RT5677_TDM1_CTRL5,
+ RT5677_IF1_DAC4_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac4_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC4 TDM Source", rt5677_if1_dac4_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_dac5_tdm_sel_enum, RT5677_TDM1_CTRL5,
+ RT5677_IF1_DAC5_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac5_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC5 TDM Source", rt5677_if1_dac5_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_dac6_tdm_sel_enum, RT5677_TDM1_CTRL5,
+ RT5677_IF1_DAC6_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac6_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC6 TDM Source", rt5677_if1_dac6_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if1_dac7_tdm_sel_enum, RT5677_TDM1_CTRL5,
+ RT5677_IF1_DAC7_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac7_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF1 DAC7 TDM Source", rt5677_if1_dac7_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_dac0_tdm_sel_enum, RT5677_TDM2_CTRL4,
+ RT5677_IF2_DAC0_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac0_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF2 DAC0 TDM Source", rt5677_if2_dac0_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_dac1_tdm_sel_enum, RT5677_TDM2_CTRL4,
+ RT5677_IF2_DAC1_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac1_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF2 DAC1 TDM Source", rt5677_if2_dac1_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_dac2_tdm_sel_enum, RT5677_TDM2_CTRL4,
+ RT5677_IF2_DAC2_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac2_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF2 DAC2 TDM Source", rt5677_if2_dac2_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_dac3_tdm_sel_enum, RT5677_TDM2_CTRL4,
+ RT5677_IF2_DAC3_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac3_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF2 DAC3 TDM Source", rt5677_if2_dac3_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_dac4_tdm_sel_enum, RT5677_TDM2_CTRL5,
+ RT5677_IF2_DAC4_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac4_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF2 DAC4 TDM Source", rt5677_if2_dac4_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_dac5_tdm_sel_enum, RT5677_TDM2_CTRL5,
+ RT5677_IF2_DAC5_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac5_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF2 DAC5 TDM Source", rt5677_if2_dac5_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_dac6_tdm_sel_enum, RT5677_TDM2_CTRL5,
+ RT5677_IF2_DAC6_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac6_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF2 DAC6 TDM Source", rt5677_if2_dac6_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5677_if2_dac7_tdm_sel_enum, RT5677_TDM2_CTRL5,
+ RT5677_IF2_DAC7_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac7_tdm_sel_mux =
+ SOC_DAPM_ENUM("IF2 DAC7 TDM Source", rt5677_if2_dac7_tdm_sel_enum);
+
+static int rt5677_bst1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+ RT5677_PWR_BST1_P, RT5677_PWR_BST1_P);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+ RT5677_PWR_BST1_P, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5677_bst2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+ RT5677_PWR_BST2_P, RT5677_PWR_BST2_P);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+ RT5677_PWR_BST2_P, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5677_set_pll1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(rt5677->regmap, RT5677_PLL1_CTRL2, 0x2, 0x2);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(rt5677->regmap, RT5677_PLL1_CTRL2, 0x2, 0x0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5677_set_pll2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(rt5677->regmap, RT5677_PLL2_CTRL2, 0x2, 0x2);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(rt5677->regmap, RT5677_PLL2_CTRL2, 0x2, 0x0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5677_set_micbias1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+ RT5677_PWR_CLK_MB1 | RT5677_PWR_PP_MB1 |
+ RT5677_PWR_CLK_MB, RT5677_PWR_CLK_MB1 |
+ RT5677_PWR_PP_MB1 | RT5677_PWR_CLK_MB);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+ RT5677_PWR_CLK_MB1 | RT5677_PWR_PP_MB1 |
+ RT5677_PWR_CLK_MB, 0);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5677_if1_adc_tdm_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ unsigned int value;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_read(rt5677->regmap, RT5677_TDM1_CTRL2, &value);
+ if (value & RT5677_IF1_ADC_CTRL_MASK)
+ regmap_update_bits(rt5677->regmap, RT5677_TDM1_CTRL1,
+ RT5677_IF1_ADC_MODE_MASK,
+ RT5677_IF1_ADC_MODE_TDM);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5677_if2_adc_tdm_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ unsigned int value;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_read(rt5677->regmap, RT5677_TDM2_CTRL2, &value);
+ if (value & RT5677_IF2_ADC_CTRL_MASK)
+ regmap_update_bits(rt5677->regmap, RT5677_TDM2_CTRL1,
+ RT5677_IF2_ADC_MODE_MASK,
+ RT5677_IF2_ADC_MODE_TDM);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5677_vref_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (codec->dapm.bias_level != SND_SOC_BIAS_ON &&
+ !rt5677->is_vref_slow) {
+ mdelay(20);
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+ RT5677_PWR_FV1 | RT5677_PWR_FV2,
+ RT5677_PWR_FV1 | RT5677_PWR_FV2);
+ rt5677->is_vref_slow = true;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT,
+ 0, rt5677_set_pll1_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("PLL2", RT5677_PWR_ANLG2, RT5677_PWR_PLL2_BIT,
+ 0, rt5677_set_pll2_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5677_ASRC_1, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5677_ASRC_1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S3 ASRC", 1, RT5677_ASRC_1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S4 ASRC", 1, RT5677_ASRC_1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5677_ASRC_2, 14, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC MONO2 L ASRC", 1, RT5677_ASRC_2, 13, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC MONO2 R ASRC", 1, RT5677_ASRC_2, 12, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC MONO3 L ASRC", 1, RT5677_ASRC_1, 15, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC MONO3 R ASRC", 1, RT5677_ASRC_1, 14, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC MONO4 L ASRC", 1, RT5677_ASRC_1, 13, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DAC MONO4 R ASRC", 1, RT5677_ASRC_1, 12, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5677_ASRC_2, 11, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC STO2 ASRC", 1, RT5677_ASRC_2, 10, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC STO3 ASRC", 1, RT5677_ASRC_2, 9, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC STO4 ASRC", 1, RT5677_ASRC_2, 8, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5677_ASRC_2, 7, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5677_ASRC_2, 6, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5677_ASRC_2, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5677_ASRC_2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO3 ASRC", 1, RT5677_ASRC_2, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC STO4 ASRC", 1, RT5677_ASRC_2, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5677_ASRC_2, 1, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5677_ASRC_2, 0, 0, NULL,
+ 0),
+
+ /* Input Side */
+ /* micbias */
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5677_PWR_ANLG2, RT5677_PWR_MB1_BIT,
+ 0, rt5677_set_micbias1_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC L1"),
+ SND_SOC_DAPM_INPUT("DMIC R1"),
+ SND_SOC_DAPM_INPUT("DMIC L2"),
+ SND_SOC_DAPM_INPUT("DMIC R2"),
+ SND_SOC_DAPM_INPUT("DMIC L3"),
+ SND_SOC_DAPM_INPUT("DMIC R3"),
+ SND_SOC_DAPM_INPUT("DMIC L4"),
+ SND_SOC_DAPM_INPUT("DMIC R4"),
+
+ SND_SOC_DAPM_INPUT("IN1P"),
+ SND_SOC_DAPM_INPUT("IN1N"),
+ SND_SOC_DAPM_INPUT("IN2P"),
+ SND_SOC_DAPM_INPUT("IN2N"),
+
+ SND_SOC_DAPM_INPUT("Haptic Generator"),
+
+ SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DMIC1 power", RT5677_DMIC_CTRL1,
+ RT5677_DMIC_1_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC2 power", RT5677_DMIC_CTRL1,
+ RT5677_DMIC_2_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC3 power", RT5677_DMIC_CTRL1,
+ RT5677_DMIC_3_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC4 power", RT5677_DMIC_CTRL2,
+ RT5677_DMIC_4_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+
+ /* Boost */
+ SND_SOC_DAPM_PGA_E("BST1", RT5677_PWR_ANLG2,
+ RT5677_PWR_BST1_BIT, 0, NULL, 0, rt5677_bst1_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("BST2", RT5677_PWR_ANLG2,
+ RT5677_PWR_BST2_BIT, 0, NULL, 0, rt5677_bst2_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_ADC("ADC 2", NULL, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_PGA("ADC 1_2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC 1 power", RT5677_PWR_DIG1,
+ RT5677_PWR_ADC_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC 2 power", RT5677_PWR_DIG1,
+ RT5677_PWR_ADC_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5677_PWR_DIG1,
+ RT5677_PWR_ADCFED1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC2 clock", RT5677_PWR_DIG1,
+ RT5677_PWR_ADCFED2_BIT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sto1_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sto1_adc1_mux),
+ SND_SOC_DAPM_MUX("Stereo1 ADC2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sto1_adc2_mux),
+ SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sto2_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sto2_adc1_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sto2_adc2_mux),
+ SND_SOC_DAPM_MUX("Stereo2 ADC LR Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sto2_adc_lr_mux),
+ SND_SOC_DAPM_MUX("Stereo3 DMIC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sto3_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo3 ADC1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sto3_adc1_mux),
+ SND_SOC_DAPM_MUX("Stereo3 ADC2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sto3_adc2_mux),
+ SND_SOC_DAPM_MUX("Stereo4 DMIC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sto4_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo4 ADC1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sto4_adc1_mux),
+ SND_SOC_DAPM_MUX("Stereo4 ADC2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sto4_adc2_mux),
+ SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_mono_dmic_l_mux),
+ SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_mono_dmic_r_mux),
+ SND_SOC_DAPM_MUX("Mono ADC2 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_mono_adc2_l_mux),
+ SND_SOC_DAPM_MUX("Mono ADC1 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_mono_adc1_l_mux),
+ SND_SOC_DAPM_MUX("Mono ADC1 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_mono_adc1_r_mux),
+ SND_SOC_DAPM_MUX("Mono ADC2 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_mono_adc2_r_mux),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5677_PWR_DIG2,
+ RT5677_PWR_ADC_S1F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("adc stereo2 filter", RT5677_PWR_DIG2,
+ RT5677_PWR_ADC_S2F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("adc stereo3 filter", RT5677_PWR_DIG2,
+ RT5677_PWR_ADC_S3F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("adc stereo4 filter", RT5677_PWR_DIG2,
+ RT5677_PWR_ADC_S4F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5677_sto1_adc_l_mix, ARRAY_SIZE(rt5677_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5677_sto1_adc_r_mix, ARRAY_SIZE(rt5677_sto1_adc_r_mix)),
+ SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5677_sto2_adc_l_mix, ARRAY_SIZE(rt5677_sto2_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5677_sto2_adc_r_mix, ARRAY_SIZE(rt5677_sto2_adc_r_mix)),
+ SND_SOC_DAPM_MIXER("Sto3 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5677_sto3_adc_l_mix, ARRAY_SIZE(rt5677_sto3_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto3 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5677_sto3_adc_r_mix, ARRAY_SIZE(rt5677_sto3_adc_r_mix)),
+ SND_SOC_DAPM_MIXER("Sto4 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5677_sto4_adc_l_mix, ARRAY_SIZE(rt5677_sto4_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto4 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5677_sto4_adc_r_mix, ARRAY_SIZE(rt5677_sto4_adc_r_mix)),
+ SND_SOC_DAPM_SUPPLY("adc mono left filter", RT5677_PWR_DIG2,
+ RT5677_PWR_ADC_MF_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5677_mono_adc_l_mix, ARRAY_SIZE(rt5677_mono_adc_l_mix)),
+ SND_SOC_DAPM_SUPPLY("adc mono right filter", RT5677_PWR_DIG2,
+ RT5677_PWR_ADC_MF_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5677_mono_adc_r_mix, ARRAY_SIZE(rt5677_mono_adc_r_mix)),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo2 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo2 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo3 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo3 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo3 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo4 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo4 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo4 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DSP */
+ SND_SOC_DAPM_MUX("IB9 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_ib9_src_mux),
+ SND_SOC_DAPM_MUX("IB8 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_ib8_src_mux),
+ SND_SOC_DAPM_MUX("IB7 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_ib7_src_mux),
+ SND_SOC_DAPM_MUX("IB6 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_ib6_src_mux),
+ SND_SOC_DAPM_MUX("IB45 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_ib45_src_mux),
+ SND_SOC_DAPM_MUX("IB23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_ib23_src_mux),
+ SND_SOC_DAPM_MUX("IB01 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_ib01_src_mux),
+ SND_SOC_DAPM_MUX("IB45 Bypass Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_ib45_bypass_src_mux),
+ SND_SOC_DAPM_MUX("IB23 Bypass Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_ib23_bypass_src_mux),
+ SND_SOC_DAPM_MUX("IB01 Bypass Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_ib01_bypass_src_mux),
+ SND_SOC_DAPM_MUX("OB23 Bypass Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_ob23_bypass_src_mux),
+ SND_SOC_DAPM_MUX("OB01 Bypass Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_ob01_bypass_src_mux),
+
+ SND_SOC_DAPM_PGA("OB45", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OB67", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("OutBound2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OutBound3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OutBound4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OutBound5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OutBound6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OutBound7", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5677_PWR_DIG1,
+ RT5677_PWR_I2S1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC7", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC01", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC23", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC45", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC67", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("I2S2", RT5677_PWR_DIG1,
+ RT5677_PWR_I2S2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC7", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC01", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC23", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC45", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC67", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("I2S3", RT5677_PWR_DIG1,
+ RT5677_PWR_I2S3_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF3 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF3 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF3 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF3 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF3 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF3 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("I2S4", RT5677_PWR_DIG1,
+ RT5677_PWR_I2S4_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF4 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF4 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF4 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF4 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF4 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF4 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("SLB", RT5677_PWR_DIG1,
+ RT5677_PWR_SLB_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB DAC0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB DAC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB DAC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB DAC5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB DAC6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB DAC7", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB DAC01", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB DAC23", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB DAC45", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB DAC67", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SLB ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("IF1 ADC1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_adc1_mux),
+ SND_SOC_DAPM_MUX("IF1 ADC2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_adc2_mux),
+ SND_SOC_DAPM_MUX("IF1 ADC3 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_adc3_mux),
+ SND_SOC_DAPM_MUX("IF1 ADC4 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_adc4_mux),
+ SND_SOC_DAPM_MUX("IF1 ADC1 Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_adc1_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 ADC2 Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_adc2_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 ADC3 Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_adc3_swap_mux),
+ SND_SOC_DAPM_MUX("IF1 ADC4 Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_adc4_swap_mux),
+ SND_SOC_DAPM_MUX_E("IF1 ADC TDM Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_adc_tdm_swap_mux, rt5677_if1_adc_tdm_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX("IF2 ADC1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_adc1_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_adc2_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC3 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_adc3_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC4 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_adc4_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC1 Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_adc1_swap_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC2 Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_adc2_swap_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC3 Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_adc3_swap_mux),
+ SND_SOC_DAPM_MUX("IF2 ADC4 Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_adc4_swap_mux),
+ SND_SOC_DAPM_MUX_E("IF2 ADC TDM Swap Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_adc_tdm_swap_mux, rt5677_if2_adc_tdm_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX("IF3 ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if3_adc_mux),
+ SND_SOC_DAPM_MUX("IF4 ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if4_adc_mux),
+ SND_SOC_DAPM_MUX("SLB ADC1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_slb_adc1_mux),
+ SND_SOC_DAPM_MUX("SLB ADC2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_slb_adc2_mux),
+ SND_SOC_DAPM_MUX("SLB ADC3 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_slb_adc3_mux),
+ SND_SOC_DAPM_MUX("SLB ADC4 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_slb_adc4_mux),
+
+ SND_SOC_DAPM_MUX("IF1 DAC0 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_dac0_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF1 DAC1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_dac1_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF1 DAC2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_dac2_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF1 DAC3 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_dac3_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF1 DAC4 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_dac4_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF1 DAC5 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_dac5_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF1 DAC6 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_dac6_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF1 DAC7 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if1_dac7_tdm_sel_mux),
+
+ SND_SOC_DAPM_MUX("IF2 DAC0 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_dac0_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF2 DAC1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_dac1_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF2 DAC2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_dac2_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF2 DAC3 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_dac3_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF2 DAC4 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_dac4_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF2 DAC5 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_dac5_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF2 DAC6 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_dac6_tdm_sel_mux),
+ SND_SOC_DAPM_MUX("IF2 DAC7 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_if2_dac7_tdm_sel_mux),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF3RX", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF3TX", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF4RX", "AIF4 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF4TX", "AIF4 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLBRX", "SLIMBus Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLBTX", "SLIMBus Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Sidetone Mux */
+ SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_sidetone_mux),
+ SND_SOC_DAPM_SUPPLY("Sidetone Power", RT5677_SIDETONE_CTRL,
+ RT5677_ST_EN_SFT, 0, NULL, 0),
+
+ /* VAD Mux*/
+ SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_vad_src_mux),
+
+ /* Tensilica DSP */
+ SND_SOC_DAPM_PGA("Tensilica DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("OB01 MIX", SND_SOC_NOPM, 0, 0,
+ rt5677_ob_01_mix, ARRAY_SIZE(rt5677_ob_01_mix)),
+ SND_SOC_DAPM_MIXER("OB23 MIX", SND_SOC_NOPM, 0, 0,
+ rt5677_ob_23_mix, ARRAY_SIZE(rt5677_ob_23_mix)),
+ SND_SOC_DAPM_MIXER("OB4 MIX", SND_SOC_NOPM, 0, 0,
+ rt5677_ob_4_mix, ARRAY_SIZE(rt5677_ob_4_mix)),
+ SND_SOC_DAPM_MIXER("OB5 MIX", SND_SOC_NOPM, 0, 0,
+ rt5677_ob_5_mix, ARRAY_SIZE(rt5677_ob_5_mix)),
+ SND_SOC_DAPM_MIXER("OB6 MIX", SND_SOC_NOPM, 0, 0,
+ rt5677_ob_6_mix, ARRAY_SIZE(rt5677_ob_6_mix)),
+ SND_SOC_DAPM_MIXER("OB7 MIX", SND_SOC_NOPM, 0, 0,
+ rt5677_ob_7_mix, ARRAY_SIZE(rt5677_ob_7_mix)),
+
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
+ rt5677_dac_l_mix, ARRAY_SIZE(rt5677_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5677_dac_r_mix, ARRAY_SIZE(rt5677_dac_r_mix)),
+ SND_SOC_DAPM_PGA("DAC1 FS", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DAC Mux */
+ SND_SOC_DAPM_MUX("DAC1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_dac1_mux),
+ SND_SOC_DAPM_MUX("ADDA1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_adda1_mux),
+ SND_SOC_DAPM_MUX("DAC12 SRC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_dac12_mux),
+ SND_SOC_DAPM_MUX("DAC3 SRC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_dac3_mux),
+
+ /* DAC2 channel Mux */
+ SND_SOC_DAPM_MUX("DAC2 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_dac2_l_mux),
+ SND_SOC_DAPM_MUX("DAC2 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_dac2_r_mux),
+
+ /* DAC3 channel Mux */
+ SND_SOC_DAPM_MUX("DAC3 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_dac3_l_mux),
+ SND_SOC_DAPM_MUX("DAC3 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_dac3_r_mux),
+
+ /* DAC4 channel Mux */
+ SND_SOC_DAPM_MUX("DAC4 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_dac4_l_mux),
+ SND_SOC_DAPM_MUX("DAC4 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5677_dac4_r_mux),
+
+ /* DAC Mixer */
+ SND_SOC_DAPM_SUPPLY("dac stereo1 filter", RT5677_PWR_DIG2,
+ RT5677_PWR_DAC_S1F_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("dac mono2 left filter", RT5677_PWR_DIG2,
+ RT5677_PWR_DAC_M2F_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("dac mono2 right filter", RT5677_PWR_DIG2,
+ RT5677_PWR_DAC_M2F_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("dac mono3 left filter", RT5677_PWR_DIG2,
+ RT5677_PWR_DAC_M3F_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("dac mono3 right filter", RT5677_PWR_DIG2,
+ RT5677_PWR_DAC_M3F_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("dac mono4 left filter", RT5677_PWR_DIG2,
+ RT5677_PWR_DAC_M4F_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("dac mono4 right filter", RT5677_PWR_DIG2,
+ RT5677_PWR_DAC_M4F_R_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5677_sto1_dac_l_mix, ARRAY_SIZE(rt5677_sto1_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5677_sto1_dac_r_mix, ARRAY_SIZE(rt5677_sto1_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5677_mono_dac_l_mix, ARRAY_SIZE(rt5677_mono_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5677_mono_dac_r_mix, ARRAY_SIZE(rt5677_mono_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("DD1 MIXL", SND_SOC_NOPM, 0, 0,
+ rt5677_dd1_l_mix, ARRAY_SIZE(rt5677_dd1_l_mix)),
+ SND_SOC_DAPM_MIXER("DD1 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5677_dd1_r_mix, ARRAY_SIZE(rt5677_dd1_r_mix)),
+ SND_SOC_DAPM_MIXER("DD2 MIXL", SND_SOC_NOPM, 0, 0,
+ rt5677_dd2_l_mix, ARRAY_SIZE(rt5677_dd2_l_mix)),
+ SND_SOC_DAPM_MIXER("DD2 MIXR", SND_SOC_NOPM, 0, 0,
+ rt5677_dd2_r_mix, ARRAY_SIZE(rt5677_dd2_r_mix)),
+ SND_SOC_DAPM_PGA("Stereo DAC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono DAC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DD1 MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DD2 MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC 1", NULL, RT5677_PWR_DIG1,
+ RT5677_PWR_DAC1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC 2", NULL, RT5677_PWR_DIG1,
+ RT5677_PWR_DAC2_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC 3", NULL, RT5677_PWR_DIG1,
+ RT5677_PWR_DAC3_BIT, 0),
+
+ /* PDM */
+ SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5677_PWR_DIG2,
+ RT5677_PWR_PDM1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5677_PWR_DIG2,
+ RT5677_PWR_PDM2_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("PDM1 L Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM1_L_SFT,
+ 1, &rt5677_pdm1_l_mux),
+ SND_SOC_DAPM_MUX("PDM1 R Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM1_R_SFT,
+ 1, &rt5677_pdm1_r_mux),
+ SND_SOC_DAPM_MUX("PDM2 L Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM2_L_SFT,
+ 1, &rt5677_pdm2_l_mux),
+ SND_SOC_DAPM_MUX("PDM2 R Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM2_R_SFT,
+ 1, &rt5677_pdm2_r_mux),
+
+ SND_SOC_DAPM_PGA_S("LOUT1 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO1_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("LOUT2 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO2_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("LOUT3 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO3_BIT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_S("LOUT1 vref", 1, SND_SOC_NOPM, 0, 0,
+ rt5677_vref_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("LOUT2 vref", 1, SND_SOC_NOPM, 0, 0,
+ rt5677_vref_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("LOUT3 vref", 1, SND_SOC_NOPM, 0, 0,
+ rt5677_vref_event, SND_SOC_DAPM_POST_PMU),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("LOUT3"),
+ SND_SOC_DAPM_OUTPUT("PDM1L"),
+ SND_SOC_DAPM_OUTPUT("PDM1R"),
+ SND_SOC_DAPM_OUTPUT("PDM2L"),
+ SND_SOC_DAPM_OUTPUT("PDM2R"),
+
+ SND_SOC_DAPM_POST("vref", rt5677_vref_event),
+};
+
+static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
+ { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", 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 },
+ { "I2S1", NULL, "I2S1 ASRC", can_use_asrc},
+ { "I2S2", NULL, "I2S2 ASRC", can_use_asrc},
+ { "I2S3", NULL, "I2S3 ASRC", can_use_asrc},
+ { "I2S4", NULL, "I2S4 ASRC", can_use_asrc},
+
+ { "dac stereo1 filter", NULL, "DAC STO ASRC", is_using_asrc },
+ { "dac mono2 left filter", NULL, "DAC MONO2 L ASRC", is_using_asrc },
+ { "dac mono2 right filter", NULL, "DAC MONO2 R ASRC", is_using_asrc },
+ { "dac mono3 left filter", NULL, "DAC MONO3 L ASRC", is_using_asrc },
+ { "dac mono3 right filter", NULL, "DAC MONO3 R ASRC", is_using_asrc },
+ { "dac mono4 left filter", NULL, "DAC MONO4 L ASRC", is_using_asrc },
+ { "dac mono4 right filter", NULL, "DAC MONO4 R ASRC", is_using_asrc },
+ { "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc },
+ { "adc stereo2 filter", NULL, "ADC STO2 ASRC", is_using_asrc },
+ { "adc stereo3 filter", NULL, "ADC STO3 ASRC", is_using_asrc },
+ { "adc stereo4 filter", NULL, "ADC STO4 ASRC", is_using_asrc },
+ { "adc mono left filter", NULL, "ADC MONO L ASRC", is_using_asrc },
+ { "adc mono right filter", NULL, "ADC MONO R ASRC", is_using_asrc },
+
+ { "DMIC1", NULL, "DMIC L1" },
+ { "DMIC1", NULL, "DMIC R1" },
+ { "DMIC2", NULL, "DMIC L2" },
+ { "DMIC2", NULL, "DMIC R2" },
+ { "DMIC3", NULL, "DMIC L3" },
+ { "DMIC3", NULL, "DMIC R3" },
+ { "DMIC4", NULL, "DMIC L4" },
+ { "DMIC4", NULL, "DMIC R4" },
+
+ { "DMIC L1", NULL, "DMIC CLK" },
+ { "DMIC R1", NULL, "DMIC CLK" },
+ { "DMIC L2", NULL, "DMIC CLK" },
+ { "DMIC R2", NULL, "DMIC CLK" },
+ { "DMIC L3", NULL, "DMIC CLK" },
+ { "DMIC R3", NULL, "DMIC CLK" },
+ { "DMIC L4", NULL, "DMIC CLK" },
+ { "DMIC R4", NULL, "DMIC CLK" },
+
+ { "DMIC L1", NULL, "DMIC1 power" },
+ { "DMIC R1", NULL, "DMIC1 power" },
+ { "DMIC L3", NULL, "DMIC3 power" },
+ { "DMIC R3", NULL, "DMIC3 power" },
+ { "DMIC L4", NULL, "DMIC4 power" },
+ { "DMIC R4", NULL, "DMIC4 power" },
+
+ { "BST1", NULL, "IN1P" },
+ { "BST1", NULL, "IN1N" },
+ { "BST2", NULL, "IN2P" },
+ { "BST2", NULL, "IN2N" },
+
+ { "IN1P", NULL, "MICBIAS1" },
+ { "IN1N", NULL, "MICBIAS1" },
+ { "IN2P", NULL, "MICBIAS1" },
+ { "IN2N", NULL, "MICBIAS1" },
+
+ { "ADC 1", NULL, "BST1" },
+ { "ADC 1", NULL, "ADC 1 power" },
+ { "ADC 1", NULL, "ADC1 clock" },
+ { "ADC 2", NULL, "BST2" },
+ { "ADC 2", NULL, "ADC 2 power" },
+ { "ADC 2", NULL, "ADC2 clock" },
+
+ { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
+ { "Stereo1 DMIC Mux", "DMIC3", "DMIC3" },
+ { "Stereo1 DMIC Mux", "DMIC4", "DMIC4" },
+
+ { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" },
+ { "Stereo2 DMIC Mux", "DMIC3", "DMIC3" },
+ { "Stereo2 DMIC Mux", "DMIC4", "DMIC4" },
+
+ { "Stereo3 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo3 DMIC Mux", "DMIC2", "DMIC2" },
+ { "Stereo3 DMIC Mux", "DMIC3", "DMIC3" },
+ { "Stereo3 DMIC Mux", "DMIC4", "DMIC4" },
+
+ { "Stereo4 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo4 DMIC Mux", "DMIC2", "DMIC2" },
+ { "Stereo4 DMIC Mux", "DMIC3", "DMIC3" },
+ { "Stereo4 DMIC Mux", "DMIC4", "DMIC4" },
+
+ { "Mono DMIC L Mux", "DMIC1", "DMIC1" },
+ { "Mono DMIC L Mux", "DMIC2", "DMIC2" },
+ { "Mono DMIC L Mux", "DMIC3", "DMIC3" },
+ { "Mono DMIC L Mux", "DMIC4", "DMIC4" },
+
+ { "Mono DMIC R Mux", "DMIC1", "DMIC1" },
+ { "Mono DMIC R Mux", "DMIC2", "DMIC2" },
+ { "Mono DMIC R Mux", "DMIC3", "DMIC3" },
+ { "Mono DMIC R Mux", "DMIC4", "DMIC4" },
+
+ { "ADC 1_2", NULL, "ADC 1" },
+ { "ADC 1_2", NULL, "ADC 2" },
+
+ { "Stereo1 ADC1 Mux", "DD MIX1", "DD1 MIX" },
+ { "Stereo1 ADC1 Mux", "ADC1/2", "ADC 1_2" },
+ { "Stereo1 ADC1 Mux", "Stereo DAC MIX", "Stereo DAC MIX" },
+
+ { "Stereo1 ADC2 Mux", "DD MIX1", "DD1 MIX" },
+ { "Stereo1 ADC2 Mux", "DMIC", "Stereo1 DMIC Mux" },
+ { "Stereo1 ADC2 Mux", "Stereo DAC MIX", "Stereo DAC MIX" },
+
+ { "Stereo2 ADC1 Mux", "DD MIX1", "DD1 MIX" },
+ { "Stereo2 ADC1 Mux", "ADC1/2", "ADC 1_2" },
+ { "Stereo2 ADC1 Mux", "Stereo DAC MIX", "Stereo DAC MIX" },
+
+ { "Stereo2 ADC2 Mux", "DD MIX1", "DD1 MIX" },
+ { "Stereo2 ADC2 Mux", "DMIC", "Stereo2 DMIC Mux" },
+ { "Stereo2 ADC2 Mux", "Stereo DAC MIX", "Stereo DAC MIX" },
+
+ { "Stereo3 ADC1 Mux", "DD MIX1", "DD1 MIX" },
+ { "Stereo3 ADC1 Mux", "ADC1/2", "ADC 1_2" },
+ { "Stereo3 ADC1 Mux", "Stereo DAC MIX", "Stereo DAC MIX" },
+
+ { "Stereo3 ADC2 Mux", "DD MIX1", "DD1 MIX" },
+ { "Stereo3 ADC2 Mux", "DMIC", "Stereo3 DMIC Mux" },
+ { "Stereo3 ADC2 Mux", "Stereo DAC MIX", "Stereo DAC MIX" },
+
+ { "Stereo4 ADC1 Mux", "DD MIX1", "DD1 MIX" },
+ { "Stereo4 ADC1 Mux", "ADC1/2", "ADC 1_2" },
+ { "Stereo4 ADC1 Mux", "DD MIX2", "DD2 MIX" },
+
+ { "Stereo4 ADC2 Mux", "DD MIX1", "DD1 MIX" },
+ { "Stereo4 ADC2 Mux", "DMIC", "Stereo3 DMIC Mux" },
+ { "Stereo4 ADC2 Mux", "DD MIX2", "DD2 MIX" },
+
+ { "Mono ADC2 L Mux", "DD MIX1L", "DD1 MIXL" },
+ { "Mono ADC2 L Mux", "DMIC", "Mono DMIC L Mux" },
+ { "Mono ADC2 L Mux", "MONO DAC MIXL", "Mono DAC MIXL" },
+
+ { "Mono ADC1 L Mux", "DD MIX1L", "DD1 MIXL" },
+ { "Mono ADC1 L Mux", "ADC1", "ADC 1" },
+ { "Mono ADC1 L Mux", "MONO DAC MIXL", "Mono DAC MIXL" },
+
+ { "Mono ADC1 R Mux", "DD MIX1R", "DD1 MIXR" },
+ { "Mono ADC1 R Mux", "ADC2", "ADC 2" },
+ { "Mono ADC1 R Mux", "MONO DAC MIXR", "Mono DAC MIXR" },
+
+ { "Mono ADC2 R Mux", "DD MIX1R", "DD1 MIXR" },
+ { "Mono ADC2 R Mux", "DMIC", "Mono DMIC R Mux" },
+ { "Mono ADC2 R Mux", "MONO DAC MIXR", "Mono DAC MIXR" },
+
+ { "Sto1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC1 Mux" },
+ { "Sto1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC2 Mux" },
+ { "Sto1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC1 Mux" },
+ { "Sto1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC2 Mux" },
+
+ { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
+ { "Stereo1 ADC MIXL", NULL, "adc stereo1 filter" },
+ { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
+ { "Stereo1 ADC MIXR", NULL, "adc stereo1 filter" },
+ { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" },
+ { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" },
+
+ { "Sto2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC1 Mux" },
+ { "Sto2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC2 Mux" },
+ { "Sto2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC1 Mux" },
+ { "Sto2 ADC MIXR", "ADC2 Switch", "Stereo2 ADC2 Mux" },
+
+ { "Sto2 ADC LR MIX", NULL, "Sto2 ADC MIXL" },
+ { "Sto2 ADC LR MIX", NULL, "Sto2 ADC MIXR" },
+
+ { "Stereo2 ADC LR Mux", "L", "Sto2 ADC MIXL" },
+ { "Stereo2 ADC LR Mux", "LR", "Sto2 ADC LR MIX" },
+
+ { "Stereo2 ADC MIXL", NULL, "Stereo2 ADC LR Mux" },
+ { "Stereo2 ADC MIXL", NULL, "adc stereo2 filter" },
+ { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" },
+ { "Stereo2 ADC MIXR", NULL, "adc stereo2 filter" },
+ { "adc stereo2 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL" },
+ { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR" },
+
+ { "Sto3 ADC MIXL", "ADC1 Switch", "Stereo3 ADC1 Mux" },
+ { "Sto3 ADC MIXL", "ADC2 Switch", "Stereo3 ADC2 Mux" },
+ { "Sto3 ADC MIXR", "ADC1 Switch", "Stereo3 ADC1 Mux" },
+ { "Sto3 ADC MIXR", "ADC2 Switch", "Stereo3 ADC2 Mux" },
+
+ { "Stereo3 ADC MIXL", NULL, "Sto3 ADC MIXL" },
+ { "Stereo3 ADC MIXL", NULL, "adc stereo3 filter" },
+ { "Stereo3 ADC MIXR", NULL, "Sto3 ADC MIXR" },
+ { "Stereo3 ADC MIXR", NULL, "adc stereo3 filter" },
+ { "adc stereo3 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Stereo3 ADC MIX", NULL, "Stereo3 ADC MIXL" },
+ { "Stereo3 ADC MIX", NULL, "Stereo3 ADC MIXR" },
+
+ { "Sto4 ADC MIXL", "ADC1 Switch", "Stereo4 ADC1 Mux" },
+ { "Sto4 ADC MIXL", "ADC2 Switch", "Stereo4 ADC2 Mux" },
+ { "Sto4 ADC MIXR", "ADC1 Switch", "Stereo4 ADC1 Mux" },
+ { "Sto4 ADC MIXR", "ADC2 Switch", "Stereo4 ADC2 Mux" },
+
+ { "Stereo4 ADC MIXL", NULL, "Sto4 ADC MIXL" },
+ { "Stereo4 ADC MIXL", NULL, "adc stereo4 filter" },
+ { "Stereo4 ADC MIXR", NULL, "Sto4 ADC MIXR" },
+ { "Stereo4 ADC MIXR", NULL, "adc stereo4 filter" },
+ { "adc stereo4 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Stereo4 ADC MIX", NULL, "Stereo4 ADC MIXL" },
+ { "Stereo4 ADC MIX", NULL, "Stereo4 ADC MIXR" },
+
+ { "Mono ADC MIXL", "ADC1 Switch", "Mono ADC1 L Mux" },
+ { "Mono ADC MIXL", "ADC2 Switch", "Mono ADC2 L Mux" },
+ { "Mono ADC MIXL", NULL, "adc mono left filter" },
+ { "adc mono left filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Mono ADC MIXR", "ADC1 Switch", "Mono ADC1 R Mux" },
+ { "Mono ADC MIXR", "ADC2 Switch", "Mono ADC2 R Mux" },
+ { "Mono ADC MIXR", NULL, "adc mono right filter" },
+ { "adc mono right filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Mono ADC MIX", NULL, "Mono ADC MIXL" },
+ { "Mono ADC MIX", NULL, "Mono ADC MIXR" },
+
+ { "VAD ADC Mux", "STO1 ADC MIX L", "Stereo1 ADC MIXL" },
+ { "VAD ADC Mux", "MONO ADC MIX L", "Mono ADC MIXL" },
+ { "VAD ADC Mux", "MONO ADC MIX R", "Mono ADC MIXR" },
+ { "VAD ADC Mux", "STO2 ADC MIX L", "Stereo2 ADC MIXL" },
+ { "VAD ADC Mux", "STO3 ADC MIX L", "Stereo3 ADC MIXL" },
+
+ { "IF1 ADC1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
+ { "IF1 ADC1 Mux", "OB01", "OB01 Bypass Mux" },
+ { "IF1 ADC1 Mux", "VAD ADC", "VAD ADC Mux" },
+
+ { "IF1 ADC2 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+ { "IF1 ADC2 Mux", "OB23", "OB23 Bypass Mux" },
+
+ { "IF1 ADC3 Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" },
+ { "IF1 ADC3 Mux", "MONO ADC MIX", "Mono ADC MIX" },
+ { "IF1 ADC3 Mux", "OB45", "OB45" },
+
+ { "IF1 ADC4 Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" },
+ { "IF1 ADC4 Mux", "OB67", "OB67" },
+ { "IF1 ADC4 Mux", "OB01", "OB01 Bypass Mux" },
+
+ { "IF1 ADC1 Swap Mux", "L/R", "IF1 ADC1 Mux" },
+ { "IF1 ADC1 Swap Mux", "R/L", "IF1 ADC1 Mux" },
+ { "IF1 ADC1 Swap Mux", "L/L", "IF1 ADC1 Mux" },
+ { "IF1 ADC1 Swap Mux", "R/R", "IF1 ADC1 Mux" },
+
+ { "IF1 ADC2 Swap Mux", "L/R", "IF1 ADC2 Mux" },
+ { "IF1 ADC2 Swap Mux", "R/L", "IF1 ADC2 Mux" },
+ { "IF1 ADC2 Swap Mux", "L/L", "IF1 ADC2 Mux" },
+ { "IF1 ADC2 Swap Mux", "R/R", "IF1 ADC2 Mux" },
+
+ { "IF1 ADC3 Swap Mux", "L/R", "IF1 ADC3 Mux" },
+ { "IF1 ADC3 Swap Mux", "R/L", "IF1 ADC3 Mux" },
+ { "IF1 ADC3 Swap Mux", "L/L", "IF1 ADC3 Mux" },
+ { "IF1 ADC3 Swap Mux", "R/R", "IF1 ADC3 Mux" },
+
+ { "IF1 ADC4 Swap Mux", "L/R", "IF1 ADC4 Mux" },
+ { "IF1 ADC4 Swap Mux", "R/L", "IF1 ADC4 Mux" },
+ { "IF1 ADC4 Swap Mux", "L/L", "IF1 ADC4 Mux" },
+ { "IF1 ADC4 Swap Mux", "R/R", "IF1 ADC4 Mux" },
+
+ { "IF1 ADC", NULL, "IF1 ADC1 Swap Mux" },
+ { "IF1 ADC", NULL, "IF1 ADC2 Swap Mux" },
+ { "IF1 ADC", NULL, "IF1 ADC3 Swap Mux" },
+ { "IF1 ADC", NULL, "IF1 ADC4 Swap Mux" },
+
+ { "IF1 ADC TDM Swap Mux", "1/2/3/4", "IF1 ADC" },
+ { "IF1 ADC TDM Swap Mux", "2/1/3/4", "IF1 ADC" },
+ { "IF1 ADC TDM Swap Mux", "2/3/1/4", "IF1 ADC" },
+ { "IF1 ADC TDM Swap Mux", "4/1/2/3", "IF1 ADC" },
+ { "IF1 ADC TDM Swap Mux", "1/3/2/4", "IF1 ADC" },
+ { "IF1 ADC TDM Swap Mux", "1/4/2/3", "IF1 ADC" },
+ { "IF1 ADC TDM Swap Mux", "3/1/2/4", "IF1 ADC" },
+ { "IF1 ADC TDM Swap Mux", "3/4/1/2", "IF1 ADC" },
+
+ { "AIF1TX", NULL, "I2S1" },
+ { "AIF1TX", NULL, "IF1 ADC TDM Swap Mux" },
+
+ { "IF2 ADC1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
+ { "IF2 ADC1 Mux", "OB01", "OB01 Bypass Mux" },
+ { "IF2 ADC1 Mux", "VAD ADC", "VAD ADC Mux" },
+
+ { "IF2 ADC2 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+ { "IF2 ADC2 Mux", "OB23", "OB23 Bypass Mux" },
+
+ { "IF2 ADC3 Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" },
+ { "IF2 ADC3 Mux", "MONO ADC MIX", "Mono ADC MIX" },
+ { "IF2 ADC3 Mux", "OB45", "OB45" },
+
+ { "IF2 ADC4 Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" },
+ { "IF2 ADC4 Mux", "OB67", "OB67" },
+ { "IF2 ADC4 Mux", "OB01", "OB01 Bypass Mux" },
+
+ { "IF2 ADC1 Swap Mux", "L/R", "IF2 ADC1 Mux" },
+ { "IF2 ADC1 Swap Mux", "R/L", "IF2 ADC1 Mux" },
+ { "IF2 ADC1 Swap Mux", "L/L", "IF2 ADC1 Mux" },
+ { "IF2 ADC1 Swap Mux", "R/R", "IF2 ADC1 Mux" },
+
+ { "IF2 ADC2 Swap Mux", "L/R", "IF2 ADC2 Mux" },
+ { "IF2 ADC2 Swap Mux", "R/L", "IF2 ADC2 Mux" },
+ { "IF2 ADC2 Swap Mux", "L/L", "IF2 ADC2 Mux" },
+ { "IF2 ADC2 Swap Mux", "R/R", "IF2 ADC2 Mux" },
+
+ { "IF2 ADC3 Swap Mux", "L/R", "IF2 ADC3 Mux" },
+ { "IF2 ADC3 Swap Mux", "R/L", "IF2 ADC3 Mux" },
+ { "IF2 ADC3 Swap Mux", "L/L", "IF2 ADC3 Mux" },
+ { "IF2 ADC3 Swap Mux", "R/R", "IF2 ADC3 Mux" },
+
+ { "IF2 ADC4 Swap Mux", "L/R", "IF2 ADC4 Mux" },
+ { "IF2 ADC4 Swap Mux", "R/L", "IF2 ADC4 Mux" },
+ { "IF2 ADC4 Swap Mux", "L/L", "IF2 ADC4 Mux" },
+ { "IF2 ADC4 Swap Mux", "R/R", "IF2 ADC4 Mux" },
+
+ { "IF2 ADC", NULL, "IF2 ADC1 Swap Mux" },
+ { "IF2 ADC", NULL, "IF2 ADC2 Swap Mux" },
+ { "IF2 ADC", NULL, "IF2 ADC3 Swap Mux" },
+ { "IF2 ADC", NULL, "IF2 ADC4 Swap Mux" },
+
+ { "IF2 ADC TDM Swap Mux", "1/2/3/4", "IF2 ADC" },
+ { "IF2 ADC TDM Swap Mux", "2/1/3/4", "IF2 ADC" },
+ { "IF2 ADC TDM Swap Mux", "3/1/2/4", "IF2 ADC" },
+ { "IF2 ADC TDM Swap Mux", "4/1/2/3", "IF2 ADC" },
+ { "IF2 ADC TDM Swap Mux", "1/3/2/4", "IF2 ADC" },
+ { "IF2 ADC TDM Swap Mux", "1/4/2/3", "IF2 ADC" },
+ { "IF2 ADC TDM Swap Mux", "2/3/1/4", "IF2 ADC" },
+ { "IF2 ADC TDM Swap Mux", "3/4/1/2", "IF2 ADC" },
+
+ { "AIF2TX", NULL, "I2S2" },
+ { "AIF2TX", NULL, "IF2 ADC TDM Swap Mux" },
+
+ { "IF3 ADC Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
+ { "IF3 ADC Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+ { "IF3 ADC Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" },
+ { "IF3 ADC Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" },
+ { "IF3 ADC Mux", "MONO ADC MIX", "Mono ADC MIX" },
+ { "IF3 ADC Mux", "OB01", "OB01 Bypass Mux" },
+ { "IF3 ADC Mux", "OB23", "OB23 Bypass Mux" },
+ { "IF3 ADC Mux", "VAD ADC", "VAD ADC Mux" },
+
+ { "AIF3TX", NULL, "I2S3" },
+ { "AIF3TX", NULL, "IF3 ADC Mux" },
+
+ { "IF4 ADC Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
+ { "IF4 ADC Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+ { "IF4 ADC Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" },
+ { "IF4 ADC Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" },
+ { "IF4 ADC Mux", "MONO ADC MIX", "Mono ADC MIX" },
+ { "IF4 ADC Mux", "OB01", "OB01 Bypass Mux" },
+ { "IF4 ADC Mux", "OB23", "OB23 Bypass Mux" },
+ { "IF4 ADC Mux", "VAD ADC", "VAD ADC Mux" },
+
+ { "AIF4TX", NULL, "I2S4" },
+ { "AIF4TX", NULL, "IF4 ADC Mux" },
+
+ { "SLB ADC1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
+ { "SLB ADC1 Mux", "OB01", "OB01 Bypass Mux" },
+ { "SLB ADC1 Mux", "VAD ADC", "VAD ADC Mux" },
+
+ { "SLB ADC2 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+ { "SLB ADC2 Mux", "OB23", "OB23 Bypass Mux" },
+
+ { "SLB ADC3 Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" },
+ { "SLB ADC3 Mux", "MONO ADC MIX", "Mono ADC MIX" },
+ { "SLB ADC3 Mux", "OB45", "OB45" },
+
+ { "SLB ADC4 Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" },
+ { "SLB ADC4 Mux", "OB67", "OB67" },
+ { "SLB ADC4 Mux", "OB01", "OB01 Bypass Mux" },
+
+ { "SLBTX", NULL, "SLB" },
+ { "SLBTX", NULL, "SLB ADC1 Mux" },
+ { "SLBTX", NULL, "SLB ADC2 Mux" },
+ { "SLBTX", NULL, "SLB ADC3 Mux" },
+ { "SLBTX", NULL, "SLB ADC4 Mux" },
+
+ { "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" },
+
+ { "IB01 Bypass Mux", "Bypass", "IB01 Mux" },
+ { "IB01 Bypass Mux", "Pass SRC", "IB01 Mux" },
+
+ { "IB23 Mux", "IF1 DAC 23", "IF1 DAC23" },
+ { "IB23 Mux", "IF2 DAC 23", "IF2 DAC23" },
+ { "IB23 Mux", "SLB DAC 23", "SLB DAC23" },
+ { "IB23 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+ { "IB23 Mux", "DAC1 FS", "DAC1 FS" },
+ { "IB23 Mux", "IF4 DAC", "IF4 DAC" },
+
+ { "IB23 Bypass Mux", "Bypass", "IB23 Mux" },
+ { "IB23 Bypass Mux", "Pass SRC", "IB23 Mux" },
+
+ { "IB45 Mux", "IF1 DAC 45", "IF1 DAC45" },
+ { "IB45 Mux", "IF2 DAC 45", "IF2 DAC45" },
+ { "IB45 Mux", "SLB DAC 45", "SLB DAC45" },
+ { "IB45 Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" },
+ { "IB45 Mux", "IF3 DAC", "IF3 DAC" },
+
+ { "IB45 Bypass Mux", "Bypass", "IB45 Mux" },
+ { "IB45 Bypass Mux", "Pass SRC", "IB45 Mux" },
+
+ { "IB6 Mux", "IF1 DAC 6", "IF1 DAC6 Mux" },
+ { "IB6 Mux", "IF2 DAC 6", "IF2 DAC6 Mux" },
+ { "IB6 Mux", "SLB DAC 6", "SLB DAC6" },
+ { "IB6 Mux", "STO4 ADC MIX L", "Stereo4 ADC MIXL" },
+ { "IB6 Mux", "IF4 DAC L", "IF4 DAC L" },
+ { "IB6 Mux", "STO1 ADC MIX L", "Stereo1 ADC MIXL" },
+ { "IB6 Mux", "STO2 ADC MIX L", "Stereo2 ADC MIXL" },
+ { "IB6 Mux", "STO3 ADC MIX L", "Stereo3 ADC MIXL" },
+
+ { "IB7 Mux", "IF1 DAC 7", "IF1 DAC7 Mux" },
+ { "IB7 Mux", "IF2 DAC 7", "IF2 DAC7 Mux" },
+ { "IB7 Mux", "SLB DAC 7", "SLB DAC7" },
+ { "IB7 Mux", "STO4 ADC MIX R", "Stereo4 ADC MIXR" },
+ { "IB7 Mux", "IF4 DAC R", "IF4 DAC R" },
+ { "IB7 Mux", "STO1 ADC MIX R", "Stereo1 ADC MIXR" },
+ { "IB7 Mux", "STO2 ADC MIX R", "Stereo2 ADC MIXR" },
+ { "IB7 Mux", "STO3 ADC MIX R", "Stereo3 ADC MIXR" },
+
+ { "IB8 Mux", "STO1 ADC MIX L", "Stereo1 ADC MIXL" },
+ { "IB8 Mux", "STO2 ADC MIX L", "Stereo2 ADC MIXL" },
+ { "IB8 Mux", "STO3 ADC MIX L", "Stereo3 ADC MIXL" },
+ { "IB8 Mux", "STO4 ADC MIX L", "Stereo4 ADC MIXL" },
+ { "IB8 Mux", "MONO ADC MIX L", "Mono ADC MIXL" },
+ { "IB8 Mux", "DACL1 FS", "DAC1 MIXL" },
+
+ { "IB9 Mux", "STO1 ADC MIX R", "Stereo1 ADC MIXR" },
+ { "IB9 Mux", "STO2 ADC MIX R", "Stereo2 ADC MIXR" },
+ { "IB9 Mux", "STO3 ADC MIX R", "Stereo3 ADC MIXR" },
+ { "IB9 Mux", "STO4 ADC MIX R", "Stereo4 ADC MIXR" },
+ { "IB9 Mux", "MONO ADC MIX R", "Mono ADC MIXR" },
+ { "IB9 Mux", "DACR1 FS", "DAC1 MIXR" },
+ { "IB9 Mux", "DAC1 FS", "DAC1 FS" },
+
+ { "OB01 MIX", "IB01 Switch", "IB01 Bypass Mux" },
+ { "OB01 MIX", "IB23 Switch", "IB23 Bypass Mux" },
+ { "OB01 MIX", "IB45 Switch", "IB45 Bypass Mux" },
+ { "OB01 MIX", "IB6 Switch", "IB6 Mux" },
+ { "OB01 MIX", "IB7 Switch", "IB7 Mux" },
+ { "OB01 MIX", "IB8 Switch", "IB8 Mux" },
+ { "OB01 MIX", "IB9 Switch", "IB9 Mux" },
+
+ { "OB23 MIX", "IB01 Switch", "IB01 Bypass Mux" },
+ { "OB23 MIX", "IB23 Switch", "IB23 Bypass Mux" },
+ { "OB23 MIX", "IB45 Switch", "IB45 Bypass Mux" },
+ { "OB23 MIX", "IB6 Switch", "IB6 Mux" },
+ { "OB23 MIX", "IB7 Switch", "IB7 Mux" },
+ { "OB23 MIX", "IB8 Switch", "IB8 Mux" },
+ { "OB23 MIX", "IB9 Switch", "IB9 Mux" },
+
+ { "OB4 MIX", "IB01 Switch", "IB01 Bypass Mux" },
+ { "OB4 MIX", "IB23 Switch", "IB23 Bypass Mux" },
+ { "OB4 MIX", "IB45 Switch", "IB45 Bypass Mux" },
+ { "OB4 MIX", "IB6 Switch", "IB6 Mux" },
+ { "OB4 MIX", "IB7 Switch", "IB7 Mux" },
+ { "OB4 MIX", "IB8 Switch", "IB8 Mux" },
+ { "OB4 MIX", "IB9 Switch", "IB9 Mux" },
+
+ { "OB5 MIX", "IB01 Switch", "IB01 Bypass Mux" },
+ { "OB5 MIX", "IB23 Switch", "IB23 Bypass Mux" },
+ { "OB5 MIX", "IB45 Switch", "IB45 Bypass Mux" },
+ { "OB5 MIX", "IB6 Switch", "IB6 Mux" },
+ { "OB5 MIX", "IB7 Switch", "IB7 Mux" },
+ { "OB5 MIX", "IB8 Switch", "IB8 Mux" },
+ { "OB5 MIX", "IB9 Switch", "IB9 Mux" },
+
+ { "OB6 MIX", "IB01 Switch", "IB01 Bypass Mux" },
+ { "OB6 MIX", "IB23 Switch", "IB23 Bypass Mux" },
+ { "OB6 MIX", "IB45 Switch", "IB45 Bypass Mux" },
+ { "OB6 MIX", "IB6 Switch", "IB6 Mux" },
+ { "OB6 MIX", "IB7 Switch", "IB7 Mux" },
+ { "OB6 MIX", "IB8 Switch", "IB8 Mux" },
+ { "OB6 MIX", "IB9 Switch", "IB9 Mux" },
+
+ { "OB7 MIX", "IB01 Switch", "IB01 Bypass Mux" },
+ { "OB7 MIX", "IB23 Switch", "IB23 Bypass Mux" },
+ { "OB7 MIX", "IB45 Switch", "IB45 Bypass Mux" },
+ { "OB7 MIX", "IB6 Switch", "IB6 Mux" },
+ { "OB7 MIX", "IB7 Switch", "IB7 Mux" },
+ { "OB7 MIX", "IB8 Switch", "IB8 Mux" },
+ { "OB7 MIX", "IB9 Switch", "IB9 Mux" },
+
+ { "OB01 Bypass Mux", "Bypass", "OB01 MIX" },
+ { "OB01 Bypass Mux", "Pass SRC", "OB01 MIX" },
+ { "OB23 Bypass Mux", "Bypass", "OB23 MIX" },
+ { "OB23 Bypass Mux", "Pass SRC", "OB23 MIX" },
+
+ { "OutBound2", NULL, "OB23 Bypass Mux" },
+ { "OutBound3", NULL, "OB23 Bypass Mux" },
+ { "OutBound4", NULL, "OB4 MIX" },
+ { "OutBound5", NULL, "OB5 MIX" },
+ { "OutBound6", NULL, "OB6 MIX" },
+ { "OutBound7", NULL, "OB7 MIX" },
+
+ { "OB45", NULL, "OutBound4" },
+ { "OB45", NULL, "OutBound5" },
+ { "OB67", NULL, "OutBound6" },
+ { "OB67", NULL, "OutBound7" },
+
+ { "IF1 DAC0", NULL, "AIF1RX" },
+ { "IF1 DAC1", NULL, "AIF1RX" },
+ { "IF1 DAC2", NULL, "AIF1RX" },
+ { "IF1 DAC3", NULL, "AIF1RX" },
+ { "IF1 DAC4", NULL, "AIF1RX" },
+ { "IF1 DAC5", NULL, "AIF1RX" },
+ { "IF1 DAC6", NULL, "AIF1RX" },
+ { "IF1 DAC7", NULL, "AIF1RX" },
+ { "IF1 DAC0", NULL, "I2S1" },
+ { "IF1 DAC1", NULL, "I2S1" },
+ { "IF1 DAC2", NULL, "I2S1" },
+ { "IF1 DAC3", NULL, "I2S1" },
+ { "IF1 DAC4", NULL, "I2S1" },
+ { "IF1 DAC5", NULL, "I2S1" },
+ { "IF1 DAC6", NULL, "I2S1" },
+ { "IF1 DAC7", NULL, "I2S1" },
+
+ { "IF1 DAC0 Mux", "Slot0", "IF1 DAC0" },
+ { "IF1 DAC0 Mux", "Slot1", "IF1 DAC1" },
+ { "IF1 DAC0 Mux", "Slot2", "IF1 DAC2" },
+ { "IF1 DAC0 Mux", "Slot3", "IF1 DAC3" },
+ { "IF1 DAC0 Mux", "Slot4", "IF1 DAC4" },
+ { "IF1 DAC0 Mux", "Slot5", "IF1 DAC5" },
+ { "IF1 DAC0 Mux", "Slot6", "IF1 DAC6" },
+ { "IF1 DAC0 Mux", "Slot7", "IF1 DAC7" },
+
+ { "IF1 DAC1 Mux", "Slot0", "IF1 DAC0" },
+ { "IF1 DAC1 Mux", "Slot1", "IF1 DAC1" },
+ { "IF1 DAC1 Mux", "Slot2", "IF1 DAC2" },
+ { "IF1 DAC1 Mux", "Slot3", "IF1 DAC3" },
+ { "IF1 DAC1 Mux", "Slot4", "IF1 DAC4" },
+ { "IF1 DAC1 Mux", "Slot5", "IF1 DAC5" },
+ { "IF1 DAC1 Mux", "Slot6", "IF1 DAC6" },
+ { "IF1 DAC1 Mux", "Slot7", "IF1 DAC7" },
+
+ { "IF1 DAC2 Mux", "Slot0", "IF1 DAC0" },
+ { "IF1 DAC2 Mux", "Slot1", "IF1 DAC1" },
+ { "IF1 DAC2 Mux", "Slot2", "IF1 DAC2" },
+ { "IF1 DAC2 Mux", "Slot3", "IF1 DAC3" },
+ { "IF1 DAC2 Mux", "Slot4", "IF1 DAC4" },
+ { "IF1 DAC2 Mux", "Slot5", "IF1 DAC5" },
+ { "IF1 DAC2 Mux", "Slot6", "IF1 DAC6" },
+ { "IF1 DAC2 Mux", "Slot7", "IF1 DAC7" },
+
+ { "IF1 DAC3 Mux", "Slot0", "IF1 DAC0" },
+ { "IF1 DAC3 Mux", "Slot1", "IF1 DAC1" },
+ { "IF1 DAC3 Mux", "Slot2", "IF1 DAC2" },
+ { "IF1 DAC3 Mux", "Slot3", "IF1 DAC3" },
+ { "IF1 DAC3 Mux", "Slot4", "IF1 DAC4" },
+ { "IF1 DAC3 Mux", "Slot5", "IF1 DAC5" },
+ { "IF1 DAC3 Mux", "Slot6", "IF1 DAC6" },
+ { "IF1 DAC3 Mux", "Slot7", "IF1 DAC7" },
+
+ { "IF1 DAC4 Mux", "Slot0", "IF1 DAC0" },
+ { "IF1 DAC4 Mux", "Slot1", "IF1 DAC1" },
+ { "IF1 DAC4 Mux", "Slot2", "IF1 DAC2" },
+ { "IF1 DAC4 Mux", "Slot3", "IF1 DAC3" },
+ { "IF1 DAC4 Mux", "Slot4", "IF1 DAC4" },
+ { "IF1 DAC4 Mux", "Slot5", "IF1 DAC5" },
+ { "IF1 DAC4 Mux", "Slot6", "IF1 DAC6" },
+ { "IF1 DAC4 Mux", "Slot7", "IF1 DAC7" },
+
+ { "IF1 DAC5 Mux", "Slot0", "IF1 DAC0" },
+ { "IF1 DAC5 Mux", "Slot1", "IF1 DAC1" },
+ { "IF1 DAC5 Mux", "Slot2", "IF1 DAC2" },
+ { "IF1 DAC5 Mux", "Slot3", "IF1 DAC3" },
+ { "IF1 DAC5 Mux", "Slot4", "IF1 DAC4" },
+ { "IF1 DAC5 Mux", "Slot5", "IF1 DAC5" },
+ { "IF1 DAC5 Mux", "Slot6", "IF1 DAC6" },
+ { "IF1 DAC5 Mux", "Slot7", "IF1 DAC7" },
+
+ { "IF1 DAC6 Mux", "Slot0", "IF1 DAC0" },
+ { "IF1 DAC6 Mux", "Slot1", "IF1 DAC1" },
+ { "IF1 DAC6 Mux", "Slot2", "IF1 DAC2" },
+ { "IF1 DAC6 Mux", "Slot3", "IF1 DAC3" },
+ { "IF1 DAC6 Mux", "Slot4", "IF1 DAC4" },
+ { "IF1 DAC6 Mux", "Slot5", "IF1 DAC5" },
+ { "IF1 DAC6 Mux", "Slot6", "IF1 DAC6" },
+ { "IF1 DAC6 Mux", "Slot7", "IF1 DAC7" },
+
+ { "IF1 DAC7 Mux", "Slot0", "IF1 DAC0" },
+ { "IF1 DAC7 Mux", "Slot1", "IF1 DAC1" },
+ { "IF1 DAC7 Mux", "Slot2", "IF1 DAC2" },
+ { "IF1 DAC7 Mux", "Slot3", "IF1 DAC3" },
+ { "IF1 DAC7 Mux", "Slot4", "IF1 DAC4" },
+ { "IF1 DAC7 Mux", "Slot5", "IF1 DAC5" },
+ { "IF1 DAC7 Mux", "Slot6", "IF1 DAC6" },
+ { "IF1 DAC7 Mux", "Slot7", "IF1 DAC7" },
+
+ { "IF1 DAC01", NULL, "IF1 DAC0 Mux" },
+ { "IF1 DAC01", NULL, "IF1 DAC1 Mux" },
+ { "IF1 DAC23", NULL, "IF1 DAC2 Mux" },
+ { "IF1 DAC23", NULL, "IF1 DAC3 Mux" },
+ { "IF1 DAC45", NULL, "IF1 DAC4 Mux" },
+ { "IF1 DAC45", NULL, "IF1 DAC5 Mux" },
+ { "IF1 DAC67", NULL, "IF1 DAC6 Mux" },
+ { "IF1 DAC67", NULL, "IF1 DAC7 Mux" },
+
+ { "IF2 DAC0", NULL, "AIF2RX" },
+ { "IF2 DAC1", NULL, "AIF2RX" },
+ { "IF2 DAC2", NULL, "AIF2RX" },
+ { "IF2 DAC3", NULL, "AIF2RX" },
+ { "IF2 DAC4", NULL, "AIF2RX" },
+ { "IF2 DAC5", NULL, "AIF2RX" },
+ { "IF2 DAC6", NULL, "AIF2RX" },
+ { "IF2 DAC7", NULL, "AIF2RX" },
+ { "IF2 DAC0", NULL, "I2S2" },
+ { "IF2 DAC1", NULL, "I2S2" },
+ { "IF2 DAC2", NULL, "I2S2" },
+ { "IF2 DAC3", NULL, "I2S2" },
+ { "IF2 DAC4", NULL, "I2S2" },
+ { "IF2 DAC5", NULL, "I2S2" },
+ { "IF2 DAC6", NULL, "I2S2" },
+ { "IF2 DAC7", NULL, "I2S2" },
+
+ { "IF2 DAC0 Mux", "Slot0", "IF2 DAC0" },
+ { "IF2 DAC0 Mux", "Slot1", "IF2 DAC1" },
+ { "IF2 DAC0 Mux", "Slot2", "IF2 DAC2" },
+ { "IF2 DAC0 Mux", "Slot3", "IF2 DAC3" },
+ { "IF2 DAC0 Mux", "Slot4", "IF2 DAC4" },
+ { "IF2 DAC0 Mux", "Slot5", "IF2 DAC5" },
+ { "IF2 DAC0 Mux", "Slot6", "IF2 DAC6" },
+ { "IF2 DAC0 Mux", "Slot7", "IF2 DAC7" },
+
+ { "IF2 DAC1 Mux", "Slot0", "IF2 DAC0" },
+ { "IF2 DAC1 Mux", "Slot1", "IF2 DAC1" },
+ { "IF2 DAC1 Mux", "Slot2", "IF2 DAC2" },
+ { "IF2 DAC1 Mux", "Slot3", "IF2 DAC3" },
+ { "IF2 DAC1 Mux", "Slot4", "IF2 DAC4" },
+ { "IF2 DAC1 Mux", "Slot5", "IF2 DAC5" },
+ { "IF2 DAC1 Mux", "Slot6", "IF2 DAC6" },
+ { "IF2 DAC1 Mux", "Slot7", "IF2 DAC7" },
+
+ { "IF2 DAC2 Mux", "Slot0", "IF2 DAC0" },
+ { "IF2 DAC2 Mux", "Slot1", "IF2 DAC1" },
+ { "IF2 DAC2 Mux", "Slot2", "IF2 DAC2" },
+ { "IF2 DAC2 Mux", "Slot3", "IF2 DAC3" },
+ { "IF2 DAC2 Mux", "Slot4", "IF2 DAC4" },
+ { "IF2 DAC2 Mux", "Slot5", "IF2 DAC5" },
+ { "IF2 DAC2 Mux", "Slot6", "IF2 DAC6" },
+ { "IF2 DAC2 Mux", "Slot7", "IF2 DAC7" },
+
+ { "IF2 DAC3 Mux", "Slot0", "IF2 DAC0" },
+ { "IF2 DAC3 Mux", "Slot1", "IF2 DAC1" },
+ { "IF2 DAC3 Mux", "Slot2", "IF2 DAC2" },
+ { "IF2 DAC3 Mux", "Slot3", "IF2 DAC3" },
+ { "IF2 DAC3 Mux", "Slot4", "IF2 DAC4" },
+ { "IF2 DAC3 Mux", "Slot5", "IF2 DAC5" },
+ { "IF2 DAC3 Mux", "Slot6", "IF2 DAC6" },
+ { "IF2 DAC3 Mux", "Slot7", "IF2 DAC7" },
+
+ { "IF2 DAC4 Mux", "Slot0", "IF2 DAC0" },
+ { "IF2 DAC4 Mux", "Slot1", "IF2 DAC1" },
+ { "IF2 DAC4 Mux", "Slot2", "IF2 DAC2" },
+ { "IF2 DAC4 Mux", "Slot3", "IF2 DAC3" },
+ { "IF2 DAC4 Mux", "Slot4", "IF2 DAC4" },
+ { "IF2 DAC4 Mux", "Slot5", "IF2 DAC5" },
+ { "IF2 DAC4 Mux", "Slot6", "IF2 DAC6" },
+ { "IF2 DAC4 Mux", "Slot7", "IF2 DAC7" },
+
+ { "IF2 DAC5 Mux", "Slot0", "IF2 DAC0" },
+ { "IF2 DAC5 Mux", "Slot1", "IF2 DAC1" },
+ { "IF2 DAC5 Mux", "Slot2", "IF2 DAC2" },
+ { "IF2 DAC5 Mux", "Slot3", "IF2 DAC3" },
+ { "IF2 DAC5 Mux", "Slot4", "IF2 DAC4" },
+ { "IF2 DAC5 Mux", "Slot5", "IF2 DAC5" },
+ { "IF2 DAC5 Mux", "Slot6", "IF2 DAC6" },
+ { "IF2 DAC5 Mux", "Slot7", "IF2 DAC7" },
+
+ { "IF2 DAC6 Mux", "Slot0", "IF2 DAC0" },
+ { "IF2 DAC6 Mux", "Slot1", "IF2 DAC1" },
+ { "IF2 DAC6 Mux", "Slot2", "IF2 DAC2" },
+ { "IF2 DAC6 Mux", "Slot3", "IF2 DAC3" },
+ { "IF2 DAC6 Mux", "Slot4", "IF2 DAC4" },
+ { "IF2 DAC6 Mux", "Slot5", "IF2 DAC5" },
+ { "IF2 DAC6 Mux", "Slot6", "IF2 DAC6" },
+ { "IF2 DAC6 Mux", "Slot7", "IF2 DAC7" },
+
+ { "IF2 DAC7 Mux", "Slot0", "IF2 DAC0" },
+ { "IF2 DAC7 Mux", "Slot1", "IF2 DAC1" },
+ { "IF2 DAC7 Mux", "Slot2", "IF2 DAC2" },
+ { "IF2 DAC7 Mux", "Slot3", "IF2 DAC3" },
+ { "IF2 DAC7 Mux", "Slot4", "IF2 DAC4" },
+ { "IF2 DAC7 Mux", "Slot5", "IF2 DAC5" },
+ { "IF2 DAC7 Mux", "Slot6", "IF2 DAC6" },
+ { "IF2 DAC7 Mux", "Slot7", "IF2 DAC7" },
+
+ { "IF2 DAC01", NULL, "IF2 DAC0 Mux" },
+ { "IF2 DAC01", NULL, "IF2 DAC1 Mux" },
+ { "IF2 DAC23", NULL, "IF2 DAC2 Mux" },
+ { "IF2 DAC23", NULL, "IF2 DAC3 Mux" },
+ { "IF2 DAC45", NULL, "IF2 DAC4 Mux" },
+ { "IF2 DAC45", NULL, "IF2 DAC5 Mux" },
+ { "IF2 DAC67", NULL, "IF2 DAC6 Mux" },
+ { "IF2 DAC67", NULL, "IF2 DAC7 Mux" },
+
+ { "IF3 DAC", NULL, "AIF3RX" },
+ { "IF3 DAC", NULL, "I2S3" },
+
+ { "IF4 DAC", NULL, "AIF4RX" },
+ { "IF4 DAC", NULL, "I2S4" },
+
+ { "IF3 DAC L", NULL, "IF3 DAC" },
+ { "IF3 DAC R", NULL, "IF3 DAC" },
+
+ { "IF4 DAC L", NULL, "IF4 DAC" },
+ { "IF4 DAC R", NULL, "IF4 DAC" },
+
+ { "SLB DAC0", NULL, "SLBRX" },
+ { "SLB DAC1", NULL, "SLBRX" },
+ { "SLB DAC2", NULL, "SLBRX" },
+ { "SLB DAC3", NULL, "SLBRX" },
+ { "SLB DAC4", NULL, "SLBRX" },
+ { "SLB DAC5", NULL, "SLBRX" },
+ { "SLB DAC6", NULL, "SLBRX" },
+ { "SLB DAC7", NULL, "SLBRX" },
+ { "SLB DAC0", NULL, "SLB" },
+ { "SLB DAC1", NULL, "SLB" },
+ { "SLB DAC2", NULL, "SLB" },
+ { "SLB DAC3", NULL, "SLB" },
+ { "SLB DAC4", NULL, "SLB" },
+ { "SLB DAC5", NULL, "SLB" },
+ { "SLB DAC6", NULL, "SLB" },
+ { "SLB DAC7", NULL, "SLB" },
+
+ { "SLB DAC01", NULL, "SLB DAC0" },
+ { "SLB DAC01", NULL, "SLB DAC1" },
+ { "SLB DAC23", NULL, "SLB DAC2" },
+ { "SLB DAC23", NULL, "SLB DAC3" },
+ { "SLB DAC45", NULL, "SLB DAC4" },
+ { "SLB DAC45", NULL, "SLB DAC5" },
+ { "SLB DAC67", NULL, "SLB DAC6" },
+ { "SLB DAC67", NULL, "SLB DAC7" },
+
+ { "ADDA1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
+ { "ADDA1 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+ { "ADDA1 Mux", "OB 67", "OB67" },
+
+ { "DAC1 Mux", "IF1 DAC 01", "IF1 DAC01" },
+ { "DAC1 Mux", "IF2 DAC 01", "IF2 DAC01" },
+ { "DAC1 Mux", "IF3 DAC LR", "IF3 DAC" },
+ { "DAC1 Mux", "IF4 DAC LR", "IF4 DAC" },
+ { "DAC1 Mux", "SLB DAC 01", "SLB DAC01" },
+ { "DAC1 Mux", "OB 01", "OB01 Bypass Mux" },
+
+ { "DAC1 MIXL", "Stereo ADC Switch", "ADDA1 Mux" },
+ { "DAC1 MIXL", "DAC1 Switch", "DAC1 Mux" },
+ { "DAC1 MIXR", "Stereo ADC Switch", "ADDA1 Mux" },
+ { "DAC1 MIXR", "DAC1 Switch", "DAC1 Mux" },
+
+ { "DAC1 FS", NULL, "DAC1 MIXL" },
+ { "DAC1 FS", NULL, "DAC1 MIXR" },
+
+ { "DAC2 L Mux", "IF1 DAC 2", "IF1 DAC2 Mux" },
+ { "DAC2 L Mux", "IF2 DAC 2", "IF2 DAC2 Mux" },
+ { "DAC2 L Mux", "IF3 DAC L", "IF3 DAC L" },
+ { "DAC2 L Mux", "IF4 DAC L", "IF4 DAC L" },
+ { "DAC2 L Mux", "SLB DAC 2", "SLB DAC2" },
+ { "DAC2 L Mux", "OB 2", "OutBound2" },
+
+ { "DAC2 R Mux", "IF1 DAC 3", "IF1 DAC3 Mux" },
+ { "DAC2 R Mux", "IF2 DAC 3", "IF2 DAC3 Mux" },
+ { "DAC2 R Mux", "IF3 DAC R", "IF3 DAC R" },
+ { "DAC2 R Mux", "IF4 DAC R", "IF4 DAC R" },
+ { "DAC2 R Mux", "SLB DAC 3", "SLB DAC3" },
+ { "DAC2 R Mux", "OB 3", "OutBound3" },
+ { "DAC2 R Mux", "Haptic Generator", "Haptic Generator" },
+ { "DAC2 R Mux", "VAD ADC", "VAD ADC Mux" },
+
+ { "DAC3 L Mux", "IF1 DAC 4", "IF1 DAC4 Mux" },
+ { "DAC3 L Mux", "IF2 DAC 4", "IF2 DAC4 Mux" },
+ { "DAC3 L Mux", "IF3 DAC L", "IF3 DAC L" },
+ { "DAC3 L Mux", "IF4 DAC L", "IF4 DAC L" },
+ { "DAC3 L Mux", "SLB DAC 4", "SLB DAC4" },
+ { "DAC3 L Mux", "OB 4", "OutBound4" },
+
+ { "DAC3 R Mux", "IF1 DAC 5", "IF1 DAC5 Mux" },
+ { "DAC3 R Mux", "IF2 DAC 5", "IF2 DAC5 Mux" },
+ { "DAC3 R Mux", "IF3 DAC R", "IF3 DAC R" },
+ { "DAC3 R Mux", "IF4 DAC R", "IF4 DAC R" },
+ { "DAC3 R Mux", "SLB DAC 5", "SLB DAC5" },
+ { "DAC3 R Mux", "OB 5", "OutBound5" },
+
+ { "DAC4 L Mux", "IF1 DAC 6", "IF1 DAC6 Mux" },
+ { "DAC4 L Mux", "IF2 DAC 6", "IF2 DAC6 Mux" },
+ { "DAC4 L Mux", "IF3 DAC L", "IF3 DAC L" },
+ { "DAC4 L Mux", "IF4 DAC L", "IF4 DAC L" },
+ { "DAC4 L Mux", "SLB DAC 6", "SLB DAC6" },
+ { "DAC4 L Mux", "OB 6", "OutBound6" },
+
+ { "DAC4 R Mux", "IF1 DAC 7", "IF1 DAC7 Mux" },
+ { "DAC4 R Mux", "IF2 DAC 7", "IF2 DAC7 Mux" },
+ { "DAC4 R Mux", "IF3 DAC R", "IF3 DAC R" },
+ { "DAC4 R Mux", "IF4 DAC R", "IF4 DAC R" },
+ { "DAC4 R Mux", "SLB DAC 7", "SLB DAC7" },
+ { "DAC4 R Mux", "OB 7", "OutBound7" },
+
+ { "Sidetone Mux", "DMIC1 L", "DMIC L1" },
+ { "Sidetone Mux", "DMIC2 L", "DMIC L2" },
+ { "Sidetone Mux", "DMIC3 L", "DMIC L3" },
+ { "Sidetone Mux", "DMIC4 L", "DMIC L4" },
+ { "Sidetone Mux", "ADC1", "ADC 1" },
+ { "Sidetone Mux", "ADC2", "ADC 2" },
+ { "Sidetone Mux", NULL, "Sidetone Power" },
+
+ { "Stereo DAC MIXL", "ST L Switch", "Sidetone Mux" },
+ { "Stereo DAC MIXL", "DAC1 L Switch", "DAC1 MIXL" },
+ { "Stereo DAC MIXL", "DAC2 L Switch", "DAC2 L Mux" },
+ { "Stereo DAC MIXL", "DAC1 R Switch", "DAC1 MIXR" },
+ { "Stereo DAC MIXL", NULL, "dac stereo1 filter" },
+ { "Stereo DAC MIXR", "ST R Switch", "Sidetone Mux" },
+ { "Stereo DAC MIXR", "DAC1 R Switch", "DAC1 MIXR" },
+ { "Stereo DAC MIXR", "DAC2 R Switch", "DAC2 R Mux" },
+ { "Stereo DAC MIXR", "DAC1 L Switch", "DAC1 MIXL" },
+ { "Stereo DAC MIXR", NULL, "dac stereo1 filter" },
+ { "dac stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Mono DAC MIXL", "ST L Switch", "Sidetone Mux" },
+ { "Mono DAC MIXL", "DAC1 L Switch", "DAC1 MIXL" },
+ { "Mono DAC MIXL", "DAC2 L Switch", "DAC2 L Mux" },
+ { "Mono DAC MIXL", "DAC2 R Switch", "DAC2 R Mux" },
+ { "Mono DAC MIXL", NULL, "dac mono2 left filter" },
+ { "dac mono2 left filter", NULL, "PLL1", is_sys_clk_from_pll },
+ { "Mono DAC MIXR", "ST R Switch", "Sidetone Mux" },
+ { "Mono DAC MIXR", "DAC1 R Switch", "DAC1 MIXR" },
+ { "Mono DAC MIXR", "DAC2 R Switch", "DAC2 R Mux" },
+ { "Mono DAC MIXR", "DAC2 L Switch", "DAC2 L Mux" },
+ { "Mono DAC MIXR", NULL, "dac mono2 right filter" },
+ { "dac mono2 right filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "DD1 MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" },
+ { "DD1 MIXL", "Mono DAC Mix L Switch", "Mono DAC MIXL" },
+ { "DD1 MIXL", "DAC3 L Switch", "DAC3 L Mux" },
+ { "DD1 MIXL", "DAC3 R Switch", "DAC3 R Mux" },
+ { "DD1 MIXL", NULL, "dac mono3 left filter" },
+ { "dac mono3 left filter", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DD1 MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" },
+ { "DD1 MIXR", "Mono DAC Mix R Switch", "Mono DAC MIXR" },
+ { "DD1 MIXR", "DAC3 L Switch", "DAC3 L Mux" },
+ { "DD1 MIXR", "DAC3 R Switch", "DAC3 R Mux" },
+ { "DD1 MIXR", NULL, "dac mono3 right filter" },
+ { "dac mono3 right filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "DD2 MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" },
+ { "DD2 MIXL", "Mono DAC Mix L Switch", "Mono DAC MIXL" },
+ { "DD2 MIXL", "DAC4 L Switch", "DAC4 L Mux" },
+ { "DD2 MIXL", "DAC4 R Switch", "DAC4 R Mux" },
+ { "DD2 MIXL", NULL, "dac mono4 left filter" },
+ { "dac mono4 left filter", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DD2 MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" },
+ { "DD2 MIXR", "Mono DAC Mix R Switch", "Mono DAC MIXR" },
+ { "DD2 MIXR", "DAC4 L Switch", "DAC4 L Mux" },
+ { "DD2 MIXR", "DAC4 R Switch", "DAC4 R Mux" },
+ { "DD2 MIXR", NULL, "dac mono4 right filter" },
+ { "dac mono4 right filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+ { "Stereo DAC MIX", NULL, "Stereo DAC MIXL" },
+ { "Stereo DAC MIX", NULL, "Stereo DAC MIXR" },
+ { "Mono DAC MIX", NULL, "Mono DAC MIXL" },
+ { "Mono DAC MIX", NULL, "Mono DAC MIXR" },
+ { "DD1 MIX", NULL, "DD1 MIXL" },
+ { "DD1 MIX", NULL, "DD1 MIXR" },
+ { "DD2 MIX", NULL, "DD2 MIXL" },
+ { "DD2 MIX", NULL, "DD2 MIXR" },
+
+ { "DAC12 SRC Mux", "STO1 DAC MIX", "Stereo DAC MIX" },
+ { "DAC12 SRC Mux", "MONO DAC MIX", "Mono DAC MIX" },
+ { "DAC12 SRC Mux", "DD MIX1", "DD1 MIX" },
+ { "DAC12 SRC Mux", "DD MIX2", "DD2 MIX" },
+
+ { "DAC3 SRC Mux", "MONO DAC MIXL", "Mono DAC MIXL" },
+ { "DAC3 SRC Mux", "MONO DAC MIXR", "Mono DAC MIXR" },
+ { "DAC3 SRC Mux", "DD MIX1L", "DD1 MIXL" },
+ { "DAC3 SRC Mux", "DD MIX2L", "DD2 MIXL" },
+
+ { "DAC 1", NULL, "DAC12 SRC Mux" },
+ { "DAC 2", NULL, "DAC12 SRC Mux" },
+ { "DAC 3", NULL, "DAC3 SRC Mux" },
+
+ { "PDM1 L Mux", "STO1 DAC MIX", "Stereo DAC MIXL" },
+ { "PDM1 L Mux", "MONO DAC MIX", "Mono DAC MIXL" },
+ { "PDM1 L Mux", "DD MIX1", "DD1 MIXL" },
+ { "PDM1 L Mux", "DD MIX2", "DD2 MIXL" },
+ { "PDM1 L Mux", NULL, "PDM1 Power" },
+ { "PDM1 R Mux", "STO1 DAC MIX", "Stereo DAC MIXR" },
+ { "PDM1 R Mux", "MONO DAC MIX", "Mono DAC MIXR" },
+ { "PDM1 R Mux", "DD MIX1", "DD1 MIXR" },
+ { "PDM1 R Mux", "DD MIX2", "DD2 MIXR" },
+ { "PDM1 R Mux", NULL, "PDM1 Power" },
+ { "PDM2 L Mux", "STO1 DAC MIX", "Stereo DAC MIXL" },
+ { "PDM2 L Mux", "MONO DAC MIX", "Mono DAC MIXL" },
+ { "PDM2 L Mux", "DD MIX1", "DD1 MIXL" },
+ { "PDM2 L Mux", "DD MIX2", "DD2 MIXL" },
+ { "PDM2 L Mux", NULL, "PDM2 Power" },
+ { "PDM2 R Mux", "STO1 DAC MIX", "Stereo DAC MIXR" },
+ { "PDM2 R Mux", "MONO DAC MIX", "Mono DAC MIXR" },
+ { "PDM2 R Mux", "DD MIX1", "DD1 MIXR" },
+ { "PDM2 R Mux", "DD MIX1", "DD2 MIXR" },
+ { "PDM2 R Mux", NULL, "PDM2 Power" },
+
+ { "LOUT1 amp", NULL, "DAC 1" },
+ { "LOUT2 amp", NULL, "DAC 2" },
+ { "LOUT3 amp", NULL, "DAC 3" },
+
+ { "LOUT1 vref", NULL, "LOUT1 amp" },
+ { "LOUT2 vref", NULL, "LOUT2 amp" },
+ { "LOUT3 vref", NULL, "LOUT3 amp" },
+
+ { "LOUT1", NULL, "LOUT1 vref" },
+ { "LOUT2", NULL, "LOUT2 vref" },
+ { "LOUT3", NULL, "LOUT3 vref" },
+
+ { "PDM1L", NULL, "PDM1 L Mux" },
+ { "PDM1R", NULL, "PDM1 R Mux" },
+ { "PDM2L", NULL, "PDM2 L Mux" },
+ { "PDM2R", NULL, "PDM2 R Mux" },
+};
+
+static const struct snd_soc_dapm_route rt5677_dmic2_clk_1[] = {
+ { "DMIC L2", NULL, "DMIC1 power" },
+ { "DMIC R2", NULL, "DMIC1 power" },
+};
+
+static const struct snd_soc_dapm_route rt5677_dmic2_clk_2[] = {
+ { "DMIC L2", NULL, "DMIC2 power" },
+ { "DMIC R2", NULL, "DMIC2 power" },
+};
+
+static int rt5677_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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",
+ 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);
+ return -EINVAL;
+ }
+ bclk_ms = frame_size > 32;
+ rt5677->bclk[dai->id] = rt5677->lrck[dai->id] * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt5677->bclk[dai->id], rt5677->lrck[dai->id]);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ val_len |= RT5677_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= RT5677_I2S_DL_24;
+ break;
+ case 8:
+ val_len |= RT5677_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5677_AIF1:
+ mask_clk = RT5677_I2S_PD1_MASK;
+ val_clk = pre_div << RT5677_I2S_PD1_SFT;
+ regmap_update_bits(rt5677->regmap, RT5677_I2S1_SDP,
+ RT5677_I2S_DL_MASK, val_len);
+ regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1,
+ mask_clk, val_clk);
+ break;
+ case RT5677_AIF2:
+ mask_clk = RT5677_I2S_PD2_MASK;
+ val_clk = pre_div << RT5677_I2S_PD2_SFT;
+ regmap_update_bits(rt5677->regmap, RT5677_I2S2_SDP,
+ RT5677_I2S_DL_MASK, val_len);
+ regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1,
+ mask_clk, val_clk);
+ break;
+ case RT5677_AIF3:
+ mask_clk = RT5677_I2S_BCLK_MS3_MASK | RT5677_I2S_PD3_MASK;
+ val_clk = bclk_ms << RT5677_I2S_BCLK_MS3_SFT |
+ pre_div << RT5677_I2S_PD3_SFT;
+ regmap_update_bits(rt5677->regmap, RT5677_I2S3_SDP,
+ RT5677_I2S_DL_MASK, val_len);
+ regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1,
+ mask_clk, val_clk);
+ break;
+ case RT5677_AIF4:
+ mask_clk = RT5677_I2S_BCLK_MS4_MASK | RT5677_I2S_PD4_MASK;
+ val_clk = bclk_ms << RT5677_I2S_BCLK_MS4_SFT |
+ pre_div << RT5677_I2S_PD4_SFT;
+ regmap_update_bits(rt5677->regmap, RT5677_I2S4_SDP,
+ RT5677_I2S_DL_MASK, val_len);
+ regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1,
+ mask_clk, val_clk);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5677_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rt5677->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT5677_I2S_MS_S;
+ rt5677->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5677_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5677_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5677_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5677_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5677_AIF1:
+ regmap_update_bits(rt5677->regmap, RT5677_I2S1_SDP,
+ RT5677_I2S_MS_MASK | RT5677_I2S_BP_MASK |
+ RT5677_I2S_DF_MASK, reg_val);
+ break;
+ case RT5677_AIF2:
+ regmap_update_bits(rt5677->regmap, RT5677_I2S2_SDP,
+ RT5677_I2S_MS_MASK | RT5677_I2S_BP_MASK |
+ RT5677_I2S_DF_MASK, reg_val);
+ break;
+ case RT5677_AIF3:
+ regmap_update_bits(rt5677->regmap, RT5677_I2S3_SDP,
+ RT5677_I2S_MS_MASK | RT5677_I2S_BP_MASK |
+ RT5677_I2S_DF_MASK, reg_val);
+ break;
+ case RT5677_AIF4:
+ regmap_update_bits(rt5677->regmap, RT5677_I2S4_SDP,
+ RT5677_I2S_MS_MASK | RT5677_I2S_BP_MASK |
+ RT5677_I2S_DF_MASK, reg_val);
+ break;
+ default:
+ break;
+ }
+
+
+ return 0;
+}
+
+static int rt5677_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+
+ if (freq == rt5677->sysclk && clk_id == rt5677->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5677_SCLK_S_MCLK:
+ reg_val |= RT5677_SCLK_SRC_MCLK;
+ break;
+ case RT5677_SCLK_S_PLL1:
+ reg_val |= RT5677_SCLK_SRC_PLL1;
+ break;
+ case RT5677_SCLK_S_RCCLK:
+ reg_val |= RT5677_SCLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+ RT5677_SCLK_SRC_MASK, reg_val);
+ rt5677->sysclk = freq;
+ rt5677->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+ return 0;
+}
+
+/**
+ * rt5677_pll_calc - Calcualte PLL M/N/K code.
+ * @freq_in: external clock provided to codec.
+ * @freq_out: target clock which codec works on.
+ * @pll_code: Pointer to structure with M, N, K, bypass K and bypass M flag.
+ *
+ * Calcualte M/N/K code and bypass K/M flag to configure PLL for codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_pll_calc(const unsigned int freq_in,
+ const unsigned int freq_out, struct rl6231_pll_code *pll_code)
+{
+ if (RT5677_PLL_INP_MIN > freq_in)
+ return -EINVAL;
+
+ return rl6231_pll_calc(freq_in, freq_out, pll_code);
+}
+
+static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt5677->pll_src && freq_in == rt5677->pll_in &&
+ freq_out == rt5677->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(codec->dev, "PLL disabled\n");
+
+ rt5677->pll_in = 0;
+ rt5677->pll_out = 0;
+ regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+ RT5677_SCLK_SRC_MASK, RT5677_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT5677_PLL1_S_MCLK:
+ regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+ RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_MCLK);
+ break;
+ case RT5677_PLL1_S_BCLK1:
+ case RT5677_PLL1_S_BCLK2:
+ case RT5677_PLL1_S_BCLK3:
+ case RT5677_PLL1_S_BCLK4:
+ switch (dai->id) {
+ case RT5677_AIF1:
+ regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+ RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_BCLK1);
+ break;
+ case RT5677_AIF2:
+ regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+ RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_BCLK2);
+ break;
+ case RT5677_AIF3:
+ regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+ RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_BCLK3);
+ break;
+ case RT5677_AIF4:
+ regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+ RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_BCLK4);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ dev_err(codec->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);
+ return ret;
+ }
+
+ dev_dbg(codec->dev, "m_bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ regmap_write(rt5677->regmap, RT5677_PLL1_CTRL1,
+ pll_code.n_code << RT5677_PLL_N_SFT | pll_code.k_code);
+ regmap_write(rt5677->regmap, RT5677_PLL1_CTRL2,
+ (pll_code.m_bp ? 0 : pll_code.m_code) << RT5677_PLL_M_SFT |
+ pll_code.m_bp << RT5677_PLL_M_BP_SFT);
+
+ rt5677->pll_in = freq_in;
+ rt5677->pll_out = freq_out;
+ rt5677->pll_src = source;
+
+ return 0;
+}
+
+static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = 0, slot_width_25 = 0;
+
+ if (rx_mask || tx_mask)
+ val |= (1 << 12);
+
+ switch (slots) {
+ case 4:
+ val |= (1 << 10);
+ break;
+ case 6:
+ val |= (2 << 10);
+ break;
+ case 8:
+ val |= (3 << 10);
+ break;
+ case 2:
+ default:
+ break;
+ }
+
+ switch (slot_width) {
+ case 20:
+ val |= (1 << 8);
+ break;
+ case 25:
+ slot_width_25 = 0x8080;
+ case 24:
+ val |= (2 << 8);
+ break;
+ case 32:
+ val |= (3 << 8);
+ break;
+ case 16:
+ default:
+ break;
+ }
+
+ switch (dai->id) {
+ case RT5677_AIF1:
+ regmap_update_bits(rt5677->regmap, RT5677_TDM1_CTRL1, 0x1f00,
+ val);
+ regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x8000,
+ slot_width_25);
+ break;
+ case RT5677_AIF2:
+ regmap_update_bits(rt5677->regmap, RT5677_TDM2_CTRL1, 0x1f00,
+ val);
+ regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x80,
+ slot_width_25);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5677_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+ 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);
+
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+ RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
+ 0x0055);
+ regmap_update_bits(rt5677->regmap,
+ RT5677_PR_BASE + RT5677_BIAS_CUR4,
+ 0x0f00, 0x0f00);
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+ RT5677_PWR_FV1 | RT5677_PWR_FV2 |
+ RT5677_PWR_VREF1 | RT5677_PWR_MB |
+ RT5677_PWR_BG | RT5677_PWR_VREF2,
+ RT5677_PWR_VREF1 | RT5677_PWR_MB |
+ RT5677_PWR_BG | RT5677_PWR_VREF2);
+ rt5677->is_vref_slow = false;
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+ RT5677_PWR_CORE, RT5677_PWR_CORE);
+ regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
+ 0x1, 0x1);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ 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_update_bits(rt5677->regmap,
+ RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0000);
+
+ if (rt5677->dsp_vad_en)
+ rt5677_set_dsp_vad(codec, 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)
+{
+ return container_of(chip, struct rt5677_priv, gpio_chip);
+}
+
+static void rt5677_gpio_set(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,
+ 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;
+
+ default:
+ break;
+ }
+}
+
+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;
+ }
+
+ return 0;
+}
+
+static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+ int value, ret;
+
+ ret = regmap_read(rt5677->regmap, RT5677_GPIO_ST, &value);
+ if (ret < 0)
+ return ret;
+
+ return (value & (0x1 << offset)) >> offset;
+}
+
+static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct rt5677_priv *rt5677 = 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;
+ }
+
+ return 0;
+}
+
+/** Configures the gpio as
+ * 0 - floating
+ * 1 - pull down
+ * 2 - pull up
+ */
+static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
+ int value)
+{
+ int shift;
+
+ switch (offset) {
+ case RT5677_GPIO1 ... RT5677_GPIO2:
+ shift = 2 * (1 - offset);
+ regmap_update_bits(rt5677->regmap,
+ RT5677_PR_BASE + RT5677_DIG_IN_PIN_ST_CTRL2,
+ 0x3 << shift,
+ (value & 0x3) << shift);
+ break;
+
+ case RT5677_GPIO3 ... RT5677_GPIO6:
+ shift = 2 * (9 - offset);
+ regmap_update_bits(rt5677->regmap,
+ RT5677_PR_BASE + RT5677_DIG_IN_PIN_ST_CTRL3,
+ 0x3 << shift,
+ (value & 0x3) << shift);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+ struct regmap_irq_chip_data *data = rt5677->irq_data;
+ 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;
+ }
+ }
+
+ return regmap_irq_get_virq(data, irq);
+}
+
+static struct gpio_chip rt5677_template_chip = {
+ .label = "rt5677",
+ .owner = THIS_MODULE,
+ .direction_output = rt5677_gpio_direction_out,
+ .set = rt5677_gpio_set,
+ .direction_input = rt5677_gpio_direction_in,
+ .get = rt5677_gpio_get,
+ .to_irq = rt5677_to_irq,
+ .can_sleep = 1,
+};
+
+static void rt5677_init_gpio(struct i2c_client *i2c)
+{
+ struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+ int ret;
+
+ rt5677->gpio_chip = rt5677_template_chip;
+ rt5677->gpio_chip.ngpio = RT5677_GPIO_NUM;
+ rt5677->gpio_chip.dev = &i2c->dev;
+ rt5677->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&rt5677->gpio_chip);
+ if (ret != 0)
+ dev_err(&i2c->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
+static void rt5677_free_gpio(struct i2c_client *i2c)
+{
+ struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+
+ gpiochip_remove(&rt5677->gpio_chip);
+}
+#else
+static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
+ int value)
+{
+}
+
+static void rt5677_init_gpio(struct i2c_client *i2c)
+{
+}
+
+static void rt5677_free_gpio(struct i2c_client *i2c)
+{
+}
+#endif
+
+static int rt5677_probe(struct snd_soc_codec *codec)
+{
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ rt5677->codec = codec;
+
+ if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) {
+ snd_soc_dapm_add_routes(&codec->dapm,
+ rt5677_dmic2_clk_2,
+ ARRAY_SIZE(rt5677_dmic2_clk_2));
+ } else { /*use dmic1 clock by default*/
+ snd_soc_dapm_add_routes(&codec->dapm,
+ rt5677_dmic2_clk_1,
+ ARRAY_SIZE(rt5677_dmic2_clk_1));
+ }
+
+ rt5677_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
+ regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
+
+ 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)
+{
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+ regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+ if (gpio_is_valid(rt5677->pow_ldo2))
+ gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt5677_suspend(struct snd_soc_codec *codec)
+{
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+ 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);
+ }
+
+ return 0;
+}
+
+static int rt5677_resume(struct snd_soc_codec *codec)
+{
+ struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+ if (!rt5677->dsp_vad_en) {
+ if (gpio_is_valid(rt5677->pow_ldo2)) {
+ gpio_set_value_cansleep(rt5677->pow_ldo2, 1);
+ msleep(10);
+ }
+
+ regcache_cache_only(rt5677->regmap, false);
+ regcache_sync(rt5677->regmap);
+ }
+
+ return 0;
+}
+#else
+#define rt5677_suspend NULL
+#define rt5677_resume NULL
+#endif
+
+static int rt5677_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct i2c_client *client = context;
+ struct rt5677_priv *rt5677 = i2c_get_clientdata(client);
+
+ if (rt5677->is_dsp_mode) {
+ if (reg > 0xff) {
+ mutex_lock(&rt5677->dsp_pri_lock);
+ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX,
+ reg & 0xff);
+ rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val);
+ mutex_unlock(&rt5677->dsp_pri_lock);
+ } else {
+ rt5677_dsp_mode_i2c_read(rt5677, reg, val);
+ }
+ } else {
+ regmap_read(rt5677->regmap_physical, reg, val);
+ }
+
+ return 0;
+}
+
+static int rt5677_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct i2c_client *client = context;
+ struct rt5677_priv *rt5677 = i2c_get_clientdata(client);
+
+ if (rt5677->is_dsp_mode) {
+ if (reg > 0xff) {
+ mutex_lock(&rt5677->dsp_pri_lock);
+ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX,
+ reg & 0xff);
+ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA,
+ val);
+ mutex_unlock(&rt5677->dsp_pri_lock);
+ } else {
+ rt5677_dsp_mode_i2c_write(rt5677, reg, val);
+ }
+ } else {
+ regmap_write(rt5677->regmap_physical, reg, val);
+ }
+
+ return 0;
+}
+
+#define RT5677_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5677_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static struct snd_soc_dai_ops rt5677_aif_dai_ops = {
+ .hw_params = rt5677_hw_params,
+ .set_fmt = rt5677_set_dai_fmt,
+ .set_sysclk = rt5677_set_dai_sysclk,
+ .set_pll = rt5677_set_dai_pll,
+ .set_tdm_slot = rt5677_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt5677_dai[] = {
+ {
+ .name = "rt5677-aif1",
+ .id = RT5677_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5677_STEREO_RATES,
+ .formats = RT5677_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5677_STEREO_RATES,
+ .formats = RT5677_FORMATS,
+ },
+ .ops = &rt5677_aif_dai_ops,
+ },
+ {
+ .name = "rt5677-aif2",
+ .id = RT5677_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5677_STEREO_RATES,
+ .formats = RT5677_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5677_STEREO_RATES,
+ .formats = RT5677_FORMATS,
+ },
+ .ops = &rt5677_aif_dai_ops,
+ },
+ {
+ .name = "rt5677-aif3",
+ .id = RT5677_AIF3,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5677_STEREO_RATES,
+ .formats = RT5677_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5677_STEREO_RATES,
+ .formats = RT5677_FORMATS,
+ },
+ .ops = &rt5677_aif_dai_ops,
+ },
+ {
+ .name = "rt5677-aif4",
+ .id = RT5677_AIF4,
+ .playback = {
+ .stream_name = "AIF4 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5677_STEREO_RATES,
+ .formats = RT5677_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5677_STEREO_RATES,
+ .formats = RT5677_FORMATS,
+ },
+ .ops = &rt5677_aif_dai_ops,
+ },
+ {
+ .name = "rt5677-slimbus",
+ .id = RT5677_AIF5,
+ .playback = {
+ .stream_name = "SLIMBus Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5677_STEREO_RATES,
+ .formats = RT5677_FORMATS,
+ },
+ .capture = {
+ .stream_name = "SLIMBus Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5677_STEREO_RATES,
+ .formats = RT5677_FORMATS,
+ },
+ .ops = &rt5677_aif_dai_ops,
+ },
+};
+
+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 regmap_config rt5677_regmap_physical = {
+ .name = "physical",
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = RT5677_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5677_ranges) *
+ RT5677_PR_SPACING),
+ .readable_reg = rt5677_readable_register,
+
+ .cache_type = REGCACHE_NONE,
+ .ranges = rt5677_ranges,
+ .num_ranges = ARRAY_SIZE(rt5677_ranges),
+};
+
+static const struct regmap_config rt5677_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = RT5677_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5677_ranges) *
+ RT5677_PR_SPACING),
+
+ .volatile_reg = rt5677_volatile_register,
+ .readable_reg = rt5677_readable_register,
+ .reg_read = rt5677_read,
+ .reg_write = rt5677_write,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt5677_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5677_reg),
+ .ranges = rt5677_ranges,
+ .num_ranges = ARRAY_SIZE(rt5677_ranges),
+};
+
+static const struct i2c_device_id rt5677_i2c_id[] = {
+ { "rt5677", RT5677 },
+ { "rt5676", RT5676 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
+
+static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np)
+{
+ 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);
+
+ /*
+ * 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;
+
+ of_property_read_u8_array(np, "realtek,gpio-config",
+ rt5677->pdata.gpio_config, RT5677_GPIO_NUM);
+
+ 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);
+
+ return 0;
+}
+
+static struct regmap_irq rt5677_irqs[] = {
+ [RT5677_IRQ_JD1] = {
+ .reg_offset = 0,
+ .mask = RT5677_EN_IRQ_GPIO_JD1,
+ },
+ [RT5677_IRQ_JD2] = {
+ .reg_offset = 0,
+ .mask = RT5677_EN_IRQ_GPIO_JD2,
+ },
+ [RT5677_IRQ_JD3] = {
+ .reg_offset = 0,
+ .mask = RT5677_EN_IRQ_GPIO_JD3,
+ },
+};
+
+static struct regmap_irq_chip rt5677_irq_chip = {
+ .name = "rt5677",
+ .irqs = rt5677_irqs,
+ .num_irqs = ARRAY_SIZE(rt5677_irqs),
+
+ .num_regs = 1,
+ .status_base = RT5677_IRQ_CTRL1,
+ .mask_base = RT5677_IRQ_CTRL1,
+ .mask_invert = 1,
+};
+
+static int rt5677_init_irq(struct i2c_client *i2c)
+{
+ int ret;
+ struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+
+ if (!rt5677->pdata.jd1_gpio &&
+ !rt5677->pdata.jd2_gpio &&
+ !rt5677->pdata.jd3_gpio)
+ return 0;
+
+ if (!i2c->irq) {
+ dev_err(&i2c->dev, "No interrupt specified\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_add_irq_chip(rt5677->regmap, i2c->irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
+ &rt5677_irq_chip, &rt5677->irq_data);
+
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register IRQ chip: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rt5677_free_irq(struct i2c_client *i2c)
+{
+ struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+
+ if (rt5677->irq_data)
+ regmap_del_irq_chip(i2c->irq, rt5677->irq_data);
+}
+
+static int rt5677_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt5677_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5677_priv *rt5677;
+ int ret;
+ unsigned int val;
+
+ rt5677 = devm_kzalloc(&i2c->dev, sizeof(struct rt5677_priv),
+ GFP_KERNEL);
+ if (rt5677 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5677);
+
+ rt5677->type = id->driver_data;
+
+ if (pdata)
+ rt5677->pdata = *pdata;
+
+ 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;
+ }
+
+ 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;
+ }
+ /* Wait a while until I2C bus becomes available. The datasheet
+ * does not specify the exact we should wait but startup
+ * sequence mentiones at least a few milliseconds.
+ */
+ msleep(10);
+ }
+
+ rt5677->regmap_physical = devm_regmap_init_i2c(i2c,
+ &rt5677_regmap_physical);
+ if (IS_ERR(rt5677->regmap_physical)) {
+ ret = PTR_ERR(rt5677->regmap_physical);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ rt5677->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5677_regmap);
+ if (IS_ERR(rt5677->regmap)) {
+ ret = PTR_ERR(rt5677->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt5677->regmap, RT5677_VENDOR_ID2, &val);
+ if (val != RT5677_DEVICE_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt5677\n", val);
+ return -ENODEV;
+ }
+
+ regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+
+ ret = regmap_register_patch(rt5677->regmap, init_list,
+ ARRAY_SIZE(init_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ if (rt5677->pdata.in1_diff)
+ regmap_update_bits(rt5677->regmap, RT5677_IN1,
+ RT5677_IN_DF1, RT5677_IN_DF1);
+
+ if (rt5677->pdata.in2_diff)
+ regmap_update_bits(rt5677->regmap, RT5677_IN1,
+ RT5677_IN_DF2, RT5677_IN_DF2);
+
+ if (rt5677->pdata.lout1_diff)
+ regmap_update_bits(rt5677->regmap, RT5677_LOUT1,
+ RT5677_LOUT1_L_DF, RT5677_LOUT1_L_DF);
+
+ if (rt5677->pdata.lout2_diff)
+ regmap_update_bits(rt5677->regmap, RT5677_LOUT1,
+ RT5677_LOUT2_L_DF, RT5677_LOUT2_L_DF);
+
+ if (rt5677->pdata.lout3_diff)
+ regmap_update_bits(rt5677->regmap, RT5677_LOUT1,
+ RT5677_LOUT3_L_DF, RT5677_LOUT3_L_DF);
+
+ if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) {
+ regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL2,
+ RT5677_GPIO5_FUNC_MASK,
+ RT5677_GPIO5_FUNC_DMIC);
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
+ RT5677_GPIO5_DIR_MASK,
+ RT5677_GPIO5_DIR_OUT);
+ }
+
+ if (rt5677->pdata.micbias1_vdd_3v3)
+ regmap_update_bits(rt5677->regmap, RT5677_MICBIAS,
+ RT5677_MICBIAS1_CTRL_VDD_MASK,
+ RT5677_MICBIAS1_CTRL_VDD_3_3V);
+
+ rt5677_init_gpio(i2c);
+ rt5677_init_irq(i2c);
+
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677,
+ rt5677_dai, ARRAY_SIZE(rt5677_dai));
+}
+
+static int 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,
+ },
+ .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");
diff --git a/kernel/sound/soc/codecs/rt5677.h b/kernel/sound/soc/codecs/rt5677.h
new file mode 100644
index 000000000..9dceb41d1
--- /dev/null
+++ b/kernel/sound/soc/codecs/rt5677.h
@@ -0,0 +1,1778 @@
+/*
+ * 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>
+
+/* Info */
+#define RT5677_RESET 0x00
+#define RT5677_VENDOR_ID 0xfd
+#define RT5677_VENDOR_ID1 0xfe
+#define RT5677_VENDOR_ID2 0xff
+/* I/O - Output */
+#define RT5677_LOUT1 0x01
+/* I/O - Input */
+#define RT5677_IN1 0x03
+#define RT5677_MICBIAS 0x04
+/* I/O - SLIMBus */
+#define RT5677_SLIMBUS_PARAM 0x07
+#define RT5677_SLIMBUS_RX 0x08
+#define RT5677_SLIMBUS_CTRL 0x09
+/* I/O */
+#define RT5677_SIDETONE_CTRL 0x13
+/* I/O - ADC/DAC */
+#define RT5677_ANA_DAC1_2_3_SRC 0x15
+#define RT5677_IF_DSP_DAC3_4_MIXER 0x16
+#define RT5677_DAC4_DIG_VOL 0x17
+#define RT5677_DAC3_DIG_VOL 0x18
+#define RT5677_DAC1_DIG_VOL 0x19
+#define RT5677_DAC2_DIG_VOL 0x1a
+#define RT5677_IF_DSP_DAC2_MIXER 0x1b
+#define RT5677_STO1_ADC_DIG_VOL 0x1c
+#define RT5677_MONO_ADC_DIG_VOL 0x1d
+#define RT5677_STO1_2_ADC_BST 0x1e
+#define RT5677_STO2_ADC_DIG_VOL 0x1f
+/* Mixer - D-D */
+#define RT5677_ADC_BST_CTRL2 0x20
+#define RT5677_STO3_4_ADC_BST 0x21
+#define RT5677_STO3_ADC_DIG_VOL 0x22
+#define RT5677_STO4_ADC_DIG_VOL 0x23
+#define RT5677_STO4_ADC_MIXER 0x24
+#define RT5677_STO3_ADC_MIXER 0x25
+#define RT5677_STO2_ADC_MIXER 0x26
+#define RT5677_STO1_ADC_MIXER 0x27
+#define RT5677_MONO_ADC_MIXER 0x28
+#define RT5677_ADC_IF_DSP_DAC1_MIXER 0x29
+#define RT5677_STO1_DAC_MIXER 0x2a
+#define RT5677_MONO_DAC_MIXER 0x2b
+#define RT5677_DD1_MIXER 0x2c
+#define RT5677_DD2_MIXER 0x2d
+#define RT5677_IF3_DATA 0x2f
+#define RT5677_IF4_DATA 0x30
+/* Mixer - PDM */
+#define RT5677_PDM_OUT_CTRL 0x31
+#define RT5677_PDM_DATA_CTRL1 0x32
+#define RT5677_PDM_DATA_CTRL2 0x33
+#define RT5677_PDM1_DATA_CTRL2 0x34
+#define RT5677_PDM1_DATA_CTRL3 0x35
+#define RT5677_PDM1_DATA_CTRL4 0x36
+#define RT5677_PDM2_DATA_CTRL2 0x37
+#define RT5677_PDM2_DATA_CTRL3 0x38
+#define RT5677_PDM2_DATA_CTRL4 0x39
+/* TDM */
+#define RT5677_TDM1_CTRL1 0x3b
+#define RT5677_TDM1_CTRL2 0x3c
+#define RT5677_TDM1_CTRL3 0x3d
+#define RT5677_TDM1_CTRL4 0x3e
+#define RT5677_TDM1_CTRL5 0x3f
+#define RT5677_TDM2_CTRL1 0x40
+#define RT5677_TDM2_CTRL2 0x41
+#define RT5677_TDM2_CTRL3 0x42
+#define RT5677_TDM2_CTRL4 0x43
+#define RT5677_TDM2_CTRL5 0x44
+/* I2C_MASTER_CTRL */
+#define RT5677_I2C_MASTER_CTRL1 0x47
+#define RT5677_I2C_MASTER_CTRL2 0x48
+#define RT5677_I2C_MASTER_CTRL3 0x49
+#define RT5677_I2C_MASTER_CTRL4 0x4a
+#define RT5677_I2C_MASTER_CTRL5 0x4b
+#define RT5677_I2C_MASTER_CTRL6 0x4c
+#define RT5677_I2C_MASTER_CTRL7 0x4d
+#define RT5677_I2C_MASTER_CTRL8 0x4e
+/* DMIC */
+#define RT5677_DMIC_CTRL1 0x50
+#define RT5677_DMIC_CTRL2 0x51
+/* Haptic Generator */
+#define RT5677_HAP_GENE_CTRL1 0x56
+#define RT5677_HAP_GENE_CTRL2 0x57
+#define RT5677_HAP_GENE_CTRL3 0x58
+#define RT5677_HAP_GENE_CTRL4 0x59
+#define RT5677_HAP_GENE_CTRL5 0x5a
+#define RT5677_HAP_GENE_CTRL6 0x5b
+#define RT5677_HAP_GENE_CTRL7 0x5c
+#define RT5677_HAP_GENE_CTRL8 0x5d
+#define RT5677_HAP_GENE_CTRL9 0x5e
+#define RT5677_HAP_GENE_CTRL10 0x5f
+/* Power */
+#define RT5677_PWR_DIG1 0x61
+#define RT5677_PWR_DIG2 0x62
+#define RT5677_PWR_ANLG1 0x63
+#define RT5677_PWR_ANLG2 0x64
+#define RT5677_PWR_DSP1 0x65
+#define RT5677_PWR_DSP_ST 0x66
+#define RT5677_PWR_DSP2 0x67
+#define RT5677_ADC_DAC_HPF_CTRL1 0x68
+/* Private Register Control */
+#define RT5677_PRIV_INDEX 0x6a
+#define RT5677_PRIV_DATA 0x6c
+/* Format - ADC/DAC */
+#define RT5677_I2S4_SDP 0x6f
+#define RT5677_I2S1_SDP 0x70
+#define RT5677_I2S2_SDP 0x71
+#define RT5677_I2S3_SDP 0x72
+#define RT5677_CLK_TREE_CTRL1 0x73
+#define RT5677_CLK_TREE_CTRL2 0x74
+#define RT5677_CLK_TREE_CTRL3 0x75
+/* Function - Analog */
+#define RT5677_PLL1_CTRL1 0x7a
+#define RT5677_PLL1_CTRL2 0x7b
+#define RT5677_PLL2_CTRL1 0x7c
+#define RT5677_PLL2_CTRL2 0x7d
+#define RT5677_GLB_CLK1 0x80
+#define RT5677_GLB_CLK2 0x81
+#define RT5677_ASRC_1 0x83
+#define RT5677_ASRC_2 0x84
+#define RT5677_ASRC_3 0x85
+#define RT5677_ASRC_4 0x86
+#define RT5677_ASRC_5 0x87
+#define RT5677_ASRC_6 0x88
+#define RT5677_ASRC_7 0x89
+#define RT5677_ASRC_8 0x8a
+#define RT5677_ASRC_9 0x8b
+#define RT5677_ASRC_10 0x8c
+#define RT5677_ASRC_11 0x8d
+#define RT5677_ASRC_12 0x8e
+#define RT5677_ASRC_13 0x8f
+#define RT5677_ASRC_14 0x90
+#define RT5677_ASRC_15 0x91
+#define RT5677_ASRC_16 0x92
+#define RT5677_ASRC_17 0x93
+#define RT5677_ASRC_18 0x94
+#define RT5677_ASRC_19 0x95
+#define RT5677_ASRC_20 0x97
+#define RT5677_ASRC_21 0x98
+#define RT5677_ASRC_22 0x99
+#define RT5677_ASRC_23 0x9a
+#define RT5677_VAD_CTRL1 0x9c
+#define RT5677_VAD_CTRL2 0x9d
+#define RT5677_VAD_CTRL3 0x9e
+#define RT5677_VAD_CTRL4 0x9f
+#define RT5677_VAD_CTRL5 0xa0
+/* Function - Digital */
+#define RT5677_DSP_INB_CTRL1 0xa3
+#define RT5677_DSP_INB_CTRL2 0xa4
+#define RT5677_DSP_IN_OUTB_CTRL 0xa5
+#define RT5677_DSP_OUTB0_1_DIG_VOL 0xa6
+#define RT5677_DSP_OUTB2_3_DIG_VOL 0xa7
+#define RT5677_DSP_OUTB4_5_DIG_VOL 0xa8
+#define RT5677_DSP_OUTB6_7_DIG_VOL 0xa9
+#define RT5677_ADC_EQ_CTRL1 0xae
+#define RT5677_ADC_EQ_CTRL2 0xaf
+#define RT5677_EQ_CTRL1 0xb0
+#define RT5677_EQ_CTRL2 0xb1
+#define RT5677_EQ_CTRL3 0xb2
+#define RT5677_SOFT_VOL_ZERO_CROSS1 0xb3
+#define RT5677_JD_CTRL1 0xb5
+#define RT5677_JD_CTRL2 0xb6
+#define RT5677_JD_CTRL3 0xb8
+#define RT5677_IRQ_CTRL1 0xbd
+#define RT5677_IRQ_CTRL2 0xbe
+#define RT5677_GPIO_ST 0xbf
+#define RT5677_GPIO_CTRL1 0xc0
+#define RT5677_GPIO_CTRL2 0xc1
+#define RT5677_GPIO_CTRL3 0xc2
+#define RT5677_STO1_ADC_HI_FILTER1 0xc5
+#define RT5677_STO1_ADC_HI_FILTER2 0xc6
+#define RT5677_MONO_ADC_HI_FILTER1 0xc7
+#define RT5677_MONO_ADC_HI_FILTER2 0xc8
+#define RT5677_STO2_ADC_HI_FILTER1 0xc9
+#define RT5677_STO2_ADC_HI_FILTER2 0xca
+#define RT5677_STO3_ADC_HI_FILTER1 0xcb
+#define RT5677_STO3_ADC_HI_FILTER2 0xcc
+#define RT5677_STO4_ADC_HI_FILTER1 0xcd
+#define RT5677_STO4_ADC_HI_FILTER2 0xce
+#define RT5677_MB_DRC_CTRL1 0xd0
+#define RT5677_DRC1_CTRL1 0xd2
+#define RT5677_DRC1_CTRL2 0xd3
+#define RT5677_DRC1_CTRL3 0xd4
+#define RT5677_DRC1_CTRL4 0xd5
+#define RT5677_DRC1_CTRL5 0xd6
+#define RT5677_DRC1_CTRL6 0xd7
+#define RT5677_DRC2_CTRL1 0xd8
+#define RT5677_DRC2_CTRL2 0xd9
+#define RT5677_DRC2_CTRL3 0xda
+#define RT5677_DRC2_CTRL4 0xdb
+#define RT5677_DRC2_CTRL5 0xdc
+#define RT5677_DRC2_CTRL6 0xdd
+#define RT5677_DRC1_HL_CTRL1 0xde
+#define RT5677_DRC1_HL_CTRL2 0xdf
+#define RT5677_DRC2_HL_CTRL1 0xe0
+#define RT5677_DRC2_HL_CTRL2 0xe1
+#define RT5677_DSP_INB1_SRC_CTRL1 0xe3
+#define RT5677_DSP_INB1_SRC_CTRL2 0xe4
+#define RT5677_DSP_INB1_SRC_CTRL3 0xe5
+#define RT5677_DSP_INB1_SRC_CTRL4 0xe6
+#define RT5677_DSP_INB2_SRC_CTRL1 0xe7
+#define RT5677_DSP_INB2_SRC_CTRL2 0xe8
+#define RT5677_DSP_INB2_SRC_CTRL3 0xe9
+#define RT5677_DSP_INB2_SRC_CTRL4 0xea
+#define RT5677_DSP_INB3_SRC_CTRL1 0xeb
+#define RT5677_DSP_INB3_SRC_CTRL2 0xec
+#define RT5677_DSP_INB3_SRC_CTRL3 0xed
+#define RT5677_DSP_INB3_SRC_CTRL4 0xee
+#define RT5677_DSP_OUTB1_SRC_CTRL1 0xef
+#define RT5677_DSP_OUTB1_SRC_CTRL2 0xf0
+#define RT5677_DSP_OUTB1_SRC_CTRL3 0xf1
+#define RT5677_DSP_OUTB1_SRC_CTRL4 0xf2
+#define RT5677_DSP_OUTB2_SRC_CTRL1 0xf3
+#define RT5677_DSP_OUTB2_SRC_CTRL2 0xf4
+#define RT5677_DSP_OUTB2_SRC_CTRL3 0xf5
+#define RT5677_DSP_OUTB2_SRC_CTRL4 0xf6
+
+/* Virtual DSP Mixer Control */
+#define RT5677_DSP_OUTB_0123_MIXER_CTRL 0xf7
+#define RT5677_DSP_OUTB_45_MIXER_CTRL 0xf8
+#define RT5677_DSP_OUTB_67_MIXER_CTRL 0xf9
+
+/* General Control */
+#define RT5677_DIG_MISC 0xfa
+#define RT5677_GEN_CTRL1 0xfb
+#define RT5677_GEN_CTRL2 0xfc
+
+/* DSP Mode I2C Control*/
+#define RT5677_DSP_I2C_OP_CODE 0x00
+#define RT5677_DSP_I2C_ADDR_LSB 0x01
+#define RT5677_DSP_I2C_ADDR_MSB 0x02
+#define RT5677_DSP_I2C_DATA_LSB 0x03
+#define RT5677_DSP_I2C_DATA_MSB 0x04
+
+/* Index of Codec Private Register definition */
+#define RT5677_PR_DRC1_CTRL_1 0x01
+#define RT5677_PR_DRC1_CTRL_2 0x02
+#define RT5677_PR_DRC1_CTRL_3 0x03
+#define RT5677_PR_DRC1_CTRL_4 0x04
+#define RT5677_PR_DRC1_CTRL_5 0x05
+#define RT5677_PR_DRC1_CTRL_6 0x06
+#define RT5677_PR_DRC1_CTRL_7 0x07
+#define RT5677_PR_DRC2_CTRL_1 0x08
+#define RT5677_PR_DRC2_CTRL_2 0x09
+#define RT5677_PR_DRC2_CTRL_3 0x0a
+#define RT5677_PR_DRC2_CTRL_4 0x0b
+#define RT5677_PR_DRC2_CTRL_5 0x0c
+#define RT5677_PR_DRC2_CTRL_6 0x0d
+#define RT5677_PR_DRC2_CTRL_7 0x0e
+#define RT5677_BIAS_CUR1 0x10
+#define RT5677_BIAS_CUR2 0x12
+#define RT5677_BIAS_CUR3 0x13
+#define RT5677_BIAS_CUR4 0x14
+#define RT5677_BIAS_CUR5 0x15
+#define RT5677_VREF_LOUT_CTRL 0x17
+#define RT5677_DIG_VOL_CTRL1 0x1a
+#define RT5677_DIG_VOL_CTRL2 0x1b
+#define RT5677_ANA_ADC_GAIN_CTRL 0x1e
+#define RT5677_VAD_SRAM_TEST1 0x20
+#define RT5677_VAD_SRAM_TEST2 0x21
+#define RT5677_VAD_SRAM_TEST3 0x22
+#define RT5677_VAD_SRAM_TEST4 0x23
+#define RT5677_PAD_DRV_CTRL 0x26
+#define RT5677_DIG_IN_PIN_ST_CTRL1 0x29
+#define RT5677_DIG_IN_PIN_ST_CTRL2 0x2a
+#define RT5677_DIG_IN_PIN_ST_CTRL3 0x2b
+#define RT5677_PLL1_INT 0x38
+#define RT5677_PLL2_INT 0x39
+#define RT5677_TEST_CTRL1 0x3a
+#define RT5677_TEST_CTRL2 0x3b
+#define RT5677_TEST_CTRL3 0x3c
+#define RT5677_CHOP_DAC_ADC 0x3d
+#define RT5677_SOFT_DEPOP_DAC_CLK_CTRL 0x3e
+#define RT5677_CROSS_OVER_FILTER1 0x90
+#define RT5677_CROSS_OVER_FILTER2 0x91
+#define RT5677_CROSS_OVER_FILTER3 0x92
+#define RT5677_CROSS_OVER_FILTER4 0x93
+#define RT5677_CROSS_OVER_FILTER5 0x94
+#define RT5677_CROSS_OVER_FILTER6 0x95
+#define RT5677_CROSS_OVER_FILTER7 0x96
+#define RT5677_CROSS_OVER_FILTER8 0x97
+#define RT5677_CROSS_OVER_FILTER9 0x98
+#define RT5677_CROSS_OVER_FILTER10 0x99
+
+/* global definition */
+#define RT5677_L_MUTE (0x1 << 15)
+#define RT5677_L_MUTE_SFT 15
+#define RT5677_VOL_L_MUTE (0x1 << 14)
+#define RT5677_VOL_L_SFT 14
+#define RT5677_R_MUTE (0x1 << 7)
+#define RT5677_R_MUTE_SFT 7
+#define RT5677_VOL_R_MUTE (0x1 << 6)
+#define RT5677_VOL_R_SFT 6
+#define RT5677_L_VOL_MASK (0x7f << 9)
+#define RT5677_L_VOL_SFT 9
+#define RT5677_R_VOL_MASK (0x7f << 1)
+#define RT5677_R_VOL_SFT 1
+
+/* LOUT1 Control (0x01) */
+#define RT5677_LOUT1_L_MUTE (0x1 << 15)
+#define RT5677_LOUT1_L_MUTE_SFT (15)
+#define RT5677_LOUT1_L_DF (0x1 << 14)
+#define RT5677_LOUT1_L_DF_SFT (14)
+#define RT5677_LOUT2_L_MUTE (0x1 << 13)
+#define RT5677_LOUT2_L_MUTE_SFT (13)
+#define RT5677_LOUT2_L_DF (0x1 << 12)
+#define RT5677_LOUT2_L_DF_SFT (12)
+#define RT5677_LOUT3_L_MUTE (0x1 << 11)
+#define RT5677_LOUT3_L_MUTE_SFT (11)
+#define RT5677_LOUT3_L_DF (0x1 << 10)
+#define RT5677_LOUT3_L_DF_SFT (10)
+#define RT5677_LOUT1_ENH_DRV (0x1 << 9)
+#define RT5677_LOUT1_ENH_DRV_SFT (9)
+#define RT5677_LOUT2_ENH_DRV (0x1 << 8)
+#define RT5677_LOUT2_ENH_DRV_SFT (8)
+#define RT5677_LOUT3_ENH_DRV (0x1 << 7)
+#define RT5677_LOUT3_ENH_DRV_SFT (7)
+
+/* IN1 Control (0x03) */
+#define RT5677_BST_MASK1 (0xf << 12)
+#define RT5677_BST_SFT1 12
+#define RT5677_BST_MASK2 (0xf << 8)
+#define RT5677_BST_SFT2 8
+#define RT5677_IN_DF1 (0x1 << 7)
+#define RT5677_IN_DF1_SFT 7
+#define RT5677_IN_DF2 (0x1 << 6)
+#define RT5677_IN_DF2_SFT 6
+
+/* Micbias Control (0x04) */
+#define RT5677_MICBIAS1_OUTVOLT_MASK (0x1 << 15)
+#define RT5677_MICBIAS1_OUTVOLT_SFT (15)
+#define RT5677_MICBIAS1_OUTVOLT_2_7V (0x0 << 15)
+#define RT5677_MICBIAS1_OUTVOLT_2_25V (0x1 << 15)
+#define RT5677_MICBIAS1_CTRL_VDD_MASK (0x1 << 14)
+#define RT5677_MICBIAS1_CTRL_VDD_SFT (14)
+#define RT5677_MICBIAS1_CTRL_VDD_1_8V (0x0 << 14)
+#define RT5677_MICBIAS1_CTRL_VDD_3_3V (0x1 << 14)
+#define RT5677_MICBIAS1_OVCD_MASK (0x1 << 11)
+#define RT5677_MICBIAS1_OVCD_SHIFT (11)
+#define RT5677_MICBIAS1_OVCD_DIS (0x0 << 11)
+#define RT5677_MICBIAS1_OVCD_EN (0x1 << 11)
+#define RT5677_MICBIAS1_OVTH_MASK (0x3 << 9)
+#define RT5677_MICBIAS1_OVTH_SFT 9
+#define RT5677_MICBIAS1_OVTH_640UA (0x0 << 9)
+#define RT5677_MICBIAS1_OVTH_1280UA (0x1 << 9)
+#define RT5677_MICBIAS1_OVTH_1920UA (0x2 << 9)
+
+/* SLIMbus Parameter (0x07) */
+
+/* SLIMbus Rx (0x08) */
+#define RT5677_SLB_ADC4_MASK (0x3 << 6)
+#define RT5677_SLB_ADC4_SFT 6
+#define RT5677_SLB_ADC3_MASK (0x3 << 4)
+#define RT5677_SLB_ADC3_SFT 4
+#define RT5677_SLB_ADC2_MASK (0x3 << 2)
+#define RT5677_SLB_ADC2_SFT 2
+#define RT5677_SLB_ADC1_MASK (0x3 << 0)
+#define RT5677_SLB_ADC1_SFT 0
+
+/* SLIMBus control (0x09) */
+
+/* Sidetone Control (0x13) */
+#define RT5677_ST_HPF_SEL_MASK (0x7 << 13)
+#define RT5677_ST_HPF_SEL_SFT 13
+#define RT5677_ST_HPF_PATH (0x1 << 12)
+#define RT5677_ST_HPF_PATH_SFT 12
+#define RT5677_ST_SEL_MASK (0x7 << 9)
+#define RT5677_ST_SEL_SFT 9
+#define RT5677_ST_EN (0x1 << 6)
+#define RT5677_ST_EN_SFT 6
+#define RT5677_ST_GAIN (0x1 << 5)
+#define RT5677_ST_GAIN_SFT 5
+#define RT5677_ST_VOL_MASK (0x1f << 0)
+#define RT5677_ST_VOL_SFT 0
+
+/* Analog DAC1/2/3 Source Control (0x15) */
+#define RT5677_ANA_DAC3_SRC_SEL_MASK (0x3 << 4)
+#define RT5677_ANA_DAC3_SRC_SEL_SFT 4
+#define RT5677_ANA_DAC1_2_SRC_SEL_MASK (0x3 << 0)
+#define RT5677_ANA_DAC1_2_SRC_SEL_SFT 0
+
+/* IF/DSP to DAC3/4 Mixer Control (0x16) */
+#define RT5677_M_DAC4_L_VOL (0x1 << 15)
+#define RT5677_M_DAC4_L_VOL_SFT 15
+#define RT5677_SEL_DAC4_L_SRC_MASK (0x7 << 12)
+#define RT5677_SEL_DAC4_L_SRC_SFT 12
+#define RT5677_M_DAC4_R_VOL (0x1 << 11)
+#define RT5677_M_DAC4_R_VOL_SFT 11
+#define RT5677_SEL_DAC4_R_SRC_MASK (0x7 << 8)
+#define RT5677_SEL_DAC4_R_SRC_SFT 8
+#define RT5677_M_DAC3_L_VOL (0x1 << 7)
+#define RT5677_M_DAC3_L_VOL_SFT 7
+#define RT5677_SEL_DAC3_L_SRC_MASK (0x7 << 4)
+#define RT5677_SEL_DAC3_L_SRC_SFT 4
+#define RT5677_M_DAC3_R_VOL (0x1 << 3)
+#define RT5677_M_DAC3_R_VOL_SFT 3
+#define RT5677_SEL_DAC3_R_SRC_MASK (0x7 << 0)
+#define RT5677_SEL_DAC3_R_SRC_SFT 0
+
+/* DAC4 Digital Volume (0x17) */
+#define RT5677_DAC4_L_VOL_MASK (0xff << 8)
+#define RT5677_DAC4_L_VOL_SFT 8
+#define RT5677_DAC4_R_VOL_MASK (0xff)
+#define RT5677_DAC4_R_VOL_SFT 0
+
+/* DAC3 Digital Volume (0x18) */
+#define RT5677_DAC3_L_VOL_MASK (0xff << 8)
+#define RT5677_DAC3_L_VOL_SFT 8
+#define RT5677_DAC3_R_VOL_MASK (0xff)
+#define RT5677_DAC3_R_VOL_SFT 0
+
+/* DAC3 Digital Volume (0x19) */
+#define RT5677_DAC1_L_VOL_MASK (0xff << 8)
+#define RT5677_DAC1_L_VOL_SFT 8
+#define RT5677_DAC1_R_VOL_MASK (0xff)
+#define RT5677_DAC1_R_VOL_SFT 0
+
+/* DAC2 Digital Volume (0x1a) */
+#define RT5677_DAC2_L_VOL_MASK (0xff << 8)
+#define RT5677_DAC2_L_VOL_SFT 8
+#define RT5677_DAC2_R_VOL_MASK (0xff)
+#define RT5677_DAC2_R_VOL_SFT 0
+
+/* IF/DSP to DAC2 Mixer Control (0x1b) */
+#define RT5677_M_DAC2_L_VOL (0x1 << 7)
+#define RT5677_M_DAC2_L_VOL_SFT 7
+#define RT5677_SEL_DAC2_L_SRC_MASK (0x7 << 4)
+#define RT5677_SEL_DAC2_L_SRC_SFT 4
+#define RT5677_M_DAC2_R_VOL (0x1 << 3)
+#define RT5677_M_DAC2_R_VOL_SFT 3
+#define RT5677_SEL_DAC2_R_SRC_MASK (0x7 << 0)
+#define RT5677_SEL_DAC2_R_SRC_SFT 0
+
+/* Stereo1 ADC Digital Volume Control (0x1c) */
+#define RT5677_STO1_ADC_L_VOL_MASK (0x3f << 9)
+#define RT5677_STO1_ADC_L_VOL_SFT 9
+#define RT5677_STO1_ADC_R_VOL_MASK (0x3f << 1)
+#define RT5677_STO1_ADC_R_VOL_SFT 1
+
+/* Mono ADC Digital Volume Control (0x1d) */
+#define RT5677_MONO_ADC_L_VOL_MASK (0x3f << 9)
+#define RT5677_MONO_ADC_L_VOL_SFT 9
+#define RT5677_MONO_ADC_R_VOL_MASK (0x3f << 1)
+#define RT5677_MONO_ADC_R_VOL_SFT 1
+
+/* Stereo 1/2 ADC Boost Gain Control (0x1e) */
+#define RT5677_STO1_ADC_L_BST_MASK (0x3 << 14)
+#define RT5677_STO1_ADC_L_BST_SFT 14
+#define RT5677_STO1_ADC_R_BST_MASK (0x3 << 12)
+#define RT5677_STO1_ADC_R_BST_SFT 12
+#define RT5677_STO1_ADC_COMP_MASK (0x3 << 10)
+#define RT5677_STO1_ADC_COMP_SFT 10
+#define RT5677_STO2_ADC_L_BST_MASK (0x3 << 8)
+#define RT5677_STO2_ADC_L_BST_SFT 8
+#define RT5677_STO2_ADC_R_BST_MASK (0x3 << 6)
+#define RT5677_STO2_ADC_R_BST_SFT 6
+#define RT5677_STO2_ADC_COMP_MASK (0x3 << 4)
+#define RT5677_STO2_ADC_COMP_SFT 4
+
+/* Stereo2 ADC Digital Volume Control (0x1f) */
+#define RT5677_STO2_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5677_STO2_ADC_L_VOL_SFT 8
+#define RT5677_STO2_ADC_R_VOL_MASK (0x7f)
+#define RT5677_STO2_ADC_R_VOL_SFT 0
+
+/* ADC Boost Gain Control 2 (0x20) */
+#define RT5677_MONO_ADC_L_BST_MASK (0x3 << 14)
+#define RT5677_MONO_ADC_L_BST_SFT 14
+#define RT5677_MONO_ADC_R_BST_MASK (0x3 << 12)
+#define RT5677_MONO_ADC_R_BST_SFT 12
+#define RT5677_MONO_ADC_COMP_MASK (0x3 << 10)
+#define RT5677_MONO_ADC_COMP_SFT 10
+
+/* Stereo 3/4 ADC Boost Gain Control (0x21) */
+#define RT5677_STO3_ADC_L_BST_MASK (0x3 << 14)
+#define RT5677_STO3_ADC_L_BST_SFT 14
+#define RT5677_STO3_ADC_R_BST_MASK (0x3 << 12)
+#define RT5677_STO3_ADC_R_BST_SFT 12
+#define RT5677_STO3_ADC_COMP_MASK (0x3 << 10)
+#define RT5677_STO3_ADC_COMP_SFT 10
+#define RT5677_STO4_ADC_L_BST_MASK (0x3 << 8)
+#define RT5677_STO4_ADC_L_BST_SFT 8
+#define RT5677_STO4_ADC_R_BST_MASK (0x3 << 6)
+#define RT5677_STO4_ADC_R_BST_SFT 6
+#define RT5677_STO4_ADC_COMP_MASK (0x3 << 4)
+#define RT5677_STO4_ADC_COMP_SFT 4
+
+/* Stereo3 ADC Digital Volume Control (0x22) */
+#define RT5677_STO3_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5677_STO3_ADC_L_VOL_SFT 8
+#define RT5677_STO3_ADC_R_VOL_MASK (0x7f)
+#define RT5677_STO3_ADC_R_VOL_SFT 0
+
+/* Stereo4 ADC Digital Volume Control (0x23) */
+#define RT5677_STO4_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5677_STO4_ADC_L_VOL_SFT 8
+#define RT5677_STO4_ADC_R_VOL_MASK (0x7f)
+#define RT5677_STO4_ADC_R_VOL_SFT 0
+
+/* Stereo4 ADC Mixer control (0x24) */
+#define RT5677_M_STO4_ADC_L2 (0x1 << 15)
+#define RT5677_M_STO4_ADC_L2_SFT 15
+#define RT5677_M_STO4_ADC_L1 (0x1 << 14)
+#define RT5677_M_STO4_ADC_L1_SFT 14
+#define RT5677_SEL_STO4_ADC1_MASK (0x3 << 12)
+#define RT5677_SEL_STO4_ADC1_SFT 12
+#define RT5677_SEL_STO4_ADC2_MASK (0x3 << 10)
+#define RT5677_SEL_STO4_ADC2_SFT 10
+#define RT5677_SEL_STO4_DMIC_MASK (0x3 << 8)
+#define RT5677_SEL_STO4_DMIC_SFT 8
+#define RT5677_M_STO4_ADC_R1 (0x1 << 7)
+#define RT5677_M_STO4_ADC_R1_SFT 7
+#define RT5677_M_STO4_ADC_R2 (0x1 << 6)
+#define RT5677_M_STO4_ADC_R2_SFT 6
+
+/* Stereo3 ADC Mixer control (0x25) */
+#define RT5677_M_STO3_ADC_L2 (0x1 << 15)
+#define RT5677_M_STO3_ADC_L2_SFT 15
+#define RT5677_M_STO3_ADC_L1 (0x1 << 14)
+#define RT5677_M_STO3_ADC_L1_SFT 14
+#define RT5677_SEL_STO3_ADC1_MASK (0x3 << 12)
+#define RT5677_SEL_STO3_ADC1_SFT 12
+#define RT5677_SEL_STO3_ADC2_MASK (0x3 << 10)
+#define RT5677_SEL_STO3_ADC2_SFT 10
+#define RT5677_SEL_STO3_DMIC_MASK (0x3 << 8)
+#define RT5677_SEL_STO3_DMIC_SFT 8
+#define RT5677_M_STO3_ADC_R1 (0x1 << 7)
+#define RT5677_M_STO3_ADC_R1_SFT 7
+#define RT5677_M_STO3_ADC_R2 (0x1 << 6)
+#define RT5677_M_STO3_ADC_R2_SFT 6
+
+/* Stereo2 ADC Mixer Control (0x26) */
+#define RT5677_M_STO2_ADC_L2 (0x1 << 15)
+#define RT5677_M_STO2_ADC_L2_SFT 15
+#define RT5677_M_STO2_ADC_L1 (0x1 << 14)
+#define RT5677_M_STO2_ADC_L1_SFT 14
+#define RT5677_SEL_STO2_ADC1_MASK (0x3 << 12)
+#define RT5677_SEL_STO2_ADC1_SFT 12
+#define RT5677_SEL_STO2_ADC2_MASK (0x3 << 10)
+#define RT5677_SEL_STO2_ADC2_SFT 10
+#define RT5677_SEL_STO2_DMIC_MASK (0x3 << 8)
+#define RT5677_SEL_STO2_DMIC_SFT 8
+#define RT5677_M_STO2_ADC_R1 (0x1 << 7)
+#define RT5677_M_STO2_ADC_R1_SFT 7
+#define RT5677_M_STO2_ADC_R2 (0x1 << 6)
+#define RT5677_M_STO2_ADC_R2_SFT 6
+#define RT5677_SEL_STO2_LR_MIX_MASK (0x1 << 0)
+#define RT5677_SEL_STO2_LR_MIX_SFT 0
+#define RT5677_SEL_STO2_LR_MIX_L (0x0 << 0)
+#define RT5677_SEL_STO2_LR_MIX_LR (0x1 << 0)
+
+/* Stereo1 ADC Mixer control (0x27) */
+#define RT5677_M_STO1_ADC_L2 (0x1 << 15)
+#define RT5677_M_STO1_ADC_L2_SFT 15
+#define RT5677_M_STO1_ADC_L1 (0x1 << 14)
+#define RT5677_M_STO1_ADC_L1_SFT 14
+#define RT5677_SEL_STO1_ADC1_MASK (0x3 << 12)
+#define RT5677_SEL_STO1_ADC1_SFT 12
+#define RT5677_SEL_STO1_ADC2_MASK (0x3 << 10)
+#define RT5677_SEL_STO1_ADC2_SFT 10
+#define RT5677_SEL_STO1_DMIC_MASK (0x3 << 8)
+#define RT5677_SEL_STO1_DMIC_SFT 8
+#define RT5677_M_STO1_ADC_R1 (0x1 << 7)
+#define RT5677_M_STO1_ADC_R1_SFT 7
+#define RT5677_M_STO1_ADC_R2 (0x1 << 6)
+#define RT5677_M_STO1_ADC_R2_SFT 6
+
+/* Mono ADC Mixer control (0x28) */
+#define RT5677_M_MONO_ADC_L2 (0x1 << 15)
+#define RT5677_M_MONO_ADC_L2_SFT 15
+#define RT5677_M_MONO_ADC_L1 (0x1 << 14)
+#define RT5677_M_MONO_ADC_L1_SFT 14
+#define RT5677_SEL_MONO_ADC_L1_MASK (0x3 << 12)
+#define RT5677_SEL_MONO_ADC_L1_SFT 12
+#define RT5677_SEL_MONO_ADC_L2_MASK (0x3 << 10)
+#define RT5677_SEL_MONO_ADC_L2_SFT 10
+#define RT5677_SEL_MONO_DMIC_L_MASK (0x3 << 8)
+#define RT5677_SEL_MONO_DMIC_L_SFT 8
+#define RT5677_M_MONO_ADC_R1 (0x1 << 7)
+#define RT5677_M_MONO_ADC_R1_SFT 7
+#define RT5677_M_MONO_ADC_R2 (0x1 << 6)
+#define RT5677_M_MONO_ADC_R2_SFT 6
+#define RT5677_SEL_MONO_ADC_R1_MASK (0x3 << 4)
+#define RT5677_SEL_MONO_ADC_R1_SFT 4
+#define RT5677_SEL_MONO_ADC_R2_MASK (0x3 << 2)
+#define RT5677_SEL_MONO_ADC_R2_SFT 2
+#define RT5677_SEL_MONO_DMIC_R_MASK (0x3 << 0)
+#define RT5677_SEL_MONO_DMIC_R_SFT 0
+
+/* ADC/IF/DSP to DAC1 Mixer control (0x29) */
+#define RT5677_M_ADDA_MIXER1_L (0x1 << 15)
+#define RT5677_M_ADDA_MIXER1_L_SFT 15
+#define RT5677_M_DAC1_L (0x1 << 14)
+#define RT5677_M_DAC1_L_SFT 14
+#define RT5677_DAC1_L_SEL_MASK (0x7 << 8)
+#define RT5677_DAC1_L_SEL_SFT 8
+#define RT5677_M_ADDA_MIXER1_R (0x1 << 7)
+#define RT5677_M_ADDA_MIXER1_R_SFT 7
+#define RT5677_M_DAC1_R (0x1 << 6)
+#define RT5677_M_DAC1_R_SFT 6
+#define RT5677_ADDA1_SEL_MASK (0x3 << 0)
+#define RT5677_ADDA1_SEL_SFT 0
+
+/* Stereo1 DAC Mixer L/R Control (0x2a) */
+#define RT5677_M_ST_DAC1_L (0x1 << 15)
+#define RT5677_M_ST_DAC1_L_SFT 15
+#define RT5677_M_DAC1_L_STO_L (0x1 << 13)
+#define RT5677_M_DAC1_L_STO_L_SFT 13
+#define RT5677_DAC1_L_STO_L_VOL_MASK (0x1 << 12)
+#define RT5677_DAC1_L_STO_L_VOL_SFT 12
+#define RT5677_M_DAC2_L_STO_L (0x1 << 11)
+#define RT5677_M_DAC2_L_STO_L_SFT 11
+#define RT5677_DAC2_L_STO_L_VOL_MASK (0x1 << 10)
+#define RT5677_DAC2_L_STO_L_VOL_SFT 10
+#define RT5677_M_DAC1_R_STO_L (0x1 << 9)
+#define RT5677_M_DAC1_R_STO_L_SFT 9
+#define RT5677_DAC1_R_STO_L_VOL_MASK (0x1 << 8)
+#define RT5677_DAC1_R_STO_L_VOL_SFT 8
+#define RT5677_M_ST_DAC1_R (0x1 << 7)
+#define RT5677_M_ST_DAC1_R_SFT 7
+#define RT5677_M_DAC1_R_STO_R (0x1 << 5)
+#define RT5677_M_DAC1_R_STO_R_SFT 5
+#define RT5677_DAC1_R_STO_R_VOL_MASK (0x1 << 4)
+#define RT5677_DAC1_R_STO_R_VOL_SFT 4
+#define RT5677_M_DAC2_R_STO_R (0x1 << 3)
+#define RT5677_M_DAC2_R_STO_R_SFT 3
+#define RT5677_DAC2_R_STO_R_VOL_MASK (0x1 << 2)
+#define RT5677_DAC2_R_STO_R_VOL_SFT 2
+#define RT5677_M_DAC1_L_STO_R (0x1 << 1)
+#define RT5677_M_DAC1_L_STO_R_SFT 1
+#define RT5677_DAC1_L_STO_R_VOL_MASK (0x1 << 0)
+#define RT5677_DAC1_L_STO_R_VOL_SFT 0
+
+/* Mono DAC Mixer L/R Control (0x2b) */
+#define RT5677_M_ST_DAC2_L (0x1 << 15)
+#define RT5677_M_ST_DAC2_L_SFT 15
+#define RT5677_M_DAC2_L_MONO_L (0x1 << 13)
+#define RT5677_M_DAC2_L_MONO_L_SFT 13
+#define RT5677_DAC2_L_MONO_L_VOL_MASK (0x1 << 12)
+#define RT5677_DAC2_L_MONO_L_VOL_SFT 12
+#define RT5677_M_DAC2_R_MONO_L (0x1 << 11)
+#define RT5677_M_DAC2_R_MONO_L_SFT 11
+#define RT5677_DAC2_R_MONO_L_VOL_MASK (0x1 << 10)
+#define RT5677_DAC2_R_MONO_L_VOL_SFT 10
+#define RT5677_M_DAC1_L_MONO_L (0x1 << 9)
+#define RT5677_M_DAC1_L_MONO_L_SFT 9
+#define RT5677_DAC1_L_MONO_L_VOL_MASK (0x1 << 8)
+#define RT5677_DAC1_L_MONO_L_VOL_SFT 8
+#define RT5677_M_ST_DAC2_R (0x1 << 7)
+#define RT5677_M_ST_DAC2_R_SFT 7
+#define RT5677_M_DAC2_R_MONO_R (0x1 << 5)
+#define RT5677_M_DAC2_R_MONO_R_SFT 5
+#define RT5677_DAC2_R_MONO_R_VOL_MASK (0x1 << 4)
+#define RT5677_DAC2_R_MONO_R_VOL_SFT 4
+#define RT5677_M_DAC1_R_MONO_R (0x1 << 3)
+#define RT5677_M_DAC1_R_MONO_R_SFT 3
+#define RT5677_DAC1_R_MONO_R_VOL_MASK (0x1 << 2)
+#define RT5677_DAC1_R_MONO_R_VOL_SFT 2
+#define RT5677_M_DAC2_L_MONO_R (0x1 << 1)
+#define RT5677_M_DAC2_L_MONO_R_SFT 1
+#define RT5677_DAC2_L_MONO_R_VOL_MASK (0x1 << 0)
+#define RT5677_DAC2_L_MONO_R_VOL_SFT 0
+
+/* DD Mixer 1 Control (0x2c) */
+#define RT5677_M_STO_L_DD1_L (0x1 << 15)
+#define RT5677_M_STO_L_DD1_L_SFT 15
+#define RT5677_STO_L_DD1_L_VOL_MASK (0x1 << 14)
+#define RT5677_STO_L_DD1_L_VOL_SFT 14
+#define RT5677_M_MONO_L_DD1_L (0x1 << 13)
+#define RT5677_M_MONO_L_DD1_L_SFT 13
+#define RT5677_MONO_L_DD1_L_VOL_MASK (0x1 << 12)
+#define RT5677_MONO_L_DD1_L_VOL_SFT 12
+#define RT5677_M_DAC3_L_DD1_L (0x1 << 11)
+#define RT5677_M_DAC3_L_DD1_L_SFT 11
+#define RT5677_DAC3_L_DD1_L_VOL_MASK (0x1 << 10)
+#define RT5677_DAC3_L_DD1_L_VOL_SFT 10
+#define RT5677_M_DAC3_R_DD1_L (0x1 << 9)
+#define RT5677_M_DAC3_R_DD1_L_SFT 9
+#define RT5677_DAC3_R_DD1_L_VOL_MASK (0x1 << 8)
+#define RT5677_DAC3_R_DD1_L_VOL_SFT 8
+#define RT5677_M_STO_R_DD1_R (0x1 << 7)
+#define RT5677_M_STO_R_DD1_R_SFT 7
+#define RT5677_STO_R_DD1_R_VOL_MASK (0x1 << 6)
+#define RT5677_STO_R_DD1_R_VOL_SFT 6
+#define RT5677_M_MONO_R_DD1_R (0x1 << 5)
+#define RT5677_M_MONO_R_DD1_R_SFT 5
+#define RT5677_MONO_R_DD1_R_VOL_MASK (0x1 << 4)
+#define RT5677_MONO_R_DD1_R_VOL_SFT 4
+#define RT5677_M_DAC3_R_DD1_R (0x1 << 3)
+#define RT5677_M_DAC3_R_DD1_R_SFT 3
+#define RT5677_DAC3_R_DD1_R_VOL_MASK (0x1 << 2)
+#define RT5677_DAC3_R_DD1_R_VOL_SFT 2
+#define RT5677_M_DAC3_L_DD1_R (0x1 << 1)
+#define RT5677_M_DAC3_L_DD1_R_SFT 1
+#define RT5677_DAC3_L_DD1_R_VOL_MASK (0x1 << 0)
+#define RT5677_DAC3_L_DD1_R_VOL_SFT 0
+
+/* DD Mixer 2 Control (0x2d) */
+#define RT5677_M_STO_L_DD2_L (0x1 << 15)
+#define RT5677_M_STO_L_DD2_L_SFT 15
+#define RT5677_STO_L_DD2_L_VOL_MASK (0x1 << 14)
+#define RT5677_STO_L_DD2_L_VOL_SFT 14
+#define RT5677_M_MONO_L_DD2_L (0x1 << 13)
+#define RT5677_M_MONO_L_DD2_L_SFT 13
+#define RT5677_MONO_L_DD2_L_VOL_MASK (0x1 << 12)
+#define RT5677_MONO_L_DD2_L_VOL_SFT 12
+#define RT5677_M_DAC4_L_DD2_L (0x1 << 11)
+#define RT5677_M_DAC4_L_DD2_L_SFT 11
+#define RT5677_DAC4_L_DD2_L_VOL_MASK (0x1 << 10)
+#define RT5677_DAC4_L_DD2_L_VOL_SFT 10
+#define RT5677_M_DAC4_R_DD2_L (0x1 << 9)
+#define RT5677_M_DAC4_R_DD2_L_SFT 9
+#define RT5677_DAC4_R_DD2_L_VOL_MASK (0x1 << 8)
+#define RT5677_DAC4_R_DD2_L_VOL_SFT 8
+#define RT5677_M_STO_R_DD2_R (0x1 << 7)
+#define RT5677_M_STO_R_DD2_R_SFT 7
+#define RT5677_STO_R_DD2_R_VOL_MASK (0x1 << 6)
+#define RT5677_STO_R_DD2_R_VOL_SFT 6
+#define RT5677_M_MONO_R_DD2_R (0x1 << 5)
+#define RT5677_M_MONO_R_DD2_R_SFT 5
+#define RT5677_MONO_R_DD2_R_VOL_MASK (0x1 << 4)
+#define RT5677_MONO_R_DD2_R_VOL_SFT 4
+#define RT5677_M_DAC4_R_DD2_R (0x1 << 3)
+#define RT5677_M_DAC4_R_DD2_R_SFT 3
+#define RT5677_DAC4_R_DD2_R_VOL_MASK (0x1 << 2)
+#define RT5677_DAC4_R_DD2_R_VOL_SFT 2
+#define RT5677_M_DAC4_L_DD2_R (0x1 << 1)
+#define RT5677_M_DAC4_L_DD2_R_SFT 1
+#define RT5677_DAC4_L_DD2_R_VOL_MASK (0x1 << 0)
+#define RT5677_DAC4_L_DD2_R_VOL_SFT 0
+
+/* IF3 data control (0x2f) */
+#define RT5677_IF3_DAC_SEL_MASK (0x3 << 6)
+#define RT5677_IF3_DAC_SEL_SFT 6
+#define RT5677_IF3_ADC_SEL_MASK (0x3 << 4)
+#define RT5677_IF3_ADC_SEL_SFT 4
+#define RT5677_IF3_ADC_IN_MASK (0xf << 0)
+#define RT5677_IF3_ADC_IN_SFT 0
+
+/* IF4 data control (0x30) */
+#define RT5677_IF4_ADC_IN_MASK (0xf << 4)
+#define RT5677_IF4_ADC_IN_SFT 4
+#define RT5677_IF4_DAC_SEL_MASK (0x3 << 2)
+#define RT5677_IF4_DAC_SEL_SFT 2
+#define RT5677_IF4_ADC_SEL_MASK (0x3 << 0)
+#define RT5677_IF4_ADC_SEL_SFT 0
+
+/* PDM Output Control (0x31) */
+#define RT5677_M_PDM1_L (0x1 << 15)
+#define RT5677_M_PDM1_L_SFT 15
+#define RT5677_SEL_PDM1_L_MASK (0x3 << 12)
+#define RT5677_SEL_PDM1_L_SFT 12
+#define RT5677_M_PDM1_R (0x1 << 11)
+#define RT5677_M_PDM1_R_SFT 11
+#define RT5677_SEL_PDM1_R_MASK (0x3 << 8)
+#define RT5677_SEL_PDM1_R_SFT 8
+#define RT5677_M_PDM2_L (0x1 << 7)
+#define RT5677_M_PDM2_L_SFT 7
+#define RT5677_SEL_PDM2_L_MASK (0x3 << 4)
+#define RT5677_SEL_PDM2_L_SFT 4
+#define RT5677_M_PDM2_R (0x1 << 3)
+#define RT5677_M_PDM2_R_SFT 3
+#define RT5677_SEL_PDM2_R_MASK (0x3 << 0)
+#define RT5677_SEL_PDM2_R_SFT 0
+
+/* PDM I2C / Data Control 1 (0x32) */
+#define RT5677_PDM2_PW_DOWN (0x1 << 7)
+#define RT5677_PDM1_PW_DOWN (0x1 << 6)
+#define RT5677_PDM2_BUSY (0x1 << 5)
+#define RT5677_PDM1_BUSY (0x1 << 4)
+#define RT5677_PDM_PATTERN (0x1 << 3)
+#define RT5677_PDM_GAIN (0x1 << 2)
+#define RT5677_PDM_DIV_MASK (0x3 << 0)
+
+/* PDM I2C / Data Control 2 (0x33) */
+#define RT5677_PDM1_I2C_ID (0xf << 12)
+#define RT5677_PDM1_EXE (0x1 << 11)
+#define RT5677_PDM1_I2C_CMD (0x1 << 10)
+#define RT5677_PDM1_I2C_EXE (0x1 << 9)
+#define RT5677_PDM1_I2C_BUSY (0x1 << 8)
+#define RT5677_PDM2_I2C_ID (0xf << 4)
+#define RT5677_PDM2_EXE (0x1 << 3)
+#define RT5677_PDM2_I2C_CMD (0x1 << 2)
+#define RT5677_PDM2_I2C_EXE (0x1 << 1)
+#define RT5677_PDM2_I2C_BUSY (0x1 << 0)
+
+/* TDM1 control 1 (0x3b) */
+#define RT5677_IF1_ADC_MODE_MASK (0x1 << 12)
+#define RT5677_IF1_ADC_MODE_SFT 12
+#define RT5677_IF1_ADC_MODE_I2S (0x0 << 12)
+#define RT5677_IF1_ADC_MODE_TDM (0x1 << 12)
+#define RT5677_IF1_ADC1_SWAP_MASK (0x3 << 6)
+#define RT5677_IF1_ADC1_SWAP_SFT 6
+#define RT5677_IF1_ADC2_SWAP_MASK (0x3 << 4)
+#define RT5677_IF1_ADC2_SWAP_SFT 4
+#define RT5677_IF1_ADC3_SWAP_MASK (0x3 << 2)
+#define RT5677_IF1_ADC3_SWAP_SFT 2
+#define RT5677_IF1_ADC4_SWAP_MASK (0x3 << 0)
+#define RT5677_IF1_ADC4_SWAP_SFT 0
+
+/* TDM1 control 2 (0x3c) */
+#define RT5677_IF1_ADC4_MASK (0x3 << 10)
+#define RT5677_IF1_ADC4_SFT 10
+#define RT5677_IF1_ADC3_MASK (0x3 << 8)
+#define RT5677_IF1_ADC3_SFT 8
+#define RT5677_IF1_ADC2_MASK (0x3 << 6)
+#define RT5677_IF1_ADC2_SFT 6
+#define RT5677_IF1_ADC1_MASK (0x3 << 4)
+#define RT5677_IF1_ADC1_SFT 4
+#define RT5677_IF1_ADC_CTRL_MASK (0x7 << 0)
+#define RT5677_IF1_ADC_CTRL_SFT 0
+
+/* TDM1 control 4 (0x3e) */
+#define RT5677_IF1_DAC0_MASK (0x7 << 12)
+#define RT5677_IF1_DAC0_SFT 12
+#define RT5677_IF1_DAC1_MASK (0x7 << 8)
+#define RT5677_IF1_DAC1_SFT 8
+#define RT5677_IF1_DAC2_MASK (0x7 << 4)
+#define RT5677_IF1_DAC2_SFT 4
+#define RT5677_IF1_DAC3_MASK (0x7 << 0)
+#define RT5677_IF1_DAC3_SFT 0
+
+/* TDM1 control 5 (0x3f) */
+#define RT5677_IF1_DAC4_MASK (0x7 << 12)
+#define RT5677_IF1_DAC4_SFT 12
+#define RT5677_IF1_DAC5_MASK (0x7 << 8)
+#define RT5677_IF1_DAC5_SFT 8
+#define RT5677_IF1_DAC6_MASK (0x7 << 4)
+#define RT5677_IF1_DAC6_SFT 4
+#define RT5677_IF1_DAC7_MASK (0x7 << 0)
+#define RT5677_IF1_DAC7_SFT 0
+
+/* TDM2 control 1 (0x40) */
+#define RT5677_IF2_ADC_MODE_MASK (0x1 << 12)
+#define RT5677_IF2_ADC_MODE_SFT 12
+#define RT5677_IF2_ADC_MODE_I2S (0x0 << 12)
+#define RT5677_IF2_ADC_MODE_TDM (0x1 << 12)
+#define RT5677_IF2_ADC1_SWAP_MASK (0x3 << 6)
+#define RT5677_IF2_ADC1_SWAP_SFT 6
+#define RT5677_IF2_ADC2_SWAP_MASK (0x3 << 4)
+#define RT5677_IF2_ADC2_SWAP_SFT 4
+#define RT5677_IF2_ADC3_SWAP_MASK (0x3 << 2)
+#define RT5677_IF2_ADC3_SWAP_SFT 2
+#define RT5677_IF2_ADC4_SWAP_MASK (0x3 << 0)
+#define RT5677_IF2_ADC4_SWAP_SFT 0
+
+/* TDM2 control 2 (0x41) */
+#define RT5677_IF2_ADC4_MASK (0x3 << 10)
+#define RT5677_IF2_ADC4_SFT 10
+#define RT5677_IF2_ADC3_MASK (0x3 << 8)
+#define RT5677_IF2_ADC3_SFT 8
+#define RT5677_IF2_ADC2_MASK (0x3 << 6)
+#define RT5677_IF2_ADC2_SFT 6
+#define RT5677_IF2_ADC1_MASK (0x3 << 4)
+#define RT5677_IF2_ADC1_SFT 4
+#define RT5677_IF2_ADC_CTRL_MASK (0x7 << 0)
+#define RT5677_IF2_ADC_CTRL_SFT 0
+
+/* TDM2 control 4 (0x43) */
+#define RT5677_IF2_DAC0_MASK (0x7 << 12)
+#define RT5677_IF2_DAC0_SFT 12
+#define RT5677_IF2_DAC1_MASK (0x7 << 8)
+#define RT5677_IF2_DAC1_SFT 8
+#define RT5677_IF2_DAC2_MASK (0x7 << 4)
+#define RT5677_IF2_DAC2_SFT 4
+#define RT5677_IF2_DAC3_MASK (0x7 << 0)
+#define RT5677_IF2_DAC3_SFT 0
+
+/* TDM2 control 5 (0x44) */
+#define RT5677_IF2_DAC4_MASK (0x7 << 12)
+#define RT5677_IF2_DAC4_SFT 12
+#define RT5677_IF2_DAC5_MASK (0x7 << 8)
+#define RT5677_IF2_DAC5_SFT 8
+#define RT5677_IF2_DAC6_MASK (0x7 << 4)
+#define RT5677_IF2_DAC6_SFT 4
+#define RT5677_IF2_DAC7_MASK (0x7 << 0)
+#define RT5677_IF2_DAC7_SFT 0
+
+/* Digital Microphone Control 1 (0x50) */
+#define RT5677_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5677_DMIC_1_EN_SFT 15
+#define RT5677_DMIC_1_DIS (0x0 << 15)
+#define RT5677_DMIC_1_EN (0x1 << 15)
+#define RT5677_DMIC_2_EN_MASK (0x1 << 14)
+#define RT5677_DMIC_2_EN_SFT 14
+#define RT5677_DMIC_2_DIS (0x0 << 14)
+#define RT5677_DMIC_2_EN (0x1 << 14)
+#define RT5677_DMIC_L_STO1_LH_MASK (0x1 << 13)
+#define RT5677_DMIC_L_STO1_LH_SFT 13
+#define RT5677_DMIC_L_STO1_LH_FALLING (0x0 << 13)
+#define RT5677_DMIC_L_STO1_LH_RISING (0x1 << 13)
+#define RT5677_DMIC_R_STO1_LH_MASK (0x1 << 12)
+#define RT5677_DMIC_R_STO1_LH_SFT 12
+#define RT5677_DMIC_R_STO1_LH_FALLING (0x0 << 12)
+#define RT5677_DMIC_R_STO1_LH_RISING (0x1 << 12)
+#define RT5677_DMIC_L_STO3_LH_MASK (0x1 << 11)
+#define RT5677_DMIC_L_STO3_LH_SFT 11
+#define RT5677_DMIC_L_STO3_LH_FALLING (0x0 << 11)
+#define RT5677_DMIC_L_STO3_LH_RISING (0x1 << 11)
+#define RT5677_DMIC_R_STO3_LH_MASK (0x1 << 10)
+#define RT5677_DMIC_R_STO3_LH_SFT 10
+#define RT5677_DMIC_R_STO3_LH_FALLING (0x0 << 10)
+#define RT5677_DMIC_R_STO3_LH_RISING (0x1 << 10)
+#define RT5677_DMIC_L_STO2_LH_MASK (0x1 << 9)
+#define RT5677_DMIC_L_STO2_LH_SFT 9
+#define RT5677_DMIC_L_STO2_LH_FALLING (0x0 << 9)
+#define RT5677_DMIC_L_STO2_LH_RISING (0x1 << 9)
+#define RT5677_DMIC_R_STO2_LH_MASK (0x1 << 8)
+#define RT5677_DMIC_R_STO2_LH_SFT 8
+#define RT5677_DMIC_R_STO2_LH_FALLING (0x0 << 8)
+#define RT5677_DMIC_R_STO2_LH_RISING (0x1 << 8)
+#define RT5677_DMIC_CLK_MASK (0x7 << 5)
+#define RT5677_DMIC_CLK_SFT 5
+#define RT5677_DMIC_3_EN_MASK (0x1 << 4)
+#define RT5677_DMIC_3_EN_SFT 4
+#define RT5677_DMIC_3_DIS (0x0 << 4)
+#define RT5677_DMIC_3_EN (0x1 << 4)
+#define RT5677_DMIC_R_MONO_LH_MASK (0x1 << 2)
+#define RT5677_DMIC_R_MONO_LH_SFT 2
+#define RT5677_DMIC_R_MONO_LH_FALLING (0x0 << 2)
+#define RT5677_DMIC_R_MONO_LH_RISING (0x1 << 2)
+#define RT5677_DMIC_L_STO4_LH_MASK (0x1 << 1)
+#define RT5677_DMIC_L_STO4_LH_SFT 1
+#define RT5677_DMIC_L_STO4_LH_FALLING (0x0 << 1)
+#define RT5677_DMIC_L_STO4_LH_RISING (0x1 << 1)
+#define RT5677_DMIC_R_STO4_LH_MASK (0x1 << 0)
+#define RT5677_DMIC_R_STO4_LH_SFT 0
+#define RT5677_DMIC_R_STO4_LH_FALLING (0x0 << 0)
+#define RT5677_DMIC_R_STO4_LH_RISING (0x1 << 0)
+
+/* Digital Microphone Control 2 (0x51) */
+#define RT5677_DMIC_4_EN_MASK (0x1 << 15)
+#define RT5677_DMIC_4_EN_SFT 15
+#define RT5677_DMIC_4_DIS (0x0 << 15)
+#define RT5677_DMIC_4_EN (0x1 << 15)
+#define RT5677_DMIC_4L_LH_MASK (0x1 << 7)
+#define RT5677_DMIC_4L_LH_SFT 7
+#define RT5677_DMIC_4L_LH_FALLING (0x0 << 7)
+#define RT5677_DMIC_4L_LH_RISING (0x1 << 7)
+#define RT5677_DMIC_4R_LH_MASK (0x1 << 6)
+#define RT5677_DMIC_4R_LH_SFT 6
+#define RT5677_DMIC_4R_LH_FALLING (0x0 << 6)
+#define RT5677_DMIC_4R_LH_RISING (0x1 << 6)
+#define RT5677_DMIC_3L_LH_MASK (0x1 << 5)
+#define RT5677_DMIC_3L_LH_SFT 5
+#define RT5677_DMIC_3L_LH_FALLING (0x0 << 5)
+#define RT5677_DMIC_3L_LH_RISING (0x1 << 5)
+#define RT5677_DMIC_3R_LH_MASK (0x1 << 4)
+#define RT5677_DMIC_3R_LH_SFT 4
+#define RT5677_DMIC_3R_LH_FALLING (0x0 << 4)
+#define RT5677_DMIC_3R_LH_RISING (0x1 << 4)
+#define RT5677_DMIC_2L_LH_MASK (0x1 << 3)
+#define RT5677_DMIC_2L_LH_SFT 3
+#define RT5677_DMIC_2L_LH_FALLING (0x0 << 3)
+#define RT5677_DMIC_2L_LH_RISING (0x1 << 3)
+#define RT5677_DMIC_2R_LH_MASK (0x1 << 2)
+#define RT5677_DMIC_2R_LH_SFT 2
+#define RT5677_DMIC_2R_LH_FALLING (0x0 << 2)
+#define RT5677_DMIC_2R_LH_RISING (0x1 << 2)
+#define RT5677_DMIC_1L_LH_MASK (0x1 << 1)
+#define RT5677_DMIC_1L_LH_SFT 1
+#define RT5677_DMIC_1L_LH_FALLING (0x0 << 1)
+#define RT5677_DMIC_1L_LH_RISING (0x1 << 1)
+#define RT5677_DMIC_1R_LH_MASK (0x1 << 0)
+#define RT5677_DMIC_1R_LH_SFT 0
+#define RT5677_DMIC_1R_LH_FALLING (0x0 << 0)
+#define RT5677_DMIC_1R_LH_RISING (0x1 << 0)
+
+/* Power Management for Digital 1 (0x61) */
+#define RT5677_PWR_I2S1 (0x1 << 15)
+#define RT5677_PWR_I2S1_BIT 15
+#define RT5677_PWR_I2S2 (0x1 << 14)
+#define RT5677_PWR_I2S2_BIT 14
+#define RT5677_PWR_I2S3 (0x1 << 13)
+#define RT5677_PWR_I2S3_BIT 13
+#define RT5677_PWR_DAC1 (0x1 << 12)
+#define RT5677_PWR_DAC1_BIT 12
+#define RT5677_PWR_DAC2 (0x1 << 11)
+#define RT5677_PWR_DAC2_BIT 11
+#define RT5677_PWR_I2S4 (0x1 << 10)
+#define RT5677_PWR_I2S4_BIT 10
+#define RT5677_PWR_SLB (0x1 << 9)
+#define RT5677_PWR_SLB_BIT 9
+#define RT5677_PWR_DAC3 (0x1 << 7)
+#define RT5677_PWR_DAC3_BIT 7
+#define RT5677_PWR_ADCFED2 (0x1 << 4)
+#define RT5677_PWR_ADCFED2_BIT 4
+#define RT5677_PWR_ADCFED1 (0x1 << 3)
+#define RT5677_PWR_ADCFED1_BIT 3
+#define RT5677_PWR_ADC_L (0x1 << 2)
+#define RT5677_PWR_ADC_L_BIT 2
+#define RT5677_PWR_ADC_R (0x1 << 1)
+#define RT5677_PWR_ADC_R_BIT 1
+#define RT5677_PWR_I2C_MASTER (0x1 << 0)
+#define RT5677_PWR_I2C_MASTER_BIT 0
+
+/* Power Management for Digital 2 (0x62) */
+#define RT5677_PWR_ADC_S1F (0x1 << 15)
+#define RT5677_PWR_ADC_S1F_BIT 15
+#define RT5677_PWR_ADC_MF_L (0x1 << 14)
+#define RT5677_PWR_ADC_MF_L_BIT 14
+#define RT5677_PWR_ADC_MF_R (0x1 << 13)
+#define RT5677_PWR_ADC_MF_R_BIT 13
+#define RT5677_PWR_DAC_S1F (0x1 << 12)
+#define RT5677_PWR_DAC_S1F_BIT 12
+#define RT5677_PWR_DAC_M2F_L (0x1 << 11)
+#define RT5677_PWR_DAC_M2F_L_BIT 11
+#define RT5677_PWR_DAC_M2F_R (0x1 << 10)
+#define RT5677_PWR_DAC_M2F_R_BIT 10
+#define RT5677_PWR_DAC_M3F_L (0x1 << 9)
+#define RT5677_PWR_DAC_M3F_L_BIT 9
+#define RT5677_PWR_DAC_M3F_R (0x1 << 8)
+#define RT5677_PWR_DAC_M3F_R_BIT 8
+#define RT5677_PWR_DAC_M4F_L (0x1 << 7)
+#define RT5677_PWR_DAC_M4F_L_BIT 7
+#define RT5677_PWR_DAC_M4F_R (0x1 << 6)
+#define RT5677_PWR_DAC_M4F_R_BIT 6
+#define RT5677_PWR_ADC_S2F (0x1 << 5)
+#define RT5677_PWR_ADC_S2F_BIT 5
+#define RT5677_PWR_ADC_S3F (0x1 << 4)
+#define RT5677_PWR_ADC_S3F_BIT 4
+#define RT5677_PWR_ADC_S4F (0x1 << 3)
+#define RT5677_PWR_ADC_S4F_BIT 3
+#define RT5677_PWR_PDM1 (0x1 << 2)
+#define RT5677_PWR_PDM1_BIT 2
+#define RT5677_PWR_PDM2 (0x1 << 1)
+#define RT5677_PWR_PDM2_BIT 1
+
+/* Power Management for Analog 1 (0x63) */
+#define RT5677_PWR_VREF1 (0x1 << 15)
+#define RT5677_PWR_VREF1_BIT 15
+#define RT5677_PWR_FV1 (0x1 << 14)
+#define RT5677_PWR_FV1_BIT 14
+#define RT5677_PWR_MB (0x1 << 13)
+#define RT5677_PWR_MB_BIT 13
+#define RT5677_PWR_LO1 (0x1 << 12)
+#define RT5677_PWR_LO1_BIT 12
+#define RT5677_PWR_BG (0x1 << 11)
+#define RT5677_PWR_BG_BIT 11
+#define RT5677_PWR_LO2 (0x1 << 10)
+#define RT5677_PWR_LO2_BIT 10
+#define RT5677_PWR_LO3 (0x1 << 9)
+#define RT5677_PWR_LO3_BIT 9
+#define RT5677_PWR_VREF2 (0x1 << 8)
+#define RT5677_PWR_VREF2_BIT 8
+#define RT5677_PWR_FV2 (0x1 << 7)
+#define RT5677_PWR_FV2_BIT 7
+#define RT5677_LDO2_SEL_MASK (0x7 << 4)
+#define RT5677_LDO2_SEL_SFT 4
+#define RT5677_LDO1_SEL_MASK (0x7 << 0)
+#define RT5677_LDO1_SEL_SFT 0
+
+/* Power Management for Analog 2 (0x64) */
+#define RT5677_PWR_BST1 (0x1 << 15)
+#define RT5677_PWR_BST1_BIT 15
+#define RT5677_PWR_BST2 (0x1 << 14)
+#define RT5677_PWR_BST2_BIT 14
+#define RT5677_PWR_CLK_MB1 (0x1 << 13)
+#define RT5677_PWR_CLK_MB1_BIT 13
+#define RT5677_PWR_SLIM (0x1 << 12)
+#define RT5677_PWR_SLIM_BIT 12
+#define RT5677_PWR_MB1 (0x1 << 11)
+#define RT5677_PWR_MB1_BIT 11
+#define RT5677_PWR_PP_MB1 (0x1 << 10)
+#define RT5677_PWR_PP_MB1_BIT 10
+#define RT5677_PWR_PLL1 (0x1 << 9)
+#define RT5677_PWR_PLL1_BIT 9
+#define RT5677_PWR_PLL2 (0x1 << 8)
+#define RT5677_PWR_PLL2_BIT 8
+#define RT5677_PWR_CORE (0x1 << 7)
+#define RT5677_PWR_CORE_BIT 7
+#define RT5677_PWR_CLK_MB (0x1 << 6)
+#define RT5677_PWR_CLK_MB_BIT 6
+#define RT5677_PWR_BST1_P (0x1 << 5)
+#define RT5677_PWR_BST1_P_BIT 5
+#define RT5677_PWR_BST2_P (0x1 << 4)
+#define RT5677_PWR_BST2_P_BIT 4
+#define RT5677_PWR_IPTV (0x1 << 3)
+#define RT5677_PWR_IPTV_BIT 3
+#define RT5677_PWR_25M_CLK (0x1 << 1)
+#define RT5677_PWR_25M_CLK_BIT 1
+#define RT5677_PWR_LDO1 (0x1 << 0)
+#define RT5677_PWR_LDO1_BIT 0
+
+/* Power Management for DSP (0x65) */
+#define RT5677_PWR_SR7 (0x1 << 10)
+#define RT5677_PWR_SR7_BIT 10
+#define RT5677_PWR_SR6 (0x1 << 9)
+#define RT5677_PWR_SR6_BIT 9
+#define RT5677_PWR_SR5 (0x1 << 8)
+#define RT5677_PWR_SR5_BIT 8
+#define RT5677_PWR_SR4 (0x1 << 7)
+#define RT5677_PWR_SR4_BIT 7
+#define RT5677_PWR_SR3 (0x1 << 6)
+#define RT5677_PWR_SR3_BIT 6
+#define RT5677_PWR_SR2 (0x1 << 5)
+#define RT5677_PWR_SR2_BIT 5
+#define RT5677_PWR_SR1 (0x1 << 4)
+#define RT5677_PWR_SR1_BIT 4
+#define RT5677_PWR_SR0 (0x1 << 3)
+#define RT5677_PWR_SR0_BIT 3
+#define RT5677_PWR_MLT (0x1 << 2)
+#define RT5677_PWR_MLT_BIT 2
+#define RT5677_PWR_DSP (0x1 << 1)
+#define RT5677_PWR_DSP_BIT 1
+#define RT5677_PWR_DSP_CPU (0x1 << 0)
+#define RT5677_PWR_DSP_CPU_BIT 0
+
+/* Power Status for DSP (0x66) */
+#define RT5677_PWR_SR7_RDY (0x1 << 9)
+#define RT5677_PWR_SR7_RDY_BIT 9
+#define RT5677_PWR_SR6_RDY (0x1 << 8)
+#define RT5677_PWR_SR6_RDY_BIT 8
+#define RT5677_PWR_SR5_RDY (0x1 << 7)
+#define RT5677_PWR_SR5_RDY_BIT 7
+#define RT5677_PWR_SR4_RDY (0x1 << 6)
+#define RT5677_PWR_SR4_RDY_BIT 6
+#define RT5677_PWR_SR3_RDY (0x1 << 5)
+#define RT5677_PWR_SR3_RDY_BIT 5
+#define RT5677_PWR_SR2_RDY (0x1 << 4)
+#define RT5677_PWR_SR2_RDY_BIT 4
+#define RT5677_PWR_SR1_RDY (0x1 << 3)
+#define RT5677_PWR_SR1_RDY_BIT 3
+#define RT5677_PWR_SR0_RDY (0x1 << 2)
+#define RT5677_PWR_SR0_RDY_BIT 2
+#define RT5677_PWR_MLT_RDY (0x1 << 1)
+#define RT5677_PWR_MLT_RDY_BIT 1
+#define RT5677_PWR_DSP_RDY (0x1 << 0)
+#define RT5677_PWR_DSP_RDY_BIT 0
+
+/* Power Management for DSP (0x67) */
+#define RT5677_PWR_SLIM_ISO (0x1 << 11)
+#define RT5677_PWR_SLIM_ISO_BIT 11
+#define RT5677_PWR_CORE_ISO (0x1 << 10)
+#define RT5677_PWR_CORE_ISO_BIT 10
+#define RT5677_PWR_DSP_ISO (0x1 << 9)
+#define RT5677_PWR_DSP_ISO_BIT 9
+#define RT5677_PWR_SR7_ISO (0x1 << 8)
+#define RT5677_PWR_SR7_ISO_BIT 8
+#define RT5677_PWR_SR6_ISO (0x1 << 7)
+#define RT5677_PWR_SR6_ISO_BIT 7
+#define RT5677_PWR_SR5_ISO (0x1 << 6)
+#define RT5677_PWR_SR5_ISO_BIT 6
+#define RT5677_PWR_SR4_ISO (0x1 << 5)
+#define RT5677_PWR_SR4_ISO_BIT 5
+#define RT5677_PWR_SR3_ISO (0x1 << 4)
+#define RT5677_PWR_SR3_ISO_BIT 4
+#define RT5677_PWR_SR2_ISO (0x1 << 3)
+#define RT5677_PWR_SR2_ISO_BIT 3
+#define RT5677_PWR_SR1_ISO (0x1 << 2)
+#define RT5677_PWR_SR1_ISO_BIT 2
+#define RT5677_PWR_SR0_ISO (0x1 << 1)
+#define RT5677_PWR_SR0_ISO_BIT 1
+#define RT5677_PWR_MLT_ISO (0x1 << 0)
+#define RT5677_PWR_MLT_ISO_BIT 0
+
+/* I2S1/2/3/4 Audio Serial Data Port Control (0x6f 0x70 0x71 0x72) */
+#define RT5677_I2S_MS_MASK (0x1 << 15)
+#define RT5677_I2S_MS_SFT 15
+#define RT5677_I2S_MS_M (0x0 << 15)
+#define RT5677_I2S_MS_S (0x1 << 15)
+#define RT5677_I2S_O_CP_MASK (0x3 << 10)
+#define RT5677_I2S_O_CP_SFT 10
+#define RT5677_I2S_O_CP_OFF (0x0 << 10)
+#define RT5677_I2S_O_CP_U_LAW (0x1 << 10)
+#define RT5677_I2S_O_CP_A_LAW (0x2 << 10)
+#define RT5677_I2S_I_CP_MASK (0x3 << 8)
+#define RT5677_I2S_I_CP_SFT 8
+#define RT5677_I2S_I_CP_OFF (0x0 << 8)
+#define RT5677_I2S_I_CP_U_LAW (0x1 << 8)
+#define RT5677_I2S_I_CP_A_LAW (0x2 << 8)
+#define RT5677_I2S_BP_MASK (0x1 << 7)
+#define RT5677_I2S_BP_SFT 7
+#define RT5677_I2S_BP_NOR (0x0 << 7)
+#define RT5677_I2S_BP_INV (0x1 << 7)
+#define RT5677_I2S_DL_MASK (0x3 << 2)
+#define RT5677_I2S_DL_SFT 2
+#define RT5677_I2S_DL_16 (0x0 << 2)
+#define RT5677_I2S_DL_20 (0x1 << 2)
+#define RT5677_I2S_DL_24 (0x2 << 2)
+#define RT5677_I2S_DL_8 (0x3 << 2)
+#define RT5677_I2S_DF_MASK (0x3 << 0)
+#define RT5677_I2S_DF_SFT 0
+#define RT5677_I2S_DF_I2S (0x0 << 0)
+#define RT5677_I2S_DF_LEFT (0x1 << 0)
+#define RT5677_I2S_DF_PCM_A (0x2 << 0)
+#define RT5677_I2S_DF_PCM_B (0x3 << 0)
+
+/* Clock Tree Control 1 (0x73) */
+#define RT5677_I2S_PD1_MASK (0x7 << 12)
+#define RT5677_I2S_PD1_SFT 12
+#define RT5677_I2S_PD1_1 (0x0 << 12)
+#define RT5677_I2S_PD1_2 (0x1 << 12)
+#define RT5677_I2S_PD1_3 (0x2 << 12)
+#define RT5677_I2S_PD1_4 (0x3 << 12)
+#define RT5677_I2S_PD1_6 (0x4 << 12)
+#define RT5677_I2S_PD1_8 (0x5 << 12)
+#define RT5677_I2S_PD1_12 (0x6 << 12)
+#define RT5677_I2S_PD1_16 (0x7 << 12)
+#define RT5677_I2S_BCLK_MS2_MASK (0x1 << 11)
+#define RT5677_I2S_BCLK_MS2_SFT 11
+#define RT5677_I2S_BCLK_MS2_32 (0x0 << 11)
+#define RT5677_I2S_BCLK_MS2_64 (0x1 << 11)
+#define RT5677_I2S_PD2_MASK (0x7 << 8)
+#define RT5677_I2S_PD2_SFT 8
+#define RT5677_I2S_PD2_1 (0x0 << 8)
+#define RT5677_I2S_PD2_2 (0x1 << 8)
+#define RT5677_I2S_PD2_3 (0x2 << 8)
+#define RT5677_I2S_PD2_4 (0x3 << 8)
+#define RT5677_I2S_PD2_6 (0x4 << 8)
+#define RT5677_I2S_PD2_8 (0x5 << 8)
+#define RT5677_I2S_PD2_12 (0x6 << 8)
+#define RT5677_I2S_PD2_16 (0x7 << 8)
+#define RT5677_I2S_BCLK_MS3_MASK (0x1 << 7)
+#define RT5677_I2S_BCLK_MS3_SFT 7
+#define RT5677_I2S_BCLK_MS3_32 (0x0 << 7)
+#define RT5677_I2S_BCLK_MS3_64 (0x1 << 7)
+#define RT5677_I2S_PD3_MASK (0x7 << 4)
+#define RT5677_I2S_PD3_SFT 4
+#define RT5677_I2S_PD3_1 (0x0 << 4)
+#define RT5677_I2S_PD3_2 (0x1 << 4)
+#define RT5677_I2S_PD3_3 (0x2 << 4)
+#define RT5677_I2S_PD3_4 (0x3 << 4)
+#define RT5677_I2S_PD3_6 (0x4 << 4)
+#define RT5677_I2S_PD3_8 (0x5 << 4)
+#define RT5677_I2S_PD3_12 (0x6 << 4)
+#define RT5677_I2S_PD3_16 (0x7 << 4)
+#define RT5677_I2S_BCLK_MS4_MASK (0x1 << 3)
+#define RT5677_I2S_BCLK_MS4_SFT 3
+#define RT5677_I2S_BCLK_MS4_32 (0x0 << 3)
+#define RT5677_I2S_BCLK_MS4_64 (0x1 << 3)
+#define RT5677_I2S_PD4_MASK (0x7 << 0)
+#define RT5677_I2S_PD4_SFT 0
+#define RT5677_I2S_PD4_1 (0x0 << 0)
+#define RT5677_I2S_PD4_2 (0x1 << 0)
+#define RT5677_I2S_PD4_3 (0x2 << 0)
+#define RT5677_I2S_PD4_4 (0x3 << 0)
+#define RT5677_I2S_PD4_6 (0x4 << 0)
+#define RT5677_I2S_PD4_8 (0x5 << 0)
+#define RT5677_I2S_PD4_12 (0x6 << 0)
+#define RT5677_I2S_PD4_16 (0x7 << 0)
+
+/* Clock Tree Control 2 (0x74) */
+#define RT5677_I2S_PD5_MASK (0x7 << 12)
+#define RT5677_I2S_PD5_SFT 12
+#define RT5677_I2S_PD5_1 (0x0 << 12)
+#define RT5677_I2S_PD5_2 (0x1 << 12)
+#define RT5677_I2S_PD5_3 (0x2 << 12)
+#define RT5677_I2S_PD5_4 (0x3 << 12)
+#define RT5677_I2S_PD5_6 (0x4 << 12)
+#define RT5677_I2S_PD5_8 (0x5 << 12)
+#define RT5677_I2S_PD5_12 (0x6 << 12)
+#define RT5677_I2S_PD5_16 (0x7 << 12)
+#define RT5677_I2S_PD6_MASK (0x7 << 8)
+#define RT5677_I2S_PD6_SFT 8
+#define RT5677_I2S_PD6_1 (0x0 << 8)
+#define RT5677_I2S_PD6_2 (0x1 << 8)
+#define RT5677_I2S_PD6_3 (0x2 << 8)
+#define RT5677_I2S_PD6_4 (0x3 << 8)
+#define RT5677_I2S_PD6_6 (0x4 << 8)
+#define RT5677_I2S_PD6_8 (0x5 << 8)
+#define RT5677_I2S_PD6_12 (0x6 << 8)
+#define RT5677_I2S_PD6_16 (0x7 << 8)
+#define RT5677_I2S_PD7_MASK (0x7 << 4)
+#define RT5677_I2S_PD7_SFT 4
+#define RT5677_I2S_PD7_1 (0x0 << 4)
+#define RT5677_I2S_PD7_2 (0x1 << 4)
+#define RT5677_I2S_PD7_3 (0x2 << 4)
+#define RT5677_I2S_PD7_4 (0x3 << 4)
+#define RT5677_I2S_PD7_6 (0x4 << 4)
+#define RT5677_I2S_PD7_8 (0x5 << 4)
+#define RT5677_I2S_PD7_12 (0x6 << 4)
+#define RT5677_I2S_PD7_16 (0x7 << 4)
+#define RT5677_I2S_PD8_MASK (0x7 << 0)
+#define RT5677_I2S_PD8_SFT 0
+#define RT5677_I2S_PD8_1 (0x0 << 0)
+#define RT5677_I2S_PD8_2 (0x1 << 0)
+#define RT5677_I2S_PD8_3 (0x2 << 0)
+#define RT5677_I2S_PD8_4 (0x3 << 0)
+#define RT5677_I2S_PD8_6 (0x4 << 0)
+#define RT5677_I2S_PD8_8 (0x5 << 0)
+#define RT5677_I2S_PD8_12 (0x6 << 0)
+#define RT5677_I2S_PD8_16 (0x7 << 0)
+
+/* Clock Tree Control 3 (0x75) */
+#define RT5677_DSP_ASRC_O_MASK (0x3 << 6)
+#define RT5677_DSP_ASRC_O_SFT 6
+#define RT5677_DSP_ASRC_O_1_0 (0x0 << 6)
+#define RT5677_DSP_ASRC_O_1_5 (0x1 << 6)
+#define RT5677_DSP_ASRC_O_2_0 (0x2 << 6)
+#define RT5677_DSP_ASRC_O_3_0 (0x3 << 6)
+#define RT5677_DSP_ASRC_I_MASK (0x3 << 4)
+#define RT5677_DSP_ASRC_I_SFT 4
+#define RT5677_DSP_ASRC_I_1_0 (0x0 << 4)
+#define RT5677_DSP_ASRC_I_1_5 (0x1 << 4)
+#define RT5677_DSP_ASRC_I_2_0 (0x2 << 4)
+#define RT5677_DSP_ASRC_I_3_0 (0x3 << 4)
+#define RT5677_DSP_BUS_PD_MASK (0x7 << 0)
+#define RT5677_DSP_BUS_PD_SFT 0
+#define RT5677_DSP_BUS_PD_1 (0x0 << 0)
+#define RT5677_DSP_BUS_PD_2 (0x1 << 0)
+#define RT5677_DSP_BUS_PD_3 (0x2 << 0)
+#define RT5677_DSP_BUS_PD_4 (0x3 << 0)
+#define RT5677_DSP_BUS_PD_6 (0x4 << 0)
+#define RT5677_DSP_BUS_PD_8 (0x5 << 0)
+#define RT5677_DSP_BUS_PD_12 (0x6 << 0)
+#define RT5677_DSP_BUS_PD_16 (0x7 << 0)
+
+#define RT5677_PLL_INP_MAX 40000000
+#define RT5677_PLL_INP_MIN 2048000
+/* PLL M/N/K Code Control 1 (0x7a 0x7c) */
+#define RT5677_PLL_N_MAX 0x1ff
+#define RT5677_PLL_N_MASK (RT5677_PLL_N_MAX << 7)
+#define RT5677_PLL_N_SFT 7
+#define RT5677_PLL_K_BP (0x1 << 5)
+#define RT5677_PLL_K_BP_SFT 5
+#define RT5677_PLL_K_MAX 0x1f
+#define RT5677_PLL_K_MASK (RT5677_PLL_K_MAX)
+#define RT5677_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x7b 0x7d) */
+#define RT5677_PLL_M_MAX 0xf
+#define RT5677_PLL_M_MASK (RT5677_PLL_M_MAX << 12)
+#define RT5677_PLL_M_SFT 12
+#define RT5677_PLL_M_BP (0x1 << 11)
+#define RT5677_PLL_M_BP_SFT 11
+
+/* Global Clock Control 1 (0x80) */
+#define RT5677_SCLK_SRC_MASK (0x3 << 14)
+#define RT5677_SCLK_SRC_SFT 14
+#define RT5677_SCLK_SRC_MCLK (0x0 << 14)
+#define RT5677_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5677_SCLK_SRC_RCCLK (0x2 << 14) /* 25MHz */
+#define RT5677_SCLK_SRC_SLIM (0x3 << 14)
+#define RT5677_PLL1_SRC_MASK (0x7 << 11)
+#define RT5677_PLL1_SRC_SFT 11
+#define RT5677_PLL1_SRC_MCLK (0x0 << 11)
+#define RT5677_PLL1_SRC_BCLK1 (0x1 << 11)
+#define RT5677_PLL1_SRC_BCLK2 (0x2 << 11)
+#define RT5677_PLL1_SRC_BCLK3 (0x3 << 11)
+#define RT5677_PLL1_SRC_BCLK4 (0x4 << 11)
+#define RT5677_PLL1_SRC_RCCLK (0x5 << 11)
+#define RT5677_PLL1_SRC_SLIM (0x6 << 11)
+#define RT5677_MCLK_SRC_MASK (0x1 << 10)
+#define RT5677_MCLK_SRC_SFT 10
+#define RT5677_MCLK1_SRC (0x0 << 10)
+#define RT5677_MCLK2_SRC (0x1 << 10)
+#define RT5677_PLL1_PD_MASK (0x1 << 8)
+#define RT5677_PLL1_PD_SFT 8
+#define RT5677_PLL1_PD_1 (0x0 << 8)
+#define RT5677_PLL1_PD_2 (0x1 << 8)
+#define RT5677_DAC_OSR_MASK (0x3 << 6)
+#define RT5677_DAC_OSR_SFT 6
+#define RT5677_DAC_OSR_128 (0x0 << 6)
+#define RT5677_DAC_OSR_64 (0x1 << 6)
+#define RT5677_DAC_OSR_32 (0x2 << 6)
+#define RT5677_ADC_OSR_MASK (0x3 << 4)
+#define RT5677_ADC_OSR_SFT 4
+#define RT5677_ADC_OSR_128 (0x0 << 4)
+#define RT5677_ADC_OSR_64 (0x1 << 4)
+#define RT5677_ADC_OSR_32 (0x2 << 4)
+
+/* Global Clock Control 2 (0x81) */
+#define RT5677_PLL2_PR_SRC_MASK (0x1 << 15)
+#define RT5677_PLL2_PR_SRC_SFT 15
+#define RT5677_PLL2_PR_SRC_MCLK1 (0x0 << 15)
+#define RT5677_PLL2_PR_SRC_MCLK2 (0x1 << 15)
+#define RT5677_PLL2_SRC_MASK (0x7 << 12)
+#define RT5677_PLL2_SRC_SFT 12
+#define RT5677_PLL2_SRC_MCLK (0x0 << 12)
+#define RT5677_PLL2_SRC_BCLK1 (0x1 << 12)
+#define RT5677_PLL2_SRC_BCLK2 (0x2 << 12)
+#define RT5677_PLL2_SRC_BCLK3 (0x3 << 12)
+#define RT5677_PLL2_SRC_BCLK4 (0x4 << 12)
+#define RT5677_PLL2_SRC_RCCLK (0x5 << 12)
+#define RT5677_PLL2_SRC_SLIM (0x6 << 12)
+#define RT5677_DSP_ASRC_O_SRC (0x3 << 10)
+#define RT5677_DSP_ASRC_O_SRC_SFT 10
+#define RT5677_DSP_ASRC_O_MCLK (0x0 << 10)
+#define RT5677_DSP_ASRC_O_PLL1 (0x1 << 10)
+#define RT5677_DSP_ASRC_O_SLIM (0x2 << 10)
+#define RT5677_DSP_ASRC_O_RCCLK (0x3 << 10)
+#define RT5677_DSP_ASRC_I_SRC (0x3 << 8)
+#define RT5677_DSP_ASRC_I_SRC_SFT 8
+#define RT5677_DSP_ASRC_I_MCLK (0x0 << 8)
+#define RT5677_DSP_ASRC_I_PLL1 (0x1 << 8)
+#define RT5677_DSP_ASRC_I_SLIM (0x2 << 8)
+#define RT5677_DSP_ASRC_I_RCCLK (0x3 << 8)
+#define RT5677_DSP_CLK_SRC_MASK (0x1 << 7)
+#define RT5677_DSP_CLK_SRC_SFT 7
+#define RT5677_DSP_CLK_SRC_PLL2 (0x0 << 7)
+#define RT5677_DSP_CLK_SRC_BYPASS (0x1 << 7)
+
+/* ASRC Control 3 (0x85) */
+#define RT5677_DA_STO_CLK_SEL_MASK (0xf << 12)
+#define RT5677_DA_STO_CLK_SEL_SFT 12
+#define RT5677_DA_MONO2L_CLK_SEL_MASK (0xf << 4)
+#define RT5677_DA_MONO2L_CLK_SEL_SFT 4
+#define RT5677_DA_MONO2R_CLK_SEL_MASK (0xf << 0)
+#define RT5677_DA_MONO2R_CLK_SEL_SFT 0
+
+/* ASRC Control 4 (0x86) */
+#define RT5677_DA_MONO3L_CLK_SEL_MASK (0xf << 12)
+#define RT5677_DA_MONO3L_CLK_SEL_SFT 12
+#define RT5677_DA_MONO3R_CLK_SEL_MASK (0xf << 8)
+#define RT5677_DA_MONO3R_CLK_SEL_SFT 8
+#define RT5677_DA_MONO4L_CLK_SEL_MASK (0xf << 4)
+#define RT5677_DA_MONO4L_CLK_SEL_SFT 4
+#define RT5677_DA_MONO4R_CLK_SEL_MASK (0xf << 0)
+#define RT5677_DA_MONO4R_CLK_SEL_SFT 0
+
+/* ASRC Control 5 (0x87) */
+#define RT5677_AD_STO1_CLK_SEL_MASK (0xf << 12)
+#define RT5677_AD_STO1_CLK_SEL_SFT 12
+#define RT5677_AD_STO2_CLK_SEL_MASK (0xf << 8)
+#define RT5677_AD_STO2_CLK_SEL_SFT 8
+#define RT5677_AD_STO3_CLK_SEL_MASK (0xf << 4)
+#define RT5677_AD_STO3_CLK_SEL_SFT 4
+#define RT5677_AD_STO4_CLK_SEL_MASK (0xf << 0)
+#define RT5677_AD_STO4_CLK_SEL_SFT 0
+
+/* ASRC Control 6 (0x88) */
+#define RT5677_AD_MONOL_CLK_SEL_MASK (0xf << 12)
+#define RT5677_AD_MONOL_CLK_SEL_SFT 12
+#define RT5677_AD_MONOR_CLK_SEL_MASK (0xf << 8)
+#define RT5677_AD_MONOR_CLK_SEL_SFT 8
+
+/* ASRC Control 7 (0x89) */
+#define RT5677_DSP_OB_0_3_CLK_SEL_MASK (0xf << 12)
+#define RT5677_DSP_OB_0_3_CLK_SEL_SFT 12
+#define RT5677_DSP_OB_4_7_CLK_SEL_MASK (0xf << 8)
+#define RT5677_DSP_OB_4_7_CLK_SEL_SFT 8
+
+/* VAD Function Control 4 (0x9f) */
+#define RT5677_VAD_SRC_MASK (0x7 << 8)
+#define RT5677_VAD_SRC_SFT 8
+
+/* DSP InBound Control (0xa3) */
+#define RT5677_IB01_SRC_MASK (0x7 << 12)
+#define RT5677_IB01_SRC_SFT 12
+#define RT5677_IB23_SRC_MASK (0x7 << 8)
+#define RT5677_IB23_SRC_SFT 8
+#define RT5677_IB45_SRC_MASK (0x7 << 4)
+#define RT5677_IB45_SRC_SFT 4
+#define RT5677_IB6_SRC_MASK (0x7 << 0)
+#define RT5677_IB6_SRC_SFT 0
+
+/* DSP InBound Control (0xa4) */
+#define RT5677_IB7_SRC_MASK (0x7 << 12)
+#define RT5677_IB7_SRC_SFT 12
+#define RT5677_IB8_SRC_MASK (0x7 << 8)
+#define RT5677_IB8_SRC_SFT 8
+#define RT5677_IB9_SRC_MASK (0x7 << 4)
+#define RT5677_IB9_SRC_SFT 4
+
+/* DSP In/OutBound Control (0xa5) */
+#define RT5677_SEL_SRC_OB23 (0x1 << 4)
+#define RT5677_SEL_SRC_OB23_SFT 4
+#define RT5677_SEL_SRC_OB01 (0x1 << 3)
+#define RT5677_SEL_SRC_OB01_SFT 3
+#define RT5677_SEL_SRC_IB45 (0x1 << 2)
+#define RT5677_SEL_SRC_IB45_SFT 2
+#define RT5677_SEL_SRC_IB23 (0x1 << 1)
+#define RT5677_SEL_SRC_IB23_SFT 1
+#define RT5677_SEL_SRC_IB01 (0x1 << 0)
+#define RT5677_SEL_SRC_IB01_SFT 0
+
+/* Jack Detect Control 1 (0xb5) */
+#define RT5677_SEL_GPIO_JD1_MASK (0x3 << 14)
+#define RT5677_SEL_GPIO_JD1_SFT 14
+#define RT5677_SEL_GPIO_JD2_MASK (0x3 << 12)
+#define RT5677_SEL_GPIO_JD2_SFT 12
+#define RT5677_SEL_GPIO_JD3_MASK (0x3 << 10)
+#define RT5677_SEL_GPIO_JD3_SFT 10
+
+/* IRQ Control 1 (0xbd) */
+#define RT5677_STA_GPIO_JD1 (0x1 << 15)
+#define RT5677_STA_GPIO_JD1_SFT 15
+#define RT5677_EN_IRQ_GPIO_JD1 (0x1 << 14)
+#define RT5677_EN_IRQ_GPIO_JD1_SFT 14
+#define RT5677_EN_GPIO_JD1_STICKY (0x1 << 13)
+#define RT5677_EN_GPIO_JD1_STICKY_SFT 13
+#define RT5677_INV_GPIO_JD1 (0x1 << 12)
+#define RT5677_INV_GPIO_JD1_SFT 12
+#define RT5677_STA_GPIO_JD2 (0x1 << 11)
+#define RT5677_STA_GPIO_JD2_SFT 11
+#define RT5677_EN_IRQ_GPIO_JD2 (0x1 << 10)
+#define RT5677_EN_IRQ_GPIO_JD2_SFT 10
+#define RT5677_EN_GPIO_JD2_STICKY (0x1 << 9)
+#define RT5677_EN_GPIO_JD2_STICKY_SFT 9
+#define RT5677_INV_GPIO_JD2 (0x1 << 8)
+#define RT5677_INV_GPIO_JD2_SFT 8
+#define RT5677_STA_MICBIAS1_OVCD (0x1 << 7)
+#define RT5677_STA_MICBIAS1_OVCD_SFT 7
+#define RT5677_EN_IRQ_MICBIAS1_OVCD (0x1 << 6)
+#define RT5677_EN_IRQ_MICBIAS1_OVCD_SFT 6
+#define RT5677_EN_MICBIAS1_OVCD_STICKY (0x1 << 5)
+#define RT5677_EN_MICBIAS1_OVCD_STICKY_SFT 5
+#define RT5677_INV_MICBIAS1_OVCD (0x1 << 4)
+#define RT5677_INV_MICBIAS1_OVCD_SFT 4
+#define RT5677_STA_GPIO_JD3 (0x1 << 3)
+#define RT5677_STA_GPIO_JD3_SFT 3
+#define RT5677_EN_IRQ_GPIO_JD3 (0x1 << 2)
+#define RT5677_EN_IRQ_GPIO_JD3_SFT 2
+#define RT5677_EN_GPIO_JD3_STICKY (0x1 << 1)
+#define RT5677_EN_GPIO_JD3_STICKY_SFT 1
+#define RT5677_INV_GPIO_JD3 (0x1 << 0)
+#define RT5677_INV_GPIO_JD3_SFT 0
+
+/* GPIO status (0xbf) */
+#define RT5677_GPIO6_STATUS_MASK (0x1 << 5)
+#define RT5677_GPIO6_STATUS_SFT 5
+#define RT5677_GPIO5_STATUS_MASK (0x1 << 4)
+#define RT5677_GPIO5_STATUS_SFT 4
+#define RT5677_GPIO4_STATUS_MASK (0x1 << 3)
+#define RT5677_GPIO4_STATUS_SFT 3
+#define RT5677_GPIO3_STATUS_MASK (0x1 << 2)
+#define RT5677_GPIO3_STATUS_SFT 2
+#define RT5677_GPIO2_STATUS_MASK (0x1 << 1)
+#define RT5677_GPIO2_STATUS_SFT 1
+#define RT5677_GPIO1_STATUS_MASK (0x1 << 0)
+#define RT5677_GPIO1_STATUS_SFT 0
+
+/* GPIO Control 1 (0xc0) */
+#define RT5677_GPIO1_PIN_MASK (0x1 << 15)
+#define RT5677_GPIO1_PIN_SFT 15
+#define RT5677_GPIO1_PIN_GPIO1 (0x0 << 15)
+#define RT5677_GPIO1_PIN_IRQ (0x1 << 15)
+#define RT5677_IPTV_MODE_MASK (0x1 << 14)
+#define RT5677_IPTV_MODE_SFT 14
+#define RT5677_IPTV_MODE_GPIO (0x0 << 14)
+#define RT5677_IPTV_MODE_IPTV (0x1 << 14)
+#define RT5677_FUNC_MODE_MASK (0x1 << 13)
+#define RT5677_FUNC_MODE_SFT 13
+#define RT5677_FUNC_MODE_DMIC_GPIO (0x0 << 13)
+#define RT5677_FUNC_MODE_JTAG (0x1 << 13)
+
+/* GPIO Control 2 (0xc1) */
+#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)
+
+/* Virtual DSP Mixer Control (0xf7 0xf8 0xf9) */
+#define RT5677_DSP_IB_01_H (0x1 << 15)
+#define RT5677_DSP_IB_01_H_SFT 15
+#define RT5677_DSP_IB_23_H (0x1 << 14)
+#define RT5677_DSP_IB_23_H_SFT 14
+#define RT5677_DSP_IB_45_H (0x1 << 13)
+#define RT5677_DSP_IB_45_H_SFT 13
+#define RT5677_DSP_IB_6_H (0x1 << 12)
+#define RT5677_DSP_IB_6_H_SFT 12
+#define RT5677_DSP_IB_7_H (0x1 << 11)
+#define RT5677_DSP_IB_7_H_SFT 11
+#define RT5677_DSP_IB_8_H (0x1 << 10)
+#define RT5677_DSP_IB_8_H_SFT 10
+#define RT5677_DSP_IB_9_H (0x1 << 9)
+#define RT5677_DSP_IB_9_H_SFT 9
+#define RT5677_DSP_IB_01_L (0x1 << 7)
+#define RT5677_DSP_IB_01_L_SFT 7
+#define RT5677_DSP_IB_23_L (0x1 << 6)
+#define RT5677_DSP_IB_23_L_SFT 6
+#define RT5677_DSP_IB_45_L (0x1 << 5)
+#define RT5677_DSP_IB_45_L_SFT 5
+#define RT5677_DSP_IB_6_L (0x1 << 4)
+#define RT5677_DSP_IB_6_L_SFT 4
+#define RT5677_DSP_IB_7_L (0x1 << 3)
+#define RT5677_DSP_IB_7_L_SFT 3
+#define RT5677_DSP_IB_8_L (0x1 << 2)
+#define RT5677_DSP_IB_8_L_SFT 2
+#define RT5677_DSP_IB_9_L (0x1 << 1)
+#define RT5677_DSP_IB_9_L_SFT 1
+
+/* General Control2 (0xfc)*/
+#define RT5677_GPIO5_FUNC_MASK (0x1 << 9)
+#define RT5677_GPIO5_FUNC_GPIO (0x0 << 9)
+#define RT5677_GPIO5_FUNC_DMIC (0x1 << 9)
+
+#define RT5677_FIRMWARE1 "rt5677_dsp_fw1.bin"
+#define RT5677_FIRMWARE2 "rt5677_dsp_fw2.bin"
+
+/* System Clock Source */
+enum {
+ RT5677_SCLK_S_MCLK,
+ RT5677_SCLK_S_PLL1,
+ RT5677_SCLK_S_RCCLK,
+};
+
+/* PLL1 Source */
+enum {
+ RT5677_PLL1_S_MCLK,
+ RT5677_PLL1_S_BCLK1,
+ RT5677_PLL1_S_BCLK2,
+ RT5677_PLL1_S_BCLK3,
+ RT5677_PLL1_S_BCLK4,
+};
+
+enum {
+ RT5677_AIF1,
+ RT5677_AIF2,
+ RT5677_AIF3,
+ RT5677_AIF4,
+ RT5677_AIF5,
+ RT5677_AIFS,
+};
+
+enum {
+ RT5677_GPIO1,
+ RT5677_GPIO2,
+ RT5677_GPIO3,
+ RT5677_GPIO4,
+ RT5677_GPIO5,
+ RT5677_GPIO6,
+ RT5677_GPIO_NUM,
+};
+
+enum {
+ RT5677_IRQ_JD1,
+ RT5677_IRQ_JD2,
+ RT5677_IRQ_JD3,
+};
+
+enum rt5677_type {
+ RT5677,
+ RT5676,
+};
+
+/* 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),
+};
+
+struct rt5677_priv {
+ struct snd_soc_codec *codec;
+ struct rt5677_platform_data pdata;
+ struct regmap *regmap, *regmap_physical;
+ const struct firmware *fw1, *fw2;
+ struct mutex dsp_cmd_lock, dsp_pri_lock;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5677_AIFS];
+ int bclk[RT5677_AIFS];
+ int master[RT5677_AIFS];
+ int pll_src;
+ int pll_in;
+ int pll_out;
+ int pow_ldo2; /* POW_LDO2 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 is_dsp_mode;
+ bool is_vref_slow;
+};
+
+int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec,
+ unsigned int filter_mask, unsigned int clk_src);
+
+#endif /* __RT5677_H__ */
diff --git a/kernel/sound/soc/codecs/sgtl5000.c b/kernel/sound/soc/codecs/sgtl5000.c
new file mode 100644
index 000000000..3593a1496
--- /dev/null
+++ b/kernel/sound/soc/codecs/sgtl5000.c
@@ -0,0 +1,1578 @@
+/*
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/log2.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>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "sgtl5000.h"
+
+#define SGTL5000_DAP_REG_OFFSET 0x0100
+#define SGTL5000_MAX_REG_OFFSET 0x013A
+
+/* 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 },
+ { SGTL5000_CHIP_DAC_VOL, 0x3c3c },
+ { SGTL5000_CHIP_PAD_STRENGTH, 0x015f },
+ { SGTL5000_CHIP_ANA_ADC_CTRL, 0x0000 },
+ { SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 },
+ { SGTL5000_CHIP_ANA_CTRL, 0x0111 },
+ { SGTL5000_CHIP_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 },
+ { SGTL5000_CHIP_SHORT_CTRL, 0x0000 },
+ { SGTL5000_CHIP_ANA_TEST2, 0x0000 },
+ { SGTL5000_DAP_CTRL, 0x0000 },
+ { SGTL5000_DAP_PEQ, 0x0000 },
+ { SGTL5000_DAP_BASS_ENHANCE, 0x0040 },
+ { SGTL5000_DAP_BASS_ENHANCE_CTRL, 0x051f },
+ { SGTL5000_DAP_AUDIO_EQ, 0x0000 },
+ { SGTL5000_DAP_SURROUND, 0x0040 },
+ { SGTL5000_DAP_EQ_BASS_BAND0, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND1, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND2, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND3, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND4, 0x002f },
+ { SGTL5000_DAP_MAIN_CHAN, 0x8000 },
+ { SGTL5000_DAP_MIX_CHAN, 0x0000 },
+ { SGTL5000_DAP_AVC_CTRL, 0x0510 },
+ { SGTL5000_DAP_AVC_THRESHOLD, 0x1473 },
+ { SGTL5000_DAP_AVC_ATTACK, 0x0028 },
+ { SGTL5000_DAP_AVC_DECAY, 0x0050 },
+};
+
+/* regulator supplies for sgtl5000, VDDD is an optional external supply */
+enum sgtl5000_regulator_supplies {
+ VDDA,
+ VDDIO,
+ VDDD,
+ SGTL5000_SUPPLY_NUM
+};
+
+/* vddd is optional supply */
+static const char *supply_names[SGTL5000_SUPPLY_NUM] = {
+ "VDDA",
+ "VDDIO",
+ "VDDD"
+};
+
+#define LDO_CONSUMER_NAME "VDDD_LDO"
+#define LDO_VOLTAGE 1200000
+
+static struct regulator_consumer_supply ldo_consumer[] = {
+ REGULATOR_SUPPLY(LDO_CONSUMER_NAME, NULL),
+};
+
+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],
+};
+
+/*
+ * 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 sgtl5000_micbias_resistor {
+ SGTL5000_MICBIAS_OFF = 0,
+ SGTL5000_MICBIAS_2K = 2,
+ SGTL5000_MICBIAS_4K = 4,
+ SGTL5000_MICBIAS_8K = 8,
+};
+
+/* sgtl5000 private structure in codec */
+struct sgtl5000_priv {
+ int sysclk; /* sysclk rate */
+ int master; /* i2s master or not */
+ int fmt; /* i2s data format */
+ struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM];
+ struct ldo_regulator *ldo;
+ struct regmap *regmap;
+ struct clk *mclk;
+ int revision;
+ u8 micbias_resistor;
+ u8 micbias_voltage;
+};
+
+/*
+ * mic_bias power on/off share the same register bits with
+ * output impedance of mic bias, when power on mic bias, we
+ * need reclaim it to impedance value.
+ * 0x0 = Powered off
+ * 0x1 = 2Kohm
+ * 0x2 = 4Kohm
+ * 0x3 = 8Kohm
+ */
+static int mic_bias_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* change mic bias resistor */
+ snd_soc_update_bits(codec, 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,
+ 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)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ const u32 mask = SGTL5000_DAC_POWERUP | SGTL5000_ADC_POWERUP;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
+ 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);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* input sources for ADC */
+static const char *adc_mux_text[] = {
+ "MIC_IN", "LINE_IN"
+};
+
+static SOC_ENUM_SINGLE_DECL(adc_enum,
+ SGTL5000_CHIP_ANA_CTRL, 2,
+ adc_mux_text);
+
+static const struct snd_kcontrol_new adc_mux =
+SOC_DAPM_ENUM("Capture Mux", adc_enum);
+
+/* input sources for DAC */
+static const char *dac_mux_text[] = {
+ "DAC", "LINE_IN"
+};
+
+static SOC_ENUM_SINGLE_DECL(dac_enum,
+ SGTL5000_CHIP_ANA_CTRL, 6,
+ dac_mux_text);
+
+static const struct snd_kcontrol_new dac_mux =
+SOC_DAPM_ENUM("Headphone Mux", dac_enum);
+
+static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("LINE_IN"),
+ SND_SOC_DAPM_INPUT("MIC_IN"),
+
+ SND_SOC_DAPM_OUTPUT("HP_OUT"),
+ SND_SOC_DAPM_OUTPUT("LINE_OUT"),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0,
+ mic_bias_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_PGA("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0),
+ 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),
+
+ /* aif for i2s input */
+ SND_SOC_DAPM_AIF_IN("AIFIN", "Playback",
+ 0, SGTL5000_CHIP_DIG_POWER,
+ 0, 0),
+
+ /* aif for i2s output */
+ SND_SOC_DAPM_AIF_OUT("AIFOUT", "Capture",
+ 0, SGTL5000_CHIP_DIG_POWER,
+ 1, 0),
+
+ SND_SOC_DAPM_ADC("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),
+};
+
+/* routes for sgtl5000 */
+static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = {
+ {"Capture Mux", "LINE_IN", "LINE_IN"}, /* line_in --> adc_mux */
+ {"Capture Mux", "MIC_IN", "MIC_IN"}, /* mic_in --> adc_mux */
+
+ {"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */
+ {"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */
+
+ {"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */
+ {"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */
+ {"LO", NULL, "DAC"}, /* dac --> line_out */
+
+ {"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */
+ {"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */
+
+ {"LINE_OUT", NULL, "LO"},
+ {"HP_OUT", NULL, "HP"},
+};
+
+/* custom function to fetch info of PCM playback volume */
+static int dac_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xfc - 0x3c;
+ return 0;
+}
+
+/*
+ * custom function to get of PCM playback volume
+ *
+ * dac volume register
+ * 15-------------8-7--------------0
+ * | R channel vol | L channel vol |
+ * -------------------------------
+ *
+ * PCM volume with 0.5017 dB steps from 0 to -90 dB
+ *
+ * register values map to dB
+ * 0x3B and less = Reserved
+ * 0x3C = 0 dB
+ * 0x3D = -0.5 dB
+ * 0xF0 = -90 dB
+ * 0xFC and greater = Muted
+ *
+ * register value map to userspace value
+ *
+ * register value 0x3c(0dB) 0xf0(-90dB)0xfc
+ * ------------------------------
+ * userspace value 0xc0 0
+ */
+static int dac_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int reg;
+ int l;
+ int r;
+
+ reg = snd_soc_read(codec, SGTL5000_CHIP_DAC_VOL);
+
+ /* get left channel volume */
+ l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) >> SGTL5000_DAC_VOL_LEFT_SHIFT;
+
+ /* get right channel volume */
+ r = (reg & SGTL5000_DAC_VOL_RIGHT_MASK) >> SGTL5000_DAC_VOL_RIGHT_SHIFT;
+
+ /* make sure value fall in (0x3c,0xfc) */
+ l = clamp(l, 0x3c, 0xfc);
+ r = clamp(r, 0x3c, 0xfc);
+
+ /* invert it and map to userspace value */
+ l = 0xfc - l;
+ r = 0xfc - r;
+
+ ucontrol->value.integer.value[0] = l;
+ ucontrol->value.integer.value[1] = r;
+
+ return 0;
+}
+
+/*
+ * custom function to put of PCM playback volume
+ *
+ * dac volume register
+ * 15-------------8-7--------------0
+ * | R channel vol | L channel vol |
+ * -------------------------------
+ *
+ * PCM volume with 0.5017 dB steps from 0 to -90 dB
+ *
+ * register values map to dB
+ * 0x3B and less = Reserved
+ * 0x3C = 0 dB
+ * 0x3D = -0.5 dB
+ * 0xF0 = -90 dB
+ * 0xFC and greater = Muted
+ *
+ * userspace value map to register value
+ *
+ * userspace value 0xc0 0
+ * ------------------------------
+ * register value 0x3c(0dB) 0xf0(-90dB)0xfc
+ */
+static int dac_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int reg;
+ int l;
+ int r;
+
+ l = ucontrol->value.integer.value[0];
+ r = ucontrol->value.integer.value[1];
+
+ /* make sure userspace volume fall in (0, 0xfc-0x3c) */
+ l = clamp(l, 0, 0xfc - 0x3c);
+ r = clamp(r, 0, 0xfc - 0x3c);
+
+ /* invert it, get the value can be set to register */
+ l = 0xfc - l;
+ r = 0xfc - r;
+
+ /* shift to get the register value */
+ reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT |
+ r << SGTL5000_DAC_VOL_RIGHT_SHIFT;
+
+ snd_soc_write(codec, SGTL5000_CHIP_DAC_VOL, reg);
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0);
+
+/* tlv for mic gain, 0db 20db 30db 40db */
+static const unsigned int mic_gain_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0),
+};
+
+/* tlv for hp volume, -51.5db to 12.0db, step .5db */
+static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0);
+
+static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
+ /* SOC_DOUBLE_S8_TLV with invert */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = dac_info_volsw,
+ .get = dac_get_volsw,
+ .put = dac_put_volsw,
+ },
+
+ SOC_DOUBLE("Capture Volume", SGTL5000_CHIP_ANA_ADC_CTRL, 0, 4, 0xf, 0),
+ SOC_SINGLE_TLV("Capture Attenuate Switch (-6dB)",
+ SGTL5000_CHIP_ANA_ADC_CTRL,
+ 8, 1, 0, capture_6db_attenuate),
+ SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0),
+
+ SOC_DOUBLE_TLV("Headphone Playback Volume",
+ SGTL5000_CHIP_ANA_HP_CTRL,
+ 0, 8,
+ 0x7f, 1,
+ headphone_volume),
+ 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),
+};
+
+/* mute the codec used by alsa core */
+static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 adcdac_ctrl = SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT;
+
+ snd_soc_update_bits(codec, SGTL5000_CHIP_ADCDAC_CTRL,
+ adcdac_ctrl, mute ? adcdac_ctrl : 0);
+
+ return 0;
+}
+
+/* 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);
+ u16 i2sctl = 0;
+
+ sgtl5000->master = 0;
+ /*
+ * i2s clock and frame master setting.
+ * ONLY support:
+ * - clock and frame slave,
+ * - clock and frame master
+ */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ i2sctl |= SGTL5000_I2S_MASTER;
+ sgtl5000->master = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* setting i2s data format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ i2sctl |= SGTL5000_I2S_MODE_PCM << SGTL5000_I2S_MODE_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ i2sctl |= SGTL5000_I2S_MODE_PCM << SGTL5000_I2S_MODE_SHIFT;
+ i2sctl |= SGTL5000_I2S_LRALIGN;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ i2sctl |= SGTL5000_I2S_MODE_I2S_LJ << SGTL5000_I2S_MODE_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ i2sctl |= SGTL5000_I2S_MODE_RJ << SGTL5000_I2S_MODE_SHIFT;
+ i2sctl |= SGTL5000_I2S_LRPOL;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ i2sctl |= SGTL5000_I2S_MODE_I2S_LJ << SGTL5000_I2S_MODE_SHIFT;
+ i2sctl |= SGTL5000_I2S_LRALIGN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sgtl5000->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ /* Clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ i2sctl |= SGTL5000_I2S_SCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, SGTL5000_CHIP_I2S_CTRL, i2sctl);
+
+ return 0;
+}
+
+/* set codec sysclk */
+static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case SGTL5000_SYSCLK:
+ sgtl5000->sysclk = freq;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * set clock according to i2s frame clock,
+ * sgtl5000 provides 2 clock sources:
+ * 1. sys_mclk: sample freq can only be configured to
+ * 1/256, 1/384, 1/512 of sys_mclk.
+ * 2. pll: can derive any audio clocks.
+ *
+ * clock setting rules:
+ * 1. in slave mode, only sys_mclk can be used
+ * 2. as constraint by sys_mclk, sample freq should be set to 32 kHz, 44.1 kHz
+ * and above.
+ * 3. usage of sys_mclk is preferred over pll to save power.
+ */
+static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
+{
+ struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ int clk_ctl = 0;
+ int sys_fs; /* sample freq */
+
+ /*
+ * sample freq should be divided by frame clock,
+ * if frame clock is lower than 44.1 kHz, sample freq should be set to
+ * 32 kHz or 44.1 kHz.
+ */
+ switch (frame_rate) {
+ case 8000:
+ case 16000:
+ sys_fs = 32000;
+ break;
+ case 11025:
+ case 22050:
+ sys_fs = 44100;
+ break;
+ default:
+ sys_fs = frame_rate;
+ break;
+ }
+
+ /* set divided factor of frame clock */
+ switch (sys_fs / frame_rate) {
+ case 4:
+ clk_ctl |= SGTL5000_RATE_MODE_DIV_4 << SGTL5000_RATE_MODE_SHIFT;
+ break;
+ case 2:
+ clk_ctl |= SGTL5000_RATE_MODE_DIV_2 << SGTL5000_RATE_MODE_SHIFT;
+ break;
+ case 1:
+ clk_ctl |= SGTL5000_RATE_MODE_DIV_1 << SGTL5000_RATE_MODE_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set the sys_fs according to frame rate */
+ switch (sys_fs) {
+ case 32000:
+ clk_ctl |= SGTL5000_SYS_FS_32k << SGTL5000_SYS_FS_SHIFT;
+ break;
+ case 44100:
+ clk_ctl |= SGTL5000_SYS_FS_44_1k << SGTL5000_SYS_FS_SHIFT;
+ break;
+ case 48000:
+ clk_ctl |= SGTL5000_SYS_FS_48k << SGTL5000_SYS_FS_SHIFT;
+ break;
+ case 96000:
+ clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT;
+ break;
+ default:
+ dev_err(codec->dev, "frame rate %d not supported\n",
+ frame_rate);
+ return -EINVAL;
+ }
+
+ /*
+ * calculate the divider of mclk/sample_freq,
+ * factor of freq = 96 kHz can only be 256, since mclk is in the range
+ * of 8 MHz - 27 MHz
+ */
+ switch (sgtl5000->sysclk / frame_rate) {
+ case 256:
+ clk_ctl |= SGTL5000_MCLK_FREQ_256FS <<
+ SGTL5000_MCLK_FREQ_SHIFT;
+ break;
+ case 384:
+ clk_ctl |= SGTL5000_MCLK_FREQ_384FS <<
+ SGTL5000_MCLK_FREQ_SHIFT;
+ break;
+ case 512:
+ clk_ctl |= SGTL5000_MCLK_FREQ_512FS <<
+ SGTL5000_MCLK_FREQ_SHIFT;
+ break;
+ default:
+ /* if mclk does not satisfy the divider, use pll */
+ if (sgtl5000->master) {
+ clk_ctl |= SGTL5000_MCLK_FREQ_PLL <<
+ SGTL5000_MCLK_FREQ_SHIFT;
+ } else {
+ dev_err(codec->dev,
+ "PLL not supported in slave mode\n");
+ dev_err(codec->dev, "%d ratio is not supported. "
+ "SYS_MCLK needs to be 256, 384 or 512 * fs\n",
+ sgtl5000->sysclk / frame_rate);
+ return -EINVAL;
+ }
+ }
+
+ /* if using pll, please check manual 6.4.2 for detail */
+ if ((clk_ctl & SGTL5000_MCLK_FREQ_MASK) == SGTL5000_MCLK_FREQ_PLL) {
+ u64 out, t;
+ int div2;
+ int pll_ctl;
+ unsigned int in, int_div, frac_div;
+
+ if (sgtl5000->sysclk > 17000000) {
+ div2 = 1;
+ in = sgtl5000->sysclk / 2;
+ } else {
+ div2 = 0;
+ in = sgtl5000->sysclk;
+ }
+ if (sys_fs == 44100)
+ out = 180633600;
+ else
+ out = 196608000;
+ t = do_div(out, in);
+ int_div = out;
+ t *= 2048;
+ do_div(t, in);
+ frac_div = t;
+ pll_ctl = int_div << SGTL5000_PLL_INT_DIV_SHIFT |
+ frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT;
+
+ snd_soc_write(codec, SGTL5000_CHIP_PLL_CTRL, pll_ctl);
+ if (div2)
+ snd_soc_update_bits(codec,
+ SGTL5000_CHIP_CLK_TOP_CTRL,
+ SGTL5000_INPUT_FREQ_DIV2,
+ SGTL5000_INPUT_FREQ_DIV2);
+ else
+ snd_soc_update_bits(codec,
+ SGTL5000_CHIP_CLK_TOP_CTRL,
+ SGTL5000_INPUT_FREQ_DIV2,
+ 0);
+
+ /* power up pll */
+ snd_soc_update_bits(codec, 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);
+ } else {
+ /* otherwise, clk_ctrl must be set before pll power down */
+ snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+
+ /* power down pll */
+ snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
+ 0);
+ }
+
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ * input: params_rate, params_fmt
+ */
+static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ int channels = params_channels(params);
+ int i2s_ctl = 0;
+ int stereo;
+ int ret;
+
+ /* sysclk should already set */
+ if (!sgtl5000->sysclk) {
+ dev_err(codec->dev, "%s: set sysclk first!\n", __func__);
+ return -EFAULT;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ stereo = SGTL5000_DAC_STEREO;
+ else
+ stereo = SGTL5000_ADC_STEREO;
+
+ /* set mono to save power */
+ snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, stereo,
+ channels == 1 ? 0 : stereo);
+
+ /* set codec clock base on lrclk */
+ ret = sgtl5000_set_clock(codec, params_rate(params));
+ if (ret)
+ return ret;
+
+ /* set i2s data format */
+ switch (params_width(params)) {
+ case 16:
+ if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J)
+ return -EINVAL;
+ i2s_ctl |= SGTL5000_I2S_DLEN_16 << SGTL5000_I2S_DLEN_SHIFT;
+ i2s_ctl |= SGTL5000_I2S_SCLKFREQ_32FS <<
+ SGTL5000_I2S_SCLKFREQ_SHIFT;
+ break;
+ case 20:
+ i2s_ctl |= SGTL5000_I2S_DLEN_20 << SGTL5000_I2S_DLEN_SHIFT;
+ i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+ SGTL5000_I2S_SCLKFREQ_SHIFT;
+ break;
+ case 24:
+ i2s_ctl |= SGTL5000_I2S_DLEN_24 << SGTL5000_I2S_DLEN_SHIFT;
+ i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+ SGTL5000_I2S_SCLKFREQ_SHIFT;
+ break;
+ case 32:
+ if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J)
+ return -EINVAL;
+ i2s_ctl |= SGTL5000_I2S_DLEN_32 << SGTL5000_I2S_DLEN_SHIFT;
+ i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+ SGTL5000_I2S_SCLKFREQ_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, 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:
+ * startup:
+ * off --> standby --> prepare --> on
+ * standby --> prepare --> on
+ *
+ * stop:
+ * on --> prepare --> standby
+ */
+static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ 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;
+ }
+ }
+
+ break;
+ case SND_SOC_BIAS_OFF:
+ regcache_cache_only(sgtl5000->regmap, true);
+ regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->supplies);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define SGTL5000_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops sgtl5000_ops = {
+ .hw_params = sgtl5000_pcm_hw_params,
+ .digital_mute = sgtl5000_digital_mute,
+ .set_fmt = sgtl5000_set_dai_fmt,
+ .set_sysclk = sgtl5000_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver sgtl5000_dai = {
+ .name = "sgtl5000",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ /*
+ * only support 8~48K + 96K,
+ * TODO modify hw_param to support more
+ */
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000,
+ .formats = SGTL5000_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000,
+ .formats = SGTL5000_FORMATS,
+ },
+ .ops = &sgtl5000_ops,
+ .symmetric_rates = 1,
+};
+
+static bool sgtl5000_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SGTL5000_CHIP_ID:
+ case SGTL5000_CHIP_ADCDAC_CTRL:
+ case SGTL5000_CHIP_ANA_STATUS:
+ return true;
+ }
+
+ return false;
+}
+
+static bool sgtl5000_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SGTL5000_CHIP_ID:
+ case SGTL5000_CHIP_DIG_POWER:
+ case SGTL5000_CHIP_CLK_CTRL:
+ case SGTL5000_CHIP_I2S_CTRL:
+ case SGTL5000_CHIP_SSS_CTRL:
+ case SGTL5000_CHIP_ADCDAC_CTRL:
+ case SGTL5000_CHIP_DAC_VOL:
+ case SGTL5000_CHIP_PAD_STRENGTH:
+ case SGTL5000_CHIP_ANA_ADC_CTRL:
+ case SGTL5000_CHIP_ANA_HP_CTRL:
+ case SGTL5000_CHIP_ANA_CTRL:
+ case SGTL5000_CHIP_LINREG_CTRL:
+ case SGTL5000_CHIP_REF_CTRL:
+ case SGTL5000_CHIP_MIC_CTRL:
+ case SGTL5000_CHIP_LINE_OUT_CTRL:
+ case SGTL5000_CHIP_LINE_OUT_VOL:
+ case SGTL5000_CHIP_ANA_POWER:
+ case SGTL5000_CHIP_PLL_CTRL:
+ case SGTL5000_CHIP_CLK_TOP_CTRL:
+ case SGTL5000_CHIP_ANA_STATUS:
+ case SGTL5000_CHIP_SHORT_CTRL:
+ case SGTL5000_CHIP_ANA_TEST2:
+ case SGTL5000_DAP_CTRL:
+ case SGTL5000_DAP_PEQ:
+ case SGTL5000_DAP_BASS_ENHANCE:
+ case SGTL5000_DAP_BASS_ENHANCE_CTRL:
+ case SGTL5000_DAP_AUDIO_EQ:
+ case SGTL5000_DAP_SURROUND:
+ case SGTL5000_DAP_FLT_COEF_ACCESS:
+ case SGTL5000_DAP_COEF_WR_B0_MSB:
+ case SGTL5000_DAP_COEF_WR_B0_LSB:
+ case SGTL5000_DAP_EQ_BASS_BAND0:
+ case SGTL5000_DAP_EQ_BASS_BAND1:
+ case SGTL5000_DAP_EQ_BASS_BAND2:
+ case SGTL5000_DAP_EQ_BASS_BAND3:
+ case SGTL5000_DAP_EQ_BASS_BAND4:
+ case SGTL5000_DAP_MAIN_CHAN:
+ case SGTL5000_DAP_MIX_CHAN:
+ case SGTL5000_DAP_AVC_CTRL:
+ case SGTL5000_DAP_AVC_THRESHOLD:
+ case SGTL5000_DAP_AVC_ATTACK:
+ case SGTL5000_DAP_AVC_DECAY:
+ case SGTL5000_DAP_COEF_WR_B1_MSB:
+ case SGTL5000_DAP_COEF_WR_B1_LSB:
+ case SGTL5000_DAP_COEF_WR_B2_MSB:
+ case SGTL5000_DAP_COEF_WR_B2_LSB:
+ case SGTL5000_DAP_COEF_WR_A1_MSB:
+ case SGTL5000_DAP_COEF_WR_A1_LSB:
+ case SGTL5000_DAP_COEF_WR_A2_MSB:
+ case SGTL5000_DAP_COEF_WR_A2_LSB:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/*
+ * sgtl5000 has 3 internal power supplies:
+ * 1. VAG, normally set to vdda/2
+ * 2. charge pump, set to different value
+ * according to voltage of vdda and vddio
+ * 3. line out VAG, normally set to vddio/2
+ *
+ * and should be set according to:
+ * 1. vddd provided by external or not
+ * 2. vdda and vddio voltage value. > 3.1v or not
+ * 3. chip revision >=0x11 or not. If >=0x11, not use external vddd.
+ */
+static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
+{
+ int vddd;
+ int vdda;
+ int vddio;
+ u16 ana_pwr;
+ u16 lreg_ctrl;
+ int vag;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+ vdda = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer);
+ vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO].consumer);
+ vddd = regulator_get_voltage(sgtl5000->supplies[VDDD].consumer);
+
+ 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");
+
+ return -EINVAL;
+ }
+
+ /* according to datasheet, maximum voltage of supplies */
+ if (vdda > 3600 || vddio > 3600 || vddd > 1980) {
+ dev_err(codec->dev,
+ "exceed max voltage vdda %dmV vddio %dmV vddd %dmV\n",
+ vdda, vddio, vddd);
+
+ return -EINVAL;
+ }
+
+ /* reset value */
+ ana_pwr = snd_soc_read(codec, 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);
+
+ if (vddio < 3100 && vdda < 3100) {
+ /* enable internal oscillator used for charge pump */
+ snd_soc_update_bits(codec, 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) {
+ ana_pwr &= ~SGTL5000_VDDC_CHRGPMP_POWERUP;
+ /* VDDC use VDDIO rail */
+ 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);
+
+ /*
+ * 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);
+
+ /*
+ * set ADC/DAC VAG to vdda / 2,
+ * should stay in range (0.8v, 1.575v)
+ */
+ vag = vdda / 2;
+ if (vag <= SGTL5000_ANA_GND_BASE)
+ vag = 0;
+ else if (vag >= SGTL5000_ANA_GND_BASE + SGTL5000_ANA_GND_STP *
+ (SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT))
+ vag = SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT;
+ else
+ vag = (vag - SGTL5000_ANA_GND_BASE) / SGTL5000_ANA_GND_STP;
+
+ snd_soc_update_bits(codec, 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 +
+ SGTL5000_LINE_OUT_GND_STP * SGTL5000_LINE_OUT_GND_MAX)
+ vag = SGTL5000_LINE_OUT_GND_MAX;
+ else
+ vag = (vag - SGTL5000_LINE_OUT_GND_BASE) /
+ SGTL5000_LINE_OUT_GND_STP;
+
+ snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
+ SGTL5000_LINE_OUT_CURRENT_MASK |
+ SGTL5000_LINE_OUT_GND_MASK,
+ 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;
+ }
+
+ sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
+
+ dev_info(codec->dev, "Using internal LDO instead of VDDD\n");
+ return 0;
+}
+
+static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
+{
+ int ret;
+ int i;
+ int external_vddd = 0;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct regulator *vddd;
+
+ 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;
+ }
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->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 0;
+
+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)
+{
+ int ret;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+ ret = sgtl5000_enable_regulators(codec);
+ if (ret)
+ return ret;
+
+ /* power up sgtl5000 */
+ ret = sgtl5000_set_power_regs(codec);
+ 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);
+
+ /* disable short cut detector */
+ snd_soc_write(codec, 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,
+ SGTL5000_ADC_EN | SGTL5000_DAC_EN);
+
+ /* enable dac volume ramp by default */
+ snd_soc_write(codec, 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);
+
+ snd_soc_write(codec, SGTL5000_CHIP_ANA_CTRL,
+ SGTL5000_HP_ZCD_EN |
+ SGTL5000_ADC_ZCD_EN);
+
+ snd_soc_update_bits(codec, 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);
+ /*
+ * disable DAP
+ * TODO:
+ * Enable DAP in kcontrol and dapm.
+ */
+ snd_soc_write(codec, SGTL5000_DAP_CTRL, 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)
+{
+ 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 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 regmap_config sgtl5000_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_stride = 2,
+
+ .max_register = SGTL5000_MAX_REG_OFFSET,
+ .volatile_reg = sgtl5000_volatile,
+ .readable_reg = sgtl5000_readable,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = sgtl5000_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(sgtl5000_reg_defaults),
+};
+
+/*
+ * Write all the default values from sgtl5000_reg_defaults[] array into the
+ * sgtl5000 registers, to make sure we always start with the sane registers
+ * values as stated in the datasheet.
+ *
+ * Since sgtl5000 does not have a reset line, nor a reset command in software,
+ * we follow this approach to guarantee we always start from the default values
+ * and avoid problems like, not being able to probe after an audio playback
+ * followed by a system reset or a 'reboot' command in Linux
+ */
+static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000)
+{
+ int i, ret, val, index;
+
+ for (i = 0; i < ARRAY_SIZE(sgtl5000_reg_defaults); i++) {
+ val = sgtl5000_reg_defaults[i].def;
+ index = sgtl5000_reg_defaults[i].reg;
+ ret = regmap_write(sgtl5000->regmap, index, val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sgtl5000_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct sgtl5000_priv *sgtl5000;
+ int ret, reg, rev;
+ struct device_node *np = client->dev.of_node;
+ u32 value;
+
+ sgtl5000 = devm_kzalloc(&client->dev, sizeof(*sgtl5000), GFP_KERNEL);
+ if (!sgtl5000)
+ return -ENOMEM;
+
+ 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;
+ }
+
+ 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 = clk_prepare_enable(sgtl5000->mclk);
+ if (ret)
+ return ret;
+
+ /* Need 8 clocks before I2C accesses */
+ udelay(1);
+
+ /* read chip information */
+ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, &reg);
+ if (ret)
+ goto disable_clk;
+
+ if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
+ SGTL5000_PARTID_PART_ID) {
+ dev_err(&client->dev,
+ "Device with ID register %x is not a sgtl5000\n", reg);
+ ret = -ENODEV;
+ goto disable_clk;
+ }
+
+ rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
+ dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev);
+ sgtl5000->revision = rev;
+
+ if (np) {
+ if (!of_property_read_u32(np,
+ "micbias-resistor-k-ohms", &value)) {
+ switch (value) {
+ case SGTL5000_MICBIAS_OFF:
+ sgtl5000->micbias_resistor = 0;
+ break;
+ case SGTL5000_MICBIAS_2K:
+ sgtl5000->micbias_resistor = 1;
+ break;
+ case SGTL5000_MICBIAS_4K:
+ sgtl5000->micbias_resistor = 2;
+ break;
+ case SGTL5000_MICBIAS_8K:
+ sgtl5000->micbias_resistor = 3;
+ break;
+ default:
+ sgtl5000->micbias_resistor = 2;
+ dev_err(&client->dev,
+ "Unsuitable MicBias resistor\n");
+ }
+ } else {
+ /* default is 4Kohms */
+ sgtl5000->micbias_resistor = 2;
+ }
+ if (!of_property_read_u32(np,
+ "micbias-voltage-m-volts", &value)) {
+ /* 1250mV => 0 */
+ /* steps of 250mV */
+ if ((value >= 1250) && (value <= 3000))
+ sgtl5000->micbias_voltage = (value / 250) - 5;
+ else {
+ sgtl5000->micbias_voltage = 0;
+ dev_err(&client->dev,
+ "Unsuitable MicBias resistor\n");
+ }
+ } else {
+ sgtl5000->micbias_voltage = 0;
+ }
+ }
+
+ i2c_set_clientdata(client, sgtl5000);
+
+ /* Ensure sgtl5000 will start with sane register values */
+ ret = sgtl5000_fill_defaults(sgtl5000);
+ if (ret)
+ goto disable_clk;
+
+ ret = snd_soc_register_codec(&client->dev,
+ &sgtl5000_driver, &sgtl5000_dai, 1);
+ if (ret)
+ goto disable_clk;
+
+ return 0;
+
+disable_clk:
+ clk_disable_unprepare(sgtl5000->mclk);
+ return ret;
+}
+
+static int sgtl5000_i2c_remove(struct i2c_client *client)
+{
+ struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+ clk_disable_unprepare(sgtl5000->mclk);
+ return 0;
+}
+
+static const struct i2c_device_id sgtl5000_id[] = {
+ {"sgtl5000", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, sgtl5000_id);
+
+static const struct of_device_id sgtl5000_dt_ids[] = {
+ { .compatible = "fsl,sgtl5000", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sgtl5000_dt_ids);
+
+static struct i2c_driver sgtl5000_i2c_driver = {
+ .driver = {
+ .name = "sgtl5000",
+ .owner = THIS_MODULE,
+ .of_match_table = sgtl5000_dt_ids,
+ },
+ .probe = sgtl5000_i2c_probe,
+ .remove = sgtl5000_i2c_remove,
+ .id_table = sgtl5000_id,
+};
+
+module_i2c_driver(sgtl5000_i2c_driver);
+
+MODULE_DESCRIPTION("Freescale SGTL5000 ALSA SoC Codec Driver");
+MODULE_AUTHOR("Zeng Zhaoming <zengzm.kernel@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/sgtl5000.h b/kernel/sound/soc/codecs/sgtl5000.h
new file mode 100644
index 000000000..bd7a344bf
--- /dev/null
+++ b/kernel/sound/soc/codecs/sgtl5000.h
@@ -0,0 +1,400 @@
+/*
+ * 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
+#define _SGTL5000_H
+
+/*
+ * Registers addresses
+ */
+#define SGTL5000_CHIP_ID 0x0000
+#define SGTL5000_CHIP_DIG_POWER 0x0002
+#define SGTL5000_CHIP_CLK_CTRL 0x0004
+#define SGTL5000_CHIP_I2S_CTRL 0x0006
+#define SGTL5000_CHIP_SSS_CTRL 0x000a
+#define SGTL5000_CHIP_ADCDAC_CTRL 0x000e
+#define SGTL5000_CHIP_DAC_VOL 0x0010
+#define SGTL5000_CHIP_PAD_STRENGTH 0x0014
+#define SGTL5000_CHIP_ANA_ADC_CTRL 0x0020
+#define SGTL5000_CHIP_ANA_HP_CTRL 0x0022
+#define SGTL5000_CHIP_ANA_CTRL 0x0024
+#define SGTL5000_CHIP_LINREG_CTRL 0x0026
+#define SGTL5000_CHIP_REF_CTRL 0x0028
+#define SGTL5000_CHIP_MIC_CTRL 0x002a
+#define SGTL5000_CHIP_LINE_OUT_CTRL 0x002c
+#define SGTL5000_CHIP_LINE_OUT_VOL 0x002e
+#define SGTL5000_CHIP_ANA_POWER 0x0030
+#define SGTL5000_CHIP_PLL_CTRL 0x0032
+#define SGTL5000_CHIP_CLK_TOP_CTRL 0x0034
+#define SGTL5000_CHIP_ANA_STATUS 0x0036
+#define SGTL5000_CHIP_SHORT_CTRL 0x003c
+#define SGTL5000_CHIP_ANA_TEST2 0x003a
+#define SGTL5000_DAP_CTRL 0x0100
+#define SGTL5000_DAP_PEQ 0x0102
+#define SGTL5000_DAP_BASS_ENHANCE 0x0104
+#define SGTL5000_DAP_BASS_ENHANCE_CTRL 0x0106
+#define SGTL5000_DAP_AUDIO_EQ 0x0108
+#define SGTL5000_DAP_SURROUND 0x010a
+#define SGTL5000_DAP_FLT_COEF_ACCESS 0x010c
+#define SGTL5000_DAP_COEF_WR_B0_MSB 0x010e
+#define SGTL5000_DAP_COEF_WR_B0_LSB 0x0110
+#define SGTL5000_DAP_EQ_BASS_BAND0 0x0116
+#define SGTL5000_DAP_EQ_BASS_BAND1 0x0118
+#define SGTL5000_DAP_EQ_BASS_BAND2 0x011a
+#define SGTL5000_DAP_EQ_BASS_BAND3 0x011c
+#define SGTL5000_DAP_EQ_BASS_BAND4 0x011e
+#define SGTL5000_DAP_MAIN_CHAN 0x0120
+#define SGTL5000_DAP_MIX_CHAN 0x0122
+#define SGTL5000_DAP_AVC_CTRL 0x0124
+#define SGTL5000_DAP_AVC_THRESHOLD 0x0126
+#define SGTL5000_DAP_AVC_ATTACK 0x0128
+#define SGTL5000_DAP_AVC_DECAY 0x012a
+#define SGTL5000_DAP_COEF_WR_B1_MSB 0x012c
+#define SGTL5000_DAP_COEF_WR_B1_LSB 0x012e
+#define SGTL5000_DAP_COEF_WR_B2_MSB 0x0130
+#define SGTL5000_DAP_COEF_WR_B2_LSB 0x0132
+#define SGTL5000_DAP_COEF_WR_A1_MSB 0x0134
+#define SGTL5000_DAP_COEF_WR_A1_LSB 0x0136
+#define SGTL5000_DAP_COEF_WR_A2_MSB 0x0138
+#define SGTL5000_DAP_COEF_WR_A2_LSB 0x013a
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * SGTL5000_CHIP_ID
+ */
+#define SGTL5000_PARTID_MASK 0xff00
+#define SGTL5000_PARTID_SHIFT 8
+#define SGTL5000_PARTID_WIDTH 8
+#define SGTL5000_PARTID_PART_ID 0xa0
+#define SGTL5000_REVID_MASK 0x00ff
+#define SGTL5000_REVID_SHIFT 0
+#define SGTL5000_REVID_WIDTH 8
+
+/*
+ * SGTL5000_CHIP_DIG_POWER
+ */
+#define SGTL5000_ADC_EN 0x0040
+#define SGTL5000_DAC_EN 0x0020
+#define SGTL5000_DAP_POWERUP 0x0010
+#define SGTL5000_I2S_OUT_POWERUP 0x0002
+#define SGTL5000_I2S_IN_POWERUP 0x0001
+
+/*
+ * SGTL5000_CHIP_CLK_CTRL
+ */
+#define SGTL5000_RATE_MODE_MASK 0x0030
+#define SGTL5000_RATE_MODE_SHIFT 4
+#define SGTL5000_RATE_MODE_WIDTH 2
+#define SGTL5000_RATE_MODE_DIV_1 0
+#define SGTL5000_RATE_MODE_DIV_2 1
+#define SGTL5000_RATE_MODE_DIV_4 2
+#define SGTL5000_RATE_MODE_DIV_6 3
+#define SGTL5000_SYS_FS_MASK 0x000c
+#define SGTL5000_SYS_FS_SHIFT 2
+#define SGTL5000_SYS_FS_WIDTH 2
+#define SGTL5000_SYS_FS_32k 0x0
+#define SGTL5000_SYS_FS_44_1k 0x1
+#define SGTL5000_SYS_FS_48k 0x2
+#define SGTL5000_SYS_FS_96k 0x3
+#define SGTL5000_MCLK_FREQ_MASK 0x0003
+#define SGTL5000_MCLK_FREQ_SHIFT 0
+#define SGTL5000_MCLK_FREQ_WIDTH 2
+#define SGTL5000_MCLK_FREQ_256FS 0x0
+#define SGTL5000_MCLK_FREQ_384FS 0x1
+#define SGTL5000_MCLK_FREQ_512FS 0x2
+#define SGTL5000_MCLK_FREQ_PLL 0x3
+
+/*
+ * SGTL5000_CHIP_I2S_CTRL
+ */
+#define SGTL5000_I2S_SCLKFREQ_MASK 0x0100
+#define SGTL5000_I2S_SCLKFREQ_SHIFT 8
+#define SGTL5000_I2S_SCLKFREQ_WIDTH 1
+#define SGTL5000_I2S_SCLKFREQ_64FS 0x0
+#define SGTL5000_I2S_SCLKFREQ_32FS 0x1 /* Not for RJ mode */
+#define SGTL5000_I2S_MASTER 0x0080
+#define SGTL5000_I2S_SCLK_INV 0x0040
+#define SGTL5000_I2S_DLEN_MASK 0x0030
+#define SGTL5000_I2S_DLEN_SHIFT 4
+#define SGTL5000_I2S_DLEN_WIDTH 2
+#define SGTL5000_I2S_DLEN_32 0x0
+#define SGTL5000_I2S_DLEN_24 0x1
+#define SGTL5000_I2S_DLEN_20 0x2
+#define SGTL5000_I2S_DLEN_16 0x3
+#define SGTL5000_I2S_MODE_MASK 0x000c
+#define SGTL5000_I2S_MODE_SHIFT 2
+#define SGTL5000_I2S_MODE_WIDTH 2
+#define SGTL5000_I2S_MODE_I2S_LJ 0x0
+#define SGTL5000_I2S_MODE_RJ 0x1
+#define SGTL5000_I2S_MODE_PCM 0x2
+#define SGTL5000_I2S_LRALIGN 0x0002
+#define SGTL5000_I2S_LRPOL 0x0001 /* set for which mode */
+
+/*
+ * SGTL5000_CHIP_SSS_CTRL
+ */
+#define SGTL5000_DAP_MIX_LRSWAP 0x4000
+#define SGTL5000_DAP_LRSWAP 0x2000
+#define SGTL5000_DAC_LRSWAP 0x1000
+#define SGTL5000_I2S_OUT_LRSWAP 0x0400
+#define SGTL5000_DAP_MIX_SEL_MASK 0x0300
+#define SGTL5000_DAP_MIX_SEL_SHIFT 8
+#define SGTL5000_DAP_MIX_SEL_WIDTH 2
+#define SGTL5000_DAP_MIX_SEL_ADC 0x0
+#define SGTL5000_DAP_MIX_SEL_I2S_IN 0x1
+#define SGTL5000_DAP_SEL_MASK 0x00c0
+#define SGTL5000_DAP_SEL_SHIFT 6
+#define SGTL5000_DAP_SEL_WIDTH 2
+#define SGTL5000_DAP_SEL_ADC 0x0
+#define SGTL5000_DAP_SEL_I2S_IN 0x1
+#define SGTL5000_DAC_SEL_MASK 0x0030
+#define SGTL5000_DAC_SEL_SHIFT 4
+#define SGTL5000_DAC_SEL_WIDTH 2
+#define SGTL5000_DAC_SEL_ADC 0x0
+#define SGTL5000_DAC_SEL_I2S_IN 0x1
+#define SGTL5000_DAC_SEL_DAP 0x3
+#define SGTL5000_I2S_OUT_SEL_MASK 0x0003
+#define SGTL5000_I2S_OUT_SEL_SHIFT 0
+#define SGTL5000_I2S_OUT_SEL_WIDTH 2
+#define SGTL5000_I2S_OUT_SEL_ADC 0x0
+#define SGTL5000_I2S_OUT_SEL_I2S_IN 0x1
+#define SGTL5000_I2S_OUT_SEL_DAP 0x3
+
+/*
+ * SGTL5000_CHIP_ADCDAC_CTRL
+ */
+#define SGTL5000_VOL_BUSY_DAC_RIGHT 0x2000
+#define SGTL5000_VOL_BUSY_DAC_LEFT 0x1000
+#define SGTL5000_DAC_VOL_RAMP_EN 0x0200
+#define SGTL5000_DAC_VOL_RAMP_EXPO 0x0100
+#define SGTL5000_DAC_MUTE_RIGHT 0x0008
+#define SGTL5000_DAC_MUTE_LEFT 0x0004
+#define SGTL5000_ADC_HPF_FREEZE 0x0002
+#define SGTL5000_ADC_HPF_BYPASS 0x0001
+
+/*
+ * SGTL5000_CHIP_DAC_VOL
+ */
+#define SGTL5000_DAC_VOL_RIGHT_MASK 0xff00
+#define SGTL5000_DAC_VOL_RIGHT_SHIFT 8
+#define SGTL5000_DAC_VOL_RIGHT_WIDTH 8
+#define SGTL5000_DAC_VOL_LEFT_MASK 0x00ff
+#define SGTL5000_DAC_VOL_LEFT_SHIFT 0
+#define SGTL5000_DAC_VOL_LEFT_WIDTH 8
+
+/*
+ * SGTL5000_CHIP_PAD_STRENGTH
+ */
+#define SGTL5000_PAD_I2S_LRCLK_MASK 0x0300
+#define SGTL5000_PAD_I2S_LRCLK_SHIFT 8
+#define SGTL5000_PAD_I2S_LRCLK_WIDTH 2
+#define SGTL5000_PAD_I2S_SCLK_MASK 0x00c0
+#define SGTL5000_PAD_I2S_SCLK_SHIFT 6
+#define SGTL5000_PAD_I2S_SCLK_WIDTH 2
+#define SGTL5000_PAD_I2S_DOUT_MASK 0x0030
+#define SGTL5000_PAD_I2S_DOUT_SHIFT 4
+#define SGTL5000_PAD_I2S_DOUT_WIDTH 2
+#define SGTL5000_PAD_I2C_SDA_MASK 0x000c
+#define SGTL5000_PAD_I2C_SDA_SHIFT 2
+#define SGTL5000_PAD_I2C_SDA_WIDTH 2
+#define SGTL5000_PAD_I2C_SCL_MASK 0x0003
+#define SGTL5000_PAD_I2C_SCL_SHIFT 0
+#define SGTL5000_PAD_I2C_SCL_WIDTH 2
+
+/*
+ * SGTL5000_CHIP_ANA_ADC_CTRL
+ */
+#define SGTL5000_ADC_VOL_M6DB 0x0100
+#define SGTL5000_ADC_VOL_RIGHT_MASK 0x00f0
+#define SGTL5000_ADC_VOL_RIGHT_SHIFT 4
+#define SGTL5000_ADC_VOL_RIGHT_WIDTH 4
+#define SGTL5000_ADC_VOL_LEFT_MASK 0x000f
+#define SGTL5000_ADC_VOL_LEFT_SHIFT 0
+#define SGTL5000_ADC_VOL_LEFT_WIDTH 4
+
+/*
+ * SGTL5000_CHIP_ANA_HP_CTRL
+ */
+#define SGTL5000_HP_VOL_RIGHT_MASK 0x7f00
+#define SGTL5000_HP_VOL_RIGHT_SHIFT 8
+#define SGTL5000_HP_VOL_RIGHT_WIDTH 7
+#define SGTL5000_HP_VOL_LEFT_MASK 0x007f
+#define SGTL5000_HP_VOL_LEFT_SHIFT 0
+#define SGTL5000_HP_VOL_LEFT_WIDTH 7
+
+/*
+ * SGTL5000_CHIP_ANA_CTRL
+ */
+#define SGTL5000_LINE_OUT_MUTE 0x0100
+#define SGTL5000_HP_SEL_MASK 0x0040
+#define SGTL5000_HP_SEL_SHIFT 6
+#define SGTL5000_HP_SEL_WIDTH 1
+#define SGTL5000_HP_SEL_DAC 0x0
+#define SGTL5000_HP_SEL_LINE_IN 0x1
+#define SGTL5000_HP_ZCD_EN 0x0020
+#define SGTL5000_HP_MUTE 0x0010
+#define SGTL5000_ADC_SEL_MASK 0x0004
+#define SGTL5000_ADC_SEL_SHIFT 2
+#define SGTL5000_ADC_SEL_WIDTH 1
+#define SGTL5000_ADC_SEL_MIC 0x0
+#define SGTL5000_ADC_SEL_LINE_IN 0x1
+#define SGTL5000_ADC_ZCD_EN 0x0002
+#define SGTL5000_ADC_MUTE 0x0001
+
+/*
+ * SGTL5000_CHIP_LINREG_CTRL
+ */
+#define SGTL5000_VDDC_MAN_ASSN_MASK 0x0040
+#define SGTL5000_VDDC_MAN_ASSN_SHIFT 6
+#define SGTL5000_VDDC_MAN_ASSN_WIDTH 1
+#define SGTL5000_VDDC_MAN_ASSN_VDDA 0x0
+#define SGTL5000_VDDC_MAN_ASSN_VDDIO 0x1
+#define SGTL5000_VDDC_ASSN_OVRD 0x0020
+#define SGTL5000_LINREG_VDDD_MASK 0x000f
+#define SGTL5000_LINREG_VDDD_SHIFT 0
+#define SGTL5000_LINREG_VDDD_WIDTH 4
+
+/*
+ * SGTL5000_CHIP_REF_CTRL
+ */
+#define SGTL5000_ANA_GND_MASK 0x01f0
+#define SGTL5000_ANA_GND_SHIFT 4
+#define SGTL5000_ANA_GND_WIDTH 5
+#define SGTL5000_ANA_GND_BASE 800 /* mv */
+#define SGTL5000_ANA_GND_STP 25 /*mv */
+#define SGTL5000_BIAS_CTRL_MASK 0x000e
+#define SGTL5000_BIAS_CTRL_SHIFT 1
+#define SGTL5000_BIAS_CTRL_WIDTH 3
+#define SGTL5000_SMALL_POP 0
+
+/*
+ * SGTL5000_CHIP_MIC_CTRL
+ */
+#define SGTL5000_BIAS_R_MASK 0x0300
+#define SGTL5000_BIAS_R_SHIFT 8
+#define SGTL5000_BIAS_R_WIDTH 2
+#define SGTL5000_BIAS_R_off 0x0
+#define SGTL5000_BIAS_R_2K 0x1
+#define SGTL5000_BIAS_R_4k 0x2
+#define SGTL5000_BIAS_R_8k 0x3
+#define SGTL5000_BIAS_VOLT_MASK 0x0070
+#define SGTL5000_BIAS_VOLT_SHIFT 4
+#define SGTL5000_BIAS_VOLT_WIDTH 3
+#define SGTL5000_MIC_GAIN_MASK 0x0003
+#define SGTL5000_MIC_GAIN_SHIFT 0
+#define SGTL5000_MIC_GAIN_WIDTH 2
+
+/*
+ * SGTL5000_CHIP_LINE_OUT_CTRL
+ */
+#define SGTL5000_LINE_OUT_CURRENT_MASK 0x0f00
+#define SGTL5000_LINE_OUT_CURRENT_SHIFT 8
+#define SGTL5000_LINE_OUT_CURRENT_WIDTH 4
+#define SGTL5000_LINE_OUT_CURRENT_180u 0x0
+#define SGTL5000_LINE_OUT_CURRENT_270u 0x1
+#define SGTL5000_LINE_OUT_CURRENT_360u 0x3
+#define SGTL5000_LINE_OUT_CURRENT_450u 0x7
+#define SGTL5000_LINE_OUT_CURRENT_540u 0xf
+#define SGTL5000_LINE_OUT_GND_MASK 0x003f
+#define SGTL5000_LINE_OUT_GND_SHIFT 0
+#define SGTL5000_LINE_OUT_GND_WIDTH 6
+#define SGTL5000_LINE_OUT_GND_BASE 800 /* mv */
+#define SGTL5000_LINE_OUT_GND_STP 25
+#define SGTL5000_LINE_OUT_GND_MAX 0x23
+
+/*
+ * SGTL5000_CHIP_LINE_OUT_VOL
+ */
+#define SGTL5000_LINE_OUT_VOL_RIGHT_MASK 0x1f00
+#define SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT 8
+#define SGTL5000_LINE_OUT_VOL_RIGHT_WIDTH 5
+#define SGTL5000_LINE_OUT_VOL_LEFT_MASK 0x001f
+#define SGTL5000_LINE_OUT_VOL_LEFT_SHIFT 0
+#define SGTL5000_LINE_OUT_VOL_LEFT_WIDTH 5
+
+/*
+ * SGTL5000_CHIP_ANA_POWER
+ */
+#define SGTL5000_DAC_STEREO 0x4000
+#define SGTL5000_LINREG_SIMPLE_POWERUP 0x2000
+#define SGTL5000_STARTUP_POWERUP 0x1000
+#define SGTL5000_VDDC_CHRGPMP_POWERUP 0x0800
+#define SGTL5000_PLL_POWERUP 0x0400
+#define SGTL5000_LINEREG_D_POWERUP 0x0200
+#define SGTL5000_VCOAMP_POWERUP 0x0100
+#define SGTL5000_VAG_POWERUP 0x0080
+#define SGTL5000_ADC_STEREO 0x0040
+#define SGTL5000_REFTOP_POWERUP 0x0020
+#define SGTL5000_HP_POWERUP 0x0010
+#define SGTL5000_DAC_POWERUP 0x0008
+#define SGTL5000_CAPLESS_HP_POWERUP 0x0004
+#define SGTL5000_ADC_POWERUP 0x0002
+#define SGTL5000_LINE_OUT_POWERUP 0x0001
+
+/*
+ * SGTL5000_CHIP_PLL_CTRL
+ */
+#define SGTL5000_PLL_INT_DIV_MASK 0xf800
+#define SGTL5000_PLL_INT_DIV_SHIFT 11
+#define SGTL5000_PLL_INT_DIV_WIDTH 5
+#define SGTL5000_PLL_FRAC_DIV_MASK 0x07ff
+#define SGTL5000_PLL_FRAC_DIV_SHIFT 0
+#define SGTL5000_PLL_FRAC_DIV_WIDTH 11
+
+/*
+ * SGTL5000_CHIP_CLK_TOP_CTRL
+ */
+#define SGTL5000_INT_OSC_EN 0x0800
+#define SGTL5000_INPUT_FREQ_DIV2 0x0008
+
+/*
+ * SGTL5000_CHIP_ANA_STATUS
+ */
+#define SGTL5000_HP_LRSHORT 0x0200
+#define SGTL5000_CAPLESS_SHORT 0x0100
+#define SGTL5000_PLL_LOCKED 0x0010
+
+/*
+ * SGTL5000_CHIP_SHORT_CTRL
+ */
+#define SGTL5000_LVLADJR_MASK 0x7000
+#define SGTL5000_LVLADJR_SHIFT 12
+#define SGTL5000_LVLADJR_WIDTH 3
+#define SGTL5000_LVLADJL_MASK 0x0700
+#define SGTL5000_LVLADJL_SHIFT 8
+#define SGTL5000_LVLADJL_WIDTH 3
+#define SGTL5000_LVLADJC_MASK 0x0070
+#define SGTL5000_LVLADJC_SHIFT 4
+#define SGTL5000_LVLADJC_WIDTH 3
+#define SGTL5000_LR_SHORT_MOD_MASK 0x000c
+#define SGTL5000_LR_SHORT_MOD_SHIFT 2
+#define SGTL5000_LR_SHORT_MOD_WIDTH 2
+#define SGTL5000_CM_SHORT_MOD_MASK 0x0003
+#define SGTL5000_CM_SHORT_MOD_SHIFT 0
+#define SGTL5000_CM_SHORT_MOD_WIDTH 2
+
+/*
+ *SGTL5000_CHIP_ANA_TEST2
+ */
+#define SGTL5000_MONO_DAC 0x1000
+
+/*
+ * SGTL5000_DAP_CTRL
+ */
+#define SGTL5000_DAP_MIX_EN 0x0010
+#define SGTL5000_DAP_EN 0x0001
+
+#define SGTL5000_SYSCLK 0x00
+#define SGTL5000_LRCLK 0x01
+
+#endif
diff --git a/kernel/sound/soc/codecs/si476x.c b/kernel/sound/soc/codecs/si476x.c
new file mode 100644
index 000000000..3e7296428
--- /dev/null
+++ b/kernel/sound/soc/codecs/si476x.c
@@ -0,0 +1,272 @@
+/*
+ * sound/soc/codecs/si476x.c -- Codec driver for SI476X chips
+ *
+ * Copyright (C) 2012 Innovative Converged Devices(ICD)
+ * Copyright (C) 2013 Andrey Smirnov
+ *
+ * Author: Andrey Smirnov <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>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#include <linux/i2c.h>
+
+#include <linux/mfd/si476x-core.h>
+
+enum si476x_audio_registers {
+ SI476X_DIGITAL_IO_OUTPUT_FORMAT = 0x0203,
+ SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE = 0x0202,
+};
+
+enum si476x_digital_io_output_format {
+ SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT = 11,
+ SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT = 8,
+};
+
+#define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK ((0x7 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \
+ (0x7 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT))
+#define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK (0x7e)
+
+enum si476x_daudio_formats {
+ SI476X_DAUDIO_MODE_I2S = (0x0 << 1),
+ SI476X_DAUDIO_MODE_DSP_A = (0x6 << 1),
+ SI476X_DAUDIO_MODE_DSP_B = (0x7 << 1),
+ SI476X_DAUDIO_MODE_LEFT_J = (0x8 << 1),
+ SI476X_DAUDIO_MODE_RIGHT_J = (0x9 << 1),
+
+ SI476X_DAUDIO_MODE_IB = (1 << 5),
+ SI476X_DAUDIO_MODE_IF = (1 << 6),
+};
+
+enum si476x_pcm_format {
+ SI476X_PCM_FORMAT_S8 = 2,
+ SI476X_PCM_FORMAT_S16_LE = 4,
+ SI476X_PCM_FORMAT_S20_3LE = 5,
+ SI476X_PCM_FORMAT_S24_LE = 6,
+};
+
+static const struct snd_soc_dapm_widget si476x_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+};
+
+static const struct snd_soc_dapm_route si476x_dapm_routes[] = {
+ { "Capture", NULL, "LOUT" },
+ { "Capture", NULL, "ROUT" },
+};
+
+static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct si476x_core *core = i2c_mfd_cell_to_core(codec_dai->dev);
+ int err;
+ u16 format = 0;
+
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ return -EINVAL;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ format |= SI476X_DAUDIO_MODE_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format |= SI476X_DAUDIO_MODE_DSP_B;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ format |= SI476X_DAUDIO_MODE_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ format |= SI476X_DAUDIO_MODE_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format |= SI476X_DAUDIO_MODE_LEFT_J;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ format |= SI476X_DAUDIO_MODE_IB;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ format |= SI476X_DAUDIO_MODE_IB |
+ SI476X_DAUDIO_MODE_IF;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ format |= SI476X_DAUDIO_MODE_IB;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ format |= SI476X_DAUDIO_MODE_IF;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ si476x_core_lock(core);
+
+ err = snd_soc_update_bits(codec_dai->codec, 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");
+ return err;
+ }
+
+ return 0;
+}
+
+static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct si476x_core *core = i2c_mfd_cell_to_core(dai->dev);
+ int rate, width, err;
+
+ rate = params_rate(params);
+ if (rate < 32000 || rate > 48000) {
+ dev_err(dai->codec->dev, "Rate: %d is not supported\n", rate);
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ case 8:
+ width = SI476X_PCM_FORMAT_S8;
+ break;
+ case 16:
+ width = SI476X_PCM_FORMAT_S16_LE;
+ break;
+ case 20:
+ width = SI476X_PCM_FORMAT_S20_3LE;
+ break;
+ case 24:
+ width = SI476X_PCM_FORMAT_S24_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ si476x_core_lock(core);
+
+ err = snd_soc_write(dai->codec, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE,
+ rate);
+ if (err < 0) {
+ dev_err(dai->codec->dev, "Failed to set sample rate\n");
+ goto out;
+ }
+
+ err = snd_soc_update_bits(dai->codec, 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");
+ goto out;
+ }
+
+out:
+ si476x_core_unlock(core);
+
+ return err;
+}
+
+static struct snd_soc_dai_ops si476x_dai_ops = {
+ .hw_params = si476x_codec_hw_params,
+ .set_fmt = si476x_codec_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver si476x_dai = {
+ .name = "si476x-codec",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE
+ },
+ .ops = &si476x_dai_ops,
+};
+
+static struct regmap *si476x_get_regmap(struct device *dev)
+{
+ return dev_get_regmap(dev->parent, NULL);
+}
+
+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 int si476x_platform_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_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 = {
+ .driver = {
+ .name = "si476x-codec",
+ },
+ .probe = si476x_platform_probe,
+ .remove = si476x_platform_remove,
+};
+module_platform_driver(si476x_platform_driver);
+
+MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
+MODULE_DESCRIPTION("ASoC Si4761/64 codec driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/sigmadsp-i2c.c b/kernel/sound/soc/codecs/sigmadsp-i2c.c
new file mode 100644
index 000000000..21ca3a5e9
--- /dev/null
+++ b/kernel/sound/soc/codecs/sigmadsp-i2c.c
@@ -0,0 +1,94 @@
+/*
+ * 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 "sigmadsp.h"
+
+static int sigmadsp_write_i2c(void *control_data,
+ unsigned int addr, const uint8_t data[], size_t len)
+{
+ uint8_t *buf;
+ int ret;
+
+ buf = kzalloc(2 + len, GFP_KERNEL | GFP_DMA);
+ if (!buf)
+ return -ENOMEM;
+
+ put_unaligned_be16(addr, buf);
+ memcpy(buf + 2, data, len);
+
+ ret = i2c_master_send(control_data, buf, len + 2);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static int sigmadsp_read_i2c(void *control_data,
+ unsigned int addr, uint8_t data[], size_t len)
+{
+ struct i2c_client *client = control_data;
+ struct i2c_msg msgs[2];
+ uint8_t buf[2];
+ int ret;
+
+ put_unaligned_be16(addr, buf);
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(buf);
+ msgs[0].buf = buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = len;
+ msgs[1].buf = data;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+ else if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+ return 0;
+}
+
+/**
+ * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance
+ * @client: The parent I2C device
+ * @ops: The sigmadsp_ops to use for this instance
+ * @firmware_name: Name of the firmware file to load
+ *
+ * Allocates a SigmaDSP instance and loads the specified firmware file.
+ *
+ * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error.
+ */
+struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client,
+ const struct sigmadsp_ops *ops, const char *firmware_name)
+{
+ struct sigmadsp *sigmadsp;
+
+ sigmadsp = devm_sigmadsp_init(&client->dev, ops, firmware_name);
+ if (IS_ERR(sigmadsp))
+ return sigmadsp;
+
+ sigmadsp->control_data = client;
+ sigmadsp->write = sigmadsp_write_i2c;
+ sigmadsp->read = sigmadsp_read_i2c;
+
+ return sigmadsp;
+}
+EXPORT_SYMBOL_GPL(devm_sigmadsp_init_i2c);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("SigmaDSP I2C firmware loader");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/sigmadsp-regmap.c b/kernel/sound/soc/codecs/sigmadsp-regmap.c
new file mode 100644
index 000000000..912861be5
--- /dev/null
+++ b/kernel/sound/soc/codecs/sigmadsp-regmap.c
@@ -0,0 +1,60 @@
+/*
+ * Load Analog Devices SigmaStudio firmware files
+ *
+ * Copyright 2009-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/regmap.h>
+#include <linux/export.h>
+#include <linux/module.h>
+
+#include "sigmadsp.h"
+
+static int sigmadsp_write_regmap(void *control_data,
+ unsigned int addr, const uint8_t data[], size_t len)
+{
+ return regmap_raw_write(control_data, addr,
+ data, len);
+}
+
+static int sigmadsp_read_regmap(void *control_data,
+ unsigned int addr, uint8_t data[], size_t len)
+{
+ return regmap_raw_read(control_data, addr,
+ data, len);
+}
+
+/**
+ * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance
+ * @dev: The parent device
+ * @regmap: Regmap instance to use
+ * @ops: The sigmadsp_ops to use for this instance
+ * @firmware_name: Name of the firmware file to load
+ *
+ * Allocates a SigmaDSP instance and loads the specified firmware file.
+ *
+ * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error.
+ */
+struct sigmadsp *devm_sigmadsp_init_regmap(struct device *dev,
+ struct regmap *regmap, const struct sigmadsp_ops *ops,
+ const char *firmware_name)
+{
+ struct sigmadsp *sigmadsp;
+
+ sigmadsp = devm_sigmadsp_init(dev, ops, firmware_name);
+ if (IS_ERR(sigmadsp))
+ return sigmadsp;
+
+ sigmadsp->control_data = regmap;
+ sigmadsp->write = sigmadsp_write_regmap;
+ sigmadsp->read = sigmadsp_read_regmap;
+
+ return sigmadsp;
+}
+EXPORT_SYMBOL_GPL(devm_sigmadsp_init_regmap);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("SigmaDSP regmap firmware loader");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/sigmadsp.c b/kernel/sound/soc/codecs/sigmadsp.c
new file mode 100644
index 000000000..d53680ac7
--- /dev/null
+++ b/kernel/sound/soc/codecs/sigmadsp.c
@@ -0,0 +1,814 @@
+/*
+ * Load Analog Devices SigmaStudio firmware files
+ *
+ * Copyright 2009-2014 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/crc32.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <sound/control.h>
+#include <sound/soc.h>
+
+#include "sigmadsp.h"
+
+#define SIGMA_MAGIC "ADISIGM"
+
+#define SIGMA_FW_CHUNK_TYPE_DATA 0
+#define SIGMA_FW_CHUNK_TYPE_CONTROL 1
+#define SIGMA_FW_CHUNK_TYPE_SAMPLERATES 2
+
+struct sigmadsp_control {
+ struct list_head head;
+ uint32_t samplerates;
+ unsigned int addr;
+ unsigned int num_bytes;
+ const char *name;
+ struct snd_kcontrol *kcontrol;
+ bool cached;
+ uint8_t cache[];
+};
+
+struct sigmadsp_data {
+ struct list_head head;
+ uint32_t samplerates;
+ unsigned int addr;
+ unsigned int length;
+ uint8_t data[];
+};
+
+struct sigma_fw_chunk {
+ __le32 length;
+ __le32 tag;
+ __le32 samplerates;
+} __packed;
+
+struct sigma_fw_chunk_data {
+ struct sigma_fw_chunk chunk;
+ __le16 addr;
+ uint8_t data[];
+} __packed;
+
+struct sigma_fw_chunk_control {
+ struct sigma_fw_chunk chunk;
+ __le16 type;
+ __le16 addr;
+ __le16 num_bytes;
+ const char name[];
+} __packed;
+
+struct sigma_fw_chunk_samplerate {
+ struct sigma_fw_chunk chunk;
+ __le32 samplerates[];
+} __packed;
+
+struct sigma_firmware_header {
+ unsigned char magic[7];
+ u8 version;
+ __le32 crc;
+} __packed;
+
+enum {
+ SIGMA_ACTION_WRITEXBYTES = 0,
+ SIGMA_ACTION_WRITESINGLE,
+ SIGMA_ACTION_WRITESAFELOAD,
+ SIGMA_ACTION_END,
+};
+
+struct sigma_action {
+ u8 instr;
+ u8 len_hi;
+ __le16 len;
+ __be16 addr;
+ unsigned char payload[];
+} __packed;
+
+static int sigmadsp_write(struct sigmadsp *sigmadsp, unsigned int addr,
+ const uint8_t data[], size_t len)
+{
+ return sigmadsp->write(sigmadsp->control_data, addr, data, len);
+}
+
+static int sigmadsp_read(struct sigmadsp *sigmadsp, unsigned int addr,
+ uint8_t data[], size_t len)
+{
+ return sigmadsp->read(sigmadsp->control_data, addr, data, len);
+}
+
+static int sigmadsp_ctrl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ struct sigmadsp_control *ctrl = (void *)kcontrol->private_value;
+
+ info->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ info->count = ctrl->num_bytes;
+
+ return 0;
+}
+
+static int sigmadsp_ctrl_write(struct sigmadsp *sigmadsp,
+ struct sigmadsp_control *ctrl, void *data)
+{
+ /* safeload loads up to 20 bytes in a atomic operation */
+ if (ctrl->num_bytes > 4 && ctrl->num_bytes <= 20 && sigmadsp->ops &&
+ sigmadsp->ops->safeload)
+ return sigmadsp->ops->safeload(sigmadsp, ctrl->addr, data,
+ ctrl->num_bytes);
+ else
+ return sigmadsp_write(sigmadsp, ctrl->addr, data,
+ ctrl->num_bytes);
+}
+
+static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sigmadsp_control *ctrl = (void *)kcontrol->private_value;
+ struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol);
+ uint8_t *data;
+ int ret = 0;
+
+ mutex_lock(&sigmadsp->lock);
+
+ data = ucontrol->value.bytes.data;
+
+ if (!(kcontrol->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
+ ret = sigmadsp_ctrl_write(sigmadsp, ctrl, data);
+
+ if (ret == 0) {
+ memcpy(ctrl->cache, data, ctrl->num_bytes);
+ ctrl->cached = true;
+ }
+
+ mutex_unlock(&sigmadsp->lock);
+
+ return ret;
+}
+
+static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sigmadsp_control *ctrl = (void *)kcontrol->private_value;
+ struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol);
+ int ret = 0;
+
+ mutex_lock(&sigmadsp->lock);
+
+ if (!ctrl->cached) {
+ ret = sigmadsp_read(sigmadsp, ctrl->addr, ctrl->cache,
+ ctrl->num_bytes);
+ }
+
+ if (ret == 0) {
+ ctrl->cached = true;
+ memcpy(ucontrol->value.bytes.data, ctrl->cache,
+ ctrl->num_bytes);
+ }
+
+ mutex_unlock(&sigmadsp->lock);
+
+ return ret;
+}
+
+static void sigmadsp_control_free(struct snd_kcontrol *kcontrol)
+{
+ struct sigmadsp_control *ctrl = (void *)kcontrol->private_value;
+
+ ctrl->kcontrol = NULL;
+}
+
+static bool sigma_fw_validate_control_name(const char *name, unsigned int len)
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++) {
+ /* Normal ASCII characters are valid */
+ if (name[i] < ' ' || name[i] > '~')
+ return false;
+ }
+
+ return true;
+}
+
+static int sigma_fw_load_control(struct sigmadsp *sigmadsp,
+ const struct sigma_fw_chunk *chunk, unsigned int length)
+{
+ const struct sigma_fw_chunk_control *ctrl_chunk;
+ struct sigmadsp_control *ctrl;
+ unsigned int num_bytes;
+ size_t name_len;
+ char *name;
+ int ret;
+
+ if (length <= sizeof(*ctrl_chunk))
+ return -EINVAL;
+
+ ctrl_chunk = (const struct sigma_fw_chunk_control *)chunk;
+
+ name_len = length - sizeof(*ctrl_chunk);
+ if (name_len >= SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ name_len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1;
+
+ /* Make sure there are no non-displayable characaters in the string */
+ if (!sigma_fw_validate_control_name(ctrl_chunk->name, name_len))
+ return -EINVAL;
+
+ num_bytes = le16_to_cpu(ctrl_chunk->num_bytes);
+ ctrl = kzalloc(sizeof(*ctrl) + num_bytes, GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ name = kzalloc(name_len + 1, GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto err_free_ctrl;
+ }
+ memcpy(name, ctrl_chunk->name, name_len);
+ name[name_len] = '\0';
+ ctrl->name = name;
+
+ ctrl->addr = le16_to_cpu(ctrl_chunk->addr);
+ ctrl->num_bytes = num_bytes;
+ ctrl->samplerates = le32_to_cpu(chunk->samplerates);
+
+ list_add_tail(&ctrl->head, &sigmadsp->ctrl_list);
+
+ return 0;
+
+err_free_ctrl:
+ kfree(ctrl);
+
+ return ret;
+}
+
+static int sigma_fw_load_data(struct sigmadsp *sigmadsp,
+ const struct sigma_fw_chunk *chunk, unsigned int length)
+{
+ const struct sigma_fw_chunk_data *data_chunk;
+ struct sigmadsp_data *data;
+
+ if (length <= sizeof(*data_chunk))
+ return -EINVAL;
+
+ data_chunk = (struct sigma_fw_chunk_data *)chunk;
+
+ length -= sizeof(*data_chunk);
+
+ data = kzalloc(sizeof(*data) + length, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = le16_to_cpu(data_chunk->addr);
+ data->length = length;
+ data->samplerates = le32_to_cpu(chunk->samplerates);
+ memcpy(data->data, data_chunk->data, length);
+ list_add_tail(&data->head, &sigmadsp->data_list);
+
+ return 0;
+}
+
+static int sigma_fw_load_samplerates(struct sigmadsp *sigmadsp,
+ const struct sigma_fw_chunk *chunk, unsigned int length)
+{
+ const struct sigma_fw_chunk_samplerate *rate_chunk;
+ unsigned int num_rates;
+ unsigned int *rates;
+ unsigned int i;
+
+ rate_chunk = (const struct sigma_fw_chunk_samplerate *)chunk;
+
+ num_rates = (length - sizeof(*rate_chunk)) / sizeof(__le32);
+
+ if (num_rates > 32 || num_rates == 0)
+ return -EINVAL;
+
+ /* We only allow one samplerates block per file */
+ if (sigmadsp->rate_constraints.count)
+ return -EINVAL;
+
+ rates = kcalloc(num_rates, sizeof(*rates), GFP_KERNEL);
+ if (!rates)
+ return -ENOMEM;
+
+ for (i = 0; i < num_rates; i++)
+ rates[i] = le32_to_cpu(rate_chunk->samplerates[i]);
+
+ sigmadsp->rate_constraints.count = num_rates;
+ sigmadsp->rate_constraints.list = rates;
+
+ return 0;
+}
+
+static int sigmadsp_fw_load_v2(struct sigmadsp *sigmadsp,
+ const struct firmware *fw)
+{
+ struct sigma_fw_chunk *chunk;
+ unsigned int length, pos;
+ int ret;
+
+ /*
+ * Make sure that there is at least one chunk to avoid integer
+ * underflows later on. Empty firmware is still valid though.
+ */
+ if (fw->size < sizeof(*chunk) + sizeof(struct sigma_firmware_header))
+ return 0;
+
+ pos = sizeof(struct sigma_firmware_header);
+
+ while (pos < fw->size - sizeof(*chunk)) {
+ chunk = (struct sigma_fw_chunk *)(fw->data + pos);
+
+ length = le32_to_cpu(chunk->length);
+
+ if (length > fw->size - pos || length < sizeof(*chunk))
+ return -EINVAL;
+
+ switch (le32_to_cpu(chunk->tag)) {
+ case SIGMA_FW_CHUNK_TYPE_DATA:
+ ret = sigma_fw_load_data(sigmadsp, chunk, length);
+ break;
+ case SIGMA_FW_CHUNK_TYPE_CONTROL:
+ ret = sigma_fw_load_control(sigmadsp, chunk, length);
+ break;
+ case SIGMA_FW_CHUNK_TYPE_SAMPLERATES:
+ ret = sigma_fw_load_samplerates(sigmadsp, chunk, length);
+ break;
+ default:
+ dev_warn(sigmadsp->dev, "Unknown chunk type: %d\n",
+ chunk->tag);
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ /*
+ * This can not overflow since if length is larger than the
+ * maximum firmware size (0x4000000) we'll error out earilier.
+ */
+ pos += ALIGN(length, sizeof(__le32));
+ }
+
+ return 0;
+}
+
+static inline u32 sigma_action_len(struct sigma_action *sa)
+{
+ return (sa->len_hi << 16) | le16_to_cpu(sa->len);
+}
+
+static size_t sigma_action_size(struct sigma_action *sa)
+{
+ size_t payload = 0;
+
+ switch (sa->instr) {
+ case SIGMA_ACTION_WRITEXBYTES:
+ case SIGMA_ACTION_WRITESINGLE:
+ case SIGMA_ACTION_WRITESAFELOAD:
+ payload = sigma_action_len(sa);
+ break;
+ default:
+ break;
+ }
+
+ payload = ALIGN(payload, 2);
+
+ return payload + sizeof(struct sigma_action);
+}
+
+/*
+ * Returns a negative error value in case of an error, 0 if processing of
+ * the firmware should be stopped after this action, 1 otherwise.
+ */
+static int process_sigma_action(struct sigmadsp *sigmadsp,
+ struct sigma_action *sa)
+{
+ size_t len = sigma_action_len(sa);
+ struct sigmadsp_data *data;
+
+ pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__,
+ sa->instr, sa->addr, len);
+
+ switch (sa->instr) {
+ case SIGMA_ACTION_WRITEXBYTES:
+ case SIGMA_ACTION_WRITESINGLE:
+ case SIGMA_ACTION_WRITESAFELOAD:
+ if (len < 3)
+ return -EINVAL;
+
+ data = kzalloc(sizeof(*data) + len - 2, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = be16_to_cpu(sa->addr);
+ data->length = len - 2;
+ memcpy(data->data, sa->payload, data->length);
+ list_add_tail(&data->head, &sigmadsp->data_list);
+ break;
+ case SIGMA_ACTION_END:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+static int sigmadsp_fw_load_v1(struct sigmadsp *sigmadsp,
+ const struct firmware *fw)
+{
+ struct sigma_action *sa;
+ size_t size, pos;
+ int ret;
+
+ pos = sizeof(struct sigma_firmware_header);
+
+ while (pos + sizeof(*sa) <= fw->size) {
+ sa = (struct sigma_action *)(fw->data + pos);
+
+ size = sigma_action_size(sa);
+ pos += size;
+ if (pos > fw->size || size == 0)
+ break;
+
+ ret = process_sigma_action(sigmadsp, sa);
+
+ pr_debug("%s: action returned %i\n", __func__, ret);
+
+ if (ret <= 0)
+ return ret;
+ }
+
+ if (pos != fw->size)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void sigmadsp_firmware_release(struct sigmadsp *sigmadsp)
+{
+ struct sigmadsp_control *ctrl, *_ctrl;
+ struct sigmadsp_data *data, *_data;
+
+ list_for_each_entry_safe(ctrl, _ctrl, &sigmadsp->ctrl_list, head) {
+ kfree(ctrl->name);
+ kfree(ctrl);
+ }
+
+ list_for_each_entry_safe(data, _data, &sigmadsp->data_list, head)
+ kfree(data);
+
+ INIT_LIST_HEAD(&sigmadsp->ctrl_list);
+ INIT_LIST_HEAD(&sigmadsp->data_list);
+}
+
+static void devm_sigmadsp_release(struct device *dev, void *res)
+{
+ sigmadsp_firmware_release((struct sigmadsp *)res);
+}
+
+static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name)
+{
+ const struct sigma_firmware_header *ssfw_head;
+ const struct firmware *fw;
+ int ret;
+ u32 crc;
+
+ /* first load the blob */
+ ret = request_firmware(&fw, name, sigmadsp->dev);
+ if (ret) {
+ pr_debug("%s: request_firmware() failed with %i\n", __func__, ret);
+ goto done;
+ }
+
+ /* then verify the header */
+ ret = -EINVAL;
+
+ /*
+ * Reject too small or unreasonable large files. The upper limit has been
+ * chosen a bit arbitrarily, but it should be enough for all practical
+ * purposes and having the limit makes it easier to avoid integer
+ * overflows later in the loading process.
+ */
+ if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) {
+ dev_err(sigmadsp->dev, "Failed to load firmware: Invalid size\n");
+ goto done;
+ }
+
+ ssfw_head = (void *)fw->data;
+ if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) {
+ dev_err(sigmadsp->dev, "Failed to load firmware: Invalid magic\n");
+ goto done;
+ }
+
+ crc = crc32(0, fw->data + sizeof(*ssfw_head),
+ fw->size - sizeof(*ssfw_head));
+ pr_debug("%s: crc=%x\n", __func__, crc);
+ if (crc != le32_to_cpu(ssfw_head->crc)) {
+ dev_err(sigmadsp->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n",
+ le32_to_cpu(ssfw_head->crc), crc);
+ goto done;
+ }
+
+ switch (ssfw_head->version) {
+ case 1:
+ ret = sigmadsp_fw_load_v1(sigmadsp, fw);
+ break;
+ case 2:
+ ret = sigmadsp_fw_load_v2(sigmadsp, fw);
+ break;
+ default:
+ dev_err(sigmadsp->dev,
+ "Failed to load firmware: Invalid version %d. Supported firmware versions: 1, 2\n",
+ ssfw_head->version);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret)
+ sigmadsp_firmware_release(sigmadsp);
+
+done:
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int sigmadsp_init(struct sigmadsp *sigmadsp, struct device *dev,
+ const struct sigmadsp_ops *ops, const char *firmware_name)
+{
+ sigmadsp->ops = ops;
+ sigmadsp->dev = dev;
+
+ INIT_LIST_HEAD(&sigmadsp->ctrl_list);
+ INIT_LIST_HEAD(&sigmadsp->data_list);
+ mutex_init(&sigmadsp->lock);
+
+ return sigmadsp_firmware_load(sigmadsp, firmware_name);
+}
+
+/**
+ * devm_sigmadsp_init() - Initialize SigmaDSP instance
+ * @dev: The parent device
+ * @ops: The sigmadsp_ops to use for this instance
+ * @firmware_name: Name of the firmware file to load
+ *
+ * Allocates a SigmaDSP instance and loads the specified firmware file.
+ *
+ * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error.
+ */
+struct sigmadsp *devm_sigmadsp_init(struct device *dev,
+ const struct sigmadsp_ops *ops, const char *firmware_name)
+{
+ struct sigmadsp *sigmadsp;
+ int ret;
+
+ sigmadsp = devres_alloc(devm_sigmadsp_release, sizeof(*sigmadsp),
+ GFP_KERNEL);
+ if (!sigmadsp)
+ return ERR_PTR(-ENOMEM);
+
+ ret = sigmadsp_init(sigmadsp, dev, ops, firmware_name);
+ if (ret) {
+ devres_free(sigmadsp);
+ return ERR_PTR(ret);
+ }
+
+ devres_add(dev, sigmadsp);
+
+ return sigmadsp;
+}
+EXPORT_SYMBOL_GPL(devm_sigmadsp_init);
+
+static int sigmadsp_rate_to_index(struct sigmadsp *sigmadsp, unsigned int rate)
+{
+ unsigned int i;
+
+ for (i = 0; i < sigmadsp->rate_constraints.count; i++) {
+ if (sigmadsp->rate_constraints.list[i] == rate)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static unsigned int sigmadsp_get_samplerate_mask(struct sigmadsp *sigmadsp,
+ unsigned int samplerate)
+{
+ int samplerate_index;
+
+ if (samplerate == 0)
+ return 0;
+
+ if (sigmadsp->rate_constraints.count) {
+ samplerate_index = sigmadsp_rate_to_index(sigmadsp, samplerate);
+ if (samplerate_index < 0)
+ return 0;
+
+ return BIT(samplerate_index);
+ } else {
+ return ~0;
+ }
+}
+
+static bool sigmadsp_samplerate_valid(unsigned int supported,
+ unsigned int requested)
+{
+ /* All samplerates are supported */
+ if (!supported)
+ return true;
+
+ return supported & requested;
+}
+
+static int sigmadsp_alloc_control(struct sigmadsp *sigmadsp,
+ struct sigmadsp_control *ctrl, unsigned int samplerate_mask)
+{
+ struct snd_kcontrol_new template;
+ struct snd_kcontrol *kcontrol;
+
+ memset(&template, 0, sizeof(template));
+ template.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ template.name = ctrl->name;
+ template.info = sigmadsp_ctrl_info;
+ template.get = sigmadsp_ctrl_get;
+ template.put = sigmadsp_ctrl_put;
+ template.private_value = (unsigned long)ctrl;
+ template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ if (!sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask))
+ template.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+
+ kcontrol = snd_ctl_new1(&template, sigmadsp);
+ if (!kcontrol)
+ return -ENOMEM;
+
+ kcontrol->private_free = sigmadsp_control_free;
+ ctrl->kcontrol = kcontrol;
+
+ return snd_ctl_add(sigmadsp->component->card->snd_card, kcontrol);
+}
+
+static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp,
+ struct sigmadsp_control *ctrl, unsigned int samplerate_mask)
+{
+ struct snd_card *card = sigmadsp->component->card->snd_card;
+ struct snd_kcontrol_volatile *vd;
+ struct snd_ctl_elem_id id;
+ bool active;
+ bool changed = false;
+
+ active = sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask);
+
+ down_write(&card->controls_rwsem);
+ if (!ctrl->kcontrol) {
+ up_write(&card->controls_rwsem);
+ 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) {
+ 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);
+}
+
+/**
+ * sigmadsp_attach() - Attach a sigmadsp instance to a ASoC component
+ * @sigmadsp: The sigmadsp instance to attach
+ * @component: The component to attach to
+ *
+ * Typically called in the components probe callback.
+ *
+ * Note, once this function has been called the firmware must not be released
+ * until after the ALSA snd_card that the component belongs to has been
+ * disconnected, even if sigmadsp_attach() returns an error.
+ */
+int sigmadsp_attach(struct sigmadsp *sigmadsp,
+ struct snd_soc_component *component)
+{
+ struct sigmadsp_control *ctrl;
+ unsigned int samplerate_mask;
+ int ret;
+
+ sigmadsp->component = component;
+
+ samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp,
+ sigmadsp->current_samplerate);
+
+ list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) {
+ ret = sigmadsp_alloc_control(sigmadsp, ctrl, samplerate_mask);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sigmadsp_attach);
+
+/**
+ * sigmadsp_setup() - Setup the DSP for the specified samplerate
+ * @sigmadsp: The sigmadsp instance to configure
+ * @samplerate: The samplerate the DSP should be configured for
+ *
+ * Loads the appropriate firmware program and parameter memory (if not already
+ * loaded) and enables the controls for the specified samplerate. Any control
+ * parameter changes that have been made previously will be restored.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int samplerate)
+{
+ struct sigmadsp_control *ctrl;
+ unsigned int samplerate_mask;
+ struct sigmadsp_data *data;
+ int ret;
+
+ if (sigmadsp->current_samplerate == samplerate)
+ return 0;
+
+ samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp, samplerate);
+ if (samplerate_mask == 0)
+ return -EINVAL;
+
+ list_for_each_entry(data, &sigmadsp->data_list, head) {
+ if (!sigmadsp_samplerate_valid(data->samplerates,
+ samplerate_mask))
+ continue;
+ ret = sigmadsp_write(sigmadsp, data->addr, data->data,
+ data->length);
+ if (ret)
+ goto err;
+ }
+
+ list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head)
+ sigmadsp_activate_ctrl(sigmadsp, ctrl, samplerate_mask);
+
+ sigmadsp->current_samplerate = samplerate;
+
+ return 0;
+err:
+ sigmadsp_reset(sigmadsp);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sigmadsp_setup);
+
+/**
+ * sigmadsp_reset() - Notify the sigmadsp instance that the DSP has been reset
+ * @sigmadsp: The sigmadsp instance to reset
+ *
+ * Should be called whenever the DSP has been reset and parameter and program
+ * memory need to be re-loaded.
+ */
+void sigmadsp_reset(struct sigmadsp *sigmadsp)
+{
+ struct sigmadsp_control *ctrl;
+
+ list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head)
+ sigmadsp_activate_ctrl(sigmadsp, ctrl, false);
+
+ sigmadsp->current_samplerate = 0;
+}
+EXPORT_SYMBOL_GPL(sigmadsp_reset);
+
+/**
+ * sigmadsp_restrict_params() - Applies DSP firmware specific constraints
+ * @sigmadsp: The sigmadsp instance
+ * @substream: The substream to restrict
+ *
+ * Applies samplerate constraints that may be required by the firmware Should
+ * typically be called from the CODEC/component drivers startup callback.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int sigmadsp_restrict_params(struct sigmadsp *sigmadsp,
+ struct snd_pcm_substream *substream)
+{
+ if (sigmadsp->rate_constraints.count == 0)
+ return 0;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &sigmadsp->rate_constraints);
+}
+EXPORT_SYMBOL_GPL(sigmadsp_restrict_params);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/sigmadsp.h b/kernel/sound/soc/codecs/sigmadsp.h
new file mode 100644
index 000000000..614475cbb
--- /dev/null
+++ b/kernel/sound/soc/codecs/sigmadsp.h
@@ -0,0 +1,66 @@
+/*
+ * Load firmware files from Analog Devices SigmaStudio
+ *
+ * Copyright 2009-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __SIGMA_FIRMWARE_H__
+#define __SIGMA_FIRMWARE_H__
+
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/list.h>
+
+#include <sound/pcm.h>
+
+struct sigmadsp;
+struct snd_soc_component;
+struct snd_pcm_substream;
+
+struct sigmadsp_ops {
+ int (*safeload)(struct sigmadsp *sigmadsp, unsigned int addr,
+ const uint8_t *data, size_t len);
+};
+
+struct sigmadsp {
+ const struct sigmadsp_ops *ops;
+
+ struct list_head ctrl_list;
+ struct list_head data_list;
+
+ struct snd_pcm_hw_constraint_list rate_constraints;
+
+ unsigned int current_samplerate;
+ struct snd_soc_component *component;
+ struct device *dev;
+
+ struct mutex lock;
+
+ void *control_data;
+ int (*write)(void *, unsigned int, const uint8_t *, size_t);
+ int (*read)(void *, unsigned int, uint8_t *, size_t);
+};
+
+struct sigmadsp *devm_sigmadsp_init(struct device *dev,
+ const struct sigmadsp_ops *ops, const char *firmware_name);
+void sigmadsp_reset(struct sigmadsp *sigmadsp);
+
+int sigmadsp_restrict_params(struct sigmadsp *sigmadsp,
+ struct snd_pcm_substream *substream);
+
+struct i2c_client;
+
+struct sigmadsp *devm_sigmadsp_init_regmap(struct device *dev,
+ struct regmap *regmap, const struct sigmadsp_ops *ops,
+ const char *firmware_name);
+struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client,
+ const struct sigmadsp_ops *ops, const char *firmware_name);
+
+int sigmadsp_attach(struct sigmadsp *sigmadsp,
+ struct snd_soc_component *component);
+int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int rate);
+void sigmadsp_reset(struct sigmadsp *sigmadsp);
+
+#endif
diff --git a/kernel/sound/soc/codecs/sirf-audio-codec.c b/kernel/sound/soc/codecs/sirf-audio-codec.c
new file mode 100644
index 000000000..0a8e43c98
--- /dev/null
+++ b/kernel/sound/soc/codecs/sirf-audio-codec.c
@@ -0,0 +1,581 @@
+/*
+ * 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/kernel/sound/soc/codecs/sirf-audio-codec.h b/kernel/sound/soc/codecs/sirf-audio-codec.h
new file mode 100644
index 000000000..ba1adc038
--- /dev/null
+++ b/kernel/sound/soc/codecs/sirf-audio-codec.h
@@ -0,0 +1,125 @@
+/*
+ * 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/kernel/sound/soc/codecs/sn95031.c b/kernel/sound/soc/codecs/sn95031.c
new file mode 100644
index 000000000..7947c0ebb
--- /dev/null
+++ b/kernel/sound/soc/codecs/sn95031.c
@@ -0,0 +1,930 @@
+/*
+ * 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_codec *codec,
+ struct snd_soc_jack *mfld_jack)
+{
+ int micbias = sn95031_get_mic_bias(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(codec);
+ return jack_type;
+}
+
+void sn95031_jack_detection(struct snd_soc_codec *codec,
+ 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(codec, jack_data->mfld_jack);
+ } else if (jack_data->intr_id & 0x8) {
+ pr_debug("headset or headphones removed\n");
+ status = 0;
+ sn95031_disable_jack_btn(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/kernel/sound/soc/codecs/sn95031.h b/kernel/sound/soc/codecs/sn95031.h
new file mode 100644
index 000000000..7651fe4e6
--- /dev/null
+++ b/kernel/sound/soc/codecs/sn95031.h
@@ -0,0 +1,133 @@
+/*
+ * 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 snd_soc_codec *codec,
+ struct mfld_jack_data *jack_data);
+
+#endif
diff --git a/kernel/sound/soc/codecs/spdif_receiver.c b/kernel/sound/soc/codecs/spdif_receiver.c
new file mode 100644
index 000000000..3ec41ccbf
--- /dev/null
+++ b/kernel/sound/soc/codecs/spdif_receiver.c
@@ -0,0 +1,91 @@
+/*
+ * ALSA SoC SPDIF DIR (Digital Interface Reciever) driver
+ *
+ * Based on ALSA SoC SPDIF DIT driver
+ *
+ * This driver is used by controllers which can operate in DIR (SPDI/F) where
+ * no codec is needed. This file provides stub codec that can be used
+ * in these configurations. SPEAr SPDIF IN Audio controller uses this driver.
+ *
+ * Author: Vipin Kumar, <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>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <linux/of.h>
+
+static const struct snd_soc_dapm_widget dir_widgets[] = {
+ SND_SOC_DAPM_INPUT("spdif-in"),
+};
+
+static const struct snd_soc_dapm_route dir_routes[] = {
+ { "Capture", NULL, "spdif-in" },
+};
+
+#define STUB_RATES SNDRV_PCM_RATE_8000_192000
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_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 struct snd_soc_dai_driver dir_stub_dai = {
+ .name = "dir-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 384,
+ .rates = STUB_RATES,
+ .formats = STUB_FORMATS,
+ },
+};
+
+static int spdif_dir_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&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", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, spdif_dir_dt_ids);
+#endif
+
+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),
+ },
+};
+
+module_platform_driver(spdif_dir_driver);
+
+MODULE_DESCRIPTION("ASoC SPDIF DIR driver");
+MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/spdif_transmitter.c b/kernel/sound/soc/codecs/spdif_transmitter.c
new file mode 100644
index 000000000..ef634a9ad
--- /dev/null
+++ b/kernel/sound/soc/codecs/spdif_transmitter.c
@@ -0,0 +1,92 @@
+/*
+ * ALSA SoC SPDIF DIT driver
+ *
+ * This driver is used by controllers which can operate in DIT (SPDI/F) where
+ * no codec is needed. This file provides stub codec that can be used
+ * in these configurations. TI DaVinci Audio controller uses this driver.
+ *
+ * Author: Steve Chen, <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>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <linux/of.h>
+
+#define DRV_NAME "spdif-dit"
+
+#define STUB_RATES SNDRV_PCM_RATE_8000_192000
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dapm_widget dit_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("spdif-out"),
+};
+
+static const struct snd_soc_dapm_route dit_routes[] = {
+ { "spdif-out", NULL, "Playback" },
+};
+
+static struct snd_soc_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 struct snd_soc_dai_driver dit_stub_dai = {
+ .name = "dit-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 384,
+ .rates = STUB_RATES,
+ .formats = STUB_FORMATS,
+ },
+};
+
+static int spdif_dit_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&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", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, spdif_dit_dt_ids);
+#endif
+
+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),
+ },
+};
+
+module_platform_driver(spdif_dit_driver);
+
+MODULE_AUTHOR("Steve Chen <schen@mvista.com>");
+MODULE_DESCRIPTION("SPDIF dummy codec driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/kernel/sound/soc/codecs/ssm2518.c b/kernel/sound/soc/codecs/ssm2518.c
new file mode 100644
index 000000000..67ea55adb
--- /dev/null
+++ b/kernel/sound/soc/codecs/ssm2518.c
@@ -0,0 +1,833 @@
+/*
+ * SSM2518 amplifier audio driver
+ *
+ * Copyright 2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#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 <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 "ssm2518.h"
+
+#define SSM2518_REG_POWER1 0x00
+#define SSM2518_REG_CLOCK 0x01
+#define SSM2518_REG_SAI_CTRL1 0x02
+#define SSM2518_REG_SAI_CTRL2 0x03
+#define SSM2518_REG_CHAN_MAP 0x04
+#define SSM2518_REG_LEFT_VOL 0x05
+#define SSM2518_REG_RIGHT_VOL 0x06
+#define SSM2518_REG_MUTE_CTRL 0x07
+#define SSM2518_REG_FAULT_CTRL 0x08
+#define SSM2518_REG_POWER2 0x09
+#define SSM2518_REG_DRC_1 0x0a
+#define SSM2518_REG_DRC_2 0x0b
+#define SSM2518_REG_DRC_3 0x0c
+#define SSM2518_REG_DRC_4 0x0d
+#define SSM2518_REG_DRC_5 0x0e
+#define SSM2518_REG_DRC_6 0x0f
+#define SSM2518_REG_DRC_7 0x10
+#define SSM2518_REG_DRC_8 0x11
+#define SSM2518_REG_DRC_9 0x12
+
+#define SSM2518_POWER1_RESET BIT(7)
+#define SSM2518_POWER1_NO_BCLK BIT(5)
+#define SSM2518_POWER1_MCS_MASK (0xf << 1)
+#define SSM2518_POWER1_MCS_64FS (0x0 << 1)
+#define SSM2518_POWER1_MCS_128FS (0x1 << 1)
+#define SSM2518_POWER1_MCS_256FS (0x2 << 1)
+#define SSM2518_POWER1_MCS_384FS (0x3 << 1)
+#define SSM2518_POWER1_MCS_512FS (0x4 << 1)
+#define SSM2518_POWER1_MCS_768FS (0x5 << 1)
+#define SSM2518_POWER1_MCS_100FS (0x6 << 1)
+#define SSM2518_POWER1_MCS_200FS (0x7 << 1)
+#define SSM2518_POWER1_MCS_400FS (0x8 << 1)
+#define SSM2518_POWER1_SPWDN BIT(0)
+
+#define SSM2518_CLOCK_ASR BIT(0)
+
+#define SSM2518_SAI_CTRL1_FMT_MASK (0x3 << 5)
+#define SSM2518_SAI_CTRL1_FMT_I2S (0x0 << 5)
+#define SSM2518_SAI_CTRL1_FMT_LJ (0x1 << 5)
+#define SSM2518_SAI_CTRL1_FMT_RJ_24BIT (0x2 << 5)
+#define SSM2518_SAI_CTRL1_FMT_RJ_16BIT (0x3 << 5)
+
+#define SSM2518_SAI_CTRL1_SAI_MASK (0x7 << 2)
+#define SSM2518_SAI_CTRL1_SAI_I2S (0x0 << 2)
+#define SSM2518_SAI_CTRL1_SAI_TDM_2 (0x1 << 2)
+#define SSM2518_SAI_CTRL1_SAI_TDM_4 (0x2 << 2)
+#define SSM2518_SAI_CTRL1_SAI_TDM_8 (0x3 << 2)
+#define SSM2518_SAI_CTRL1_SAI_TDM_16 (0x4 << 2)
+#define SSM2518_SAI_CTRL1_SAI_MONO (0x5 << 2)
+
+#define SSM2518_SAI_CTRL1_FS_MASK (0x3)
+#define SSM2518_SAI_CTRL1_FS_8000_12000 (0x0)
+#define SSM2518_SAI_CTRL1_FS_16000_24000 (0x1)
+#define SSM2518_SAI_CTRL1_FS_32000_48000 (0x2)
+#define SSM2518_SAI_CTRL1_FS_64000_96000 (0x3)
+
+#define SSM2518_SAI_CTRL2_BCLK_INTERAL BIT(7)
+#define SSM2518_SAI_CTRL2_LRCLK_PULSE BIT(6)
+#define SSM2518_SAI_CTRL2_LRCLK_INVERT BIT(5)
+#define SSM2518_SAI_CTRL2_MSB BIT(4)
+#define SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK (0x3 << 2)
+#define SSM2518_SAI_CTRL2_SLOT_WIDTH_32 (0x0 << 2)
+#define SSM2518_SAI_CTRL2_SLOT_WIDTH_24 (0x1 << 2)
+#define SSM2518_SAI_CTRL2_SLOT_WIDTH_16 (0x2 << 2)
+#define SSM2518_SAI_CTRL2_BCLK_INVERT BIT(1)
+
+#define SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET 4
+#define SSM2518_CHAN_MAP_RIGHT_SLOT_MASK 0xf0
+#define SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET 0
+#define SSM2518_CHAN_MAP_LEFT_SLOT_MASK 0x0f
+
+#define SSM2518_MUTE_CTRL_ANA_GAIN BIT(5)
+#define SSM2518_MUTE_CTRL_MUTE_MASTER BIT(0)
+
+#define SSM2518_POWER2_APWDN BIT(0)
+
+#define SSM2518_DAC_MUTE BIT(6)
+#define SSM2518_DAC_FS_MASK 0x07
+#define SSM2518_DAC_FS_8000 0x00
+#define SSM2518_DAC_FS_16000 0x01
+#define SSM2518_DAC_FS_32000 0x02
+#define SSM2518_DAC_FS_64000 0x03
+#define SSM2518_DAC_FS_128000 0x04
+
+struct ssm2518 {
+ struct regmap *regmap;
+ bool right_j;
+
+ unsigned int sysclk;
+ const struct snd_pcm_hw_constraint_list *constraints;
+
+ int enable_gpio;
+};
+
+static const struct reg_default ssm2518_reg_defaults[] = {
+ { 0x00, 0x05 },
+ { 0x01, 0x00 },
+ { 0x02, 0x02 },
+ { 0x03, 0x00 },
+ { 0x04, 0x10 },
+ { 0x05, 0x40 },
+ { 0x06, 0x40 },
+ { 0x07, 0x81 },
+ { 0x08, 0x0c },
+ { 0x09, 0x99 },
+ { 0x0a, 0x7c },
+ { 0x0b, 0x5b },
+ { 0x0c, 0x57 },
+ { 0x0d, 0x89 },
+ { 0x0e, 0x8c },
+ { 0x0f, 0x77 },
+ { 0x10, 0x26 },
+ { 0x11, 0x1c },
+ { 0x12, 0x97 },
+};
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(ssm2518_vol_tlv, -7125, 2400);
+static const DECLARE_TLV_DB_SCALE(ssm2518_compressor_tlv, -3400, 200, 0);
+static const DECLARE_TLV_DB_SCALE(ssm2518_expander_tlv, -8100, 300, 0);
+static const DECLARE_TLV_DB_SCALE(ssm2518_noise_gate_tlv, -9600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(ssm2518_post_drc_tlv, -2400, 300, 0);
+
+static const DECLARE_TLV_DB_RANGE(ssm2518_limiter_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(-2200, 200, 0),
+ 7, 15, TLV_DB_SCALE_ITEM(-800, 100, 0),
+);
+
+static const char * const ssm2518_drc_peak_detector_attack_time_text[] = {
+ "0 ms", "0.1 ms", "0.19 ms", "0.37 ms", "0.75 ms", "1.5 ms", "3 ms",
+ "6 ms", "12 ms", "24 ms", "48 ms", "96 ms", "192 ms", "384 ms",
+ "768 ms", "1536 ms",
+};
+
+static const char * const ssm2518_drc_peak_detector_release_time_text[] = {
+ "0 ms", "1.5 ms", "3 ms", "6 ms", "12 ms", "24 ms", "48 ms", "96 ms",
+ "192 ms", "384 ms", "768 ms", "1536 ms", "3072 ms", "6144 ms",
+ "12288 ms", "24576 ms"
+};
+
+static const char * const ssm2518_drc_hold_time_text[] = {
+ "0 ms", "0.67 ms", "1.33 ms", "2.67 ms", "5.33 ms", "10.66 ms",
+ "21.32 ms", "42.64 ms", "85.28 ms", "170.56 ms", "341.12 ms",
+ "682.24 ms", "1364 ms",
+};
+
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_attack_time_enum,
+ SSM2518_REG_DRC_2, 4, ssm2518_drc_peak_detector_attack_time_text);
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_release_time_enum,
+ SSM2518_REG_DRC_2, 0, ssm2518_drc_peak_detector_release_time_text);
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_attack_time_enum,
+ SSM2518_REG_DRC_6, 4, ssm2518_drc_peak_detector_attack_time_text);
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_decay_time_enum,
+ SSM2518_REG_DRC_6, 0, ssm2518_drc_peak_detector_release_time_text);
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_hold_time_enum,
+ SSM2518_REG_DRC_7, 4, ssm2518_drc_hold_time_text);
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_noise_gate_hold_time_enum,
+ SSM2518_REG_DRC_7, 0, ssm2518_drc_hold_time_text);
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_rms_averaging_time_enum,
+ SSM2518_REG_DRC_9, 0, ssm2518_drc_peak_detector_release_time_text);
+
+static const struct snd_kcontrol_new ssm2518_snd_controls[] = {
+ SOC_SINGLE("Playback De-emphasis Switch", SSM2518_REG_MUTE_CTRL,
+ 4, 1, 0),
+ SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2518_REG_LEFT_VOL,
+ SSM2518_REG_RIGHT_VOL, 0, 0xff, 1, ssm2518_vol_tlv),
+ SOC_DOUBLE("Master Playback Switch", SSM2518_REG_MUTE_CTRL, 2, 1, 1, 1),
+
+ SOC_SINGLE("Amp Low Power Mode Switch", SSM2518_REG_POWER2, 4, 1, 0),
+ SOC_SINGLE("DAC Low Power Mode Switch", SSM2518_REG_POWER2, 3, 1, 0),
+
+ SOC_SINGLE("DRC Limiter Switch", SSM2518_REG_DRC_1, 5, 1, 0),
+ SOC_SINGLE("DRC Compressor Switch", SSM2518_REG_DRC_1, 4, 1, 0),
+ SOC_SINGLE("DRC Expander Switch", SSM2518_REG_DRC_1, 3, 1, 0),
+ SOC_SINGLE("DRC Noise Gate Switch", SSM2518_REG_DRC_1, 2, 1, 0),
+ SOC_DOUBLE("DRC Switch", SSM2518_REG_DRC_1, 0, 1, 1, 0),
+
+ SOC_SINGLE_TLV("DRC Limiter Threshold Volume",
+ SSM2518_REG_DRC_3, 4, 15, 1, ssm2518_limiter_tlv),
+ SOC_SINGLE_TLV("DRC Compressor Lower Threshold Volume",
+ SSM2518_REG_DRC_3, 0, 15, 1, ssm2518_compressor_tlv),
+ SOC_SINGLE_TLV("DRC Expander Upper Threshold Volume", SSM2518_REG_DRC_4,
+ 4, 15, 1, ssm2518_expander_tlv),
+ SOC_SINGLE_TLV("DRC Noise Gate Threshold Volume",
+ SSM2518_REG_DRC_4, 0, 15, 1, ssm2518_noise_gate_tlv),
+ SOC_SINGLE_TLV("DRC Upper Output Threshold Volume",
+ SSM2518_REG_DRC_5, 4, 15, 1, ssm2518_limiter_tlv),
+ SOC_SINGLE_TLV("DRC Lower Output Threshold Volume",
+ SSM2518_REG_DRC_5, 0, 15, 1, ssm2518_noise_gate_tlv),
+ SOC_SINGLE_TLV("DRC Post Volume", SSM2518_REG_DRC_8,
+ 2, 15, 1, ssm2518_post_drc_tlv),
+
+ SOC_ENUM("DRC Peak Detector Attack Time",
+ ssm2518_drc_peak_detector_attack_time_enum),
+ SOC_ENUM("DRC Peak Detector Release Time",
+ ssm2518_drc_peak_detector_release_time_enum),
+ SOC_ENUM("DRC Attack Time", ssm2518_drc_attack_time_enum),
+ SOC_ENUM("DRC Decay Time", ssm2518_drc_decay_time_enum),
+ SOC_ENUM("DRC Hold Time", ssm2518_drc_hold_time_enum),
+ SOC_ENUM("DRC Noise Gate Hold Time",
+ ssm2518_drc_noise_gate_hold_time_enum),
+ SOC_ENUM("DRC RMS Averaging Time", ssm2518_drc_rms_averaging_time_enum),
+};
+
+static const struct snd_soc_dapm_widget ssm2518_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DACL", "HiFi Playback", SSM2518_REG_POWER2, 1, 1),
+ SND_SOC_DAPM_DAC("DACR", "HiFi Playback", SSM2518_REG_POWER2, 2, 1),
+
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+};
+
+static const struct snd_soc_dapm_route ssm2518_routes[] = {
+ { "OUTL", NULL, "DACL" },
+ { "OUTR", NULL, "DACR" },
+};
+
+struct ssm2518_mcs_lut {
+ unsigned int rate;
+ const unsigned int *sysclks;
+};
+
+static const unsigned int ssm2518_sysclks_2048000[] = {
+ 2048000, 4096000, 8192000, 12288000, 16384000, 24576000,
+ 3200000, 6400000, 12800000, 0
+};
+
+static const unsigned int ssm2518_sysclks_2822000[] = {
+ 2822000, 5644800, 11289600, 16934400, 22579200, 33868800,
+ 4410000, 8820000, 17640000, 0
+};
+
+static const unsigned int ssm2518_sysclks_3072000[] = {
+ 3072000, 6144000, 12288000, 16384000, 24576000, 38864000,
+ 4800000, 9600000, 19200000, 0
+};
+
+static const struct ssm2518_mcs_lut ssm2518_mcs_lut[] = {
+ { 8000, ssm2518_sysclks_2048000, },
+ { 11025, ssm2518_sysclks_2822000, },
+ { 12000, ssm2518_sysclks_3072000, },
+ { 16000, ssm2518_sysclks_2048000, },
+ { 24000, ssm2518_sysclks_3072000, },
+ { 22050, ssm2518_sysclks_2822000, },
+ { 32000, ssm2518_sysclks_2048000, },
+ { 44100, ssm2518_sysclks_2822000, },
+ { 48000, ssm2518_sysclks_3072000, },
+ { 96000, ssm2518_sysclks_3072000, },
+};
+
+static const unsigned int ssm2518_rates_2048000[] = {
+ 8000, 16000, 32000,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2048000 = {
+ .list = ssm2518_rates_2048000,
+ .count = ARRAY_SIZE(ssm2518_rates_2048000),
+};
+
+static const unsigned int ssm2518_rates_2822000[] = {
+ 11025, 22050, 44100,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2822000 = {
+ .list = ssm2518_rates_2822000,
+ .count = ARRAY_SIZE(ssm2518_rates_2822000),
+};
+
+static const unsigned int ssm2518_rates_3072000[] = {
+ 12000, 24000, 48000, 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2518_constraints_3072000 = {
+ .list = ssm2518_rates_3072000,
+ .count = ARRAY_SIZE(ssm2518_rates_3072000),
+};
+
+static const unsigned int ssm2518_rates_12288000[] = {
+ 8000, 12000, 16000, 24000, 32000, 48000, 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2518_constraints_12288000 = {
+ .list = ssm2518_rates_12288000,
+ .count = ARRAY_SIZE(ssm2518_rates_12288000),
+};
+
+static unsigned int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
+ unsigned int rate)
+{
+ const unsigned int *sysclks = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ssm2518_mcs_lut); i++) {
+ if (ssm2518_mcs_lut[i].rate == rate) {
+ sysclks = ssm2518_mcs_lut[i].sysclks;
+ break;
+ }
+ }
+
+ if (!sysclks)
+ return -EINVAL;
+
+ for (i = 0; sysclks[i]; i++) {
+ if (sysclks[i] == ssm2518->sysclk)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int ssm2518_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ unsigned int rate = params_rate(params);
+ unsigned int ctrl1, ctrl1_mask;
+ int mcs;
+ int ret;
+
+ mcs = ssm2518_lookup_mcs(ssm2518, rate);
+ if (mcs < 0)
+ return mcs;
+
+ ctrl1_mask = SSM2518_SAI_CTRL1_FS_MASK;
+
+ if (rate >= 8000 && rate <= 12000)
+ ctrl1 = SSM2518_SAI_CTRL1_FS_8000_12000;
+ else if (rate >= 16000 && rate <= 24000)
+ ctrl1 = SSM2518_SAI_CTRL1_FS_16000_24000;
+ else if (rate >= 32000 && rate <= 48000)
+ ctrl1 = SSM2518_SAI_CTRL1_FS_32000_48000;
+ else if (rate >= 64000 && rate <= 96000)
+ ctrl1 = SSM2518_SAI_CTRL1_FS_64000_96000;
+ else
+ return -EINVAL;
+
+ if (ssm2518->right_j) {
+ switch (params_width(params)) {
+ case 16:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_16BIT;
+ break;
+ case 24:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ctrl1_mask |= SSM2518_SAI_CTRL1_FMT_MASK;
+ }
+
+ /* Disable auto samplerate detection */
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_CLOCK,
+ SSM2518_CLOCK_ASR, SSM2518_CLOCK_ASR);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1,
+ ctrl1_mask, ctrl1);
+ if (ret < 0)
+ return ret;
+
+ return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_MCS_MASK, mcs << 1);
+}
+
+static int ssm2518_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int val;
+
+ if (mute)
+ val = SSM2518_MUTE_CTRL_MUTE_MASTER;
+ else
+ val = 0;
+
+ return regmap_update_bits(ssm2518->regmap, SSM2518_REG_MUTE_CTRL,
+ SSM2518_MUTE_CTRL_MUTE_MASTER, val);
+}
+
+static int ssm2518_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int ctrl1 = 0, ctrl2 = 0;
+ bool invert_fclk;
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ invert_fclk = false;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT;
+ invert_fclk = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert_fclk = true;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT;
+ invert_fclk = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ssm2518->right_j = false;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ;
+ invert_fclk = !invert_fclk;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT;
+ ssm2518->right_j = true;
+ invert_fclk = !invert_fclk;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE;
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S;
+ invert_fclk = false;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE;
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ;
+ invert_fclk = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (invert_fclk)
+ ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_INVERT;
+
+ ret = regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, ctrl1);
+ if (ret)
+ return ret;
+
+ return regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL2, ctrl2);
+}
+
+static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable)
+{
+ int ret = 0;
+
+ if (!enable) {
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_SPWDN, SSM2518_POWER1_SPWDN);
+ regcache_mark_dirty(ssm2518->regmap);
+ }
+
+ if (gpio_is_valid(ssm2518->enable_gpio))
+ gpio_set_value(ssm2518->enable_gpio, enable);
+
+ regcache_cache_only(ssm2518->regmap, !enable);
+
+ if (enable) {
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_SPWDN | SSM2518_POWER1_RESET, 0x00);
+ regcache_sync(ssm2518->regmap);
+ }
+
+ return ret;
+}
+
+static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ ret = ssm2518_set_power(ssm2518, true);
+ break;
+ case SND_SOC_BIAS_OFF:
+ ret = ssm2518_set_power(ssm2518, false);
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+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);
+ unsigned int ctrl1, ctrl2;
+ int left_slot, right_slot;
+ int ret;
+
+ if (slots == 0)
+ return regmap_update_bits(ssm2518->regmap,
+ SSM2518_REG_SAI_CTRL1, SSM2518_SAI_CTRL1_SAI_MASK,
+ SSM2518_SAI_CTRL1_SAI_I2S);
+
+ if (tx_mask == 0 || rx_mask != 0)
+ return -EINVAL;
+
+ if (slots == 1) {
+ if (tx_mask != 1)
+ return -EINVAL;
+ left_slot = 0;
+ right_slot = 0;
+ } else {
+ /* We assume the left channel < right channel */
+ left_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << left_slot);
+ if (tx_mask == 0) {
+ right_slot = left_slot;
+ } else {
+ right_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << right_slot);
+ }
+ }
+
+ if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
+ return -EINVAL;
+
+ switch (width) {
+ case 16:
+ ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_16;
+ break;
+ case 24:
+ ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_24;
+ break;
+ case 32:
+ ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slots) {
+ case 1:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_MONO;
+ break;
+ case 2:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_2;
+ break;
+ case 4:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_4;
+ break;
+ case 8:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_8;
+ break;
+ case 16:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_write(ssm2518->regmap, SSM2518_REG_CHAN_MAP,
+ (left_slot << SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET) |
+ (right_slot << SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1,
+ SSM2518_SAI_CTRL1_SAI_MASK, ctrl1);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL2,
+ SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK, ctrl2);
+}
+
+static int ssm2518_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+
+ if (ssm2518->constraints)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, ssm2518->constraints);
+
+ return 0;
+}
+
+#define SSM2518_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32)
+
+static const struct snd_soc_dai_ops ssm2518_dai_ops = {
+ .startup = ssm2518_startup,
+ .hw_params = ssm2518_hw_params,
+ .digital_mute = ssm2518_mute,
+ .set_fmt = ssm2518_set_dai_fmt,
+ .set_tdm_slot = ssm2518_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver ssm2518_dai = {
+ .name = "ssm2518-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SSM2518_FORMATS,
+ },
+ .ops = &ssm2518_dai_ops,
+};
+
+static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ if (clk_id != SSM2518_SYSCLK)
+ return -EINVAL;
+
+ switch (source) {
+ case SSM2518_SYSCLK_SRC_MCLK:
+ val = 0;
+ break;
+ case SSM2518_SYSCLK_SRC_BCLK:
+ /* In this case the bitclock is used as the system clock, and
+ * the bitclock signal needs to be connected to the MCLK pin and
+ * the BCLK pin is left unconnected */
+ val = SSM2518_POWER1_NO_BCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (freq) {
+ case 0:
+ ssm2518->constraints = NULL;
+ break;
+ case 2048000:
+ case 4096000:
+ case 8192000:
+ case 3200000:
+ case 6400000:
+ case 12800000:
+ ssm2518->constraints = &ssm2518_constraints_2048000;
+ break;
+ case 2822000:
+ case 5644800:
+ case 11289600:
+ case 16934400:
+ case 22579200:
+ case 33868800:
+ case 4410000:
+ case 8820000:
+ case 17640000:
+ ssm2518->constraints = &ssm2518_constraints_2822000;
+ break;
+ case 3072000:
+ case 6144000:
+ case 38864000:
+ case 4800000:
+ case 9600000:
+ case 19200000:
+ ssm2518->constraints = &ssm2518_constraints_3072000;
+ break;
+ case 12288000:
+ case 16384000:
+ case 24576000:
+ ssm2518->constraints = &ssm2518_constraints_12288000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ssm2518->sysclk = freq;
+
+ return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_NO_BCLK, val);
+}
+
+static 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 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)
+{
+ struct ssm2518_platform_data *pdata = i2c->dev.platform_data;
+ struct ssm2518 *ssm2518;
+ int ret;
+
+ ssm2518 = devm_kzalloc(&i2c->dev, sizeof(*ssm2518), GFP_KERNEL);
+ 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;
+ }
+
+ 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;
+ }
+
+ i2c_set_clientdata(i2c, ssm2518);
+
+ ssm2518->regmap = devm_regmap_init_i2c(i2c, &ssm2518_regmap_config);
+ if (IS_ERR(ssm2518->regmap))
+ return PTR_ERR(ssm2518->regmap);
+
+ /*
+ * The reset bit is obviously volatile, but we need to be able to cache
+ * the other bits in the register, so we can't just mark the whole
+ * register as volatile. Since this is the only place where we'll ever
+ * touch the reset bit just bypass the cache for this operation.
+ */
+ regcache_cache_bypass(ssm2518->regmap, true);
+ ret = regmap_write(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_RESET);
+ regcache_cache_bypass(ssm2518->regmap, false);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER2,
+ SSM2518_POWER2_APWDN, 0x00);
+ if (ret)
+ return ret;
+
+ ret = ssm2518_set_power(ssm2518, false);
+ if (ret)
+ return ret;
+
+ return snd_soc_register_codec(&i2c->dev, &ssm2518_codec_driver,
+ &ssm2518_dai, 1);
+}
+
+static int ssm2518_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id ssm2518_i2c_ids[] = {
+ { "ssm2518", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids);
+
+static struct i2c_driver ssm2518_driver = {
+ .driver = {
+ .name = "ssm2518",
+ .owner = THIS_MODULE,
+ },
+ .probe = ssm2518_i2c_probe,
+ .remove = ssm2518_i2c_remove,
+ .id_table = ssm2518_i2c_ids,
+};
+module_i2c_driver(ssm2518_driver);
+
+MODULE_DESCRIPTION("ASoC SSM2518 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ssm2518.h b/kernel/sound/soc/codecs/ssm2518.h
new file mode 100644
index 000000000..62511d805
--- /dev/null
+++ b/kernel/sound/soc/codecs/ssm2518.h
@@ -0,0 +1,20 @@
+/*
+ * 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__
+#define __SND_SOC_CODECS_SSM2518_H__
+
+#define SSM2518_SYSCLK 0
+
+enum ssm2518_sysclk_src {
+ SSM2518_SYSCLK_SRC_MCLK = 0,
+ SSM2518_SYSCLK_SRC_BCLK = 1,
+};
+
+#endif
diff --git a/kernel/sound/soc/codecs/ssm2602-i2c.c b/kernel/sound/soc/codecs/ssm2602-i2c.c
new file mode 100644
index 000000000..0d9779d6b
--- /dev/null
+++ b/kernel/sound/soc/codecs/ssm2602-i2c.c
@@ -0,0 +1,66 @@
+/*
+ * SSM2602/SSM2603/SSM2604 I2C audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "ssm2602.h"
+
+/*
+ * ssm2602 2 wire address is determined by GPIO5
+ * state during powerup.
+ * low = 0x1a
+ * high = 0x1b
+ */
+static int ssm2602_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return ssm2602_probe(&client->dev, id->driver_data,
+ 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 },
+ { "ssm2604", SSM2604 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
+
+static const struct of_device_id ssm2602_of_match[] = {
+ { .compatible = "adi,ssm2602", },
+ { .compatible = "adi,ssm2603", },
+ { .compatible = "adi,ssm2604", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ssm2602_of_match);
+
+static struct i2c_driver ssm2602_i2c_driver = {
+ .driver = {
+ .name = "ssm2602",
+ .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);
+
+MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 I2C driver");
+MODULE_AUTHOR("Cliff Cai");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ssm2602-spi.c b/kernel/sound/soc/codecs/ssm2602-spi.c
new file mode 100644
index 000000000..b5df14fbe
--- /dev/null
+++ b/kernel/sound/soc/codecs/ssm2602-spi.c
@@ -0,0 +1,48 @@
+/*
+ * SSM2602 SPI audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "ssm2602.h"
+
+static int ssm2602_spi_probe(struct spi_device *spi)
+{
+ return ssm2602_probe(&spi->dev, SSM2602,
+ devm_regmap_init_spi(spi, &ssm2602_regmap_config));
+}
+
+static 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", },
+ { }
+};
+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);
+
+MODULE_DESCRIPTION("ASoC SSM2602 SPI driver");
+MODULE_AUTHOR("Cliff Cai");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ssm2602.c b/kernel/sound/soc/codecs/ssm2602.c
new file mode 100644
index 000000000..314eaece1
--- /dev/null
+++ b/kernel/sound/soc/codecs/ssm2602.c
@@ -0,0 +1,651 @@
+/*
+ * 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
+ */
+
+#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 "ssm2602.h"
+
+/* codec private data */
+struct ssm2602_priv {
+ unsigned int sysclk;
+ const struct snd_pcm_hw_constraint_list *sysclk_constraints;
+
+ struct regmap *regmap;
+
+ enum ssm2602_type type;
+ unsigned int clk_out_pwr;
+};
+
+/*
+ * ssm2602 register cache
+ * We can't read the ssm2602 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ * There is no point in caching the reset register
+ */
+static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
+ 0x0097, 0x0097, 0x0079, 0x0079,
+ 0x000a, 0x0008, 0x009f, 0x000a,
+ 0x0000, 0x0000
+};
+
+
+/*Appending several "None"s just for OSS mixer use*/
+static const char *ssm2602_input_select[] = {
+ "Line", "Mic",
+};
+
+static const char *ssm2602_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+
+static const struct soc_enum ssm2602_enum[] = {
+ SOC_ENUM_SINGLE(SSM2602_APANA, 2, ARRAY_SIZE(ssm2602_input_select),
+ ssm2602_input_select),
+ SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, ARRAY_SIZE(ssm2602_deemph),
+ ssm2602_deemph),
+};
+
+static const unsigned int ssm260x_outmix_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
+ 48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0),
+};
+
+static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0);
+
+static const struct snd_kcontrol_new ssm260x_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 45, 0,
+ ssm260x_inpga_tlv),
+SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
+
+SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1),
+SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0),
+
+SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]),
+};
+
+static const struct snd_kcontrol_new ssm2602_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V,
+ 0, 127, 0, ssm260x_outmix_tlv),
+SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
+ 7, 1, 0),
+SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1,
+ ssm260x_sidetone_tlv),
+
+SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
+SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
+SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
+};
+
+/* Output Mixer */
+static const struct snd_kcontrol_new ssm260x_output_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0),
+SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0),
+SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
+};
+
+/* Input mux */
+static const struct snd_kcontrol_new ssm2602_input_mux_controls =
+SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
+
+static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
+SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("Digital Core Power", SSM2602_ACTIVE, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_INPUT("RLINEIN"),
+SND_SOC_DAPM_INPUT("LLINEIN"),
+};
+
+static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
+ ssm260x_output_mixer_controls,
+ ARRAY_SIZE(ssm260x_output_mixer_controls)),
+
+SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
+SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
+
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
+SND_SOC_DAPM_INPUT("MICIN"),
+};
+
+static const struct snd_soc_dapm_widget ssm2604_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
+ ssm260x_output_mixer_controls,
+ ARRAY_SIZE(ssm260x_output_mixer_controls) - 1), /* Last element is the mic */
+};
+
+static const struct snd_soc_dapm_route ssm260x_routes[] = {
+ {"DAC", NULL, "Digital Core Power"},
+ {"ADC", NULL, "Digital Core Power"},
+
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
+
+ {"ROUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+
+ {"Line Input", NULL, "LLINEIN"},
+ {"Line Input", NULL, "RLINEIN"},
+};
+
+static const struct snd_soc_dapm_route ssm2602_routes[] = {
+ {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
+
+ {"RHPOUT", NULL, "Output Mixer"},
+ {"LHPOUT", NULL, "Output Mixer"},
+
+ {"Input Mux", "Line", "Line Input"},
+ {"Input Mux", "Mic", "Mic Bias"},
+ {"ADC", NULL, "Input Mux"},
+
+ {"Mic Bias", NULL, "MICIN"},
+};
+
+static const struct snd_soc_dapm_route ssm2604_routes[] = {
+ {"ADC", NULL, "Line Input"},
+};
+
+static const unsigned int ssm2602_rates_12288000[] = {
+ 8000, 16000, 32000, 48000, 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = {
+ .list = ssm2602_rates_12288000,
+ .count = ARRAY_SIZE(ssm2602_rates_12288000),
+};
+
+static const unsigned int ssm2602_rates_11289600[] = {
+ 8000, 11025, 22050, 44100, 88200,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = {
+ .list = ssm2602_rates_11289600,
+ .count = ARRAY_SIZE(ssm2602_rates_11289600),
+};
+
+struct ssm2602_coeff {
+ u32 mclk;
+ u32 rate;
+ u8 srate;
+};
+
+#define SSM2602_COEFF_SRATE(sr, bosr, usb) (((sr) << 2) | ((bosr) << 1) | (usb))
+
+/* codec mclk clock coefficients */
+static const struct ssm2602_coeff ssm2602_coeff_table[] = {
+ /* 48k */
+ {12288000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x0)},
+ {18432000, 48000, SSM2602_COEFF_SRATE(0x0, 0x1, 0x0)},
+ {12000000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x1)},
+
+ /* 32k */
+ {12288000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x0)},
+ {18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)},
+ {12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)},
+
+ /* 16k */
+ {12288000, 16000, SSM2602_COEFF_SRATE(0x5, 0x0, 0x0)},
+ {18432000, 16000, SSM2602_COEFF_SRATE(0x5, 0x1, 0x0)},
+ {12000000, 16000, SSM2602_COEFF_SRATE(0xa, 0x0, 0x1)},
+
+ /* 8k */
+ {12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)},
+ {18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)},
+ {11289600, 8000, SSM2602_COEFF_SRATE(0xb, 0x0, 0x0)},
+ {16934400, 8000, SSM2602_COEFF_SRATE(0xb, 0x1, 0x0)},
+ {12000000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x1)},
+
+ /* 96k */
+ {12288000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x0)},
+ {18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)},
+ {12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)},
+
+ /* 11.025k */
+ {11289600, 11025, SSM2602_COEFF_SRATE(0xc, 0x0, 0x0)},
+ {16934400, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x0)},
+ {12000000, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x1)},
+
+ /* 22.05k */
+ {11289600, 22050, SSM2602_COEFF_SRATE(0xd, 0x0, 0x0)},
+ {16934400, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x0)},
+ {12000000, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x1)},
+
+ /* 44.1k */
+ {11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)},
+ {16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)},
+ {12000000, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x1)},
+
+ /* 88.2k */
+ {11289600, 88200, SSM2602_COEFF_SRATE(0xf, 0x0, 0x0)},
+ {16934400, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x0)},
+ {12000000, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x1)},
+};
+
+static inline int ssm2602_get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
+ if (ssm2602_coeff_table[i].rate == rate &&
+ ssm2602_coeff_table[i].mclk == mclk)
+ return ssm2602_coeff_table[i].srate;
+ }
+ return -EINVAL;
+}
+
+static int ssm2602_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
+ unsigned int iface;
+
+ if (srate < 0)
+ return srate;
+
+ regmap_write(ssm2602->regmap, SSM2602_SRATE, srate);
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ iface = 0x0;
+ break;
+ case 20:
+ iface = 0x4;
+ break;
+ case 24:
+ iface = 0x8;
+ break;
+ case 32:
+ iface = 0xc;
+ break;
+ default:
+ return -EINVAL;
+ }
+ regmap_update_bits(ssm2602->regmap, SSM2602_IFACE,
+ IFACE_AUDIO_DATA_LEN, iface);
+ return 0;
+}
+
+static int ssm2602_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+
+ if (ssm2602->sysclk_constraints) {
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ ssm2602->sysclk_constraints);
+ }
+
+ return 0;
+}
+
+static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(dai->codec);
+
+ if (mute)
+ regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI,
+ APDIGI_ENABLE_DAC_MUTE,
+ APDIGI_ENABLE_DAC_MUTE);
+ else
+ regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI,
+ APDIGI_ENABLE_DAC_MUTE, 0);
+ return 0;
+}
+
+static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+
+ if (dir == SND_SOC_CLOCK_IN) {
+ if (clk_id != SSM2602_SYSCLK)
+ return -EINVAL;
+
+ switch (freq) {
+ case 12288000:
+ case 18432000:
+ ssm2602->sysclk_constraints = &ssm2602_constraints_12288000;
+ break;
+ case 11289600:
+ case 16934400:
+ ssm2602->sysclk_constraints = &ssm2602_constraints_11289600;
+ break;
+ case 12000000:
+ ssm2602->sysclk_constraints = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ssm2602->sysclk = freq;
+ } else {
+ unsigned int mask;
+
+ switch (clk_id) {
+ case SSM2602_CLK_CLKOUT:
+ mask = PWR_CLK_OUT_PDN;
+ break;
+ case SSM2602_CLK_XTO:
+ mask = PWR_OSC_PDN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (freq == 0)
+ ssm2602->clk_out_pwr |= mask;
+ else
+ ssm2602->clk_out_pwr &= ~mask;
+
+ regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
+ PWR_CLK_OUT_PDN | PWR_OSC_PDN, ssm2602->clk_out_pwr);
+ }
+
+ return 0;
+}
+
+static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec_dai->codec);
+ unsigned int iface = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface |= 0x0040;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0013;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0003;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface */
+ regmap_write(ssm2602->regmap, SSM2602_IFACE, iface);
+ return 0;
+}
+
+static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* vref/mid on, osc and clkout on if enabled */
+ regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
+ PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN,
+ ssm2602->clk_out_pwr);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* everything off except vref/vmid, */
+ regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
+ PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN,
+ PWR_CLK_OUT_PDN | PWR_OSC_PDN);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* everything off */
+ regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
+ PWR_POWER_OFF, PWR_POWER_OFF);
+ break;
+
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000)
+
+#define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops ssm2602_dai_ops = {
+ .startup = ssm2602_startup,
+ .hw_params = ssm2602_hw_params,
+ .digital_mute = ssm2602_mute,
+ .set_sysclk = ssm2602_set_dai_sysclk,
+ .set_fmt = ssm2602_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver ssm2602_dai = {
+ .name = "ssm2602-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SSM2602_RATES,
+ .formats = SSM2602_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SSM2602_RATES,
+ .formats = SSM2602_FORMATS,},
+ .ops = &ssm2602_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+};
+
+static int ssm2602_resume(struct snd_soc_codec *codec)
+{
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_sync(ssm2602->regmap);
+
+ return 0;
+}
+
+static int ssm2602_codec_probe(struct snd_soc_codec *codec)
+{
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int ret;
+
+ regmap_update_bits(ssm2602->regmap, SSM2602_LOUT1V,
+ LOUT1V_LRHP_BOTH, LOUT1V_LRHP_BOTH);
+ regmap_update_bits(ssm2602->regmap, SSM2602_ROUT1V,
+ ROUT1V_RLHP_BOTH, ROUT1V_RLHP_BOTH);
+
+ ret = snd_soc_add_codec_controls(codec, ssm2602_snd_controls,
+ ARRAY_SIZE(ssm2602_snd_controls));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets,
+ ARRAY_SIZE(ssm2602_dapm_widgets));
+ if (ret)
+ return ret;
+
+ return snd_soc_dapm_add_routes(dapm, ssm2602_routes,
+ ARRAY_SIZE(ssm2602_routes));
+}
+
+static int ssm2604_codec_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
+ ARRAY_SIZE(ssm2604_dapm_widgets));
+ if (ret)
+ return ret;
+
+ return snd_soc_dapm_add_routes(dapm, ssm2604_routes,
+ ARRAY_SIZE(ssm2604_routes));
+}
+
+static int ssm260x_codec_probe(struct snd_soc_codec *codec)
+{
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = regmap_write(ssm2602->regmap, SSM2602_RESET, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
+
+ /* set the update bits */
+ regmap_update_bits(ssm2602->regmap, SSM2602_LINVOL,
+ LINVOL_LRIN_BOTH, LINVOL_LRIN_BOTH);
+ regmap_update_bits(ssm2602->regmap, SSM2602_RINVOL,
+ RINVOL_RLIN_BOTH, RINVOL_RLIN_BOTH);
+ /*select Line in as default input*/
+ regmap_write(ssm2602->regmap, SSM2602_APANA, APANA_SELECT_DAC |
+ APANA_ENABLE_MIC_BOOST);
+
+ switch (ssm2602->type) {
+ case SSM2602:
+ ret = ssm2602_codec_probe(codec);
+ break;
+ case SSM2604:
+ ret = ssm2604_codec_probe(codec);
+ 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 bool ssm2602_register_volatile(struct device *dev, unsigned int reg)
+{
+ return reg == SSM2602_RESET;
+}
+
+const struct regmap_config ssm2602_regmap_config = {
+ .val_bits = 9,
+ .reg_bits = 7,
+
+ .max_register = SSM2602_RESET,
+ .volatile_reg = ssm2602_register_volatile,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults_raw = ssm2602_reg,
+ .num_reg_defaults_raw = ARRAY_SIZE(ssm2602_reg),
+};
+EXPORT_SYMBOL_GPL(ssm2602_regmap_config);
+
+int ssm2602_probe(struct device *dev, enum ssm2602_type type,
+ struct regmap *regmap)
+{
+ struct ssm2602_priv *ssm2602;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ssm2602 = devm_kzalloc(dev, sizeof(*ssm2602), GFP_KERNEL);
+ if (ssm2602 == NULL)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, ssm2602);
+ ssm2602->type = type;
+ ssm2602->regmap = regmap;
+
+ return snd_soc_register_codec(dev, &soc_codec_dev_ssm2602,
+ &ssm2602_dai, 1);
+}
+EXPORT_SYMBOL_GPL(ssm2602_probe);
+
+MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver");
+MODULE_AUTHOR("Cliff Cai");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/ssm2602.h b/kernel/sound/soc/codecs/ssm2602.h
new file mode 100644
index 000000000..747538847
--- /dev/null
+++ b/kernel/sound/soc/codecs/ssm2602.h
@@ -0,0 +1,139 @@
+/*
+ * File: sound/soc/codecs/ssm2602.h
+ * Author: Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created: Tue June 06 2008
+ *
+ * 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
+ */
+
+#ifndef _SSM2602_H
+#define _SSM2602_H
+
+#include <linux/regmap.h>
+
+struct device;
+
+enum ssm2602_type {
+ SSM2602,
+ SSM2604,
+};
+
+extern const struct regmap_config ssm2602_regmap_config;
+
+int ssm2602_probe(struct device *dev, enum ssm2602_type type,
+ struct regmap *regmap);
+
+/* SSM2602 Codec Register definitions */
+
+#define SSM2602_LINVOL 0x00
+#define SSM2602_RINVOL 0x01
+#define SSM2602_LOUT1V 0x02
+#define SSM2602_ROUT1V 0x03
+#define SSM2602_APANA 0x04
+#define SSM2602_APDIGI 0x05
+#define SSM2602_PWR 0x06
+#define SSM2602_IFACE 0x07
+#define SSM2602_SRATE 0x08
+#define SSM2602_ACTIVE 0x09
+#define SSM2602_RESET 0x0f
+
+/*SSM2602 Codec Register Field definitions
+ *(Mask value to extract the corresponding Register field)
+ */
+
+/*Left ADC Volume Control (SSM2602_REG_LEFT_ADC_VOL)*/
+#define LINVOL_LIN_VOL 0x01F /* Left Channel PGA Volume control */
+#define LINVOL_LIN_ENABLE_MUTE 0x080 /* Left Channel Input Mute */
+#define LINVOL_LRIN_BOTH 0x100 /* Left Channel Line Input Volume update */
+
+/*Right ADC Volume Control (SSM2602_REG_RIGHT_ADC_VOL)*/
+#define RINVOL_RIN_VOL 0x01F /* Right Channel PGA Volume control */
+#define RINVOL_RIN_ENABLE_MUTE 0x080 /* Right Channel Input Mute */
+#define RINVOL_RLIN_BOTH 0x100 /* Right Channel Line Input Volume update */
+
+/*Left DAC Volume Control (SSM2602_REG_LEFT_DAC_VOL)*/
+#define LOUT1V_LHP_VOL 0x07F /* Left Channel Headphone volume control */
+#define LOUT1V_ENABLE_LZC 0x080 /* Left Channel Zero cross detect enable */
+#define LOUT1V_LRHP_BOTH 0x100 /* Left Channel Headphone volume update */
+
+/*Right DAC Volume Control (SSM2602_REG_RIGHT_DAC_VOL)*/
+#define ROUT1V_RHP_VOL 0x07F /* Right Channel Headphone volume control */
+#define ROUT1V_ENABLE_RZC 0x080 /* Right Channel Zero cross detect enable */
+#define ROUT1V_RLHP_BOTH 0x100 /* Right Channel Headphone volume update */
+
+/*Analogue Audio Path Control (SSM2602_REG_ANALOGUE_PATH)*/
+#define APANA_ENABLE_MIC_BOOST 0x001 /* Primary Microphone Amplifier gain booster control */
+#define APANA_ENABLE_MIC_MUTE 0x002 /* Microphone Mute Control */
+#define APANA_ADC_IN_SELECT 0x004 /* Microphone/Line IN select to ADC (1=MIC, 0=Line In) */
+#define APANA_ENABLE_BYPASS 0x008 /* Line input bypass to line output */
+#define APANA_SELECT_DAC 0x010 /* Select DAC (1=Select DAC, 0=Don't Select DAC) */
+#define APANA_ENABLE_SIDETONE 0x020 /* Enable/Disable Side Tone */
+#define APANA_SIDETONE_ATTN 0x0C0 /* Side Tone Attenuation */
+#define APANA_ENABLE_MIC_BOOST2 0x100 /* Secondary Microphone Amplifier gain booster control */
+
+/*Digital Audio Path Control (SSM2602_REG_DIGITAL_PATH)*/
+#define APDIGI_ENABLE_ADC_HPF 0x001 /* Enable/Disable ADC Highpass Filter */
+#define APDIGI_DE_EMPHASIS 0x006 /* De-Emphasis Control */
+#define APDIGI_ENABLE_DAC_MUTE 0x008 /* DAC Mute Control */
+#define APDIGI_STORE_OFFSET 0x010 /* Store/Clear DC offset when HPF is disabled */
+
+/*Power Down Control (SSM2602_REG_POWER)
+ *(1=Enable PowerDown, 0=Disable PowerDown)
+ */
+#define PWR_LINE_IN_PDN 0x001 /* Line Input Power Down */
+#define PWR_MIC_PDN 0x002 /* Microphone Input & Bias Power Down */
+#define PWR_ADC_PDN 0x004 /* ADC Power Down */
+#define PWR_DAC_PDN 0x008 /* DAC Power Down */
+#define PWR_OUT_PDN 0x010 /* Outputs Power Down */
+#define PWR_OSC_PDN 0x020 /* Oscillator Power Down */
+#define PWR_CLK_OUT_PDN 0x040 /* CLKOUT Power Down */
+#define PWR_POWER_OFF 0x080 /* POWEROFF Mode */
+
+/*Digital Audio Interface Format (SSM2602_REG_DIGITAL_IFACE)*/
+#define IFACE_IFACE_FORMAT 0x003 /* Digital Audio input format control */
+#define IFACE_AUDIO_DATA_LEN 0x00C /* Audio Data word length control */
+#define IFACE_DAC_LR_POLARITY 0x010 /* Polarity Control for clocks in RJ,LJ and I2S modes */
+#define IFACE_DAC_LR_SWAP 0x020 /* Swap DAC data control */
+#define IFACE_ENABLE_MASTER 0x040 /* Enable/Disable Master Mode */
+#define IFACE_BCLK_INVERT 0x080 /* Bit Clock Inversion control */
+
+/*Sampling Control (SSM2602_REG_SAMPLING_CTRL)*/
+#define SRATE_ENABLE_USB_MODE 0x001 /* Enable/Disable USB Mode */
+#define SRATE_BOS_RATE 0x002 /* Base Over-Sampling rate */
+#define SRATE_SAMPLE_RATE 0x03C /* Clock setting condition (Sampling rate control) */
+#define SRATE_CORECLK_DIV2 0x040 /* Core Clock divider select */
+#define SRATE_CLKOUT_DIV2 0x080 /* Clock Out divider select */
+
+/*Active Control (SSM2602_REG_ACTIVE_CTRL)*/
+#define ACTIVE_ACTIVATE_CODEC 0x001 /* Activate Codec Digital Audio Interface */
+
+/*********************************************************************/
+
+#define SSM2602_CACHEREGNUM 10
+
+enum ssm2602_clk {
+ SSM2602_SYSCLK,
+ SSM2602_CLK_CLKOUT,
+ SSM2602_CLK_XTO
+};
+
+#endif
diff --git a/kernel/sound/soc/codecs/ssm4567.c b/kernel/sound/soc/codecs/ssm4567.c
new file mode 100644
index 000000000..a98448510
--- /dev/null
+++ b/kernel/sound/soc/codecs/ssm4567.c
@@ -0,0 +1,471 @@
+/*
+ * SSM4567 amplifier audio driver
+ *
+ * Copyright 2014 Google Chromium project.
+ * Author: Anatol Pomozov <anatol@chromium.org>
+ *
+ * Based on code copyright/by:
+ * Copyright 2013 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/init.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>
+
+#define SSM4567_REG_POWER_CTRL 0x00
+#define SSM4567_REG_AMP_SNS_CTRL 0x01
+#define SSM4567_REG_DAC_CTRL 0x02
+#define SSM4567_REG_DAC_VOLUME 0x03
+#define SSM4567_REG_SAI_CTRL_1 0x04
+#define SSM4567_REG_SAI_CTRL_2 0x05
+#define SSM4567_REG_SAI_PLACEMENT_1 0x06
+#define SSM4567_REG_SAI_PLACEMENT_2 0x07
+#define SSM4567_REG_SAI_PLACEMENT_3 0x08
+#define SSM4567_REG_SAI_PLACEMENT_4 0x09
+#define SSM4567_REG_SAI_PLACEMENT_5 0x0a
+#define SSM4567_REG_SAI_PLACEMENT_6 0x0b
+#define SSM4567_REG_BATTERY_V_OUT 0x0c
+#define SSM4567_REG_LIMITER_CTRL_1 0x0d
+#define SSM4567_REG_LIMITER_CTRL_2 0x0e
+#define SSM4567_REG_LIMITER_CTRL_3 0x0f
+#define SSM4567_REG_STATUS_1 0x10
+#define SSM4567_REG_STATUS_2 0x11
+#define SSM4567_REG_FAULT_CTRL 0x12
+#define SSM4567_REG_PDM_CTRL 0x13
+#define SSM4567_REG_MCLK_RATIO 0x14
+#define SSM4567_REG_BOOST_CTRL_1 0x15
+#define SSM4567_REG_BOOST_CTRL_2 0x16
+#define SSM4567_REG_SOFT_RESET 0xff
+
+/* POWER_CTRL */
+#define SSM4567_POWER_APWDN_EN BIT(7)
+#define SSM4567_POWER_BSNS_PWDN BIT(6)
+#define SSM4567_POWER_VSNS_PWDN BIT(5)
+#define SSM4567_POWER_ISNS_PWDN BIT(4)
+#define SSM4567_POWER_BOOST_PWDN BIT(3)
+#define SSM4567_POWER_AMP_PWDN BIT(2)
+#define SSM4567_POWER_VBAT_ONLY BIT(1)
+#define SSM4567_POWER_SPWDN BIT(0)
+
+/* DAC_CTRL */
+#define SSM4567_DAC_HV BIT(7)
+#define SSM4567_DAC_MUTE BIT(6)
+#define SSM4567_DAC_HPF BIT(5)
+#define SSM4567_DAC_LPM BIT(4)
+#define SSM4567_DAC_FS_MASK 0x7
+#define SSM4567_DAC_FS_8000_12000 0x0
+#define SSM4567_DAC_FS_16000_24000 0x1
+#define SSM4567_DAC_FS_32000_48000 0x2
+#define SSM4567_DAC_FS_64000_96000 0x3
+#define SSM4567_DAC_FS_128000_192000 0x4
+
+/* SAI_CTRL_1 */
+#define SSM4567_SAI_CTRL_1_BCLK BIT(6)
+#define SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK (0x3 << 4)
+#define SSM4567_SAI_CTRL_1_TDM_BLCKS_32 (0x0 << 4)
+#define SSM4567_SAI_CTRL_1_TDM_BLCKS_48 (0x1 << 4)
+#define SSM4567_SAI_CTRL_1_TDM_BLCKS_64 (0x2 << 4)
+#define SSM4567_SAI_CTRL_1_FSYNC BIT(3)
+#define SSM4567_SAI_CTRL_1_LJ BIT(2)
+#define SSM4567_SAI_CTRL_1_TDM BIT(1)
+#define SSM4567_SAI_CTRL_1_PDM BIT(0)
+
+/* SAI_CTRL_2 */
+#define SSM4567_SAI_CTRL_2_AUTO_SLOT BIT(3)
+#define SSM4567_SAI_CTRL_2_TDM_SLOT_MASK 0x7
+#define SSM4567_SAI_CTRL_2_TDM_SLOT(x) (x)
+
+struct ssm4567 {
+ struct regmap *regmap;
+};
+
+static const struct reg_default ssm4567_reg_defaults[] = {
+ { SSM4567_REG_POWER_CTRL, 0x81 },
+ { SSM4567_REG_AMP_SNS_CTRL, 0x09 },
+ { SSM4567_REG_DAC_CTRL, 0x32 },
+ { SSM4567_REG_DAC_VOLUME, 0x40 },
+ { SSM4567_REG_SAI_CTRL_1, 0x00 },
+ { SSM4567_REG_SAI_CTRL_2, 0x08 },
+ { SSM4567_REG_SAI_PLACEMENT_1, 0x01 },
+ { SSM4567_REG_SAI_PLACEMENT_2, 0x20 },
+ { SSM4567_REG_SAI_PLACEMENT_3, 0x32 },
+ { SSM4567_REG_SAI_PLACEMENT_4, 0x07 },
+ { SSM4567_REG_SAI_PLACEMENT_5, 0x07 },
+ { SSM4567_REG_SAI_PLACEMENT_6, 0x07 },
+ { SSM4567_REG_BATTERY_V_OUT, 0x00 },
+ { SSM4567_REG_LIMITER_CTRL_1, 0xa4 },
+ { SSM4567_REG_LIMITER_CTRL_2, 0x73 },
+ { SSM4567_REG_LIMITER_CTRL_3, 0x00 },
+ { SSM4567_REG_STATUS_1, 0x00 },
+ { SSM4567_REG_STATUS_2, 0x00 },
+ { SSM4567_REG_FAULT_CTRL, 0x30 },
+ { SSM4567_REG_PDM_CTRL, 0x40 },
+ { SSM4567_REG_MCLK_RATIO, 0x11 },
+ { SSM4567_REG_BOOST_CTRL_1, 0x03 },
+ { SSM4567_REG_BOOST_CTRL_2, 0x00 },
+ { SSM4567_REG_SOFT_RESET, 0x00 },
+};
+
+
+static bool ssm4567_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SSM4567_REG_POWER_CTRL ... SSM4567_REG_BOOST_CTRL_2:
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+static bool ssm4567_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SSM4567_REG_POWER_CTRL ... SSM4567_REG_SAI_PLACEMENT_6:
+ case SSM4567_REG_LIMITER_CTRL_1 ... SSM4567_REG_LIMITER_CTRL_3:
+ case SSM4567_REG_FAULT_CTRL ... SSM4567_REG_BOOST_CTRL_2:
+ /* The datasheet states that soft reset register is read-only,
+ * but logically it is write-only. */
+ case SSM4567_REG_SOFT_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool ssm4567_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SSM4567_REG_BATTERY_V_OUT:
+ case SSM4567_REG_STATUS_1 ... SSM4567_REG_STATUS_2:
+ case SSM4567_REG_SOFT_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(ssm4567_vol_tlv, -7125, 2400);
+
+static const struct snd_kcontrol_new ssm4567_snd_controls[] = {
+ SOC_SINGLE_TLV("Master Playback Volume", SSM4567_REG_DAC_VOLUME, 0,
+ 0xff, 1, ssm4567_vol_tlv),
+ SOC_SINGLE("DAC Low Power Mode Switch", SSM4567_REG_DAC_CTRL, 4, 1, 0),
+ SOC_SINGLE("DAC High Pass Filter Switch", SSM4567_REG_DAC_CTRL,
+ 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ssm4567_amplifier_boost_control =
+ SOC_DAPM_SINGLE("Switch", SSM4567_REG_POWER_CTRL, 1, 1, 1);
+
+static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM4567_REG_POWER_CTRL, 2, 1),
+ SND_SOC_DAPM_SWITCH("Amplifier Boost", SSM4567_REG_POWER_CTRL, 3, 1,
+ &ssm4567_amplifier_boost_control),
+
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route ssm4567_routes[] = {
+ { "OUT", NULL, "Amplifier Boost" },
+ { "Amplifier Boost", "Switch", "DAC" },
+ { "OUT", NULL, "DAC" },
+};
+
+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);
+ unsigned int rate = params_rate(params);
+ unsigned int dacfs;
+
+ if (rate >= 8000 && rate <= 12000)
+ dacfs = SSM4567_DAC_FS_8000_12000;
+ else if (rate >= 16000 && rate <= 24000)
+ dacfs = SSM4567_DAC_FS_16000_24000;
+ else if (rate >= 32000 && rate <= 48000)
+ dacfs = SSM4567_DAC_FS_32000_48000;
+ else if (rate >= 64000 && rate <= 96000)
+ dacfs = SSM4567_DAC_FS_64000_96000;
+ else if (rate >= 128000 && rate <= 192000)
+ dacfs = SSM4567_DAC_FS_128000_192000;
+ else
+ return -EINVAL;
+
+ return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL,
+ SSM4567_DAC_FS_MASK, dacfs);
+}
+
+static int ssm4567_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int val;
+
+ val = mute ? SSM4567_DAC_MUTE : 0;
+ return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL,
+ SSM4567_DAC_MUTE, val);
+}
+
+static int ssm4567_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai);
+ unsigned int blcks;
+ int slot;
+ int ret;
+
+ if (tx_mask == 0)
+ return -EINVAL;
+
+ if (rx_mask && rx_mask != tx_mask)
+ return -EINVAL;
+
+ slot = __ffs(tx_mask);
+ if (tx_mask != BIT(slot))
+ return -EINVAL;
+
+ switch (width) {
+ case 32:
+ blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_32;
+ break;
+ case 48:
+ blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_48;
+ break;
+ case 64:
+ blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_64;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_2,
+ SSM4567_SAI_CTRL_2_AUTO_SLOT | SSM4567_SAI_CTRL_2_TDM_SLOT_MASK,
+ SSM4567_SAI_CTRL_2_TDM_SLOT(slot));
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1,
+ SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK, blcks);
+}
+
+static int ssm4567_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai);
+ unsigned int ctrl1 = 0;
+ bool invert_fclk;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ invert_fclk = false;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1 |= SSM4567_SAI_CTRL_1_BCLK;
+ invert_fclk = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC;
+ invert_fclk = true;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ctrl1 |= SSM4567_SAI_CTRL_1_BCLK;
+ invert_fclk = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1 |= SSM4567_SAI_CTRL_1_LJ;
+ invert_fclk = !invert_fclk;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1 |= SSM4567_SAI_CTRL_1_TDM;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1 |= SSM4567_SAI_CTRL_1_TDM | SSM4567_SAI_CTRL_1_LJ;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ ctrl1 |= SSM4567_SAI_CTRL_1_PDM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (invert_fclk)
+ ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC;
+
+ return regmap_write(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1, ctrl1);
+}
+
+static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable)
+{
+ int ret = 0;
+
+ if (!enable) {
+ ret = regmap_update_bits(ssm4567->regmap,
+ SSM4567_REG_POWER_CTRL,
+ SSM4567_POWER_SPWDN, SSM4567_POWER_SPWDN);
+ regcache_mark_dirty(ssm4567->regmap);
+ }
+
+ regcache_cache_only(ssm4567->regmap, !enable);
+
+ if (enable) {
+ ret = regmap_update_bits(ssm4567->regmap,
+ SSM4567_REG_POWER_CTRL,
+ SSM4567_POWER_SPWDN, 0x00);
+ regcache_sync(ssm4567->regmap);
+ }
+
+ return ret;
+}
+
+static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ ret = ssm4567_set_power(ssm4567, true);
+ break;
+ case SND_SOC_BIAS_OFF:
+ ret = ssm4567_set_power(ssm4567, false);
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ssm4567_dai_ops = {
+ .hw_params = ssm4567_hw_params,
+ .digital_mute = ssm4567_mute,
+ .set_fmt = ssm4567_set_dai_fmt,
+ .set_tdm_slot = ssm4567_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver ssm4567_dai = {
+ .name = "ssm4567-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32,
+ },
+ .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 regmap_config ssm4567_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 8,
+
+ .max_register = SSM4567_REG_SOFT_RESET,
+ .readable_reg = ssm4567_readable_reg,
+ .writeable_reg = ssm4567_writeable_reg,
+ .volatile_reg = ssm4567_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = ssm4567_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ssm4567_reg_defaults),
+};
+
+static int ssm4567_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ssm4567 *ssm4567;
+ int ret;
+
+ ssm4567 = devm_kzalloc(&i2c->dev, sizeof(*ssm4567), GFP_KERNEL);
+ if (ssm4567 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, ssm4567);
+
+ ssm4567->regmap = devm_regmap_init_i2c(i2c, &ssm4567_regmap_config);
+ if (IS_ERR(ssm4567->regmap))
+ return PTR_ERR(ssm4567->regmap);
+
+ ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET, 0x00);
+ if (ret)
+ return ret;
+
+ ret = ssm4567_set_power(ssm4567, false);
+ if (ret)
+ return ret;
+
+ return snd_soc_register_codec(&i2c->dev, &ssm4567_codec_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 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids);
+
+static struct i2c_driver ssm4567_driver = {
+ .driver = {
+ .name = "ssm4567",
+ .owner = THIS_MODULE,
+ },
+ .probe = ssm4567_i2c_probe,
+ .remove = ssm4567_i2c_remove,
+ .id_table = ssm4567_i2c_ids,
+};
+module_i2c_driver(ssm4567_driver);
+
+MODULE_DESCRIPTION("ASoC SSM4567 driver");
+MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/sta32x.c b/kernel/sound/soc/codecs/sta32x.c
new file mode 100644
index 000000000..007a0e3bc
--- /dev/null
+++ b/kernel/sound/soc/codecs/sta32x.c
@@ -0,0 +1,1166 @@
+/*
+ * Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system
+ *
+ * Copyright: 2011 Raumfeld GmbH
+ * Author: Johannes Stezenbach <js@sig21.net>
+ *
+ * based on code from:
+ * Wolfson Microelectronics PLC.
+ * 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__
+
+#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/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/slab.h>
+#include <linux/workqueue.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/sta32x.h>
+#include "sta32x.h"
+
+#define STA32X_RATES (SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000)
+
+#define STA32X_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_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)
+
+/* Power-up register defaults */
+static const struct reg_default sta32x_regs[] = {
+ { 0x0, 0x63 },
+ { 0x1, 0x80 },
+ { 0x2, 0xc2 },
+ { 0x3, 0x40 },
+ { 0x4, 0xc2 },
+ { 0x5, 0x5c },
+ { 0x6, 0x10 },
+ { 0x7, 0xff },
+ { 0x8, 0x60 },
+ { 0x9, 0x60 },
+ { 0xa, 0x60 },
+ { 0xb, 0x80 },
+ { 0xc, 0x00 },
+ { 0xd, 0x00 },
+ { 0xe, 0x00 },
+ { 0xf, 0x40 },
+ { 0x10, 0x80 },
+ { 0x11, 0x77 },
+ { 0x12, 0x6a },
+ { 0x13, 0x69 },
+ { 0x14, 0x6a },
+ { 0x15, 0x69 },
+ { 0x16, 0x00 },
+ { 0x17, 0x00 },
+ { 0x18, 0x00 },
+ { 0x19, 0x00 },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x00 },
+ { 0x1e, 0x00 },
+ { 0x1f, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x23, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x27, 0x2d },
+ { 0x28, 0xc0 },
+ { 0x2b, 0x00 },
+ { 0x2c, 0x0c },
+};
+
+static const struct regmap_range sta32x_write_regs_range[] = {
+ regmap_reg_range(STA32X_CONFA, STA32X_FDRC2),
+};
+
+static const struct regmap_range sta32x_read_regs_range[] = {
+ regmap_reg_range(STA32X_CONFA, STA32X_FDRC2),
+};
+
+static const struct regmap_range sta32x_volatile_regs_range[] = {
+ regmap_reg_range(STA32X_CFADDR2, STA32X_CFUD),
+};
+
+static const struct regmap_access_table sta32x_write_regs = {
+ .yes_ranges = sta32x_write_regs_range,
+ .n_yes_ranges = ARRAY_SIZE(sta32x_write_regs_range),
+};
+
+static const struct regmap_access_table sta32x_read_regs = {
+ .yes_ranges = sta32x_read_regs_range,
+ .n_yes_ranges = ARRAY_SIZE(sta32x_read_regs_range),
+};
+
+static const struct regmap_access_table sta32x_volatile_regs = {
+ .yes_ranges = sta32x_volatile_regs_range,
+ .n_yes_ranges = ARRAY_SIZE(sta32x_volatile_regs_range),
+};
+
+/* regulator power supply names */
+static const char *sta32x_supply_names[] = {
+ "Vdda", /* analog supply, 3.3VV */
+ "Vdd3", /* digital supply, 3.3V */
+ "Vcc" /* power amp spply, 10V - 36V */
+};
+
+/* codec private data */
+struct sta32x_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(sta32x_supply_names)];
+ struct snd_soc_codec *codec;
+ struct sta32x_platform_data *pdata;
+
+ unsigned int mclk;
+ unsigned int format;
+
+ u32 coef_shadow[STA32X_COEF_COUNT];
+ struct delayed_work watchdog_work;
+ int shutdown;
+ struct gpio_desc *gpiod_nreset;
+ struct mutex coeff_lock;
+};
+
+static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(chvol_tlv, -7950, 50, 1);
+static const DECLARE_TLV_DB_SCALE(tone_tlv, -120, 200, 0);
+
+static const char *sta32x_drc_ac[] = {
+ "Anti-Clipping", "Dynamic Range Compression" };
+static const char *sta32x_auto_eq_mode[] = {
+ "User", "Preset", "Loudness" };
+static const char *sta32x_auto_gc_mode[] = {
+ "User", "AC no clipping", "AC limited clipping (10%)",
+ "DRC nighttime listening mode" };
+static const char *sta32x_auto_xo_mode[] = {
+ "User", "80Hz", "100Hz", "120Hz", "140Hz", "160Hz", "180Hz", "200Hz",
+ "220Hz", "240Hz", "260Hz", "280Hz", "300Hz", "320Hz", "340Hz", "360Hz" };
+static const char *sta32x_preset_eq_mode[] = {
+ "Flat", "Rock", "Soft Rock", "Jazz", "Classical", "Dance", "Pop", "Soft",
+ "Hard", "Party", "Vocal", "Hip-Hop", "Dialog", "Bass-boost #1",
+ "Bass-boost #2", "Bass-boost #3", "Loudness 1", "Loudness 2",
+ "Loudness 3", "Loudness 4", "Loudness 5", "Loudness 6", "Loudness 7",
+ "Loudness 8", "Loudness 9", "Loudness 10", "Loudness 11", "Loudness 12",
+ "Loudness 13", "Loudness 14", "Loudness 15", "Loudness 16" };
+static const char *sta32x_limiter_select[] = {
+ "Limiter Disabled", "Limiter #1", "Limiter #2" };
+static const char *sta32x_limiter_attack_rate[] = {
+ "3.1584", "2.7072", "2.2560", "1.8048", "1.3536", "0.9024",
+ "0.4512", "0.2256", "0.1504", "0.1123", "0.0902", "0.0752",
+ "0.0645", "0.0564", "0.0501", "0.0451" };
+static const char *sta32x_limiter_release_rate[] = {
+ "0.5116", "0.1370", "0.0744", "0.0499", "0.0360", "0.0299",
+ "0.0264", "0.0208", "0.0198", "0.0172", "0.0147", "0.0137",
+ "0.0134", "0.0117", "0.0110", "0.0104" };
+static DECLARE_TLV_DB_RANGE(sta32x_limiter_ac_attack_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 8, 16, TLV_DB_SCALE_ITEM(300, 100, 0),
+);
+
+static DECLARE_TLV_DB_RANGE(sta32x_limiter_ac_release_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(-2900, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(-2000, 0, 0),
+ 3, 8, TLV_DB_SCALE_ITEM(-1400, 200, 0),
+ 8, 16, TLV_DB_SCALE_ITEM(-700, 100, 0),
+);
+
+static DECLARE_TLV_DB_RANGE(sta32x_limiter_drc_attack_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(-3100, 200, 0),
+ 8, 13, TLV_DB_SCALE_ITEM(-1600, 100, 0),
+ 14, 16, TLV_DB_SCALE_ITEM(-1000, 300, 0),
+);
+
+static DECLARE_TLV_DB_RANGE(sta32x_limiter_drc_release_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
+ 1, 2, TLV_DB_SCALE_ITEM(-3800, 200, 0),
+ 3, 4, TLV_DB_SCALE_ITEM(-3300, 200, 0),
+ 5, 12, TLV_DB_SCALE_ITEM(-3000, 200, 0),
+ 13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0),
+);
+
+static SOC_ENUM_SINGLE_DECL(sta32x_drc_ac_enum,
+ STA32X_CONFD, STA32X_CONFD_DRC_SHIFT,
+ sta32x_drc_ac);
+static SOC_ENUM_SINGLE_DECL(sta32x_auto_eq_enum,
+ STA32X_AUTO1, STA32X_AUTO1_AMEQ_SHIFT,
+ sta32x_auto_eq_mode);
+static SOC_ENUM_SINGLE_DECL(sta32x_auto_gc_enum,
+ STA32X_AUTO1, STA32X_AUTO1_AMGC_SHIFT,
+ sta32x_auto_gc_mode);
+static SOC_ENUM_SINGLE_DECL(sta32x_auto_xo_enum,
+ STA32X_AUTO2, STA32X_AUTO2_XO_SHIFT,
+ sta32x_auto_xo_mode);
+static SOC_ENUM_SINGLE_DECL(sta32x_preset_eq_enum,
+ STA32X_AUTO3, STA32X_AUTO3_PEQ_SHIFT,
+ sta32x_preset_eq_mode);
+static SOC_ENUM_SINGLE_DECL(sta32x_limiter_ch1_enum,
+ STA32X_C1CFG, STA32X_CxCFG_LS_SHIFT,
+ sta32x_limiter_select);
+static SOC_ENUM_SINGLE_DECL(sta32x_limiter_ch2_enum,
+ STA32X_C2CFG, STA32X_CxCFG_LS_SHIFT,
+ sta32x_limiter_select);
+static SOC_ENUM_SINGLE_DECL(sta32x_limiter_ch3_enum,
+ STA32X_C3CFG, STA32X_CxCFG_LS_SHIFT,
+ sta32x_limiter_select);
+static SOC_ENUM_SINGLE_DECL(sta32x_limiter1_attack_rate_enum,
+ STA32X_L1AR, STA32X_LxA_SHIFT,
+ sta32x_limiter_attack_rate);
+static SOC_ENUM_SINGLE_DECL(sta32x_limiter2_attack_rate_enum,
+ STA32X_L2AR, STA32X_LxA_SHIFT,
+ sta32x_limiter_attack_rate);
+static SOC_ENUM_SINGLE_DECL(sta32x_limiter1_release_rate_enum,
+ STA32X_L1AR, STA32X_LxR_SHIFT,
+ sta32x_limiter_release_rate);
+static SOC_ENUM_SINGLE_DECL(sta32x_limiter2_release_rate_enum,
+ STA32X_L2AR, STA32X_LxR_SHIFT,
+ sta32x_limiter_release_rate);
+
+/* byte array controls for setting biquad, mixer, scaling coefficients;
+ * for biquads all five coefficients need to be set in one go,
+ * mixer and pre/postscale coefs can be set individually;
+ * each coef is 24bit, the bytes are ordered in the same way
+ * as given in the STA32x data sheet (big endian; b1, b2, a1, a2, b0)
+ */
+
+static int sta32x_coefficient_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int numcoef = kcontrol->private_value >> 16;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = 3 * numcoef;
+ return 0;
+}
+
+static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ int numcoef = kcontrol->private_value >> 16;
+ int index = kcontrol->private_value & 0xffff;
+ unsigned int cfud, val;
+ int i, ret = 0;
+
+ mutex_lock(&sta32x->coeff_lock);
+
+ /* preserve reserved bits in STA32X_CFUD */
+ regmap_read(sta32x->regmap, STA32X_CFUD, &cfud);
+ cfud &= 0xf0;
+ /*
+ * chip documentation does not say if the bits are self clearing,
+ * so do it explicitly
+ */
+ regmap_write(sta32x->regmap, STA32X_CFUD, cfud);
+
+ regmap_write(sta32x->regmap, STA32X_CFADDR2, index);
+ if (numcoef == 1) {
+ regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x04);
+ } else if (numcoef == 5) {
+ regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x08);
+ } else {
+ ret = -EINVAL;
+ goto exit_unlock;
+ }
+
+ for (i = 0; i < 3 * numcoef; i++) {
+ regmap_read(sta32x->regmap, STA32X_B1CF1 + i, &val);
+ ucontrol->value.bytes.data[i] = val;
+ }
+
+exit_unlock:
+ mutex_unlock(&sta32x->coeff_lock);
+
+ return ret;
+}
+
+static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ int numcoef = kcontrol->private_value >> 16;
+ int index = kcontrol->private_value & 0xffff;
+ unsigned int cfud;
+ int i;
+
+ /* preserve reserved bits in STA32X_CFUD */
+ regmap_read(sta32x->regmap, STA32X_CFUD, &cfud);
+ cfud &= 0xf0;
+ /*
+ * chip documentation does not say if the bits are self clearing,
+ * so do it explicitly
+ */
+ regmap_write(sta32x->regmap, STA32X_CFUD, cfud);
+
+ regmap_write(sta32x->regmap, STA32X_CFADDR2, index);
+ for (i = 0; i < numcoef && (index + i < STA32X_COEF_COUNT); i++)
+ sta32x->coef_shadow[index + i] =
+ (ucontrol->value.bytes.data[3 * i] << 16)
+ | (ucontrol->value.bytes.data[3 * i + 1] << 8)
+ | (ucontrol->value.bytes.data[3 * i + 2]);
+ for (i = 0; i < 3 * numcoef; i++)
+ regmap_write(sta32x->regmap, STA32X_B1CF1 + i,
+ ucontrol->value.bytes.data[i]);
+ if (numcoef == 1)
+ regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x01);
+ else if (numcoef == 5)
+ regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x02);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sta32x_sync_coef_shadow(struct snd_soc_codec *codec)
+{
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ unsigned int cfud;
+ int i;
+
+ /* preserve reserved bits in STA32X_CFUD */
+ regmap_read(sta32x->regmap, STA32X_CFUD, &cfud);
+ cfud &= 0xf0;
+
+ for (i = 0; i < STA32X_COEF_COUNT; i++) {
+ regmap_write(sta32x->regmap, STA32X_CFADDR2, i);
+ regmap_write(sta32x->regmap, STA32X_B1CF1,
+ (sta32x->coef_shadow[i] >> 16) & 0xff);
+ regmap_write(sta32x->regmap, STA32X_B1CF2,
+ (sta32x->coef_shadow[i] >> 8) & 0xff);
+ regmap_write(sta32x->regmap, STA32X_B1CF3,
+ (sta32x->coef_shadow[i]) & 0xff);
+ /*
+ * chip documentation does not say if the bits are
+ * self-clearing, so do it explicitly
+ */
+ regmap_write(sta32x->regmap, STA32X_CFUD, cfud);
+ regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x01);
+ }
+ return 0;
+}
+
+static int sta32x_cache_sync(struct snd_soc_codec *codec)
+{
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ 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);
+ rc = regcache_sync(sta32x->regmap);
+ regmap_write(sta32x->regmap, STA32X_MMUTE, mute);
+ return rc;
+}
+
+/* work around ESD issue where sta32x resets and loses all configuration */
+static void sta32x_watchdog(struct work_struct *work)
+{
+ struct sta32x_priv *sta32x = container_of(work, struct sta32x_priv,
+ watchdog_work.work);
+ struct snd_soc_codec *codec = sta32x->codec;
+ unsigned int confa, confa_cached;
+
+ /* check if sta32x has reset itself */
+ confa_cached = snd_soc_read(codec, STA32X_CONFA);
+ regcache_cache_bypass(sta32x->regmap, true);
+ confa = snd_soc_read(codec, STA32X_CONFA);
+ regcache_cache_bypass(sta32x->regmap, false);
+ if (confa != confa_cached) {
+ regcache_mark_dirty(sta32x->regmap);
+ sta32x_cache_sync(codec);
+ }
+
+ if (!sta32x->shutdown)
+ queue_delayed_work(system_power_efficient_wq,
+ &sta32x->watchdog_work,
+ round_jiffies_relative(HZ));
+}
+
+static void sta32x_watchdog_start(struct sta32x_priv *sta32x)
+{
+ if (sta32x->pdata->needs_esd_watchdog) {
+ sta32x->shutdown = 0;
+ queue_delayed_work(system_power_efficient_wq,
+ &sta32x->watchdog_work,
+ round_jiffies_relative(HZ));
+ }
+}
+
+static void sta32x_watchdog_stop(struct sta32x_priv *sta32x)
+{
+ if (sta32x->pdata->needs_esd_watchdog) {
+ sta32x->shutdown = 1;
+ cancel_delayed_work_sync(&sta32x->watchdog_work);
+ }
+}
+
+#define SINGLE_COEF(xname, index) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = sta32x_coefficient_info, \
+ .get = sta32x_coefficient_get,\
+ .put = sta32x_coefficient_put, \
+ .private_value = index | (1 << 16) }
+
+#define BIQUAD_COEFS(xname, index) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = sta32x_coefficient_info, \
+ .get = sta32x_coefficient_get,\
+ .put = sta32x_coefficient_put, \
+ .private_value = index | (5 << 16) }
+
+static const struct snd_kcontrol_new sta32x_snd_controls[] = {
+SOC_SINGLE_TLV("Master Volume", STA32X_MVOL, 0, 0xff, 1, mvol_tlv),
+SOC_SINGLE("Master Switch", STA32X_MMUTE, 0, 1, 1),
+SOC_SINGLE("Ch1 Switch", STA32X_MMUTE, 1, 1, 1),
+SOC_SINGLE("Ch2 Switch", STA32X_MMUTE, 2, 1, 1),
+SOC_SINGLE("Ch3 Switch", STA32X_MMUTE, 3, 1, 1),
+SOC_SINGLE_TLV("Ch1 Volume", STA32X_C1VOL, 0, 0xff, 1, chvol_tlv),
+SOC_SINGLE_TLV("Ch2 Volume", STA32X_C2VOL, 0, 0xff, 1, chvol_tlv),
+SOC_SINGLE_TLV("Ch3 Volume", STA32X_C3VOL, 0, 0xff, 1, chvol_tlv),
+SOC_SINGLE("De-emphasis Filter Switch", STA32X_CONFD, STA32X_CONFD_DEMP_SHIFT, 1, 0),
+SOC_ENUM("Compressor/Limiter Switch", sta32x_drc_ac_enum),
+SOC_SINGLE("Miami Mode Switch", STA32X_CONFD, STA32X_CONFD_MME_SHIFT, 1, 0),
+SOC_SINGLE("Zero Cross Switch", STA32X_CONFE, STA32X_CONFE_ZCE_SHIFT, 1, 0),
+SOC_SINGLE("Soft Ramp Switch", STA32X_CONFE, STA32X_CONFE_SVE_SHIFT, 1, 0),
+SOC_SINGLE("Auto-Mute Switch", STA32X_CONFF, STA32X_CONFF_IDE_SHIFT, 1, 0),
+SOC_ENUM("Automode EQ", sta32x_auto_eq_enum),
+SOC_ENUM("Automode GC", sta32x_auto_gc_enum),
+SOC_ENUM("Automode XO", sta32x_auto_xo_enum),
+SOC_ENUM("Preset EQ", sta32x_preset_eq_enum),
+SOC_SINGLE("Ch1 Tone Control Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_TCB_SHIFT, 1, 0),
+SOC_SINGLE("Ch2 Tone Control Bypass Switch", STA32X_C2CFG, STA32X_CxCFG_TCB_SHIFT, 1, 0),
+SOC_SINGLE("Ch1 EQ Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_EQBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch2 EQ Bypass Switch", STA32X_C2CFG, STA32X_CxCFG_EQBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch1 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch2 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch3 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0),
+SOC_ENUM("Ch1 Limiter Select", sta32x_limiter_ch1_enum),
+SOC_ENUM("Ch2 Limiter Select", sta32x_limiter_ch2_enum),
+SOC_ENUM("Ch3 Limiter Select", sta32x_limiter_ch3_enum),
+SOC_SINGLE_TLV("Bass Tone Control", STA32X_TONE, STA32X_TONE_BTC_SHIFT, 15, 0, tone_tlv),
+SOC_SINGLE_TLV("Treble Tone Control", STA32X_TONE, STA32X_TONE_TTC_SHIFT, 15, 0, tone_tlv),
+SOC_ENUM("Limiter1 Attack Rate (dB/ms)", sta32x_limiter1_attack_rate_enum),
+SOC_ENUM("Limiter2 Attack Rate (dB/ms)", sta32x_limiter2_attack_rate_enum),
+SOC_ENUM("Limiter1 Release Rate (dB/ms)", sta32x_limiter1_release_rate_enum),
+SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta32x_limiter2_release_rate_enum),
+
+/* depending on mode, the attack/release thresholds have
+ * two different enum definitions; provide both
+ */
+SOC_SINGLE_TLV("Limiter1 Attack Threshold (AC Mode)", STA32X_L1ATRT, STA32X_LxA_SHIFT,
+ 16, 0, sta32x_limiter_ac_attack_tlv),
+SOC_SINGLE_TLV("Limiter2 Attack Threshold (AC Mode)", STA32X_L2ATRT, STA32X_LxA_SHIFT,
+ 16, 0, sta32x_limiter_ac_attack_tlv),
+SOC_SINGLE_TLV("Limiter1 Release Threshold (AC Mode)", STA32X_L1ATRT, STA32X_LxR_SHIFT,
+ 16, 0, sta32x_limiter_ac_release_tlv),
+SOC_SINGLE_TLV("Limiter2 Release Threshold (AC Mode)", STA32X_L2ATRT, STA32X_LxR_SHIFT,
+ 16, 0, sta32x_limiter_ac_release_tlv),
+SOC_SINGLE_TLV("Limiter1 Attack Threshold (DRC Mode)", STA32X_L1ATRT, STA32X_LxA_SHIFT,
+ 16, 0, sta32x_limiter_drc_attack_tlv),
+SOC_SINGLE_TLV("Limiter2 Attack Threshold (DRC Mode)", STA32X_L2ATRT, STA32X_LxA_SHIFT,
+ 16, 0, sta32x_limiter_drc_attack_tlv),
+SOC_SINGLE_TLV("Limiter1 Release Threshold (DRC Mode)", STA32X_L1ATRT, STA32X_LxR_SHIFT,
+ 16, 0, sta32x_limiter_drc_release_tlv),
+SOC_SINGLE_TLV("Limiter2 Release Threshold (DRC Mode)", STA32X_L2ATRT, STA32X_LxR_SHIFT,
+ 16, 0, sta32x_limiter_drc_release_tlv),
+
+BIQUAD_COEFS("Ch1 - Biquad 1", 0),
+BIQUAD_COEFS("Ch1 - Biquad 2", 5),
+BIQUAD_COEFS("Ch1 - Biquad 3", 10),
+BIQUAD_COEFS("Ch1 - Biquad 4", 15),
+BIQUAD_COEFS("Ch2 - Biquad 1", 20),
+BIQUAD_COEFS("Ch2 - Biquad 2", 25),
+BIQUAD_COEFS("Ch2 - Biquad 3", 30),
+BIQUAD_COEFS("Ch2 - Biquad 4", 35),
+BIQUAD_COEFS("High-pass", 40),
+BIQUAD_COEFS("Low-pass", 45),
+SINGLE_COEF("Ch1 - Prescale", 50),
+SINGLE_COEF("Ch2 - Prescale", 51),
+SINGLE_COEF("Ch1 - Postscale", 52),
+SINGLE_COEF("Ch2 - Postscale", 53),
+SINGLE_COEF("Ch3 - Postscale", 54),
+SINGLE_COEF("Thermal warning - Postscale", 55),
+SINGLE_COEF("Ch1 - Mix 1", 56),
+SINGLE_COEF("Ch1 - Mix 2", 57),
+SINGLE_COEF("Ch2 - Mix 1", 58),
+SINGLE_COEF("Ch2 - Mix 2", 59),
+SINGLE_COEF("Ch3 - Mix 1", 60),
+SINGLE_COEF("Ch3 - Mix 2", 61),
+};
+
+static const struct snd_soc_dapm_widget sta32x_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("LEFT"),
+SND_SOC_DAPM_OUTPUT("RIGHT"),
+SND_SOC_DAPM_OUTPUT("SUB"),
+};
+
+static const struct snd_soc_dapm_route sta32x_dapm_routes[] = {
+ { "LEFT", NULL, "DAC" },
+ { "RIGHT", NULL, "DAC" },
+ { "SUB", NULL, "DAC" },
+};
+
+/* MCLK interpolation ratio per fs */
+static struct {
+ int fs;
+ int ir;
+} interpolation_ratios[] = {
+ { 32000, 0 },
+ { 44100, 0 },
+ { 48000, 0 },
+ { 88200, 1 },
+ { 96000, 1 },
+ { 176400, 2 },
+ { 192000, 2 },
+};
+
+/* MCLK to fs clock ratios */
+static int mcs_ratio_table[3][7] = {
+ { 768, 512, 384, 256, 128, 576, 0 },
+ { 384, 256, 192, 128, 64, 0 },
+ { 384, 256, 192, 128, 64, 0 },
+};
+
+/**
+ * sta32x_set_dai_sysclk - configure MCLK
+ * @codec_dai: the codec DAI
+ * @clk_id: the clock ID (ignored)
+ * @freq: the MCLK input frequency
+ * @dir: the clock direction (ignored)
+ *
+ * The value of MCLK is used to determine which sample rates are supported
+ * by the STA32X, based on the mclk_ratios table.
+ *
+ * This function must be called by the machine driver's 'startup' function,
+ * otherwise the list of supported sample rates will not be available in
+ * time for ALSA.
+ *
+ * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause
+ * theoretically possible sample rates to be enabled. Call it again with a
+ * proper value set one the external clock is set (most probably you would do
+ * that from a machine's driver 'hw_param' hook.
+ */
+static int sta32x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "mclk=%u\n", freq);
+ sta32x->mclk = freq;
+
+ return 0;
+}
+
+/**
+ * sta32x_set_dai_fmt - configure the codec for the selected audio format
+ * @codec_dai: the codec DAI
+ * @fmt: a SND_SOC_DAIFMT_x value indicating the data format
+ *
+ * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the
+ * codec accordingly.
+ */
+static int sta32x_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ u8 confb = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ sta32x->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ confb |= STA32X_CONFB_C2IM;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ confb |= STA32X_CONFB_C1IM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(sta32x->regmap, STA32X_CONFB,
+ STA32X_CONFB_C1IM | STA32X_CONFB_C2IM, confb);
+}
+
+/**
+ * sta32x_hw_params - program the STA32X with the given hardware parameters.
+ * @substream: the audio stream
+ * @params: the hardware parameters to set
+ * @dai: the SOC DAI (ignored)
+ *
+ * This function programs the hardware with the values provided.
+ * Specifically, the sample rate and the data format.
+ */
+static int sta32x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ int i, mcs = -EINVAL, ir = -EINVAL;
+ unsigned int confa, confb;
+ unsigned int rate, ratio;
+ int ret;
+
+ if (!sta32x->mclk) {
+ dev_err(codec->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);
+
+ for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) {
+ if (interpolation_ratios[i].fs == rate) {
+ ir = interpolation_ratios[i].ir;
+ break;
+ }
+ }
+
+ if (ir < 0) {
+ dev_err(codec->dev, "Unsupported samplerate: %u\n", rate);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 6; i++) {
+ if (mcs_ratio_table[ir][i] == ratio) {
+ mcs = i;
+ break;
+ }
+ }
+
+ if (mcs < 0) {
+ dev_err(codec->dev, "Unresolvable ratio: %u\n", ratio);
+ return -EINVAL;
+ }
+
+ confa = (ir << STA32X_CONFA_IR_SHIFT) |
+ (mcs << STA32X_CONFA_MCS_SHIFT);
+ confb = 0;
+
+ switch (params_width(params)) {
+ case 24:
+ dev_dbg(codec->dev, "24bit\n");
+ /* fall through */
+ case 32:
+ dev_dbg(codec->dev, "24bit or 32bit\n");
+ switch (sta32x->format) {
+ case SND_SOC_DAIFMT_I2S:
+ confb |= 0x0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ confb |= 0x1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ confb |= 0x2;
+ break;
+ }
+
+ break;
+ case 20:
+ dev_dbg(codec->dev, "20bit\n");
+ switch (sta32x->format) {
+ case SND_SOC_DAIFMT_I2S:
+ confb |= 0x4;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ confb |= 0x5;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ confb |= 0x6;
+ break;
+ }
+
+ break;
+ case 18:
+ dev_dbg(codec->dev, "18bit\n");
+ switch (sta32x->format) {
+ case SND_SOC_DAIFMT_I2S:
+ confb |= 0x8;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ confb |= 0x9;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ confb |= 0xa;
+ break;
+ }
+
+ break;
+ case 16:
+ dev_dbg(codec->dev, "16bit\n");
+ switch (sta32x->format) {
+ case SND_SOC_DAIFMT_I2S:
+ confb |= 0x0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ confb |= 0xd;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ confb |= 0xe;
+ break;
+ }
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(sta32x->regmap, STA32X_CONFA,
+ STA32X_CONFA_MCS_MASK | STA32X_CONFA_IR_MASK,
+ confa);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(sta32x->regmap, STA32X_CONFB,
+ STA32X_CONFB_SAI_MASK | STA32X_CONFB_SAIFB,
+ confb);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int sta32x_startup_sequence(struct sta32x_priv *sta32x)
+{
+ if (sta32x->gpiod_nreset) {
+ gpiod_set_value(sta32x->gpiod_nreset, 0);
+ mdelay(1);
+ gpiod_set_value(sta32x->gpiod_nreset, 1);
+ mdelay(1);
+ }
+
+ return 0;
+}
+
+/**
+ * sta32x_set_bias_level - DAPM callback
+ * @codec: the codec 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
+ * all registers must be restored after power on.
+ */
+static int sta32x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "level = %d\n", level);
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* Full power on */
+ regmap_update_bits(sta32x->regmap, STA32X_CONFF,
+ STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
+ STA32X_CONFF_PWDN | STA32X_CONFF_EAPD);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ 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;
+ }
+
+ sta32x_startup_sequence(sta32x);
+ sta32x_cache_sync(codec);
+ sta32x_watchdog_start(sta32x);
+ }
+
+ /* Power down */
+ regmap_update_bits(sta32x->regmap, STA32X_CONFF,
+ STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
+ 0);
+
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* The chip runs through the power down sequence for us. */
+ regmap_update_bits(sta32x->regmap, STA32X_CONFF,
+ STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, 0);
+ msleep(300);
+ sta32x_watchdog_stop(sta32x);
+
+ if (sta32x->gpiod_nreset)
+ gpiod_set_value(sta32x->gpiod_nreset, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies),
+ sta32x->supplies);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sta32x_dai_ops = {
+ .hw_params = sta32x_hw_params,
+ .set_sysclk = sta32x_set_dai_sysclk,
+ .set_fmt = sta32x_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver sta32x_dai = {
+ .name = "sta32x-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = STA32X_RATES,
+ .formats = STA32X_FORMATS,
+ },
+ .ops = &sta32x_dai_ops,
+};
+
+static int sta32x_probe(struct snd_soc_codec *codec)
+{
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct sta32x_platform_data *pdata = sta32x->pdata;
+ int i, ret = 0, thermal = 0;
+ 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;
+ }
+
+ ret = sta32x_startup_sequence(sta32x);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to startup device\n");
+ return ret;
+ }
+
+ /* CONFA */
+ if (!pdata->thermal_warning_recovery)
+ thermal |= STA32X_CONFA_TWAB;
+ if (!pdata->thermal_warning_adjustment)
+ thermal |= STA32X_CONFA_TWRB;
+ if (!pdata->fault_detect_recovery)
+ thermal |= STA32X_CONFA_FDRB;
+ regmap_update_bits(sta32x->regmap, STA32X_CONFA,
+ STA32X_CONFA_TWAB | STA32X_CONFA_TWRB |
+ STA32X_CONFA_FDRB,
+ thermal);
+
+ /* CONFC */
+ regmap_update_bits(sta32x->regmap, STA32X_CONFC,
+ STA32X_CONFC_CSZ_MASK,
+ pdata->drop_compensation_ns
+ << STA32X_CONFC_CSZ_SHIFT);
+
+ /* CONFE */
+ regmap_update_bits(sta32x->regmap, STA32X_CONFE,
+ STA32X_CONFE_MPCV,
+ pdata->max_power_use_mpcc ?
+ STA32X_CONFE_MPCV : 0);
+ regmap_update_bits(sta32x->regmap, STA32X_CONFE,
+ STA32X_CONFE_MPC,
+ pdata->max_power_correction ?
+ STA32X_CONFE_MPC : 0);
+ regmap_update_bits(sta32x->regmap, STA32X_CONFE,
+ STA32X_CONFE_AME,
+ pdata->am_reduction_mode ?
+ STA32X_CONFE_AME : 0);
+ regmap_update_bits(sta32x->regmap, STA32X_CONFE,
+ STA32X_CONFE_PWMS,
+ pdata->odd_pwm_speed_mode ?
+ STA32X_CONFE_PWMS : 0);
+
+ /* CONFF */
+ regmap_update_bits(sta32x->regmap, STA32X_CONFF,
+ STA32X_CONFF_IDE,
+ pdata->invalid_input_detect_mute ?
+ STA32X_CONFF_IDE : 0);
+
+ /* select output configuration */
+ regmap_update_bits(sta32x->regmap, STA32X_CONFF,
+ STA32X_CONFF_OCFG_MASK,
+ pdata->output_conf
+ << STA32X_CONFF_OCFG_SHIFT);
+
+ /* channel to output mapping */
+ regmap_update_bits(sta32x->regmap, STA32X_C1CFG,
+ STA32X_CxCFG_OM_MASK,
+ pdata->ch1_output_mapping
+ << STA32X_CxCFG_OM_SHIFT);
+ regmap_update_bits(sta32x->regmap, STA32X_C2CFG,
+ STA32X_CxCFG_OM_MASK,
+ pdata->ch2_output_mapping
+ << STA32X_CxCFG_OM_SHIFT);
+ regmap_update_bits(sta32x->regmap, STA32X_C3CFG,
+ STA32X_CxCFG_OM_MASK,
+ pdata->ch3_output_mapping
+ << STA32X_CxCFG_OM_SHIFT);
+
+ /* initialize coefficient shadow RAM with reset values */
+ for (i = 4; i <= 49; i += 5)
+ sta32x->coef_shadow[i] = 0x400000;
+ for (i = 50; i <= 54; i++)
+ sta32x->coef_shadow[i] = 0x7fffff;
+ sta32x->coef_shadow[55] = 0x5a9df7;
+ sta32x->coef_shadow[56] = 0x7fffff;
+ sta32x->coef_shadow[59] = 0x7fffff;
+ sta32x->coef_shadow[60] = 0x400000;
+ sta32x->coef_shadow[61] = 0x400000;
+
+ if (sta32x->pdata->needs_esd_watchdog)
+ INIT_DELAYED_WORK(&sta32x->watchdog_work, sta32x_watchdog);
+
+ sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ /* Bias level configuration will have done an extra enable */
+ regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
+
+ return 0;
+}
+
+static int sta32x_remove(struct snd_soc_codec *codec)
+{
+ struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+
+ sta32x_watchdog_stop(sta32x);
+ regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
+
+ return 0;
+}
+
+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 regmap_config sta32x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = STA32X_FDRC2,
+ .reg_defaults = sta32x_regs,
+ .num_reg_defaults = ARRAY_SIZE(sta32x_regs),
+ .cache_type = REGCACHE_RBTREE,
+ .wr_table = &sta32x_write_regs,
+ .rd_table = &sta32x_read_regs,
+ .volatile_table = &sta32x_volatile_regs,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id st32x_dt_ids[] = {
+ { .compatible = "st,sta32x", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, st32x_dt_ids);
+
+static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x)
+{
+ struct device_node *np = dev->of_node;
+ struct sta32x_platform_data *pdata;
+ u16 tmp;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ of_property_read_u8(np, "st,output-conf",
+ &pdata->output_conf);
+ of_property_read_u8(np, "st,ch1-output-mapping",
+ &pdata->ch1_output_mapping);
+ of_property_read_u8(np, "st,ch2-output-mapping",
+ &pdata->ch2_output_mapping);
+ of_property_read_u8(np, "st,ch3-output-mapping",
+ &pdata->ch3_output_mapping);
+
+ 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;
+
+ 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;
+
+ /* CONFF */
+ if (of_get_property(np, "st,invalid-input-detect-mute", NULL))
+ pdata->invalid_input_detect_mute = 1;
+
+ sta32x->pdata = pdata;
+
+ return 0;
+}
+#endif
+
+static int sta32x_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct sta32x_priv *sta32x;
+ int ret, i;
+
+ sta32x = devm_kzalloc(&i2c->dev, sizeof(struct sta32x_priv),
+ GFP_KERNEL);
+ if (!sta32x)
+ return -ENOMEM;
+
+ mutex_init(&sta32x->coeff_lock);
+ sta32x->pdata = dev_get_platdata(dev);
+
+#ifdef CONFIG_OF
+ if (dev->of_node) {
+ ret = sta32x_probe_dt(dev, sta32x);
+ if (ret < 0)
+ return ret;
+ }
+#endif
+
+ /* 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)
+ return ret;
+
+ sta32x->gpiod_nreset = NULL;
+ } else {
+ gpiod_direction_output(sta32x->gpiod_nreset, 0);
+ }
+
+ /* regulators */
+ for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++)
+ sta32x->supplies[i].supply = sta32x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(sta32x->supplies),
+ sta32x->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ sta32x->regmap = devm_regmap_init_i2c(i2c, &sta32x_regmap);
+ if (IS_ERR(sta32x->regmap)) {
+ ret = PTR_ERR(sta32x->regmap);
+ dev_err(dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, sta32x);
+
+ ret = snd_soc_register_codec(dev, &sta32x_codec, &sta32x_dai, 1);
+ if (ret < 0)
+ dev_err(dev, "Failed to register codec (%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 },
+ { }
+};
+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,
+ .id_table = sta32x_i2c_id,
+};
+
+module_i2c_driver(sta32x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC STA32X driver");
+MODULE_AUTHOR("Johannes Stezenbach <js@sig21.net>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/sta32x.h b/kernel/sound/soc/codecs/sta32x.h
new file mode 100644
index 000000000..d3191c983
--- /dev/null
+++ b/kernel/sound/soc/codecs/sta32x.h
@@ -0,0 +1,211 @@
+/*
+ * Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system
+ *
+ * Copyright: 2011 Raumfeld GmbH
+ * Author: Johannes Stezenbach <js@sig21.net>
+ *
+ * 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
+
+/* STA326 register addresses */
+
+#define STA32X_REGISTER_COUNT 0x2d
+#define STA32X_COEF_COUNT 62
+
+#define STA32X_CONFA 0x00
+#define STA32X_CONFB 0x01
+#define STA32X_CONFC 0x02
+#define STA32X_CONFD 0x03
+#define STA32X_CONFE 0x04
+#define STA32X_CONFF 0x05
+#define STA32X_MMUTE 0x06
+#define STA32X_MVOL 0x07
+#define STA32X_C1VOL 0x08
+#define STA32X_C2VOL 0x09
+#define STA32X_C3VOL 0x0a
+#define STA32X_AUTO1 0x0b
+#define STA32X_AUTO2 0x0c
+#define STA32X_AUTO3 0x0d
+#define STA32X_C1CFG 0x0e
+#define STA32X_C2CFG 0x0f
+#define STA32X_C3CFG 0x10
+#define STA32X_TONE 0x11
+#define STA32X_L1AR 0x12
+#define STA32X_L1ATRT 0x13
+#define STA32X_L2AR 0x14
+#define STA32X_L2ATRT 0x15
+#define STA32X_CFADDR2 0x16
+#define STA32X_B1CF1 0x17
+#define STA32X_B1CF2 0x18
+#define STA32X_B1CF3 0x19
+#define STA32X_B2CF1 0x1a
+#define STA32X_B2CF2 0x1b
+#define STA32X_B2CF3 0x1c
+#define STA32X_A1CF1 0x1d
+#define STA32X_A1CF2 0x1e
+#define STA32X_A1CF3 0x1f
+#define STA32X_A2CF1 0x20
+#define STA32X_A2CF2 0x21
+#define STA32X_A2CF3 0x22
+#define STA32X_B0CF1 0x23
+#define STA32X_B0CF2 0x24
+#define STA32X_B0CF3 0x25
+#define STA32X_CFUD 0x26
+#define STA32X_MPCC1 0x27
+#define STA32X_MPCC2 0x28
+/* Reserved 0x29 */
+/* Reserved 0x2a */
+#define STA32X_Reserved 0x2a
+#define STA32X_FDRC1 0x2b
+#define STA32X_FDRC2 0x2c
+/* Reserved 0x2d */
+
+
+/* STA326 register field definitions */
+
+/* 0x00 CONFA */
+#define STA32X_CONFA_MCS_MASK 0x03
+#define STA32X_CONFA_MCS_SHIFT 0
+#define STA32X_CONFA_IR_MASK 0x18
+#define STA32X_CONFA_IR_SHIFT 3
+#define STA32X_CONFA_TWRB 0x20
+#define STA32X_CONFA_TWAB 0x40
+#define STA32X_CONFA_FDRB 0x80
+
+/* 0x01 CONFB */
+#define STA32X_CONFB_SAI_MASK 0x0f
+#define STA32X_CONFB_SAI_SHIFT 0
+#define STA32X_CONFB_SAIFB 0x10
+#define STA32X_CONFB_DSCKE 0x20
+#define STA32X_CONFB_C1IM 0x40
+#define STA32X_CONFB_C2IM 0x80
+
+/* 0x02 CONFC */
+#define STA32X_CONFC_OM_MASK 0x03
+#define STA32X_CONFC_OM_SHIFT 0
+#define STA32X_CONFC_CSZ_MASK 0x7c
+#define STA32X_CONFC_CSZ_SHIFT 2
+
+/* 0x03 CONFD */
+#define STA32X_CONFD_HPB 0x01
+#define STA32X_CONFD_HPB_SHIFT 0
+#define STA32X_CONFD_DEMP 0x02
+#define STA32X_CONFD_DEMP_SHIFT 1
+#define STA32X_CONFD_DSPB 0x04
+#define STA32X_CONFD_DSPB_SHIFT 2
+#define STA32X_CONFD_PSL 0x08
+#define STA32X_CONFD_PSL_SHIFT 3
+#define STA32X_CONFD_BQL 0x10
+#define STA32X_CONFD_BQL_SHIFT 4
+#define STA32X_CONFD_DRC 0x20
+#define STA32X_CONFD_DRC_SHIFT 5
+#define STA32X_CONFD_ZDE 0x40
+#define STA32X_CONFD_ZDE_SHIFT 6
+#define STA32X_CONFD_MME 0x80
+#define STA32X_CONFD_MME_SHIFT 7
+
+/* 0x04 CONFE */
+#define STA32X_CONFE_MPCV 0x01
+#define STA32X_CONFE_MPCV_SHIFT 0
+#define STA32X_CONFE_MPC 0x02
+#define STA32X_CONFE_MPC_SHIFT 1
+#define STA32X_CONFE_AME 0x08
+#define STA32X_CONFE_AME_SHIFT 3
+#define STA32X_CONFE_PWMS 0x10
+#define STA32X_CONFE_PWMS_SHIFT 4
+#define STA32X_CONFE_ZCE 0x40
+#define STA32X_CONFE_ZCE_SHIFT 6
+#define STA32X_CONFE_SVE 0x80
+#define STA32X_CONFE_SVE_SHIFT 7
+
+/* 0x05 CONFF */
+#define STA32X_CONFF_OCFG_MASK 0x03
+#define STA32X_CONFF_OCFG_SHIFT 0
+#define STA32X_CONFF_IDE 0x04
+#define STA32X_CONFF_IDE_SHIFT 2
+#define STA32X_CONFF_BCLE 0x08
+#define STA32X_CONFF_ECLE 0x20
+#define STA32X_CONFF_PWDN 0x40
+#define STA32X_CONFF_EAPD 0x80
+
+/* 0x06 MMUTE */
+#define STA32X_MMUTE_MMUTE 0x01
+
+/* 0x0b AUTO1 */
+#define STA32X_AUTO1_AMEQ_MASK 0x03
+#define STA32X_AUTO1_AMEQ_SHIFT 0
+#define STA32X_AUTO1_AMV_MASK 0xc0
+#define STA32X_AUTO1_AMV_SHIFT 2
+#define STA32X_AUTO1_AMGC_MASK 0x30
+#define STA32X_AUTO1_AMGC_SHIFT 4
+#define STA32X_AUTO1_AMPS 0x80
+
+/* 0x0c AUTO2 */
+#define STA32X_AUTO2_AMAME 0x01
+#define STA32X_AUTO2_AMAM_MASK 0x0e
+#define STA32X_AUTO2_AMAM_SHIFT 1
+#define STA32X_AUTO2_XO_MASK 0xf0
+#define STA32X_AUTO2_XO_SHIFT 4
+
+/* 0x0d AUTO3 */
+#define STA32X_AUTO3_PEQ_MASK 0x1f
+#define STA32X_AUTO3_PEQ_SHIFT 0
+
+/* 0x0e 0x0f 0x10 CxCFG */
+#define STA32X_CxCFG_TCB 0x01 /* only C1 and C2 */
+#define STA32X_CxCFG_TCB_SHIFT 0
+#define STA32X_CxCFG_EQBP 0x02 /* only C1 and C2 */
+#define STA32X_CxCFG_EQBP_SHIFT 1
+#define STA32X_CxCFG_VBP 0x03
+#define STA32X_CxCFG_VBP_SHIFT 2
+#define STA32X_CxCFG_BO 0x04
+#define STA32X_CxCFG_LS_MASK 0x30
+#define STA32X_CxCFG_LS_SHIFT 4
+#define STA32X_CxCFG_OM_MASK 0xc0
+#define STA32X_CxCFG_OM_SHIFT 6
+
+/* 0x11 TONE */
+#define STA32X_TONE_BTC_SHIFT 0
+#define STA32X_TONE_TTC_SHIFT 4
+
+/* 0x12 0x13 0x14 0x15 limiter attack/release */
+#define STA32X_LxA_SHIFT 0
+#define STA32X_LxR_SHIFT 4
+
+/* 0x26 CFUD */
+#define STA32X_CFUD_W1 0x01
+#define STA32X_CFUD_WA 0x02
+#define STA32X_CFUD_R1 0x04
+#define STA32X_CFUD_RA 0x08
+
+
+/* biquad filter coefficient table offsets */
+#define STA32X_C1_BQ_BASE 0
+#define STA32X_C2_BQ_BASE 20
+#define STA32X_CH_BQ_NUM 4
+#define STA32X_BQ_NUM_COEF 5
+#define STA32X_XO_HP_BQ_BASE 40
+#define STA32X_XO_LP_BQ_BASE 45
+#define STA32X_C1_PRESCALE 50
+#define STA32X_C2_PRESCALE 51
+#define STA32X_C1_POSTSCALE 52
+#define STA32X_C2_POSTSCALE 53
+#define STA32X_C3_POSTSCALE 54
+#define STA32X_TW_POSTSCALE 55
+#define STA32X_C1_MIX1 56
+#define STA32X_C1_MIX2 57
+#define STA32X_C2_MIX1 58
+#define STA32X_C2_MIX2 59
+#define STA32X_C3_MIX1 60
+#define STA32X_C3_MIX2 61
+
+#endif /* _ASOC_STA_32X_H */
diff --git a/kernel/sound/soc/codecs/sta350.c b/kernel/sound/soc/codecs/sta350.c
new file mode 100644
index 000000000..669e32282
--- /dev/null
+++ b/kernel/sound/soc/codecs/sta350.c
@@ -0,0 +1,1280 @@
+/*
+ * Codec driver for ST STA350 2.1-channel high-efficiency digital audio system
+ *
+ * Copyright: 2014 Raumfeld GmbH
+ * Author: Sven Brandau <info@brandau.biz>
+ *
+ * based on code from:
+ * Raumfeld GmbH
+ * Johannes Stezenbach <js@sig21.net>
+ * Wolfson Microelectronics PLC.
+ * 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__
+
+#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/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.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 <sound/sta350.h>
+#include "sta350.h"
+
+#define STA350_RATES (SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000)
+
+#define STA350_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_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)
+
+/* Power-up register defaults */
+static const struct reg_default sta350_regs[] = {
+ { 0x0, 0x63 },
+ { 0x1, 0x80 },
+ { 0x2, 0xdf },
+ { 0x3, 0x40 },
+ { 0x4, 0xc2 },
+ { 0x5, 0x5c },
+ { 0x6, 0x00 },
+ { 0x7, 0xff },
+ { 0x8, 0x60 },
+ { 0x9, 0x60 },
+ { 0xa, 0x60 },
+ { 0xb, 0x00 },
+ { 0xc, 0x00 },
+ { 0xd, 0x00 },
+ { 0xe, 0x00 },
+ { 0xf, 0x40 },
+ { 0x10, 0x80 },
+ { 0x11, 0x77 },
+ { 0x12, 0x6a },
+ { 0x13, 0x69 },
+ { 0x14, 0x6a },
+ { 0x15, 0x69 },
+ { 0x16, 0x00 },
+ { 0x17, 0x00 },
+ { 0x18, 0x00 },
+ { 0x19, 0x00 },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x00 },
+ { 0x1e, 0x00 },
+ { 0x1f, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x23, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x27, 0x2a },
+ { 0x28, 0xc0 },
+ { 0x29, 0xf3 },
+ { 0x2a, 0x33 },
+ { 0x2b, 0x00 },
+ { 0x2c, 0x0c },
+ { 0x31, 0x00 },
+ { 0x36, 0x00 },
+ { 0x37, 0x00 },
+ { 0x38, 0x00 },
+ { 0x39, 0x01 },
+ { 0x3a, 0xee },
+ { 0x3b, 0xff },
+ { 0x3c, 0x7e },
+ { 0x3d, 0xc0 },
+ { 0x3e, 0x26 },
+ { 0x3f, 0x00 },
+ { 0x48, 0x00 },
+ { 0x49, 0x00 },
+ { 0x4a, 0x00 },
+ { 0x4b, 0x04 },
+ { 0x4c, 0x00 },
+};
+
+static const struct regmap_range sta350_write_regs_range[] = {
+ regmap_reg_range(STA350_CONFA, STA350_AUTO2),
+ regmap_reg_range(STA350_C1CFG, STA350_FDRC2),
+ regmap_reg_range(STA350_EQCFG, STA350_EVOLRES),
+ regmap_reg_range(STA350_NSHAPE, STA350_MISC2),
+};
+
+static const struct regmap_range sta350_read_regs_range[] = {
+ regmap_reg_range(STA350_CONFA, STA350_AUTO2),
+ regmap_reg_range(STA350_C1CFG, STA350_STATUS),
+ regmap_reg_range(STA350_EQCFG, STA350_EVOLRES),
+ regmap_reg_range(STA350_NSHAPE, STA350_MISC2),
+};
+
+static const struct regmap_range sta350_volatile_regs_range[] = {
+ regmap_reg_range(STA350_CFADDR2, STA350_CFUD),
+ regmap_reg_range(STA350_STATUS, STA350_STATUS),
+};
+
+static const struct regmap_access_table sta350_write_regs = {
+ .yes_ranges = sta350_write_regs_range,
+ .n_yes_ranges = ARRAY_SIZE(sta350_write_regs_range),
+};
+
+static const struct regmap_access_table sta350_read_regs = {
+ .yes_ranges = sta350_read_regs_range,
+ .n_yes_ranges = ARRAY_SIZE(sta350_read_regs_range),
+};
+
+static const struct regmap_access_table sta350_volatile_regs = {
+ .yes_ranges = sta350_volatile_regs_range,
+ .n_yes_ranges = ARRAY_SIZE(sta350_volatile_regs_range),
+};
+
+/* regulator power supply names */
+static const char * const sta350_supply_names[] = {
+ "vdd-dig", /* digital supply, 3.3V */
+ "vdd-pll", /* pll supply, 3.3V */
+ "vcc" /* power amp supply, 5V - 26V */
+};
+
+/* codec private data */
+struct sta350_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(sta350_supply_names)];
+ struct sta350_platform_data *pdata;
+
+ unsigned int mclk;
+ unsigned int format;
+
+ u32 coef_shadow[STA350_COEF_COUNT];
+ int shutdown;
+
+ struct gpio_desc *gpiod_nreset;
+ struct gpio_desc *gpiod_power_down;
+
+ struct mutex coeff_lock;
+};
+
+static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(chvol_tlv, -7950, 50, 1);
+static const DECLARE_TLV_DB_SCALE(tone_tlv, -1200, 200, 0);
+
+static const char * const sta350_drc_ac[] = {
+ "Anti-Clipping", "Dynamic Range Compression"
+};
+static const char * const sta350_auto_gc_mode[] = {
+ "User", "AC no clipping", "AC limited clipping (10%)",
+ "DRC nighttime listening mode"
+};
+static const char * const sta350_auto_xo_mode[] = {
+ "User", "80Hz", "100Hz", "120Hz", "140Hz", "160Hz", "180Hz",
+ "200Hz", "220Hz", "240Hz", "260Hz", "280Hz", "300Hz", "320Hz",
+ "340Hz", "360Hz"
+};
+static const char * const sta350_binary_output[] = {
+ "FFX 3-state output - normal operation", "Binary output"
+};
+static const char * const sta350_limiter_select[] = {
+ "Limiter Disabled", "Limiter #1", "Limiter #2"
+};
+static const char * const sta350_limiter_attack_rate[] = {
+ "3.1584", "2.7072", "2.2560", "1.8048", "1.3536", "0.9024",
+ "0.4512", "0.2256", "0.1504", "0.1123", "0.0902", "0.0752",
+ "0.0645", "0.0564", "0.0501", "0.0451"
+};
+static const char * const sta350_limiter_release_rate[] = {
+ "0.5116", "0.1370", "0.0744", "0.0499", "0.0360", "0.0299",
+ "0.0264", "0.0208", "0.0198", "0.0172", "0.0147", "0.0137",
+ "0.0134", "0.0117", "0.0110", "0.0104"
+};
+static const char * const sta350_noise_shaper_type[] = {
+ "Third order", "Fourth order"
+};
+
+static DECLARE_TLV_DB_RANGE(sta350_limiter_ac_attack_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 8, 16, TLV_DB_SCALE_ITEM(300, 100, 0),
+);
+
+static DECLARE_TLV_DB_RANGE(sta350_limiter_ac_release_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(-2900, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(-2000, 0, 0),
+ 3, 8, TLV_DB_SCALE_ITEM(-1400, 200, 0),
+ 8, 16, TLV_DB_SCALE_ITEM(-700, 100, 0),
+);
+
+static DECLARE_TLV_DB_RANGE(sta350_limiter_drc_attack_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(-3100, 200, 0),
+ 8, 13, TLV_DB_SCALE_ITEM(-1600, 100, 0),
+ 14, 16, TLV_DB_SCALE_ITEM(-1000, 300, 0),
+);
+
+static DECLARE_TLV_DB_RANGE(sta350_limiter_drc_release_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
+ 1, 2, TLV_DB_SCALE_ITEM(-3800, 200, 0),
+ 3, 4, TLV_DB_SCALE_ITEM(-3300, 200, 0),
+ 5, 12, TLV_DB_SCALE_ITEM(-3000, 200, 0),
+ 13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0),
+);
+
+static SOC_ENUM_SINGLE_DECL(sta350_drc_ac_enum,
+ STA350_CONFD, STA350_CONFD_DRC_SHIFT,
+ sta350_drc_ac);
+static SOC_ENUM_SINGLE_DECL(sta350_noise_shaper_enum,
+ STA350_CONFE, STA350_CONFE_NSBW_SHIFT,
+ sta350_noise_shaper_type);
+static SOC_ENUM_SINGLE_DECL(sta350_auto_gc_enum,
+ STA350_AUTO1, STA350_AUTO1_AMGC_SHIFT,
+ sta350_auto_gc_mode);
+static SOC_ENUM_SINGLE_DECL(sta350_auto_xo_enum,
+ STA350_AUTO2, STA350_AUTO2_XO_SHIFT,
+ sta350_auto_xo_mode);
+static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch1_enum,
+ STA350_C1CFG, STA350_CxCFG_BO_SHIFT,
+ sta350_binary_output);
+static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch2_enum,
+ STA350_C2CFG, STA350_CxCFG_BO_SHIFT,
+ sta350_binary_output);
+static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch3_enum,
+ STA350_C3CFG, STA350_CxCFG_BO_SHIFT,
+ sta350_binary_output);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch1_enum,
+ STA350_C1CFG, STA350_CxCFG_LS_SHIFT,
+ sta350_limiter_select);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch2_enum,
+ STA350_C2CFG, STA350_CxCFG_LS_SHIFT,
+ sta350_limiter_select);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch3_enum,
+ STA350_C3CFG, STA350_CxCFG_LS_SHIFT,
+ sta350_limiter_select);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter1_attack_rate_enum,
+ STA350_L1AR, STA350_LxA_SHIFT,
+ sta350_limiter_attack_rate);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter2_attack_rate_enum,
+ STA350_L2AR, STA350_LxA_SHIFT,
+ sta350_limiter_attack_rate);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter1_release_rate_enum,
+ STA350_L1AR, STA350_LxR_SHIFT,
+ sta350_limiter_release_rate);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter2_release_rate_enum,
+ STA350_L2AR, STA350_LxR_SHIFT,
+ sta350_limiter_release_rate);
+
+/*
+ * byte array controls for setting biquad, mixer, scaling coefficients;
+ * for biquads all five coefficients need to be set in one go,
+ * mixer and pre/postscale coefs can be set individually;
+ * each coef is 24bit, the bytes are ordered in the same way
+ * as given in the STA350 data sheet (big endian; b1, b2, a1, a2, b0)
+ */
+
+static int sta350_coefficient_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int numcoef = kcontrol->private_value >> 16;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = 3 * numcoef;
+ return 0;
+}
+
+static int sta350_coefficient_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ int numcoef = kcontrol->private_value >> 16;
+ int index = kcontrol->private_value & 0xffff;
+ unsigned int cfud, val;
+ int i, ret = 0;
+
+ mutex_lock(&sta350->coeff_lock);
+
+ /* preserve reserved bits in STA350_CFUD */
+ regmap_read(sta350->regmap, STA350_CFUD, &cfud);
+ cfud &= 0xf0;
+ /*
+ * chip documentation does not say if the bits are self clearing,
+ * so do it explicitly
+ */
+ regmap_write(sta350->regmap, STA350_CFUD, cfud);
+
+ regmap_write(sta350->regmap, STA350_CFADDR2, index);
+ if (numcoef == 1) {
+ regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x04);
+ } else if (numcoef == 5) {
+ regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x08);
+ } else {
+ ret = -EINVAL;
+ goto exit_unlock;
+ }
+
+ for (i = 0; i < 3 * numcoef; i++) {
+ regmap_read(sta350->regmap, STA350_B1CF1 + i, &val);
+ ucontrol->value.bytes.data[i] = val;
+ }
+
+exit_unlock:
+ mutex_unlock(&sta350->coeff_lock);
+
+ return ret;
+}
+
+static int sta350_coefficient_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ int numcoef = kcontrol->private_value >> 16;
+ int index = kcontrol->private_value & 0xffff;
+ unsigned int cfud;
+ int i;
+
+ /* preserve reserved bits in STA350_CFUD */
+ regmap_read(sta350->regmap, STA350_CFUD, &cfud);
+ cfud &= 0xf0;
+ /*
+ * chip documentation does not say if the bits are self clearing,
+ * so do it explicitly
+ */
+ regmap_write(sta350->regmap, STA350_CFUD, cfud);
+
+ regmap_write(sta350->regmap, STA350_CFADDR2, index);
+ for (i = 0; i < numcoef && (index + i < STA350_COEF_COUNT); i++)
+ sta350->coef_shadow[index + i] =
+ (ucontrol->value.bytes.data[3 * i] << 16)
+ | (ucontrol->value.bytes.data[3 * i + 1] << 8)
+ | (ucontrol->value.bytes.data[3 * i + 2]);
+ for (i = 0; i < 3 * numcoef; i++)
+ regmap_write(sta350->regmap, STA350_B1CF1 + i,
+ ucontrol->value.bytes.data[i]);
+ if (numcoef == 1)
+ regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x01);
+ else if (numcoef == 5)
+ regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x02);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sta350_sync_coef_shadow(struct snd_soc_codec *codec)
+{
+ struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ unsigned int cfud;
+ int i;
+
+ /* preserve reserved bits in STA350_CFUD */
+ regmap_read(sta350->regmap, STA350_CFUD, &cfud);
+ cfud &= 0xf0;
+
+ for (i = 0; i < STA350_COEF_COUNT; i++) {
+ regmap_write(sta350->regmap, STA350_CFADDR2, i);
+ regmap_write(sta350->regmap, STA350_B1CF1,
+ (sta350->coef_shadow[i] >> 16) & 0xff);
+ regmap_write(sta350->regmap, STA350_B1CF2,
+ (sta350->coef_shadow[i] >> 8) & 0xff);
+ regmap_write(sta350->regmap, STA350_B1CF3,
+ (sta350->coef_shadow[i]) & 0xff);
+ /*
+ * chip documentation does not say if the bits are
+ * self-clearing, so do it explicitly
+ */
+ regmap_write(sta350->regmap, STA350_CFUD, cfud);
+ regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x01);
+ }
+ return 0;
+}
+
+static int sta350_cache_sync(struct snd_soc_codec *codec)
+{
+ struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ 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);
+ rc = regcache_sync(sta350->regmap);
+ regmap_write(sta350->regmap, STA350_MMUTE, mute);
+ return rc;
+}
+
+#define SINGLE_COEF(xname, index) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = sta350_coefficient_info, \
+ .get = sta350_coefficient_get,\
+ .put = sta350_coefficient_put, \
+ .private_value = index | (1 << 16) }
+
+#define BIQUAD_COEFS(xname, index) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = sta350_coefficient_info, \
+ .get = sta350_coefficient_get,\
+ .put = sta350_coefficient_put, \
+ .private_value = index | (5 << 16) }
+
+static const struct snd_kcontrol_new sta350_snd_controls[] = {
+SOC_SINGLE_TLV("Master Volume", STA350_MVOL, 0, 0xff, 1, mvol_tlv),
+/* VOL */
+SOC_SINGLE_TLV("Ch1 Volume", STA350_C1VOL, 0, 0xff, 1, chvol_tlv),
+SOC_SINGLE_TLV("Ch2 Volume", STA350_C2VOL, 0, 0xff, 1, chvol_tlv),
+SOC_SINGLE_TLV("Ch3 Volume", STA350_C3VOL, 0, 0xff, 1, chvol_tlv),
+/* CONFD */
+SOC_SINGLE("High Pass Filter Bypass Switch",
+ STA350_CONFD, STA350_CONFD_HPB_SHIFT, 1, 1),
+SOC_SINGLE("De-emphasis Filter Switch",
+ STA350_CONFD, STA350_CONFD_DEMP_SHIFT, 1, 0),
+SOC_SINGLE("DSP Bypass Switch",
+ STA350_CONFD, STA350_CONFD_DSPB_SHIFT, 1, 0),
+SOC_SINGLE("Post-scale Link Switch",
+ STA350_CONFD, STA350_CONFD_PSL_SHIFT, 1, 0),
+SOC_SINGLE("Biquad Coefficient Link Switch",
+ STA350_CONFD, STA350_CONFD_BQL_SHIFT, 1, 0),
+SOC_ENUM("Compressor/Limiter Switch", sta350_drc_ac_enum),
+SOC_ENUM("Noise Shaper Bandwidth", sta350_noise_shaper_enum),
+SOC_SINGLE("Zero-detect Mute Enable Switch",
+ STA350_CONFD, STA350_CONFD_ZDE_SHIFT, 1, 0),
+SOC_SINGLE("Submix Mode Switch",
+ STA350_CONFD, STA350_CONFD_SME_SHIFT, 1, 0),
+/* CONFE */
+SOC_SINGLE("Zero Cross Switch", STA350_CONFE, STA350_CONFE_ZCE_SHIFT, 1, 0),
+SOC_SINGLE("Soft Ramp Switch", STA350_CONFE, STA350_CONFE_SVE_SHIFT, 1, 0),
+/* MUTE */
+SOC_SINGLE("Master Switch", STA350_MMUTE, STA350_MMUTE_MMUTE_SHIFT, 1, 1),
+SOC_SINGLE("Ch1 Switch", STA350_MMUTE, STA350_MMUTE_C1M_SHIFT, 1, 1),
+SOC_SINGLE("Ch2 Switch", STA350_MMUTE, STA350_MMUTE_C2M_SHIFT, 1, 1),
+SOC_SINGLE("Ch3 Switch", STA350_MMUTE, STA350_MMUTE_C3M_SHIFT, 1, 1),
+/* AUTOx */
+SOC_ENUM("Automode GC", sta350_auto_gc_enum),
+SOC_ENUM("Automode XO", sta350_auto_xo_enum),
+/* CxCFG */
+SOC_SINGLE("Ch1 Tone Control Bypass Switch",
+ STA350_C1CFG, STA350_CxCFG_TCB_SHIFT, 1, 0),
+SOC_SINGLE("Ch2 Tone Control Bypass Switch",
+ STA350_C2CFG, STA350_CxCFG_TCB_SHIFT, 1, 0),
+SOC_SINGLE("Ch1 EQ Bypass Switch",
+ STA350_C1CFG, STA350_CxCFG_EQBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch2 EQ Bypass Switch",
+ STA350_C2CFG, STA350_CxCFG_EQBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch1 Master Volume Bypass Switch",
+ STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch2 Master Volume Bypass Switch",
+ STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch3 Master Volume Bypass Switch",
+ STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0),
+SOC_ENUM("Ch1 Binary Output Select", sta350_binary_output_ch1_enum),
+SOC_ENUM("Ch2 Binary Output Select", sta350_binary_output_ch2_enum),
+SOC_ENUM("Ch3 Binary Output Select", sta350_binary_output_ch3_enum),
+SOC_ENUM("Ch1 Limiter Select", sta350_limiter_ch1_enum),
+SOC_ENUM("Ch2 Limiter Select", sta350_limiter_ch2_enum),
+SOC_ENUM("Ch3 Limiter Select", sta350_limiter_ch3_enum),
+/* TONE */
+SOC_SINGLE_RANGE_TLV("Bass Tone Control Volume",
+ STA350_TONE, STA350_TONE_BTC_SHIFT, 1, 13, 0, tone_tlv),
+SOC_SINGLE_RANGE_TLV("Treble Tone Control Volume",
+ STA350_TONE, STA350_TONE_TTC_SHIFT, 1, 13, 0, tone_tlv),
+SOC_ENUM("Limiter1 Attack Rate (dB/ms)", sta350_limiter1_attack_rate_enum),
+SOC_ENUM("Limiter2 Attack Rate (dB/ms)", sta350_limiter2_attack_rate_enum),
+SOC_ENUM("Limiter1 Release Rate (dB/ms)", sta350_limiter1_release_rate_enum),
+SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta350_limiter2_release_rate_enum),
+
+/*
+ * depending on mode, the attack/release thresholds have
+ * two different enum definitions; provide both
+ */
+SOC_SINGLE_TLV("Limiter1 Attack Threshold (AC Mode)",
+ STA350_L1ATRT, STA350_LxA_SHIFT,
+ 16, 0, sta350_limiter_ac_attack_tlv),
+SOC_SINGLE_TLV("Limiter2 Attack Threshold (AC Mode)",
+ STA350_L2ATRT, STA350_LxA_SHIFT,
+ 16, 0, sta350_limiter_ac_attack_tlv),
+SOC_SINGLE_TLV("Limiter1 Release Threshold (AC Mode)",
+ STA350_L1ATRT, STA350_LxR_SHIFT,
+ 16, 0, sta350_limiter_ac_release_tlv),
+SOC_SINGLE_TLV("Limiter2 Release Threshold (AC Mode)",
+ STA350_L2ATRT, STA350_LxR_SHIFT,
+ 16, 0, sta350_limiter_ac_release_tlv),
+SOC_SINGLE_TLV("Limiter1 Attack Threshold (DRC Mode)",
+ STA350_L1ATRT, STA350_LxA_SHIFT,
+ 16, 0, sta350_limiter_drc_attack_tlv),
+SOC_SINGLE_TLV("Limiter2 Attack Threshold (DRC Mode)",
+ STA350_L2ATRT, STA350_LxA_SHIFT,
+ 16, 0, sta350_limiter_drc_attack_tlv),
+SOC_SINGLE_TLV("Limiter1 Release Threshold (DRC Mode)",
+ STA350_L1ATRT, STA350_LxR_SHIFT,
+ 16, 0, sta350_limiter_drc_release_tlv),
+SOC_SINGLE_TLV("Limiter2 Release Threshold (DRC Mode)",
+ STA350_L2ATRT, STA350_LxR_SHIFT,
+ 16, 0, sta350_limiter_drc_release_tlv),
+
+BIQUAD_COEFS("Ch1 - Biquad 1", 0),
+BIQUAD_COEFS("Ch1 - Biquad 2", 5),
+BIQUAD_COEFS("Ch1 - Biquad 3", 10),
+BIQUAD_COEFS("Ch1 - Biquad 4", 15),
+BIQUAD_COEFS("Ch2 - Biquad 1", 20),
+BIQUAD_COEFS("Ch2 - Biquad 2", 25),
+BIQUAD_COEFS("Ch2 - Biquad 3", 30),
+BIQUAD_COEFS("Ch2 - Biquad 4", 35),
+BIQUAD_COEFS("High-pass", 40),
+BIQUAD_COEFS("Low-pass", 45),
+SINGLE_COEF("Ch1 - Prescale", 50),
+SINGLE_COEF("Ch2 - Prescale", 51),
+SINGLE_COEF("Ch1 - Postscale", 52),
+SINGLE_COEF("Ch2 - Postscale", 53),
+SINGLE_COEF("Ch3 - Postscale", 54),
+SINGLE_COEF("Thermal warning - Postscale", 55),
+SINGLE_COEF("Ch1 - Mix 1", 56),
+SINGLE_COEF("Ch1 - Mix 2", 57),
+SINGLE_COEF("Ch2 - Mix 1", 58),
+SINGLE_COEF("Ch2 - Mix 2", 59),
+SINGLE_COEF("Ch3 - Mix 1", 60),
+SINGLE_COEF("Ch3 - Mix 2", 61),
+};
+
+static const struct snd_soc_dapm_widget sta350_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("LEFT"),
+SND_SOC_DAPM_OUTPUT("RIGHT"),
+SND_SOC_DAPM_OUTPUT("SUB"),
+};
+
+static const struct snd_soc_dapm_route sta350_dapm_routes[] = {
+ { "LEFT", NULL, "DAC" },
+ { "RIGHT", NULL, "DAC" },
+ { "SUB", NULL, "DAC" },
+ { "DAC", NULL, "Playback" },
+};
+
+/* MCLK interpolation ratio per fs */
+static struct {
+ int fs;
+ int ir;
+} interpolation_ratios[] = {
+ { 32000, 0 },
+ { 44100, 0 },
+ { 48000, 0 },
+ { 88200, 1 },
+ { 96000, 1 },
+ { 176400, 2 },
+ { 192000, 2 },
+};
+
+/* MCLK to fs clock ratios */
+static int mcs_ratio_table[3][6] = {
+ { 768, 512, 384, 256, 128, 576 },
+ { 384, 256, 192, 128, 64, 0 },
+ { 192, 128, 96, 64, 32, 0 },
+};
+
+/**
+ * sta350_set_dai_sysclk - configure MCLK
+ * @codec_dai: the codec DAI
+ * @clk_id: the clock ID (ignored)
+ * @freq: the MCLK input frequency
+ * @dir: the clock direction (ignored)
+ *
+ * The value of MCLK is used to determine which sample rates are supported
+ * by the STA350, based on the mcs_ratio_table.
+ *
+ * This function must be called by the machine driver's 'startup' function,
+ * otherwise the list of supported sample rates will not be available in
+ * time for ALSA.
+ */
+static int sta350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "mclk=%u\n", freq);
+ sta350->mclk = freq;
+
+ return 0;
+}
+
+/**
+ * sta350_set_dai_fmt - configure the codec for the selected audio format
+ * @codec_dai: the codec DAI
+ * @fmt: a SND_SOC_DAIFMT_x value indicating the data format
+ *
+ * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the
+ * codec accordingly.
+ */
+static int sta350_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ unsigned int confb = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ sta350->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ confb |= STA350_CONFB_C2IM;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ confb |= STA350_CONFB_C1IM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(sta350->regmap, STA350_CONFB,
+ STA350_CONFB_C1IM | STA350_CONFB_C2IM, confb);
+}
+
+/**
+ * sta350_hw_params - program the STA350 with the given hardware parameters.
+ * @substream: the audio stream
+ * @params: the hardware parameters to set
+ * @dai: the SOC DAI (ignored)
+ *
+ * This function programs the hardware with the values provided.
+ * Specifically, the sample rate and the data format.
+ */
+static int sta350_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ int i, mcs = -EINVAL, ir = -EINVAL;
+ unsigned int confa, confb;
+ unsigned int rate, ratio;
+ int ret;
+
+ if (!sta350->mclk) {
+ dev_err(codec->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);
+
+ for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) {
+ if (interpolation_ratios[i].fs == rate) {
+ ir = interpolation_ratios[i].ir;
+ break;
+ }
+ }
+
+ if (ir < 0) {
+ dev_err(codec->dev, "Unsupported samplerate: %u\n", rate);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 6; i++) {
+ if (mcs_ratio_table[ir][i] == ratio) {
+ mcs = i;
+ break;
+ }
+ }
+
+ if (mcs < 0) {
+ dev_err(codec->dev, "Unresolvable ratio: %u\n", ratio);
+ return -EINVAL;
+ }
+
+ confa = (ir << STA350_CONFA_IR_SHIFT) |
+ (mcs << STA350_CONFA_MCS_SHIFT);
+ confb = 0;
+
+ switch (params_width(params)) {
+ case 24:
+ dev_dbg(codec->dev, "24bit\n");
+ /* fall through */
+ case 32:
+ dev_dbg(codec->dev, "24bit or 32bit\n");
+ switch (sta350->format) {
+ case SND_SOC_DAIFMT_I2S:
+ confb |= 0x0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ confb |= 0x1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ confb |= 0x2;
+ break;
+ }
+
+ break;
+ case 20:
+ dev_dbg(codec->dev, "20bit\n");
+ switch (sta350->format) {
+ case SND_SOC_DAIFMT_I2S:
+ confb |= 0x4;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ confb |= 0x5;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ confb |= 0x6;
+ break;
+ }
+
+ break;
+ case 18:
+ dev_dbg(codec->dev, "18bit\n");
+ switch (sta350->format) {
+ case SND_SOC_DAIFMT_I2S:
+ confb |= 0x8;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ confb |= 0x9;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ confb |= 0xa;
+ break;
+ }
+
+ break;
+ case 16:
+ dev_dbg(codec->dev, "16bit\n");
+ switch (sta350->format) {
+ case SND_SOC_DAIFMT_I2S:
+ confb |= 0x0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ confb |= 0xd;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ confb |= 0xe;
+ break;
+ }
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(sta350->regmap, STA350_CONFA,
+ STA350_CONFA_MCS_MASK | STA350_CONFA_IR_MASK,
+ confa);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(sta350->regmap, STA350_CONFB,
+ STA350_CONFB_SAI_MASK | STA350_CONFB_SAIFB,
+ confb);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int sta350_startup_sequence(struct sta350_priv *sta350)
+{
+ if (sta350->gpiod_power_down)
+ gpiod_set_value(sta350->gpiod_power_down, 1);
+
+ if (sta350->gpiod_nreset) {
+ gpiod_set_value(sta350->gpiod_nreset, 0);
+ mdelay(1);
+ gpiod_set_value(sta350->gpiod_nreset, 1);
+ mdelay(1);
+ }
+
+ return 0;
+}
+
+/**
+ * sta350_set_bias_level - DAPM callback
+ * @codec: the codec 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
+ * all registers must be restored after power on.
+ */
+static int sta350_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ dev_dbg(codec->dev, "level = %d\n", level);
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* Full power on */
+ regmap_update_bits(sta350->regmap, STA350_CONFF,
+ STA350_CONFF_PWDN | STA350_CONFF_EAPD,
+ STA350_CONFF_PWDN | STA350_CONFF_EAPD);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(
+ ARRAY_SIZE(sta350->supplies),
+ sta350->supplies);
+ if (ret < 0) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+ sta350_startup_sequence(sta350);
+ sta350_cache_sync(codec);
+ }
+
+ /* Power down */
+ regmap_update_bits(sta350->regmap, STA350_CONFF,
+ STA350_CONFF_PWDN | STA350_CONFF_EAPD,
+ 0);
+
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* The chip runs through the power down sequence for us */
+ regmap_update_bits(sta350->regmap, STA350_CONFF,
+ STA350_CONFF_PWDN | STA350_CONFF_EAPD, 0);
+
+ /* power down: low */
+ if (sta350->gpiod_power_down)
+ gpiod_set_value(sta350->gpiod_power_down, 0);
+
+ if (sta350->gpiod_nreset)
+ gpiod_set_value(sta350->gpiod_nreset, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(sta350->supplies),
+ sta350->supplies);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sta350_dai_ops = {
+ .hw_params = sta350_hw_params,
+ .set_sysclk = sta350_set_dai_sysclk,
+ .set_fmt = sta350_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver sta350_dai = {
+ .name = "sta350-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = STA350_RATES,
+ .formats = STA350_FORMATS,
+ },
+ .ops = &sta350_dai_ops,
+};
+
+static int sta350_probe(struct snd_soc_codec *codec)
+{
+ struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ 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);
+ return ret;
+ }
+
+ ret = sta350_startup_sequence(sta350);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to startup device\n");
+ return ret;
+ }
+
+ /* CONFA */
+ if (!pdata->thermal_warning_recovery)
+ thermal |= STA350_CONFA_TWAB;
+ if (!pdata->thermal_warning_adjustment)
+ thermal |= STA350_CONFA_TWRB;
+ if (!pdata->fault_detect_recovery)
+ thermal |= STA350_CONFA_FDRB;
+ regmap_update_bits(sta350->regmap, STA350_CONFA,
+ STA350_CONFA_TWAB | STA350_CONFA_TWRB |
+ STA350_CONFA_FDRB,
+ thermal);
+
+ /* CONFC */
+ regmap_update_bits(sta350->regmap, STA350_CONFC,
+ STA350_CONFC_OM_MASK,
+ pdata->ffx_power_output_mode
+ << STA350_CONFC_OM_SHIFT);
+ regmap_update_bits(sta350->regmap, STA350_CONFC,
+ STA350_CONFC_CSZ_MASK,
+ pdata->drop_compensation_ns
+ << STA350_CONFC_CSZ_SHIFT);
+ regmap_update_bits(sta350->regmap,
+ STA350_CONFC,
+ STA350_CONFC_OCRB,
+ pdata->oc_warning_adjustment ?
+ STA350_CONFC_OCRB : 0);
+
+ /* CONFE */
+ regmap_update_bits(sta350->regmap, STA350_CONFE,
+ STA350_CONFE_MPCV,
+ pdata->max_power_use_mpcc ?
+ STA350_CONFE_MPCV : 0);
+ regmap_update_bits(sta350->regmap, STA350_CONFE,
+ STA350_CONFE_MPC,
+ pdata->max_power_correction ?
+ STA350_CONFE_MPC : 0);
+ regmap_update_bits(sta350->regmap, STA350_CONFE,
+ STA350_CONFE_AME,
+ pdata->am_reduction_mode ?
+ STA350_CONFE_AME : 0);
+ regmap_update_bits(sta350->regmap, STA350_CONFE,
+ STA350_CONFE_PWMS,
+ pdata->odd_pwm_speed_mode ?
+ STA350_CONFE_PWMS : 0);
+ regmap_update_bits(sta350->regmap, STA350_CONFE,
+ STA350_CONFE_DCCV,
+ pdata->distortion_compensation ?
+ STA350_CONFE_DCCV : 0);
+ /* CONFF */
+ regmap_update_bits(sta350->regmap, STA350_CONFF,
+ STA350_CONFF_IDE,
+ pdata->invalid_input_detect_mute ?
+ STA350_CONFF_IDE : 0);
+ regmap_update_bits(sta350->regmap, STA350_CONFF,
+ STA350_CONFF_OCFG_MASK,
+ pdata->output_conf
+ << STA350_CONFF_OCFG_SHIFT);
+
+ /* channel to output mapping */
+ regmap_update_bits(sta350->regmap, STA350_C1CFG,
+ STA350_CxCFG_OM_MASK,
+ pdata->ch1_output_mapping
+ << STA350_CxCFG_OM_SHIFT);
+ regmap_update_bits(sta350->regmap, STA350_C2CFG,
+ STA350_CxCFG_OM_MASK,
+ pdata->ch2_output_mapping
+ << STA350_CxCFG_OM_SHIFT);
+ regmap_update_bits(sta350->regmap, STA350_C3CFG,
+ STA350_CxCFG_OM_MASK,
+ pdata->ch3_output_mapping
+ << STA350_CxCFG_OM_SHIFT);
+
+ /* miscellaneous registers */
+ regmap_update_bits(sta350->regmap, STA350_MISC1,
+ STA350_MISC1_CPWMEN,
+ pdata->activate_mute_output ?
+ STA350_MISC1_CPWMEN : 0);
+ regmap_update_bits(sta350->regmap, STA350_MISC1,
+ STA350_MISC1_BRIDGOFF,
+ pdata->bridge_immediate_off ?
+ STA350_MISC1_BRIDGOFF : 0);
+ regmap_update_bits(sta350->regmap, STA350_MISC1,
+ STA350_MISC1_NSHHPEN,
+ pdata->noise_shape_dc_cut ?
+ STA350_MISC1_NSHHPEN : 0);
+ regmap_update_bits(sta350->regmap, STA350_MISC1,
+ STA350_MISC1_RPDNEN,
+ pdata->powerdown_master_vol ?
+ STA350_MISC1_RPDNEN: 0);
+
+ regmap_update_bits(sta350->regmap, STA350_MISC2,
+ STA350_MISC2_PNDLSL_MASK,
+ pdata->powerdown_delay_divider
+ << STA350_MISC2_PNDLSL_SHIFT);
+
+ /* initialize coefficient shadow RAM with reset values */
+ for (i = 4; i <= 49; i += 5)
+ sta350->coef_shadow[i] = 0x400000;
+ for (i = 50; i <= 54; i++)
+ sta350->coef_shadow[i] = 0x7fffff;
+ sta350->coef_shadow[55] = 0x5a9df7;
+ sta350->coef_shadow[56] = 0x7fffff;
+ sta350->coef_shadow[59] = 0x7fffff;
+ sta350->coef_shadow[60] = 0x400000;
+ sta350->coef_shadow[61] = 0x400000;
+
+ sta350_set_bias_level(codec, 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)
+{
+ struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+
+ 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 regmap_config sta350_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = STA350_MISC2,
+ .reg_defaults = sta350_regs,
+ .num_reg_defaults = ARRAY_SIZE(sta350_regs),
+ .cache_type = REGCACHE_RBTREE,
+ .wr_table = &sta350_write_regs,
+ .rd_table = &sta350_read_regs,
+ .volatile_table = &sta350_volatile_regs,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id st350_dt_ids[] = {
+ { .compatible = "st,sta350", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, st350_dt_ids);
+
+static const char * const sta350_ffx_modes[] = {
+ [STA350_FFX_PM_DROP_COMP] = "drop-compensation",
+ [STA350_FFX_PM_TAPERED_COMP] = "tapered-compensation",
+ [STA350_FFX_PM_FULL_POWER] = "full-power-mode",
+ [STA350_FFX_PM_VARIABLE_DROP_COMP] = "variable-drop-compensation",
+};
+
+static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
+{
+ struct device_node *np = dev->of_node;
+ struct sta350_platform_data *pdata;
+ const char *ffx_power_mode;
+ u16 tmp;
+ u8 tmp8;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ of_property_read_u8(np, "st,output-conf",
+ &pdata->output_conf);
+ of_property_read_u8(np, "st,ch1-output-mapping",
+ &pdata->ch1_output_mapping);
+ of_property_read_u8(np, "st,ch2-output-mapping",
+ &pdata->ch2_output_mapping);
+ of_property_read_u8(np, "st,ch3-output-mapping",
+ &pdata->ch3_output_mapping);
+
+ 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->ffx_power_output_mode = STA350_FFX_PM_VARIABLE_DROP_COMP;
+ if (!of_property_read_string(np, "st,ffx-power-output-mode",
+ &ffx_power_mode)) {
+ int i, mode = -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(sta350_ffx_modes); i++)
+ if (!strcasecmp(ffx_power_mode, sta350_ffx_modes[i]))
+ mode = i;
+
+ if (mode < 0)
+ dev_warn(dev, "Unsupported ffx output mode: %s\n",
+ ffx_power_mode);
+ else
+ pdata->ffx_power_output_mode = mode;
+ }
+
+ tmp = 140;
+ of_property_read_u16(np, "st,drop-compensation-ns", &tmp);
+ pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20;
+
+ if (of_get_property(np, "st,overcurrent-warning-adjustment", NULL))
+ pdata->oc_warning_adjustment = 1;
+
+ /* 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;
+
+ /* CONFF */
+ if (of_get_property(np, "st,invalid-input-detect-mute", NULL))
+ pdata->invalid_input_detect_mute = 1;
+
+ /* 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;
+
+ if (!of_property_read_u8(np, "st,powerdown-delay-divider", &tmp8)) {
+ if (is_power_of_2(tmp8) && tmp8 >= 1 && tmp8 <= 128)
+ pdata->powerdown_delay_divider = ilog2(tmp8);
+ else
+ dev_warn(dev, "Unsupported powerdown delay divider %d\n",
+ tmp8);
+ }
+
+ sta350->pdata = pdata;
+
+ return 0;
+}
+#endif
+
+static int sta350_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct sta350_priv *sta350;
+ int ret, i;
+
+ sta350 = devm_kzalloc(dev, sizeof(struct sta350_priv), GFP_KERNEL);
+ if (!sta350)
+ return -ENOMEM;
+
+ mutex_init(&sta350->coeff_lock);
+ sta350->pdata = dev_get_platdata(dev);
+
+#ifdef CONFIG_OF
+ if (dev->of_node) {
+ ret = sta350_probe_dt(dev, sta350);
+ if (ret < 0)
+ return ret;
+ }
+#endif
+
+ /* GPIOs */
+ sta350->gpiod_nreset = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(sta350->gpiod_nreset))
+ return PTR_ERR(sta350->gpiod_nreset);
+
+ sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(sta350->gpiod_power_down))
+ return PTR_ERR(sta350->gpiod_power_down);
+
+ /* regulators */
+ for (i = 0; i < ARRAY_SIZE(sta350->supplies); i++)
+ sta350->supplies[i].supply = sta350_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sta350->supplies),
+ sta350->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ sta350->regmap = devm_regmap_init_i2c(i2c, &sta350_regmap);
+ if (IS_ERR(sta350->regmap)) {
+ ret = PTR_ERR(sta350->regmap);
+ dev_err(dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, sta350);
+
+ ret = snd_soc_register_codec(dev, &sta350_codec, &sta350_dai, 1);
+ if (ret < 0)
+ dev_err(dev, "Failed to register codec (%d)\n", ret);
+
+ return ret;
+}
+
+static int sta350_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id sta350_i2c_id[] = {
+ { "sta350", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sta350_i2c_id);
+
+static struct i2c_driver sta350_i2c_driver = {
+ .driver = {
+ .name = "sta350",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(st350_dt_ids),
+ },
+ .probe = sta350_i2c_probe,
+ .remove = sta350_i2c_remove,
+ .id_table = sta350_i2c_id,
+};
+
+module_i2c_driver(sta350_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC STA350 driver");
+MODULE_AUTHOR("Sven Brandau <info@brandau.biz>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/sta350.h b/kernel/sound/soc/codecs/sta350.h
new file mode 100644
index 000000000..fb7285290
--- /dev/null
+++ b/kernel/sound/soc/codecs/sta350.h
@@ -0,0 +1,238 @@
+/*
+ * Codec driver for ST STA350 2.1-channel high-efficiency digital audio system
+ *
+ * Copyright: 2011 Raumfeld GmbH
+ * Author: Sven Brandau <info@brandau.biz>
+ *
+ * based on code from:
+ * Raumfeld GmbH
+ * 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 */
+
+#define STA350_REGISTER_COUNT 0x4D
+#define STA350_COEF_COUNT 62
+
+#define STA350_CONFA 0x00
+#define STA350_CONFB 0x01
+#define STA350_CONFC 0x02
+#define STA350_CONFD 0x03
+#define STA350_CONFE 0x04
+#define STA350_CONFF 0x05
+#define STA350_MMUTE 0x06
+#define STA350_MVOL 0x07
+#define STA350_C1VOL 0x08
+#define STA350_C2VOL 0x09
+#define STA350_C3VOL 0x0a
+#define STA350_AUTO1 0x0b
+#define STA350_AUTO2 0x0c
+#define STA350_AUTO3 0x0d
+#define STA350_C1CFG 0x0e
+#define STA350_C2CFG 0x0f
+#define STA350_C3CFG 0x10
+#define STA350_TONE 0x11
+#define STA350_L1AR 0x12
+#define STA350_L1ATRT 0x13
+#define STA350_L2AR 0x14
+#define STA350_L2ATRT 0x15
+#define STA350_CFADDR2 0x16
+#define STA350_B1CF1 0x17
+#define STA350_B1CF2 0x18
+#define STA350_B1CF3 0x19
+#define STA350_B2CF1 0x1a
+#define STA350_B2CF2 0x1b
+#define STA350_B2CF3 0x1c
+#define STA350_A1CF1 0x1d
+#define STA350_A1CF2 0x1e
+#define STA350_A1CF3 0x1f
+#define STA350_A2CF1 0x20
+#define STA350_A2CF2 0x21
+#define STA350_A2CF3 0x22
+#define STA350_B0CF1 0x23
+#define STA350_B0CF2 0x24
+#define STA350_B0CF3 0x25
+#define STA350_CFUD 0x26
+#define STA350_MPCC1 0x27
+#define STA350_MPCC2 0x28
+#define STA350_DCC1 0x29
+#define STA350_DCC2 0x2a
+#define STA350_FDRC1 0x2b
+#define STA350_FDRC2 0x2c
+#define STA350_STATUS 0x2d
+/* reserved: 0x2d - 0x30 */
+#define STA350_EQCFG 0x31
+#define STA350_EATH1 0x32
+#define STA350_ERTH1 0x33
+#define STA350_EATH2 0x34
+#define STA350_ERTH2 0x35
+#define STA350_CONFX 0x36
+#define STA350_SVCA 0x37
+#define STA350_SVCB 0x38
+#define STA350_RMS0A 0x39
+#define STA350_RMS0B 0x3a
+#define STA350_RMS0C 0x3b
+#define STA350_RMS1A 0x3c
+#define STA350_RMS1B 0x3d
+#define STA350_RMS1C 0x3e
+#define STA350_EVOLRES 0x3f
+/* reserved: 0x40 - 0x47 */
+#define STA350_NSHAPE 0x48
+#define STA350_CTXB4B1 0x49
+#define STA350_CTXB7B5 0x4a
+#define STA350_MISC1 0x4b
+#define STA350_MISC2 0x4c
+
+/* 0x00 CONFA */
+#define STA350_CONFA_MCS_MASK 0x03
+#define STA350_CONFA_MCS_SHIFT 0
+#define STA350_CONFA_IR_MASK 0x18
+#define STA350_CONFA_IR_SHIFT 3
+#define STA350_CONFA_TWRB BIT(5)
+#define STA350_CONFA_TWAB BIT(6)
+#define STA350_CONFA_FDRB BIT(7)
+
+/* 0x01 CONFB */
+#define STA350_CONFB_SAI_MASK 0x0f
+#define STA350_CONFB_SAI_SHIFT 0
+#define STA350_CONFB_SAIFB BIT(4)
+#define STA350_CONFB_DSCKE BIT(5)
+#define STA350_CONFB_C1IM BIT(6)
+#define STA350_CONFB_C2IM BIT(7)
+
+/* 0x02 CONFC */
+#define STA350_CONFC_OM_MASK 0x03
+#define STA350_CONFC_OM_SHIFT 0
+#define STA350_CONFC_CSZ_MASK 0x3c
+#define STA350_CONFC_CSZ_SHIFT 2
+#define STA350_CONFC_OCRB BIT(7)
+
+/* 0x03 CONFD */
+#define STA350_CONFD_HPB_SHIFT 0
+#define STA350_CONFD_DEMP_SHIFT 1
+#define STA350_CONFD_DSPB_SHIFT 2
+#define STA350_CONFD_PSL_SHIFT 3
+#define STA350_CONFD_BQL_SHIFT 4
+#define STA350_CONFD_DRC_SHIFT 5
+#define STA350_CONFD_ZDE_SHIFT 6
+#define STA350_CONFD_SME_SHIFT 7
+
+/* 0x04 CONFE */
+#define STA350_CONFE_MPCV BIT(0)
+#define STA350_CONFE_MPCV_SHIFT 0
+#define STA350_CONFE_MPC BIT(1)
+#define STA350_CONFE_MPC_SHIFT 1
+#define STA350_CONFE_NSBW BIT(2)
+#define STA350_CONFE_NSBW_SHIFT 2
+#define STA350_CONFE_AME BIT(3)
+#define STA350_CONFE_AME_SHIFT 3
+#define STA350_CONFE_PWMS BIT(4)
+#define STA350_CONFE_PWMS_SHIFT 4
+#define STA350_CONFE_DCCV BIT(5)
+#define STA350_CONFE_DCCV_SHIFT 5
+#define STA350_CONFE_ZCE BIT(6)
+#define STA350_CONFE_ZCE_SHIFT 6
+#define STA350_CONFE_SVE BIT(7)
+#define STA350_CONFE_SVE_SHIFT 7
+
+/* 0x05 CONFF */
+#define STA350_CONFF_OCFG_MASK 0x03
+#define STA350_CONFF_OCFG_SHIFT 0
+#define STA350_CONFF_IDE BIT(2)
+#define STA350_CONFF_BCLE BIT(3)
+#define STA350_CONFF_LDTE BIT(4)
+#define STA350_CONFF_ECLE BIT(5)
+#define STA350_CONFF_PWDN BIT(6)
+#define STA350_CONFF_EAPD BIT(7)
+
+/* 0x06 MMUTE */
+#define STA350_MMUTE_MMUTE 0x01
+#define STA350_MMUTE_MMUTE_SHIFT 0
+#define STA350_MMUTE_C1M 0x02
+#define STA350_MMUTE_C1M_SHIFT 1
+#define STA350_MMUTE_C2M 0x04
+#define STA350_MMUTE_C2M_SHIFT 2
+#define STA350_MMUTE_C3M 0x08
+#define STA350_MMUTE_C3M_SHIFT 3
+#define STA350_MMUTE_LOC_MASK 0xC0
+#define STA350_MMUTE_LOC_SHIFT 6
+
+/* 0x0b AUTO1 */
+#define STA350_AUTO1_AMGC_MASK 0x30
+#define STA350_AUTO1_AMGC_SHIFT 4
+
+/* 0x0c AUTO2 */
+#define STA350_AUTO2_AMAME 0x01
+#define STA350_AUTO2_AMAM_MASK 0x0e
+#define STA350_AUTO2_AMAM_SHIFT 1
+#define STA350_AUTO2_XO_MASK 0xf0
+#define STA350_AUTO2_XO_SHIFT 4
+
+/* 0x0d AUTO3 */
+#define STA350_AUTO3_PEQ_MASK 0x1f
+#define STA350_AUTO3_PEQ_SHIFT 0
+
+/* 0x0e 0x0f 0x10 CxCFG */
+#define STA350_CxCFG_TCB_SHIFT 0
+#define STA350_CxCFG_EQBP_SHIFT 1
+#define STA350_CxCFG_VBP_SHIFT 2
+#define STA350_CxCFG_BO_SHIFT 3
+#define STA350_CxCFG_LS_SHIFT 4
+#define STA350_CxCFG_OM_MASK 0xc0
+#define STA350_CxCFG_OM_SHIFT 6
+
+/* 0x11 TONE */
+#define STA350_TONE_BTC_SHIFT 0
+#define STA350_TONE_TTC_SHIFT 4
+
+/* 0x12 0x13 0x14 0x15 limiter attack/release */
+#define STA350_LxA_SHIFT 0
+#define STA350_LxR_SHIFT 4
+
+/* 0x26 CFUD */
+#define STA350_CFUD_W1 0x01
+#define STA350_CFUD_WA 0x02
+#define STA350_CFUD_R1 0x04
+#define STA350_CFUD_RA 0x08
+
+
+/* biquad filter coefficient table offsets */
+#define STA350_C1_BQ_BASE 0
+#define STA350_C2_BQ_BASE 20
+#define STA350_CH_BQ_NUM 4
+#define STA350_BQ_NUM_COEF 5
+#define STA350_XO_HP_BQ_BASE 40
+#define STA350_XO_LP_BQ_BASE 45
+#define STA350_C1_PRESCALE 50
+#define STA350_C2_PRESCALE 51
+#define STA350_C1_POSTSCALE 52
+#define STA350_C2_POSTSCALE 53
+#define STA350_C3_POSTSCALE 54
+#define STA350_TW_POSTSCALE 55
+#define STA350_C1_MIX1 56
+#define STA350_C1_MIX2 57
+#define STA350_C2_MIX1 58
+#define STA350_C2_MIX2 59
+#define STA350_C3_MIX1 60
+#define STA350_C3_MIX2 61
+
+/* miscellaneous register 1 */
+#define STA350_MISC1_CPWMEN BIT(2)
+#define STA350_MISC1_BRIDGOFF BIT(5)
+#define STA350_MISC1_NSHHPEN BIT(6)
+#define STA350_MISC1_RPDNEN BIT(7)
+
+/* miscellaneous register 2 */
+#define STA350_MISC2_PNDLSL_MASK 0x1c
+#define STA350_MISC2_PNDLSL_SHIFT 2
+
+#endif /* _ASOC_STA_350_H */
diff --git a/kernel/sound/soc/codecs/sta529.c b/kernel/sound/soc/codecs/sta529.c
new file mode 100644
index 000000000..b0f436d10
--- /dev/null
+++ b/kernel/sound/soc/codecs/sta529.c
@@ -0,0 +1,399 @@
+/*
+ * ASoC codec driver for spear platform
+ *
+ * sound/soc/codecs/sta529.c -- spear ALSA Soc codec driver
+ *
+ * Copyright (C) 2012 ST Microelectronics
+ * Rajeev Kumar <rajeevkumar.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm.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/tlv.h>
+
+/* STA529 Register offsets */
+#define STA529_FFXCFG0 0x00
+#define STA529_FFXCFG1 0x01
+#define STA529_MVOL 0x02
+#define STA529_LVOL 0x03
+#define STA529_RVOL 0x04
+#define STA529_TTF0 0x05
+#define STA529_TTF1 0x06
+#define STA529_TTP0 0x07
+#define STA529_TTP1 0x08
+#define STA529_S2PCFG0 0x0A
+#define STA529_S2PCFG1 0x0B
+#define STA529_P2SCFG0 0x0C
+#define STA529_P2SCFG1 0x0D
+#define STA529_PLLCFG0 0x14
+#define STA529_PLLCFG1 0x15
+#define STA529_PLLCFG2 0x16
+#define STA529_PLLCFG3 0x17
+#define STA529_PLLPFE 0x18
+#define STA529_PLLST 0x19
+#define STA529_ADCCFG 0x1E /*mic_select*/
+#define STA529_CKOCFG 0x1F
+#define STA529_MISC 0x20
+#define STA529_PADST0 0x21
+#define STA529_PADST1 0x22
+#define STA529_FFXST 0x23
+#define STA529_PWMIN1 0x2D
+#define STA529_PWMIN2 0x2E
+#define STA529_POWST 0x32
+
+#define STA529_MAX_REGISTER 0x32
+
+#define STA529_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define STA529_FORMAT (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define S2PC_VALUE 0x98
+#define CLOCK_OUT 0x60
+#define DATA_FORMAT_MSK 0x0E
+#define LEFT_J_DATA_FORMAT 0x00
+#define I2S_DATA_FORMAT 0x02
+#define RIGHT_J_DATA_FORMAT 0x04
+#define CODEC_MUTE_VAL 0x80
+
+#define POWER_CNTLMSAK 0x40
+#define POWER_STDBY 0x40
+#define FFX_MASK 0x80
+#define FFX_OFF 0x80
+#define POWER_UP 0x00
+#define FFX_CLK_ENB 0x01
+#define FFX_CLK_DIS 0x00
+#define FFX_CLK_MSK 0x01
+#define PLAY_FREQ_RANGE_MSK 0x70
+#define CAP_FREQ_RANGE_MSK 0x0C
+#define PDATA_LEN_MSK 0xC0
+#define BCLK_TO_FS_MSK 0x30
+#define AUDIO_MUTE_MSK 0x80
+
+static const struct reg_default sta529_reg_defaults[] = {
+ { 0, 0x35 }, /* R0 - FFX Configuration reg 0 */
+ { 1, 0xc8 }, /* R1 - FFX Configuration reg 1 */
+ { 2, 0x50 }, /* R2 - Master Volume */
+ { 3, 0x00 }, /* R3 - Left Volume */
+ { 4, 0x00 }, /* R4 - Right Volume */
+ { 10, 0xb2 }, /* R10 - S2P Config Reg 0 */
+ { 11, 0x41 }, /* R11 - S2P Config Reg 1 */
+ { 12, 0x92 }, /* R12 - P2S Config Reg 0 */
+ { 13, 0x41 }, /* R13 - P2S Config Reg 1 */
+ { 30, 0xd2 }, /* R30 - ADC Config Reg */
+ { 31, 0x40 }, /* R31 - clock Out Reg */
+ { 32, 0x21 }, /* R32 - Misc Register */
+};
+
+struct sta529 {
+ struct regmap *regmap;
+};
+
+static bool sta529_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+
+ case STA529_FFXCFG0:
+ case STA529_FFXCFG1:
+ case STA529_MVOL:
+ case STA529_LVOL:
+ case STA529_RVOL:
+ case STA529_S2PCFG0:
+ case STA529_S2PCFG1:
+ case STA529_P2SCFG0:
+ case STA529_P2SCFG1:
+ case STA529_ADCCFG:
+ case STA529_CKOCFG:
+ case STA529_MISC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+static const char *pwm_mode_text[] = { "Binary", "Headphone", "Ternary",
+ "Phase-shift"};
+
+static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -9150, 50, 0);
+static const DECLARE_TLV_DB_SCALE(master_vol_tlv, -12750, 50, 0);
+static SOC_ENUM_SINGLE_DECL(pwm_src, STA529_FFXCFG1, 4, pwm_mode_text);
+
+static const struct snd_kcontrol_new sta529_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Playback Volume", STA529_LVOL, STA529_RVOL, 0,
+ 127, 0, out_gain_tlv),
+ SOC_SINGLE_TLV("Master Playback Volume", STA529_MVOL, 0, 127, 1,
+ master_vol_tlv),
+ SOC_ENUM("PWM Select", pwm_src),
+};
+
+static int sta529_set_bias_level(struct snd_soc_codec *codec, enum
+ snd_soc_bias_level level)
+{
+ struct sta529 *sta529 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_update_bits(codec, STA529_FFXCFG0, POWER_CNTLMSAK,
+ POWER_UP);
+ snd_soc_update_bits(codec, STA529_MISC, FFX_CLK_MSK,
+ FFX_CLK_ENB);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ regcache_sync(sta529->regmap);
+ snd_soc_update_bits(codec, STA529_FFXCFG0,
+ POWER_CNTLMSAK, POWER_STDBY);
+ /* Making FFX output to zero */
+ snd_soc_update_bits(codec, STA529_FFXCFG0, FFX_MASK,
+ FFX_OFF);
+ snd_soc_update_bits(codec, 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;
+
+}
+
+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;
+ int pdata, play_freq_val, record_freq_val;
+ int bclk_to_fs_ratio;
+
+ switch (params_width(params)) {
+ case 16:
+ pdata = 1;
+ bclk_to_fs_ratio = 0;
+ break;
+ case 24:
+ pdata = 2;
+ bclk_to_fs_ratio = 1;
+ break;
+ case 32:
+ pdata = 3;
+ bclk_to_fs_ratio = 2;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported format\n");
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 11025:
+ play_freq_val = 0;
+ record_freq_val = 2;
+ break;
+ case 16000:
+ case 22050:
+ play_freq_val = 1;
+ record_freq_val = 0;
+ break;
+
+ case 32000:
+ case 44100:
+ case 48000:
+ play_freq_val = 2;
+ record_freq_val = 0;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported rate\n");
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_soc_update_bits(codec, STA529_S2PCFG1, PDATA_LEN_MSK,
+ pdata << 6);
+ snd_soc_update_bits(codec, STA529_S2PCFG1, BCLK_TO_FS_MSK,
+ bclk_to_fs_ratio << 4);
+ snd_soc_update_bits(codec, STA529_MISC, PLAY_FREQ_RANGE_MSK,
+ play_freq_val << 4);
+ } else {
+ snd_soc_update_bits(codec, STA529_P2SCFG1, PDATA_LEN_MSK,
+ pdata << 6);
+ snd_soc_update_bits(codec, STA529_P2SCFG1, BCLK_TO_FS_MSK,
+ bclk_to_fs_ratio << 4);
+ snd_soc_update_bits(codec, STA529_MISC, CAP_FREQ_RANGE_MSK,
+ record_freq_val << 2);
+ }
+
+ return 0;
+}
+
+static int sta529_mute(struct snd_soc_dai *dai, int mute)
+{
+ u8 val = 0;
+
+ if (mute)
+ val |= CODEC_MUTE_VAL;
+
+ snd_soc_update_bits(dai->codec, 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;
+ u8 mode = 0;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode = LEFT_J_DATA_FORMAT;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ mode = I2S_DATA_FORMAT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ mode = RIGHT_J_DATA_FORMAT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, STA529_S2PCFG0, DATA_FORMAT_MSK, mode);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sta529_dai_ops = {
+ .hw_params = sta529_hw_params,
+ .set_fmt = sta529_set_dai_fmt,
+ .digital_mute = sta529_mute,
+};
+
+static struct snd_soc_dai_driver sta529_dai = {
+ .name = "sta529-audio",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = STA529_RATES,
+ .formats = STA529_FORMAT,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = STA529_RATES,
+ .formats = STA529_FORMAT,
+ },
+ .ops = &sta529_dai_ops,
+};
+
+static const struct snd_soc_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 regmap_config sta529_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = STA529_MAX_REGISTER,
+ .readable_reg = sta529_readable,
+
+ .cache_type = REGCACHE_RBTREE,
+ .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)
+{
+ 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;
+
+ sta529->regmap = devm_regmap_init_i2c(i2c, &sta529_regmap);
+ if (IS_ERR(sta529->regmap)) {
+ ret = PTR_ERR(sta529->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, sta529);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &sta529_codec_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 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sta529_i2c_id);
+
+static struct i2c_driver sta529_i2c_driver = {
+ .driver = {
+ .name = "sta529",
+ .owner = THIS_MODULE,
+ },
+ .probe = sta529_i2c_probe,
+ .remove = sta529_i2c_remove,
+ .id_table = sta529_i2c_id,
+};
+
+module_i2c_driver(sta529_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC STA529 codec driver");
+MODULE_AUTHOR("Rajeev Kumar <rajeevkumar.linux@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/stac9766.c b/kernel/sound/soc/codecs/stac9766.c
new file mode 100644
index 000000000..6464caf72
--- /dev/null
+++ b/kernel/sound/soc/codecs/stac9766.c
@@ -0,0 +1,407 @@
+/*
+ * 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
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "stac9766.h"
+
+/*
+ * 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 char *stac9766_record_mux[] = {"Mic", "CD", "Video", "AUX",
+ "Line", "Stereo Mix", "Mono Mix", "Phone"};
+static const char *stac9766_mono_mux[] = {"Mix", "Mic"};
+static const char *stac9766_mic_mux[] = {"Mic1", "Mic2"};
+static const char *stac9766_SPDIF_mux[] = {"PCM", "ADC Record"};
+static const char *stac9766_popbypass_mux[] = {"Normal", "Bypass Mixer"};
+static const char *stac9766_record_all_mux[] = {"All analog",
+ "Analog plus DAC"};
+static const char *stac9766_boost1[] = {"0dB", "10dB"};
+static const char *stac9766_boost2[] = {"0dB", "20dB"};
+static const char *stac9766_stereo_mic[] = {"Off", "On"};
+
+static SOC_ENUM_DOUBLE_DECL(stac9766_record_enum,
+ AC97_REC_SEL, 8, 0, stac9766_record_mux);
+static SOC_ENUM_SINGLE_DECL(stac9766_mono_enum,
+ AC97_GENERAL_PURPOSE, 9, stac9766_mono_mux);
+static SOC_ENUM_SINGLE_DECL(stac9766_mic_enum,
+ AC97_GENERAL_PURPOSE, 8, stac9766_mic_mux);
+static SOC_ENUM_SINGLE_DECL(stac9766_SPDIF_enum,
+ AC97_STAC_DA_CONTROL, 1, stac9766_SPDIF_mux);
+static SOC_ENUM_SINGLE_DECL(stac9766_popbypass_enum,
+ AC97_GENERAL_PURPOSE, 15, stac9766_popbypass_mux);
+static SOC_ENUM_SINGLE_DECL(stac9766_record_all_enum,
+ AC97_STAC_ANALOG_SPECIAL, 12,
+ stac9766_record_all_mux);
+static SOC_ENUM_SINGLE_DECL(stac9766_boost1_enum,
+ AC97_MIC, 6, stac9766_boost1); /* 0/10dB */
+static SOC_ENUM_SINGLE_DECL(stac9766_boost2_enum,
+ AC97_STAC_ANALOG_SPECIAL, 2, stac9766_boost2); /* 0/20dB */
+static SOC_ENUM_SINGLE_DECL(stac9766_stereo_mic_enum,
+ AC97_STAC_STEREO_MIC, 2, stac9766_stereo_mic);
+
+static const 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 struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
+ SOC_DOUBLE_TLV("Speaker Volume", AC97_MASTER, 8, 0, 31, 1, master_tlv),
+ SOC_SINGLE("Speaker Switch", AC97_MASTER, 15, 1, 1),
+ SOC_DOUBLE_TLV("Headphone Volume", AC97_HEADPHONE, 8, 0, 31, 1,
+ master_tlv),
+ SOC_SINGLE("Headphone Switch", AC97_HEADPHONE, 15, 1, 1),
+ SOC_SINGLE_TLV("Mono Out Volume", AC97_MASTER_MONO, 0, 31, 1,
+ master_tlv),
+ SOC_SINGLE("Mono Out Switch", AC97_MASTER_MONO, 15, 1, 1),
+
+ SOC_DOUBLE_TLV("Record Volume", AC97_REC_GAIN, 8, 0, 15, 0, record_tlv),
+ SOC_SINGLE("Record Switch", AC97_REC_GAIN, 15, 1, 1),
+
+
+ SOC_SINGLE_TLV("Beep Volume", AC97_PC_BEEP, 1, 15, 1, beep_tlv),
+ SOC_SINGLE("Beep Switch", AC97_PC_BEEP, 15, 1, 1),
+ SOC_SINGLE("Beep Frequency", AC97_PC_BEEP, 5, 127, 1),
+ SOC_SINGLE_TLV("Phone Volume", AC97_PHONE, 0, 31, 1, mix_tlv),
+ SOC_SINGLE("Phone Switch", AC97_PHONE, 15, 1, 1),
+
+ SOC_ENUM("Mic Boost1", stac9766_boost1_enum),
+ SOC_ENUM("Mic Boost2", stac9766_boost2_enum),
+ SOC_SINGLE_TLV("Mic Volume", AC97_MIC, 0, 31, 1, mix_tlv),
+ SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1),
+ SOC_ENUM("Stereo Mic", stac9766_stereo_mic_enum),
+
+ SOC_DOUBLE_TLV("Line Volume", AC97_LINE, 8, 0, 31, 1, mix_tlv),
+ SOC_SINGLE("Line Switch", AC97_LINE, 15, 1, 1),
+ SOC_DOUBLE_TLV("CD Volume", AC97_CD, 8, 0, 31, 1, mix_tlv),
+ SOC_SINGLE("CD Switch", AC97_CD, 15, 1, 1),
+ SOC_DOUBLE_TLV("AUX Volume", AC97_AUX, 8, 0, 31, 1, mix_tlv),
+ SOC_SINGLE("AUX Switch", AC97_AUX, 15, 1, 1),
+ SOC_DOUBLE_TLV("Video Volume", AC97_VIDEO, 8, 0, 31, 1, mix_tlv),
+ SOC_SINGLE("Video Switch", AC97_VIDEO, 15, 1, 1),
+
+ SOC_DOUBLE_TLV("DAC Volume", AC97_PCM, 8, 0, 31, 1, mix_tlv),
+ SOC_SINGLE("DAC Switch", AC97_PCM, 15, 1, 1),
+ SOC_SINGLE("Loopback Test Switch", AC97_GENERAL_PURPOSE, 7, 1, 0),
+ SOC_SINGLE("3D Volume", AC97_3D_CONTROL, 3, 2, 1),
+ SOC_SINGLE("3D Switch", AC97_GENERAL_PURPOSE, 13, 1, 0),
+
+ SOC_ENUM("SPDIF Mux", stac9766_SPDIF_enum),
+ SOC_ENUM("Mic1/2 Mux", stac9766_mic_enum),
+ SOC_ENUM("Record All Mux", stac9766_record_all_enum),
+ SOC_ENUM("Record Mux", stac9766_record_enum),
+ SOC_ENUM("Mono Mux", stac9766_mono_enum),
+ SOC_ENUM("Pop Bypass Mux", stac9766_popbypass_enum),
+};
+
+static int 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_pcm_runtime *runtime = substream->runtime;
+ unsigned short reg, vra;
+
+ vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
+
+ vra |= 0x1; /* enable variable rate audio */
+ vra &= ~0x4; /* disable SPDIF output */
+
+ stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
+
+ 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);
+}
+
+static int ac97_digital_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned short reg, vra;
+
+ stac9766_ac97_write(codec, AC97_SPDIF, 0x2002);
+
+ vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
+ vra |= 0x5; /* Enable VRA and SPDIF out */
+
+ stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
+
+ reg = AC97_PCM_FRONT_DAC_RATE;
+
+ return stac9766_ac97_write(codec, reg, runtime->rate);
+}
+
+static int stac9766_set_bias_level(struct snd_soc_codec *codec,
+ 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);
+ break;
+ case SND_SOC_BIAS_OFF: /* Off, without power */
+ /* disable everything including AC link */
+ stac9766_ac97_write(codec, 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)
+{
+ 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;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops stac9766_dai_ops_analog = {
+ .prepare = ac97_analog_prepare,
+};
+
+static const struct snd_soc_dai_ops stac9766_dai_ops_digital = {
+ .prepare = ac97_digital_prepare,
+};
+
+static struct snd_soc_dai_driver stac9766_dai[] = {
+{
+ .name = "stac9766-hifi-analog",
+
+ /* stream cababilities */
+ .playback = {
+ .stream_name = "stac9766 analog",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SND_SOC_STD_AC97_FMTS,
+ },
+ .capture = {
+ .stream_name = "stac9766 analog",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SND_SOC_STD_AC97_FMTS,
+ },
+ /* alsa ops */
+ .ops = &stac9766_dai_ops_analog,
+},
+{
+ .name = "stac9766-hifi-IEC958",
+
+ /* stream cababilities */
+ .playback = {
+ .stream_name = "stac9766 IEC958",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE,
+ },
+ /* alsa ops */
+ .ops = &stac9766_dai_ops_digital,
+}
+};
+
+static int stac9766_codec_probe(struct snd_soc_codec *codec)
+{
+ struct snd_ac97 *ac97;
+ int ret = 0;
+
+ ac97 = snd_soc_new_ac97_codec(codec);
+ 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;
+ }
+
+ return 0;
+
+codec_err:
+ snd_soc_free_ac97_codec(ac97);
+ return ret;
+}
+
+static int stac9766_codec_remove(struct snd_soc_codec *codec)
+{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_free_ac97_codec(ac97);
+ return 0;
+}
+
+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 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;
+}
+
+static struct platform_driver stac9766_codec_driver = {
+ .driver = {
+ .name = "stac9766-codec",
+ },
+
+ .probe = stac9766_probe,
+ .remove = stac9766_remove,
+};
+
+module_platform_driver(stac9766_codec_driver);
+
+MODULE_DESCRIPTION("ASoC stac9766 driver");
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/stac9766.h b/kernel/sound/soc/codecs/stac9766.h
new file mode 100644
index 000000000..c726f907e
--- /dev/null
+++ b/kernel/sound/soc/codecs/stac9766.h
@@ -0,0 +1,17 @@
+/*
+ * 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/kernel/sound/soc/codecs/tas2552.c b/kernel/sound/soc/codecs/tas2552.c
new file mode 100644
index 000000000..dfb4ff5cc
--- /dev/null
+++ b/kernel/sound/soc/codecs/tas2552.c
@@ -0,0 +1,565 @@
+/*
+ * tas2552.c - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://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>
+
+#include <linux/gpio/consumer.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/tlv.h>
+#include <sound/tas2552-plat.h>
+
+#include "tas2552.h"
+
+static 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_LIMIT_RATE_HYS, 0x08},
+ {TAS2552_CFG_2, 0xef},
+ {TAS2552_SER_CTRL_1, 0x00},
+ {TAS2552_SER_CTRL_2, 0x00},
+ {TAS2552_PLL_CTRL_1, 0x10},
+ {TAS2552_PLL_CTRL_2, 0x00},
+ {TAS2552_PLL_CTRL_3, 0x00},
+ {TAS2552_BTIP, 0x8f},
+ {TAS2552_BTS_CTRL, 0x80},
+ {TAS2552_LIMIT_RELEASE, 0x04},
+ {TAS2552_LIMIT_INT_COUNT, 0x00},
+ {TAS2552_EDGE_RATE_CTRL, 0x40},
+ {TAS2552_VBAT_DATA, 0x00},
+};
+
+#define TAS2552_NUM_SUPPLIES 3
+static const char *tas2552_supply_names[TAS2552_NUM_SUPPLIES] = {
+ "vbat", /* vbat voltage */
+ "iovdd", /* I/O Voltage */
+ "avdd", /* Analog DAC Voltage */
+};
+
+struct tas2552_data {
+ struct snd_soc_codec *codec;
+ 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;
+};
+
+/* Input mux controls */
+static const char *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_soc_dapm_widget tas2552_dapm_widgets[] =
+{
+ SND_SOC_DAPM_INPUT("IN"),
+
+ /* MUX Controls */
+ SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0,
+ tas2552_input_mux_control),
+
+ SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("OUT")
+};
+
+static const struct snd_soc_dapm_route tas2552_audio_map[] = {
+ {"DAC", NULL, "DAC IN"},
+ {"Input selection", "Digital", "DAC"},
+ {"Input selection", "Analog", "IN"},
+ {"ClassD", NULL, "Input selection"},
+ {"OUT", NULL, "ClassD"},
+ {"ClassD", NULL, "PLL"},
+};
+
+#ifdef CONFIG_PM
+static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
+{
+ u8 cfg1_reg;
+
+ if (sw_shutdown)
+ cfg1_reg = 0;
+ else
+ cfg1_reg = TAS2552_SWS_MASK;
+
+ snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1,
+ TAS2552_SWS_MASK, cfg1_reg);
+}
+#endif
+
+static int tas2552_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 tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
+ int sample_rate, pll_clk;
+ int d;
+ u8 p, j;
+
+ if (!tas2552->mclk)
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
+
+ if (tas2552->mclk == TAS2552_245MHZ_CLK ||
+ tas2552->mclk == TAS2552_225MHZ_CLK) {
+ /* By pass the PLL configuration */
+ snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
+ TAS2552_PLL_BYPASS_MASK,
+ TAS2552_PLL_BYPASS);
+ } else {
+ /* Fill in the PLL control registers for J & D
+ * PLL_CLK = (.5 * freq * 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);
+ 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;
+ }
+
+ j = (pll_clk * 2 * (1 << p)) / tas2552->mclk;
+ d = (pll_clk * 2 * (1 << p)) % tas2552->mclk;
+
+ 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);
+
+ }
+
+ return 0;
+}
+
+static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 serial_format;
+ u8 serial_control_mask;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ serial_format = 0x00;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ serial_format = TAS2552_WORD_CLK_MASK;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ serial_format = TAS2552_BIT_CLK_MASK;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ serial_format = (TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK);
+ break;
+ default:
+ dev_vdbg(codec->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;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ serial_format |= TAS2552_DAIFMT_DSP;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ serial_format |= TAS2552_DAIFMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ serial_format |= TAS2552_DAIFMT_LEFT_J;
+ break;
+ default:
+ dev_vdbg(codec->dev, "DAI Format is not found\n");
+ return -EINVAL;
+ }
+
+ 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);
+
+ 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);
+
+ tas2552->mclk = freq;
+
+ return 0;
+}
+
+static int tas2552_mute(struct snd_soc_dai *dai, int mute)
+{
+ u8 cfg1_reg;
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute)
+ cfg1_reg = TAS2552_MUTE_MASK;
+ else
+ cfg1_reg = ~TAS2552_MUTE_MASK;
+
+ snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK, 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);
+
+ regcache_cache_only(tas2552->regmap, true);
+ regcache_mark_dirty(tas2552->regmap);
+
+ if (tas2552->enable_gpio)
+ gpiod_set_value(tas2552->enable_gpio, 0);
+
+ return 0;
+}
+
+static int tas2552_runtime_resume(struct device *dev)
+{
+ struct tas2552_data *tas2552 = dev_get_drvdata(dev);
+
+ if (tas2552->enable_gpio)
+ gpiod_set_value(tas2552->enable_gpio, 1);
+
+ tas2552_sw_shutdown(tas2552, 1);
+
+ regcache_cache_only(tas2552->regmap, false);
+ regcache_sync(tas2552->regmap);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops tas2552_pm = {
+ SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume,
+ NULL)
+};
+
+static struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
+ .hw_params = tas2552_hw_params,
+ .set_sysclk = tas2552_set_dai_sysclk,
+ .set_fmt = tas2552_set_dai_fmt,
+ .digital_mute = tas2552_mute,
+};
+
+/* Formats supported by TAS2552 driver. */
+#define TAS2552_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/* TAS2552 dai structure. */
+static struct snd_soc_dai_driver tas2552_dai[] = {
+ {
+ .name = "tas2552-amplifier",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = TAS2552_FORMATS,
+ },
+ .ops = &tas2552_speaker_dai_ops,
+ },
+};
+
+/*
+ * DAC digital volumes. From -7 to 24 dB in 1 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24);
+
+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 struct reg_default tas2552_init_regs[] = {
+ { TAS2552_RESERVED_0D, 0xc0 },
+};
+
+static int tas2552_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ tas2552->codec = codec;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
+ tas2552->supplies);
+
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (tas2552->enable_gpio)
+ gpiod_set_value(tas2552->enable_gpio, 1);
+
+ ret = pm_runtime_get_sync(codec->dev);
+ if (ret < 0) {
+ dev_err(codec->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_write(codec, 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);
+
+ regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
+ tas2552->supplies);
+ return -EIO;
+}
+
+static int tas2552_codec_remove(struct snd_soc_codec *codec)
+{
+ struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+
+ pm_runtime_put(codec->dev);
+
+ if (tas2552->enable_gpio)
+ gpiod_set_value(tas2552->enable_gpio, 0);
+
+ return 0;
+};
+
+#ifdef CONFIG_PM
+static int tas2552_suspend(struct snd_soc_codec *codec)
+{
+ struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ 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",
+ ret);
+ return 0;
+}
+
+static int tas2552_resume(struct snd_soc_codec *codec)
+{
+ struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ 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",
+ ret);
+ }
+
+ return 0;
+}
+#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 regmap_config tas2552_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = TAS2552_MAX_REG,
+ .reg_defaults = tas2552_reg_defs,
+ .num_reg_defaults = ARRAY_SIZE(tas2552_reg_defs),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int tas2552_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev;
+ struct tas2552_data *data;
+ int ret;
+ int i;
+
+ dev = &client->dev;
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(data->enable_gpio))
+ return PTR_ERR(data->enable_gpio);
+
+ data->tas2552_client = client;
+ data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ ret = PTR_ERR(data->regmap);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
+ data->supplies[i].supply = tas2552_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
+ data->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev, 1000);
+ pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_enable(&client->dev);
+ pm_runtime_mark_last_busy(&client->dev);
+ pm_runtime_put_sync_autosuspend(&client->dev);
+
+ dev_set_drvdata(&client->dev, data);
+
+ ret = snd_soc_register_codec(&client->dev,
+ &soc_codec_dev_tas2552,
+ tas2552_dai, ARRAY_SIZE(tas2552_dai));
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static int tas2552_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id tas2552_id[] = {
+ { "tas2552", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas2552_id);
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id tas2552_of_match[] = {
+ { .compatible = "ti,tas2552", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tas2552_of_match);
+#endif
+
+static struct i2c_driver tas2552_i2c_driver = {
+ .driver = {
+ .name = "tas2552",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tas2552_of_match),
+ .pm = &tas2552_pm,
+ },
+ .probe = tas2552_probe,
+ .remove = tas2552_i2c_remove,
+ .id_table = tas2552_id,
+};
+
+module_i2c_driver(tas2552_i2c_driver);
+
+MODULE_AUTHOR("Dan Muprhy <dmurphy@ti.com>");
+MODULE_DESCRIPTION("TAS2552 Audio amplifier driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/tas2552.h b/kernel/sound/soc/codecs/tas2552.h
new file mode 100644
index 000000000..6cea8f31b
--- /dev/null
+++ b/kernel/sound/soc/codecs/tas2552.h
@@ -0,0 +1,129 @@
+/*
+ * tas2552.h - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://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_CFG_1 0x01
+#define TAS2552_CFG_2 0x02
+#define TAS2552_CFG_3 0x03
+#define TAS2552_DOUT 0x04
+#define TAS2552_SER_CTRL_1 0x05
+#define TAS2552_SER_CTRL_2 0x06
+#define TAS2552_OUTPUT_DATA 0x07
+#define TAS2552_PLL_CTRL_1 0x08
+#define TAS2552_PLL_CTRL_2 0x09
+#define TAS2552_PLL_CTRL_3 0x0a
+#define TAS2552_BTIP 0x0b
+#define TAS2552_BTS_CTRL 0x0c
+#define TAS2552_RESERVED_0D 0x0d
+#define TAS2552_LIMIT_RATE_HYS 0x0e
+#define TAS2552_LIMIT_RELEASE 0x0f
+#define TAS2552_LIMIT_INT_COUNT 0x10
+#define TAS2552_PDM_CFG 0x11
+#define TAS2552_PGA_GAIN 0x12
+#define TAS2552_EDGE_RATE_CTRL 0x13
+#define TAS2552_BOOST_PT_CTRL 0x14
+#define TAS2552_VER_NUM 0x16
+#define TAS2552_VBAT_DATA 0x19
+#define TAS2552_MAX_REG 0x20
+
+/* 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)
+
+/* CFG2 Register Masks */
+#define TAS2552_CLASSD_EN (1 << 7)
+#define TAS2552_BOOST_EN (1 << 6)
+#define TAS2552_APT_EN (1 << 5)
+#define TAS2552_PLL_ENABLE (1 << 3)
+#define TAS2552_LIM_EN (1 << 2)
+#define TAS2552_IVSENSE_EN (1 << 1)
+
+/* CFG3 Register Masks */
+#define TAS2552_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_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)
+
+/* 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)
+
+/* 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)
+
+/* 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
+
+#endif
diff --git a/kernel/sound/soc/codecs/tas5086.c b/kernel/sound/soc/codecs/tas5086.c
new file mode 100644
index 000000000..32942bed3
--- /dev/null
+++ b/kernel/sound/soc/codecs/tas5086.c
@@ -0,0 +1,1009 @@
+/*
+ * 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
+ * - implement non-default PWM start
+ *
+ * Note that this chip has a very unusual register layout, specifically
+ * because the registers are of unequal size, and multi-byte registers
+ * require bulk writes to take effect. Regmap does not support that kind
+ * of devices.
+ *
+ * Currently, the driver does not touch any of the registers >= 0x20, so
+ * it doesn't matter because the entire map can be accessed as 8-bit
+ * array. In case more features will be added in the future
+ * that require access to higher registers, the entire regmap H/W I/O
+ * routines have to be open-coded.
+ */
+
+#include <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/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>
+#include <sound/tlv.h>
+#include <sound/tas5086.h>
+
+#define TAS5086_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define TAS5086_PCM_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000)
+
+/*
+ * TAS5086 registers
+ */
+#define TAS5086_CLOCK_CONTROL 0x00 /* Clock control register */
+#define TAS5086_CLOCK_RATE(val) (val << 5)
+#define TAS5086_CLOCK_RATE_MASK (0x7 << 5)
+#define TAS5086_CLOCK_RATIO(val) (val << 2)
+#define TAS5086_CLOCK_RATIO_MASK (0x7 << 2)
+#define TAS5086_CLOCK_SCLK_RATIO_48 (1 << 1)
+#define TAS5086_CLOCK_VALID (1 << 0)
+
+#define TAS5086_DEEMPH_MASK 0x03
+#define TAS5086_SOFT_MUTE_ALL 0x3f
+
+#define TAS5086_DEV_ID 0x01 /* Device ID register */
+#define TAS5086_ERROR_STATUS 0x02 /* Error status register */
+#define TAS5086_SYS_CONTROL_1 0x03 /* System control register 1 */
+#define TAS5086_SERIAL_DATA_IF 0x04 /* Serial data interface register */
+#define TAS5086_SYS_CONTROL_2 0x05 /* System control register 2 */
+#define TAS5086_SOFT_MUTE 0x06 /* Soft mute register */
+#define TAS5086_MASTER_VOL 0x07 /* Master volume */
+#define TAS5086_CHANNEL_VOL(X) (0x08 + (X)) /* Channel 1-6 volume */
+#define TAS5086_VOLUME_CONTROL 0x09 /* Volume control register */
+#define TAS5086_MOD_LIMIT 0x10 /* Modulation limit register */
+#define TAS5086_PWM_START 0x18 /* PWM start register */
+#define TAS5086_SURROUND 0x19 /* Surround register */
+#define TAS5086_SPLIT_CAP_CHARGE 0x1a /* Split cap charge period register */
+#define TAS5086_OSC_TRIM 0x1b /* Oscillator trim register */
+#define TAS5086_BKNDERR 0x1c
+#define TAS5086_INPUT_MUX 0x20
+#define TAS5086_PWM_OUTPUT_MUX 0x25
+
+#define TAS5086_MAX_REGISTER TAS5086_PWM_OUTPUT_MUX
+
+#define TAS5086_PWM_START_MIDZ_FOR_START_1 (1 << 7)
+#define TAS5086_PWM_START_MIDZ_FOR_START_2 (1 << 6)
+#define TAS5086_PWM_START_CHANNEL_MASK (0x3f)
+
+/*
+ * Default TAS5086 power-up configuration
+ */
+static const struct reg_default tas5086_reg_defaults[] = {
+ { 0x00, 0x6c },
+ { 0x01, 0x03 },
+ { 0x02, 0x00 },
+ { 0x03, 0xa0 },
+ { 0x04, 0x05 },
+ { 0x05, 0x60 },
+ { 0x06, 0x00 },
+ { 0x07, 0xff },
+ { 0x08, 0x30 },
+ { 0x09, 0x30 },
+ { 0x0a, 0x30 },
+ { 0x0b, 0x30 },
+ { 0x0c, 0x30 },
+ { 0x0d, 0x30 },
+ { 0x0e, 0xb1 },
+ { 0x0f, 0x00 },
+ { 0x10, 0x02 },
+ { 0x11, 0x00 },
+ { 0x12, 0x00 },
+ { 0x13, 0x00 },
+ { 0x14, 0x00 },
+ { 0x15, 0x00 },
+ { 0x16, 0x00 },
+ { 0x17, 0x00 },
+ { 0x18, 0x3f },
+ { 0x19, 0x00 },
+ { 0x1a, 0x18 },
+ { 0x1b, 0x82 },
+ { 0x1c, 0x05 },
+};
+
+static int tas5086_register_size(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS5086_CLOCK_CONTROL ... TAS5086_BKNDERR:
+ return 1;
+ case TAS5086_INPUT_MUX:
+ case TAS5086_PWM_OUTPUT_MUX:
+ return 4;
+ }
+
+ dev_err(dev, "Unsupported register address: %d\n", reg);
+ return 0;
+}
+
+static bool tas5086_accessible_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0f:
+ case 0x11 ... 0x17:
+ case 0x1d ... 0x1f:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool tas5086_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS5086_DEV_ID:
+ case TAS5086_ERROR_STATUS:
+ return true;
+ }
+
+ return false;
+}
+
+static bool tas5086_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return tas5086_accessible_reg(dev, reg) && (reg != TAS5086_DEV_ID);
+}
+
+static int tas5086_reg_write(void *context, unsigned int reg,
+ unsigned int value)
+{
+ struct i2c_client *client = context;
+ unsigned int i, size;
+ uint8_t buf[5];
+ int ret;
+
+ size = tas5086_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
+
+ buf[0] = reg;
+
+ for (i = size; i >= 1; --i) {
+ buf[i] = value;
+ value >>= 8;
+ }
+
+ ret = i2c_master_send(client, buf, size + 1);
+ if (ret == size + 1)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int tas5086_reg_read(void *context, unsigned int reg,
+ unsigned int *value)
+{
+ struct i2c_client *client = context;
+ uint8_t send_buf, recv_buf[4];
+ struct i2c_msg msgs[2];
+ unsigned int size;
+ unsigned int i;
+ int ret;
+
+ size = tas5086_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
+
+ send_buf = reg;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = &send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = size;
+ msgs[1].buf = recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+ else if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *value = 0;
+
+ for (i = 0; i < size; i++) {
+ *value <<= 8;
+ *value |= recv_buf[i];
+ }
+
+ return 0;
+}
+
+static const char * const supply_names[] = {
+ "dvdd", "avdd"
+};
+
+struct tas5086_private {
+ struct regmap *regmap;
+ unsigned int mclk, sclk;
+ unsigned int format;
+ bool deemph;
+ unsigned int charge_period;
+ unsigned int pwm_start_mid_z;
+ /* Current sample rate for de-emphasis control */
+ int rate;
+ /* GPIO driving Reset pin, if any */
+ int gpio_nreset;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+};
+
+static int tas5086_deemph[] = { 0, 32000, 44100, 48000 };
+
+static int tas5086_set_deemph(struct snd_soc_codec *codec)
+{
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ int i, val = 0;
+
+ if (priv->deemph)
+ for (i = 0; i < ARRAY_SIZE(tas5086_deemph); i++)
+ if (tas5086_deemph[i] == priv->rate)
+ val = i;
+
+ return regmap_update_bits(priv->regmap, TAS5086_SYS_CONTROL_1,
+ TAS5086_DEEMPH_MASK, val);
+}
+
+static int tas5086_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = priv->deemph;
+
+ return 0;
+}
+
+static int tas5086_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->deemph = ucontrol->value.integer.value[0];
+
+ return tas5086_set_deemph(codec);
+}
+
+
+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);
+
+ switch (clk_id) {
+ case TAS5086_CLK_IDX_MCLK:
+ priv->mclk = freq;
+ break;
+ case TAS5086_CLK_IDX_SCLK:
+ priv->sclk = freq;
+ break;
+ }
+
+ return 0;
+}
+
+static int tas5086_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ /* 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");
+ return -EINVAL;
+ }
+
+ /* we need to refer to the data format from hw_params() */
+ priv->format = format;
+
+ return 0;
+}
+
+static const int tas5086_sample_rates[] = {
+ 32000, 38000, 44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const int tas5086_ratios[] = {
+ 64, 128, 192, 256, 384, 512
+};
+
+static int index_in_array(const int *array, int len, int needle)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (array[i] == needle)
+ return i;
+
+ return -ENOENT;
+}
+
+static int tas5086_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ int val;
+ int ret;
+
+ priv->rate = params_rate(params);
+
+ /* Look up the sample rate and refer to the offset in the list */
+ val = index_in_array(tas5086_sample_rates,
+ ARRAY_SIZE(tas5086_sample_rates), priv->rate);
+
+ if (val < 0) {
+ dev_err(codec->dev, "Invalid sample rate\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL,
+ TAS5086_CLOCK_RATE_MASK,
+ TAS5086_CLOCK_RATE(val));
+ if (ret < 0)
+ return ret;
+
+ /* MCLK / Fs ratio */
+ val = index_in_array(tas5086_ratios, ARRAY_SIZE(tas5086_ratios),
+ priv->mclk / priv->rate);
+ if (val < 0) {
+ dev_err(codec->dev, "Inavlid MCLK / Fs ratio\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL,
+ TAS5086_CLOCK_RATIO_MASK,
+ TAS5086_CLOCK_RATIO(val));
+ if (ret < 0)
+ return ret;
+
+
+ ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL,
+ TAS5086_CLOCK_SCLK_RATIO_48,
+ (priv->sclk == 48 * priv->rate) ?
+ TAS5086_CLOCK_SCLK_RATIO_48 : 0);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The chip has a very unituitive register mapping and muxes information
+ * about data format and sample depth into the same register, but not on
+ * a logical bit-boundary. Hence, we have to refer to the format passed
+ * in the set_dai_fmt() callback and set up everything from here.
+ *
+ * First, determine the 'base' value, using the format ...
+ */
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = 0x00;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = 0x03;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = 0x06;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ /* ... then add the offset for the sample bit depth. */
+ switch (params_width(params)) {
+ case 16:
+ val += 0;
+ break;
+ case 20:
+ val += 1;
+ break;
+ case 24:
+ val += 2;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid bit width\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_write(priv->regmap, TAS5086_SERIAL_DATA_IF, val);
+ if (ret < 0)
+ return ret;
+
+ /* clock is considered valid now */
+ ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL,
+ TAS5086_CLOCK_VALID, TAS5086_CLOCK_VALID);
+ if (ret < 0)
+ return ret;
+
+ return tas5086_set_deemph(codec);
+}
+
+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);
+ unsigned int val = 0;
+
+ if (mute)
+ val = TAS5086_SOFT_MUTE_ALL;
+
+ return regmap_write(priv->regmap, TAS5086_SOFT_MUTE, val);
+}
+
+static void tas5086_reset(struct tas5086_private *priv)
+{
+ if (gpio_is_valid(priv->gpio_nreset)) {
+ /* Reset codec - minimum assertion time is 400ns */
+ gpio_direction_output(priv->gpio_nreset, 0);
+ udelay(1);
+ gpio_set_value(priv->gpio_nreset, 1);
+
+ /* Codec needs ~15ms to wake up */
+ msleep(15);
+ }
+}
+
+/* charge period values in microseconds */
+static const int tas5086_charge_period[] = {
+ 13000, 16900, 23400, 31200, 41600, 54600, 72800, 96200,
+ 130000, 156000, 234000, 312000, 416000, 546000, 728000, 962000,
+ 1300000, 169000, 2340000, 3120000, 4160000, 5460000, 7280000, 9620000,
+};
+
+static int tas5086_init(struct device *dev, struct tas5086_private *priv)
+{
+ int ret, i;
+
+ /*
+ * If any of the channels is configured to start in Mid-Z mode,
+ * configure 'part 1' of the PWM starts to use Mid-Z, and tell
+ * all configured mid-z channels to start start under 'part 1'.
+ */
+ if (priv->pwm_start_mid_z)
+ regmap_write(priv->regmap, TAS5086_PWM_START,
+ TAS5086_PWM_START_MIDZ_FOR_START_1 |
+ priv->pwm_start_mid_z);
+
+ /* lookup and set split-capacitor charge period */
+ if (priv->charge_period == 0) {
+ regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0);
+ } else {
+ i = index_in_array(tas5086_charge_period,
+ ARRAY_SIZE(tas5086_charge_period),
+ priv->charge_period);
+ if (i >= 0)
+ regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE,
+ i + 0x08);
+ else
+ dev_warn(dev,
+ "Invalid split-cap charge period of %d ns.\n",
+ priv->charge_period);
+ }
+
+ /* enable factory trim */
+ ret = regmap_write(priv->regmap, TAS5086_OSC_TRIM, 0x00);
+ if (ret < 0)
+ return ret;
+
+ /* start all channels */
+ ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x20);
+ if (ret < 0)
+ return ret;
+
+ /* mute all channels for now */
+ ret = regmap_write(priv->regmap, TAS5086_SOFT_MUTE,
+ TAS5086_SOFT_MUTE_ALL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* TAS5086 controls */
+static const DECLARE_TLV_DB_SCALE(tas5086_dac_tlv, -10350, 50, 1);
+
+static const struct snd_kcontrol_new tas5086_controls[] = {
+ SOC_SINGLE_TLV("Master Playback Volume", TAS5086_MASTER_VOL,
+ 0, 0xff, 1, tas5086_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 1/2 Playback Volume",
+ TAS5086_CHANNEL_VOL(0), TAS5086_CHANNEL_VOL(1),
+ 0, 0xff, 1, tas5086_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 3/4 Playback Volume",
+ TAS5086_CHANNEL_VOL(2), TAS5086_CHANNEL_VOL(3),
+ 0, 0xff, 1, tas5086_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 5/6 Playback Volume",
+ TAS5086_CHANNEL_VOL(4), TAS5086_CHANNEL_VOL(5),
+ 0, 0xff, 1, tas5086_dac_tlv),
+ SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
+ tas5086_get_deemph, tas5086_put_deemph),
+};
+
+/* Input mux controls */
+static const char *tas5086_dapm_sdin_texts[] =
+{
+ "SDIN1-L", "SDIN1-R", "SDIN2-L", "SDIN2-R",
+ "SDIN3-L", "SDIN3-R", "Ground (0)", "nc"
+};
+
+static const struct soc_enum tas5086_dapm_input_mux_enum[] = {
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 20, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 16, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 12, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 8, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 4, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 0, 8, tas5086_dapm_sdin_texts),
+};
+
+static const struct snd_kcontrol_new tas5086_dapm_input_mux_controls[] = {
+ SOC_DAPM_ENUM("Channel 1 input", tas5086_dapm_input_mux_enum[0]),
+ SOC_DAPM_ENUM("Channel 2 input", tas5086_dapm_input_mux_enum[1]),
+ SOC_DAPM_ENUM("Channel 3 input", tas5086_dapm_input_mux_enum[2]),
+ SOC_DAPM_ENUM("Channel 4 input", tas5086_dapm_input_mux_enum[3]),
+ SOC_DAPM_ENUM("Channel 5 input", tas5086_dapm_input_mux_enum[4]),
+ SOC_DAPM_ENUM("Channel 6 input", tas5086_dapm_input_mux_enum[5]),
+};
+
+/* Output mux controls */
+static const char *tas5086_dapm_channel_texts[] =
+ { "Channel 1 Mux", "Channel 2 Mux", "Channel 3 Mux",
+ "Channel 4 Mux", "Channel 5 Mux", "Channel 6 Mux" };
+
+static const struct soc_enum tas5086_dapm_output_mux_enum[] = {
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 20, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 16, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 12, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 8, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 4, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 0, 6, tas5086_dapm_channel_texts),
+};
+
+static const struct snd_kcontrol_new tas5086_dapm_output_mux_controls[] = {
+ SOC_DAPM_ENUM("PWM1 Output", tas5086_dapm_output_mux_enum[0]),
+ SOC_DAPM_ENUM("PWM2 Output", tas5086_dapm_output_mux_enum[1]),
+ SOC_DAPM_ENUM("PWM3 Output", tas5086_dapm_output_mux_enum[2]),
+ SOC_DAPM_ENUM("PWM4 Output", tas5086_dapm_output_mux_enum[3]),
+ SOC_DAPM_ENUM("PWM5 Output", tas5086_dapm_output_mux_enum[4]),
+ SOC_DAPM_ENUM("PWM6 Output", tas5086_dapm_output_mux_enum[5]),
+};
+
+static const struct snd_soc_dapm_widget tas5086_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("SDIN1-L"),
+ SND_SOC_DAPM_INPUT("SDIN1-R"),
+ SND_SOC_DAPM_INPUT("SDIN2-L"),
+ SND_SOC_DAPM_INPUT("SDIN2-R"),
+ SND_SOC_DAPM_INPUT("SDIN3-L"),
+ SND_SOC_DAPM_INPUT("SDIN3-R"),
+ SND_SOC_DAPM_INPUT("SDIN4-L"),
+ SND_SOC_DAPM_INPUT("SDIN4-R"),
+
+ SND_SOC_DAPM_OUTPUT("PWM1"),
+ SND_SOC_DAPM_OUTPUT("PWM2"),
+ SND_SOC_DAPM_OUTPUT("PWM3"),
+ SND_SOC_DAPM_OUTPUT("PWM4"),
+ SND_SOC_DAPM_OUTPUT("PWM5"),
+ SND_SOC_DAPM_OUTPUT("PWM6"),
+
+ SND_SOC_DAPM_MUX("Channel 1 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[0]),
+ SND_SOC_DAPM_MUX("Channel 2 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[1]),
+ SND_SOC_DAPM_MUX("Channel 3 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[2]),
+ SND_SOC_DAPM_MUX("Channel 4 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[3]),
+ SND_SOC_DAPM_MUX("Channel 5 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[4]),
+ SND_SOC_DAPM_MUX("Channel 6 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[5]),
+
+ SND_SOC_DAPM_MUX("PWM1 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[0]),
+ SND_SOC_DAPM_MUX("PWM2 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[1]),
+ SND_SOC_DAPM_MUX("PWM3 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[2]),
+ SND_SOC_DAPM_MUX("PWM4 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[3]),
+ SND_SOC_DAPM_MUX("PWM5 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[4]),
+ SND_SOC_DAPM_MUX("PWM6 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[5]),
+};
+
+static const struct snd_soc_dapm_route tas5086_dapm_routes[] = {
+ /* SDIN inputs -> channel muxes */
+ { "Channel 1 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 1 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 1 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 1 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 1 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 1 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 2 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 2 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 2 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 2 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 2 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 2 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 2 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 2 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 2 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 2 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 2 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 2 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 3 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 3 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 3 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 3 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 3 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 3 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 4 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 4 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 4 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 4 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 4 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 4 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 5 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 5 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 5 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 5 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 5 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 5 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 6 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 6 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 6 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 6 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 6 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 6 Mux", "SDIN3-R", "SDIN3-R" },
+
+ /* Channel muxes -> PWM muxes */
+ { "PWM1 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM2 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM3 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM4 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM5 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM6 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+
+ { "PWM1 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM2 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM3 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM4 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM5 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM6 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+
+ { "PWM1 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM2 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM3 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM4 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM5 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM6 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+
+ { "PWM1 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM2 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM3 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM4 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM5 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM6 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+
+ { "PWM1 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM2 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM3 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM4 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM5 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM6 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+
+ { "PWM1 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM2 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM3 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM4 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM5 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM6 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+
+ /* The PWM muxes are directly connected to the PWM outputs */
+ { "PWM1", NULL, "PWM1 Mux" },
+ { "PWM2", NULL, "PWM2 Mux" },
+ { "PWM3", NULL, "PWM3 Mux" },
+ { "PWM4", NULL, "PWM4 Mux" },
+ { "PWM5", NULL, "PWM5 Mux" },
+ { "PWM6", NULL, "PWM6 Mux" },
+
+};
+
+static const struct snd_soc_dai_ops tas5086_dai_ops = {
+ .hw_params = tas5086_hw_params,
+ .set_sysclk = tas5086_set_dai_sysclk,
+ .set_fmt = tas5086_set_dai_fmt,
+ .mute_stream = tas5086_mute_stream,
+};
+
+static struct snd_soc_dai_driver tas5086_dai = {
+ .name = "tas5086-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 6,
+ .rates = TAS5086_PCM_RATES,
+ .formats = TAS5086_PCM_FORMATS,
+ },
+ .ops = &tas5086_dai_ops,
+};
+
+#ifdef CONFIG_PM
+static int tas5086_soc_suspend(struct snd_soc_codec *codec)
+{
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ /* Shut down all channels */
+ ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x60);
+ if (ret < 0)
+ return ret;
+
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+
+ return 0;
+}
+
+static int tas5086_soc_resume(struct snd_soc_codec *codec)
+{
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+ if (ret < 0)
+ return ret;
+
+ tas5086_reset(priv);
+ regcache_mark_dirty(priv->regmap);
+
+ ret = tas5086_init(codec->dev, priv);
+ if (ret < 0)
+ return ret;
+
+ ret = regcache_sync(priv->regmap);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+#else
+#define tas5086_soc_suspend NULL
+#define tas5086_soc_resume NULL
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_OF
+static const struct of_device_id tas5086_dt_ids[] = {
+ { .compatible = "ti,tas5086", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tas5086_dt_ids);
+#endif
+
+static int tas5086_probe(struct snd_soc_codec *codec)
+{
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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);
+ 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;
+
+ of_property_read_u32(of_node, "ti,charge-period",
+ &priv->charge_period);
+
+ for (i = 0; i < 6; i++) {
+ char name[25];
+
+ snprintf(name, sizeof(name),
+ "ti,mid-z-channel-%d", i + 1);
+
+ if (of_get_property(of_node, name, NULL) != NULL)
+ priv->pwm_start_mid_z |= 1 << i;
+ }
+ }
+
+ tas5086_reset(priv);
+ ret = tas5086_init(codec->dev, priv);
+ if (ret < 0)
+ goto exit_disable_regulators;
+
+ /* set master volume to 0 dB */
+ ret = regmap_write(priv->regmap, TAS5086_MASTER_VOL, 0x30);
+ if (ret < 0)
+ goto exit_disable_regulators;
+
+ return 0;
+
+exit_disable_regulators:
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+
+ return ret;
+}
+
+static int tas5086_remove(struct snd_soc_codec *codec)
+{
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (gpio_is_valid(priv->gpio_nreset))
+ /* Set codec to the reset state */
+ gpio_set_value(priv->gpio_nreset, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+
+ return 0;
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_tas5086 = {
+ .probe = tas5086_probe,
+ .remove = tas5086_remove,
+ .suspend = tas5086_soc_suspend,
+ .resume = tas5086_soc_resume,
+ .controls = tas5086_controls,
+ .num_controls = ARRAY_SIZE(tas5086_controls),
+ .dapm_widgets = tas5086_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas5086_dapm_widgets),
+ .dapm_routes = tas5086_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tas5086_dapm_routes),
+};
+
+static const struct i2c_device_id tas5086_i2c_id[] = {
+ { "tas5086", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas5086_i2c_id);
+
+static const struct regmap_config tas5086_regmap = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = TAS5086_MAX_REGISTER,
+ .reg_defaults = tas5086_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas5086_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = tas5086_volatile_reg,
+ .writeable_reg = tas5086_writeable_reg,
+ .readable_reg = tas5086_accessible_reg,
+ .reg_read = tas5086_reg_read,
+ .reg_write = tas5086_reg_write,
+};
+
+static int tas5086_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct tas5086_private *priv;
+ struct device *dev = &i2c->dev;
+ int gpio_nreset = -EINVAL;
+ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ priv->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get regulators: %d\n", ret);
+ return ret;
+ }
+
+ priv->regmap = devm_regmap_init(dev, NULL, i2c, &tas5086_regmap);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, priv);
+
+ if (of_match_device(of_match_ptr(tas5086_dt_ids), dev)) {
+ struct device_node *of_node = dev->of_node;
+ gpio_nreset = of_get_named_gpio(of_node, "reset-gpio", 0);
+ }
+
+ if (gpio_is_valid(gpio_nreset))
+ if (devm_gpio_request(dev, gpio_nreset, "TAS5086 Reset"))
+ gpio_nreset = -EINVAL;
+
+ priv->gpio_nreset = gpio_nreset;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ tas5086_reset(priv);
+
+ /* The TAS5086 always returns 0x03 in its TAS5086_DEV_ID register */
+ ret = regmap_read(priv->regmap, TAS5086_DEV_ID, &i);
+ if (ret == 0 && i != 0x3) {
+ dev_err(dev,
+ "Failed to identify TAS5086 codec (got %02x)\n", i);
+ ret = -ENODEV;
+ }
+
+ /*
+ * The chip has been identified, so we can turn off the power
+ * again until the dai link is set up.
+ */
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+
+ if (ret == 0)
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_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 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,
+ .probe = tas5086_i2c_probe,
+ .remove = tas5086_i2c_remove,
+};
+
+module_i2c_driver(tas5086_i2c_driver);
+
+MODULE_AUTHOR("Daniel Mack <zonque@gmail.com>");
+MODULE_DESCRIPTION("Texas Instruments TAS5086 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/tfa9879.c b/kernel/sound/soc/codecs/tfa9879.c
new file mode 100644
index 000000000..aab0af681
--- /dev/null
+++ b/kernel/sound/soc/codecs/tfa9879.c
@@ -0,0 +1,328 @@
+/*
+ * tfa9879.c -- driver for NXP Semiconductors TFA9879
+ *
+ * Copyright (C) 2014 Axentia Technologies AB
+ * Author: Peter Rosin <peda@axentia.se>
+ *
+ * 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>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+
+#include "tfa9879.h"
+
+struct tfa9879_priv {
+ struct regmap *regmap;
+ int lsb_justified;
+};
+
+static int tfa9879_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tfa9879_priv *tfa9879 = snd_soc_codec_get_drvdata(codec);
+ int fs;
+ int i2s_set = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs = TFA9879_I2S_FS_8000;
+ break;
+ case 11025:
+ fs = TFA9879_I2S_FS_11025;
+ break;
+ case 12000:
+ fs = TFA9879_I2S_FS_12000;
+ break;
+ case 16000:
+ fs = TFA9879_I2S_FS_16000;
+ break;
+ case 22050:
+ fs = TFA9879_I2S_FS_22050;
+ break;
+ case 24000:
+ fs = TFA9879_I2S_FS_24000;
+ break;
+ case 32000:
+ fs = TFA9879_I2S_FS_32000;
+ break;
+ case 44100:
+ fs = TFA9879_I2S_FS_44100;
+ break;
+ case 48000:
+ fs = TFA9879_I2S_FS_48000;
+ break;
+ case 64000:
+ fs = TFA9879_I2S_FS_64000;
+ break;
+ case 88200:
+ fs = TFA9879_I2S_FS_88200;
+ break;
+ case 96000:
+ fs = TFA9879_I2S_FS_96000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ i2s_set = TFA9879_I2S_SET_LSB_J_16;
+ break;
+ case 24:
+ i2s_set = TFA9879_I2S_SET_LSB_J_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (tfa9879->lsb_justified)
+ snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1,
+ TFA9879_I2S_SET_MASK,
+ i2s_set << TFA9879_I2S_SET_SHIFT);
+
+ snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1,
+ TFA9879_I2S_FS_MASK,
+ fs << TFA9879_I2S_FS_SHIFT);
+ return 0;
+}
+
+static int tfa9879_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ snd_soc_update_bits(codec, TFA9879_MISC_CONTROL,
+ TFA9879_S_MUTE_MASK,
+ !!mute << TFA9879_S_MUTE_SHIFT);
+
+ return 0;
+}
+
+static int tfa9879_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tfa9879_priv *tfa9879 = snd_soc_codec_get_drvdata(codec);
+ int i2s_set;
+ int sck_pol;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ sck_pol = TFA9879_SCK_POL_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ sck_pol = TFA9879_SCK_POL_INVERSE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ tfa9879->lsb_justified = 0;
+ i2s_set = TFA9879_I2S_SET_I2S_24;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ tfa9879->lsb_justified = 0;
+ i2s_set = TFA9879_I2S_SET_MSB_J_24;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ tfa9879->lsb_justified = 1;
+ i2s_set = TFA9879_I2S_SET_LSB_J_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1,
+ TFA9879_SCK_POL_MASK,
+ sck_pol << TFA9879_SCK_POL_SHIFT);
+ snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1,
+ TFA9879_I2S_SET_MASK,
+ i2s_set << TFA9879_I2S_SET_SHIFT);
+ return 0;
+}
+
+static struct reg_default tfa9879_regs[] = {
+ { TFA9879_DEVICE_CONTROL, 0x0000 }, /* 0x00 */
+ { TFA9879_SERIAL_INTERFACE_1, 0x0a18 }, /* 0x01 */
+ { TFA9879_PCM_IOM2_FORMAT_1, 0x0007 }, /* 0x02 */
+ { TFA9879_SERIAL_INTERFACE_2, 0x0a18 }, /* 0x03 */
+ { TFA9879_PCM_IOM2_FORMAT_2, 0x0007 }, /* 0x04 */
+ { TFA9879_EQUALIZER_A1, 0x59dd }, /* 0x05 */
+ { TFA9879_EQUALIZER_A2, 0xc63e }, /* 0x06 */
+ { TFA9879_EQUALIZER_B1, 0x651a }, /* 0x07 */
+ { TFA9879_EQUALIZER_B2, 0xe53e }, /* 0x08 */
+ { TFA9879_EQUALIZER_C1, 0x4616 }, /* 0x09 */
+ { TFA9879_EQUALIZER_C2, 0xd33e }, /* 0x0a */
+ { TFA9879_EQUALIZER_D1, 0x4df3 }, /* 0x0b */
+ { TFA9879_EQUALIZER_D2, 0xea3e }, /* 0x0c */
+ { TFA9879_EQUALIZER_E1, 0x5ee0 }, /* 0x0d */
+ { TFA9879_EQUALIZER_E2, 0xf93e }, /* 0x0e */
+ { TFA9879_BYPASS_CONTROL, 0x0093 }, /* 0x0f */
+ { TFA9879_DYNAMIC_RANGE_COMPR, 0x92ba }, /* 0x10 */
+ { TFA9879_BASS_TREBLE, 0x12a5 }, /* 0x11 */
+ { TFA9879_HIGH_PASS_FILTER, 0x0004 }, /* 0x12 */
+ { TFA9879_VOLUME_CONTROL, 0x10bd }, /* 0x13 */
+ { TFA9879_MISC_CONTROL, 0x0000 }, /* 0x14 */
+};
+
+static bool tfa9879_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg == TFA9879_MISC_STATUS;
+}
+
+static const DECLARE_TLV_DB_SCALE(volume_tlv, -7050, 50, 1);
+static const DECLARE_TLV_DB_SCALE(tb_gain_tlv, -1800, 200, 0);
+static const char * const tb_freq_text[] = {
+ "Low", "Mid", "High"
+};
+static const struct soc_enum treble_freq_enum =
+ SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_TRBLE_SHIFT,
+ ARRAY_SIZE(tb_freq_text), tb_freq_text);
+static const struct soc_enum bass_freq_enum =
+ SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_BASS_SHIFT,
+ ARRAY_SIZE(tb_freq_text), tb_freq_text);
+
+static const struct snd_kcontrol_new tfa9879_controls[] = {
+ SOC_SINGLE_TLV("PCM Playback Volume", TFA9879_VOLUME_CONTROL,
+ TFA9879_VOL_SHIFT, 0xbd, 1, volume_tlv),
+ SOC_SINGLE_TLV("Treble Volume", TFA9879_BASS_TREBLE,
+ TFA9879_G_TRBLE_SHIFT, 18, 0, tb_gain_tlv),
+ SOC_SINGLE_TLV("Bass Volume", TFA9879_BASS_TREBLE,
+ TFA9879_G_BASS_SHIFT, 18, 0, tb_gain_tlv),
+ SOC_ENUM("Treble Corner Freq", treble_freq_enum),
+ SOC_ENUM("Bass Corner Freq", bass_freq_enum),
+};
+
+static const struct snd_soc_dapm_widget tfa9879_dapm_widgets[] = {
+SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_DAC("DAC", NULL, TFA9879_DEVICE_CONTROL, TFA9879_OPMODE_SHIFT, 0),
+SND_SOC_DAPM_OUTPUT("LINEOUT"),
+SND_SOC_DAPM_SUPPLY("POWER", TFA9879_DEVICE_CONTROL, TFA9879_POWERUP_SHIFT, 0,
+ NULL, 0),
+};
+
+static const struct snd_soc_dapm_route tfa9879_dapm_routes[] = {
+ { "DAC", NULL, "AIFINL" },
+ { "DAC", NULL, "AIFINR" },
+
+ { "LINEOUT", NULL, "DAC" },
+
+ { "DAC", NULL, "POWER" },
+};
+
+static const struct snd_soc_codec_driver tfa9879_codec = {
+ .controls = tfa9879_controls,
+ .num_controls = ARRAY_SIZE(tfa9879_controls),
+
+ .dapm_widgets = tfa9879_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tfa9879_dapm_widgets),
+ .dapm_routes = tfa9879_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tfa9879_dapm_routes),
+};
+
+static const struct regmap_config tfa9879_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .volatile_reg = tfa9879_volatile_reg,
+ .max_register = TFA9879_MISC_STATUS,
+ .reg_defaults = tfa9879_regs,
+ .num_reg_defaults = ARRAY_SIZE(tfa9879_regs),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct snd_soc_dai_ops tfa9879_dai_ops = {
+ .hw_params = tfa9879_hw_params,
+ .digital_mute = tfa9879_digital_mute,
+ .set_fmt = tfa9879_set_fmt,
+};
+
+#define TFA9879_RATES SNDRV_PCM_RATE_8000_96000
+
+#define TFA9879_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver tfa9879_dai = {
+ .name = "tfa9879-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = TFA9879_RATES,
+ .formats = TFA9879_FORMATS, },
+ .ops = &tfa9879_dai_ops,
+};
+
+static int tfa9879_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct tfa9879_priv *tfa9879;
+ int i;
+
+ tfa9879 = devm_kzalloc(&i2c->dev, sizeof(*tfa9879), GFP_KERNEL);
+ if (!tfa9879)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, tfa9879);
+
+ tfa9879->regmap = devm_regmap_init_i2c(i2c, &tfa9879_regmap);
+ if (IS_ERR(tfa9879->regmap))
+ return PTR_ERR(tfa9879->regmap);
+
+ /* Ensure the device is in reset state */
+ for (i = 0; i < ARRAY_SIZE(tfa9879_regs); i++)
+ regmap_write(tfa9879->regmap,
+ tfa9879_regs[i].reg, tfa9879_regs[i].def);
+
+ return snd_soc_register_codec(&i2c->dev, &tfa9879_codec,
+ &tfa9879_dai, 1);
+}
+
+static int tfa9879_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id tfa9879_i2c_id[] = {
+ { "tfa9879", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tfa9879_i2c_id);
+
+static struct i2c_driver tfa9879_i2c_driver = {
+ .driver = {
+ .name = "tfa9879",
+ .owner = THIS_MODULE,
+ },
+ .probe = tfa9879_i2c_probe,
+ .remove = tfa9879_i2c_remove,
+ .id_table = tfa9879_i2c_id,
+};
+
+module_i2c_driver(tfa9879_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NXP Semiconductors TFA9879 driver");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/tfa9879.h b/kernel/sound/soc/codecs/tfa9879.h
new file mode 100644
index 000000000..3408c90c4
--- /dev/null
+++ b/kernel/sound/soc/codecs/tfa9879.h
@@ -0,0 +1,202 @@
+/*
+ * tfa9879.h -- driver for NXP Semiconductors TFA9879
+ *
+ * Copyright (C) 2014 Axentia Technologies AB
+ * Author: Peter Rosin <peda@axentia.se>
+ *
+ * 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 _TFA9879_H
+#define _TFA9879_H
+
+#define TFA9879_DEVICE_CONTROL 0x00
+#define TFA9879_SERIAL_INTERFACE_1 0x01
+#define TFA9879_PCM_IOM2_FORMAT_1 0x02
+#define TFA9879_SERIAL_INTERFACE_2 0x03
+#define TFA9879_PCM_IOM2_FORMAT_2 0x04
+#define TFA9879_EQUALIZER_A1 0x05
+#define TFA9879_EQUALIZER_A2 0x06
+#define TFA9879_EQUALIZER_B1 0x07
+#define TFA9879_EQUALIZER_B2 0x08
+#define TFA9879_EQUALIZER_C1 0x09
+#define TFA9879_EQUALIZER_C2 0x0a
+#define TFA9879_EQUALIZER_D1 0x0b
+#define TFA9879_EQUALIZER_D2 0x0c
+#define TFA9879_EQUALIZER_E1 0x0d
+#define TFA9879_EQUALIZER_E2 0x0e
+#define TFA9879_BYPASS_CONTROL 0x0f
+#define TFA9879_DYNAMIC_RANGE_COMPR 0x10
+#define TFA9879_BASS_TREBLE 0x11
+#define TFA9879_HIGH_PASS_FILTER 0x12
+#define TFA9879_VOLUME_CONTROL 0x13
+#define TFA9879_MISC_CONTROL 0x14
+#define TFA9879_MISC_STATUS 0x15
+
+/* TFA9879_DEVICE_CONTROL */
+#define TFA9879_INPUT_SEL_MASK 0x0010
+#define TFA9879_INPUT_SEL_SHIFT 4
+#define TFA9879_OPMODE_MASK 0x0008
+#define TFA9879_OPMODE_SHIFT 3
+#define TFA9879_RESET_MASK 0x0002
+#define TFA9879_RESET_SHIFT 1
+#define TFA9879_POWERUP_MASK 0x0001
+#define TFA9879_POWERUP_SHIFT 0
+
+/* TFA9879_SERIAL_INTERFACE */
+#define TFA9879_MONO_SEL_MASK 0x0c00
+#define TFA9879_MONO_SEL_SHIFT 10
+#define TFA9879_MONO_SEL_LEFT 0
+#define TFA9879_MONO_SEL_RIGHT 1
+#define TFA9879_MONO_SEL_BOTH 2
+#define TFA9879_I2S_FS_MASK 0x03c0
+#define TFA9879_I2S_FS_SHIFT 6
+#define TFA9879_I2S_FS_8000 0
+#define TFA9879_I2S_FS_11025 1
+#define TFA9879_I2S_FS_12000 2
+#define TFA9879_I2S_FS_16000 3
+#define TFA9879_I2S_FS_22050 4
+#define TFA9879_I2S_FS_24000 5
+#define TFA9879_I2S_FS_32000 6
+#define TFA9879_I2S_FS_44100 7
+#define TFA9879_I2S_FS_48000 8
+#define TFA9879_I2S_FS_64000 9
+#define TFA9879_I2S_FS_88200 10
+#define TFA9879_I2S_FS_96000 11
+#define TFA9879_I2S_SET_MASK 0x0038
+#define TFA9879_I2S_SET_SHIFT 3
+#define TFA9879_I2S_SET_MSB_J_24 2
+#define TFA9879_I2S_SET_I2S_24 3
+#define TFA9879_I2S_SET_LSB_J_16 4
+#define TFA9879_I2S_SET_LSB_J_18 5
+#define TFA9879_I2S_SET_LSB_J_20 6
+#define TFA9879_I2S_SET_LSB_J_24 7
+#define TFA9879_SCK_POL_MASK 0x0004
+#define TFA9879_SCK_POL_SHIFT 2
+#define TFA9879_SCK_POL_NORMAL 0
+#define TFA9879_SCK_POL_INVERSE 1
+#define TFA9879_I_MODE_MASK 0x0003
+#define TFA9879_I_MODE_SHIFT 0
+#define TFA9879_I_MODE_I2S 0
+#define TFA9879_I_MODE_PCM_IOM2_SHORT 1
+#define TFA9879_I_MODE_PCM_IOM2_LONG 2
+
+/* TFA9879_PCM_IOM2_FORMAT */
+#define TFA9879_PCM_FS_MASK 0x0800
+#define TFA9879_PCM_FS_SHIFT 11
+#define TFA9879_A_LAW_MASK 0x0400
+#define TFA9879_A_LAW_SHIFT 10
+#define TFA9879_PCM_COMP_MASK 0x0200
+#define TFA9879_PCM_COMP_SHIFT 9
+#define TFA9879_PCM_DL_MASK 0x0100
+#define TFA9879_PCM_DL_SHIFT 8
+#define TFA9879_D1_SLOT_MASK 0x00f0
+#define TFA9879_D1_SLOT_SHIFT 4
+#define TFA9879_D2_SLOT_MASK 0x000f
+#define TFA9879_D2_SLOT_SHIFT 0
+
+/* TFA9879_EQUALIZER_X1 */
+#define TFA9879_T1_MASK 0x8000
+#define TFA9879_T1_SHIFT 15
+#define TFA9879_K1M_MASK 0x7ff0
+#define TFA9879_K1M_SHIFT 4
+#define TFA9879_K1E_MASK 0x000f
+#define TFA9879_K1E_SHIFT 0
+
+/* TFA9879_EQUALIZER_X2 */
+#define TFA9879_T2_MASK 0x8000
+#define TFA9879_T2_SHIFT 15
+#define TFA9879_K2M_MASK 0x7800
+#define TFA9879_K2M_SHIFT 11
+#define TFA9879_K2E_MASK 0x0700
+#define TFA9879_K2E_SHIFT 8
+#define TFA9879_K0_MASK 0x00fe
+#define TFA9879_K0_SHIFT 1
+#define TFA9879_S_MASK 0x0001
+#define TFA9879_S_SHIFT 0
+
+/* TFA9879_BYPASS_CONTROL */
+#define TFA9879_L_OCP_MASK 0x00c0
+#define TFA9879_L_OCP_SHIFT 6
+#define TFA9879_L_OTP_MASK 0x0030
+#define TFA9879_L_OTP_SHIFT 4
+#define TFA9879_CLIPCTRL_MASK 0x0008
+#define TFA9879_CLIPCTRL_SHIFT 3
+#define TFA9879_HPF_BP_MASK 0x0004
+#define TFA9879_HPF_BP_SHIFT 2
+#define TFA9879_DRC_BP_MASK 0x0002
+#define TFA9879_DRC_BP_SHIFT 1
+#define TFA9879_EQ_BP_MASK 0x0001
+#define TFA9879_EQ_BP_SHIFT 0
+
+/* TFA9879_DYNAMIC_RANGE_COMPR */
+#define TFA9879_AT_LVL_MASK 0xf000
+#define TFA9879_AT_LVL_SHIFT 12
+#define TFA9879_AT_RATE_MASK 0x0f00
+#define TFA9879_AT_RATE_SHIFT 8
+#define TFA9879_RL_LVL_MASK 0x00f0
+#define TFA9879_RL_LVL_SHIFT 4
+#define TFA9879_RL_RATE_MASK 0x000f
+#define TFA9879_RL_RATE_SHIFT 0
+
+/* TFA9879_BASS_TREBLE */
+#define TFA9879_G_TRBLE_MASK 0x3e00
+#define TFA9879_G_TRBLE_SHIFT 9
+#define TFA9879_F_TRBLE_MASK 0x0180
+#define TFA9879_F_TRBLE_SHIFT 7
+#define TFA9879_G_BASS_MASK 0x007c
+#define TFA9879_G_BASS_SHIFT 2
+#define TFA9879_F_BASS_MASK 0x0003
+#define TFA9879_F_BASS_SHIFT 0
+
+/* TFA9879_HIGH_PASS_FILTER */
+#define TFA9879_HP_CTRL_MASK 0x00ff
+#define TFA9879_HP_CTRL_SHIFT 0
+
+/* TFA9879_VOLUME_CONTROL */
+#define TFA9879_ZR_CRSS_MASK 0x1000
+#define TFA9879_ZR_CRSS_SHIFT 12
+#define TFA9879_VOL_MASK 0x00ff
+#define TFA9879_VOL_SHIFT 0
+
+/* TFA9879_MISC_CONTROL */
+#define TFA9879_DE_PHAS_MASK 0x0c00
+#define TFA9879_DE_PHAS_SHIFT 10
+#define TFA9879_H_MUTE_MASK 0x0200
+#define TFA9879_H_MUTE_SHIFT 9
+#define TFA9879_S_MUTE_MASK 0x0100
+#define TFA9879_S_MUTE_SHIFT 8
+#define TFA9879_P_LIM_MASK 0x00ff
+#define TFA9879_P_LIM_SHIFT 0
+
+/* TFA9879_MISC_STATUS */
+#define TFA9879_PS_MASK 0x4000
+#define TFA9879_PS_SHIFT 14
+#define TFA9879_PORA_MASK 0x2000
+#define TFA9879_PORA_SHIFT 13
+#define TFA9879_AMP_MASK 0x0600
+#define TFA9879_AMP_SHIFT 9
+#define TFA9879_IBP_2_MASK 0x0100
+#define TFA9879_IBP_2_SHIFT 8
+#define TFA9879_OFP_2_MASK 0x0080
+#define TFA9879_OFP_2_SHIFT 7
+#define TFA9879_UFP_2_MASK 0x0040
+#define TFA9879_UFP_2_SHIFT 6
+#define TFA9879_IBP_1_MASK 0x0020
+#define TFA9879_IBP_1_SHIFT 5
+#define TFA9879_OFP_1_MASK 0x0010
+#define TFA9879_OFP_1_SHIFT 4
+#define TFA9879_UFP_1_MASK 0x0008
+#define TFA9879_UFP_1_SHIFT 3
+#define TFA9879_OCPOKA_MASK 0x0004
+#define TFA9879_OCPOKA_SHIFT 2
+#define TFA9879_OCPOKB_MASK 0x0002
+#define TFA9879_OCPOKB_SHIFT 1
+#define TFA9879_OTPOK_MASK 0x0001
+#define TFA9879_OTPOK_SHIFT 0
+
+#endif
diff --git a/kernel/sound/soc/codecs/tlv320aic23-i2c.c b/kernel/sound/soc/codecs/tlv320aic23-i2c.c
new file mode 100644
index 000000000..78a94af65
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320aic23-i2c.c
@@ -0,0 +1,67 @@
+/*
+ * ALSA SoC TLV320AIC23 codec driver I2C interface
+ *
+ * Author: Arun KS, <arunks@mistralsolutions.com>
+ * Copyright: (C) 2008 Mistral Solutions Pvt Ltd.,
+ *
+ * Based on sound/soc/codecs/wm8731.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/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "tlv320aic23.h"
+
+static int tlv320aic23_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *i2c_id)
+{
+ struct regmap *regmap;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EINVAL;
+
+ regmap = devm_regmap_init_i2c(i2c, &tlv320aic23_regmap);
+ return tlv320aic23_probe(&i2c->dev, regmap);
+}
+
+static int tlv320aic23_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+ return 0;
+}
+
+static const struct i2c_device_id tlv320aic23_id[] = {
+ {"tlv320aic23", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tlv320aic23_id);
+
+static const struct of_device_id tlv320aic23_of_match[] = {
+ { .compatible = "ti,tlv320aic23", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tlv320aic23_of_match);
+
+static struct i2c_driver tlv320aic23_i2c_driver = {
+ .driver = {
+ .name = "tlv320aic23-codec",
+ .of_match_table = of_match_ptr(tlv320aic23_of_match),
+ },
+ .probe = tlv320aic23_i2c_probe,
+ .remove = tlv320aic23_i2c_remove,
+ .id_table = tlv320aic23_id,
+};
+
+module_i2c_driver(tlv320aic23_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver I2C");
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/tlv320aic23-spi.c b/kernel/sound/soc/codecs/tlv320aic23-spi.c
new file mode 100644
index 000000000..3b387e41d
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320aic23-spi.c
@@ -0,0 +1,56 @@
+/*
+ * ALSA SoC TLV320AIC23 codec driver SPI interface
+ *
+ * Author: Arun KS, <arunks@mistralsolutions.com>
+ * Copyright: (C) 2008 Mistral Solutions Pvt Ltd.,
+ *
+ * Based on sound/soc/codecs/wm8731.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>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "tlv320aic23.h"
+
+static int aic23_spi_probe(struct spi_device *spi)
+{
+ int ret;
+ struct regmap *regmap;
+
+ dev_dbg(&spi->dev, "probing tlv320aic23 spi device\n");
+
+ spi->mode = SPI_MODE_0;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ regmap = devm_regmap_init_spi(spi, &tlv320aic23_regmap);
+ return tlv320aic23_probe(&spi->dev, regmap);
+}
+
+static int aic23_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver aic23_spi = {
+ .driver = {
+ .name = "tlv320aic23",
+ .owner = THIS_MODULE,
+ },
+ .probe = aic23_spi_probe,
+ .remove = aic23_spi_remove,
+};
+
+module_spi_driver(aic23_spi);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver SPI");
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/tlv320aic23.c b/kernel/sound/soc/codecs/tlv320aic23.c
new file mode 100644
index 000000000..cc17e7e51
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320aic23.c
@@ -0,0 +1,617 @@
+/*
+ * ALSA SoC TLV320AIC23 codec driver
+ *
+ * Author: Arun KS, <arunks@mistralsolutions.com>
+ * Copyright: (C) 2008 Mistral Solutions Pvt Ltd.,
+ *
+ * Based on sound/soc/codecs/wm8731.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.
+ *
+ * Notes:
+ * The AIC23 is a driver for a low power stereo audio
+ * codec tlv320aic23
+ *
+ * The machine layer should disable unsupported inputs/outputs by
+ * snd_soc_dapm_disable_pin(codec, "LHPOUT"), etc.
+ */
+
+#include <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/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+
+#include "tlv320aic23.h"
+
+/*
+ * AIC23 register cache
+ */
+static const struct reg_default tlv320aic23_reg[] = {
+ { 0, 0x0097 },
+ { 1, 0x0097 },
+ { 2, 0x00F9 },
+ { 3, 0x00F9 },
+ { 4, 0x001A },
+ { 5, 0x0004 },
+ { 6, 0x0007 },
+ { 7, 0x0001 },
+ { 8, 0x0020 },
+ { 9, 0x0000 },
+};
+
+const struct regmap_config tlv320aic23_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = TLV320AIC23_RESET,
+ .reg_defaults = tlv320aic23_reg,
+ .num_reg_defaults = ARRAY_SIZE(tlv320aic23_reg),
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL(tlv320aic23_regmap);
+
+static const char *rec_src_text[] = { "Line", "Mic" };
+static const char *deemph_text[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+
+static SOC_ENUM_SINGLE_DECL(rec_src_enum,
+ TLV320AIC23_ANLG, 2, rec_src_text);
+
+static const struct snd_kcontrol_new tlv320aic23_rec_src_mux_controls =
+SOC_DAPM_ENUM("Input Select", rec_src_enum);
+
+static SOC_ENUM_SINGLE_DECL(tlv320aic23_rec_src,
+ TLV320AIC23_ANLG, 2, rec_src_text);
+static SOC_ENUM_SINGLE_DECL(tlv320aic23_deemph,
+ TLV320AIC23_DIGT, 1, deemph_text);
+
+static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -12100, 100, 0);
+static const DECLARE_TLV_DB_SCALE(input_gain_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_vol_tlv, -1800, 300, 0);
+
+static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ u16 val, reg;
+
+ val = (ucontrol->value.integer.value[0] & 0x07);
+
+ /* linear conversion to userspace
+ * 000 = -6db
+ * 001 = -9db
+ * 010 = -12db
+ * 011 = -18db (Min)
+ * 100 = 0db (Max)
+ */
+ val = (val >= 4) ? 4 : (3 - val);
+
+ reg = snd_soc_read(codec, TLV320AIC23_ANLG) & (~0x1C0);
+ snd_soc_write(codec, TLV320AIC23_ANLG, reg | (val << 6));
+
+ return 0;
+}
+
+static int snd_soc_tlv320aic23_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ u16 val;
+
+ val = snd_soc_read(codec, TLV320AIC23_ANLG) & (0x1C0);
+ val = val >> 6;
+ val = (val >= 4) ? 4 : (3 - val);
+ ucontrol->value.integer.value[0] = val;
+ return 0;
+
+}
+
+static const struct snd_kcontrol_new tlv320aic23_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Playback Volume", TLV320AIC23_LCHNVOL,
+ TLV320AIC23_RCHNVOL, 0, 127, 0, out_gain_tlv),
+ SOC_SINGLE("Digital Playback Switch", TLV320AIC23_DIGT, 3, 1, 1),
+ SOC_DOUBLE_R("Line Input Switch", TLV320AIC23_LINVOL,
+ TLV320AIC23_RINVOL, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("Line Input Volume", TLV320AIC23_LINVOL,
+ TLV320AIC23_RINVOL, 0, 31, 0, input_gain_tlv),
+ SOC_SINGLE("Mic Input Switch", TLV320AIC23_ANLG, 1, 1, 1),
+ SOC_SINGLE("Mic Booster Switch", TLV320AIC23_ANLG, 0, 1, 0),
+ SOC_SINGLE_EXT_TLV("Sidetone Volume", TLV320AIC23_ANLG, 6, 4, 0,
+ snd_soc_tlv320aic23_get_volsw,
+ snd_soc_tlv320aic23_put_volsw, sidetone_vol_tlv),
+ SOC_ENUM("Playback De-emphasis", tlv320aic23_deemph),
+};
+
+/* PGA Mixer controls for Line and Mic switch */
+static const struct snd_kcontrol_new tlv320aic23_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Line Bypass Switch", TLV320AIC23_ANLG, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", TLV320AIC23_ANLG, 5, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", TLV320AIC23_ANLG, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", TLV320AIC23_PWR, 3, 1),
+ SND_SOC_DAPM_ADC("ADC", "Capture", TLV320AIC23_PWR, 2, 1),
+ SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
+ &tlv320aic23_rec_src_mux_controls),
+ SND_SOC_DAPM_MIXER("Output Mixer", TLV320AIC23_PWR, 4, 1,
+ &tlv320aic23_output_mixer_controls[0],
+ ARRAY_SIZE(tlv320aic23_output_mixer_controls)),
+ SND_SOC_DAPM_PGA("Line Input", TLV320AIC23_PWR, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic Input", TLV320AIC23_PWR, 1, 1, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LHPOUT"),
+ SND_SOC_DAPM_OUTPUT("RHPOUT"),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+
+ SND_SOC_DAPM_INPUT("LLINEIN"),
+ SND_SOC_DAPM_INPUT("RLINEIN"),
+
+ SND_SOC_DAPM_INPUT("MICIN"),
+};
+
+static const struct snd_soc_dapm_route tlv320aic23_intercon[] = {
+ /* Output Mixer */
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
+ {"Output Mixer", "Playback Switch", "DAC"},
+ {"Output Mixer", "Mic Sidetone Switch", "Mic Input"},
+
+ /* Outputs */
+ {"RHPOUT", NULL, "Output Mixer"},
+ {"LHPOUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+ {"ROUT", NULL, "Output Mixer"},
+
+ /* Inputs */
+ {"Line Input", "NULL", "LLINEIN"},
+ {"Line Input", "NULL", "RLINEIN"},
+
+ {"Mic Input", "NULL", "MICIN"},
+
+ /* input mux */
+ {"Capture Source", "Line", "Line Input"},
+ {"Capture Source", "Mic", "Mic Input"},
+ {"ADC", NULL, "Capture Source"},
+
+};
+
+/* AIC23 driver data */
+struct aic23 {
+ struct regmap *regmap;
+ int mclk;
+ int requested_adc;
+ int requested_dac;
+};
+
+/*
+ * Common Crystals used
+ * 11.2896 Mhz /128 = *88.2k /192 = 58.8k
+ * 12.0000 Mhz /125 = *96k /136 = 88.235K
+ * 12.2880 Mhz /128 = *96k /192 = 64k
+ * 16.9344 Mhz /128 = 132.3k /192 = *88.2k
+ * 18.4320 Mhz /128 = 144k /192 = *96k
+ */
+
+/*
+ * Normal BOSR 0-256/2 = 128, 1-384/2 = 192
+ * USB BOSR 0-250/2 = 125, 1-272/2 = 136
+ */
+static const int bosr_usb_divisor_table[] = {
+ 128, 125, 192, 136
+};
+#define LOWER_GROUP ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<6) | (1<<7))
+#define UPPER_GROUP ((1<<8) | (1<<9) | (1<<10) | (1<<11) | (1<<15))
+static const unsigned short sr_valid_mask[] = {
+ LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 0*/
+ LOWER_GROUP, /* Usb, bosr - 0*/
+ LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 1*/
+ UPPER_GROUP, /* Usb, bosr - 1*/
+};
+/*
+ * Every divisor is a factor of 11*12
+ */
+#define SR_MULT (11*12)
+#define A(x) (SR_MULT/x)
+static const unsigned char sr_adc_mult_table[] = {
+ A(2), A(2), A(12), A(12), 0, 0, A(3), A(1),
+ A(2), A(2), A(11), A(11), 0, 0, 0, A(1)
+};
+static const unsigned char sr_dac_mult_table[] = {
+ A(2), A(12), A(2), A(12), 0, 0, A(3), A(1),
+ A(2), A(11), A(2), A(11), 0, 0, 0, A(1)
+};
+
+static unsigned get_score(int adc, int adc_l, int adc_h, int need_adc,
+ int dac, int dac_l, int dac_h, int need_dac)
+{
+ if ((adc >= adc_l) && (adc <= adc_h) &&
+ (dac >= dac_l) && (dac <= dac_h)) {
+ int diff_adc = need_adc - adc;
+ int diff_dac = need_dac - dac;
+ return abs(diff_adc) + abs(diff_dac);
+ }
+ return UINT_MAX;
+}
+
+static int find_rate(int mclk, u32 need_adc, u32 need_dac)
+{
+ int i, j;
+ int best_i = -1;
+ int best_j = -1;
+ int best_div = 0;
+ unsigned best_score = UINT_MAX;
+ int adc_l, adc_h, dac_l, dac_h;
+
+ need_adc *= SR_MULT;
+ need_dac *= SR_MULT;
+ /*
+ * rates given are +/- 1/32
+ */
+ adc_l = need_adc - (need_adc >> 5);
+ adc_h = need_adc + (need_adc >> 5);
+ dac_l = need_dac - (need_dac >> 5);
+ dac_h = need_dac + (need_dac >> 5);
+ for (i = 0; i < ARRAY_SIZE(bosr_usb_divisor_table); i++) {
+ int base = mclk / bosr_usb_divisor_table[i];
+ int mask = sr_valid_mask[i];
+ for (j = 0; j < ARRAY_SIZE(sr_adc_mult_table);
+ j++, mask >>= 1) {
+ int adc;
+ int dac;
+ int score;
+ if ((mask & 1) == 0)
+ continue;
+ adc = base * sr_adc_mult_table[j];
+ dac = base * sr_dac_mult_table[j];
+ score = get_score(adc, adc_l, adc_h, need_adc,
+ dac, dac_l, dac_h, need_dac);
+ if (best_score > score) {
+ best_score = score;
+ best_i = i;
+ best_j = j;
+ best_div = 0;
+ }
+ score = get_score((adc >> 1), adc_l, adc_h, need_adc,
+ (dac >> 1), dac_l, dac_h, need_dac);
+ /* prefer to have a /2 */
+ if ((score != UINT_MAX) && (best_score >= score)) {
+ best_score = score;
+ best_i = i;
+ best_j = j;
+ best_div = 1;
+ }
+ }
+ }
+ return (best_j << 2) | best_i | (best_div << TLV320AIC23_CLKIN_SHIFT);
+}
+
+#ifdef DEBUG
+static void get_current_sample_rates(struct snd_soc_codec *codec, int mclk,
+ u32 *sample_rate_adc, u32 *sample_rate_dac)
+{
+ int src = snd_soc_read(codec, TLV320AIC23_SRATE);
+ int sr = (src >> 2) & 0x0f;
+ int val = (mclk / bosr_usb_divisor_table[src & 3]);
+ int adc = (val * sr_adc_mult_table[sr]) / SR_MULT;
+ int dac = (val * sr_dac_mult_table[sr]) / SR_MULT;
+ if (src & TLV320AIC23_CLKIN_HALF) {
+ adc >>= 1;
+ dac >>= 1;
+ }
+ *sample_rate_adc = adc;
+ *sample_rate_dac = dac;
+}
+#endif
+
+static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk,
+ u32 sample_rate_adc, u32 sample_rate_dac)
+{
+ /* Search for the right sample rate */
+ int data = find_rate(mclk, sample_rate_adc, sample_rate_dac);
+ if (data < 0) {
+ printk(KERN_ERR "%s:Invalid rate %u,%u requested\n",
+ __func__, sample_rate_adc, sample_rate_dac);
+ return -EINVAL;
+ }
+ snd_soc_write(codec, TLV320AIC23_SRATE, data);
+#ifdef DEBUG
+ {
+ u32 adc, dac;
+ get_current_sample_rates(codec, mclk, &adc, &dac);
+ printk(KERN_DEBUG "actual samplerate = %u,%u reg=%x\n",
+ adc, dac, data);
+ }
+#endif
+ return 0;
+}
+
+static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 iface_reg;
+ int ret;
+ struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
+ u32 sample_rate_adc = aic23->requested_adc;
+ u32 sample_rate_dac = aic23->requested_dac;
+ u32 sample_rate = params_rate(params);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ aic23->requested_dac = sample_rate_dac = sample_rate;
+ if (!sample_rate_adc)
+ sample_rate_adc = sample_rate;
+ } else {
+ aic23->requested_adc = sample_rate_adc = sample_rate;
+ if (!sample_rate_dac)
+ sample_rate_dac = sample_rate;
+ }
+ ret = set_sample_rate_control(codec, aic23->mclk, sample_rate_adc,
+ sample_rate_dac);
+ if (ret < 0)
+ return ret;
+
+ iface_reg = snd_soc_read(codec, TLV320AIC23_DIGT_FMT) & ~(0x03 << 2);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface_reg |= (0x01 << 2);
+ break;
+ case 24:
+ iface_reg |= (0x02 << 2);
+ break;
+ case 32:
+ iface_reg |= (0x03 << 2);
+ break;
+ }
+ snd_soc_write(codec, TLV320AIC23_DIGT_FMT, iface_reg);
+
+ return 0;
+}
+
+static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ /* set active */
+ snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0001);
+
+ return 0;
+}
+
+static void tlv320aic23_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
+
+ /* deactivate */
+ if (!snd_soc_codec_is_active(codec)) {
+ udelay(50);
+ snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0);
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ aic23->requested_dac = 0;
+ else
+ aic23->requested_adc = 0;
+}
+
+static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 reg;
+
+ reg = snd_soc_read(codec, TLV320AIC23_DIGT);
+ if (mute)
+ reg |= TLV320AIC23_DACM_MUTE;
+
+ else
+ reg &= ~TLV320AIC23_DACM_MUTE;
+
+ snd_soc_write(codec, TLV320AIC23_DIGT, reg);
+
+ return 0;
+}
+
+static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface_reg;
+
+ iface_reg = snd_soc_read(codec, TLV320AIC23_DIGT_FMT) & (~0x03);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface_reg |= TLV320AIC23_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ iface_reg &= ~TLV320AIC23_MS_MASTER;
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface_reg |= TLV320AIC23_FOR_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface_reg |= TLV320AIC23_LRP_ON;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface_reg |= TLV320AIC23_FOR_DSP;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface_reg |= TLV320AIC23_FOR_LJUST;
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ snd_soc_write(codec, TLV320AIC23_DIGT_FMT, iface_reg);
+
+ return 0;
+}
+
+static int tlv320aic23_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct aic23 *aic23 = snd_soc_dai_get_drvdata(codec_dai);
+ aic23->mclk = freq;
+ return 0;
+}
+
+static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg = snd_soc_read(codec, TLV320AIC23_PWR) & 0x17f;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* vref/mid, osc on, dac unmute */
+ reg &= ~(TLV320AIC23_DEVICE_PWR_OFF | TLV320AIC23_OSC_OFF | \
+ TLV320AIC23_DAC_OFF);
+ snd_soc_write(codec, TLV320AIC23_PWR, reg);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* everything off except vref/vmid, */
+ snd_soc_write(codec, TLV320AIC23_PWR,
+ reg | TLV320AIC23_CLK_OFF);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* everything off, dac mute, inactive */
+ snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0);
+ snd_soc_write(codec, TLV320AIC23_PWR, 0x1ff);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define AIC23_RATES SNDRV_PCM_RATE_8000_96000
+#define AIC23_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops tlv320aic23_dai_ops = {
+ .prepare = tlv320aic23_pcm_prepare,
+ .hw_params = tlv320aic23_hw_params,
+ .shutdown = tlv320aic23_shutdown,
+ .digital_mute = tlv320aic23_mute,
+ .set_fmt = tlv320aic23_set_dai_fmt,
+ .set_sysclk = tlv320aic23_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver tlv320aic23_dai = {
+ .name = "tlv320aic23-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC23_RATES,
+ .formats = AIC23_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC23_RATES,
+ .formats = AIC23_FORMATS,},
+ .ops = &tlv320aic23_dai_ops,
+};
+
+static int tlv320aic23_resume(struct snd_soc_codec *codec)
+{
+ struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
+ regcache_mark_dirty(aic23->regmap);
+ regcache_sync(aic23->regmap);
+
+ return 0;
+}
+
+static int tlv320aic23_codec_probe(struct snd_soc_codec *codec)
+{
+ /* Reset codec */
+ snd_soc_write(codec, TLV320AIC23_RESET, 0);
+
+ snd_soc_write(codec, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K);
+
+ /* Unmute input */
+ snd_soc_update_bits(codec, TLV320AIC23_LINVOL,
+ TLV320AIC23_LIM_MUTED, TLV320AIC23_LRS_ENABLED);
+
+ snd_soc_update_bits(codec, TLV320AIC23_RINVOL,
+ TLV320AIC23_LIM_MUTED, TLV320AIC23_LRS_ENABLED);
+
+ snd_soc_update_bits(codec, TLV320AIC23_ANLG,
+ TLV320AIC23_BYPASS_ON | TLV320AIC23_MICM_MUTED,
+ 0);
+
+ /* Default output volume */
+ snd_soc_write(codec, TLV320AIC23_LCHNVOL,
+ TLV320AIC23_DEFAULT_OUT_VOL & TLV320AIC23_OUT_VOL_MASK);
+ snd_soc_write(codec, TLV320AIC23_RCHNVOL,
+ TLV320AIC23_DEFAULT_OUT_VOL & TLV320AIC23_OUT_VOL_MASK);
+
+ snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x1);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = {
+ .probe = tlv320aic23_codec_probe,
+ .resume = tlv320aic23_resume,
+ .set_bias_level = tlv320aic23_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = tlv320aic23_snd_controls,
+ .num_controls = ARRAY_SIZE(tlv320aic23_snd_controls),
+ .dapm_widgets = tlv320aic23_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
+ .dapm_routes = tlv320aic23_intercon,
+ .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon),
+};
+
+int tlv320aic23_probe(struct device *dev, struct regmap *regmap)
+{
+ struct aic23 *aic23;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ aic23 = devm_kzalloc(dev, sizeof(struct aic23), GFP_KERNEL);
+ if (aic23 == NULL)
+ return -ENOMEM;
+
+ aic23->regmap = regmap;
+
+ dev_set_drvdata(dev, aic23);
+
+ return snd_soc_register_codec(dev, &soc_codec_dev_tlv320aic23,
+ &tlv320aic23_dai, 1);
+}
+EXPORT_SYMBOL(tlv320aic23_probe);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver");
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/tlv320aic23.h b/kernel/sound/soc/codecs/tlv320aic23.h
new file mode 100644
index 000000000..3a7235a04
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320aic23.h
@@ -0,0 +1,125 @@
+/*
+ * ALSA SoC TLV320AIC23 codec driver
+ *
+ * Author: Arun KS, <arunks@mistralsolutions.com>
+ * Copyright: (C) 2008 Mistral Solutions Pvt 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.
+ */
+
+#ifndef _TLV320AIC23_H
+#define _TLV320AIC23_H
+
+struct device;
+struct regmap_config;
+
+extern const struct regmap_config tlv320aic23_regmap;
+int tlv320aic23_probe(struct device *dev, struct regmap *regmap);
+
+/* Codec TLV320AIC23 */
+#define TLV320AIC23_LINVOL 0x00
+#define TLV320AIC23_RINVOL 0x01
+#define TLV320AIC23_LCHNVOL 0x02
+#define TLV320AIC23_RCHNVOL 0x03
+#define TLV320AIC23_ANLG 0x04
+#define TLV320AIC23_DIGT 0x05
+#define TLV320AIC23_PWR 0x06
+#define TLV320AIC23_DIGT_FMT 0x07
+#define TLV320AIC23_SRATE 0x08
+#define TLV320AIC23_ACTIVE 0x09
+#define TLV320AIC23_RESET 0x0F
+
+/* Left (right) line input volume control register */
+#define TLV320AIC23_LRS_ENABLED 0x0100
+#define TLV320AIC23_LIM_MUTED 0x0080
+#define TLV320AIC23_LIV_DEFAULT 0x0017
+#define TLV320AIC23_LIV_MAX 0x001f
+#define TLV320AIC23_LIV_MIN 0x0000
+
+/* Left (right) channel headphone volume control register */
+#define TLV320AIC23_LZC_ON 0x0080
+#define TLV320AIC23_LHV_DEFAULT 0x0079
+#define TLV320AIC23_LHV_MAX 0x007f
+#define TLV320AIC23_LHV_MIN 0x0000
+
+/* Analog audio path control register */
+#define TLV320AIC23_STA_REG(x) ((x)<<6)
+#define TLV320AIC23_STE_ENABLED 0x0020
+#define TLV320AIC23_DAC_SELECTED 0x0010
+#define TLV320AIC23_BYPASS_ON 0x0008
+#define TLV320AIC23_INSEL_MIC 0x0004
+#define TLV320AIC23_MICM_MUTED 0x0002
+#define TLV320AIC23_MICB_20DB 0x0001
+
+/* Digital audio path control register */
+#define TLV320AIC23_DACM_MUTE 0x0008
+#define TLV320AIC23_DEEMP_32K 0x0002
+#define TLV320AIC23_DEEMP_44K 0x0004
+#define TLV320AIC23_DEEMP_48K 0x0006
+#define TLV320AIC23_ADCHP_ON 0x0001
+
+/* Power control down register */
+#define TLV320AIC23_DEVICE_PWR_OFF 0x0080
+#define TLV320AIC23_CLK_OFF 0x0040
+#define TLV320AIC23_OSC_OFF 0x0020
+#define TLV320AIC23_OUT_OFF 0x0010
+#define TLV320AIC23_DAC_OFF 0x0008
+#define TLV320AIC23_ADC_OFF 0x0004
+#define TLV320AIC23_MIC_OFF 0x0002
+#define TLV320AIC23_LINE_OFF 0x0001
+
+/* Digital audio interface register */
+#define TLV320AIC23_MS_MASTER 0x0040
+#define TLV320AIC23_LRSWAP_ON 0x0020
+#define TLV320AIC23_LRP_ON 0x0010
+#define TLV320AIC23_IWL_16 0x0000
+#define TLV320AIC23_IWL_20 0x0004
+#define TLV320AIC23_IWL_24 0x0008
+#define TLV320AIC23_IWL_32 0x000C
+#define TLV320AIC23_FOR_I2S 0x0002
+#define TLV320AIC23_FOR_DSP 0x0003
+#define TLV320AIC23_FOR_LJUST 0x0001
+
+/* Sample rate control register */
+#define TLV320AIC23_CLKOUT_HALF 0x0080
+#define TLV320AIC23_CLKIN_HALF 0x0040
+#define TLV320AIC23_BOSR_384fs 0x0002 /* BOSR_272fs in USB mode */
+#define TLV320AIC23_USB_CLK_ON 0x0001
+#define TLV320AIC23_SR_MASK 0xf
+#define TLV320AIC23_CLKOUT_SHIFT 7
+#define TLV320AIC23_CLKIN_SHIFT 6
+#define TLV320AIC23_SR_SHIFT 2
+#define TLV320AIC23_BOSR_SHIFT 1
+
+/* Digital interface register */
+#define TLV320AIC23_ACT_ON 0x0001
+
+/*
+ * AUDIO related MACROS
+ */
+
+#define TLV320AIC23_DEFAULT_OUT_VOL 0x70
+#define TLV320AIC23_DEFAULT_IN_VOLUME 0x10
+
+#define TLV320AIC23_OUT_VOL_MIN TLV320AIC23_LHV_MIN
+#define TLV320AIC23_OUT_VOL_MAX TLV320AIC23_LHV_MAX
+#define TLV320AIC23_OUT_VO_RANGE (TLV320AIC23_OUT_VOL_MAX - \
+ TLV320AIC23_OUT_VOL_MIN)
+#define TLV320AIC23_OUT_VOL_MASK TLV320AIC23_OUT_VOL_MAX
+
+#define TLV320AIC23_IN_VOL_MIN TLV320AIC23_LIV_MIN
+#define TLV320AIC23_IN_VOL_MAX TLV320AIC23_LIV_MAX
+#define TLV320AIC23_IN_VOL_RANGE (TLV320AIC23_IN_VOL_MAX - \
+ TLV320AIC23_IN_VOL_MIN)
+#define TLV320AIC23_IN_VOL_MASK TLV320AIC23_IN_VOL_MAX
+
+#define TLV320AIC23_SIDETONE_MASK 0x1c0
+#define TLV320AIC23_SIDETONE_0 0x100
+#define TLV320AIC23_SIDETONE_6 0x000
+#define TLV320AIC23_SIDETONE_9 0x040
+#define TLV320AIC23_SIDETONE_12 0x080
+#define TLV320AIC23_SIDETONE_18 0x0c0
+
+#endif /* _TLV320AIC23_H */
diff --git a/kernel/sound/soc/codecs/tlv320aic26.c b/kernel/sound/soc/codecs/tlv320aic26.c
new file mode 100644
index 000000000..620ab9ea1
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320aic26.c
@@ -0,0 +1,382 @@
+/*
+ * Texas Instruments TLV320AIC26 low power audio CODEC
+ * ALSA SoC CODEC driver
+ *
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.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 "tlv320aic26.h"
+
+MODULE_DESCRIPTION("ASoC TLV320AIC26 codec driver");
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_LICENSE("GPL");
+
+/* AIC26 driver private data */
+struct aic26 {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ int master;
+ int datfm;
+ int mclk;
+
+ /* Keyclick parameters */
+ int keyclick_amplitude;
+ int keyclick_freq;
+ int keyclick_len;
+};
+
+static const struct snd_soc_dapm_widget tlv320aic26_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MICIN"),
+SND_SOC_DAPM_INPUT("AUX"),
+
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+static const struct snd_soc_dapm_route tlv320aic26_dapm_routes[] = {
+ { "Capture", NULL, "MICIN" },
+ { "Capture", NULL, "AUX" },
+
+ { "HPL", NULL, "Playback" },
+ { "HPR", NULL, "Playback" },
+};
+
+/* ---------------------------------------------------------------------
+ * Digital Audio Interface Operations
+ */
+static int aic26_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec);
+ int fsref, divisor, wlen, pval, jval, dval, qval;
+ u16 reg;
+
+ dev_dbg(&aic26->spi->dev, "aic26_hw_params(substream=%p, params=%p)\n",
+ substream, params);
+ dev_dbg(&aic26->spi->dev, "rate=%i width=%d\n", params_rate(params),
+ params_width(params));
+
+ switch (params_rate(params)) {
+ case 8000: fsref = 48000; divisor = AIC26_DIV_6; break;
+ case 11025: fsref = 44100; divisor = AIC26_DIV_4; break;
+ case 12000: fsref = 48000; divisor = AIC26_DIV_4; break;
+ case 16000: fsref = 48000; divisor = AIC26_DIV_3; break;
+ case 22050: fsref = 44100; divisor = AIC26_DIV_2; break;
+ case 24000: fsref = 48000; divisor = AIC26_DIV_2; break;
+ case 32000: fsref = 48000; divisor = AIC26_DIV_1_5; break;
+ case 44100: fsref = 44100; divisor = AIC26_DIV_1; break;
+ case 48000: fsref = 48000; divisor = AIC26_DIV_1; break;
+ default:
+ dev_dbg(&aic26->spi->dev, "bad rate\n"); return -EINVAL;
+ }
+
+ /* select data word length */
+ switch (params_width(params)) {
+ case 8: wlen = AIC26_WLEN_16; break;
+ case 16: wlen = AIC26_WLEN_16; break;
+ case 24: wlen = AIC26_WLEN_24; break;
+ case 32: wlen = AIC26_WLEN_32; break;
+ default:
+ dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL;
+ }
+
+ /**
+ * Configure PLL
+ * fsref = (mclk * PLLM) / 2048
+ * where PLLM = J.DDDD (DDDD register ranges from 0 to 9999, decimal)
+ */
+ pval = 1;
+ /* compute J portion of multiplier */
+ jval = fsref / (aic26->mclk / 2048);
+ /* compute fractional DDDD component of multiplier */
+ dval = fsref - (jval * (aic26->mclk / 2048));
+ dval = (10000 * dval) / (aic26->mclk / 2048);
+ dev_dbg(&aic26->spi->dev, "Setting PLLM to %d.%04d\n", jval, dval);
+ qval = 0;
+ reg = 0x8000 | qval << 11 | pval << 8 | jval << 2;
+ snd_soc_write(codec, AIC26_REG_PLL_PROG1, reg);
+ reg = dval << 2;
+ snd_soc_write(codec, AIC26_REG_PLL_PROG2, reg);
+
+ /* Audio Control 3 (master mode, fsref rate) */
+ if (aic26->master)
+ reg = 0x0800;
+ if (fsref == 48000)
+ reg = 0x2000;
+ snd_soc_update_bits(codec, AIC26_REG_AUDIO_CTRL3, 0xf800, reg);
+
+ /* Audio Control 1 (FSref divisor) */
+ reg = wlen | aic26->datfm | (divisor << 3) | divisor;
+ snd_soc_update_bits(codec, AIC26_REG_AUDIO_CTRL1, 0xfff, reg);
+
+ return 0;
+}
+
+/**
+ * aic26_mute - Mute control to reduce noise when changing audio format
+ */
+static int aic26_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec);
+ u16 reg;
+
+ dev_dbg(&aic26->spi->dev, "aic26_mute(dai=%p, mute=%i)\n",
+ dai, mute);
+
+ if (mute)
+ reg = 0x8080;
+ else
+ reg = 0;
+ snd_soc_update_bits(codec, AIC26_REG_DAC_GAIN, 0x8000, reg);
+
+ return 0;
+}
+
+static int aic26_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(&aic26->spi->dev, "aic26_set_sysclk(dai=%p, clk_id==%i,"
+ " freq=%i, dir=%i)\n",
+ codec_dai, clk_id, freq, dir);
+
+ /* MCLK needs to fall between 2MHz and 50 MHz */
+ if ((freq < 2000000) || (freq > 50000000))
+ return -EINVAL;
+
+ aic26->mclk = freq;
+ return 0;
+}
+
+static int aic26_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(&aic26->spi->dev, "aic26_set_fmt(dai=%p, fmt==%i)\n",
+ codec_dai, fmt);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM: aic26->master = 1; break;
+ case SND_SOC_DAIFMT_CBS_CFS: aic26->master = 0; break;
+ default:
+ dev_dbg(&aic26->spi->dev, "bad master\n"); return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S: aic26->datfm = AIC26_DATFM_I2S; break;
+ case SND_SOC_DAIFMT_DSP_A: aic26->datfm = AIC26_DATFM_DSP; break;
+ case SND_SOC_DAIFMT_RIGHT_J: aic26->datfm = AIC26_DATFM_RIGHTJ; break;
+ case SND_SOC_DAIFMT_LEFT_J: aic26->datfm = AIC26_DATFM_LEFTJ; break;
+ default:
+ dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * Digital Audio Interface Definition
+ */
+#define AIC26_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000)
+#define AIC26_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |\
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
+
+static const struct snd_soc_dai_ops aic26_dai_ops = {
+ .hw_params = aic26_hw_params,
+ .digital_mute = aic26_mute,
+ .set_sysclk = aic26_set_sysclk,
+ .set_fmt = aic26_set_fmt,
+};
+
+static struct snd_soc_dai_driver aic26_dai = {
+ .name = "tlv320aic26-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC26_RATES,
+ .formats = AIC26_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC26_RATES,
+ .formats = AIC26_FORMATS,
+ },
+ .ops = &aic26_dai_ops,
+};
+
+/* ---------------------------------------------------------------------
+ * ALSA controls
+ */
+static const char *aic26_capture_src_text[] = {"Mic", "Aux"};
+static SOC_ENUM_SINGLE_DECL(aic26_capture_src_enum,
+ AIC26_REG_AUDIO_CTRL1, 12,
+ aic26_capture_src_text);
+
+static const struct snd_kcontrol_new aic26_snd_controls[] = {
+ /* Output */
+ SOC_DOUBLE("PCM Playback Volume", AIC26_REG_DAC_GAIN, 8, 0, 0x7f, 1),
+ SOC_DOUBLE("PCM Playback Switch", AIC26_REG_DAC_GAIN, 15, 7, 1, 1),
+ SOC_SINGLE("PCM Capture Volume", AIC26_REG_ADC_GAIN, 8, 0x7f, 0),
+ SOC_SINGLE("PCM Capture Mute", AIC26_REG_ADC_GAIN, 15, 1, 1),
+ SOC_SINGLE("Keyclick activate", AIC26_REG_AUDIO_CTRL2, 15, 0x1, 0),
+ SOC_SINGLE("Keyclick amplitude", AIC26_REG_AUDIO_CTRL2, 12, 0x7, 0),
+ SOC_SINGLE("Keyclick frequency", AIC26_REG_AUDIO_CTRL2, 8, 0x7, 0),
+ SOC_SINGLE("Keyclick period", AIC26_REG_AUDIO_CTRL2, 4, 0xf, 0),
+ SOC_ENUM("Capture Source", aic26_capture_src_enum),
+};
+
+/* ---------------------------------------------------------------------
+ * SPI device portion of driver: sysfs files for debugging
+ */
+
+static ssize_t aic26_keyclick_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct aic26 *aic26 = dev_get_drvdata(dev);
+ int val, amp, freq, len;
+
+ val = snd_soc_read(aic26->codec, AIC26_REG_AUDIO_CTRL2);
+ amp = (val >> 12) & 0x7;
+ freq = (125 << ((val >> 8) & 0x7)) >> 1;
+ len = 2 * (1 + ((val >> 4) & 0xf));
+
+ return sprintf(buf, "amp=%x freq=%iHz len=%iclks\n", amp, freq, len);
+}
+
+/* Any write to the keyclick attribute will trigger the keyclick event */
+static ssize_t aic26_keyclick_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aic26 *aic26 = dev_get_drvdata(dev);
+
+ snd_soc_update_bits(aic26->codec, AIC26_REG_AUDIO_CTRL2,
+ 0x8000, 0x800);
+
+ return count;
+}
+
+static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
+
+/* ---------------------------------------------------------------------
+ * SoC CODEC portion of driver: probe and release routines
+ */
+static int aic26_probe(struct snd_soc_codec *codec)
+{
+ struct aic26 *aic26 = dev_get_drvdata(codec->dev);
+ int ret, reg;
+
+ aic26->codec = codec;
+
+ /* Reset the codec to power on defaults */
+ snd_soc_write(codec, AIC26_REG_RESET, 0xBB00);
+
+ /* Power up CODEC */
+ snd_soc_write(codec, AIC26_REG_POWER_CTRL, 0);
+
+ /* Audio Control 3 (master mode, fsref rate) */
+ reg = snd_soc_read(codec, AIC26_REG_AUDIO_CTRL3);
+ reg &= ~0xf800;
+ reg |= 0x0800; /* set master mode */
+ snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
+
+ /* Register the sysfs files for debugging */
+ /* Create SysFS files */
+ ret = device_create_file(codec->dev, &dev_attr_keyclick);
+ if (ret)
+ dev_info(codec->dev, "error creating sysfs files\n");
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver aic26_soc_codec_dev = {
+ .probe = aic26_probe,
+ .controls = aic26_snd_controls,
+ .num_controls = ARRAY_SIZE(aic26_snd_controls),
+ .dapm_widgets = tlv320aic26_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic26_dapm_widgets),
+ .dapm_routes = tlv320aic26_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tlv320aic26_dapm_routes),
+};
+
+static const struct regmap_config aic26_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+};
+
+/* ---------------------------------------------------------------------
+ * SPI device portion of driver: probe and release routines and SPI
+ * driver registration.
+ */
+static int aic26_spi_probe(struct spi_device *spi)
+{
+ struct aic26 *aic26;
+ int ret;
+
+ dev_dbg(&spi->dev, "probing tlv320aic26 spi device\n");
+
+ /* Allocate driver data */
+ aic26 = devm_kzalloc(&spi->dev, sizeof *aic26, GFP_KERNEL);
+ if (!aic26)
+ return -ENOMEM;
+
+ aic26->regmap = devm_regmap_init_spi(spi, &aic26_regmap);
+ if (IS_ERR(aic26->regmap))
+ return PTR_ERR(aic26->regmap);
+
+ /* Initialize the driver data */
+ aic26->spi = spi;
+ dev_set_drvdata(&spi->dev, aic26);
+ aic26->master = 1;
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &aic26_soc_codec_dev, &aic26_dai, 1);
+ return ret;
+}
+
+static int aic26_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver aic26_spi = {
+ .driver = {
+ .name = "tlv320aic26-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = aic26_spi_probe,
+ .remove = aic26_spi_remove,
+};
+
+module_spi_driver(aic26_spi);
diff --git a/kernel/sound/soc/codecs/tlv320aic26.h b/kernel/sound/soc/codecs/tlv320aic26.h
new file mode 100644
index 000000000..629b85e75
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320aic26.h
@@ -0,0 +1,90 @@
+/*
+ * Texas Instruments TLV320AIC26 low power audio CODEC
+ * register definitions
+ *
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ */
+
+#ifndef _TLV320AIC16_H_
+#define _TLV320AIC16_H_
+
+/* AIC26 Registers */
+#define AIC26_PAGE_ADDR(page, offset) ((page << 11) | offset << 5)
+
+/* Page 0: Auxiliary data registers */
+#define AIC26_REG_BAT1 AIC26_PAGE_ADDR(0, 0x05)
+#define AIC26_REG_BAT2 AIC26_PAGE_ADDR(0, 0x06)
+#define AIC26_REG_AUX AIC26_PAGE_ADDR(0, 0x07)
+#define AIC26_REG_TEMP1 AIC26_PAGE_ADDR(0, 0x09)
+#define AIC26_REG_TEMP2 AIC26_PAGE_ADDR(0, 0x0A)
+
+/* Page 1: Auxiliary control registers */
+#define AIC26_REG_AUX_ADC AIC26_PAGE_ADDR(1, 0x00)
+#define AIC26_REG_STATUS AIC26_PAGE_ADDR(1, 0x01)
+#define AIC26_REG_REFERENCE AIC26_PAGE_ADDR(1, 0x03)
+#define AIC26_REG_RESET AIC26_PAGE_ADDR(1, 0x04)
+
+/* Page 2: Audio control registers */
+#define AIC26_REG_AUDIO_CTRL1 AIC26_PAGE_ADDR(2, 0x00)
+#define AIC26_REG_ADC_GAIN AIC26_PAGE_ADDR(2, 0x01)
+#define AIC26_REG_DAC_GAIN AIC26_PAGE_ADDR(2, 0x02)
+#define AIC26_REG_SIDETONE AIC26_PAGE_ADDR(2, 0x03)
+#define AIC26_REG_AUDIO_CTRL2 AIC26_PAGE_ADDR(2, 0x04)
+#define AIC26_REG_POWER_CTRL AIC26_PAGE_ADDR(2, 0x05)
+#define AIC26_REG_AUDIO_CTRL3 AIC26_PAGE_ADDR(2, 0x06)
+
+#define AIC26_REG_FILTER_COEFF_L_N0 AIC26_PAGE_ADDR(2, 0x07)
+#define AIC26_REG_FILTER_COEFF_L_N1 AIC26_PAGE_ADDR(2, 0x08)
+#define AIC26_REG_FILTER_COEFF_L_N2 AIC26_PAGE_ADDR(2, 0x09)
+#define AIC26_REG_FILTER_COEFF_L_N3 AIC26_PAGE_ADDR(2, 0x0A)
+#define AIC26_REG_FILTER_COEFF_L_N4 AIC26_PAGE_ADDR(2, 0x0B)
+#define AIC26_REG_FILTER_COEFF_L_N5 AIC26_PAGE_ADDR(2, 0x0C)
+#define AIC26_REG_FILTER_COEFF_L_D1 AIC26_PAGE_ADDR(2, 0x0D)
+#define AIC26_REG_FILTER_COEFF_L_D2 AIC26_PAGE_ADDR(2, 0x0E)
+#define AIC26_REG_FILTER_COEFF_L_D4 AIC26_PAGE_ADDR(2, 0x0F)
+#define AIC26_REG_FILTER_COEFF_L_D5 AIC26_PAGE_ADDR(2, 0x10)
+#define AIC26_REG_FILTER_COEFF_R_N0 AIC26_PAGE_ADDR(2, 0x11)
+#define AIC26_REG_FILTER_COEFF_R_N1 AIC26_PAGE_ADDR(2, 0x12)
+#define AIC26_REG_FILTER_COEFF_R_N2 AIC26_PAGE_ADDR(2, 0x13)
+#define AIC26_REG_FILTER_COEFF_R_N3 AIC26_PAGE_ADDR(2, 0x14)
+#define AIC26_REG_FILTER_COEFF_R_N4 AIC26_PAGE_ADDR(2, 0x15)
+#define AIC26_REG_FILTER_COEFF_R_N5 AIC26_PAGE_ADDR(2, 0x16)
+#define AIC26_REG_FILTER_COEFF_R_D1 AIC26_PAGE_ADDR(2, 0x17)
+#define AIC26_REG_FILTER_COEFF_R_D2 AIC26_PAGE_ADDR(2, 0x18)
+#define AIC26_REG_FILTER_COEFF_R_D4 AIC26_PAGE_ADDR(2, 0x19)
+#define AIC26_REG_FILTER_COEFF_R_D5 AIC26_PAGE_ADDR(2, 0x1A)
+
+#define AIC26_REG_PLL_PROG1 AIC26_PAGE_ADDR(2, 0x1B)
+#define AIC26_REG_PLL_PROG2 AIC26_PAGE_ADDR(2, 0x1C)
+#define AIC26_REG_AUDIO_CTRL4 AIC26_PAGE_ADDR(2, 0x1D)
+#define AIC26_REG_AUDIO_CTRL5 AIC26_PAGE_ADDR(2, 0x1E)
+
+/* fsref dividers; used in register 'Audio Control 1' */
+enum aic26_divisors {
+ AIC26_DIV_1 = 0,
+ AIC26_DIV_1_5 = 1,
+ AIC26_DIV_2 = 2,
+ AIC26_DIV_3 = 3,
+ AIC26_DIV_4 = 4,
+ AIC26_DIV_5 = 5,
+ AIC26_DIV_5_5 = 6,
+ AIC26_DIV_6 = 7,
+};
+
+/* Digital data format */
+enum aic26_datfm {
+ AIC26_DATFM_I2S = 0 << 8,
+ AIC26_DATFM_DSP = 1 << 8,
+ AIC26_DATFM_RIGHTJ = 2 << 8, /* right justified */
+ AIC26_DATFM_LEFTJ = 3 << 8, /* left justified */
+};
+
+/* Sample word length in bits; used in register 'Audio Control 1' */
+enum aic26_wlen {
+ AIC26_WLEN_16 = 0 << 10,
+ AIC26_WLEN_20 = 1 << 10,
+ AIC26_WLEN_24 = 2 << 10,
+ AIC26_WLEN_32 = 3 << 10,
+};
+
+#endif /* _TLV320AIC16_H_ */
diff --git a/kernel/sound/soc/codecs/tlv320aic31xx.c b/kernel/sound/soc/codecs/tlv320aic31xx.c
new file mode 100644
index 000000000..c86dd9aae
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320aic31xx.c
@@ -0,0 +1,1299 @@
+/*
+ * ALSA SoC TLV320AIC31XX codec driver
+ *
+ * Copyright (C) 2014 Texas Instruments, Inc.
+ *
+ * Author: Jyri Sarha <jsarha@ti.com>
+ *
+ * Based on ground work by: Ajit Kulkarni <x0175765@ti.com>
+ *
+ * This package 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 PACKAGE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * The TLV320AIC31xx series of audio codec is a low-power, highly integrated
+ * high performance codec which provides a stereo DAC, a mono ADC,
+ * and mono/stereo Class-D speaker driver.
+ */
+
+#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/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
+#include <linux/of_gpio.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 <dt-bindings/sound/tlv320aic31xx-micbias.h>
+
+#include "tlv320aic31xx.h"
+
+static const struct reg_default aic31xx_reg_defaults[] = {
+ { AIC31XX_CLKMUX, 0x00 },
+ { AIC31XX_PLLPR, 0x11 },
+ { AIC31XX_PLLJ, 0x04 },
+ { AIC31XX_PLLDMSB, 0x00 },
+ { AIC31XX_PLLDLSB, 0x00 },
+ { AIC31XX_NDAC, 0x01 },
+ { AIC31XX_MDAC, 0x01 },
+ { AIC31XX_DOSRMSB, 0x00 },
+ { AIC31XX_DOSRLSB, 0x80 },
+ { AIC31XX_NADC, 0x01 },
+ { AIC31XX_MADC, 0x01 },
+ { AIC31XX_AOSR, 0x80 },
+ { AIC31XX_IFACE1, 0x00 },
+ { AIC31XX_DATA_OFFSET, 0x00 },
+ { AIC31XX_IFACE2, 0x00 },
+ { AIC31XX_BCLKN, 0x01 },
+ { AIC31XX_DACSETUP, 0x14 },
+ { AIC31XX_DACMUTE, 0x0c },
+ { AIC31XX_LDACVOL, 0x00 },
+ { AIC31XX_RDACVOL, 0x00 },
+ { AIC31XX_ADCSETUP, 0x00 },
+ { AIC31XX_ADCFGA, 0x80 },
+ { AIC31XX_ADCVOL, 0x00 },
+ { AIC31XX_HPDRIVER, 0x04 },
+ { AIC31XX_SPKAMP, 0x06 },
+ { AIC31XX_DACMIXERROUTE, 0x00 },
+ { AIC31XX_LANALOGHPL, 0x7f },
+ { AIC31XX_RANALOGHPR, 0x7f },
+ { AIC31XX_LANALOGSPL, 0x7f },
+ { AIC31XX_RANALOGSPR, 0x7f },
+ { AIC31XX_HPLGAIN, 0x02 },
+ { AIC31XX_HPRGAIN, 0x02 },
+ { AIC31XX_SPLGAIN, 0x00 },
+ { AIC31XX_SPRGAIN, 0x00 },
+ { AIC31XX_MICBIAS, 0x00 },
+ { AIC31XX_MICPGA, 0x80 },
+ { AIC31XX_MICPGAPI, 0x00 },
+ { AIC31XX_MICPGAMI, 0x00 },
+};
+
+static bool aic31xx_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AIC31XX_PAGECTL: /* regmap implementation requires this */
+ case AIC31XX_RESET: /* always clears after write */
+ case AIC31XX_OT_FLAG:
+ case AIC31XX_ADCFLAG:
+ case AIC31XX_DACFLAG1:
+ case AIC31XX_DACFLAG2:
+ case AIC31XX_OFFLAG: /* Sticky interrupt flags */
+ case AIC31XX_INTRDACFLAG: /* Sticky interrupt flags */
+ case AIC31XX_INTRADCFLAG: /* Sticky interrupt flags */
+ case AIC31XX_INTRDACFLAG2:
+ case AIC31XX_INTRADCFLAG2:
+ return true;
+ }
+ return false;
+}
+
+static bool aic31xx_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AIC31XX_OT_FLAG:
+ case AIC31XX_ADCFLAG:
+ case AIC31XX_DACFLAG1:
+ case AIC31XX_DACFLAG2:
+ case AIC31XX_OFFLAG: /* Sticky interrupt flags */
+ case AIC31XX_INTRDACFLAG: /* Sticky interrupt flags */
+ case AIC31XX_INTRADCFLAG: /* Sticky interrupt flags */
+ case AIC31XX_INTRDACFLAG2:
+ case AIC31XX_INTRADCFLAG2:
+ return false;
+ }
+ return true;
+}
+
+static const struct regmap_range_cfg aic31xx_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 12 * 128,
+ .selector_reg = AIC31XX_PAGECTL,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config aic31xx_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = aic31xx_writeable,
+ .volatile_reg = aic31xx_volatile,
+ .reg_defaults = aic31xx_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(aic31xx_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .ranges = aic31xx_ranges,
+ .num_ranges = ARRAY_SIZE(aic31xx_ranges),
+ .max_register = 12 * 128,
+};
+
+#define AIC31XX_NUM_SUPPLIES 6
+static const char * const aic31xx_supply_names[AIC31XX_NUM_SUPPLIES] = {
+ "HPVDD",
+ "SPRVDD",
+ "SPLVDD",
+ "AVDD",
+ "IOVDD",
+ "DVDD",
+};
+
+struct aic31xx_disable_nb {
+ struct notifier_block nb;
+ struct aic31xx_priv *aic31xx;
+};
+
+struct aic31xx_priv {
+ struct snd_soc_codec *codec;
+ u8 i2c_regs_status;
+ struct device *dev;
+ struct regmap *regmap;
+ struct aic31xx_pdata pdata;
+ struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES];
+ struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES];
+ unsigned int sysclk;
+ u8 p_div;
+ int rate_div_line;
+};
+
+struct aic31xx_rate_divs {
+ u32 mclk_p;
+ u32 rate;
+ u8 pll_j;
+ u16 pll_d;
+ u16 dosr;
+ u8 ndac;
+ u8 mdac;
+ u8 aosr;
+ u8 nadc;
+ u8 madc;
+};
+
+/* ADC dividers can be disabled by cofiguring them to 0 */
+static const struct aic31xx_rate_divs aic31xx_divs[] = {
+ /* mclk/p rate pll: j d dosr ndac mdac aors nadc madc */
+ /* 8k rate */
+ {12000000, 8000, 8, 1920, 128, 48, 2, 128, 48, 2},
+ {12000000, 8000, 8, 1920, 128, 32, 3, 128, 32, 3},
+ {12500000, 8000, 7, 8643, 128, 48, 2, 128, 48, 2},
+ /* 11.025k rate */
+ {12000000, 11025, 7, 5264, 128, 32, 2, 128, 32, 2},
+ {12000000, 11025, 8, 4672, 128, 24, 3, 128, 24, 3},
+ {12500000, 11025, 7, 2253, 128, 32, 2, 128, 32, 2},
+ /* 16k rate */
+ {12000000, 16000, 8, 1920, 128, 24, 2, 128, 24, 2},
+ {12000000, 16000, 8, 1920, 128, 16, 3, 128, 16, 3},
+ {12500000, 16000, 7, 8643, 128, 24, 2, 128, 24, 2},
+ /* 22.05k rate */
+ {12000000, 22050, 7, 5264, 128, 16, 2, 128, 16, 2},
+ {12000000, 22050, 8, 4672, 128, 12, 3, 128, 12, 3},
+ {12500000, 22050, 7, 2253, 128, 16, 2, 128, 16, 2},
+ /* 32k rate */
+ {12000000, 32000, 8, 1920, 128, 12, 2, 128, 12, 2},
+ {12000000, 32000, 8, 1920, 128, 8, 3, 128, 8, 3},
+ {12500000, 32000, 7, 8643, 128, 12, 2, 128, 12, 2},
+ /* 44.1k rate */
+ {12000000, 44100, 7, 5264, 128, 8, 2, 128, 8, 2},
+ {12000000, 44100, 8, 4672, 128, 6, 3, 128, 6, 3},
+ {12500000, 44100, 7, 2253, 128, 8, 2, 128, 8, 2},
+ /* 48k rate */
+ {12000000, 48000, 8, 1920, 128, 8, 2, 128, 8, 2},
+ {12000000, 48000, 7, 6800, 96, 5, 4, 96, 5, 4},
+ {12500000, 48000, 7, 8643, 128, 8, 2, 128, 8, 2},
+ /* 88.2k rate */
+ {12000000, 88200, 7, 5264, 64, 8, 2, 64, 8, 2},
+ {12000000, 88200, 8, 4672, 64, 6, 3, 64, 6, 3},
+ {12500000, 88200, 7, 2253, 64, 8, 2, 64, 8, 2},
+ /* 96k rate */
+ {12000000, 96000, 8, 1920, 64, 8, 2, 64, 8, 2},
+ {12000000, 96000, 7, 6800, 48, 5, 4, 48, 5, 4},
+ {12500000, 96000, 7, 8643, 64, 8, 2, 64, 8, 2},
+ /* 176.4k rate */
+ {12000000, 176400, 7, 5264, 32, 8, 2, 32, 8, 2},
+ {12000000, 176400, 8, 4672, 32, 6, 3, 32, 6, 3},
+ {12500000, 176400, 7, 2253, 32, 8, 2, 32, 8, 2},
+ /* 192k rate */
+ {12000000, 192000, 8, 1920, 32, 8, 2, 32, 8, 2},
+ {12000000, 192000, 7, 6800, 24, 5, 4, 24, 5, 4},
+ {12500000, 192000, 7, 8643, 32, 8, 2, 32, 8, 2},
+};
+
+static const char * const ldac_in_text[] = {
+ "Off", "Left Data", "Right Data", "Mono"
+};
+
+static const char * const rdac_in_text[] = {
+ "Off", "Right Data", "Left Data", "Mono"
+};
+
+static SOC_ENUM_SINGLE_DECL(ldac_in_enum, AIC31XX_DACSETUP, 4, ldac_in_text);
+
+static SOC_ENUM_SINGLE_DECL(rdac_in_enum, AIC31XX_DACSETUP, 2, rdac_in_text);
+
+static const char * const mic_select_text[] = {
+ "Off", "FFR 10 Ohm", "FFR 20 Ohm", "FFR 40 Ohm"
+};
+
+static SOC_ENUM_SINGLE_DECL(mic1lp_p_enum, AIC31XX_MICPGAPI, 6,
+ mic_select_text);
+static SOC_ENUM_SINGLE_DECL(mic1rp_p_enum, AIC31XX_MICPGAPI, 4,
+ mic_select_text);
+static SOC_ENUM_SINGLE_DECL(mic1lm_p_enum, AIC31XX_MICPGAPI, 2,
+ mic_select_text);
+
+static SOC_ENUM_SINGLE_DECL(cm_m_enum, AIC31XX_MICPGAMI, 6, mic_select_text);
+static SOC_ENUM_SINGLE_DECL(mic1lm_m_enum, AIC31XX_MICPGAMI, 4,
+ mic_select_text);
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0);
+static const DECLARE_TLV_DB_SCALE(adc_fgain_tlv, 0, 10, 0);
+static const DECLARE_TLV_DB_SCALE(adc_cgain_tlv, -2000, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_pga_tlv, 0, 50, 0);
+static const DECLARE_TLV_DB_SCALE(hp_drv_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(class_D_drv_tlv, 600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -6350, 50, 0);
+static const DECLARE_TLV_DB_SCALE(sp_vol_tlv, -6350, 50, 0);
+
+/*
+ * controls to be exported to the user space
+ */
+static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
+ SOC_DOUBLE_R_S_TLV("DAC Playback Volume", AIC31XX_LDACVOL,
+ AIC31XX_RDACVOL, 0, -127, 48, 7, 0, dac_vol_tlv),
+
+ SOC_SINGLE_TLV("ADC Fine Capture Volume", AIC31XX_ADCFGA, 4, 4, 1,
+ adc_fgain_tlv),
+
+ SOC_SINGLE("ADC Capture Switch", AIC31XX_ADCFGA, 7, 1, 1),
+ SOC_DOUBLE_R_S_TLV("ADC Capture Volume", AIC31XX_ADCVOL, AIC31XX_ADCVOL,
+ 0, -24, 40, 6, 0, adc_cgain_tlv),
+
+ SOC_SINGLE_TLV("Mic PGA Capture Volume", AIC31XX_MICPGA, 0,
+ 119, 0, mic_pga_tlv),
+
+ SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,
+ AIC31XX_HPRGAIN, 2, 1, 0),
+ SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,
+ AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),
+
+ SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,
+ AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),
+};
+
+static const struct snd_kcontrol_new aic311x_snd_controls[] = {
+ SOC_DOUBLE_R("Speaker Driver Playback Switch", AIC31XX_SPLGAIN,
+ AIC31XX_SPRGAIN, 2, 1, 0),
+ SOC_DOUBLE_R_TLV("Speaker Driver Playback Volume", AIC31XX_SPLGAIN,
+ AIC31XX_SPRGAIN, 3, 3, 0, class_D_drv_tlv),
+
+ SOC_DOUBLE_R_TLV("Speaker Analog Playback Volume", AIC31XX_LANALOGSPL,
+ AIC31XX_RANALOGSPR, 0, 0x7F, 1, sp_vol_tlv),
+};
+
+static const struct snd_kcontrol_new aic310x_snd_controls[] = {
+ SOC_SINGLE("Speaker Driver Playback Switch", AIC31XX_SPLGAIN,
+ 2, 1, 0),
+ SOC_SINGLE_TLV("Speaker Driver Playback Volume", AIC31XX_SPLGAIN,
+ 3, 3, 0, class_D_drv_tlv),
+
+ SOC_SINGLE_TLV("Speaker Analog Playback Volume", AIC31XX_LANALOGSPL,
+ 0, 0x7F, 1, sp_vol_tlv),
+};
+
+static const struct snd_kcontrol_new ldac_in_control =
+ SOC_DAPM_ENUM("DAC Left Input", ldac_in_enum);
+
+static const struct snd_kcontrol_new rdac_in_control =
+ SOC_DAPM_ENUM("DAC Right Input", rdac_in_enum);
+
+static int aic31xx_wait_bits(struct aic31xx_priv *aic31xx, unsigned int reg,
+ unsigned int mask, unsigned int wbits, int sleep,
+ int count)
+{
+ unsigned int bits;
+ int counter = count;
+ int ret = regmap_read(aic31xx->regmap, reg, &bits);
+
+ while ((bits & mask) != wbits && counter && !ret) {
+ usleep_range(sleep, sleep * 2);
+ ret = regmap_read(aic31xx->regmap, reg, &bits);
+ counter--;
+ }
+ if ((bits & mask) != wbits) {
+ dev_err(aic31xx->dev,
+ "%s: Failed! 0x%x was 0x%x expected 0x%x (%d, 0x%x, %d us)\n",
+ __func__, reg, bits, wbits, ret, mask,
+ (count - counter) * sleep);
+ ret = -1;
+ }
+ return ret;
+}
+
+#define WIDGET_BIT(reg, shift) (((shift) << 8) | (reg))
+
+static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg = AIC31XX_DACFLAG1;
+ unsigned int mask;
+
+ switch (WIDGET_BIT(w->reg, w->shift)) {
+ case WIDGET_BIT(AIC31XX_DACSETUP, 7):
+ mask = AIC31XX_LDACPWRSTATUS_MASK;
+ break;
+ case WIDGET_BIT(AIC31XX_DACSETUP, 6):
+ mask = AIC31XX_RDACPWRSTATUS_MASK;
+ break;
+ case WIDGET_BIT(AIC31XX_HPDRIVER, 7):
+ mask = AIC31XX_HPLDRVPWRSTATUS_MASK;
+ break;
+ case WIDGET_BIT(AIC31XX_HPDRIVER, 6):
+ mask = AIC31XX_HPRDRVPWRSTATUS_MASK;
+ break;
+ case WIDGET_BIT(AIC31XX_SPKAMP, 7):
+ mask = AIC31XX_SPLDRVPWRSTATUS_MASK;
+ break;
+ case WIDGET_BIT(AIC31XX_SPKAMP, 6):
+ mask = AIC31XX_SPRDRVPWRSTATUS_MASK;
+ break;
+ case WIDGET_BIT(AIC31XX_ADCSETUP, 7):
+ mask = AIC31XX_ADCPWRSTATUS_MASK;
+ reg = AIC31XX_ADCFLAG;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown widget '%s' calling %s\n",
+ w->name, __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return aic31xx_wait_bits(aic31xx, reg, mask, mask, 5000, 100);
+ case SND_SOC_DAPM_POST_PMD:
+ return aic31xx_wait_bits(aic31xx, reg, mask, 0, 5000, 100);
+ default:
+ dev_dbg(codec->dev,
+ "Unhandled dapm widget event %d from %s\n",
+ event, w->name);
+ }
+ return 0;
+}
+
+static const struct snd_kcontrol_new left_output_switches[] = {
+ SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
+ SOC_DAPM_SINGLE("From MIC1LP", AIC31XX_DACMIXERROUTE, 5, 1, 0),
+ SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_output_switches[] = {
+ SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
+ SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new p_term_mic1lp =
+ SOC_DAPM_ENUM("MIC1LP P-Terminal", mic1lp_p_enum);
+
+static const struct snd_kcontrol_new p_term_mic1rp =
+ SOC_DAPM_ENUM("MIC1RP P-Terminal", mic1rp_p_enum);
+
+static const struct snd_kcontrol_new p_term_mic1lm =
+ SOC_DAPM_ENUM("MIC1LM P-Terminal", mic1lm_p_enum);
+
+static const struct snd_kcontrol_new m_term_mic1lm =
+ SOC_DAPM_ENUM("MIC1LM M-Terminal", mic1lm_m_enum);
+
+static const struct snd_kcontrol_new aic31xx_dapm_hpl_switch =
+ SOC_DAPM_SINGLE("Switch", AIC31XX_LANALOGHPL, 7, 1, 0);
+
+static const struct snd_kcontrol_new aic31xx_dapm_hpr_switch =
+ SOC_DAPM_SINGLE("Switch", AIC31XX_RANALOGHPR, 7, 1, 0);
+
+static const struct snd_kcontrol_new aic31xx_dapm_spl_switch =
+ SOC_DAPM_SINGLE("Switch", AIC31XX_LANALOGSPL, 7, 1, 0);
+
+static const struct snd_kcontrol_new aic31xx_dapm_spr_switch =
+ SOC_DAPM_SINGLE("Switch", AIC31XX_RANALOGSPR, 7, 1, 0);
+
+static int mic_bias_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* change mic bias voltage to user defined */
+ snd_soc_update_bits(codec, AIC31XX_MICBIAS,
+ AIC31XX_MICBIAS_MASK,
+ aic31xx->pdata.micbias_vg <<
+ AIC31XX_MICBIAS_SHIFT);
+ dev_dbg(codec->dev, "%s: turned on\n", __func__);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* turn mic bias off */
+ snd_soc_update_bits(codec, AIC31XX_MICBIAS,
+ AIC31XX_MICBIAS_MASK, 0);
+ dev_dbg(codec->dev, "%s: turned off\n", __func__);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("DAC Left Input",
+ SND_SOC_NOPM, 0, 0, &ldac_in_control),
+ SND_SOC_DAPM_MUX("DAC Right Input",
+ SND_SOC_NOPM, 0, 0, &rdac_in_control),
+ /* DACs */
+ SND_SOC_DAPM_DAC_E("DAC Left", "Left Playback",
+ AIC31XX_DACSETUP, 7, 0, aic31xx_dapm_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_DAC_E("DAC Right", "Right Playback",
+ AIC31XX_DACSETUP, 6, 0, aic31xx_dapm_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
+ left_output_switches,
+ ARRAY_SIZE(left_output_switches)),
+ SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
+ right_output_switches,
+ ARRAY_SIZE(right_output_switches)),
+
+ SND_SOC_DAPM_SWITCH("HP Left", SND_SOC_NOPM, 0, 0,
+ &aic31xx_dapm_hpl_switch),
+ SND_SOC_DAPM_SWITCH("HP Right", SND_SOC_NOPM, 0, 0,
+ &aic31xx_dapm_hpr_switch),
+
+ /* Output drivers */
+ SND_SOC_DAPM_OUT_DRV_E("HPL Driver", AIC31XX_HPDRIVER, 7, 0,
+ NULL, 0, aic31xx_dapm_power_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUT_DRV_E("HPR Driver", AIC31XX_HPDRIVER, 6, 0,
+ NULL, 0, aic31xx_dapm_power_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,
+ aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* Input Selection to MIC_PGA */
+ SND_SOC_DAPM_MUX("MIC1LP P-Terminal", SND_SOC_NOPM, 0, 0,
+ &p_term_mic1lp),
+ SND_SOC_DAPM_MUX("MIC1RP P-Terminal", SND_SOC_NOPM, 0, 0,
+ &p_term_mic1rp),
+ SND_SOC_DAPM_MUX("MIC1LM P-Terminal", SND_SOC_NOPM, 0, 0,
+ &p_term_mic1lm),
+
+ SND_SOC_DAPM_MUX("MIC1LM M-Terminal", SND_SOC_NOPM, 0, 0,
+ &m_term_mic1lm),
+ /* Enabling & Disabling MIC Gain Ctl */
+ SND_SOC_DAPM_PGA("MIC_GAIN_CTL", AIC31XX_MICPGA,
+ 7, 1, NULL, 0),
+
+ /* Mic Bias */
+ SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MIC1LP"),
+ SND_SOC_DAPM_INPUT("MIC1RP"),
+ SND_SOC_DAPM_INPUT("MIC1LM"),
+};
+
+static const struct snd_soc_dapm_widget aic311x_dapm_widgets[] = {
+ /* AIC3111 and AIC3110 have stereo class-D amplifier */
+ SND_SOC_DAPM_OUT_DRV_E("SPL ClassD", AIC31XX_SPKAMP, 7, 0, NULL, 0,
+ aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("SPR ClassD", AIC31XX_SPKAMP, 6, 0, NULL, 0,
+ aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("Speaker Left", SND_SOC_NOPM, 0, 0,
+ &aic31xx_dapm_spl_switch),
+ SND_SOC_DAPM_SWITCH("Speaker Right", SND_SOC_NOPM, 0, 0,
+ &aic31xx_dapm_spr_switch),
+ SND_SOC_DAPM_OUTPUT("SPL"),
+ SND_SOC_DAPM_OUTPUT("SPR"),
+};
+
+/* AIC3100 and AIC3120 have only mono class-D amplifier */
+static const struct snd_soc_dapm_widget aic310x_dapm_widgets[] = {
+ SND_SOC_DAPM_OUT_DRV_E("SPK ClassD", AIC31XX_SPKAMP, 7, 0, NULL, 0,
+ aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("Speaker", SND_SOC_NOPM, 0, 0,
+ &aic31xx_dapm_spl_switch),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+};
+
+static const struct snd_soc_dapm_route
+aic31xx_audio_map[] = {
+ /* DAC Input Routing */
+ {"DAC Left Input", "Left Data", "DAC IN"},
+ {"DAC Left Input", "Right Data", "DAC IN"},
+ {"DAC Left Input", "Mono", "DAC IN"},
+ {"DAC Right Input", "Left Data", "DAC IN"},
+ {"DAC Right Input", "Right Data", "DAC IN"},
+ {"DAC Right Input", "Mono", "DAC IN"},
+ {"DAC Left", NULL, "DAC Left Input"},
+ {"DAC Right", NULL, "DAC Right Input"},
+
+ /* Mic input */
+ {"MIC1LP P-Terminal", "FFR 10 Ohm", "MIC1LP"},
+ {"MIC1LP P-Terminal", "FFR 20 Ohm", "MIC1LP"},
+ {"MIC1LP P-Terminal", "FFR 40 Ohm", "MIC1LP"},
+ {"MIC1RP P-Terminal", "FFR 10 Ohm", "MIC1RP"},
+ {"MIC1RP P-Terminal", "FFR 20 Ohm", "MIC1RP"},
+ {"MIC1RP P-Terminal", "FFR 40 Ohm", "MIC1RP"},
+ {"MIC1LM P-Terminal", "FFR 10 Ohm", "MIC1LM"},
+ {"MIC1LM P-Terminal", "FFR 20 Ohm", "MIC1LM"},
+ {"MIC1LM P-Terminal", "FFR 40 Ohm", "MIC1LM"},
+
+ {"MIC1LM M-Terminal", "FFR 10 Ohm", "MIC1LM"},
+ {"MIC1LM M-Terminal", "FFR 20 Ohm", "MIC1LM"},
+ {"MIC1LM M-Terminal", "FFR 40 Ohm", "MIC1LM"},
+
+ {"MIC_GAIN_CTL", NULL, "MIC1LP P-Terminal"},
+ {"MIC_GAIN_CTL", NULL, "MIC1RP P-Terminal"},
+ {"MIC_GAIN_CTL", NULL, "MIC1LM P-Terminal"},
+ {"MIC_GAIN_CTL", NULL, "MIC1LM M-Terminal"},
+
+ {"ADC", NULL, "MIC_GAIN_CTL"},
+
+ /* Left Output */
+ {"Output Left", "From Left DAC", "DAC Left"},
+ {"Output Left", "From MIC1LP", "MIC1LP"},
+ {"Output Left", "From MIC1RP", "MIC1RP"},
+
+ /* Right Output */
+ {"Output Right", "From Right DAC", "DAC Right"},
+ {"Output Right", "From MIC1RP", "MIC1RP"},
+
+ /* HPL path */
+ {"HP Left", "Switch", "Output Left"},
+ {"HPL Driver", NULL, "HP Left"},
+ {"HPL", NULL, "HPL Driver"},
+
+ /* HPR path */
+ {"HP Right", "Switch", "Output Right"},
+ {"HPR Driver", NULL, "HP Right"},
+ {"HPR", NULL, "HPR Driver"},
+};
+
+static const struct snd_soc_dapm_route
+aic311x_audio_map[] = {
+ /* SP L path */
+ {"Speaker Left", "Switch", "Output Left"},
+ {"SPL ClassD", NULL, "Speaker Left"},
+ {"SPL", NULL, "SPL ClassD"},
+
+ /* SP R path */
+ {"Speaker Right", "Switch", "Output Right"},
+ {"SPR ClassD", NULL, "Speaker Right"},
+ {"SPR", NULL, "SPR ClassD"},
+};
+
+static const struct snd_soc_dapm_route
+aic310x_audio_map[] = {
+ /* SP L path */
+ {"Speaker", "Switch", "Output Left"},
+ {"SPK ClassD", NULL, "Speaker"},
+ {"SPK", NULL, "SPK ClassD"},
+};
+
+static int aic31xx_add_controls(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+
+ if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT)
+ ret = snd_soc_add_codec_controls(
+ codec, aic311x_snd_controls,
+ ARRAY_SIZE(aic311x_snd_controls));
+ else
+ ret = snd_soc_add_codec_controls(
+ codec, aic310x_snd_controls,
+ ARRAY_SIZE(aic310x_snd_controls));
+
+ return ret;
+}
+
+static int aic31xx_add_widgets(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT) {
+ ret = snd_soc_dapm_new_controls(
+ dapm, aic311x_dapm_widgets,
+ ARRAY_SIZE(aic311x_dapm_widgets));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, aic311x_audio_map,
+ ARRAY_SIZE(aic311x_audio_map));
+ if (ret)
+ return ret;
+ } else {
+ ret = snd_soc_dapm_new_controls(
+ dapm, aic310x_dapm_widgets,
+ ARRAY_SIZE(aic310x_dapm_widgets));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, aic310x_audio_map,
+ ARRAY_SIZE(aic310x_audio_map));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aic31xx_setup_pll(struct snd_soc_codec *codec,
+ struct snd_pcm_hw_params *params)
+{
+ struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ int bclk_score = snd_soc_params_to_frame_size(params);
+ int mclk_p = aic31xx->sysclk / aic31xx->p_div;
+ int bclk_n = 0;
+ int match = -1;
+ int i;
+
+ /* Use PLL as CODEC_CLKIN and DAC_CLK as BDIV_CLKIN */
+ snd_soc_update_bits(codec, AIC31XX_CLKMUX,
+ AIC31XX_CODEC_CLKIN_MASK, AIC31XX_CODEC_CLKIN_PLL);
+ snd_soc_update_bits(codec, AIC31XX_IFACE2,
+ AIC31XX_BDIVCLK_MASK, AIC31XX_DAC2BCLK);
+
+ for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++) {
+ if (aic31xx_divs[i].rate == params_rate(params) &&
+ aic31xx_divs[i].mclk_p == mclk_p) {
+ int s = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) %
+ snd_soc_params_to_frame_size(params);
+ int bn = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) /
+ snd_soc_params_to_frame_size(params);
+ if (s < bclk_score && bn > 0) {
+ match = i;
+ bclk_n = bn;
+ bclk_score = s;
+ }
+ }
+ }
+
+ if (match == -1) {
+ dev_err(codec->dev,
+ "%s: Sample rate (%u) and format not supported\n",
+ __func__, params_rate(params));
+ /* See bellow for details how fix this. */
+ return -EINVAL;
+ }
+ if (bclk_score != 0) {
+ dev_warn(codec->dev, "Can not produce exact bitclock");
+ /* This is fine if using dsp format, but if using i2s
+ there may be trouble. To fix the issue edit the
+ aic31xx_divs table for your mclk and sample
+ rate. Details can be found from:
+ http://www.ti.com/lit/ds/symlink/tlv320aic3100.pdf
+ Section: 5.6 CLOCK Generation and PLL
+ */
+ }
+ i = match;
+
+ /* PLL configuration */
+ snd_soc_update_bits(codec, AIC31XX_PLLPR, AIC31XX_PLL_MASK,
+ (aic31xx->p_div << 4) | 0x01);
+ snd_soc_write(codec, AIC31XX_PLLJ, aic31xx_divs[i].pll_j);
+
+ snd_soc_write(codec, AIC31XX_PLLDMSB,
+ aic31xx_divs[i].pll_d >> 8);
+ snd_soc_write(codec, AIC31XX_PLLDLSB,
+ aic31xx_divs[i].pll_d & 0xff);
+
+ /* DAC dividers configuration */
+ snd_soc_update_bits(codec, AIC31XX_NDAC, AIC31XX_PLL_MASK,
+ aic31xx_divs[i].ndac);
+ snd_soc_update_bits(codec, AIC31XX_MDAC, AIC31XX_PLL_MASK,
+ aic31xx_divs[i].mdac);
+
+ snd_soc_write(codec, AIC31XX_DOSRMSB, aic31xx_divs[i].dosr >> 8);
+ snd_soc_write(codec, AIC31XX_DOSRLSB, aic31xx_divs[i].dosr & 0xff);
+
+ /* ADC dividers configuration. Write reset value 1 if not used. */
+ snd_soc_update_bits(codec, AIC31XX_NADC, AIC31XX_PLL_MASK,
+ aic31xx_divs[i].nadc ? aic31xx_divs[i].nadc : 1);
+ snd_soc_update_bits(codec, AIC31XX_MADC, AIC31XX_PLL_MASK,
+ aic31xx_divs[i].madc ? aic31xx_divs[i].madc : 1);
+
+ snd_soc_write(codec, AIC31XX_AOSR, aic31xx_divs[i].aosr);
+
+ /* Bit clock divider configuration. */
+ snd_soc_update_bits(codec, AIC31XX_BCLKN,
+ AIC31XX_PLL_MASK, bclk_n);
+
+ aic31xx->rate_div_line = i;
+
+ dev_dbg(codec->dev,
+ "pll %d.%04d/%d dosr %d n %d m %d aosr %d n %d m %d bclk_n %d\n",
+ aic31xx_divs[i].pll_j, aic31xx_divs[i].pll_d,
+ aic31xx->p_div, aic31xx_divs[i].dosr,
+ aic31xx_divs[i].ndac, aic31xx_divs[i].mdac,
+ aic31xx_divs[i].aosr, aic31xx_divs[i].nadc,
+ aic31xx_divs[i].madc, bclk_n);
+
+ return 0;
+}
+
+static int aic31xx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 data = 0;
+
+ dev_dbg(codec->dev, "## %s: width %d rate %d\n",
+ __func__, params_width(params),
+ params_rate(params));
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ data = (AIC31XX_WORD_LEN_20BITS <<
+ AIC31XX_IFACE1_DATALEN_SHIFT);
+ break;
+ case 24:
+ data = (AIC31XX_WORD_LEN_24BITS <<
+ AIC31XX_IFACE1_DATALEN_SHIFT);
+ break;
+ case 32:
+ data = (AIC31XX_WORD_LEN_32BITS <<
+ AIC31XX_IFACE1_DATALEN_SHIFT);
+ break;
+ default:
+ dev_err(codec->dev, "%s: Unsupported width %d\n",
+ __func__, params_width(params));
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, AIC31XX_IFACE1,
+ AIC31XX_IFACE1_DATALEN_MASK,
+ data);
+
+ return aic31xx_setup_pll(codec, params);
+}
+
+static int aic31xx_dac_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ if (mute) {
+ snd_soc_update_bits(codec, AIC31XX_DACMUTE,
+ AIC31XX_DACMUTE_MASK,
+ AIC31XX_DACMUTE_MASK);
+ } else {
+ snd_soc_update_bits(codec, AIC31XX_DACMUTE,
+ AIC31XX_DACMUTE_MASK, 0x0);
+ }
+
+ return 0;
+}
+
+static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 iface_reg1 = 0;
+ u8 iface_reg2 = 0;
+ u8 dsp_a_val = 0;
+
+ dev_dbg(codec->dev, "## %s: fmt = 0x%x\n", __func__, fmt);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface_reg1 |= AIC31XX_BCLK_MASTER | AIC31XX_WCLK_MASTER;
+ break;
+ default:
+ dev_alert(codec->dev, "Invalid DAI master/slave interface\n");
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ dsp_a_val = 0x1;
+ case SND_SOC_DAIFMT_DSP_B:
+ /* NOTE: BCLKINV bit value 1 equas NB and 0 equals IB */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ iface_reg2 |= AIC31XX_BCLKINV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+ iface_reg1 |= (AIC31XX_DSP_MODE <<
+ AIC31XX_IFACE1_DATATYPE_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface_reg1 |= (AIC31XX_RIGHT_JUSTIFIED_MODE <<
+ AIC31XX_IFACE1_DATATYPE_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface_reg1 |= (AIC31XX_LEFT_JUSTIFIED_MODE <<
+ AIC31XX_IFACE1_DATATYPE_SHIFT);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI interface format\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, AIC31XX_IFACE1,
+ AIC31XX_IFACE1_DATATYPE_MASK |
+ AIC31XX_IFACE1_MASTER_MASK,
+ iface_reg1);
+ snd_soc_update_bits(codec, AIC31XX_DATA_OFFSET,
+ AIC31XX_DATA_OFFSET_MASK,
+ dsp_a_val);
+ snd_soc_update_bits(codec, AIC31XX_IFACE2,
+ AIC31XX_BCLKINV_MASK,
+ iface_reg2);
+
+ return 0;
+}
+
+static int aic31xx_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 aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ dev_dbg(codec->dev, "## %s: clk_id = %d, freq = %d, dir = %d\n",
+ __func__, clk_id, freq, dir);
+
+ for (i = 1; freq/i > 20000000 && i < 8; i++)
+ ;
+ if (freq/i > 20000000) {
+ dev_err(aic31xx->dev, "%s: Too high mclk frequency %u\n",
+ __func__, freq);
+ return -EINVAL;
+ }
+ aic31xx->p_div = i;
+
+ for (i = 0; i < ARRAY_SIZE(aic31xx_divs) &&
+ aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++)
+ ;
+ if (i == ARRAY_SIZE(aic31xx_divs)) {
+ dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
+ __func__, freq);
+ return -EINVAL;
+ }
+
+ /* set clock on MCLK, BCLK, or GPIO1 as PLL input */
+ snd_soc_update_bits(codec, AIC31XX_CLKMUX, AIC31XX_PLL_CLKIN_MASK,
+ clk_id << AIC31XX_PLL_CLKIN_SHIFT);
+
+ aic31xx->sysclk = freq;
+ return 0;
+}
+
+static int aic31xx_regulator_event(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct aic31xx_disable_nb *disable_nb =
+ container_of(nb, struct aic31xx_disable_nb, nb);
+ struct aic31xx_priv *aic31xx = disable_nb->aic31xx;
+
+ if (event & REGULATOR_EVENT_DISABLE) {
+ /*
+ * Put codec to reset and as at least one of the
+ * supplies was disabled.
+ */
+ if (gpio_is_valid(aic31xx->pdata.gpio_reset))
+ gpio_set_value(aic31xx->pdata.gpio_reset, 0);
+
+ regcache_mark_dirty(aic31xx->regmap);
+ dev_dbg(aic31xx->dev, "## %s: DISABLE received\n", __func__);
+ }
+
+ return 0;
+}
+
+static void aic31xx_clk_on(struct snd_soc_codec *codec)
+{
+ struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ u8 mask = AIC31XX_PM_MASK;
+ u8 on = AIC31XX_PM_MASK;
+
+ dev_dbg(codec->dev, "codec clock -> on (rate %d)\n",
+ aic31xx_divs[aic31xx->rate_div_line].rate);
+ snd_soc_update_bits(codec, AIC31XX_PLLPR, mask, on);
+ mdelay(10);
+ snd_soc_update_bits(codec, AIC31XX_NDAC, mask, on);
+ snd_soc_update_bits(codec, AIC31XX_MDAC, mask, on);
+ if (aic31xx_divs[aic31xx->rate_div_line].nadc)
+ snd_soc_update_bits(codec, AIC31XX_NADC, mask, on);
+ if (aic31xx_divs[aic31xx->rate_div_line].madc)
+ snd_soc_update_bits(codec, AIC31XX_MADC, mask, on);
+ snd_soc_update_bits(codec, AIC31XX_BCLKN, mask, on);
+}
+
+static void aic31xx_clk_off(struct snd_soc_codec *codec)
+{
+ u8 mask = AIC31XX_PM_MASK;
+ u8 off = 0;
+
+ dev_dbg(codec->dev, "codec clock -> off\n");
+ snd_soc_update_bits(codec, AIC31XX_BCLKN, mask, off);
+ snd_soc_update_bits(codec, AIC31XX_MADC, mask, off);
+ snd_soc_update_bits(codec, AIC31XX_NADC, mask, off);
+ snd_soc_update_bits(codec, AIC31XX_MDAC, mask, off);
+ snd_soc_update_bits(codec, AIC31XX_NDAC, mask, off);
+ snd_soc_update_bits(codec, AIC31XX_PLLPR, mask, off);
+}
+
+static int aic31xx_power_on(struct snd_soc_codec *codec)
+{
+ struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(aic31xx->supplies),
+ aic31xx->supplies);
+ if (ret)
+ return ret;
+
+ if (gpio_is_valid(aic31xx->pdata.gpio_reset)) {
+ gpio_set_value(aic31xx->pdata.gpio_reset, 1);
+ udelay(100);
+ }
+ regcache_cache_only(aic31xx->regmap, false);
+ ret = regcache_sync(aic31xx->regmap);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to restore cache: %d\n", ret);
+ regcache_cache_only(aic31xx->regmap, true);
+ regulator_bulk_disable(ARRAY_SIZE(aic31xx->supplies),
+ aic31xx->supplies);
+ return ret;
+ }
+ return 0;
+}
+
+static int aic31xx_power_off(struct snd_soc_codec *codec)
+{
+ struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ regcache_cache_only(aic31xx->regmap, true);
+ ret = regulator_bulk_disable(ARRAY_SIZE(aic31xx->supplies),
+ aic31xx->supplies);
+
+ return ret;
+}
+
+static int aic31xx_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ dev_dbg(codec->dev, "## %s: %d -> %d\n", __func__,
+ codec->dapm.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)
+ aic31xx_clk_on(codec);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ switch (codec->dapm.bias_level) {
+ case SND_SOC_BIAS_OFF:
+ aic31xx_power_on(codec);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ aic31xx_clk_off(codec);
+ break;
+ default:
+ BUG();
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
+ aic31xx_power_off(codec);
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int aic31xx_codec_probe(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ dev_dbg(aic31xx->dev, "## %s\n", __func__);
+
+ aic31xx = snd_soc_codec_get_drvdata(codec);
+
+ aic31xx->codec = codec;
+
+ for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) {
+ aic31xx->disable_nb[i].nb.notifier_call =
+ aic31xx_regulator_event;
+ aic31xx->disable_nb[i].aic31xx = aic31xx;
+ ret = regulator_register_notifier(aic31xx->supplies[i].consumer,
+ &aic31xx->disable_nb[i].nb);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to request regulator notifier: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ regcache_cache_only(aic31xx->regmap, true);
+ regcache_mark_dirty(aic31xx->regmap);
+
+ ret = aic31xx_add_controls(codec);
+ if (ret)
+ return ret;
+
+ ret = aic31xx_add_widgets(codec);
+
+ return ret;
+}
+
+static int aic31xx_codec_remove(struct snd_soc_codec *codec)
+{
+ struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++)
+ regulator_unregister_notifier(aic31xx->supplies[i].consumer,
+ &aic31xx->disable_nb[i].nb);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_driver_aic31xx = {
+ .probe = aic31xx_codec_probe,
+ .remove = aic31xx_codec_remove,
+ .set_bias_level = aic31xx_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = aic31xx_snd_controls,
+ .num_controls = ARRAY_SIZE(aic31xx_snd_controls),
+ .dapm_widgets = aic31xx_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aic31xx_dapm_widgets),
+ .dapm_routes = aic31xx_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(aic31xx_audio_map),
+};
+
+static struct snd_soc_dai_ops aic31xx_dai_ops = {
+ .hw_params = aic31xx_hw_params,
+ .set_sysclk = aic31xx_set_dai_sysclk,
+ .set_fmt = aic31xx_set_dai_fmt,
+ .digital_mute = aic31xx_dac_mute,
+};
+
+static struct snd_soc_dai_driver aic31xx_dai_driver[] = {
+ {
+ .name = "tlv320aic31xx-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AIC31XX_RATES,
+ .formats = AIC31XX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AIC31XX_RATES,
+ .formats = AIC31XX_FORMATS,
+ },
+ .ops = &aic31xx_dai_ops,
+ .symmetric_rates = 1,
+ }
+};
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tlv320aic31xx_of_match[] = {
+ { .compatible = "ti,tlv320aic310x" },
+ { .compatible = "ti,tlv320aic311x" },
+ { .compatible = "ti,tlv320aic3100" },
+ { .compatible = "ti,tlv320aic3110" },
+ { .compatible = "ti,tlv320aic3120" },
+ { .compatible = "ti,tlv320aic3111" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tlv320aic31xx_of_match);
+
+static void aic31xx_pdata_from_of(struct aic31xx_priv *aic31xx)
+{
+ struct device_node *np = aic31xx->dev->of_node;
+ unsigned int value = MICBIAS_2_0V;
+ int ret;
+
+ of_property_read_u32(np, "ai31xx-micbias-vg", &value);
+ switch (value) {
+ case MICBIAS_2_0V:
+ case MICBIAS_2_5V:
+ case MICBIAS_AVDDV:
+ aic31xx->pdata.micbias_vg = value;
+ break;
+ default:
+ dev_err(aic31xx->dev,
+ "Bad ai31xx-micbias-vg value %d DT\n",
+ value);
+ aic31xx->pdata.micbias_vg = MICBIAS_2_0V;
+ }
+
+ ret = of_get_named_gpio(np, "gpio-reset", 0);
+ if (ret > 0)
+ aic31xx->pdata.gpio_reset = ret;
+}
+#else /* CONFIG_OF */
+static void aic31xx_pdata_from_of(struct aic31xx_priv *aic31xx)
+{
+}
+#endif /* CONFIG_OF */
+
+static int aic31xx_device_init(struct aic31xx_priv *aic31xx)
+{
+ int ret, i;
+
+ dev_set_drvdata(aic31xx->dev, aic31xx);
+
+ if (dev_get_platdata(aic31xx->dev))
+ memcpy(&aic31xx->pdata, dev_get_platdata(aic31xx->dev),
+ sizeof(aic31xx->pdata));
+ else if (aic31xx->dev->of_node)
+ aic31xx_pdata_from_of(aic31xx);
+
+ if (aic31xx->pdata.gpio_reset) {
+ ret = devm_gpio_request_one(aic31xx->dev,
+ aic31xx->pdata.gpio_reset,
+ GPIOF_OUT_INIT_HIGH,
+ "aic31xx-reset-pin");
+ if (ret < 0) {
+ dev_err(aic31xx->dev, "not able to acquire gpio\n");
+ return ret;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++)
+ aic31xx->supplies[i].supply = aic31xx_supply_names[i];
+
+ ret = devm_regulator_bulk_get(aic31xx->dev,
+ ARRAY_SIZE(aic31xx->supplies),
+ aic31xx->supplies);
+ if (ret != 0)
+ dev_err(aic31xx->dev, "Failed to request supplies: %d\n", ret);
+
+ return ret;
+}
+
+static int aic31xx_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct aic31xx_priv *aic31xx;
+ int ret;
+ const struct regmap_config *regmap_config;
+
+ dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__,
+ id->name, (int) id->driver_data);
+
+ regmap_config = &aic31xx_i2c_regmap;
+
+ aic31xx = devm_kzalloc(&i2c->dev, sizeof(*aic31xx), GFP_KERNEL);
+ if (aic31xx == NULL)
+ return -ENOMEM;
+
+ aic31xx->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+ if (IS_ERR(aic31xx->regmap)) {
+ ret = PTR_ERR(aic31xx->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+ aic31xx->dev = &i2c->dev;
+
+ aic31xx->pdata.codec_type = id->driver_data;
+
+ ret = aic31xx_device_init(aic31xx);
+ if (ret)
+ return ret;
+
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_driver_aic31xx,
+ aic31xx_dai_driver,
+ ARRAY_SIZE(aic31xx_dai_driver));
+}
+
+static int aic31xx_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+ return 0;
+}
+
+static const struct i2c_device_id aic31xx_i2c_id[] = {
+ { "tlv320aic310x", AIC3100 },
+ { "tlv320aic311x", AIC3110 },
+ { "tlv320aic3100", AIC3100 },
+ { "tlv320aic3110", AIC3110 },
+ { "tlv320aic3120", AIC3120 },
+ { "tlv320aic3111", AIC3111 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
+
+static struct i2c_driver aic31xx_i2c_driver = {
+ .driver = {
+ .name = "tlv320aic31xx-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tlv320aic31xx_of_match),
+ },
+ .probe = aic31xx_i2c_probe,
+ .remove = aic31xx_i2c_remove,
+ .id_table = aic31xx_i2c_id,
+};
+
+module_i2c_driver(aic31xx_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC3111 codec driver");
+MODULE_AUTHOR("Jyri Sarha");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/tlv320aic31xx.h b/kernel/sound/soc/codecs/tlv320aic31xx.h
new file mode 100644
index 000000000..fe16c3460
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320aic31xx.h
@@ -0,0 +1,259 @@
+/*
+ * ALSA SoC TLV320AIC31XX codec driver
+ *
+ * Copyright (C) 2013 Texas Instruments, Inc.
+ *
+ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+#ifndef _TLV320AIC31XX_H
+#define _TLV320AIC31XX_H
+
+#define AIC31XX_RATES SNDRV_PCM_RATE_8000_192000
+
+#define AIC31XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE \
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+
+#define AIC31XX_STEREO_CLASS_D_BIT 0x1
+#define AIC31XX_MINIDSP_BIT 0x2
+
+enum aic31xx_type {
+ AIC3100 = 0,
+ AIC3110 = AIC31XX_STEREO_CLASS_D_BIT,
+ AIC3120 = AIC31XX_MINIDSP_BIT,
+ AIC3111 = (AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT),
+};
+
+struct aic31xx_pdata {
+ enum aic31xx_type codec_type;
+ unsigned int gpio_reset;
+ int micbias_vg;
+};
+
+/* Page Control Register */
+#define AIC31XX_PAGECTL 0x00
+
+/* Page 0 Registers */
+/* Software reset register */
+#define AIC31XX_RESET 0x01
+/* OT FLAG register */
+#define AIC31XX_OT_FLAG 0x03
+/* Clock clock Gen muxing, Multiplexers*/
+#define AIC31XX_CLKMUX 0x04
+/* PLL P and R-VAL register */
+#define AIC31XX_PLLPR 0x05
+/* PLL J-VAL register */
+#define AIC31XX_PLLJ 0x06
+/* PLL D-VAL MSB register */
+#define AIC31XX_PLLDMSB 0x07
+/* PLL D-VAL LSB register */
+#define AIC31XX_PLLDLSB 0x08
+/* DAC NDAC_VAL register*/
+#define AIC31XX_NDAC 0x0B
+/* DAC MDAC_VAL register */
+#define AIC31XX_MDAC 0x0C
+/* DAC OSR setting register 1, MSB value */
+#define AIC31XX_DOSRMSB 0x0D
+/* DAC OSR setting register 2, LSB value */
+#define AIC31XX_DOSRLSB 0x0E
+#define AIC31XX_MINI_DSP_INPOL 0x10
+/* Clock setting register 8, PLL */
+#define AIC31XX_NADC 0x12
+/* Clock setting register 9, PLL */
+#define AIC31XX_MADC 0x13
+/* ADC Oversampling (AOSR) Register */
+#define AIC31XX_AOSR 0x14
+/* Clock setting register 9, Multiplexers */
+#define AIC31XX_CLKOUTMUX 0x19
+/* Clock setting register 10, CLOCKOUT M divider value */
+#define AIC31XX_CLKOUTMVAL 0x1A
+/* Audio Interface Setting Register 1 */
+#define AIC31XX_IFACE1 0x1B
+/* Audio Data Slot Offset Programming */
+#define AIC31XX_DATA_OFFSET 0x1C
+/* Audio Interface Setting Register 2 */
+#define AIC31XX_IFACE2 0x1D
+/* Clock setting register 11, BCLK N Divider */
+#define AIC31XX_BCLKN 0x1E
+/* Audio Interface Setting Register 3, Secondary Audio Interface */
+#define AIC31XX_IFACESEC1 0x1F
+/* Audio Interface Setting Register 4 */
+#define AIC31XX_IFACESEC2 0x20
+/* Audio Interface Setting Register 5 */
+#define AIC31XX_IFACESEC3 0x21
+/* I2C Bus Condition */
+#define AIC31XX_I2C 0x22
+/* ADC FLAG */
+#define AIC31XX_ADCFLAG 0x24
+/* DAC Flag Registers */
+#define AIC31XX_DACFLAG1 0x25
+#define AIC31XX_DACFLAG2 0x26
+/* Sticky Interrupt flag (overflow) */
+#define AIC31XX_OFFLAG 0x27
+/* Sticy DAC Interrupt flags */
+#define AIC31XX_INTRDACFLAG 0x2C
+/* Sticy ADC Interrupt flags */
+#define AIC31XX_INTRADCFLAG 0x2D
+/* DAC Interrupt flags 2 */
+#define AIC31XX_INTRDACFLAG2 0x2E
+/* ADC Interrupt flags 2 */
+#define AIC31XX_INTRADCFLAG2 0x2F
+/* INT1 interrupt control */
+#define AIC31XX_INT1CTRL 0x30
+/* INT2 interrupt control */
+#define AIC31XX_INT2CTRL 0x31
+/* GPIO1 control */
+#define AIC31XX_GPIO1 0x33
+
+#define AIC31XX_DACPRB 0x3C
+/* ADC Instruction Set Register */
+#define AIC31XX_ADCPRB 0x3D
+/* DAC channel setup register */
+#define AIC31XX_DACSETUP 0x3F
+/* DAC Mute and volume control register */
+#define AIC31XX_DACMUTE 0x40
+/* Left DAC channel digital volume control */
+#define AIC31XX_LDACVOL 0x41
+/* Right DAC channel digital volume control */
+#define AIC31XX_RDACVOL 0x42
+/* Headset detection */
+#define AIC31XX_HSDETECT 0x43
+/* ADC Digital Mic */
+#define AIC31XX_ADCSETUP 0x51
+/* ADC Digital Volume Control Fine Adjust */
+#define AIC31XX_ADCFGA 0x52
+/* ADC Digital Volume Control Coarse Adjust */
+#define AIC31XX_ADCVOL 0x53
+
+
+/* Page 1 Registers */
+/* Headphone drivers */
+#define AIC31XX_HPDRIVER 0x9F
+/* Class-D Speakear Amplifier */
+#define AIC31XX_SPKAMP 0xA0
+/* HP Output Drivers POP Removal Settings */
+#define AIC31XX_HPPOP 0xA1
+/* Output Driver PGA Ramp-Down Period Control */
+#define AIC31XX_SPPGARAMP 0xA2
+/* DAC_L and DAC_R Output Mixer Routing */
+#define AIC31XX_DACMIXERROUTE 0xA3
+/* Left Analog Vol to HPL */
+#define AIC31XX_LANALOGHPL 0xA4
+/* Right Analog Vol to HPR */
+#define AIC31XX_RANALOGHPR 0xA5
+/* Left Analog Vol to SPL */
+#define AIC31XX_LANALOGSPL 0xA6
+/* Right Analog Vol to SPR */
+#define AIC31XX_RANALOGSPR 0xA7
+/* HPL Driver */
+#define AIC31XX_HPLGAIN 0xA8
+/* HPR Driver */
+#define AIC31XX_HPRGAIN 0xA9
+/* SPL Driver */
+#define AIC31XX_SPLGAIN 0xAA
+/* SPR Driver */
+#define AIC31XX_SPRGAIN 0xAB
+/* HP Driver Control */
+#define AIC31XX_HPCONTROL 0xAC
+/* MIC Bias Control */
+#define AIC31XX_MICBIAS 0xAE
+/* MIC PGA*/
+#define AIC31XX_MICPGA 0xAF
+/* Delta-Sigma Mono ADC Channel Fine-Gain Input Selection for P-Terminal */
+#define AIC31XX_MICPGAPI 0xB0
+/* ADC Input Selection for M-Terminal */
+#define AIC31XX_MICPGAMI 0xB1
+/* Input CM Settings */
+#define AIC31XX_MICPGACM 0xB2
+
+/* Bits, masks and shifts */
+
+/* AIC31XX_CLKMUX */
+#define AIC31XX_PLL_CLKIN_MASK 0x0c
+#define AIC31XX_PLL_CLKIN_SHIFT 2
+#define AIC31XX_PLL_CLKIN_MCLK 0
+#define AIC31XX_CODEC_CLKIN_MASK 0x03
+#define AIC31XX_CODEC_CLKIN_SHIFT 0
+#define AIC31XX_CODEC_CLKIN_PLL 3
+#define AIC31XX_CODEC_CLKIN_BCLK 1
+
+/* AIC31XX_PLLPR, AIC31XX_NDAC, AIC31XX_MDAC, AIC31XX_NADC, AIC31XX_MADC,
+ AIC31XX_BCLKN */
+#define AIC31XX_PLL_MASK 0x7f
+#define AIC31XX_PM_MASK 0x80
+
+/* AIC31XX_IFACE1 */
+#define AIC31XX_WORD_LEN_16BITS 0x00
+#define AIC31XX_WORD_LEN_20BITS 0x01
+#define AIC31XX_WORD_LEN_24BITS 0x02
+#define AIC31XX_WORD_LEN_32BITS 0x03
+#define AIC31XX_IFACE1_DATALEN_MASK 0x30
+#define AIC31XX_IFACE1_DATALEN_SHIFT (4)
+#define AIC31XX_IFACE1_DATATYPE_MASK 0xC0
+#define AIC31XX_IFACE1_DATATYPE_SHIFT (6)
+#define AIC31XX_I2S_MODE 0x00
+#define AIC31XX_DSP_MODE 0x01
+#define AIC31XX_RIGHT_JUSTIFIED_MODE 0x02
+#define AIC31XX_LEFT_JUSTIFIED_MODE 0x03
+#define AIC31XX_IFACE1_MASTER_MASK 0x0C
+#define AIC31XX_BCLK_MASTER 0x08
+#define AIC31XX_WCLK_MASTER 0x04
+
+/* AIC31XX_DATA_OFFSET */
+#define AIC31XX_DATA_OFFSET_MASK 0xFF
+
+/* AIC31XX_IFACE2 */
+#define AIC31XX_BCLKINV_MASK 0x08
+#define AIC31XX_BDIVCLK_MASK 0x03
+#define AIC31XX_DAC2BCLK 0x00
+#define AIC31XX_DACMOD2BCLK 0x01
+#define AIC31XX_ADC2BCLK 0x02
+#define AIC31XX_ADCMOD2BCLK 0x03
+
+/* AIC31XX_ADCFLAG */
+#define AIC31XX_ADCPWRSTATUS_MASK 0x40
+
+/* AIC31XX_DACFLAG1 */
+#define AIC31XX_LDACPWRSTATUS_MASK 0x80
+#define AIC31XX_RDACPWRSTATUS_MASK 0x08
+#define AIC31XX_HPLDRVPWRSTATUS_MASK 0x20
+#define AIC31XX_HPRDRVPWRSTATUS_MASK 0x02
+#define AIC31XX_SPLDRVPWRSTATUS_MASK 0x10
+#define AIC31XX_SPRDRVPWRSTATUS_MASK 0x01
+
+/* AIC31XX_INTRDACFLAG */
+#define AIC31XX_HPSCDETECT_MASK 0x80
+#define AIC31XX_BUTTONPRESS_MASK 0x20
+#define AIC31XX_HSPLUG_MASK 0x10
+#define AIC31XX_LDRCTHRES_MASK 0x08
+#define AIC31XX_RDRCTHRES_MASK 0x04
+#define AIC31XX_DACSINT_MASK 0x02
+#define AIC31XX_DACAINT_MASK 0x01
+
+/* AIC31XX_INT1CTRL */
+#define AIC31XX_HSPLUGDET_MASK 0x80
+#define AIC31XX_BUTTONPRESSDET_MASK 0x40
+#define AIC31XX_DRCTHRES_MASK 0x20
+#define AIC31XX_AGCNOISE_MASK 0x10
+#define AIC31XX_OC_MASK 0x08
+#define AIC31XX_ENGINE_MASK 0x04
+
+/* AIC31XX_DACSETUP */
+#define AIC31XX_SOFTSTEP_MASK 0x03
+
+/* AIC31XX_DACMUTE */
+#define AIC31XX_DACMUTE_MASK 0x0C
+
+/* AIC31XX_MICBIAS */
+#define AIC31XX_MICBIAS_MASK 0x03
+#define AIC31XX_MICBIAS_SHIFT 0
+
+#endif /* _TLV320AIC31XX_H */
diff --git a/kernel/sound/soc/codecs/tlv320aic32x4.c b/kernel/sound/soc/codecs/tlv320aic32x4.c
new file mode 100644
index 000000000..015467ed6
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320aic32x4.c
@@ -0,0 +1,887 @@
+/*
+ * linux/sound/soc/codecs/tlv320aic32x4.c
+ *
+ * Copyright 2011 Vista Silicon S.L.
+ *
+ * Author: Javier Martin <javier.martin@vista-silicon.com>
+ *
+ * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/i2c.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/tlv320aic32x4.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 "tlv320aic32x4.h"
+
+struct aic32x4_rate_divs {
+ u32 mclk;
+ u32 rate;
+ u8 p_val;
+ u8 pll_j;
+ u16 pll_d;
+ u16 dosr;
+ u8 ndac;
+ u8 mdac;
+ u8 aosr;
+ u8 nadc;
+ u8 madc;
+ u8 blck_N;
+};
+
+struct aic32x4_priv {
+ struct regmap *regmap;
+ u32 sysclk;
+ u32 power_cfg;
+ u32 micpga_routing;
+ bool swapdacs;
+ int rstn_gpio;
+ struct clk *mclk;
+
+ struct regulator *supply_ldo;
+ struct regulator *supply_iov;
+ struct regulator *supply_dv;
+ struct regulator *supply_av;
+};
+
+/* 0dB min, 0.5dB steps */
+static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0);
+/* -63.5dB min, 0.5dB steps */
+static DECLARE_TLV_DB_SCALE(tlv_pcm, -6350, 50, 0);
+/* -6dB min, 1dB steps */
+static DECLARE_TLV_DB_SCALE(tlv_driver_gain, -600, 100, 0);
+/* -12dB min, 0.5dB steps */
+static DECLARE_TLV_DB_SCALE(tlv_adc_vol, -1200, 50, 0);
+
+static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
+ SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL,
+ AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm),
+ SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN,
+ AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0,
+ tlv_driver_gain),
+ SOC_DOUBLE_R_S_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN,
+ AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0,
+ tlv_driver_gain),
+ SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN,
+ AIC32X4_HPRGAIN, 6, 0x01, 1),
+ SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN,
+ AIC32X4_LORGAIN, 6, 0x01, 1),
+ SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL,
+ AIC32X4_RMICPGAVOL, 7, 0x01, 1),
+
+ SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0),
+ SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0),
+
+ SOC_DOUBLE_R_S_TLV("ADC Level Volume", AIC32X4_LADCVOL,
+ AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol),
+ SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL,
+ AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5),
+
+ SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0),
+
+ SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0),
+ SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0),
+ SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1,
+ 4, 0x07, 0),
+ SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1,
+ 0, 0x03, 0),
+ SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2,
+ 6, 0x03, 0),
+ SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2,
+ 1, 0x1F, 0),
+ SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3,
+ 0, 0x7F, 0),
+ SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4,
+ 3, 0x1F, 0),
+ SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5,
+ 3, 0x1F, 0),
+ SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6,
+ 0, 0x1F, 0),
+ SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7,
+ 0, 0x0F, 0),
+};
+
+static const struct aic32x4_rate_divs aic32x4_divs[] = {
+ /* 8k rate */
+ {AIC32X4_FREQ_12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24},
+ {AIC32X4_FREQ_24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24},
+ {AIC32X4_FREQ_25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24},
+ /* 11.025k rate */
+ {AIC32X4_FREQ_12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16},
+ {AIC32X4_FREQ_24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16},
+ /* 16k rate */
+ {AIC32X4_FREQ_12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12},
+ {AIC32X4_FREQ_24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12},
+ {AIC32X4_FREQ_25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12},
+ /* 22.05k rate */
+ {AIC32X4_FREQ_12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8},
+ {AIC32X4_FREQ_24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8},
+ {AIC32X4_FREQ_25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8},
+ /* 32k rate */
+ {AIC32X4_FREQ_12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6},
+ {AIC32X4_FREQ_24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6},
+ /* 44.1k rate */
+ {AIC32X4_FREQ_12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4},
+ {AIC32X4_FREQ_24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4},
+ {AIC32X4_FREQ_25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4},
+ /* 48k rate */
+ {AIC32X4_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4},
+ {AIC32X4_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4},
+ {AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4}
+};
+
+static const struct snd_kcontrol_new hpl_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0),
+ SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new hpr_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_HPRROUTE, 3, 1, 0),
+ SOC_DAPM_SINGLE("IN1_R Switch", AIC32X4_HPRROUTE, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new lol_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_LOLROUTE, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new lor_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_LORROUTE, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("IN1_L P Switch", AIC32X4_LMICPGAPIN, 6, 1, 0),
+ SOC_DAPM_SINGLE("IN2_L P Switch", AIC32X4_LMICPGAPIN, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN3_L P Switch", AIC32X4_LMICPGAPIN, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("IN1_R P Switch", AIC32X4_RMICPGAPIN, 6, 1, 0),
+ SOC_DAPM_SINGLE("IN2_R P Switch", AIC32X4_RMICPGAPIN, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN3_R P Switch", AIC32X4_RMICPGAPIN, 2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", AIC32X4_DACSETUP, 7, 0),
+ SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0,
+ &hpl_output_mixer_controls[0],
+ ARRAY_SIZE(hpl_output_mixer_controls)),
+ SND_SOC_DAPM_PGA("HPL Power", AIC32X4_OUTPWRCTL, 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("LOL Output Mixer", SND_SOC_NOPM, 0, 0,
+ &lol_output_mixer_controls[0],
+ ARRAY_SIZE(lol_output_mixer_controls)),
+ SND_SOC_DAPM_PGA("LOL Power", AIC32X4_OUTPWRCTL, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", AIC32X4_DACSETUP, 6, 0),
+ SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0,
+ &hpr_output_mixer_controls[0],
+ ARRAY_SIZE(hpr_output_mixer_controls)),
+ SND_SOC_DAPM_PGA("HPR Power", AIC32X4_OUTPWRCTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("LOR Output Mixer", SND_SOC_NOPM, 0, 0,
+ &lor_output_mixer_controls[0],
+ ARRAY_SIZE(lor_output_mixer_controls)),
+ SND_SOC_DAPM_PGA("LOR Power", AIC32X4_OUTPWRCTL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0,
+ &left_input_mixer_controls[0],
+ ARRAY_SIZE(left_input_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0,
+ &right_input_mixer_controls[0],
+ ARRAY_SIZE(right_input_mixer_controls)),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0),
+ SND_SOC_DAPM_MICBIAS("Mic Bias", AIC32X4_MICBIAS, 6, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("LOL"),
+ SND_SOC_DAPM_OUTPUT("LOR"),
+ SND_SOC_DAPM_INPUT("IN1_L"),
+ SND_SOC_DAPM_INPUT("IN1_R"),
+ SND_SOC_DAPM_INPUT("IN2_L"),
+ SND_SOC_DAPM_INPUT("IN2_R"),
+ SND_SOC_DAPM_INPUT("IN3_L"),
+ SND_SOC_DAPM_INPUT("IN3_R"),
+};
+
+static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = {
+ /* Left Output */
+ {"HPL Output Mixer", "L_DAC Switch", "Left DAC"},
+ {"HPL Output Mixer", "IN1_L Switch", "IN1_L"},
+
+ {"HPL Power", NULL, "HPL Output Mixer"},
+ {"HPL", NULL, "HPL Power"},
+
+ {"LOL Output Mixer", "L_DAC Switch", "Left DAC"},
+
+ {"LOL Power", NULL, "LOL Output Mixer"},
+ {"LOL", NULL, "LOL Power"},
+
+ /* Right Output */
+ {"HPR Output Mixer", "R_DAC Switch", "Right DAC"},
+ {"HPR Output Mixer", "IN1_R Switch", "IN1_R"},
+
+ {"HPR Power", NULL, "HPR Output Mixer"},
+ {"HPR", NULL, "HPR Power"},
+
+ {"LOR Output Mixer", "R_DAC Switch", "Right DAC"},
+
+ {"LOR Power", NULL, "LOR Output Mixer"},
+ {"LOR", NULL, "LOR Power"},
+
+ /* Left input */
+ {"Left Input Mixer", "IN1_L P Switch", "IN1_L"},
+ {"Left Input Mixer", "IN2_L P Switch", "IN2_L"},
+ {"Left Input Mixer", "IN3_L P Switch", "IN3_L"},
+
+ {"Left ADC", NULL, "Left Input Mixer"},
+
+ /* Right Input */
+ {"Right Input Mixer", "IN1_R P Switch", "IN1_R"},
+ {"Right Input Mixer", "IN2_R P Switch", "IN2_R"},
+ {"Right Input Mixer", "IN3_R P Switch", "IN3_R"},
+
+ {"Right ADC", NULL, "Right Input Mixer"},
+};
+
+static const struct regmap_range_cfg aic32x4_regmap_pages[] = {
+ {
+ .selector_reg = 0,
+ .selector_mask = 0xff,
+ .window_start = 0,
+ .window_len = 128,
+ .range_min = 0,
+ .range_max = AIC32X4_RMICPGAVOL,
+ },
+};
+
+static const struct regmap_config aic32x4_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AIC32X4_RMICPGAVOL,
+ .ranges = aic32x4_regmap_pages,
+ .num_ranges = ARRAY_SIZE(aic32x4_regmap_pages),
+};
+
+static inline int aic32x4_get_divs(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(aic32x4_divs); i++) {
+ if ((aic32x4_divs[i].rate == rate)
+ && (aic32x4_divs[i].mclk == mclk)) {
+ return i;
+ }
+ }
+ printk(KERN_ERR "aic32x4: master clock and sample rate is not supported\n");
+ return -EINVAL;
+}
+
+static int aic32x4_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 aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+
+ switch (freq) {
+ case AIC32X4_FREQ_12000000:
+ case AIC32X4_FREQ_24000000:
+ case AIC32X4_FREQ_25000000:
+ aic32x4->sysclk = freq;
+ return 0;
+ }
+ printk(KERN_ERR "aic32x4: invalid frequency to set DAI system clock\n");
+ return -EINVAL;
+}
+
+static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 iface_reg_1;
+ u8 iface_reg_2;
+ u8 iface_reg_3;
+
+ iface_reg_1 = snd_soc_read(codec, AIC32X4_IFACE1);
+ iface_reg_1 = iface_reg_1 & ~(3 << 6 | 3 << 2);
+ iface_reg_2 = snd_soc_read(codec, AIC32X4_IFACE2);
+ iface_reg_2 = 0;
+ iface_reg_3 = snd_soc_read(codec, AIC32X4_IFACE3);
+ iface_reg_3 = iface_reg_3 & ~(1 << 3);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface_reg_1 |= AIC32X4_BCLKMASTER | AIC32X4_WCLKMASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ printk(KERN_ERR "aic32x4: invalid DAI master/slave interface\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT);
+ iface_reg_3 |= (1 << 3); /* invert bit clock */
+ iface_reg_2 = 0x01; /* add offset 1 */
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT);
+ iface_reg_3 |= (1 << 3); /* invert bit clock */
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface_reg_1 |=
+ (AIC32X4_RIGHT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface_reg_1 |=
+ (AIC32X4_LEFT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT);
+ break;
+ default:
+ printk(KERN_ERR "aic32x4: invalid DAI interface format\n");
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, AIC32X4_IFACE1, iface_reg_1);
+ snd_soc_write(codec, AIC32X4_IFACE2, iface_reg_2);
+ snd_soc_write(codec, AIC32X4_IFACE3, iface_reg_3);
+ return 0;
+}
+
+static int aic32x4_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 aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+ u8 data;
+ int i;
+
+ i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params));
+ if (i < 0) {
+ printk(KERN_ERR "aic32x4: sampling rate not supported\n");
+ return i;
+ }
+
+ /* Use PLL as CODEC_CLKIN and DAC_MOD_CLK as BDIV_CLKIN */
+ snd_soc_write(codec, AIC32X4_CLKMUX, AIC32X4_PLLCLKIN);
+ snd_soc_write(codec, AIC32X4_IFACE3, AIC32X4_DACMOD2BCLK);
+
+ /* We will fix R value to 1 and will make P & J=K.D as varialble */
+ data = snd_soc_read(codec, AIC32X4_PLLPR);
+ data &= ~(7 << 4);
+ snd_soc_write(codec, AIC32X4_PLLPR,
+ (data | (aic32x4_divs[i].p_val << 4) | 0x01));
+
+ snd_soc_write(codec, AIC32X4_PLLJ, aic32x4_divs[i].pll_j);
+
+ snd_soc_write(codec, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8));
+ snd_soc_write(codec, AIC32X4_PLLDLSB,
+ (aic32x4_divs[i].pll_d & 0xff));
+
+ /* NDAC divider value */
+ data = snd_soc_read(codec, AIC32X4_NDAC);
+ data &= ~(0x7f);
+ snd_soc_write(codec, AIC32X4_NDAC, data | aic32x4_divs[i].ndac);
+
+ /* MDAC divider value */
+ data = snd_soc_read(codec, AIC32X4_MDAC);
+ data &= ~(0x7f);
+ snd_soc_write(codec, AIC32X4_MDAC, data | aic32x4_divs[i].mdac);
+
+ /* DOSR MSB & LSB values */
+ snd_soc_write(codec, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8);
+ snd_soc_write(codec, AIC32X4_DOSRLSB,
+ (aic32x4_divs[i].dosr & 0xff));
+
+ /* NADC divider value */
+ data = snd_soc_read(codec, AIC32X4_NADC);
+ data &= ~(0x7f);
+ snd_soc_write(codec, AIC32X4_NADC, data | aic32x4_divs[i].nadc);
+
+ /* MADC divider value */
+ data = snd_soc_read(codec, AIC32X4_MADC);
+ data &= ~(0x7f);
+ snd_soc_write(codec, AIC32X4_MADC, data | aic32x4_divs[i].madc);
+
+ /* AOSR value */
+ snd_soc_write(codec, AIC32X4_AOSR, aic32x4_divs[i].aosr);
+
+ /* BCLK N divider */
+ data = snd_soc_read(codec, AIC32X4_BCLKN);
+ data &= ~(0x7f);
+ snd_soc_write(codec, AIC32X4_BCLKN, data | aic32x4_divs[i].blck_N);
+
+ data = snd_soc_read(codec, AIC32X4_IFACE1);
+ data = data & ~(3 << 4);
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ data |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_DOSRMSB_SHIFT);
+ break;
+ case 24:
+ data |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_DOSRMSB_SHIFT);
+ break;
+ case 32:
+ data |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_DOSRMSB_SHIFT);
+ break;
+ }
+ snd_soc_write(codec, AIC32X4_IFACE1, data);
+
+ if (params_channels(params) == 1) {
+ data = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN;
+ } else {
+ if (aic32x4->swapdacs)
+ data = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2RCHN;
+ else
+ data = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN;
+ }
+ snd_soc_update_bits(codec, AIC32X4_DACSETUP, AIC32X4_DAC_CHAN_MASK,
+ data);
+
+ return 0;
+}
+
+static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 dac_reg;
+
+ dac_reg = snd_soc_read(codec, AIC32X4_DACMUTE) & ~AIC32X4_MUTEON;
+ if (mute)
+ snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg | AIC32X4_MUTEON);
+ else
+ snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg);
+ return 0;
+}
+
+static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* Switch on master clock */
+ ret = clk_prepare_enable(aic32x4->mclk);
+ if (ret) {
+ dev_err(codec->dev, "Failed to enable master clock\n");
+ return ret;
+ }
+
+ /* Switch on PLL */
+ snd_soc_update_bits(codec, AIC32X4_PLLPR,
+ AIC32X4_PLLEN, AIC32X4_PLLEN);
+
+ /* Switch on NDAC Divider */
+ snd_soc_update_bits(codec, AIC32X4_NDAC,
+ AIC32X4_NDACEN, AIC32X4_NDACEN);
+
+ /* Switch on MDAC Divider */
+ snd_soc_update_bits(codec, AIC32X4_MDAC,
+ AIC32X4_MDACEN, AIC32X4_MDACEN);
+
+ /* Switch on NADC Divider */
+ snd_soc_update_bits(codec, AIC32X4_NADC,
+ AIC32X4_NADCEN, AIC32X4_NADCEN);
+
+ /* Switch on MADC Divider */
+ snd_soc_update_bits(codec, AIC32X4_MADC,
+ AIC32X4_MADCEN, AIC32X4_MADCEN);
+
+ /* Switch on BCLK_N Divider */
+ snd_soc_update_bits(codec, AIC32X4_BCLKN,
+ AIC32X4_BCLKEN, AIC32X4_BCLKEN);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* Switch off BCLK_N Divider */
+ snd_soc_update_bits(codec, AIC32X4_BCLKN,
+ AIC32X4_BCLKEN, 0);
+
+ /* Switch off MADC Divider */
+ snd_soc_update_bits(codec, AIC32X4_MADC,
+ AIC32X4_MADCEN, 0);
+
+ /* Switch off NADC Divider */
+ snd_soc_update_bits(codec, AIC32X4_NADC,
+ AIC32X4_NADCEN, 0);
+
+ /* Switch off MDAC Divider */
+ snd_soc_update_bits(codec, AIC32X4_MDAC,
+ AIC32X4_MDACEN, 0);
+
+ /* Switch off NDAC Divider */
+ snd_soc_update_bits(codec, AIC32X4_NDAC,
+ AIC32X4_NDACEN, 0);
+
+ /* Switch off PLL */
+ snd_soc_update_bits(codec, AIC32X4_PLLPR,
+ AIC32X4_PLLEN, 0);
+
+ /* Switch off master clock */
+ clk_disable_unprepare(aic32x4->mclk);
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define AIC32X4_RATES SNDRV_PCM_RATE_8000_48000
+#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops aic32x4_ops = {
+ .hw_params = aic32x4_hw_params,
+ .digital_mute = aic32x4_mute,
+ .set_fmt = aic32x4_set_dai_fmt,
+ .set_sysclk = aic32x4_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver aic32x4_dai = {
+ .name = "tlv320aic32x4-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AIC32X4_RATES,
+ .formats = AIC32X4_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AIC32X4_RATES,
+ .formats = AIC32X4_FORMATS,},
+ .ops = &aic32x4_ops,
+ .symmetric_rates = 1,
+};
+
+static int aic32x4_probe(struct snd_soc_codec *codec)
+{
+ struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+ u32 tmp_reg;
+
+ if (gpio_is_valid(aic32x4->rstn_gpio)) {
+ ndelay(10);
+ gpio_set_value(aic32x4->rstn_gpio, 1);
+ }
+
+ snd_soc_write(codec, AIC32X4_RESET, 0x01);
+
+ /* Power platform configuration */
+ if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) {
+ snd_soc_write(codec, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN |
+ AIC32X4_MICBIAS_2075V);
+ }
+ if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE)
+ snd_soc_write(codec, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE);
+
+ tmp_reg = (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) ?
+ AIC32X4_LDOCTLEN : 0;
+ snd_soc_write(codec, AIC32X4_LDOCTL, tmp_reg);
+
+ tmp_reg = snd_soc_read(codec, AIC32X4_CMMODE);
+ if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36)
+ tmp_reg |= AIC32X4_LDOIN_18_36;
+ if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED)
+ tmp_reg |= AIC32X4_LDOIN2HP;
+ snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg);
+
+ /* Mic PGA routing */
+ if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K)
+ snd_soc_write(codec, AIC32X4_LMICPGANIN,
+ AIC32X4_LMICPGANIN_IN2R_10K);
+ else
+ snd_soc_write(codec, AIC32X4_LMICPGANIN,
+ AIC32X4_LMICPGANIN_CM1L_10K);
+ if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K)
+ snd_soc_write(codec, AIC32X4_RMICPGANIN,
+ AIC32X4_RMICPGANIN_IN1L_10K);
+ else
+ snd_soc_write(codec, AIC32X4_RMICPGANIN,
+ AIC32X4_RMICPGANIN_CM1R_10K);
+
+ /*
+ * Workaround: for an unknown reason, the ADC needs to be powered up
+ * and down for the first capture to work properly. It seems related to
+ * a HW BUG or some kind of behavior not documented in the datasheet.
+ */
+ tmp_reg = snd_soc_read(codec, AIC32X4_ADCSETUP);
+ snd_soc_write(codec, AIC32X4_ADCSETUP, tmp_reg |
+ AIC32X4_LADC_EN | AIC32X4_RADC_EN);
+ snd_soc_write(codec, AIC32X4_ADCSETUP, tmp_reg);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
+ .probe = aic32x4_probe,
+ .set_bias_level = aic32x4_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = aic32x4_snd_controls,
+ .num_controls = ARRAY_SIZE(aic32x4_snd_controls),
+ .dapm_widgets = aic32x4_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets),
+ .dapm_routes = aic32x4_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes),
+};
+
+static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4,
+ struct device_node *np)
+{
+ aic32x4->swapdacs = false;
+ aic32x4->micpga_routing = 0;
+ aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0);
+
+ return 0;
+}
+
+static void aic32x4_disable_regulators(struct aic32x4_priv *aic32x4)
+{
+ regulator_disable(aic32x4->supply_iov);
+
+ if (!IS_ERR(aic32x4->supply_ldo))
+ regulator_disable(aic32x4->supply_ldo);
+
+ if (!IS_ERR(aic32x4->supply_dv))
+ regulator_disable(aic32x4->supply_dv);
+
+ if (!IS_ERR(aic32x4->supply_av))
+ regulator_disable(aic32x4->supply_av);
+}
+
+static int aic32x4_setup_regulators(struct device *dev,
+ struct aic32x4_priv *aic32x4)
+{
+ int ret = 0;
+
+ aic32x4->supply_ldo = devm_regulator_get_optional(dev, "ldoin");
+ aic32x4->supply_iov = devm_regulator_get(dev, "iov");
+ aic32x4->supply_dv = devm_regulator_get_optional(dev, "dv");
+ aic32x4->supply_av = devm_regulator_get_optional(dev, "av");
+
+ /* Check if the regulator requirements are fulfilled */
+
+ if (IS_ERR(aic32x4->supply_iov)) {
+ dev_err(dev, "Missing supply 'iov'\n");
+ return PTR_ERR(aic32x4->supply_iov);
+ }
+
+ if (IS_ERR(aic32x4->supply_ldo)) {
+ if (PTR_ERR(aic32x4->supply_ldo) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ if (IS_ERR(aic32x4->supply_dv)) {
+ dev_err(dev, "Missing supply 'dv' or 'ldoin'\n");
+ return PTR_ERR(aic32x4->supply_dv);
+ }
+ if (IS_ERR(aic32x4->supply_av)) {
+ dev_err(dev, "Missing supply 'av' or 'ldoin'\n");
+ return PTR_ERR(aic32x4->supply_av);
+ }
+ } else {
+ if (IS_ERR(aic32x4->supply_dv) &&
+ PTR_ERR(aic32x4->supply_dv) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (IS_ERR(aic32x4->supply_av) &&
+ PTR_ERR(aic32x4->supply_av) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ }
+
+ ret = regulator_enable(aic32x4->supply_iov);
+ if (ret) {
+ dev_err(dev, "Failed to enable regulator iov\n");
+ return ret;
+ }
+
+ if (!IS_ERR(aic32x4->supply_ldo)) {
+ ret = regulator_enable(aic32x4->supply_ldo);
+ if (ret) {
+ dev_err(dev, "Failed to enable regulator ldo\n");
+ goto error_ldo;
+ }
+ }
+
+ if (!IS_ERR(aic32x4->supply_dv)) {
+ ret = regulator_enable(aic32x4->supply_dv);
+ if (ret) {
+ dev_err(dev, "Failed to enable regulator dv\n");
+ goto error_dv;
+ }
+ }
+
+ if (!IS_ERR(aic32x4->supply_av)) {
+ ret = regulator_enable(aic32x4->supply_av);
+ if (ret) {
+ dev_err(dev, "Failed to enable regulator av\n");
+ goto error_av;
+ }
+ }
+
+ if (!IS_ERR(aic32x4->supply_ldo) && IS_ERR(aic32x4->supply_av))
+ aic32x4->power_cfg |= AIC32X4_PWR_AIC32X4_LDO_ENABLE;
+
+ return 0;
+
+error_av:
+ if (!IS_ERR(aic32x4->supply_dv))
+ regulator_disable(aic32x4->supply_dv);
+
+error_dv:
+ if (!IS_ERR(aic32x4->supply_ldo))
+ regulator_disable(aic32x4->supply_ldo);
+
+error_ldo:
+ regulator_disable(aic32x4->supply_iov);
+ return ret;
+}
+
+static int aic32x4_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct aic32x4_pdata *pdata = i2c->dev.platform_data;
+ struct aic32x4_priv *aic32x4;
+ struct device_node *np = i2c->dev.of_node;
+ int ret;
+
+ aic32x4 = devm_kzalloc(&i2c->dev, sizeof(struct aic32x4_priv),
+ GFP_KERNEL);
+ if (aic32x4 == NULL)
+ return -ENOMEM;
+
+ aic32x4->regmap = devm_regmap_init_i2c(i2c, &aic32x4_regmap);
+ if (IS_ERR(aic32x4->regmap))
+ return PTR_ERR(aic32x4->regmap);
+
+ i2c_set_clientdata(i2c, aic32x4);
+
+ if (pdata) {
+ aic32x4->power_cfg = pdata->power_cfg;
+ aic32x4->swapdacs = pdata->swapdacs;
+ aic32x4->micpga_routing = pdata->micpga_routing;
+ aic32x4->rstn_gpio = pdata->rstn_gpio;
+ } else if (np) {
+ ret = aic32x4_parse_dt(aic32x4, np);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to parse DT node\n");
+ return ret;
+ }
+ } else {
+ aic32x4->power_cfg = 0;
+ aic32x4->swapdacs = false;
+ aic32x4->micpga_routing = 0;
+ aic32x4->rstn_gpio = -1;
+ }
+
+ aic32x4->mclk = devm_clk_get(&i2c->dev, "mclk");
+ if (IS_ERR(aic32x4->mclk)) {
+ dev_err(&i2c->dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n");
+ return PTR_ERR(aic32x4->mclk);
+ }
+
+ if (gpio_is_valid(aic32x4->rstn_gpio)) {
+ ret = devm_gpio_request_one(&i2c->dev, aic32x4->rstn_gpio,
+ GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn");
+ if (ret != 0)
+ return ret;
+ }
+
+ ret = aic32x4_setup_regulators(&i2c->dev, aic32x4);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to setup regulators\n");
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_aic32x4, &aic32x4_dai, 1);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to register codec\n");
+ aic32x4_disable_regulators(aic32x4);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, aic32x4);
+
+ return 0;
+}
+
+static int aic32x4_i2c_remove(struct i2c_client *client)
+{
+ struct aic32x4_priv *aic32x4 = i2c_get_clientdata(client);
+
+ aic32x4_disable_regulators(aic32x4);
+
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id aic32x4_i2c_id[] = {
+ { "tlv320aic32x4", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);
+
+static const struct of_device_id aic32x4_of_id[] = {
+ { .compatible = "ti,tlv320aic32x4", },
+ { /* senitel */ }
+};
+MODULE_DEVICE_TABLE(of, aic32x4_of_id);
+
+static struct i2c_driver aic32x4_i2c_driver = {
+ .driver = {
+ .name = "tlv320aic32x4",
+ .owner = THIS_MODULE,
+ .of_match_table = aic32x4_of_id,
+ },
+ .probe = aic32x4_i2c_probe,
+ .remove = aic32x4_i2c_remove,
+ .id_table = aic32x4_i2c_id,
+};
+
+module_i2c_driver(aic32x4_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver");
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/tlv320aic32x4.h b/kernel/sound/soc/codecs/tlv320aic32x4.h
new file mode 100644
index 000000000..995f033a8
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320aic32x4.h
@@ -0,0 +1,149 @@
+/*
+ * tlv320aic32x4.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 _TLV320AIC32X4_H
+#define _TLV320AIC32X4_H
+
+/* tlv320aic32x4 register space (in decimal to match datasheet) */
+
+#define AIC32X4_PAGE1 128
+
+#define AIC32X4_PSEL 0
+#define AIC32X4_RESET 1
+#define AIC32X4_CLKMUX 4
+#define AIC32X4_PLLPR 5
+#define AIC32X4_PLLJ 6
+#define AIC32X4_PLLDMSB 7
+#define AIC32X4_PLLDLSB 8
+#define AIC32X4_NDAC 11
+#define AIC32X4_MDAC 12
+#define AIC32X4_DOSRMSB 13
+#define AIC32X4_DOSRLSB 14
+#define AIC32X4_NADC 18
+#define AIC32X4_MADC 19
+#define AIC32X4_AOSR 20
+#define AIC32X4_CLKMUX2 25
+#define AIC32X4_CLKOUTM 26
+#define AIC32X4_IFACE1 27
+#define AIC32X4_IFACE2 28
+#define AIC32X4_IFACE3 29
+#define AIC32X4_BCLKN 30
+#define AIC32X4_IFACE4 31
+#define AIC32X4_IFACE5 32
+#define AIC32X4_IFACE6 33
+#define AIC32X4_DOUTCTL 53
+#define AIC32X4_DINCTL 54
+#define AIC32X4_DACSPB 60
+#define AIC32X4_ADCSPB 61
+#define AIC32X4_DACSETUP 63
+#define AIC32X4_DACMUTE 64
+#define AIC32X4_LDACVOL 65
+#define AIC32X4_RDACVOL 66
+#define AIC32X4_ADCSETUP 81
+#define AIC32X4_ADCFGA 82
+#define AIC32X4_LADCVOL 83
+#define AIC32X4_RADCVOL 84
+#define AIC32X4_LAGC1 86
+#define AIC32X4_LAGC2 87
+#define AIC32X4_LAGC3 88
+#define AIC32X4_LAGC4 89
+#define AIC32X4_LAGC5 90
+#define AIC32X4_LAGC6 91
+#define AIC32X4_LAGC7 92
+#define AIC32X4_RAGC1 94
+#define AIC32X4_RAGC2 95
+#define AIC32X4_RAGC3 96
+#define AIC32X4_RAGC4 97
+#define AIC32X4_RAGC5 98
+#define AIC32X4_RAGC6 99
+#define AIC32X4_RAGC7 100
+#define AIC32X4_PWRCFG (AIC32X4_PAGE1 + 1)
+#define AIC32X4_LDOCTL (AIC32X4_PAGE1 + 2)
+#define AIC32X4_OUTPWRCTL (AIC32X4_PAGE1 + 9)
+#define AIC32X4_CMMODE (AIC32X4_PAGE1 + 10)
+#define AIC32X4_HPLROUTE (AIC32X4_PAGE1 + 12)
+#define AIC32X4_HPRROUTE (AIC32X4_PAGE1 + 13)
+#define AIC32X4_LOLROUTE (AIC32X4_PAGE1 + 14)
+#define AIC32X4_LORROUTE (AIC32X4_PAGE1 + 15)
+#define AIC32X4_HPLGAIN (AIC32X4_PAGE1 + 16)
+#define AIC32X4_HPRGAIN (AIC32X4_PAGE1 + 17)
+#define AIC32X4_LOLGAIN (AIC32X4_PAGE1 + 18)
+#define AIC32X4_LORGAIN (AIC32X4_PAGE1 + 19)
+#define AIC32X4_HEADSTART (AIC32X4_PAGE1 + 20)
+#define AIC32X4_MICBIAS (AIC32X4_PAGE1 + 51)
+#define AIC32X4_LMICPGAPIN (AIC32X4_PAGE1 + 52)
+#define AIC32X4_LMICPGANIN (AIC32X4_PAGE1 + 54)
+#define AIC32X4_RMICPGAPIN (AIC32X4_PAGE1 + 55)
+#define AIC32X4_RMICPGANIN (AIC32X4_PAGE1 + 57)
+#define AIC32X4_FLOATINGINPUT (AIC32X4_PAGE1 + 58)
+#define AIC32X4_LMICPGAVOL (AIC32X4_PAGE1 + 59)
+#define AIC32X4_RMICPGAVOL (AIC32X4_PAGE1 + 60)
+
+#define AIC32X4_FREQ_12000000 12000000
+#define AIC32X4_FREQ_24000000 24000000
+#define AIC32X4_FREQ_25000000 25000000
+
+#define AIC32X4_WORD_LEN_16BITS 0x00
+#define AIC32X4_WORD_LEN_20BITS 0x01
+#define AIC32X4_WORD_LEN_24BITS 0x02
+#define AIC32X4_WORD_LEN_32BITS 0x03
+
+#define AIC32X4_LADC_EN (1 << 7)
+#define AIC32X4_RADC_EN (1 << 6)
+
+#define AIC32X4_I2S_MODE 0x00
+#define AIC32X4_DSP_MODE 0x01
+#define AIC32X4_RIGHT_JUSTIFIED_MODE 0x02
+#define AIC32X4_LEFT_JUSTIFIED_MODE 0x03
+
+#define AIC32X4_AVDDWEAKDISABLE 0x08
+#define AIC32X4_LDOCTLEN 0x01
+
+#define AIC32X4_LDOIN_18_36 0x01
+#define AIC32X4_LDOIN2HP 0x02
+
+#define AIC32X4_DACSPBLOCK_MASK 0x1f
+#define AIC32X4_ADCSPBLOCK_MASK 0x1f
+
+#define AIC32X4_PLLJ_SHIFT 6
+#define AIC32X4_DOSRMSB_SHIFT 4
+
+#define AIC32X4_PLLCLKIN 0x03
+
+#define AIC32X4_MICBIAS_LDOIN 0x08
+#define AIC32X4_MICBIAS_2075V 0x60
+
+#define AIC32X4_LMICPGANIN_IN2R_10K 0x10
+#define AIC32X4_LMICPGANIN_CM1L_10K 0x40
+#define AIC32X4_RMICPGANIN_IN1L_10K 0x10
+#define AIC32X4_RMICPGANIN_CM1R_10K 0x40
+
+#define AIC32X4_LMICPGAVOL_NOGAIN 0x80
+#define AIC32X4_RMICPGAVOL_NOGAIN 0x80
+
+#define AIC32X4_BCLKMASTER 0x08
+#define AIC32X4_WCLKMASTER 0x04
+#define AIC32X4_PLLEN (0x01 << 7)
+#define AIC32X4_NDACEN (0x01 << 7)
+#define AIC32X4_MDACEN (0x01 << 7)
+#define AIC32X4_NADCEN (0x01 << 7)
+#define AIC32X4_MADCEN (0x01 << 7)
+#define AIC32X4_BCLKEN (0x01 << 7)
+#define AIC32X4_DACEN (0x03 << 6)
+#define AIC32X4_RDAC2LCHN (0x02 << 2)
+#define AIC32X4_LDAC2RCHN (0x02 << 4)
+#define AIC32X4_LDAC2LCHN (0x01 << 4)
+#define AIC32X4_RDAC2RCHN (0x01 << 2)
+#define AIC32X4_DAC_CHAN_MASK 0x3c
+
+#define AIC32X4_SSTEP2WCLK 0x01
+#define AIC32X4_MUTEON 0x0C
+#define AIC32X4_DACMOD2BCLK 0x01
+
+#endif /* _TLV320AIC32X4_H */
diff --git a/kernel/sound/soc/codecs/tlv320aic3x.c b/kernel/sound/soc/codecs/tlv320aic3x.c
new file mode 100644
index 000000000..51c4713ac
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320aic3x.c
@@ -0,0 +1,1840 @@
+/*
+ * ALSA SoC TLV320AIC3X codec driver
+ *
+ * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
+ * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * Based on sound/soc/codecs/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.
+ *
+ * Notes:
+ * The AIC3X is a driver for a low power stereo audio
+ * codecs aic31, aic32, aic33, aic3007.
+ *
+ * It supports full aic33 codec functionality.
+ * The compatibility with aic32, aic31 and aic3007 is as follows:
+ * aic32/aic3007 | aic31
+ * ---------------------------------------
+ * MONO_LOUT -> N/A | MONO_LOUT -> N/A
+ * | IN1L -> LINE1L
+ * | IN1R -> LINE1R
+ * | IN2L -> LINE2L
+ * | IN2R -> LINE2R
+ * | MIC3L/R -> N/A
+ * truncated internal functionality in
+ * accordance with documentation
+ * ---------------------------------------
+ *
+ * Hence the machine layer should disable unsupported inputs/outputs by
+ * snd_soc_dapm_disable_pin(codec, "MONO_LOUT"), etc.
+ */
+
+#include <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/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
+#include <linux/of_gpio.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 <sound/tlv320aic3x.h>
+
+#include "tlv320aic3x.h"
+
+#define AIC3X_NUM_SUPPLIES 4
+static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = {
+ "IOVDD", /* I/O Voltage */
+ "DVDD", /* Digital Core Voltage */
+ "AVDD", /* Analog DAC Voltage */
+ "DRVDD", /* ADC Analog and Output Driver Voltage */
+};
+
+static LIST_HEAD(reset_list);
+
+struct aic3x_priv;
+
+struct aic3x_disable_nb {
+ struct notifier_block nb;
+ struct aic3x_priv *aic3x;
+};
+
+/* codec private data */
+struct aic3x_priv {
+ struct snd_soc_codec *codec;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES];
+ struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES];
+ struct aic3x_setup_data *setup;
+ unsigned int sysclk;
+ unsigned int dai_fmt;
+ unsigned int tdm_delay;
+ struct list_head list;
+ int master;
+ int gpio_reset;
+ int power;
+#define AIC3X_MODEL_3X 0
+#define AIC3X_MODEL_33 1
+#define AIC3X_MODEL_3007 2
+#define AIC3X_MODEL_3104 3
+ u16 model;
+
+ /* Selects the micbias voltage */
+ enum aic3x_micbias_voltage micbias_vg;
+};
+
+static const struct reg_default aic3x_reg[] = {
+ { 0, 0x00 }, { 1, 0x00 }, { 2, 0x00 }, { 3, 0x10 },
+ { 4, 0x04 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
+ { 8, 0x00 }, { 9, 0x00 }, { 10, 0x00 }, { 11, 0x01 },
+ { 12, 0x00 }, { 13, 0x00 }, { 14, 0x00 }, { 15, 0x80 },
+ { 16, 0x80 }, { 17, 0xff }, { 18, 0xff }, { 19, 0x78 },
+ { 20, 0x78 }, { 21, 0x78 }, { 22, 0x78 }, { 23, 0x78 },
+ { 24, 0x78 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0xfe },
+ { 28, 0x00 }, { 29, 0x00 }, { 30, 0xfe }, { 31, 0x00 },
+ { 32, 0x18 }, { 33, 0x18 }, { 34, 0x00 }, { 35, 0x00 },
+ { 36, 0x00 }, { 37, 0x00 }, { 38, 0x00 }, { 39, 0x00 },
+ { 40, 0x00 }, { 41, 0x00 }, { 42, 0x00 }, { 43, 0x80 },
+ { 44, 0x80 }, { 45, 0x00 }, { 46, 0x00 }, { 47, 0x00 },
+ { 48, 0x00 }, { 49, 0x00 }, { 50, 0x00 }, { 51, 0x04 },
+ { 52, 0x00 }, { 53, 0x00 }, { 54, 0x00 }, { 55, 0x00 },
+ { 56, 0x00 }, { 57, 0x00 }, { 58, 0x04 }, { 59, 0x00 },
+ { 60, 0x00 }, { 61, 0x00 }, { 62, 0x00 }, { 63, 0x00 },
+ { 64, 0x00 }, { 65, 0x04 }, { 66, 0x00 }, { 67, 0x00 },
+ { 68, 0x00 }, { 69, 0x00 }, { 70, 0x00 }, { 71, 0x00 },
+ { 72, 0x04 }, { 73, 0x00 }, { 74, 0x00 }, { 75, 0x00 },
+ { 76, 0x00 }, { 77, 0x00 }, { 78, 0x00 }, { 79, 0x00 },
+ { 80, 0x00 }, { 81, 0x00 }, { 82, 0x00 }, { 83, 0x00 },
+ { 84, 0x00 }, { 85, 0x00 }, { 86, 0x00 }, { 87, 0x00 },
+ { 88, 0x00 }, { 89, 0x00 }, { 90, 0x00 }, { 91, 0x00 },
+ { 92, 0x00 }, { 93, 0x00 }, { 94, 0x00 }, { 95, 0x00 },
+ { 96, 0x00 }, { 97, 0x00 }, { 98, 0x00 }, { 99, 0x00 },
+ { 100, 0x00 }, { 101, 0x00 }, { 102, 0x02 }, { 103, 0x00 },
+ { 104, 0x00 }, { 105, 0x00 }, { 106, 0x00 }, { 107, 0x00 },
+ { 108, 0x00 }, { 109, 0x00 },
+};
+
+static const struct regmap_config aic3x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = DAC_ICC_ADJ,
+ .reg_defaults = aic3x_reg,
+ .num_reg_defaults = ARRAY_SIZE(aic3x_reg),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
+ SOC_SINGLE_EXT(xname, reg, shift, mask, invert, \
+ snd_soc_dapm_get_volsw, snd_soc_dapm_put_volsw_aic3x)
+
+/*
+ * All input lines are connected when !0xf and disconnected with 0xf bit field,
+ * so we have to use specific dapm_put call for input mixer
+ */
+static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned short val;
+ struct snd_soc_dapm_update update;
+ int connect, change;
+
+ val = (ucontrol->value.integer.value[0] & mask);
+
+ mask = 0xf;
+ if (val)
+ val = mask;
+
+ connect = !!val;
+
+ if (invert)
+ val = mask - val;
+
+ mask <<= shift;
+ val <<= shift;
+
+ change = snd_soc_test_bits(codec, reg, mask, val);
+ if (change) {
+ update.kcontrol = kcontrol;
+ update.reg = reg;
+ update.mask = mask;
+ update.val = val;
+
+ snd_soc_dapm_mixer_update_power(&codec->dapm, kcontrol, connect,
+ &update);
+ }
+
+ return change;
+}
+
+/*
+ * mic bias power on/off share the same register bits with
+ * output voltage of mic bias. when power on mic bias, we
+ * need reclaim it to voltage value.
+ * 0x0 = Powered off
+ * 0x1 = MICBIAS output is powered to 2.0V,
+ * 0x2 = MICBIAS output is powered to 2.5V
+ * 0x3 = MICBIAS output is connected to AVDD
+ */
+static int mic_bias_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* change mic bias voltage to user defined */
+ snd_soc_update_bits(codec, MICBIAS_CTRL,
+ MICBIAS_LEVEL_MASK,
+ aic3x->micbias_vg << MICBIAS_LEVEL_SHIFT);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, MICBIAS_CTRL,
+ MICBIAS_LEVEL_MASK, 0);
+ break;
+ }
+ return 0;
+}
+
+static const char * const aic3x_left_dac_mux[] = {
+ "DAC_L1", "DAC_L3", "DAC_L2" };
+static SOC_ENUM_SINGLE_DECL(aic3x_left_dac_enum, DAC_LINE_MUX, 6,
+ aic3x_left_dac_mux);
+
+static const char * const aic3x_right_dac_mux[] = {
+ "DAC_R1", "DAC_R3", "DAC_R2" };
+static SOC_ENUM_SINGLE_DECL(aic3x_right_dac_enum, DAC_LINE_MUX, 4,
+ aic3x_right_dac_mux);
+
+static const char * const aic3x_left_hpcom_mux[] = {
+ "differential of HPLOUT", "constant VCM", "single-ended" };
+static SOC_ENUM_SINGLE_DECL(aic3x_left_hpcom_enum, HPLCOM_CFG, 4,
+ aic3x_left_hpcom_mux);
+
+static const char * const aic3x_right_hpcom_mux[] = {
+ "differential of HPROUT", "constant VCM", "single-ended",
+ "differential of HPLCOM", "external feedback" };
+static SOC_ENUM_SINGLE_DECL(aic3x_right_hpcom_enum, HPRCOM_CFG, 3,
+ aic3x_right_hpcom_mux);
+
+static const char * const aic3x_linein_mode_mux[] = {
+ "single-ended", "differential" };
+static SOC_ENUM_SINGLE_DECL(aic3x_line1l_2_l_enum, LINE1L_2_LADC_CTRL, 7,
+ aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line1l_2_r_enum, LINE1L_2_RADC_CTRL, 7,
+ aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line1r_2_l_enum, LINE1R_2_LADC_CTRL, 7,
+ aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line1r_2_r_enum, LINE1R_2_RADC_CTRL, 7,
+ aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line2l_2_ldac_enum, LINE2L_2_LADC_CTRL, 7,
+ aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line2r_2_rdac_enum, LINE2R_2_RADC_CTRL, 7,
+ aic3x_linein_mode_mux);
+
+static const char * const aic3x_adc_hpf[] = {
+ "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" };
+static SOC_ENUM_DOUBLE_DECL(aic3x_adc_hpf_enum, AIC3X_CODEC_DFILT_CTRL, 6, 4,
+ aic3x_adc_hpf);
+
+static const char * const aic3x_agc_level[] = {
+ "-5.5dB", "-8dB", "-10dB", "-12dB",
+ "-14dB", "-17dB", "-20dB", "-24dB" };
+static SOC_ENUM_SINGLE_DECL(aic3x_lagc_level_enum, LAGC_CTRL_A, 4,
+ aic3x_agc_level);
+static SOC_ENUM_SINGLE_DECL(aic3x_ragc_level_enum, RAGC_CTRL_A, 4,
+ aic3x_agc_level);
+
+static const char * const aic3x_agc_attack[] = {
+ "8ms", "11ms", "16ms", "20ms" };
+static SOC_ENUM_SINGLE_DECL(aic3x_lagc_attack_enum, LAGC_CTRL_A, 2,
+ aic3x_agc_attack);
+static SOC_ENUM_SINGLE_DECL(aic3x_ragc_attack_enum, RAGC_CTRL_A, 2,
+ aic3x_agc_attack);
+
+static const char * const aic3x_agc_decay[] = {
+ "100ms", "200ms", "400ms", "500ms" };
+static SOC_ENUM_SINGLE_DECL(aic3x_lagc_decay_enum, LAGC_CTRL_A, 0,
+ aic3x_agc_decay);
+static SOC_ENUM_SINGLE_DECL(aic3x_ragc_decay_enum, RAGC_CTRL_A, 0,
+ aic3x_agc_decay);
+
+static const char * const aic3x_poweron_time[] = {
+ "0us", "10us", "100us", "1ms", "10ms", "50ms",
+ "100ms", "200ms", "400ms", "800ms", "2s", "4s" };
+static SOC_ENUM_SINGLE_DECL(aic3x_poweron_time_enum, HPOUT_POP_REDUCTION, 4,
+ aic3x_poweron_time);
+
+static const char * const aic3x_rampup_step[] = { "0ms", "1ms", "2ms", "4ms" };
+static SOC_ENUM_SINGLE_DECL(aic3x_rampup_step_enum, HPOUT_POP_REDUCTION, 2,
+ aic3x_rampup_step);
+
+/*
+ * DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(dac_tlv, -6350, 50, 0);
+/* ADC PGA gain volumes. From 0 to 59.5 dB in 0.5 dB steps */
+static DECLARE_TLV_DB_SCALE(adc_tlv, 0, 50, 0);
+/*
+ * Output stage volumes. From -78.3 to 0 dB. Muted below -78.3 dB.
+ * Step size is approximately 0.5 dB over most of the scale but increasing
+ * near the very low levels.
+ * Define dB scale so that it is mostly correct for range about -55 to 0 dB
+ * but having increasing dB difference below that (and where it doesn't count
+ * so much). This setting shows -50 dB (actual is -50.3 dB) for register
+ * value 100 and -58.5 dB (actual is -78.3 dB) for register value 117.
+ */
+static DECLARE_TLV_DB_SCALE(output_stage_tlv, -5900, 50, 1);
+
+static const struct snd_kcontrol_new aic3x_snd_controls[] = {
+ /* Output */
+ SOC_DOUBLE_R_TLV("PCM Playback Volume",
+ LDAC_VOL, RDAC_VOL, 0, 0x7f, 1, dac_tlv),
+
+ /*
+ * Output controls that map to output mixer switches. Note these are
+ * only for swapped L-to-R and R-to-L routes. See below stereo controls
+ * for direct L-to-L and R-to-R routes.
+ */
+ SOC_SINGLE_TLV("Left Line Mixer PGAR Bypass Volume",
+ PGAR_2_LLOPM_VOL, 0, 118, 1, output_stage_tlv),
+ SOC_SINGLE_TLV("Left Line Mixer DACR1 Playback Volume",
+ DACR1_2_LLOPM_VOL, 0, 118, 1, output_stage_tlv),
+
+ SOC_SINGLE_TLV("Right Line Mixer PGAL Bypass Volume",
+ PGAL_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv),
+ SOC_SINGLE_TLV("Right Line Mixer DACL1 Playback Volume",
+ DACL1_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv),
+
+ SOC_SINGLE_TLV("Left HP Mixer PGAR Bypass Volume",
+ PGAR_2_HPLOUT_VOL, 0, 118, 1, output_stage_tlv),
+ SOC_SINGLE_TLV("Left HP Mixer DACR1 Playback Volume",
+ DACR1_2_HPLOUT_VOL, 0, 118, 1, output_stage_tlv),
+
+ SOC_SINGLE_TLV("Right HP Mixer PGAL Bypass Volume",
+ PGAL_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv),
+ SOC_SINGLE_TLV("Right HP Mixer DACL1 Playback Volume",
+ DACL1_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv),
+
+ SOC_SINGLE_TLV("Left HPCOM Mixer PGAR Bypass Volume",
+ PGAR_2_HPLCOM_VOL, 0, 118, 1, output_stage_tlv),
+ SOC_SINGLE_TLV("Left HPCOM Mixer DACR1 Playback Volume",
+ DACR1_2_HPLCOM_VOL, 0, 118, 1, output_stage_tlv),
+
+ SOC_SINGLE_TLV("Right HPCOM Mixer PGAL Bypass Volume",
+ PGAL_2_HPRCOM_VOL, 0, 118, 1, output_stage_tlv),
+ SOC_SINGLE_TLV("Right HPCOM Mixer DACL1 Playback Volume",
+ DACL1_2_HPRCOM_VOL, 0, 118, 1, output_stage_tlv),
+
+ /* Stereo output controls for direct L-to-L and R-to-R routes */
+ SOC_DOUBLE_R_TLV("Line PGA Bypass Volume",
+ PGAL_2_LLOPM_VOL, PGAR_2_RLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
+ SOC_DOUBLE_R_TLV("Line DAC Playback Volume",
+ DACL1_2_LLOPM_VOL, DACR1_2_RLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
+
+ SOC_DOUBLE_R_TLV("HP PGA Bypass Volume",
+ PGAL_2_HPLOUT_VOL, PGAR_2_HPROUT_VOL,
+ 0, 118, 1, output_stage_tlv),
+ SOC_DOUBLE_R_TLV("HP DAC Playback Volume",
+ DACL1_2_HPLOUT_VOL, DACR1_2_HPROUT_VOL,
+ 0, 118, 1, output_stage_tlv),
+
+ SOC_DOUBLE_R_TLV("HPCOM PGA Bypass Volume",
+ PGAL_2_HPLCOM_VOL, PGAR_2_HPRCOM_VOL,
+ 0, 118, 1, output_stage_tlv),
+ SOC_DOUBLE_R_TLV("HPCOM DAC Playback Volume",
+ DACL1_2_HPLCOM_VOL, DACR1_2_HPRCOM_VOL,
+ 0, 118, 1, output_stage_tlv),
+
+ /* Output pin mute controls */
+ SOC_DOUBLE_R("Line Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3,
+ 0x01, 0),
+ SOC_DOUBLE_R("HP Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,
+ 0x01, 0),
+ SOC_DOUBLE_R("HPCOM Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3,
+ 0x01, 0),
+
+ /*
+ * Note: enable Automatic input Gain Controller with care. It can
+ * adjust PGA to max value when ADC is on and will never go back.
+ */
+ SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0),
+ SOC_ENUM("Left AGC Target level", aic3x_lagc_level_enum),
+ SOC_ENUM("Right AGC Target level", aic3x_ragc_level_enum),
+ SOC_ENUM("Left AGC Attack time", aic3x_lagc_attack_enum),
+ SOC_ENUM("Right AGC Attack time", aic3x_ragc_attack_enum),
+ SOC_ENUM("Left AGC Decay time", aic3x_lagc_decay_enum),
+ SOC_ENUM("Right AGC Decay time", aic3x_ragc_decay_enum),
+
+ /* De-emphasis */
+ SOC_DOUBLE("De-emphasis Switch", AIC3X_CODEC_DFILT_CTRL, 2, 0, 0x01, 0),
+
+ /* Input */
+ SOC_DOUBLE_R_TLV("PGA Capture Volume", LADC_VOL, RADC_VOL,
+ 0, 119, 0, adc_tlv),
+ SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
+
+ SOC_ENUM("ADC HPF Cut-off", aic3x_adc_hpf_enum),
+
+ /* Pop reduction */
+ SOC_ENUM("Output Driver Power-On time", aic3x_poweron_time_enum),
+ SOC_ENUM("Output Driver Ramp-up step", aic3x_rampup_step_enum),
+};
+
+/* For other than tlv320aic3104 */
+static const struct snd_kcontrol_new aic3x_extra_snd_controls[] = {
+ /*
+ * Output controls that map to output mixer switches. Note these are
+ * only for swapped L-to-R and R-to-L routes. See below stereo controls
+ * for direct L-to-L and R-to-R routes.
+ */
+ SOC_SINGLE_TLV("Left Line Mixer Line2R Bypass Volume",
+ LINE2R_2_LLOPM_VOL, 0, 118, 1, output_stage_tlv),
+
+ SOC_SINGLE_TLV("Right Line Mixer Line2L Bypass Volume",
+ LINE2L_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv),
+
+ SOC_SINGLE_TLV("Left HP Mixer Line2R Bypass Volume",
+ LINE2R_2_HPLOUT_VOL, 0, 118, 1, output_stage_tlv),
+
+ SOC_SINGLE_TLV("Right HP Mixer Line2L Bypass Volume",
+ LINE2L_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv),
+
+ SOC_SINGLE_TLV("Left HPCOM Mixer Line2R Bypass Volume",
+ LINE2R_2_HPLCOM_VOL, 0, 118, 1, output_stage_tlv),
+
+ SOC_SINGLE_TLV("Right HPCOM Mixer Line2L Bypass Volume",
+ LINE2L_2_HPRCOM_VOL, 0, 118, 1, output_stage_tlv),
+
+ /* Stereo output controls for direct L-to-L and R-to-R routes */
+ SOC_DOUBLE_R_TLV("Line Line2 Bypass Volume",
+ LINE2L_2_LLOPM_VOL, LINE2R_2_RLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
+
+ SOC_DOUBLE_R_TLV("HP Line2 Bypass Volume",
+ LINE2L_2_HPLOUT_VOL, LINE2R_2_HPROUT_VOL,
+ 0, 118, 1, output_stage_tlv),
+
+ SOC_DOUBLE_R_TLV("HPCOM Line2 Bypass Volume",
+ LINE2L_2_HPLCOM_VOL, LINE2R_2_HPRCOM_VOL,
+ 0, 118, 1, output_stage_tlv),
+};
+
+static const struct snd_kcontrol_new aic3x_mono_controls[] = {
+ SOC_DOUBLE_R_TLV("Mono Line2 Bypass Volume",
+ LINE2L_2_MONOLOPM_VOL, LINE2R_2_MONOLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
+ SOC_DOUBLE_R_TLV("Mono PGA Bypass Volume",
+ PGAL_2_MONOLOPM_VOL, PGAR_2_MONOLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
+ SOC_DOUBLE_R_TLV("Mono DAC Playback Volume",
+ DACL1_2_MONOLOPM_VOL, DACR1_2_MONOLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
+
+ SOC_SINGLE("Mono Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0),
+};
+
+/*
+ * Class-D amplifier gain. From 0 to 18 dB in 6 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(classd_amp_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new aic3x_classd_amp_gain_ctrl =
+ SOC_DOUBLE_TLV("Class-D Playback Volume", CLASSD_CTRL, 6, 4, 3, 0, classd_amp_tlv);
+
+/* Left DAC Mux */
+static const struct snd_kcontrol_new aic3x_left_dac_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_left_dac_enum);
+
+/* Right DAC Mux */
+static const struct snd_kcontrol_new aic3x_right_dac_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_right_dac_enum);
+
+/* Left HPCOM Mux */
+static const struct snd_kcontrol_new aic3x_left_hpcom_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_left_hpcom_enum);
+
+/* Right HPCOM Mux */
+static const struct snd_kcontrol_new aic3x_right_hpcom_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_right_hpcom_enum);
+
+/* Left Line Mixer */
+static const struct snd_kcontrol_new aic3x_left_line_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_LLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_LLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_LLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_LLOPM_VOL, 7, 1, 0),
+ /* Not on tlv320aic3104 */
+ SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_LLOPM_VOL, 7, 1, 0),
+};
+
+/* Right Line Mixer */
+static const struct snd_kcontrol_new aic3x_right_line_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_RLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_RLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_RLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_RLOPM_VOL, 7, 1, 0),
+ /* Not on tlv320aic3104 */
+ SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_RLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0),
+};
+
+/* Mono Mixer */
+static const struct snd_kcontrol_new aic3x_mono_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_MONOLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_MONOLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_MONOLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_MONOLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_MONOLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_MONOLOPM_VOL, 7, 1, 0),
+};
+
+/* Left HP Mixer */
+static const struct snd_kcontrol_new aic3x_left_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_HPLOUT_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_HPLOUT_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_HPLOUT_VOL, 7, 1, 0),
+ /* Not on tlv320aic3104 */
+ SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPLOUT_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPLOUT_VOL, 7, 1, 0),
+};
+
+/* Right HP Mixer */
+static const struct snd_kcontrol_new aic3x_right_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_HPROUT_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_HPROUT_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_HPROUT_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_HPROUT_VOL, 7, 1, 0),
+ /* Not on tlv320aic3104 */
+ SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPROUT_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPROUT_VOL, 7, 1, 0),
+};
+
+/* Left HPCOM Mixer */
+static const struct snd_kcontrol_new aic3x_left_hpcom_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_HPLCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_HPLCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_HPLCOM_VOL, 7, 1, 0),
+ /* Not on tlv320aic3104 */
+ SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPLCOM_VOL, 7, 1, 0),
+};
+
+/* Right HPCOM Mixer */
+static const struct snd_kcontrol_new aic3x_right_hpcom_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_HPRCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_HPRCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_HPRCOM_VOL, 7, 1, 0),
+ /* Not on tlv320aic3104 */
+ SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPRCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0),
+};
+
+/* Left PGA Mixer */
+static const struct snd_kcontrol_new aic3x_left_pga_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_LADC_CTRL, 3, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Line2L Switch", LINE2L_2_LADC_CTRL, 3, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_LADC_CTRL, 0, 1, 1),
+};
+
+/* Right PGA Mixer */
+static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_RADC_CTRL, 3, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Line2R Switch", LINE2R_2_RADC_CTRL, 3, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_RADC_CTRL, 4, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1),
+};
+
+/* Left PGA Mixer for tlv320aic3104 */
+static const struct snd_kcontrol_new aic3104_left_pga_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_LADC_CTRL, 3, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Mic2L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Mic2R Switch", MIC3LR_2_LADC_CTRL, 0, 1, 1),
+};
+
+/* Right PGA Mixer for tlv320aic3104 */
+static const struct snd_kcontrol_new aic3104_right_pga_mixer_controls[] = {
+ SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_RADC_CTRL, 3, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Mic2L Switch", MIC3LR_2_RADC_CTRL, 4, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Mic2R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1),
+};
+
+/* Left Line1 Mux */
+static const struct snd_kcontrol_new aic3x_left_line1l_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_line1l_2_l_enum);
+static const struct snd_kcontrol_new aic3x_right_line1l_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_line1l_2_r_enum);
+
+/* Right Line1 Mux */
+static const struct snd_kcontrol_new aic3x_right_line1r_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_line1r_2_r_enum);
+static const struct snd_kcontrol_new aic3x_left_line1r_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_line1r_2_l_enum);
+
+/* Left Line2 Mux */
+static const struct snd_kcontrol_new aic3x_left_line2_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_line2l_2_ldac_enum);
+
+/* Right Line2 Mux */
+static const struct snd_kcontrol_new aic3x_right_line2_mux_controls =
+SOC_DAPM_ENUM("Route", aic3x_line2r_2_rdac_enum);
+
+static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
+ /* Left DAC to Left Outputs */
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", DAC_PWR, 7, 0),
+ SND_SOC_DAPM_MUX("Left DAC Mux", SND_SOC_NOPM, 0, 0,
+ &aic3x_left_dac_mux_controls),
+ SND_SOC_DAPM_MUX("Left HPCOM Mux", SND_SOC_NOPM, 0, 0,
+ &aic3x_left_hpcom_mux_controls),
+ SND_SOC_DAPM_PGA("Left Line Out", LLOPM_CTRL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left HP Out", HPLOUT_CTRL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left HP Com", HPLCOM_CTRL, 0, 0, NULL, 0),
+
+ /* Right DAC to Right Outputs */
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", DAC_PWR, 6, 0),
+ SND_SOC_DAPM_MUX("Right DAC Mux", SND_SOC_NOPM, 0, 0,
+ &aic3x_right_dac_mux_controls),
+ SND_SOC_DAPM_MUX("Right HPCOM Mux", SND_SOC_NOPM, 0, 0,
+ &aic3x_right_hpcom_mux_controls),
+ SND_SOC_DAPM_PGA("Right Line Out", RLOPM_CTRL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right HP Out", HPROUT_CTRL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right HP Com", HPRCOM_CTRL, 0, 0, NULL, 0),
+
+ /* Inputs to Left ADC */
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0),
+ SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0,
+ &aic3x_left_line1l_mux_controls),
+ SND_SOC_DAPM_MUX("Left Line1R Mux", SND_SOC_NOPM, 0, 0,
+ &aic3x_left_line1r_mux_controls),
+
+ /* Inputs to Right ADC */
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
+ LINE1R_2_RADC_CTRL, 2, 0),
+ SND_SOC_DAPM_MUX("Right Line1L Mux", SND_SOC_NOPM, 0, 0,
+ &aic3x_right_line1l_mux_controls),
+ SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0,
+ &aic3x_right_line1r_mux_controls),
+
+ /* Mic Bias */
+ SND_SOC_DAPM_SUPPLY("Mic Bias", MICBIAS_CTRL, 6, 0,
+ mic_bias_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_OUTPUT("LLOUT"),
+ SND_SOC_DAPM_OUTPUT("RLOUT"),
+ SND_SOC_DAPM_OUTPUT("HPLOUT"),
+ SND_SOC_DAPM_OUTPUT("HPROUT"),
+ SND_SOC_DAPM_OUTPUT("HPLCOM"),
+ SND_SOC_DAPM_OUTPUT("HPRCOM"),
+
+ SND_SOC_DAPM_INPUT("LINE1L"),
+ SND_SOC_DAPM_INPUT("LINE1R"),
+
+ /*
+ * Virtual output pin to detection block inside codec. This can be
+ * used to keep codec bias on if gpio or detection features are needed.
+ * Force pin on or construct a path with an input jack and mic bias
+ * widgets.
+ */
+ SND_SOC_DAPM_OUTPUT("Detection"),
+};
+
+/* For other than tlv320aic3104 */
+static const struct snd_soc_dapm_widget aic3x_extra_dapm_widgets[] = {
+ /* Inputs to Left ADC */
+ SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_left_pga_mixer_controls[0],
+ ARRAY_SIZE(aic3x_left_pga_mixer_controls)),
+ SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0,
+ &aic3x_left_line2_mux_controls),
+
+ /* Inputs to Right ADC */
+ SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_right_pga_mixer_controls[0],
+ ARRAY_SIZE(aic3x_right_pga_mixer_controls)),
+ SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
+ &aic3x_right_line2_mux_controls),
+
+ /*
+ * Not a real mic bias widget but similar function. This is for dynamic
+ * control of GPIO1 digital mic modulator clock output function when
+ * using digital mic.
+ */
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "GPIO1 dmic modclk",
+ AIC3X_GPIO1_REG, 4, 0xf,
+ AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK,
+ AIC3X_GPIO1_FUNC_DISABLED),
+
+ /*
+ * Also similar function like mic bias. Selects digital mic with
+ * configurable oversampling rate instead of ADC converter.
+ */
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 128",
+ AIC3X_ASD_INTF_CTRLA, 0, 3, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 64",
+ AIC3X_ASD_INTF_CTRLA, 0, 3, 2, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 32",
+ AIC3X_ASD_INTF_CTRLA, 0, 3, 3, 0),
+
+ /* Output mixers */
+ SND_SOC_DAPM_MIXER("Left Line Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_left_line_mixer_controls[0],
+ ARRAY_SIZE(aic3x_left_line_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Line Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_right_line_mixer_controls[0],
+ ARRAY_SIZE(aic3x_right_line_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_left_hp_mixer_controls[0],
+ ARRAY_SIZE(aic3x_left_hp_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_right_hp_mixer_controls[0],
+ ARRAY_SIZE(aic3x_right_hp_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Left HPCOM Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_left_hpcom_mixer_controls[0],
+ ARRAY_SIZE(aic3x_left_hpcom_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right HPCOM Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_right_hpcom_mixer_controls[0],
+ ARRAY_SIZE(aic3x_right_hpcom_mixer_controls)),
+
+ SND_SOC_DAPM_INPUT("MIC3L"),
+ SND_SOC_DAPM_INPUT("MIC3R"),
+ SND_SOC_DAPM_INPUT("LINE2L"),
+ SND_SOC_DAPM_INPUT("LINE2R"),
+};
+
+/* For tlv320aic3104 */
+static const struct snd_soc_dapm_widget aic3104_extra_dapm_widgets[] = {
+ /* Inputs to Left ADC */
+ SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3104_left_pga_mixer_controls[0],
+ ARRAY_SIZE(aic3104_left_pga_mixer_controls)),
+
+ /* Inputs to Right ADC */
+ SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3104_right_pga_mixer_controls[0],
+ ARRAY_SIZE(aic3104_right_pga_mixer_controls)),
+
+ /* Output mixers */
+ SND_SOC_DAPM_MIXER("Left Line Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_left_line_mixer_controls[0],
+ ARRAY_SIZE(aic3x_left_line_mixer_controls) - 2),
+ SND_SOC_DAPM_MIXER("Right Line Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_right_line_mixer_controls[0],
+ ARRAY_SIZE(aic3x_right_line_mixer_controls) - 2),
+ SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_left_hp_mixer_controls[0],
+ ARRAY_SIZE(aic3x_left_hp_mixer_controls) - 2),
+ SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_right_hp_mixer_controls[0],
+ ARRAY_SIZE(aic3x_right_hp_mixer_controls) - 2),
+ SND_SOC_DAPM_MIXER("Left HPCOM Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_left_hpcom_mixer_controls[0],
+ ARRAY_SIZE(aic3x_left_hpcom_mixer_controls) - 2),
+ SND_SOC_DAPM_MIXER("Right HPCOM Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_right_hpcom_mixer_controls[0],
+ ARRAY_SIZE(aic3x_right_hpcom_mixer_controls) - 2),
+
+ SND_SOC_DAPM_INPUT("MIC2L"),
+ SND_SOC_DAPM_INPUT("MIC2R"),
+};
+
+static const struct snd_soc_dapm_widget aic3x_dapm_mono_widgets[] = {
+ /* Mono Output */
+ SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_mono_mixer_controls[0],
+ ARRAY_SIZE(aic3x_mono_mixer_controls)),
+
+ SND_SOC_DAPM_OUTPUT("MONO_LOUT"),
+};
+
+static const struct snd_soc_dapm_widget aic3007_dapm_widgets[] = {
+ /* Class-D outputs */
+ SND_SOC_DAPM_PGA("Left Class-D Out", CLASSD_CTRL, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Class-D Out", CLASSD_CTRL, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("SPOP"),
+ SND_SOC_DAPM_OUTPUT("SPOM"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ /* Left Input */
+ {"Left Line1L Mux", "single-ended", "LINE1L"},
+ {"Left Line1L Mux", "differential", "LINE1L"},
+ {"Left Line1R Mux", "single-ended", "LINE1R"},
+ {"Left Line1R Mux", "differential", "LINE1R"},
+
+ {"Left PGA Mixer", "Line1L Switch", "Left Line1L Mux"},
+ {"Left PGA Mixer", "Line1R Switch", "Left Line1R Mux"},
+
+ {"Left ADC", NULL, "Left PGA Mixer"},
+
+ /* Right Input */
+ {"Right Line1R Mux", "single-ended", "LINE1R"},
+ {"Right Line1R Mux", "differential", "LINE1R"},
+ {"Right Line1L Mux", "single-ended", "LINE1L"},
+ {"Right Line1L Mux", "differential", "LINE1L"},
+
+ {"Right PGA Mixer", "Line1L Switch", "Right Line1L Mux"},
+ {"Right PGA Mixer", "Line1R Switch", "Right Line1R Mux"},
+
+ {"Right ADC", NULL, "Right PGA Mixer"},
+
+ /* Left DAC Output */
+ {"Left DAC Mux", "DAC_L1", "Left DAC"},
+ {"Left DAC Mux", "DAC_L2", "Left DAC"},
+ {"Left DAC Mux", "DAC_L3", "Left DAC"},
+
+ /* Right DAC Output */
+ {"Right DAC Mux", "DAC_R1", "Right DAC"},
+ {"Right DAC Mux", "DAC_R2", "Right DAC"},
+ {"Right DAC Mux", "DAC_R3", "Right DAC"},
+
+ /* Left Line Output */
+ {"Left Line Mixer", "PGAL Bypass Switch", "Left PGA Mixer"},
+ {"Left Line Mixer", "DACL1 Switch", "Left DAC Mux"},
+ {"Left Line Mixer", "PGAR Bypass Switch", "Right PGA Mixer"},
+ {"Left Line Mixer", "DACR1 Switch", "Right DAC Mux"},
+
+ {"Left Line Out", NULL, "Left Line Mixer"},
+ {"Left Line Out", NULL, "Left DAC Mux"},
+ {"LLOUT", NULL, "Left Line Out"},
+
+ /* Right Line Output */
+ {"Right Line Mixer", "PGAL Bypass Switch", "Left PGA Mixer"},
+ {"Right Line Mixer", "DACL1 Switch", "Left DAC Mux"},
+ {"Right Line Mixer", "PGAR Bypass Switch", "Right PGA Mixer"},
+ {"Right Line Mixer", "DACR1 Switch", "Right DAC Mux"},
+
+ {"Right Line Out", NULL, "Right Line Mixer"},
+ {"Right Line Out", NULL, "Right DAC Mux"},
+ {"RLOUT", NULL, "Right Line Out"},
+
+ /* Left HP Output */
+ {"Left HP Mixer", "PGAL Bypass Switch", "Left PGA Mixer"},
+ {"Left HP Mixer", "DACL1 Switch", "Left DAC Mux"},
+ {"Left HP Mixer", "PGAR Bypass Switch", "Right PGA Mixer"},
+ {"Left HP Mixer", "DACR1 Switch", "Right DAC Mux"},
+
+ {"Left HP Out", NULL, "Left HP Mixer"},
+ {"Left HP Out", NULL, "Left DAC Mux"},
+ {"HPLOUT", NULL, "Left HP Out"},
+
+ /* Right HP Output */
+ {"Right HP Mixer", "PGAL Bypass Switch", "Left PGA Mixer"},
+ {"Right HP Mixer", "DACL1 Switch", "Left DAC Mux"},
+ {"Right HP Mixer", "PGAR Bypass Switch", "Right PGA Mixer"},
+ {"Right HP Mixer", "DACR1 Switch", "Right DAC Mux"},
+
+ {"Right HP Out", NULL, "Right HP Mixer"},
+ {"Right HP Out", NULL, "Right DAC Mux"},
+ {"HPROUT", NULL, "Right HP Out"},
+
+ /* Left HPCOM Output */
+ {"Left HPCOM Mixer", "PGAL Bypass Switch", "Left PGA Mixer"},
+ {"Left HPCOM Mixer", "DACL1 Switch", "Left DAC Mux"},
+ {"Left HPCOM Mixer", "PGAR Bypass Switch", "Right PGA Mixer"},
+ {"Left HPCOM Mixer", "DACR1 Switch", "Right DAC Mux"},
+
+ {"Left HPCOM Mux", "differential of HPLOUT", "Left HP Mixer"},
+ {"Left HPCOM Mux", "constant VCM", "Left HPCOM Mixer"},
+ {"Left HPCOM Mux", "single-ended", "Left HPCOM Mixer"},
+ {"Left HP Com", NULL, "Left HPCOM Mux"},
+ {"HPLCOM", NULL, "Left HP Com"},
+
+ /* Right HPCOM Output */
+ {"Right HPCOM Mixer", "PGAL Bypass Switch", "Left PGA Mixer"},
+ {"Right HPCOM Mixer", "DACL1 Switch", "Left DAC Mux"},
+ {"Right HPCOM Mixer", "PGAR Bypass Switch", "Right PGA Mixer"},
+ {"Right HPCOM Mixer", "DACR1 Switch", "Right DAC Mux"},
+
+ {"Right HPCOM Mux", "differential of HPROUT", "Right HP Mixer"},
+ {"Right HPCOM Mux", "constant VCM", "Right HPCOM Mixer"},
+ {"Right HPCOM Mux", "single-ended", "Right HPCOM Mixer"},
+ {"Right HPCOM Mux", "differential of HPLCOM", "Left HPCOM Mixer"},
+ {"Right HPCOM Mux", "external feedback", "Right HPCOM Mixer"},
+ {"Right HP Com", NULL, "Right HPCOM Mux"},
+ {"HPRCOM", NULL, "Right HP Com"},
+};
+
+/* For other than tlv320aic3104 */
+static const struct snd_soc_dapm_route intercon_extra[] = {
+ /* Left Input */
+ {"Left Line2L Mux", "single-ended", "LINE2L"},
+ {"Left Line2L Mux", "differential", "LINE2L"},
+
+ {"Left PGA Mixer", "Line2L Switch", "Left Line2L Mux"},
+ {"Left PGA Mixer", "Mic3L Switch", "MIC3L"},
+ {"Left PGA Mixer", "Mic3R Switch", "MIC3R"},
+
+ {"Left ADC", NULL, "GPIO1 dmic modclk"},
+
+ /* Right Input */
+ {"Right Line2R Mux", "single-ended", "LINE2R"},
+ {"Right Line2R Mux", "differential", "LINE2R"},
+
+ {"Right PGA Mixer", "Line2R Switch", "Right Line2R Mux"},
+ {"Right PGA Mixer", "Mic3L Switch", "MIC3L"},
+ {"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
+
+ {"Right ADC", NULL, "GPIO1 dmic modclk"},
+
+ /*
+ * Logical path between digital mic enable and GPIO1 modulator clock
+ * output function
+ */
+ {"GPIO1 dmic modclk", NULL, "DMic Rate 128"},
+ {"GPIO1 dmic modclk", NULL, "DMic Rate 64"},
+ {"GPIO1 dmic modclk", NULL, "DMic Rate 32"},
+
+ /* Left Line Output */
+ {"Left Line Mixer", "Line2L Bypass Switch", "Left Line2L Mux"},
+ {"Left Line Mixer", "Line2R Bypass Switch", "Right Line2R Mux"},
+
+ /* Right Line Output */
+ {"Right Line Mixer", "Line2L Bypass Switch", "Left Line2L Mux"},
+ {"Right Line Mixer", "Line2R Bypass Switch", "Right Line2R Mux"},
+
+ /* Left HP Output */
+ {"Left HP Mixer", "Line2L Bypass Switch", "Left Line2L Mux"},
+ {"Left HP Mixer", "Line2R Bypass Switch", "Right Line2R Mux"},
+
+ /* Right HP Output */
+ {"Right HP Mixer", "Line2L Bypass Switch", "Left Line2L Mux"},
+ {"Right HP Mixer", "Line2R Bypass Switch", "Right Line2R Mux"},
+
+ /* Left HPCOM Output */
+ {"Left HPCOM Mixer", "Line2L Bypass Switch", "Left Line2L Mux"},
+ {"Left HPCOM Mixer", "Line2R Bypass Switch", "Right Line2R Mux"},
+
+ /* Right HPCOM Output */
+ {"Right HPCOM Mixer", "Line2L Bypass Switch", "Left Line2L Mux"},
+ {"Right HPCOM Mixer", "Line2R Bypass Switch", "Right Line2R Mux"},
+};
+
+/* For tlv320aic3104 */
+static const struct snd_soc_dapm_route intercon_extra_3104[] = {
+ /* Left Input */
+ {"Left PGA Mixer", "Mic2L Switch", "MIC2L"},
+ {"Left PGA Mixer", "Mic2R Switch", "MIC2R"},
+
+ /* Right Input */
+ {"Right PGA Mixer", "Mic2L Switch", "MIC2L"},
+ {"Right PGA Mixer", "Mic2R Switch", "MIC2R"},
+};
+
+static const struct snd_soc_dapm_route intercon_mono[] = {
+ /* Mono Output */
+ {"Mono Mixer", "Line2L Bypass Switch", "Left Line2L Mux"},
+ {"Mono Mixer", "PGAL Bypass Switch", "Left PGA Mixer"},
+ {"Mono Mixer", "DACL1 Switch", "Left DAC Mux"},
+ {"Mono Mixer", "Line2R Bypass Switch", "Right Line2R Mux"},
+ {"Mono Mixer", "PGAR Bypass Switch", "Right PGA Mixer"},
+ {"Mono Mixer", "DACR1 Switch", "Right DAC Mux"},
+ {"Mono Out", NULL, "Mono Mixer"},
+ {"MONO_LOUT", NULL, "Mono Out"},
+};
+
+static const struct snd_soc_dapm_route intercon_3007[] = {
+ /* Class-D outputs */
+ {"Left Class-D Out", NULL, "Left Line Out"},
+ {"Right Class-D Out", NULL, "Left Line Out"},
+ {"SPOP", NULL, "Left Class-D Out"},
+ {"SPOM", NULL, "Right Class-D Out"},
+};
+
+static int aic3x_add_widgets(struct snd_soc_codec *codec)
+{
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ switch (aic3x->model) {
+ case AIC3X_MODEL_3X:
+ case AIC3X_MODEL_33:
+ snd_soc_dapm_new_controls(dapm, aic3x_extra_dapm_widgets,
+ ARRAY_SIZE(aic3x_extra_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon_extra,
+ ARRAY_SIZE(intercon_extra));
+ snd_soc_dapm_new_controls(dapm, aic3x_dapm_mono_widgets,
+ ARRAY_SIZE(aic3x_dapm_mono_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon_mono,
+ ARRAY_SIZE(intercon_mono));
+ break;
+ case AIC3X_MODEL_3007:
+ snd_soc_dapm_new_controls(dapm, aic3x_extra_dapm_widgets,
+ ARRAY_SIZE(aic3x_extra_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon_extra,
+ ARRAY_SIZE(intercon_extra));
+ snd_soc_dapm_new_controls(dapm, aic3007_dapm_widgets,
+ ARRAY_SIZE(aic3007_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon_3007,
+ ARRAY_SIZE(intercon_3007));
+ break;
+ case AIC3X_MODEL_3104:
+ snd_soc_dapm_new_controls(dapm, aic3104_extra_dapm_widgets,
+ ARRAY_SIZE(aic3104_extra_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon_extra_3104,
+ ARRAY_SIZE(intercon_extra_3104));
+ break;
+ }
+
+ return 0;
+}
+
+static int aic3x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
+ u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
+ u16 d, pll_d = 1;
+ int clk;
+
+ /* select data word length */
+ data = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ data |= (0x01 << 4);
+ break;
+ case 24:
+ data |= (0x02 << 4);
+ break;
+ case 32:
+ data |= (0x03 << 4);
+ break;
+ }
+ snd_soc_write(codec, AIC3X_ASD_INTF_CTRLB, data);
+
+ /* Fsref can be 44100 or 48000 */
+ fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
+
+ /* Try to find a value for Q which allows us to bypass the PLL and
+ * generate CODEC_CLK directly. */
+ for (pll_q = 2; pll_q < 18; pll_q++)
+ if (aic3x->sysclk / (128 * pll_q) == fsref) {
+ bypass_pll = 1;
+ break;
+ }
+
+ if (bypass_pll) {
+ pll_q &= 0xf;
+ snd_soc_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
+ snd_soc_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
+ /* disable PLL if it is bypassed */
+ snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, PLL_ENABLE, 0);
+
+ } else {
+ snd_soc_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);
+ /* enable PLL when it is used */
+ snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG,
+ PLL_ENABLE, PLL_ENABLE);
+ }
+
+ /* Route Left DAC to left channel input and
+ * right DAC to right channel input */
+ data = (LDAC2LCH | RDAC2RCH);
+ data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
+ if (params_rate(params) >= 64000)
+ data |= DUAL_RATE_MODE;
+ snd_soc_write(codec, AIC3X_CODEC_DATAPATH_REG, data);
+
+ /* codec sample rate select */
+ data = (fsref * 20) / params_rate(params);
+ if (params_rate(params) < 64000)
+ data /= 2;
+ data /= 5;
+ data -= 2;
+ data |= (data << 4);
+ snd_soc_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data);
+
+ if (bypass_pll)
+ return 0;
+
+ /* Use PLL, compute appropriate setup for j, d, r and p, the closest
+ * one wins the game. Try with d==0 first, next with d!=0.
+ * Constraints for j are according to the datasheet.
+ * The sysclk is divided by 1000 to prevent integer overflows.
+ */
+
+ codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000);
+
+ for (r = 1; r <= 16; r++)
+ for (p = 1; p <= 8; p++) {
+ for (j = 4; j <= 55; j++) {
+ /* This is actually 1000*((j+(d/10000))*r)/p
+ * The term had to be converted to get
+ * rid of the division by 10000; d = 0 here
+ */
+ int tmp_clk = (1000 * j * r) / p;
+
+ /* Check whether this values get closer than
+ * the best ones we had before
+ */
+ if (abs(codec_clk - tmp_clk) <
+ abs(codec_clk - last_clk)) {
+ pll_j = j; pll_d = 0;
+ pll_r = r; pll_p = p;
+ last_clk = tmp_clk;
+ }
+
+ /* Early exit for exact matches */
+ if (tmp_clk == codec_clk)
+ goto found;
+ }
+ }
+
+ /* try with d != 0 */
+ for (p = 1; p <= 8; p++) {
+ j = codec_clk * p / 1000;
+
+ if (j < 4 || j > 11)
+ continue;
+
+ /* do not use codec_clk here since we'd loose precision */
+ d = ((2048 * p * fsref) - j * aic3x->sysclk)
+ * 100 / (aic3x->sysclk/100);
+
+ clk = (10000 * j + d) / (10 * p);
+
+ /* check whether this values get closer than the best
+ * ones we had before */
+ if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) {
+ pll_j = j; pll_d = d; pll_r = 1; pll_p = p;
+ last_clk = clk;
+ }
+
+ /* Early exit for exact matches */
+ if (clk == codec_clk)
+ goto found;
+ }
+
+ if (last_clk == 0) {
+ printk(KERN_ERR "%s(): unable to setup PLL\n", __func__);
+ return -EINVAL;
+ }
+
+found:
+ snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, PLLP_MASK, pll_p);
+ snd_soc_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG,
+ pll_r << PLLR_SHIFT);
+ snd_soc_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT);
+ snd_soc_write(codec, AIC3X_PLL_PROGC_REG,
+ (pll_d >> 6) << PLLD_MSB_SHIFT);
+ snd_soc_write(codec, AIC3X_PLL_PROGD_REG,
+ (pll_d & 0x3F) << PLLD_LSB_SHIFT);
+
+ return 0;
+}
+
+static int aic3x_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ int delay = 0;
+
+ /* TDM slot selection only valid in DSP_A/_B mode */
+ if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A)
+ delay += (aic3x->tdm_delay + 1);
+ else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B)
+ delay += aic3x->tdm_delay;
+
+ /* Configure data delay */
+ snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
+
+ return 0;
+}
+
+static int aic3x_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 ldac_reg = snd_soc_read(codec, LDAC_VOL) & ~MUTE_ON;
+ u8 rdac_reg = snd_soc_read(codec, RDAC_VOL) & ~MUTE_ON;
+
+ if (mute) {
+ snd_soc_write(codec, LDAC_VOL, ldac_reg | MUTE_ON);
+ snd_soc_write(codec, RDAC_VOL, rdac_reg | MUTE_ON);
+ } else {
+ snd_soc_write(codec, LDAC_VOL, ldac_reg);
+ snd_soc_write(codec, RDAC_VOL, rdac_reg);
+ }
+
+ return 0;
+}
+
+static int aic3x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+
+ /* set clock on MCLK or GPIO2 or BCLK */
+ snd_soc_update_bits(codec, AIC3X_CLKGEN_CTRL_REG, PLLCLK_IN_MASK,
+ clk_id << PLLCLK_IN_SHIFT);
+ snd_soc_update_bits(codec, AIC3X_CLKGEN_CTRL_REG, CLKDIV_IN_MASK,
+ clk_id << CLKDIV_IN_SHIFT);
+
+ aic3x->sysclk = freq;
+ return 0;
+}
+
+static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ u8 iface_areg, iface_breg;
+
+ iface_areg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
+ iface_breg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aic3x->master = 1;
+ iface_areg |= BIT_CLK_MASTER | WORD_CLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ aic3x->master = 0;
+ iface_areg &= ~(BIT_CLK_MASTER | WORD_CLK_MASTER);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * match both interface format and signal polarities since they
+ * are fixed
+ */
+ switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
+ SND_SOC_DAIFMT_INV_MASK)) {
+ case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
+ break;
+ case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
+ case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
+ iface_breg |= (0x01 << 6);
+ break;
+ case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF):
+ iface_breg |= (0x02 << 6);
+ break;
+ case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
+ iface_breg |= (0x03 << 6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ aic3x->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ /* set iface */
+ snd_soc_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
+ snd_soc_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
+
+ return 0;
+}
+
+static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ unsigned int lsb;
+
+ if (tx_mask != rx_mask) {
+ dev_err(codec->dev, "tx and rx masks must be symmetric\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(!tx_mask)) {
+ dev_err(codec->dev, "tx and rx masks need to be non 0\n");
+ return -EINVAL;
+ }
+
+ /* TDM based on DSP mode requires slots to be adjacent */
+ lsb = __ffs(tx_mask);
+ if ((lsb + 1) != __fls(tx_mask)) {
+ dev_err(codec->dev, "Invalid mask, slots must be adjacent\n");
+ return -EINVAL;
+ }
+
+ aic3x->tdm_delay = lsb * slot_width;
+
+ /* DOUT in high-impedance on inactive bit clocks */
+ snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA,
+ DOUT_TRISTATE, DOUT_TRISTATE);
+
+ return 0;
+}
+
+static int aic3x_regulator_event(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct aic3x_disable_nb *disable_nb =
+ container_of(nb, struct aic3x_disable_nb, nb);
+ struct aic3x_priv *aic3x = disable_nb->aic3x;
+
+ if (event & REGULATOR_EVENT_DISABLE) {
+ /*
+ * Put codec to reset and require cache sync as at least one
+ * of the supplies was disabled
+ */
+ if (gpio_is_valid(aic3x->gpio_reset))
+ gpio_set_value(aic3x->gpio_reset, 0);
+ regcache_mark_dirty(aic3x->regmap);
+ }
+
+ return 0;
+}
+
+static int aic3x_set_power(struct snd_soc_codec *codec, int power)
+{
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ unsigned int pll_c, pll_d;
+ int ret;
+
+ if (power) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies),
+ aic3x->supplies);
+ if (ret)
+ goto out;
+ aic3x->power = 1;
+
+ if (gpio_is_valid(aic3x->gpio_reset)) {
+ udelay(1);
+ gpio_set_value(aic3x->gpio_reset, 1);
+ }
+
+ /* Sync reg_cache with the hardware */
+ regcache_cache_only(aic3x->regmap, false);
+ regcache_sync(aic3x->regmap);
+
+ /* Rewrite paired PLL D registers in case cached sync skipped
+ * writing one of them and thus caused other one also not
+ * being written
+ */
+ pll_c = snd_soc_read(codec, AIC3X_PLL_PROGC_REG);
+ pll_d = snd_soc_read(codec, AIC3X_PLL_PROGD_REG);
+ if (pll_c == aic3x_reg[AIC3X_PLL_PROGC_REG].def ||
+ pll_d == aic3x_reg[AIC3X_PLL_PROGD_REG].def) {
+ snd_soc_write(codec, AIC3X_PLL_PROGC_REG, pll_c);
+ snd_soc_write(codec, AIC3X_PLL_PROGD_REG, pll_d);
+ }
+ } else {
+ /*
+ * Do soft reset to this codec instance in order to clear
+ * possible VDD leakage currents in case the supply regulators
+ * remain on
+ */
+ snd_soc_write(codec, AIC3X_RESET, SOFT_RESET);
+ regcache_mark_dirty(aic3x->regmap);
+ aic3x->power = 0;
+ /* HW writes are needless when bias is off */
+ regcache_cache_only(aic3x->regmap, true);
+ ret = regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies),
+ aic3x->supplies);
+ }
+out:
+ return ret;
+}
+
+static int aic3x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY &&
+ aic3x->master) {
+ /* enable pll */
+ snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG,
+ PLL_ENABLE, PLL_ENABLE);
+ }
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (!aic3x->power)
+ aic3x_set_power(codec, 1);
+ if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE &&
+ aic3x->master) {
+ /* disable pll */
+ snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG,
+ PLL_ENABLE, 0);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ if (aic3x->power)
+ aic3x_set_power(codec, 0);
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+#define AIC3X_RATES SNDRV_PCM_RATE_8000_96000
+#define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops aic3x_dai_ops = {
+ .hw_params = aic3x_hw_params,
+ .prepare = aic3x_prepare,
+ .digital_mute = aic3x_mute,
+ .set_sysclk = aic3x_set_dai_sysclk,
+ .set_fmt = aic3x_set_dai_fmt,
+ .set_tdm_slot = aic3x_set_dai_tdm_slot,
+};
+
+static struct snd_soc_dai_driver aic3x_dai = {
+ .name = "tlv320aic3x-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC3X_RATES,
+ .formats = AIC3X_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC3X_RATES,
+ .formats = AIC3X_FORMATS,},
+ .ops = &aic3x_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static void aic3x_mono_init(struct snd_soc_codec *codec)
+{
+ /* DAC to Mono Line Out default volume and route to Output mixer */
+ snd_soc_write(codec, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+ snd_soc_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+
+ /* unmute all outputs */
+ snd_soc_update_bits(codec, MONOLOPM_CTRL, UNMUTE, UNMUTE);
+
+ /* PGA to Mono Line Out default volume, disconnect from Output Mixer */
+ snd_soc_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL);
+
+ /* Line2 to Mono Out default volume, disconnect from Output Mixer */
+ snd_soc_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
+}
+
+/*
+ * initialise the AIC3X driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int aic3x_init(struct snd_soc_codec *codec)
+{
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);
+ snd_soc_write(codec, AIC3X_RESET, SOFT_RESET);
+
+ /* DAC default volume and mute */
+ snd_soc_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON);
+ snd_soc_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON);
+
+ /* DAC to HP default volume and route to Output mixer */
+ snd_soc_write(codec, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON);
+ snd_soc_write(codec, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON);
+ snd_soc_write(codec, DACL1_2_HPLCOM_VOL, DEFAULT_VOL | ROUTE_ON);
+ snd_soc_write(codec, DACR1_2_HPRCOM_VOL, DEFAULT_VOL | ROUTE_ON);
+ /* DAC to Line Out default volume and route to Output mixer */
+ snd_soc_write(codec, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+ snd_soc_write(codec, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+
+ /* unmute all outputs */
+ snd_soc_update_bits(codec, LLOPM_CTRL, UNMUTE, UNMUTE);
+ snd_soc_update_bits(codec, RLOPM_CTRL, UNMUTE, UNMUTE);
+ snd_soc_update_bits(codec, HPLOUT_CTRL, UNMUTE, UNMUTE);
+ snd_soc_update_bits(codec, HPROUT_CTRL, UNMUTE, UNMUTE);
+ snd_soc_update_bits(codec, HPLCOM_CTRL, UNMUTE, UNMUTE);
+ snd_soc_update_bits(codec, HPRCOM_CTRL, UNMUTE, UNMUTE);
+
+ /* ADC default volume and unmute */
+ snd_soc_write(codec, LADC_VOL, DEFAULT_GAIN);
+ snd_soc_write(codec, RADC_VOL, DEFAULT_GAIN);
+ /* By default route Line1 to ADC PGA mixer */
+ snd_soc_write(codec, LINE1L_2_LADC_CTRL, 0x0);
+ snd_soc_write(codec, LINE1R_2_RADC_CTRL, 0x0);
+
+ /* PGA to HP Bypass default volume, disconnect from Output Mixer */
+ snd_soc_write(codec, PGAL_2_HPLOUT_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, PGAR_2_HPROUT_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, PGAL_2_HPLCOM_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, PGAR_2_HPRCOM_VOL, DEFAULT_VOL);
+ /* PGA to Line Out default volume, disconnect from Output Mixer */
+ snd_soc_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
+
+ /* Line2 to HP Bypass default volume, disconnect from Output Mixer */
+ snd_soc_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
+ /* Line2 Line Out default volume, disconnect from Output Mixer */
+ snd_soc_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
+
+ switch (aic3x->model) {
+ case AIC3X_MODEL_3X:
+ case AIC3X_MODEL_33:
+ aic3x_mono_init(codec);
+ break;
+ case AIC3X_MODEL_3007:
+ snd_soc_write(codec, CLASSD_CTRL, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static bool aic3x_is_shared_reset(struct aic3x_priv *aic3x)
+{
+ struct aic3x_priv *a;
+
+ list_for_each_entry(a, &reset_list, list) {
+ if (gpio_is_valid(aic3x->gpio_reset) &&
+ aic3x->gpio_reset == a->gpio_reset)
+ return true;
+ }
+
+ return false;
+}
+
+static int aic3x_probe(struct snd_soc_codec *codec)
+{
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ int ret, i;
+
+ INIT_LIST_HEAD(&aic3x->list);
+ aic3x->codec = codec;
+
+ for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) {
+ aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event;
+ aic3x->disable_nb[i].aic3x = aic3x;
+ ret = regulator_register_notifier(aic3x->supplies[i].consumer,
+ &aic3x->disable_nb[i].nb);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to request regulator notifier: %d\n",
+ ret);
+ goto err_notif;
+ }
+ }
+
+ regcache_mark_dirty(aic3x->regmap);
+ aic3x_init(codec);
+
+ if (aic3x->setup) {
+ if (aic3x->model != AIC3X_MODEL_3104) {
+ /* setup GPIO functions */
+ snd_soc_write(codec, AIC3X_GPIO1_REG,
+ (aic3x->setup->gpio_func[0] & 0xf) << 4);
+ snd_soc_write(codec, AIC3X_GPIO2_REG,
+ (aic3x->setup->gpio_func[1] & 0xf) << 4);
+ } else {
+ dev_warn(codec->dev, "GPIO functionality is not supported on tlv320aic3104\n");
+ }
+ }
+
+ switch (aic3x->model) {
+ case AIC3X_MODEL_3X:
+ case AIC3X_MODEL_33:
+ snd_soc_add_codec_controls(codec, aic3x_extra_snd_controls,
+ ARRAY_SIZE(aic3x_extra_snd_controls));
+ snd_soc_add_codec_controls(codec, aic3x_mono_controls,
+ ARRAY_SIZE(aic3x_mono_controls));
+ break;
+ case AIC3X_MODEL_3007:
+ snd_soc_add_codec_controls(codec, aic3x_extra_snd_controls,
+ ARRAY_SIZE(aic3x_extra_snd_controls));
+ snd_soc_add_codec_controls(codec,
+ &aic3x_classd_amp_gain_ctrl, 1);
+ break;
+ case AIC3X_MODEL_3104:
+ break;
+ }
+
+ /* set mic bias voltage */
+ switch (aic3x->micbias_vg) {
+ case AIC3X_MICBIAS_2_0V:
+ case AIC3X_MICBIAS_2_5V:
+ case AIC3X_MICBIAS_AVDDV:
+ snd_soc_update_bits(codec, MICBIAS_CTRL,
+ MICBIAS_LEVEL_MASK,
+ (aic3x->micbias_vg) << MICBIAS_LEVEL_SHIFT);
+ break;
+ case AIC3X_MICBIAS_OFF:
+ /*
+ * noting to do. target won't enter here. This is just to avoid
+ * compile time warning "warning: enumeration value
+ * 'AIC3X_MICBIAS_OFF' not handled in switch"
+ */
+ break;
+ }
+
+ aic3x_add_widgets(codec);
+
+ return 0;
+
+err_notif:
+ while (i--)
+ regulator_unregister_notifier(aic3x->supplies[i].consumer,
+ &aic3x->disable_nb[i].nb);
+ return ret;
+}
+
+static int aic3x_remove(struct snd_soc_codec *codec)
+{
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ list_del(&aic3x->list);
+ for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
+ regulator_unregister_notifier(aic3x->supplies[i].consumer,
+ &aic3x->disable_nb[i].nb);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_aic3x = {
+ .set_bias_level = aic3x_set_bias_level,
+ .idle_bias_off = true,
+ .probe = aic3x_probe,
+ .remove = aic3x_remove,
+ .controls = aic3x_snd_controls,
+ .num_controls = ARRAY_SIZE(aic3x_snd_controls),
+ .dapm_widgets = aic3x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aic3x_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+};
+
+/*
+ * AIC3X 2 wire address can be up to 4 devices with device addresses
+ * 0x18, 0x19, 0x1A, 0x1B
+ */
+
+static const struct i2c_device_id aic3x_i2c_id[] = {
+ { "tlv320aic3x", AIC3X_MODEL_3X },
+ { "tlv320aic33", AIC3X_MODEL_33 },
+ { "tlv320aic3007", AIC3X_MODEL_3007 },
+ { "tlv320aic3106", AIC3X_MODEL_3X },
+ { "tlv320aic3104", AIC3X_MODEL_3104 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
+
+static const struct reg_default aic3007_class_d[] = {
+ /* Class-D speaker driver init; datasheet p. 46 */
+ { AIC3X_PAGE_SELECT, 0x0D },
+ { 0xD, 0x0D },
+ { 0x8, 0x5C },
+ { 0x8, 0x5D },
+ { 0x8, 0x5C },
+ { AIC3X_PAGE_SELECT, 0x00 },
+};
+
+/*
+ * If the i2c layer weren't so broken, we could pass this kind of data
+ * around
+ */
+static int aic3x_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct aic3x_pdata *pdata = i2c->dev.platform_data;
+ struct aic3x_priv *aic3x;
+ struct aic3x_setup_data *ai3x_setup;
+ struct device_node *np = i2c->dev.of_node;
+ int ret, i;
+ u32 value;
+
+ aic3x = devm_kzalloc(&i2c->dev, sizeof(struct aic3x_priv), GFP_KERNEL);
+ if (!aic3x)
+ return -ENOMEM;
+
+ aic3x->regmap = devm_regmap_init_i2c(i2c, &aic3x_regmap);
+ if (IS_ERR(aic3x->regmap)) {
+ ret = PTR_ERR(aic3x->regmap);
+ return ret;
+ }
+
+ regcache_cache_only(aic3x->regmap, true);
+
+ i2c_set_clientdata(i2c, aic3x);
+ if (pdata) {
+ aic3x->gpio_reset = pdata->gpio_reset;
+ aic3x->setup = pdata->setup;
+ aic3x->micbias_vg = pdata->micbias_vg;
+ } else if (np) {
+ ai3x_setup = devm_kzalloc(&i2c->dev, sizeof(*ai3x_setup),
+ GFP_KERNEL);
+ if (!ai3x_setup)
+ return -ENOMEM;
+
+ ret = of_get_named_gpio(np, "gpio-reset", 0);
+ if (ret >= 0)
+ aic3x->gpio_reset = ret;
+ else
+ aic3x->gpio_reset = -1;
+
+ if (of_property_read_u32_array(np, "ai3x-gpio-func",
+ ai3x_setup->gpio_func, 2) >= 0) {
+ aic3x->setup = ai3x_setup;
+ }
+
+ if (!of_property_read_u32(np, "ai3x-micbias-vg", &value)) {
+ switch (value) {
+ case 1 :
+ aic3x->micbias_vg = AIC3X_MICBIAS_2_0V;
+ break;
+ case 2 :
+ aic3x->micbias_vg = AIC3X_MICBIAS_2_5V;
+ break;
+ case 3 :
+ aic3x->micbias_vg = AIC3X_MICBIAS_AVDDV;
+ break;
+ default :
+ aic3x->micbias_vg = AIC3X_MICBIAS_OFF;
+ dev_err(&i2c->dev, "Unsuitable MicBias voltage "
+ "found in DT\n");
+ }
+ } else {
+ aic3x->micbias_vg = AIC3X_MICBIAS_OFF;
+ }
+
+ } else {
+ aic3x->gpio_reset = -1;
+ }
+
+ aic3x->model = id->driver_data;
+
+ if (gpio_is_valid(aic3x->gpio_reset) &&
+ !aic3x_is_shared_reset(aic3x)) {
+ ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset");
+ if (ret != 0)
+ goto err;
+ gpio_direction_output(aic3x->gpio_reset, 0);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
+ aic3x->supplies[i].supply = aic3x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(aic3x->supplies),
+ aic3x->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ goto err_gpio;
+ }
+
+ if (aic3x->model == AIC3X_MODEL_3007) {
+ ret = regmap_register_patch(aic3x->regmap, aic3007_class_d,
+ ARRAY_SIZE(aic3007_class_d));
+ if (ret != 0)
+ dev_err(&i2c->dev, "Failed to init class D: %d\n",
+ ret);
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_aic3x, &aic3x_dai, 1);
+
+ if (ret != 0)
+ goto err_gpio;
+
+ list_add(&aic3x->list, &reset_list);
+
+ return 0;
+
+err_gpio:
+ if (gpio_is_valid(aic3x->gpio_reset) &&
+ !aic3x_is_shared_reset(aic3x))
+ gpio_free(aic3x->gpio_reset);
+err:
+ return ret;
+}
+
+static int aic3x_i2c_remove(struct i2c_client *client)
+{
+ struct aic3x_priv *aic3x = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+ if (gpio_is_valid(aic3x->gpio_reset) &&
+ !aic3x_is_shared_reset(aic3x)) {
+ gpio_set_value(aic3x->gpio_reset, 0);
+ gpio_free(aic3x->gpio_reset);
+ }
+ return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tlv320aic3x_of_match[] = {
+ { .compatible = "ti,tlv320aic3x", },
+ { .compatible = "ti,tlv320aic33" },
+ { .compatible = "ti,tlv320aic3007" },
+ { .compatible = "ti,tlv320aic3106" },
+ { .compatible = "ti,tlv320aic3104" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tlv320aic3x_of_match);
+#endif
+
+/* machine i2c codec control layer */
+static struct i2c_driver aic3x_i2c_driver = {
+ .driver = {
+ .name = "tlv320aic3x-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tlv320aic3x_of_match),
+ },
+ .probe = aic3x_i2c_probe,
+ .remove = aic3x_i2c_remove,
+ .id_table = aic3x_i2c_id,
+};
+
+module_i2c_driver(aic3x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/tlv320aic3x.h b/kernel/sound/soc/codecs/tlv320aic3x.h
new file mode 100644
index 000000000..89fa692df
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320aic3x.h
@@ -0,0 +1,283 @@
+/*
+ * ALSA SoC TLV320AIC3X codec driver
+ *
+ * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
+ * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.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 _AIC3X_H
+#define _AIC3X_H
+
+/* AIC3X register space */
+#define AIC3X_CACHEREGNUM 110
+
+/* Page select register */
+#define AIC3X_PAGE_SELECT 0
+/* Software reset register */
+#define AIC3X_RESET 1
+/* Codec Sample rate select register */
+#define AIC3X_SAMPLE_RATE_SEL_REG 2
+/* PLL progrramming register A */
+#define AIC3X_PLL_PROGA_REG 3
+/* PLL progrramming register B */
+#define AIC3X_PLL_PROGB_REG 4
+/* PLL progrramming register C */
+#define AIC3X_PLL_PROGC_REG 5
+/* PLL progrramming register D */
+#define AIC3X_PLL_PROGD_REG 6
+/* Codec datapath setup register */
+#define AIC3X_CODEC_DATAPATH_REG 7
+/* Audio serial data interface control register A */
+#define AIC3X_ASD_INTF_CTRLA 8
+/* Audio serial data interface control register B */
+#define AIC3X_ASD_INTF_CTRLB 9
+/* Audio serial data interface control register C */
+#define AIC3X_ASD_INTF_CTRLC 10
+/* Audio overflow status and PLL R value programming register */
+#define AIC3X_OVRF_STATUS_AND_PLLR_REG 11
+/* Audio codec digital filter control register */
+#define AIC3X_CODEC_DFILT_CTRL 12
+/* Headset/button press detection register */
+#define AIC3X_HEADSET_DETECT_CTRL_A 13
+#define AIC3X_HEADSET_DETECT_CTRL_B 14
+/* ADC PGA Gain control registers */
+#define LADC_VOL 15
+#define RADC_VOL 16
+/* MIC3 control registers */
+#define MIC3LR_2_LADC_CTRL 17
+#define MIC3LR_2_RADC_CTRL 18
+/* Line1 Input control registers */
+#define LINE1L_2_LADC_CTRL 19
+#define LINE1R_2_LADC_CTRL 21
+#define LINE1R_2_RADC_CTRL 22
+#define LINE1L_2_RADC_CTRL 24
+/* Line2 Input control registers */
+#define LINE2L_2_LADC_CTRL 20
+#define LINE2R_2_RADC_CTRL 23
+/* MICBIAS Control Register */
+#define MICBIAS_CTRL 25
+
+/* AGC Control Registers A, B, C */
+#define LAGC_CTRL_A 26
+#define LAGC_CTRL_B 27
+#define LAGC_CTRL_C 28
+#define RAGC_CTRL_A 29
+#define RAGC_CTRL_B 30
+#define RAGC_CTRL_C 31
+
+/* DAC Power and Left High Power Output control registers */
+#define DAC_PWR 37
+#define HPLCOM_CFG 37
+/* Right High Power Output control registers */
+#define HPRCOM_CFG 38
+/* High Power Output Stage Control Register */
+#define HPOUT_SC 40
+/* DAC Output Switching control registers */
+#define DAC_LINE_MUX 41
+/* High Power Output Driver Pop Reduction registers */
+#define HPOUT_POP_REDUCTION 42
+/* DAC Digital control registers */
+#define LDAC_VOL 43
+#define RDAC_VOL 44
+/* Left High Power Output control registers */
+#define LINE2L_2_HPLOUT_VOL 45
+#define PGAL_2_HPLOUT_VOL 46
+#define DACL1_2_HPLOUT_VOL 47
+#define LINE2R_2_HPLOUT_VOL 48
+#define PGAR_2_HPLOUT_VOL 49
+#define DACR1_2_HPLOUT_VOL 50
+#define HPLOUT_CTRL 51
+/* Left High Power COM control registers */
+#define LINE2L_2_HPLCOM_VOL 52
+#define PGAL_2_HPLCOM_VOL 53
+#define DACL1_2_HPLCOM_VOL 54
+#define LINE2R_2_HPLCOM_VOL 55
+#define PGAR_2_HPLCOM_VOL 56
+#define DACR1_2_HPLCOM_VOL 57
+#define HPLCOM_CTRL 58
+/* Right High Power Output control registers */
+#define LINE2L_2_HPROUT_VOL 59
+#define PGAL_2_HPROUT_VOL 60
+#define DACL1_2_HPROUT_VOL 61
+#define LINE2R_2_HPROUT_VOL 62
+#define PGAR_2_HPROUT_VOL 63
+#define DACR1_2_HPROUT_VOL 64
+#define HPROUT_CTRL 65
+/* Right High Power COM control registers */
+#define LINE2L_2_HPRCOM_VOL 66
+#define PGAL_2_HPRCOM_VOL 67
+#define DACL1_2_HPRCOM_VOL 68
+#define LINE2R_2_HPRCOM_VOL 69
+#define PGAR_2_HPRCOM_VOL 70
+#define DACR1_2_HPRCOM_VOL 71
+#define HPRCOM_CTRL 72
+/* Mono Line Output Plus/Minus control registers */
+#define LINE2L_2_MONOLOPM_VOL 73
+#define PGAL_2_MONOLOPM_VOL 74
+#define DACL1_2_MONOLOPM_VOL 75
+#define LINE2R_2_MONOLOPM_VOL 76
+#define PGAR_2_MONOLOPM_VOL 77
+#define DACR1_2_MONOLOPM_VOL 78
+#define MONOLOPM_CTRL 79
+/* Class-D speaker driver on tlv320aic3007 */
+#define CLASSD_CTRL 73
+/* Left Line Output Plus/Minus control registers */
+#define LINE2L_2_LLOPM_VOL 80
+#define PGAL_2_LLOPM_VOL 81
+#define DACL1_2_LLOPM_VOL 82
+#define LINE2R_2_LLOPM_VOL 83
+#define PGAR_2_LLOPM_VOL 84
+#define DACR1_2_LLOPM_VOL 85
+#define LLOPM_CTRL 86
+/* Right Line Output Plus/Minus control registers */
+#define LINE2L_2_RLOPM_VOL 87
+#define PGAL_2_RLOPM_VOL 88
+#define DACL1_2_RLOPM_VOL 89
+#define LINE2R_2_RLOPM_VOL 90
+#define PGAR_2_RLOPM_VOL 91
+#define DACR1_2_RLOPM_VOL 92
+#define RLOPM_CTRL 93
+/* GPIO/IRQ registers */
+#define AIC3X_STICKY_IRQ_FLAGS_REG 96
+#define AIC3X_RT_IRQ_FLAGS_REG 97
+#define AIC3X_GPIO1_REG 98
+#define AIC3X_GPIO2_REG 99
+#define AIC3X_GPIOA_REG 100
+#define AIC3X_GPIOB_REG 101
+/* Clock generation control register */
+#define AIC3X_CLKGEN_CTRL_REG 102
+/* New AGC registers */
+#define LAGCN_ATTACK 103
+#define LAGCN_DECAY 104
+#define RAGCN_ATTACK 105
+#define RAGCN_DECAY 106
+/* New Programmable ADC Digital Path and I2C Bus Condition Register */
+#define NEW_ADC_DIGITALPATH 107
+/* Passive Analog Signal Bypass Selection During Powerdown Register */
+#define PASSIVE_BYPASS 108
+/* DAC Quiescent Current Adjustment Register */
+#define DAC_ICC_ADJ 109
+
+/* Page select register bits */
+#define PAGE0_SELECT 0
+#define PAGE1_SELECT 1
+
+/* Audio serial data interface control register A bits */
+#define BIT_CLK_MASTER 0x80
+#define WORD_CLK_MASTER 0x40
+#define DOUT_TRISTATE 0x20
+
+/* Codec Datapath setup register 7 */
+#define FSREF_44100 (1 << 7)
+#define FSREF_48000 (0 << 7)
+#define DUAL_RATE_MODE ((1 << 5) | (1 << 6))
+#define LDAC2LCH (0x1 << 3)
+#define RDAC2RCH (0x1 << 1)
+#define LDAC2RCH (0x2 << 3)
+#define RDAC2LCH (0x2 << 1)
+#define LDAC2MONOMIX (0x3 << 3)
+#define RDAC2MONOMIX (0x3 << 1)
+
+/* PLL registers bitfields */
+#define PLLP_SHIFT 0
+#define PLLP_MASK 7
+#define PLLQ_SHIFT 3
+#define PLLR_SHIFT 0
+#define PLLJ_SHIFT 2
+#define PLLD_MSB_SHIFT 0
+#define PLLD_LSB_SHIFT 2
+
+/* Clock generation register bits */
+#define CODEC_CLKIN_PLLDIV 0
+#define CODEC_CLKIN_CLKDIV 1
+#define PLL_CLKIN_SHIFT 4
+#define MCLK_SOURCE 0x0
+#define PLL_CLKDIV_SHIFT 0
+#define PLLCLK_IN_MASK 0x30
+#define PLLCLK_IN_SHIFT 4
+#define CLKDIV_IN_MASK 0xc0
+#define CLKDIV_IN_SHIFT 6
+/* clock in source */
+#define CLKIN_MCLK 0
+#define CLKIN_GPIO2 1
+#define CLKIN_BCLK 2
+
+/* Software reset register bits */
+#define SOFT_RESET 0x80
+
+/* PLL progrramming register A bits */
+#define PLL_ENABLE 0x80
+
+/* Route bits */
+#define ROUTE_ON 0x80
+
+/* Mute bits */
+#define UNMUTE 0x08
+#define MUTE_ON 0x80
+
+/* Power bits */
+#define LADC_PWR_ON 0x04
+#define RADC_PWR_ON 0x04
+#define LDAC_PWR_ON 0x80
+#define RDAC_PWR_ON 0x40
+#define HPLOUT_PWR_ON 0x01
+#define HPROUT_PWR_ON 0x01
+#define HPLCOM_PWR_ON 0x01
+#define HPRCOM_PWR_ON 0x01
+#define MONOLOPM_PWR_ON 0x01
+#define LLOPM_PWR_ON 0x01
+#define RLOPM_PWR_ON 0x01
+
+#define INVERT_VOL(val) (0x7f - val)
+
+/* Default output volume (inverted) */
+#define DEFAULT_VOL INVERT_VOL(0x50)
+/* Default input volume */
+#define DEFAULT_GAIN 0x20
+
+/* MICBIAS Control Register */
+#define MICBIAS_LEVEL_SHIFT (6)
+#define MICBIAS_LEVEL_MASK (3 << 6)
+
+/* headset detection / button API */
+
+/* The AIC3x supports detection of stereo headsets (GND + left + right signal)
+ * and cellular headsets (GND + speaker output + microphone input).
+ * It is recommended to enable MIC bias for this function to work properly.
+ * For more information, please refer to the datasheet. */
+enum {
+ AIC3X_HEADSET_DETECT_OFF = 0,
+ AIC3X_HEADSET_DETECT_STEREO = 1,
+ AIC3X_HEADSET_DETECT_CELLULAR = 2,
+ AIC3X_HEADSET_DETECT_BOTH = 3
+};
+
+enum {
+ AIC3X_HEADSET_DEBOUNCE_16MS = 0,
+ AIC3X_HEADSET_DEBOUNCE_32MS = 1,
+ AIC3X_HEADSET_DEBOUNCE_64MS = 2,
+ AIC3X_HEADSET_DEBOUNCE_128MS = 3,
+ AIC3X_HEADSET_DEBOUNCE_256MS = 4,
+ AIC3X_HEADSET_DEBOUNCE_512MS = 5
+};
+
+enum {
+ AIC3X_BUTTON_DEBOUNCE_0MS = 0,
+ AIC3X_BUTTON_DEBOUNCE_8MS = 1,
+ AIC3X_BUTTON_DEBOUNCE_16MS = 2,
+ AIC3X_BUTTON_DEBOUNCE_32MS = 3
+};
+
+#define AIC3X_HEADSET_DETECT_ENABLED 0x80
+#define AIC3X_HEADSET_DETECT_SHIFT 5
+#define AIC3X_HEADSET_DETECT_MASK 3
+#define AIC3X_HEADSET_DEBOUNCE_SHIFT 2
+#define AIC3X_HEADSET_DEBOUNCE_MASK 7
+#define AIC3X_BUTTON_DEBOUNCE_SHIFT 0
+#define AIC3X_BUTTON_DEBOUNCE_MASK 3
+
+#endif /* _AIC3X_H */
diff --git a/kernel/sound/soc/codecs/tlv320dac33.c b/kernel/sound/soc/codecs/tlv320dac33.c
new file mode 100644
index 000000000..4e3e607de
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320dac33.c
@@ -0,0 +1,1600 @@
+/*
+ * ALSA SoC Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Copyright: (C) 2009 Nokia 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.
+ *
+ * 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 <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.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 <sound/tlv320dac33-plat.h>
+#include "tlv320dac33.h"
+
+/*
+ * The internal FIFO is 24576 bytes long
+ * It can be configured to hold 16bit or 24bit samples
+ * In 16bit configuration the FIFO can hold 6144 stereo samples
+ * In 24bit configuration the FIFO can hold 4096 stereo samples
+ */
+#define DAC33_FIFO_SIZE_16BIT 6144
+#define DAC33_FIFO_SIZE_24BIT 4096
+#define DAC33_MODE7_MARGIN 10 /* Safety margin for FIFO in Mode7 */
+
+#define BURST_BASEFREQ_HZ 49152000
+
+#define SAMPLES_TO_US(rate, samples) \
+ (1000000000 / (((rate) * 1000) / (samples)))
+
+#define US_TO_SAMPLES(rate, us) \
+ ((rate) / (1000000 / ((us) < 1000000 ? (us) : 1000000)))
+
+#define UTHR_FROM_PERIOD_SIZE(samples, playrate, burstrate) \
+ (((samples)*5000) / (((burstrate)*5000) / ((burstrate) - (playrate))))
+
+static void dac33_calculate_times(struct snd_pcm_substream *substream,
+ struct snd_soc_codec *codec);
+static int dac33_prepare_chip(struct snd_pcm_substream *substream,
+ struct snd_soc_codec *codec);
+
+enum dac33_state {
+ DAC33_IDLE = 0,
+ DAC33_PREFILL,
+ DAC33_PLAYBACK,
+ DAC33_FLUSH,
+};
+
+enum dac33_fifo_modes {
+ DAC33_FIFO_BYPASS = 0,
+ DAC33_FIFO_MODE1,
+ DAC33_FIFO_MODE7,
+ DAC33_FIFO_LAST_MODE,
+};
+
+#define DAC33_NUM_SUPPLIES 3
+static const char *dac33_supply_names[DAC33_NUM_SUPPLIES] = {
+ "AVDD",
+ "DVDD",
+ "IOVDD",
+};
+
+struct tlv320dac33_priv {
+ struct mutex mutex;
+ struct workqueue_struct *dac33_wq;
+ struct work_struct work;
+ struct snd_soc_codec *codec;
+ struct regulator_bulk_data supplies[DAC33_NUM_SUPPLIES];
+ struct snd_pcm_substream *substream;
+ int power_gpio;
+ int chip_power;
+ int irq;
+ unsigned int refclk;
+
+ unsigned int alarm_threshold; /* set to be half of LATENCY_TIME_MS */
+ enum dac33_fifo_modes fifo_mode;/* FIFO mode selection */
+ unsigned int fifo_size; /* Size of the FIFO in samples */
+ unsigned int nsample; /* burst read amount from host */
+ int mode1_latency; /* latency caused by the i2c writes in
+ * us */
+ u8 burst_bclkdiv; /* BCLK divider value in burst mode */
+ unsigned int burst_rate; /* Interface speed in Burst modes */
+
+ int keep_bclk; /* Keep the BCLK continuously running
+ * in FIFO modes */
+ spinlock_t lock;
+ unsigned long long t_stamp1; /* Time stamp for FIFO modes to */
+ unsigned long long t_stamp2; /* calculate the FIFO caused delay */
+
+ unsigned int mode1_us_burst; /* Time to burst read n number of
+ * samples */
+ unsigned int mode7_us_to_lthr; /* Time to reach lthr from uthr */
+
+ unsigned int uthr;
+
+ enum dac33_state state;
+ void *control_data;
+};
+
+static const u8 dac33_reg[DAC33_CACHEREGNUM] = {
+0x00, 0x00, 0x00, 0x00, /* 0x00 - 0x03 */
+0x00, 0x00, 0x00, 0x00, /* 0x04 - 0x07 */
+0x00, 0x00, 0x00, 0x00, /* 0x08 - 0x0b */
+0x00, 0x00, 0x00, 0x00, /* 0x0c - 0x0f */
+0x00, 0x00, 0x00, 0x00, /* 0x10 - 0x13 */
+0x00, 0x00, 0x00, 0x00, /* 0x14 - 0x17 */
+0x00, 0x00, 0x00, 0x00, /* 0x18 - 0x1b */
+0x00, 0x00, 0x00, 0x00, /* 0x1c - 0x1f */
+0x00, 0x00, 0x00, 0x00, /* 0x20 - 0x23 */
+0x00, 0x00, 0x00, 0x00, /* 0x24 - 0x27 */
+0x00, 0x00, 0x00, 0x00, /* 0x28 - 0x2b */
+0x00, 0x00, 0x00, 0x80, /* 0x2c - 0x2f */
+0x80, 0x00, 0x00, 0x00, /* 0x30 - 0x33 */
+0x00, 0x00, 0x00, 0x00, /* 0x34 - 0x37 */
+0x00, 0x00, /* 0x38 - 0x39 */
+/* Registers 0x3a - 0x3f are reserved */
+ 0x00, 0x00, /* 0x3a - 0x3b */
+0x00, 0x00, 0x00, 0x00, /* 0x3c - 0x3f */
+
+0x00, 0x00, 0x00, 0x00, /* 0x40 - 0x43 */
+0x00, 0x80, /* 0x44 - 0x45 */
+/* Registers 0x46 - 0x47 are reserved */
+ 0x80, 0x80, /* 0x46 - 0x47 */
+
+0x80, 0x00, 0x00, /* 0x48 - 0x4a */
+/* Registers 0x4b - 0x7c are reserved */
+ 0x00, /* 0x4b */
+0x00, 0x00, 0x00, 0x00, /* 0x4c - 0x4f */
+0x00, 0x00, 0x00, 0x00, /* 0x50 - 0x53 */
+0x00, 0x00, 0x00, 0x00, /* 0x54 - 0x57 */
+0x00, 0x00, 0x00, 0x00, /* 0x58 - 0x5b */
+0x00, 0x00, 0x00, 0x00, /* 0x5c - 0x5f */
+0x00, 0x00, 0x00, 0x00, /* 0x60 - 0x63 */
+0x00, 0x00, 0x00, 0x00, /* 0x64 - 0x67 */
+0x00, 0x00, 0x00, 0x00, /* 0x68 - 0x6b */
+0x00, 0x00, 0x00, 0x00, /* 0x6c - 0x6f */
+0x00, 0x00, 0x00, 0x00, /* 0x70 - 0x73 */
+0x00, 0x00, 0x00, 0x00, /* 0x74 - 0x77 */
+0x00, 0x00, 0x00, 0x00, /* 0x78 - 0x7b */
+0x00, /* 0x7c */
+
+ 0xda, 0x33, 0x03, /* 0x7d - 0x7f */
+};
+
+/* Register read and write */
+static inline unsigned int dac33_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned reg)
+{
+ u8 *cache = codec->reg_cache;
+ if (reg >= DAC33_CACHEREGNUM)
+ return 0;
+
+ return cache[reg];
+}
+
+static inline void dac33_write_reg_cache(struct snd_soc_codec *codec,
+ u8 reg, u8 value)
+{
+ u8 *cache = codec->reg_cache;
+ if (reg >= DAC33_CACHEREGNUM)
+ return;
+
+ cache[reg] = value;
+}
+
+static int dac33_read(struct snd_soc_codec *codec, unsigned int reg,
+ u8 *value)
+{
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ int val, ret = 0;
+
+ *value = reg & 0xff;
+
+ /* If powered off, return the cached value */
+ if (dac33->chip_power) {
+ val = i2c_smbus_read_byte_data(codec->control_data, value[0]);
+ if (val < 0) {
+ dev_err(codec->dev, "Read failed (%d)\n", val);
+ value[0] = dac33_read_reg_cache(codec, reg);
+ ret = val;
+ } else {
+ value[0] = val;
+ dac33_write_reg_cache(codec, reg, val);
+ }
+ } else {
+ value[0] = dac33_read_reg_cache(codec, reg);
+ }
+
+ return ret;
+}
+
+static int dac33_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ u8 data[2];
+ int ret = 0;
+
+ /*
+ * data is
+ * D15..D8 dac33 register offset
+ * D7...D0 register data
+ */
+ data[0] = reg & 0xff;
+ data[1] = value & 0xff;
+
+ dac33_write_reg_cache(codec, data[0], data[1]);
+ if (dac33->chip_power) {
+ ret = codec->hw_write(codec->control_data, data, 2);
+ if (ret != 2)
+ dev_err(codec->dev, "Write failed (%d)\n", ret);
+ else
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int dac33_write_locked(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&dac33->mutex);
+ ret = dac33_write(codec, reg, value);
+ mutex_unlock(&dac33->mutex);
+
+ return ret;
+}
+
+#define DAC33_I2C_ADDR_AUTOINC 0x80
+static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ u8 data[3];
+ int ret = 0;
+
+ /*
+ * data is
+ * D23..D16 dac33 register offset
+ * D15..D8 register data MSB
+ * D7...D0 register data LSB
+ */
+ data[0] = reg & 0xff;
+ data[1] = (value >> 8) & 0xff;
+ data[2] = value & 0xff;
+
+ dac33_write_reg_cache(codec, data[0], data[1]);
+ dac33_write_reg_cache(codec, data[0] + 1, data[2]);
+
+ if (dac33->chip_power) {
+ /* We need to set autoincrement mode for 16 bit writes */
+ data[0] |= DAC33_I2C_ADDR_AUTOINC;
+ ret = codec->hw_write(codec->control_data, data, 3);
+ if (ret != 3)
+ dev_err(codec->dev, "Write failed (%d)\n", ret);
+ else
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void dac33_init_chip(struct snd_soc_codec *codec)
+{
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+
+ if (unlikely(!dac33->chip_power))
+ return;
+
+ /* A : DAC sample rate Fsref/1.5 */
+ dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(0));
+ /* B : DAC src=normal, not muted */
+ dac33_write(codec, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT |
+ DAC33_DACSRCL_LEFT);
+ /* C : (defaults) */
+ dac33_write(codec, DAC33_DAC_CTRL_C, 0x00);
+
+ /* 73 : volume soft stepping control,
+ clock source = internal osc (?) */
+ dac33_write(codec, DAC33_ANA_VOL_SOFT_STEP_CTRL, DAC33_VOLCLKEN);
+
+ /* Restore only selected registers (gains mostly) */
+ dac33_write(codec, DAC33_LDAC_DIG_VOL_CTRL,
+ dac33_read_reg_cache(codec, DAC33_LDAC_DIG_VOL_CTRL));
+ dac33_write(codec, DAC33_RDAC_DIG_VOL_CTRL,
+ dac33_read_reg_cache(codec, DAC33_RDAC_DIG_VOL_CTRL));
+
+ dac33_write(codec, DAC33_LINEL_TO_LLO_VOL,
+ dac33_read_reg_cache(codec, DAC33_LINEL_TO_LLO_VOL));
+ dac33_write(codec, DAC33_LINER_TO_RLO_VOL,
+ dac33_read_reg_cache(codec, DAC33_LINER_TO_RLO_VOL));
+
+ dac33_write(codec, DAC33_OUT_AMP_CTRL,
+ dac33_read_reg_cache(codec, DAC33_OUT_AMP_CTRL));
+
+ dac33_write(codec, DAC33_LDAC_PWR_CTRL,
+ dac33_read_reg_cache(codec, DAC33_LDAC_PWR_CTRL));
+ dac33_write(codec, DAC33_RDAC_PWR_CTRL,
+ dac33_read_reg_cache(codec, DAC33_RDAC_PWR_CTRL));
+}
+
+static inline int dac33_read_id(struct snd_soc_codec *codec)
+{
+ int i, ret = 0;
+ u8 reg;
+
+ for (i = 0; i < 3; i++) {
+ ret = dac33_read(codec, DAC33_DEVICE_ID_MSB + i, &reg);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static inline void dac33_soft_power(struct snd_soc_codec *codec, int power)
+{
+ u8 reg;
+
+ reg = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+ if (power)
+ reg |= DAC33_PDNALLB;
+ else
+ reg &= ~(DAC33_PDNALLB | DAC33_OSCPDNB |
+ DAC33_DACRPDNB | DAC33_DACLPDNB);
+ dac33_write(codec, DAC33_PWR_CTRL, reg);
+}
+
+static inline void dac33_disable_digital(struct snd_soc_codec *codec)
+{
+ u8 reg;
+
+ /* Stop the DAI clock */
+ reg = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+ reg &= ~DAC33_BCLKON;
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, reg);
+
+ /* Power down the Oscillator, and DACs */
+ reg = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+ reg &= ~(DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB);
+ dac33_write(codec, DAC33_PWR_CTRL, reg);
+}
+
+static int dac33_hard_power(struct snd_soc_codec *codec, int power)
+{
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ mutex_lock(&dac33->mutex);
+
+ /* Safety check */
+ if (unlikely(power == dac33->chip_power)) {
+ dev_dbg(codec->dev, "Trying to set the same power state: %s\n",
+ power ? "ON" : "OFF");
+ goto exit;
+ }
+
+ if (power) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(dac33->supplies),
+ dac33->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n", ret);
+ goto exit;
+ }
+
+ if (dac33->power_gpio >= 0)
+ gpio_set_value(dac33->power_gpio, 1);
+
+ dac33->chip_power = 1;
+ } else {
+ dac33_soft_power(codec, 0);
+ if (dac33->power_gpio >= 0)
+ gpio_set_value(dac33->power_gpio, 0);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(dac33->supplies),
+ dac33->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to disable supplies: %d\n", ret);
+ goto exit;
+ }
+
+ dac33->chip_power = 0;
+ }
+
+exit:
+ mutex_unlock(&dac33->mutex);
+ return ret;
+}
+
+static int dac33_playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (likely(dac33->substream)) {
+ dac33_calculate_times(dac33->substream, codec);
+ dac33_prepare_chip(dac33->substream, codec);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dac33_disable_digital(codec);
+ break;
+ }
+ return 0;
+}
+
+static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = dac33->fifo_mode;
+
+ return 0;
+}
+
+static int dac33_set_fifo_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if (dac33->fifo_mode == ucontrol->value.integer.value[0])
+ return 0;
+ /* Do not allow changes while stream is running*/
+ if (snd_soc_codec_is_active(codec))
+ return -EPERM;
+
+ if (ucontrol->value.integer.value[0] < 0 ||
+ ucontrol->value.integer.value[0] >= DAC33_FIFO_LAST_MODE)
+ ret = -EINVAL;
+ else
+ dac33->fifo_mode = ucontrol->value.integer.value[0];
+
+ return ret;
+}
+
+/* Codec operation modes */
+static const char *dac33_fifo_mode_texts[] = {
+ "Bypass", "Mode 1", "Mode 7"
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(dac33_fifo_mode_enum, dac33_fifo_mode_texts);
+
+/* L/R Line Output Gain */
+static const char *lr_lineout_gain_texts[] = {
+ "Line -12dB DAC 0dB", "Line -6dB DAC 6dB",
+ "Line 0dB DAC 12dB", "Line 6dB DAC 18dB",
+};
+
+static SOC_ENUM_SINGLE_DECL(l_lineout_gain_enum,
+ DAC33_LDAC_PWR_CTRL, 0,
+ lr_lineout_gain_texts);
+
+static SOC_ENUM_SINGLE_DECL(r_lineout_gain_enum,
+ DAC33_RDAC_PWR_CTRL, 0,
+ lr_lineout_gain_texts);
+
+/*
+ * DACL/R digital volume control:
+ * from 0 dB to -63.5 in 0.5 dB steps
+ * Need to be inverted later on:
+ * 0x00 == 0 dB
+ * 0x7f == -63.5 dB
+ */
+static DECLARE_TLV_DB_SCALE(dac_digivol_tlv, -6350, 50, 0);
+
+static const struct snd_kcontrol_new dac33_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC Digital Playback Volume",
+ DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL,
+ 0, 0x7f, 1, dac_digivol_tlv),
+ SOC_DOUBLE_R("DAC Digital Playback Switch",
+ DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL, 7, 1, 1),
+ SOC_DOUBLE_R("Line to Line Out Volume",
+ DAC33_LINEL_TO_LLO_VOL, DAC33_LINER_TO_RLO_VOL, 0, 127, 1),
+ SOC_ENUM("Left Line Output Gain", l_lineout_gain_enum),
+ SOC_ENUM("Right Line Output Gain", r_lineout_gain_enum),
+};
+
+static const struct snd_kcontrol_new dac33_mode_snd_controls[] = {
+ SOC_ENUM_EXT("FIFO Mode", dac33_fifo_mode_enum,
+ dac33_get_fifo_mode, dac33_set_fifo_mode),
+};
+
+/* Analog bypass */
+static const struct snd_kcontrol_new dac33_dapm_abypassl_control =
+ SOC_DAPM_SINGLE("Switch", DAC33_LINEL_TO_LLO_VOL, 7, 1, 1);
+
+static const struct snd_kcontrol_new dac33_dapm_abypassr_control =
+ SOC_DAPM_SINGLE("Switch", DAC33_LINER_TO_RLO_VOL, 7, 1, 1);
+
+/* LOP L/R invert selection */
+static const char *dac33_lr_lom_texts[] = {"DAC", "LOP"};
+
+static SOC_ENUM_SINGLE_DECL(dac33_left_lom_enum,
+ DAC33_OUT_AMP_CTRL, 3,
+ dac33_lr_lom_texts);
+
+static const struct snd_kcontrol_new dac33_dapm_left_lom_control =
+SOC_DAPM_ENUM("Route", dac33_left_lom_enum);
+
+static SOC_ENUM_SINGLE_DECL(dac33_right_lom_enum,
+ DAC33_OUT_AMP_CTRL, 2,
+ dac33_lr_lom_texts);
+
+static const struct snd_kcontrol_new dac33_dapm_right_lom_control =
+SOC_DAPM_ENUM("Route", dac33_right_lom_enum);
+
+static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("LEFT_LO"),
+ SND_SOC_DAPM_OUTPUT("RIGHT_LO"),
+
+ SND_SOC_DAPM_INPUT("LINEL"),
+ SND_SOC_DAPM_INPUT("LINER"),
+
+ SND_SOC_DAPM_DAC("DACL", "Left Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DACR", "Right Playback", SND_SOC_NOPM, 0, 0),
+
+ /* Analog bypass */
+ SND_SOC_DAPM_SWITCH("Analog Left Bypass", SND_SOC_NOPM, 0, 0,
+ &dac33_dapm_abypassl_control),
+ SND_SOC_DAPM_SWITCH("Analog Right Bypass", SND_SOC_NOPM, 0, 0,
+ &dac33_dapm_abypassr_control),
+
+ SND_SOC_DAPM_MUX("Left LOM Inverted From", SND_SOC_NOPM, 0, 0,
+ &dac33_dapm_left_lom_control),
+ SND_SOC_DAPM_MUX("Right LOM Inverted From", SND_SOC_NOPM, 0, 0,
+ &dac33_dapm_right_lom_control),
+ /*
+ * For DAPM path, when only the anlog bypass path is enabled, and the
+ * LOP inverted from the corresponding DAC side.
+ * This is needed, so we can attach the DAC power supply in this case.
+ */
+ SND_SOC_DAPM_PGA("Left Bypass PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Bypass PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Left Amplifier",
+ DAC33_OUT_AMP_PWR_CTRL, 6, 3, 3, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Right Amplifier",
+ DAC33_OUT_AMP_PWR_CTRL, 4, 3, 3, 0),
+
+ SND_SOC_DAPM_SUPPLY("Left DAC Power",
+ DAC33_LDAC_PWR_CTRL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Right DAC Power",
+ DAC33_RDAC_PWR_CTRL, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Codec Power",
+ DAC33_PWR_CTRL, 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_PRE("Pre Playback", dac33_playback_event),
+ SND_SOC_DAPM_POST("Post Playback", dac33_playback_event),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Analog bypass */
+ {"Analog Left Bypass", "Switch", "LINEL"},
+ {"Analog Right Bypass", "Switch", "LINER"},
+
+ {"Output Left Amplifier", NULL, "DACL"},
+ {"Output Right Amplifier", NULL, "DACR"},
+
+ {"Left Bypass PGA", NULL, "Analog Left Bypass"},
+ {"Right Bypass PGA", NULL, "Analog Right Bypass"},
+
+ {"Left LOM Inverted From", "DAC", "Left Bypass PGA"},
+ {"Right LOM Inverted From", "DAC", "Right Bypass PGA"},
+ {"Left LOM Inverted From", "LOP", "Analog Left Bypass"},
+ {"Right LOM Inverted From", "LOP", "Analog Right Bypass"},
+
+ {"Output Left Amplifier", NULL, "Left LOM Inverted From"},
+ {"Output Right Amplifier", NULL, "Right LOM Inverted From"},
+
+ {"DACL", NULL, "Left DAC Power"},
+ {"DACR", NULL, "Right DAC Power"},
+
+ {"Left Bypass PGA", NULL, "Left DAC Power"},
+ {"Right Bypass PGA", NULL, "Right DAC Power"},
+
+ /* output */
+ {"LEFT_LO", NULL, "Output Left Amplifier"},
+ {"RIGHT_LO", NULL, "Output Right Amplifier"},
+
+ {"LEFT_LO", NULL, "Codec Power"},
+ {"RIGHT_LO", NULL, "Codec Power"},
+};
+
+static int dac33_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ /* Coming from OFF, switch on the codec */
+ ret = dac33_hard_power(codec, 1);
+ if (ret != 0)
+ return ret;
+
+ dac33_init_chip(codec);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Do not power off, when the codec is already off */
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ return 0;
+ ret = dac33_hard_power(codec, 0);
+ if (ret != 0)
+ return ret;
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33)
+{
+ struct snd_soc_codec *codec = dac33->codec;
+ unsigned int delay;
+ unsigned long flags;
+
+ switch (dac33->fifo_mode) {
+ case DAC33_FIFO_MODE1:
+ dac33_write16(codec, DAC33_NSAMPLE_MSB,
+ DAC33_THRREG(dac33->nsample));
+
+ /* Take the timestamps */
+ spin_lock_irqsave(&dac33->lock, flags);
+ dac33->t_stamp2 = ktime_to_us(ktime_get());
+ dac33->t_stamp1 = dac33->t_stamp2;
+ spin_unlock_irqrestore(&dac33->lock, flags);
+
+ dac33_write16(codec, DAC33_PREFILL_MSB,
+ DAC33_THRREG(dac33->alarm_threshold));
+ /* Enable Alarm Threshold IRQ with a delay */
+ delay = SAMPLES_TO_US(dac33->burst_rate,
+ dac33->alarm_threshold) + 1000;
+ usleep_range(delay, delay + 500);
+ dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT);
+ break;
+ case DAC33_FIFO_MODE7:
+ /* Take the timestamp */
+ spin_lock_irqsave(&dac33->lock, flags);
+ dac33->t_stamp1 = ktime_to_us(ktime_get());
+ /* Move back the timestamp with drain time */
+ dac33->t_stamp1 -= dac33->mode7_us_to_lthr;
+ spin_unlock_irqrestore(&dac33->lock, flags);
+
+ dac33_write16(codec, DAC33_PREFILL_MSB,
+ DAC33_THRREG(DAC33_MODE7_MARGIN));
+
+ /* Enable Upper Threshold IRQ */
+ dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MUT);
+ break;
+ default:
+ dev_warn(codec->dev, "Unhandled FIFO mode: %d\n",
+ dac33->fifo_mode);
+ break;
+ }
+}
+
+static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33)
+{
+ struct snd_soc_codec *codec = dac33->codec;
+ unsigned long flags;
+
+ switch (dac33->fifo_mode) {
+ case DAC33_FIFO_MODE1:
+ /* Take the timestamp */
+ spin_lock_irqsave(&dac33->lock, flags);
+ dac33->t_stamp2 = ktime_to_us(ktime_get());
+ spin_unlock_irqrestore(&dac33->lock, flags);
+
+ dac33_write16(codec, DAC33_NSAMPLE_MSB,
+ DAC33_THRREG(dac33->nsample));
+ break;
+ case DAC33_FIFO_MODE7:
+ /* At the moment we are not using interrupts in mode7 */
+ break;
+ default:
+ dev_warn(codec->dev, "Unhandled FIFO mode: %d\n",
+ dac33->fifo_mode);
+ break;
+ }
+}
+
+static void dac33_work(struct work_struct *work)
+{
+ struct snd_soc_codec *codec;
+ struct tlv320dac33_priv *dac33;
+ u8 reg;
+
+ dac33 = container_of(work, struct tlv320dac33_priv, work);
+ codec = dac33->codec;
+
+ mutex_lock(&dac33->mutex);
+ switch (dac33->state) {
+ case DAC33_PREFILL:
+ dac33->state = DAC33_PLAYBACK;
+ dac33_prefill_handler(dac33);
+ break;
+ case DAC33_PLAYBACK:
+ dac33_playback_handler(dac33);
+ break;
+ case DAC33_IDLE:
+ break;
+ case DAC33_FLUSH:
+ dac33->state = DAC33_IDLE;
+ /* Mask all interrupts from dac33 */
+ dac33_write(codec, DAC33_FIFO_IRQ_MASK, 0);
+
+ /* flush fifo */
+ reg = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+ reg |= DAC33_FIFOFLUSH;
+ dac33_write(codec, DAC33_FIFO_CTRL_A, reg);
+ break;
+ }
+ mutex_unlock(&dac33->mutex);
+}
+
+static irqreturn_t dac33_interrupt_handler(int irq, void *dev)
+{
+ struct snd_soc_codec *codec = dev;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dac33->lock, flags);
+ dac33->t_stamp1 = ktime_to_us(ktime_get());
+ spin_unlock_irqrestore(&dac33->lock, flags);
+
+ /* Do not schedule the workqueue in Mode7 */
+ if (dac33->fifo_mode != DAC33_FIFO_MODE7)
+ queue_work(dac33->dac33_wq, &dac33->work);
+
+ return IRQ_HANDLED;
+}
+
+static void dac33_oscwait(struct snd_soc_codec *codec)
+{
+ int timeout = 60;
+ u8 reg;
+
+ do {
+ usleep_range(1000, 2000);
+ dac33_read(codec, DAC33_INT_OSC_STATUS, &reg);
+ } while (((reg & 0x03) != DAC33_OSCSTATUS_NORMAL) && timeout--);
+ if ((reg & 0x03) != DAC33_OSCSTATUS_NORMAL)
+ dev_err(codec->dev,
+ "internal oscillator calibration failed\n");
+}
+
+static int dac33_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+
+ /* Stream started, save the substream pointer */
+ dac33->substream = substream;
+
+ return 0;
+}
+
+static void dac33_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+
+ dac33->substream = NULL;
+}
+
+#define CALC_BURST_RATE(bclkdiv, bclk_per_sample) \
+ (BURST_BASEFREQ_HZ / bclkdiv / bclk_per_sample)
+static int dac33_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+
+ /* Check parameters for validity */
+ switch (params_rate(params)) {
+ case 44100:
+ case 48000:
+ break;
+ default:
+ dev_err(codec->dev, "unsupported rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ dac33->fifo_size = DAC33_FIFO_SIZE_16BIT;
+ dac33->burst_rate = CALC_BURST_RATE(dac33->burst_bclkdiv, 32);
+ break;
+ case 32:
+ dac33->fifo_size = DAC33_FIFO_SIZE_24BIT;
+ dac33->burst_rate = CALC_BURST_RATE(dac33->burst_bclkdiv, 64);
+ break;
+ default:
+ dev_err(codec->dev, "unsupported width %d\n",
+ params_width(params));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define CALC_OSCSET(rate, refclk) ( \
+ ((((rate * 10000) / refclk) * 4096) + 7000) / 10000)
+#define CALC_RATIOSET(rate, refclk) ( \
+ ((((refclk * 100000) / rate) * 16384) + 50000) / 100000)
+
+/*
+ * tlv320dac33 is strict on the sequence of the register writes, if the register
+ * writes happens in different order, than dac33 might end up in unknown state.
+ * Use the known, working sequence of register writes to initialize the dac33.
+ */
+static int dac33_prepare_chip(struct snd_pcm_substream *substream,
+ struct snd_soc_codec *codec)
+{
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ unsigned int oscset, ratioset, pwr_ctrl, reg_tmp;
+ u8 aictrl_a, aictrl_b, fifoctrl_a;
+
+ switch (substream->runtime->rate) {
+ case 44100:
+ case 48000:
+ oscset = CALC_OSCSET(substream->runtime->rate, dac33->refclk);
+ ratioset = CALC_RATIOSET(substream->runtime->rate,
+ dac33->refclk);
+ break;
+ default:
+ dev_err(codec->dev, "unsupported rate %d\n",
+ substream->runtime->rate);
+ return -EINVAL;
+ }
+
+
+ aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
+ aictrl_a &= ~(DAC33_NCYCL_MASK | DAC33_WLEN_MASK);
+ /* Read FIFO control A, and clear FIFO flush bit */
+ fifoctrl_a = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+ fifoctrl_a &= ~DAC33_FIFOFLUSH;
+
+ fifoctrl_a &= ~DAC33_WIDTH;
+ switch (substream->runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ aictrl_a |= (DAC33_NCYCL_16 | DAC33_WLEN_16);
+ fifoctrl_a |= DAC33_WIDTH;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ aictrl_a |= (DAC33_NCYCL_32 | DAC33_WLEN_24);
+ break;
+ default:
+ dev_err(codec->dev, "unsupported format %d\n",
+ substream->runtime->format);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dac33->mutex);
+
+ if (!dac33->chip_power) {
+ /*
+ * Chip is not powered yet.
+ * Do the init in the dac33_set_bias_level later.
+ */
+ mutex_unlock(&dac33->mutex);
+ return 0;
+ }
+
+ dac33_soft_power(codec, 0);
+ dac33_soft_power(codec, 1);
+
+ reg_tmp = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
+ dac33_write(codec, DAC33_INT_OSC_CTRL, reg_tmp);
+
+ /* Write registers 0x08 and 0x09 (MSB, LSB) */
+ dac33_write16(codec, DAC33_INT_OSC_FREQ_RAT_A, oscset);
+
+ /* OSC calibration time */
+ dac33_write(codec, DAC33_CALIB_TIME, 96);
+
+ /* adjustment treshold & step */
+ dac33_write(codec, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) |
+ DAC33_ADJSTEP(1));
+
+ /* div=4 / gain=1 / div */
+ dac33_write(codec, DAC33_INT_OSC_CTRL_C, DAC33_REFDIV(4));
+
+ pwr_ctrl = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+ pwr_ctrl |= DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB;
+ dac33_write(codec, DAC33_PWR_CTRL, pwr_ctrl);
+
+ dac33_oscwait(codec);
+
+ if (dac33->fifo_mode) {
+ /* Generic for all FIFO modes */
+ /* 50-51 : ASRC Control registers */
+ dac33_write(codec, DAC33_ASRC_CTRL_A, DAC33_SRCLKDIV(1));
+ dac33_write(codec, DAC33_ASRC_CTRL_B, 1); /* ??? */
+
+ /* Write registers 0x34 and 0x35 (MSB, LSB) */
+ dac33_write16(codec, DAC33_SRC_REF_CLK_RATIO_A, ratioset);
+
+ /* Set interrupts to high active */
+ dac33_write(codec, DAC33_INTP_CTRL_A, DAC33_INTPM_AHIGH);
+ } else {
+ /* FIFO bypass mode */
+ /* 50-51 : ASRC Control registers */
+ dac33_write(codec, DAC33_ASRC_CTRL_A, DAC33_SRCBYP);
+ dac33_write(codec, DAC33_ASRC_CTRL_B, 0); /* ??? */
+ }
+
+ /* Interrupt behaviour configuration */
+ switch (dac33->fifo_mode) {
+ case DAC33_FIFO_MODE1:
+ dac33_write(codec, DAC33_FIFO_IRQ_MODE_B,
+ DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL));
+ break;
+ case DAC33_FIFO_MODE7:
+ dac33_write(codec, DAC33_FIFO_IRQ_MODE_A,
+ DAC33_UTM(DAC33_FIFO_IRQ_MODE_LEVEL));
+ break;
+ default:
+ /* in FIFO bypass mode, the interrupts are not used */
+ break;
+ }
+
+ aictrl_b = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+
+ switch (dac33->fifo_mode) {
+ case DAC33_FIFO_MODE1:
+ /*
+ * For mode1:
+ * Disable the FIFO bypass (Enable the use of FIFO)
+ * Select nSample mode
+ * BCLK is only running when data is needed by DAC33
+ */
+ fifoctrl_a &= ~DAC33_FBYPAS;
+ fifoctrl_a &= ~DAC33_FAUTO;
+ if (dac33->keep_bclk)
+ aictrl_b |= DAC33_BCLKON;
+ else
+ aictrl_b &= ~DAC33_BCLKON;
+ break;
+ case DAC33_FIFO_MODE7:
+ /*
+ * For mode1:
+ * Disable the FIFO bypass (Enable the use of FIFO)
+ * Select Threshold mode
+ * BCLK is only running when data is needed by DAC33
+ */
+ fifoctrl_a &= ~DAC33_FBYPAS;
+ fifoctrl_a |= DAC33_FAUTO;
+ if (dac33->keep_bclk)
+ aictrl_b |= DAC33_BCLKON;
+ else
+ aictrl_b &= ~DAC33_BCLKON;
+ break;
+ default:
+ /*
+ * For FIFO bypass mode:
+ * Enable the FIFO bypass (Disable the FIFO use)
+ * Set the BCLK as continuous
+ */
+ fifoctrl_a |= DAC33_FBYPAS;
+ aictrl_b |= DAC33_BCLKON;
+ break;
+ }
+
+ dac33_write(codec, DAC33_FIFO_CTRL_A, fifoctrl_a);
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b);
+
+ /*
+ * BCLK divide ratio
+ * 0: 1.5
+ * 1: 1
+ * 2: 2
+ * ...
+ * 254: 254
+ * 255: 255
+ */
+ if (dac33->fifo_mode)
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C,
+ dac33->burst_bclkdiv);
+ else
+ if (substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE)
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32);
+ else
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 16);
+
+ switch (dac33->fifo_mode) {
+ case DAC33_FIFO_MODE1:
+ dac33_write16(codec, DAC33_ATHR_MSB,
+ DAC33_THRREG(dac33->alarm_threshold));
+ break;
+ case DAC33_FIFO_MODE7:
+ /*
+ * Configure the threshold levels, and leave 10 sample space
+ * at the bottom, and also at the top of the FIFO
+ */
+ dac33_write16(codec, DAC33_UTHR_MSB, DAC33_THRREG(dac33->uthr));
+ dac33_write16(codec, DAC33_LTHR_MSB,
+ DAC33_THRREG(DAC33_MODE7_MARGIN));
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&dac33->mutex);
+
+ return 0;
+}
+
+static void dac33_calculate_times(struct snd_pcm_substream *substream,
+ struct snd_soc_codec *codec)
+{
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ unsigned int period_size = substream->runtime->period_size;
+ unsigned int rate = substream->runtime->rate;
+ unsigned int nsample_limit;
+
+ /* In bypass mode we don't need to calculate */
+ if (!dac33->fifo_mode)
+ return;
+
+ switch (dac33->fifo_mode) {
+ case DAC33_FIFO_MODE1:
+ /* Number of samples under i2c latency */
+ dac33->alarm_threshold = US_TO_SAMPLES(rate,
+ dac33->mode1_latency);
+ nsample_limit = dac33->fifo_size - dac33->alarm_threshold;
+
+ if (period_size <= dac33->alarm_threshold)
+ /*
+ * Configure nSamaple to number of periods,
+ * which covers the latency requironment.
+ */
+ dac33->nsample = period_size *
+ ((dac33->alarm_threshold / period_size) +
+ (dac33->alarm_threshold % period_size ?
+ 1 : 0));
+ else if (period_size > nsample_limit)
+ dac33->nsample = nsample_limit;
+ else
+ dac33->nsample = period_size;
+
+ dac33->mode1_us_burst = SAMPLES_TO_US(dac33->burst_rate,
+ dac33->nsample);
+ dac33->t_stamp1 = 0;
+ dac33->t_stamp2 = 0;
+ break;
+ case DAC33_FIFO_MODE7:
+ dac33->uthr = UTHR_FROM_PERIOD_SIZE(period_size, rate,
+ dac33->burst_rate) + 9;
+ if (dac33->uthr > (dac33->fifo_size - DAC33_MODE7_MARGIN))
+ dac33->uthr = dac33->fifo_size - DAC33_MODE7_MARGIN;
+ if (dac33->uthr < (DAC33_MODE7_MARGIN + 10))
+ dac33->uthr = (DAC33_MODE7_MARGIN + 10);
+
+ dac33->mode7_us_to_lthr =
+ SAMPLES_TO_US(substream->runtime->rate,
+ dac33->uthr - DAC33_MODE7_MARGIN + 1);
+ dac33->t_stamp1 = 0;
+ break;
+ default:
+ break;
+ }
+
+}
+
+static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (dac33->fifo_mode) {
+ dac33->state = DAC33_PREFILL;
+ queue_work(dac33->dac33_wq, &dac33->work);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (dac33->fifo_mode) {
+ dac33->state = DAC33_FLUSH;
+ queue_work(dac33->dac33_wq, &dac33->work);
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static snd_pcm_sframes_t dac33_dai_delay(
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ unsigned long long t0, t1, t_now;
+ unsigned int time_delta, uthr;
+ int samples_out, samples_in, samples;
+ snd_pcm_sframes_t delay = 0;
+ unsigned long flags;
+
+ switch (dac33->fifo_mode) {
+ case DAC33_FIFO_BYPASS:
+ break;
+ case DAC33_FIFO_MODE1:
+ spin_lock_irqsave(&dac33->lock, flags);
+ t0 = dac33->t_stamp1;
+ t1 = dac33->t_stamp2;
+ spin_unlock_irqrestore(&dac33->lock, flags);
+ t_now = ktime_to_us(ktime_get());
+
+ /* We have not started to fill the FIFO yet, delay is 0 */
+ if (!t1)
+ goto out;
+
+ if (t0 > t1) {
+ /*
+ * Phase 1:
+ * After Alarm threshold, and before nSample write
+ */
+ time_delta = t_now - t0;
+ samples_out = time_delta ? US_TO_SAMPLES(
+ substream->runtime->rate,
+ time_delta) : 0;
+
+ if (likely(dac33->alarm_threshold > samples_out))
+ delay = dac33->alarm_threshold - samples_out;
+ else
+ delay = 0;
+ } else if ((t_now - t1) <= dac33->mode1_us_burst) {
+ /*
+ * Phase 2:
+ * After nSample write (during burst operation)
+ */
+ time_delta = t_now - t0;
+ samples_out = time_delta ? US_TO_SAMPLES(
+ substream->runtime->rate,
+ time_delta) : 0;
+
+ time_delta = t_now - t1;
+ samples_in = time_delta ? US_TO_SAMPLES(
+ dac33->burst_rate,
+ time_delta) : 0;
+
+ samples = dac33->alarm_threshold;
+ samples += (samples_in - samples_out);
+
+ if (likely(samples > 0))
+ delay = samples;
+ else
+ delay = 0;
+ } else {
+ /*
+ * Phase 3:
+ * After burst operation, before next alarm threshold
+ */
+ time_delta = t_now - t0;
+ samples_out = time_delta ? US_TO_SAMPLES(
+ substream->runtime->rate,
+ time_delta) : 0;
+
+ samples_in = dac33->nsample;
+ samples = dac33->alarm_threshold;
+ samples += (samples_in - samples_out);
+
+ if (likely(samples > 0))
+ delay = samples > dac33->fifo_size ?
+ dac33->fifo_size : samples;
+ else
+ delay = 0;
+ }
+ break;
+ case DAC33_FIFO_MODE7:
+ spin_lock_irqsave(&dac33->lock, flags);
+ t0 = dac33->t_stamp1;
+ uthr = dac33->uthr;
+ spin_unlock_irqrestore(&dac33->lock, flags);
+ t_now = ktime_to_us(ktime_get());
+
+ /* We have not started to fill the FIFO yet, delay is 0 */
+ if (!t0)
+ goto out;
+
+ if (t_now <= t0) {
+ /*
+ * Either the timestamps are messed or equal. Report
+ * maximum delay
+ */
+ delay = uthr;
+ goto out;
+ }
+
+ time_delta = t_now - t0;
+ if (time_delta <= dac33->mode7_us_to_lthr) {
+ /*
+ * Phase 1:
+ * After burst (draining phase)
+ */
+ samples_out = US_TO_SAMPLES(
+ substream->runtime->rate,
+ time_delta);
+
+ if (likely(uthr > samples_out))
+ delay = uthr - samples_out;
+ else
+ delay = 0;
+ } else {
+ /*
+ * Phase 2:
+ * During burst operation
+ */
+ time_delta = time_delta - dac33->mode7_us_to_lthr;
+
+ samples_out = US_TO_SAMPLES(
+ substream->runtime->rate,
+ time_delta);
+ samples_in = US_TO_SAMPLES(
+ dac33->burst_rate,
+ time_delta);
+ delay = DAC33_MODE7_MARGIN + samples_in - samples_out;
+
+ if (unlikely(delay > uthr))
+ delay = uthr;
+ }
+ break;
+ default:
+ dev_warn(codec->dev, "Unhandled FIFO mode: %d\n",
+ dac33->fifo_mode);
+ break;
+ }
+out:
+ return delay;
+}
+
+static int dac33_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ u8 ioc_reg, asrcb_reg;
+
+ ioc_reg = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
+ asrcb_reg = dac33_read_reg_cache(codec, DAC33_ASRC_CTRL_B);
+ switch (clk_id) {
+ case TLV320DAC33_MCLK:
+ ioc_reg |= DAC33_REFSEL;
+ asrcb_reg |= DAC33_SRCREFSEL;
+ break;
+ case TLV320DAC33_SLEEPCLK:
+ ioc_reg &= ~DAC33_REFSEL;
+ asrcb_reg &= ~DAC33_SRCREFSEL;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock ID (%d)\n", clk_id);
+ break;
+ }
+ dac33->refclk = freq;
+
+ dac33_write_reg_cache(codec, DAC33_INT_OSC_CTRL, ioc_reg);
+ dac33_write_reg_cache(codec, DAC33_ASRC_CTRL_B, asrcb_reg);
+
+ return 0;
+}
+
+static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ u8 aictrl_a, aictrl_b;
+
+ aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
+ aictrl_b = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Codec Master */
+ aictrl_a |= (DAC33_MSBCLK | DAC33_MSWCLK);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Codec Slave */
+ if (dac33->fifo_mode) {
+ dev_err(codec->dev, "FIFO mode requires master mode\n");
+ return -EINVAL;
+ } else
+ aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ aictrl_a &= ~DAC33_AFMT_MASK;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ aictrl_a |= DAC33_AFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ aictrl_a |= DAC33_AFMT_DSP;
+ aictrl_b &= ~DAC33_DATA_DELAY_MASK;
+ aictrl_b |= DAC33_DATA_DELAY(0);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ aictrl_a |= DAC33_AFMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aictrl_a |= DAC33_AFMT_LEFT_J;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported format (%u)\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ dac33_write_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
+ dac33_write_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b);
+
+ return 0;
+}
+
+static int dac33_soc_probe(struct snd_soc_codec *codec)
+{
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ codec->control_data = dac33->control_data;
+ codec->hw_write = (hw_write_t) i2c_master_send;
+ dac33->codec = codec;
+
+ /* Read the tlv320dac33 ID registers */
+ ret = dac33_hard_power(codec, 1);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to power up codec: %d\n", ret);
+ goto err_power;
+ }
+ ret = dac33_read_id(codec);
+ dac33_hard_power(codec, 0);
+
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read chip ID: %d\n", ret);
+ ret = -ENODEV;
+ goto err_power;
+ }
+
+ /* Check if the IRQ number is valid and request it */
+ if (dac33->irq >= 0) {
+ ret = request_irq(dac33->irq, dac33_interrupt_handler,
+ IRQF_TRIGGER_RISING,
+ codec->component.name, codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Could not request IRQ%d (%d)\n",
+ dac33->irq, ret);
+ dac33->irq = -1;
+ }
+ if (dac33->irq != -1) {
+ /* Setup work queue */
+ dac33->dac33_wq =
+ create_singlethread_workqueue("tlv320dac33");
+ if (dac33->dac33_wq == NULL) {
+ free_irq(dac33->irq, codec);
+ return -ENOMEM;
+ }
+
+ INIT_WORK(&dac33->work, dac33_work);
+ }
+ }
+
+ /* Only add the FIFO controls, if we have valid IRQ number */
+ if (dac33->irq >= 0)
+ snd_soc_add_codec_controls(codec, dac33_mode_snd_controls,
+ ARRAY_SIZE(dac33_mode_snd_controls));
+
+err_power:
+ return ret;
+}
+
+static int dac33_soc_remove(struct snd_soc_codec *codec)
+{
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+
+ if (dac33->irq >= 0) {
+ free_irq(dac33->irq, dac33->codec);
+ destroy_workqueue(dac33->dac33_wq);
+ }
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_tlv320dac33 = {
+ .read = dac33_read_reg_cache,
+ .write = dac33_write_locked,
+ .set_bias_level = dac33_set_bias_level,
+ .idle_bias_off = true,
+ .reg_cache_size = ARRAY_SIZE(dac33_reg),
+ .reg_word_size = sizeof(u8),
+ .reg_cache_default = dac33_reg,
+ .probe = dac33_soc_probe,
+ .remove = dac33_soc_remove,
+
+ .controls = dac33_snd_controls,
+ .num_controls = ARRAY_SIZE(dac33_snd_controls),
+ .dapm_widgets = dac33_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dac33_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+};
+
+#define DAC33_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+#define DAC33_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops dac33_dai_ops = {
+ .startup = dac33_startup,
+ .shutdown = dac33_shutdown,
+ .hw_params = dac33_hw_params,
+ .trigger = dac33_pcm_trigger,
+ .delay = dac33_dai_delay,
+ .set_sysclk = dac33_set_dai_sysclk,
+ .set_fmt = dac33_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver dac33_dai = {
+ .name = "tlv320dac33-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = DAC33_RATES,
+ .formats = DAC33_FORMATS,
+ .sig_bits = 24,
+ },
+ .ops = &dac33_dai_ops,
+};
+
+static int dac33_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tlv320dac33_platform_data *pdata;
+ struct tlv320dac33_priv *dac33;
+ int ret, i;
+
+ if (client->dev.platform_data == NULL) {
+ dev_err(&client->dev, "Platform data not set\n");
+ return -ENODEV;
+ }
+ pdata = client->dev.platform_data;
+
+ dac33 = devm_kzalloc(&client->dev, sizeof(struct tlv320dac33_priv),
+ GFP_KERNEL);
+ if (dac33 == NULL)
+ return -ENOMEM;
+
+ dac33->control_data = client;
+ mutex_init(&dac33->mutex);
+ spin_lock_init(&dac33->lock);
+
+ i2c_set_clientdata(client, dac33);
+
+ dac33->power_gpio = pdata->power_gpio;
+ dac33->burst_bclkdiv = pdata->burst_bclkdiv;
+ dac33->keep_bclk = pdata->keep_bclk;
+ dac33->mode1_latency = pdata->mode1_latency;
+ if (!dac33->mode1_latency)
+ dac33->mode1_latency = 10000; /* 10ms */
+ dac33->irq = client->irq;
+ /* Disable FIFO use by default */
+ dac33->fifo_mode = DAC33_FIFO_BYPASS;
+
+ /* Check if the reset GPIO number is valid and request it */
+ if (dac33->power_gpio >= 0) {
+ ret = gpio_request(dac33->power_gpio, "tlv320dac33 reset");
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to request reset GPIO (%d)\n",
+ dac33->power_gpio);
+ goto err_gpio;
+ }
+ gpio_direction_output(dac33->power_gpio, 0);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dac33->supplies); i++)
+ dac33->supplies[i].supply = dac33_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(dac33->supplies),
+ dac33->supplies);
+
+ if (ret != 0) {
+ dev_err(&client->dev, "Failed to request supplies: %d\n", ret);
+ goto err_get;
+ }
+
+ ret = snd_soc_register_codec(&client->dev,
+ &soc_codec_dev_tlv320dac33, &dac33_dai, 1);
+ if (ret < 0)
+ goto err_get;
+
+ return ret;
+err_get:
+ if (dac33->power_gpio >= 0)
+ gpio_free(dac33->power_gpio);
+err_gpio:
+ return ret;
+}
+
+static int dac33_i2c_remove(struct i2c_client *client)
+{
+ struct tlv320dac33_priv *dac33 = i2c_get_clientdata(client);
+
+ if (unlikely(dac33->chip_power))
+ dac33_hard_power(dac33->codec, 0);
+
+ if (dac33->power_gpio >= 0)
+ gpio_free(dac33->power_gpio);
+
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id tlv320dac33_i2c_id[] = {
+ {
+ .name = "tlv320dac33",
+ .driver_data = 0,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, tlv320dac33_i2c_id);
+
+static struct i2c_driver tlv320dac33_i2c_driver = {
+ .driver = {
+ .name = "tlv320dac33-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = dac33_i2c_probe,
+ .remove = dac33_i2c_remove,
+ .id_table = tlv320dac33_i2c_id,
+};
+
+module_i2c_driver(tlv320dac33_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TLV320DAC33 codec driver");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/tlv320dac33.h b/kernel/sound/soc/codecs/tlv320dac33.h
new file mode 100644
index 000000000..ed6967074
--- /dev/null
+++ b/kernel/sound/soc/codecs/tlv320dac33.h
@@ -0,0 +1,264 @@
+/*
+ * ALSA SoC Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Copyright: (C) 2009 Nokia 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.
+ *
+ * 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 __TLV320DAC33_H
+#define __TLV320DAC33_H
+
+#define DAC33_PAGE_SELECT 0x00
+#define DAC33_PWR_CTRL 0x01
+#define DAC33_PLL_CTRL_A 0x02
+#define DAC33_PLL_CTRL_B 0x03
+#define DAC33_PLL_CTRL_C 0x04
+#define DAC33_PLL_CTRL_D 0x05
+#define DAC33_PLL_CTRL_E 0x06
+#define DAC33_INT_OSC_CTRL 0x07
+#define DAC33_INT_OSC_FREQ_RAT_A 0x08
+#define DAC33_INT_OSC_FREQ_RAT_B 0x09
+#define DAC33_INT_OSC_DAC_RATIO_SET 0x0A
+#define DAC33_CALIB_TIME 0x0B
+#define DAC33_INT_OSC_CTRL_B 0x0C
+#define DAC33_INT_OSC_CTRL_C 0x0D
+#define DAC33_INT_OSC_STATUS 0x0E
+#define DAC33_INT_OSC_DAC_RATIO_READ 0x0F
+#define DAC33_INT_OSC_FREQ_RAT_READ_A 0x10
+#define DAC33_INT_OSC_FREQ_RAT_READ_B 0x11
+#define DAC33_SER_AUDIOIF_CTRL_A 0x12
+#define DAC33_SER_AUDIOIF_CTRL_B 0x13
+#define DAC33_SER_AUDIOIF_CTRL_C 0x14
+#define DAC33_FIFO_CTRL_A 0x15
+#define DAC33_UTHR_MSB 0x16
+#define DAC33_UTHR_LSB 0x17
+#define DAC33_ATHR_MSB 0x18
+#define DAC33_ATHR_LSB 0x19
+#define DAC33_LTHR_MSB 0x1A
+#define DAC33_LTHR_LSB 0x1B
+#define DAC33_PREFILL_MSB 0x1C
+#define DAC33_PREFILL_LSB 0x1D
+#define DAC33_NSAMPLE_MSB 0x1E
+#define DAC33_NSAMPLE_LSB 0x1F
+#define DAC33_FIFO_WPTR_MSB 0x20
+#define DAC33_FIFO_WPTR_LSB 0x21
+#define DAC33_FIFO_RPTR_MSB 0x22
+#define DAC33_FIFO_RPTR_LSB 0x23
+#define DAC33_FIFO_DEPTH_MSB 0x24
+#define DAC33_FIFO_DEPTH_LSB 0x25
+#define DAC33_SAMPLES_REMAINING_MSB 0x26
+#define DAC33_SAMPLES_REMAINING_LSB 0x27
+#define DAC33_FIFO_IRQ_FLAG 0x28
+#define DAC33_FIFO_IRQ_MASK 0x29
+#define DAC33_FIFO_IRQ_MODE_A 0x2A
+#define DAC33_FIFO_IRQ_MODE_B 0x2B
+#define DAC33_DAC_CTRL_A 0x2C
+#define DAC33_DAC_CTRL_B 0x2D
+#define DAC33_DAC_CTRL_C 0x2E
+#define DAC33_LDAC_DIG_VOL_CTRL 0x2F
+#define DAC33_RDAC_DIG_VOL_CTRL 0x30
+#define DAC33_DAC_STATUS_FLAGS 0x31
+#define DAC33_ASRC_CTRL_A 0x32
+#define DAC33_ASRC_CTRL_B 0x33
+#define DAC33_SRC_REF_CLK_RATIO_A 0x34
+#define DAC33_SRC_REF_CLK_RATIO_B 0x35
+#define DAC33_SRC_EST_REF_CLK_RATIO_A 0x36
+#define DAC33_SRC_EST_REF_CLK_RATIO_B 0x37
+#define DAC33_INTP_CTRL_A 0x38
+#define DAC33_INTP_CTRL_B 0x39
+/* Registers 0x3A - 0x3F Reserved */
+#define DAC33_LDAC_PWR_CTRL 0x40
+#define DAC33_RDAC_PWR_CTRL 0x41
+#define DAC33_OUT_AMP_CM_CTRL 0x42
+#define DAC33_OUT_AMP_PWR_CTRL 0x43
+#define DAC33_OUT_AMP_CTRL 0x44
+#define DAC33_LINEL_TO_LLO_VOL 0x45
+/* Registers 0x45 - 0x47 Reserved */
+#define DAC33_LINER_TO_RLO_VOL 0x48
+#define DAC33_ANA_VOL_SOFT_STEP_CTRL 0x49
+#define DAC33_OSC_TRIM 0x4A
+/* Registers 0x4B - 0x7C Reserved */
+#define DAC33_DEVICE_ID_MSB 0x7D
+#define DAC33_DEVICE_ID_LSB 0x7E
+#define DAC33_DEVICE_REV_ID 0x7F
+
+#define DAC33_CACHEREGNUM 128
+
+/* Bit definitions */
+
+/* DAC33_PWR_CTRL (0x01) */
+#define DAC33_DACRPDNB (0x01 << 0)
+#define DAC33_DACLPDNB (0x01 << 1)
+#define DAC33_OSCPDNB (0x01 << 2)
+#define DAC33_PLLPDNB (0x01 << 3)
+#define DAC33_PDNALLB (0x01 << 4)
+#define DAC33_SOFT_RESET (0x01 << 7)
+
+/* DAC33_INT_OSC_CTRL (0x07) */
+#define DAC33_REFSEL (0x01 << 1)
+
+/* DAC33_INT_OSC_CTRL_B (0x0C) */
+#define DAC33_ADJSTEP(x) (x << 0)
+#define DAC33_ADJTHRSHLD(x) (x << 4)
+
+/* DAC33_INT_OSC_CTRL_C (0x0D) */
+#define DAC33_REFDIV(x) (x << 4)
+
+/* DAC33_INT_OSC_STATUS (0x0E) */
+#define DAC33_OSCSTATUS_IDLE_CALIB (0x00)
+#define DAC33_OSCSTATUS_NORMAL (0x01)
+#define DAC33_OSCSTATUS_ADJUSTMENT (0x03)
+#define DAC33_OSCSTATUS_NOT_USED (0x02)
+
+/* DAC33_SER_AUDIOIF_CTRL_A (0x12) */
+#define DAC33_MSWCLK (0x01 << 0)
+#define DAC33_MSBCLK (0x01 << 1)
+#define DAC33_AFMT_MASK (0x03 << 2)
+#define DAC33_AFMT_I2S (0x00 << 2)
+#define DAC33_AFMT_DSP (0x01 << 2)
+#define DAC33_AFMT_RIGHT_J (0x02 << 2)
+#define DAC33_AFMT_LEFT_J (0x03 << 2)
+#define DAC33_WLEN_MASK (0x03 << 4)
+#define DAC33_WLEN_16 (0x00 << 4)
+#define DAC33_WLEN_20 (0x01 << 4)
+#define DAC33_WLEN_24 (0x02 << 4)
+#define DAC33_WLEN_32 (0x03 << 4)
+#define DAC33_NCYCL_MASK (0x03 << 6)
+#define DAC33_NCYCL_16 (0x00 << 6)
+#define DAC33_NCYCL_20 (0x01 << 6)
+#define DAC33_NCYCL_24 (0x02 << 6)
+#define DAC33_NCYCL_32 (0x03 << 6)
+
+/* DAC33_SER_AUDIOIF_CTRL_B (0x13) */
+#define DAC33_DATA_DELAY_MASK (0x03 << 2)
+#define DAC33_DATA_DELAY(x) (x << 2)
+#define DAC33_BCLKON (0x01 << 5)
+
+/* DAC33_FIFO_CTRL_A (0x15) */
+#define DAC33_WIDTH (0x01 << 0)
+#define DAC33_FBYPAS (0x01 << 1)
+#define DAC33_FAUTO (0x01 << 2)
+#define DAC33_FIFOFLUSH (0x01 << 3)
+
+/*
+ * UTHR, ATHR, LTHR, PREFILL, NSAMPLE (0x16 - 0x1F)
+ * 13-bit values
+*/
+#define DAC33_THRREG(x) (((x) & 0x1FFF) << 3)
+
+/* DAC33_FIFO_IRQ_MASK (0x29) */
+#define DAC33_MNS (0x01 << 0)
+#define DAC33_MPS (0x01 << 1)
+#define DAC33_MAT (0x01 << 2)
+#define DAC33_MLT (0x01 << 3)
+#define DAC33_MUT (0x01 << 4)
+#define DAC33_MUF (0x01 << 5)
+#define DAC33_MOF (0x01 << 6)
+
+#define DAC33_FIFO_IRQ_MODE_MASK (0x03)
+#define DAC33_FIFO_IRQ_MODE_RISING (0x00)
+#define DAC33_FIFO_IRQ_MODE_FALLING (0x01)
+#define DAC33_FIFO_IRQ_MODE_LEVEL (0x02)
+#define DAC33_FIFO_IRQ_MODE_EDGE (0x03)
+
+/* DAC33_FIFO_IRQ_MODE_A (0x2A) */
+#define DAC33_UTM(x) (x << 0)
+#define DAC33_UFM(x) (x << 2)
+#define DAC33_OFM(x) (x << 4)
+
+/* DAC33_FIFO_IRQ_MODE_B (0x2B) */
+#define DAC33_NSM(x) (x << 0)
+#define DAC33_PSM(x) (x << 2)
+#define DAC33_ATM(x) (x << 4)
+#define DAC33_LTM(x) (x << 6)
+
+/* DAC33_DAC_CTRL_A (0x2C) */
+#define DAC33_DACRATE(x) (x << 0)
+#define DAC33_DACDUAL (0x01 << 4)
+#define DAC33_DACLKSEL_MASK (0x03 << 5)
+#define DAC33_DACLKSEL_INTSOC (0x00 << 5)
+#define DAC33_DACLKSEL_PLL (0x01 << 5)
+#define DAC33_DACLKSEL_MCLK (0x02 << 5)
+#define DAC33_DACLKSEL_BCLK (0x03 << 5)
+
+/* DAC33_DAC_CTRL_B (0x2D) */
+#define DAC33_DACSRCR_MASK (0x03 << 0)
+#define DAC33_DACSRCR_MUTE (0x00 << 0)
+#define DAC33_DACSRCR_RIGHT (0x01 << 0)
+#define DAC33_DACSRCR_LEFT (0x02 << 0)
+#define DAC33_DACSRCR_MONOMIX (0x03 << 0)
+#define DAC33_DACSRCL_MASK (0x03 << 2)
+#define DAC33_DACSRCL_MUTE (0x00 << 2)
+#define DAC33_DACSRCL_LEFT (0x01 << 2)
+#define DAC33_DACSRCL_RIGHT (0x02 << 2)
+#define DAC33_DACSRCL_MONOMIX (0x03 << 2)
+#define DAC33_DVOLSTEP_MASK (0x03 << 4)
+#define DAC33_DVOLSTEP_SS_PERFS (0x00 << 4)
+#define DAC33_DVOLSTEP_SS_PER2FS (0x01 << 4)
+#define DAC33_DVOLSTEP_SS_DISABLED (0x02 << 4)
+#define DAC33_DVOLCTRL_MASK (0x03 << 6)
+#define DAC33_DVOLCTRL_LR_INDEPENDENT1 (0x00 << 6)
+#define DAC33_DVOLCTRL_LR_RIGHT_CONTROL (0x01 << 6)
+#define DAC33_DVOLCTRL_LR_LEFT_CONTROL (0x02 << 6)
+#define DAC33_DVOLCTRL_LR_INDEPENDENT2 (0x03 << 6)
+
+/* DAC33_DAC_CTRL_C (0x2E) */
+#define DAC33_DEEMENR (0x01 << 0)
+#define DAC33_EFFENR (0x01 << 1)
+#define DAC33_DEEMENL (0x01 << 2)
+#define DAC33_EFFENL (0x01 << 3)
+#define DAC33_EN3D (0x01 << 4)
+#define DAC33_RESYNMUTE (0x01 << 5)
+#define DAC33_RESYNEN (0x01 << 6)
+
+/* DAC33_ASRC_CTRL_A (0x32) */
+#define DAC33_SRCBYP (0x01 << 0)
+#define DAC33_SRCLKSEL_MASK (0x03 << 1)
+#define DAC33_SRCLKSEL_INTSOC (0x00 << 1)
+#define DAC33_SRCLKSEL_PLL (0x01 << 1)
+#define DAC33_SRCLKSEL_MCLK (0x02 << 1)
+#define DAC33_SRCLKSEL_BCLK (0x03 << 1)
+#define DAC33_SRCLKDIV(x) (x << 3)
+
+/* DAC33_ASRC_CTRL_B (0x33) */
+#define DAC33_SRCSETUP(x) (x << 0)
+#define DAC33_SRCREFSEL (0x01 << 4)
+#define DAC33_SRCREFDIV(x) (x << 5)
+
+/* DAC33_INTP_CTRL_A (0x38) */
+#define DAC33_INTPSEL (0x01 << 0)
+#define DAC33_INTPM_MASK (0x03 << 1)
+#define DAC33_INTPM_ALOW_OPENDRAIN (0x00 << 1)
+#define DAC33_INTPM_ALOW (0x01 << 1)
+#define DAC33_INTPM_AHIGH (0x02 << 1)
+
+/* DAC33_LDAC_PWR_CTRL (0x40) */
+/* DAC33_RDAC_PWR_CTRL (0x41) */
+#define DAC33_DACLRNUM (0x01 << 2)
+#define DAC33_LROUT_GAIN(x) (x << 0)
+
+/* DAC33_ANA_VOL_SOFT_STEP_CTRL (0x49) */
+#define DAC33_VOLCLKSEL (0x01 << 0)
+#define DAC33_VOLCLKEN (0x01 << 1)
+#define DAC33_VOLBYPASS (0x01 << 2)
+
+#define TLV320DAC33_MCLK 0
+#define TLV320DAC33_SLEEPCLK 1
+
+#endif /* __TLV320DAC33_H */
diff --git a/kernel/sound/soc/codecs/tpa6130a2.c b/kernel/sound/soc/codecs/tpa6130a2.c
new file mode 100644
index 000000000..6fac9e034
--- /dev/null
+++ b/kernel/sound/soc/codecs/tpa6130a2.c
@@ -0,0 +1,503 @@
+/*
+ * ALSA SoC Texas Instruments TPA6130A2 headset stereo amplifier driver
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@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 <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/tpa6130a2-plat.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#include "tpa6130a2.h"
+
+enum tpa_model {
+ TPA6130A2,
+ TPA6140A2,
+};
+
+static struct i2c_client *tpa6130a2_client;
+
+/* This struct is used to save the context */
+struct tpa6130a2_data {
+ struct mutex mutex;
+ unsigned char regs[TPA6130A2_CACHEREGNUM];
+ struct regulator *supply;
+ int power_gpio;
+ u8 power_state:1;
+ enum tpa_model id;
+};
+
+static int tpa6130a2_i2c_read(int reg)
+{
+ struct tpa6130a2_data *data;
+ int val;
+
+ if (WARN_ON(!tpa6130a2_client))
+ return -EINVAL;
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ /* If powered off, return the cached value */
+ if (data->power_state) {
+ val = i2c_smbus_read_byte_data(tpa6130a2_client, reg);
+ if (val < 0)
+ dev_err(&tpa6130a2_client->dev, "Read failed\n");
+ else
+ data->regs[reg] = val;
+ } else {
+ val = data->regs[reg];
+ }
+
+ return val;
+}
+
+static int tpa6130a2_i2c_write(int reg, u8 value)
+{
+ struct tpa6130a2_data *data;
+ int val = 0;
+
+ if (WARN_ON(!tpa6130a2_client))
+ return -EINVAL;
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ if (data->power_state) {
+ val = i2c_smbus_write_byte_data(tpa6130a2_client, reg, value);
+ if (val < 0) {
+ dev_err(&tpa6130a2_client->dev, "Write failed\n");
+ return val;
+ }
+ }
+
+ /* Either powered on or off, we save the context */
+ data->regs[reg] = value;
+
+ return val;
+}
+
+static u8 tpa6130a2_read(int reg)
+{
+ struct tpa6130a2_data *data;
+
+ if (WARN_ON(!tpa6130a2_client))
+ return 0;
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ return data->regs[reg];
+}
+
+static int tpa6130a2_initialize(void)
+{
+ struct tpa6130a2_data *data;
+ int i, ret = 0;
+
+ if (WARN_ON(!tpa6130a2_client))
+ return -EINVAL;
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ for (i = 1; i < TPA6130A2_REG_VERSION; i++) {
+ ret = tpa6130a2_i2c_write(i, data->regs[i]);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static int tpa6130a2_power(u8 power)
+{
+ struct tpa6130a2_data *data;
+ u8 val;
+ int ret = 0;
+
+ if (WARN_ON(!tpa6130a2_client))
+ return -EINVAL;
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ mutex_lock(&data->mutex);
+ if (power == data->power_state)
+ goto exit;
+
+ if (power) {
+ ret = regulator_enable(data->supply);
+ if (ret != 0) {
+ dev_err(&tpa6130a2_client->dev,
+ "Failed to enable supply: %d\n", ret);
+ goto exit;
+ }
+ /* Power on */
+ if (data->power_gpio >= 0)
+ gpio_set_value(data->power_gpio, 1);
+
+ data->power_state = 1;
+ ret = tpa6130a2_initialize();
+ if (ret < 0) {
+ dev_err(&tpa6130a2_client->dev,
+ "Failed to initialize chip\n");
+ if (data->power_gpio >= 0)
+ gpio_set_value(data->power_gpio, 0);
+ regulator_disable(data->supply);
+ data->power_state = 0;
+ goto exit;
+ }
+ } else {
+ /* set SWS */
+ val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+ val |= TPA6130A2_SWS;
+ tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+
+ /* Power off */
+ if (data->power_gpio >= 0)
+ gpio_set_value(data->power_gpio, 0);
+
+ ret = regulator_disable(data->supply);
+ if (ret != 0) {
+ dev_err(&tpa6130a2_client->dev,
+ "Failed to disable supply: %d\n", ret);
+ goto exit;
+ }
+
+ data->power_state = 0;
+ }
+
+exit:
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+static int tpa6130a2_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct tpa6130a2_data *data;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+
+ if (WARN_ON(!tpa6130a2_client))
+ return -EINVAL;
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ mutex_lock(&data->mutex);
+
+ ucontrol->value.integer.value[0] =
+ (tpa6130a2_read(reg) >> shift) & mask;
+
+ if (invert)
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+
+ mutex_unlock(&data->mutex);
+ return 0;
+}
+
+static int tpa6130a2_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct tpa6130a2_data *data;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val = (ucontrol->value.integer.value[0] & mask);
+ unsigned int val_reg;
+
+ if (WARN_ON(!tpa6130a2_client))
+ return -EINVAL;
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ if (invert)
+ val = max - val;
+
+ mutex_lock(&data->mutex);
+
+ val_reg = tpa6130a2_read(reg);
+ if (((val_reg >> shift) & mask) == val) {
+ mutex_unlock(&data->mutex);
+ return 0;
+ }
+
+ val_reg &= ~(mask << shift);
+ val_reg |= val << shift;
+ tpa6130a2_i2c_write(reg, val_reg);
+
+ mutex_unlock(&data->mutex);
+
+ return 1;
+}
+
+/*
+ * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going
+ * down in gain.
+ */
+static const unsigned int tpa6130_tlv[] = {
+ TLV_DB_RANGE_HEAD(10),
+ 0, 1, TLV_DB_SCALE_ITEM(-5950, 600, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(-5000, 250, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(-4550, 160, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(-4140, 190, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(-3650, 120, 0),
+ 10, 11, TLV_DB_SCALE_ITEM(-3330, 160, 0),
+ 12, 13, TLV_DB_SCALE_ITEM(-3040, 180, 0),
+ 14, 20, TLV_DB_SCALE_ITEM(-2710, 110, 0),
+ 21, 37, TLV_DB_SCALE_ITEM(-1960, 74, 0),
+ 38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0),
+};
+
+static const struct snd_kcontrol_new tpa6130a2_controls[] = {
+ SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume",
+ TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
+ tpa6130a2_get_volsw, tpa6130a2_put_volsw,
+ tpa6130_tlv),
+};
+
+static const unsigned int tpa6140_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 8, TLV_DB_SCALE_ITEM(-5900, 400, 0),
+ 9, 16, TLV_DB_SCALE_ITEM(-2500, 200, 0),
+ 17, 31, TLV_DB_SCALE_ITEM(-1000, 100, 0),
+};
+
+static const struct snd_kcontrol_new tpa6140a2_controls[] = {
+ SOC_SINGLE_EXT_TLV("TPA6140A2 Headphone Playback Volume",
+ TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0,
+ tpa6130a2_get_volsw, tpa6130a2_put_volsw,
+ tpa6140_tlv),
+};
+
+/*
+ * Enable or disable channel (left or right)
+ * The bit number for mute and amplifier are the same per channel:
+ * bit 6: Right channel
+ * bit 7: Left channel
+ * in both registers.
+ */
+static void tpa6130a2_channel_enable(u8 channel, int enable)
+{
+ u8 val;
+
+ if (enable) {
+ /* Enable channel */
+ /* Enable amplifier */
+ val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+ val |= channel;
+ val &= ~TPA6130A2_SWS;
+ tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+
+ /* Unmute channel */
+ val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
+ val &= ~channel;
+ tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
+ } else {
+ /* Disable channel */
+ /* Mute channel */
+ val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
+ val |= channel;
+ tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
+
+ /* Disable amplifier */
+ val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+ val &= ~channel;
+ tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+ }
+}
+
+int tpa6130a2_stereo_enable(struct snd_soc_codec *codec, int enable)
+{
+ int ret = 0;
+ if (enable) {
+ ret = tpa6130a2_power(1);
+ if (ret < 0)
+ return ret;
+ tpa6130a2_channel_enable(TPA6130A2_HP_EN_R | TPA6130A2_HP_EN_L,
+ 1);
+ } else {
+ tpa6130a2_channel_enable(TPA6130A2_HP_EN_R | TPA6130A2_HP_EN_L,
+ 0);
+ ret = tpa6130a2_power(0);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tpa6130a2_stereo_enable);
+
+int tpa6130a2_add_controls(struct snd_soc_codec *codec)
+{
+ struct tpa6130a2_data *data;
+
+ if (tpa6130a2_client == NULL)
+ return -ENODEV;
+
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ if (data->id == TPA6140A2)
+ return snd_soc_add_codec_controls(codec, tpa6140a2_controls,
+ ARRAY_SIZE(tpa6140a2_controls));
+ else
+ return snd_soc_add_codec_controls(codec, tpa6130a2_controls,
+ ARRAY_SIZE(tpa6130a2_controls));
+}
+EXPORT_SYMBOL_GPL(tpa6130a2_add_controls);
+
+static int tpa6130a2_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev;
+ struct tpa6130a2_data *data;
+ struct tpa6130a2_platform_data *pdata = client->dev.platform_data;
+ struct device_node *np = client->dev.of_node;
+ const char *regulator;
+ int ret;
+
+ dev = &client->dev;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (pdata) {
+ data->power_gpio = pdata->power_gpio;
+ } else if (np) {
+ data->power_gpio = of_get_named_gpio(np, "power-gpio", 0);
+ } else {
+ dev_err(dev, "Platform data not set\n");
+ dump_stack();
+ return -ENODEV;
+ }
+
+ tpa6130a2_client = client;
+
+ i2c_set_clientdata(tpa6130a2_client, data);
+
+ data->id = id->driver_data;
+
+ mutex_init(&data->mutex);
+
+ /* Set default register values */
+ data->regs[TPA6130A2_REG_CONTROL] = TPA6130A2_SWS;
+ data->regs[TPA6130A2_REG_VOL_MUTE] = TPA6130A2_MUTE_R |
+ TPA6130A2_MUTE_L;
+
+ if (data->power_gpio >= 0) {
+ ret = devm_gpio_request(dev, data->power_gpio,
+ "tpa6130a2 enable");
+ if (ret < 0) {
+ dev_err(dev, "Failed to request power GPIO (%d)\n",
+ data->power_gpio);
+ goto err_gpio;
+ }
+ gpio_direction_output(data->power_gpio, 0);
+ }
+
+ switch (data->id) {
+ default:
+ dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n",
+ data->id);
+ case TPA6130A2:
+ regulator = "Vdd";
+ break;
+ case TPA6140A2:
+ regulator = "AVdd";
+ break;
+ }
+
+ data->supply = devm_regulator_get(dev, regulator);
+ if (IS_ERR(data->supply)) {
+ ret = PTR_ERR(data->supply);
+ dev_err(dev, "Failed to request supply: %d\n", ret);
+ goto err_gpio;
+ }
+
+ ret = tpa6130a2_power(1);
+ if (ret != 0)
+ goto err_gpio;
+
+
+ /* Read version */
+ ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) &
+ TPA6130A2_VERSION_MASK;
+ if ((ret != 1) && (ret != 2))
+ dev_warn(dev, "UNTESTED version detected (%d)\n", ret);
+
+ /* Disable the chip */
+ ret = tpa6130a2_power(0);
+ if (ret != 0)
+ goto err_gpio;
+
+ return 0;
+
+err_gpio:
+ tpa6130a2_client = NULL;
+
+ return ret;
+}
+
+static int tpa6130a2_remove(struct i2c_client *client)
+{
+ tpa6130a2_power(0);
+ tpa6130a2_client = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id tpa6130a2_id[] = {
+ { "tpa6130a2", TPA6130A2 },
+ { "tpa6140a2", TPA6140A2 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tpa6130a2_id);
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id tpa6130a2_of_match[] = {
+ { .compatible = "ti,tpa6130a2", },
+ { .compatible = "ti,tpa6140a2" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tpa6130a2_of_match);
+#endif
+
+static struct i2c_driver tpa6130a2_i2c_driver = {
+ .driver = {
+ .name = "tpa6130a2",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tpa6130a2_of_match),
+ },
+ .probe = tpa6130a2_probe,
+ .remove = tpa6130a2_remove,
+ .id_table = tpa6130a2_id,
+};
+
+module_i2c_driver(tpa6130a2_i2c_driver);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/tpa6130a2.h b/kernel/sound/soc/codecs/tpa6130a2.h
new file mode 100644
index 000000000..417444020
--- /dev/null
+++ b/kernel/sound/soc/codecs/tpa6130a2.h
@@ -0,0 +1,62 @@
+/*
+ * ALSA SoC TPA6130A2 amplifier driver
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@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
+ *
+ */
+
+#ifndef __TPA6130A2_H__
+#define __TPA6130A2_H__
+
+/* Register addresses */
+#define TPA6130A2_REG_CONTROL 0x01
+#define TPA6130A2_REG_VOL_MUTE 0x02
+#define TPA6130A2_REG_OUT_IMPEDANCE 0x03
+#define TPA6130A2_REG_VERSION 0x04
+
+#define TPA6130A2_CACHEREGNUM (TPA6130A2_REG_VERSION + 1)
+
+/* Register bits */
+/* TPA6130A2_REG_CONTROL (0x01) */
+#define TPA6130A2_SWS (0x01 << 0)
+#define TPA6130A2_TERMAL (0x01 << 1)
+#define TPA6130A2_MODE(x) (x << 4)
+#define TPA6130A2_MODE_STEREO (0x00)
+#define TPA6130A2_MODE_DUAL_MONO (0x01)
+#define TPA6130A2_MODE_BRIDGE (0x02)
+#define TPA6130A2_MODE_MASK (0x03)
+#define TPA6130A2_HP_EN_R (0x01 << 6)
+#define TPA6130A2_HP_EN_L (0x01 << 7)
+
+/* TPA6130A2_REG_VOL_MUTE (0x02) */
+#define TPA6130A2_VOLUME(x) ((x & 0x3f) << 0)
+#define TPA6130A2_MUTE_R (0x01 << 6)
+#define TPA6130A2_MUTE_L (0x01 << 7)
+
+/* TPA6130A2_REG_OUT_IMPEDANCE (0x03) */
+#define TPA6130A2_HIZ_R (0x01 << 0)
+#define TPA6130A2_HIZ_L (0x01 << 1)
+
+/* TPA6130A2_REG_VERSION (0x04) */
+#define TPA6130A2_VERSION_MASK (0x0f)
+
+extern int tpa6130a2_add_controls(struct snd_soc_codec *codec);
+extern int tpa6130a2_stereo_enable(struct snd_soc_codec *codec, int enable);
+
+#endif /* __TPA6130A2_H__ */
diff --git a/kernel/sound/soc/codecs/ts3a227e.c b/kernel/sound/soc/codecs/ts3a227e.c
new file mode 100644
index 000000000..9fd80ac18
--- /dev/null
+++ b/kernel/sound/soc/codecs/ts3a227e.c
@@ -0,0 +1,349 @@
+/*
+ * TS3A227E Autonomous Audio Accessory Detection and Configuration Switch
+ *
+ * Copyright (C) 2014 Google, 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.
+ */
+
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+
+#include "ts3a227e.h"
+
+struct ts3a227e {
+ struct regmap *regmap;
+ struct snd_soc_jack *jack;
+ bool plugged;
+ bool mic_present;
+ unsigned int buttons_held;
+};
+
+/* Button values to be reported on the jack */
+static const int ts3a227e_buttons[] = {
+ SND_JACK_BTN_0,
+ SND_JACK_BTN_1,
+ SND_JACK_BTN_2,
+ SND_JACK_BTN_3,
+};
+
+#define TS3A227E_NUM_BUTTONS 4
+#define TS3A227E_JACK_MASK (SND_JACK_HEADPHONE | \
+ SND_JACK_MICROPHONE | \
+ SND_JACK_BTN_0 | \
+ SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | \
+ SND_JACK_BTN_3)
+
+/* TS3A227E registers */
+#define TS3A227E_REG_DEVICE_ID 0x00
+#define TS3A227E_REG_INTERRUPT 0x01
+#define TS3A227E_REG_KP_INTERRUPT 0x02
+#define TS3A227E_REG_INTERRUPT_DISABLE 0x03
+#define TS3A227E_REG_SETTING_1 0x04
+#define TS3A227E_REG_SETTING_2 0x05
+#define TS3A227E_REG_SETTING_3 0x06
+#define TS3A227E_REG_SWITCH_CONTROL_1 0x07
+#define TS3A227E_REG_SWITCH_CONTROL_2 0x08
+#define TS3A227E_REG_SWITCH_STATUS_1 0x09
+#define TS3A227E_REG_SWITCH_STATUS_2 0x0a
+#define TS3A227E_REG_ACCESSORY_STATUS 0x0b
+#define TS3A227E_REG_ADC_OUTPUT 0x0c
+#define TS3A227E_REG_KP_THRESHOLD_1 0x0d
+#define TS3A227E_REG_KP_THRESHOLD_2 0x0e
+#define TS3A227E_REG_KP_THRESHOLD_3 0x0f
+
+/* TS3A227E_REG_INTERRUPT 0x01 */
+#define INS_REM_EVENT 0x01
+#define DETECTION_COMPLETE_EVENT 0x02
+
+/* TS3A227E_REG_KP_INTERRUPT 0x02 */
+#define PRESS_MASK(idx) (0x01 << (2 * (idx)))
+#define RELEASE_MASK(idx) (0x02 << (2 * (idx)))
+
+/* TS3A227E_REG_INTERRUPT_DISABLE 0x03 */
+#define INS_REM_INT_DISABLE 0x01
+#define DETECTION_COMPLETE_INT_DISABLE 0x02
+#define ADC_COMPLETE_INT_DISABLE 0x04
+#define INTB_DISABLE 0x08
+
+/* TS3A227E_REG_SETTING_2 0x05 */
+#define KP_ENABLE 0x04
+
+/* TS3A227E_REG_SETTING_3 0x06 */
+#define MICBIAS_SETTING_SFT (3)
+#define MICBIAS_SETTING_MASK (0x7 << MICBIAS_SETTING_SFT)
+
+/* TS3A227E_REG_ACCESSORY_STATUS 0x0b */
+#define TYPE_3_POLE 0x01
+#define TYPE_4_POLE_OMTP 0x02
+#define TYPE_4_POLE_STANDARD 0x04
+#define JACK_INSERTED 0x08
+#define EITHER_MIC_MASK (TYPE_4_POLE_OMTP | TYPE_4_POLE_STANDARD)
+
+static const struct reg_default ts3a227e_reg_defaults[] = {
+ { TS3A227E_REG_DEVICE_ID, 0x10 },
+ { TS3A227E_REG_INTERRUPT, 0x00 },
+ { TS3A227E_REG_KP_INTERRUPT, 0x00 },
+ { TS3A227E_REG_INTERRUPT_DISABLE, 0x08 },
+ { TS3A227E_REG_SETTING_1, 0x23 },
+ { TS3A227E_REG_SETTING_2, 0x00 },
+ { TS3A227E_REG_SETTING_3, 0x0e },
+ { TS3A227E_REG_SWITCH_CONTROL_1, 0x00 },
+ { TS3A227E_REG_SWITCH_CONTROL_2, 0x00 },
+ { TS3A227E_REG_SWITCH_STATUS_1, 0x0c },
+ { TS3A227E_REG_SWITCH_STATUS_2, 0x00 },
+ { TS3A227E_REG_ACCESSORY_STATUS, 0x00 },
+ { TS3A227E_REG_ADC_OUTPUT, 0x00 },
+ { TS3A227E_REG_KP_THRESHOLD_1, 0x20 },
+ { TS3A227E_REG_KP_THRESHOLD_2, 0x40 },
+ { TS3A227E_REG_KP_THRESHOLD_3, 0x68 },
+};
+
+static bool ts3a227e_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TS3A227E_REG_DEVICE_ID ... TS3A227E_REG_KP_THRESHOLD_3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool ts3a227e_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TS3A227E_REG_INTERRUPT_DISABLE ... TS3A227E_REG_SWITCH_CONTROL_2:
+ case TS3A227E_REG_KP_THRESHOLD_1 ... TS3A227E_REG_KP_THRESHOLD_3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool ts3a227e_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TS3A227E_REG_INTERRUPT ... TS3A227E_REG_INTERRUPT_DISABLE:
+ case TS3A227E_REG_SETTING_2:
+ case TS3A227E_REG_SWITCH_STATUS_1 ... TS3A227E_REG_ADC_OUTPUT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void ts3a227e_jack_report(struct ts3a227e *ts3a227e)
+{
+ unsigned int i;
+ int report = 0;
+
+ if (!ts3a227e->jack)
+ return;
+
+ if (ts3a227e->plugged)
+ report = SND_JACK_HEADPHONE;
+ if (ts3a227e->mic_present)
+ report |= SND_JACK_MICROPHONE;
+ for (i = 0; i < TS3A227E_NUM_BUTTONS; i++) {
+ if (ts3a227e->buttons_held & (1 << i))
+ report |= ts3a227e_buttons[i];
+ }
+ snd_soc_jack_report(ts3a227e->jack, report, TS3A227E_JACK_MASK);
+}
+
+static void ts3a227e_new_jack_state(struct ts3a227e *ts3a227e, unsigned acc_reg)
+{
+ bool plugged, mic_present;
+
+ plugged = !!(acc_reg & JACK_INSERTED);
+ mic_present = plugged && !!(acc_reg & EITHER_MIC_MASK);
+
+ ts3a227e->plugged = plugged;
+
+ if (mic_present != ts3a227e->mic_present) {
+ ts3a227e->mic_present = mic_present;
+ ts3a227e->buttons_held = 0;
+ if (mic_present) {
+ /* Enable key press detection. */
+ regmap_update_bits(ts3a227e->regmap,
+ TS3A227E_REG_SETTING_2,
+ KP_ENABLE, KP_ENABLE);
+ }
+ }
+}
+
+static irqreturn_t ts3a227e_interrupt(int irq, void *data)
+{
+ struct ts3a227e *ts3a227e = (struct ts3a227e *)data;
+ struct regmap *regmap = ts3a227e->regmap;
+ unsigned int int_reg, kp_int_reg, acc_reg, i;
+
+ /* Check for plug/unplug. */
+ regmap_read(regmap, TS3A227E_REG_INTERRUPT, &int_reg);
+ if (int_reg & (DETECTION_COMPLETE_EVENT | INS_REM_EVENT)) {
+ regmap_read(regmap, TS3A227E_REG_ACCESSORY_STATUS, &acc_reg);
+ ts3a227e_new_jack_state(ts3a227e, acc_reg);
+ }
+
+ /* Report any key events. */
+ regmap_read(regmap, TS3A227E_REG_KP_INTERRUPT, &kp_int_reg);
+ for (i = 0; i < TS3A227E_NUM_BUTTONS; i++) {
+ if (kp_int_reg & PRESS_MASK(i))
+ ts3a227e->buttons_held |= (1 << i);
+ if (kp_int_reg & RELEASE_MASK(i))
+ ts3a227e->buttons_held &= ~(1 << i);
+ }
+
+ ts3a227e_jack_report(ts3a227e);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ts3a227e_enable_jack_detect - Specify a jack for event reporting
+ *
+ * @component: component to register the jack with
+ * @jack: jack to use to report headset and button events on
+ *
+ * After this function has been called the headset insert/remove and button
+ * events 0-3 will be routed to the given jack. Jack can be null to stop
+ * reporting.
+ */
+int ts3a227e_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct ts3a227e *ts3a227e = snd_soc_component_get_drvdata(component);
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ts3a227e->jack = jack;
+ ts3a227e_jack_report(ts3a227e);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ts3a227e_enable_jack_detect);
+
+static struct snd_soc_component_driver ts3a227e_soc_driver;
+
+static const struct regmap_config ts3a227e_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 8,
+
+ .max_register = TS3A227E_REG_KP_THRESHOLD_3,
+ .readable_reg = ts3a227e_readable_reg,
+ .writeable_reg = ts3a227e_writeable_reg,
+ .volatile_reg = ts3a227e_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = ts3a227e_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ts3a227e_reg_defaults),
+};
+
+static int ts3a227e_parse_dt(struct ts3a227e *ts3a227e, struct device_node *np)
+{
+ u32 micbias;
+ int err;
+
+ err = of_property_read_u32(np, "ti,micbias", &micbias);
+ if (!err) {
+ regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_SETTING_3,
+ MICBIAS_SETTING_MASK,
+ (micbias & 0x07) << MICBIAS_SETTING_SFT);
+ }
+
+ return 0;
+}
+
+static int ts3a227e_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ts3a227e *ts3a227e;
+ struct device *dev = &i2c->dev;
+ int ret;
+ unsigned int acc_reg;
+
+ ts3a227e = devm_kzalloc(&i2c->dev, sizeof(*ts3a227e), GFP_KERNEL);
+ if (ts3a227e == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, ts3a227e);
+
+ ts3a227e->regmap = devm_regmap_init_i2c(i2c, &ts3a227e_regmap_config);
+ if (IS_ERR(ts3a227e->regmap))
+ return PTR_ERR(ts3a227e->regmap);
+
+ if (dev->of_node) {
+ ret = ts3a227e_parse_dt(ts3a227e, dev->of_node);
+ if (ret) {
+ dev_err(dev, "Failed to parse device tree: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = devm_request_threaded_irq(dev, i2c->irq, NULL, ts3a227e_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "TS3A227E", ts3a227e);
+ if (ret) {
+ dev_err(dev, "Cannot request irq %d (%d)\n", i2c->irq, ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev, &ts3a227e_soc_driver,
+ NULL, 0);
+ if (ret)
+ return ret;
+
+ /* Enable interrupts except for ADC complete. */
+ regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_INTERRUPT_DISABLE,
+ INTB_DISABLE | ADC_COMPLETE_INT_DISABLE,
+ ADC_COMPLETE_INT_DISABLE);
+
+ /* Read jack status because chip might not trigger interrupt at boot. */
+ regmap_read(ts3a227e->regmap, TS3A227E_REG_ACCESSORY_STATUS, &acc_reg);
+ ts3a227e_new_jack_state(ts3a227e, acc_reg);
+ ts3a227e_jack_report(ts3a227e);
+
+ return 0;
+}
+
+static const struct i2c_device_id ts3a227e_i2c_ids[] = {
+ { "ts3a227e", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ts3a227e_i2c_ids);
+
+static const struct of_device_id ts3a227e_of_match[] = {
+ { .compatible = "ti,ts3a227e", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ts3a227e_of_match);
+
+static struct i2c_driver ts3a227e_driver = {
+ .driver = {
+ .name = "ts3a227e",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ts3a227e_of_match),
+ },
+ .probe = ts3a227e_i2c_probe,
+ .id_table = ts3a227e_i2c_ids,
+};
+module_i2c_driver(ts3a227e_driver);
+
+MODULE_DESCRIPTION("ASoC ts3a227e driver");
+MODULE_AUTHOR("Dylan Reid <dgreid@chromium.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/ts3a227e.h b/kernel/sound/soc/codecs/ts3a227e.h
new file mode 100644
index 000000000..e2acf9c5b
--- /dev/null
+++ b/kernel/sound/soc/codecs/ts3a227e.h
@@ -0,0 +1,17 @@
+/*
+ * TS3A227E Autonous Audio Accessory Detection and Configureation Switch
+ *
+ * Copyright (C) 2014 Google, 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 _TS3A227E_H
+#define _TS3A227E_H
+
+int ts3a227e_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack);
+
+#endif
diff --git a/kernel/sound/soc/codecs/twl4030.c b/kernel/sound/soc/codecs/twl4030.c
new file mode 100644
index 000000000..d04693e9c
--- /dev/null
+++ b/kernel/sound/soc/codecs/twl4030.c
@@ -0,0 +1,2241 @@
+/*
+ * ALSA SoC TWL4030 codec driver
+ *
+ * Author: Steve Sakoman, <steve@sakoman.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 <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/of.h>
+#include <linux/of_gpio.h>
+#include <linux/i2c/twl.h>
+#include <linux/slab.h>
+#include <linux/gpio.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>
+
+/* Register descriptions are here */
+#include <linux/mfd/twl4030-audio.h>
+
+/* TWL4030 PMBR1 Register */
+#define TWL4030_PMBR1_REG 0x0D
+/* TWL4030 PMBR1 Register GPIO6 mux bits */
+#define TWL4030_GPIO6_PWM0_MUTE(value) ((value & 0x03) << 2)
+
+#define TWL4030_CACHEREGNUM (TWL4030_REG_MISC_SET_2 + 1)
+
+/* codec private data */
+struct twl4030_priv {
+ unsigned int codec_powered;
+
+ /* reference counts of AIF/APLL users */
+ unsigned int apll_enabled;
+
+ struct snd_pcm_substream *master_substream;
+ struct snd_pcm_substream *slave_substream;
+
+ unsigned int configured;
+ unsigned int rate;
+ unsigned int sample_bits;
+ unsigned int channels;
+
+ unsigned int sysclk;
+
+ /* Output (with associated amp) states */
+ u8 hsl_enabled, hsr_enabled;
+ u8 earpiece_enabled;
+ u8 predrivel_enabled, predriver_enabled;
+ u8 carkitl_enabled, carkitr_enabled;
+ u8 ctl_cache[TWL4030_REG_PRECKR_CTL - TWL4030_REG_EAR_CTL + 1];
+
+ struct twl4030_codec_data *pdata;
+};
+
+static void tw4030_init_ctl_cache(struct twl4030_priv *twl4030)
+{
+ int i;
+ u8 byte;
+
+ for (i = TWL4030_REG_EAR_CTL; i <= TWL4030_REG_PRECKR_CTL; i++) {
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, i);
+ twl4030->ctl_cache[i - TWL4030_REG_EAR_CTL] = byte;
+ }
+}
+
+static unsigned int twl4030_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ u8 value = 0;
+
+ if (reg >= TWL4030_CACHEREGNUM)
+ return -EIO;
+
+ switch (reg) {
+ case TWL4030_REG_EAR_CTL:
+ case TWL4030_REG_PREDL_CTL:
+ case TWL4030_REG_PREDR_CTL:
+ case TWL4030_REG_PRECKL_CTL:
+ case TWL4030_REG_PRECKR_CTL:
+ case TWL4030_REG_HS_GAIN_SET:
+ value = twl4030->ctl_cache[reg - TWL4030_REG_EAR_CTL];
+ break;
+ default:
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &value, reg);
+ break;
+ }
+
+ return value;
+}
+
+static bool twl4030_can_write_to_chip(struct twl4030_priv *twl4030,
+ unsigned int reg)
+{
+ bool write_to_reg = false;
+
+ /* Decide if the given register can be written */
+ switch (reg) {
+ case TWL4030_REG_EAR_CTL:
+ if (twl4030->earpiece_enabled)
+ write_to_reg = true;
+ break;
+ case TWL4030_REG_PREDL_CTL:
+ if (twl4030->predrivel_enabled)
+ write_to_reg = true;
+ break;
+ case TWL4030_REG_PREDR_CTL:
+ if (twl4030->predriver_enabled)
+ write_to_reg = true;
+ break;
+ case TWL4030_REG_PRECKL_CTL:
+ if (twl4030->carkitl_enabled)
+ write_to_reg = true;
+ break;
+ case TWL4030_REG_PRECKR_CTL:
+ if (twl4030->carkitr_enabled)
+ write_to_reg = true;
+ break;
+ case TWL4030_REG_HS_GAIN_SET:
+ if (twl4030->hsl_enabled || twl4030->hsr_enabled)
+ write_to_reg = true;
+ break;
+ default:
+ /* All other register can be written */
+ write_to_reg = true;
+ break;
+ }
+
+ return write_to_reg;
+}
+
+static int twl4030_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+
+ /* Update the ctl cache */
+ switch (reg) {
+ case TWL4030_REG_EAR_CTL:
+ case TWL4030_REG_PREDL_CTL:
+ case TWL4030_REG_PREDR_CTL:
+ case TWL4030_REG_PRECKL_CTL:
+ case TWL4030_REG_PRECKR_CTL:
+ case TWL4030_REG_HS_GAIN_SET:
+ twl4030->ctl_cache[reg - TWL4030_REG_EAR_CTL] = value;
+ break;
+ default:
+ break;
+ }
+
+ if (twl4030_can_write_to_chip(twl4030, reg))
+ return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
+
+ return 0;
+}
+
+static inline void twl4030_wait_ms(int time)
+{
+ if (time < 60) {
+ time *= 1000;
+ usleep_range(time, time + 500);
+ } else {
+ msleep(time);
+ }
+}
+
+static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
+{
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ int mode;
+
+ if (enable == twl4030->codec_powered)
+ return;
+
+ if (enable)
+ mode = twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER);
+ else
+ mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
+
+ if (mode >= 0)
+ twl4030->codec_powered = enable;
+
+ /* REVISIT: this delay is present in TI sample drivers */
+ /* but there seems to be no TRM requirement for it */
+ udelay(10);
+}
+
+static void twl4030_setup_pdata_of(struct twl4030_codec_data *pdata,
+ struct device_node *node)
+{
+ int value;
+
+ of_property_read_u32(node, "ti,digimic_delay",
+ &pdata->digimic_delay);
+ of_property_read_u32(node, "ti,ramp_delay_value",
+ &pdata->ramp_delay_value);
+ of_property_read_u32(node, "ti,offset_cncl_path",
+ &pdata->offset_cncl_path);
+ if (!of_property_read_u32(node, "ti,hs_extmute", &value))
+ pdata->hs_extmute = value;
+
+ pdata->hs_extmute_gpio = of_get_named_gpio(node,
+ "ti,hs_extmute_gpio", 0);
+ if (gpio_is_valid(pdata->hs_extmute_gpio))
+ pdata->hs_extmute = 1;
+}
+
+static struct twl4030_codec_data *twl4030_get_pdata(struct snd_soc_codec *codec)
+{
+ struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev);
+ struct device_node *twl4030_codec_node = NULL;
+
+ twl4030_codec_node = of_find_node_by_name(codec->dev->parent->of_node,
+ "codec");
+
+ if (!pdata && twl4030_codec_node) {
+ pdata = devm_kzalloc(codec->dev,
+ sizeof(struct twl4030_codec_data),
+ GFP_KERNEL);
+ if (!pdata) {
+ dev_err(codec->dev, "Can not allocate memory\n");
+ return NULL;
+ }
+ twl4030_setup_pdata_of(pdata, twl4030_codec_node);
+ }
+
+ return pdata;
+}
+
+static void twl4030_init_chip(struct snd_soc_codec *codec)
+{
+ struct twl4030_codec_data *pdata;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ u8 reg, byte;
+ int i = 0;
+
+ pdata = twl4030_get_pdata(codec);
+
+ if (pdata && pdata->hs_extmute) {
+ if (gpio_is_valid(pdata->hs_extmute_gpio)) {
+ int ret;
+
+ if (!pdata->hs_extmute_gpio)
+ dev_warn(codec->dev,
+ "Extmute GPIO is 0 is this correct?\n");
+
+ ret = gpio_request_one(pdata->hs_extmute_gpio,
+ GPIOF_OUT_INIT_LOW,
+ "hs_extmute");
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to get hs_extmute GPIO\n");
+ pdata->hs_extmute_gpio = -1;
+ }
+ } else {
+ u8 pin_mux;
+
+ /* Set TWL4030 GPIO6 as EXTMUTE signal */
+ twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux,
+ TWL4030_PMBR1_REG);
+ pin_mux &= ~TWL4030_GPIO6_PWM0_MUTE(0x03);
+ pin_mux |= TWL4030_GPIO6_PWM0_MUTE(0x02);
+ twl_i2c_write_u8(TWL4030_MODULE_INTBR, pin_mux,
+ TWL4030_PMBR1_REG);
+ }
+ }
+
+ /* Initialize the local ctl register cache */
+ tw4030_init_ctl_cache(twl4030);
+
+ /* anti-pop when changing analog gain */
+ reg = twl4030_read(codec, TWL4030_REG_MISC_SET_1);
+ twl4030_write(codec, TWL4030_REG_MISC_SET_1,
+ reg | TWL4030_SMOOTH_ANAVOL_EN);
+
+ twl4030_write(codec, TWL4030_REG_OPTION,
+ TWL4030_ATXL1_EN | TWL4030_ATXR1_EN |
+ TWL4030_ARXL2_EN | TWL4030_ARXR2_EN);
+
+ /* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */
+ twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32);
+
+ /* Machine dependent setup */
+ if (!pdata)
+ return;
+
+ twl4030->pdata = pdata;
+
+ reg = twl4030_read(codec, TWL4030_REG_HS_POPN_SET);
+ reg &= ~TWL4030_RAMP_DELAY;
+ reg |= (pdata->ramp_delay_value << 2);
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, reg);
+
+ /* initiate offset cancellation */
+ twl4030_codec_enable(codec, 1);
+
+ reg = twl4030_read(codec, TWL4030_REG_ANAMICL);
+ reg &= ~TWL4030_OFFSET_CNCL_SEL;
+ reg |= pdata->offset_cncl_path;
+ twl4030_write(codec, TWL4030_REG_ANAMICL,
+ reg | TWL4030_CNCL_OFFSET_START);
+
+ /*
+ * Wait for offset cancellation to complete.
+ * Since this takes a while, do not slam the i2c.
+ * Start polling the status after ~20ms.
+ */
+ msleep(20);
+ do {
+ usleep_range(1000, 2000);
+ twl_set_regcache_bypass(TWL4030_MODULE_AUDIO_VOICE, true);
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
+ TWL4030_REG_ANAMICL);
+ twl_set_regcache_bypass(TWL4030_MODULE_AUDIO_VOICE, false);
+ } while ((i++ < 100) &&
+ ((byte & TWL4030_CNCL_OFFSET_START) ==
+ TWL4030_CNCL_OFFSET_START));
+
+ twl4030_codec_enable(codec, 0);
+}
+
+static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
+{
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+
+ if (enable) {
+ twl4030->apll_enabled++;
+ if (twl4030->apll_enabled == 1)
+ twl4030_audio_enable_resource(
+ TWL4030_AUDIO_RES_APLL);
+ } else {
+ twl4030->apll_enabled--;
+ if (!twl4030->apll_enabled)
+ twl4030_audio_disable_resource(
+ TWL4030_AUDIO_RES_APLL);
+ }
+}
+
+/* Earpiece */
+static const struct snd_kcontrol_new twl4030_dapm_earpiece_controls[] = {
+ SOC_DAPM_SINGLE("Voice", TWL4030_REG_EAR_CTL, 0, 1, 0),
+ SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_EAR_CTL, 1, 1, 0),
+ SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_EAR_CTL, 2, 1, 0),
+ SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_EAR_CTL, 3, 1, 0),
+};
+
+/* PreDrive Left */
+static const struct snd_kcontrol_new twl4030_dapm_predrivel_controls[] = {
+ SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDL_CTL, 0, 1, 0),
+ SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PREDL_CTL, 1, 1, 0),
+ SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDL_CTL, 2, 1, 0),
+ SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDL_CTL, 3, 1, 0),
+};
+
+/* PreDrive Right */
+static const struct snd_kcontrol_new twl4030_dapm_predriver_controls[] = {
+ SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDR_CTL, 0, 1, 0),
+ SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PREDR_CTL, 1, 1, 0),
+ SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDR_CTL, 2, 1, 0),
+ SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDR_CTL, 3, 1, 0),
+};
+
+/* Headset Left */
+static const struct snd_kcontrol_new twl4030_dapm_hsol_controls[] = {
+ SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 0, 1, 0),
+ SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_HS_SEL, 1, 1, 0),
+ SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_HS_SEL, 2, 1, 0),
+};
+
+/* Headset Right */
+static const struct snd_kcontrol_new twl4030_dapm_hsor_controls[] = {
+ SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 3, 1, 0),
+ SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_HS_SEL, 4, 1, 0),
+ SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_HS_SEL, 5, 1, 0),
+};
+
+/* Carkit Left */
+static const struct snd_kcontrol_new twl4030_dapm_carkitl_controls[] = {
+ SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKL_CTL, 0, 1, 0),
+ SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PRECKL_CTL, 1, 1, 0),
+ SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PRECKL_CTL, 2, 1, 0),
+};
+
+/* Carkit Right */
+static const struct snd_kcontrol_new twl4030_dapm_carkitr_controls[] = {
+ SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKR_CTL, 0, 1, 0),
+ SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PRECKR_CTL, 1, 1, 0),
+ SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PRECKR_CTL, 2, 1, 0),
+};
+
+/* Handsfree Left */
+static const char *twl4030_handsfreel_texts[] =
+ {"Voice", "AudioL1", "AudioL2", "AudioR2"};
+
+static SOC_ENUM_SINGLE_DECL(twl4030_handsfreel_enum,
+ TWL4030_REG_HFL_CTL, 0,
+ twl4030_handsfreel_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_handsfreel_control =
+SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum);
+
+/* Handsfree Left virtual mute */
+static const struct snd_kcontrol_new twl4030_dapm_handsfreelmute_control =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+/* Handsfree Right */
+static const char *twl4030_handsfreer_texts[] =
+ {"Voice", "AudioR1", "AudioR2", "AudioL2"};
+
+static SOC_ENUM_SINGLE_DECL(twl4030_handsfreer_enum,
+ TWL4030_REG_HFR_CTL, 0,
+ twl4030_handsfreer_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control =
+SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum);
+
+/* Handsfree Right virtual mute */
+static const struct snd_kcontrol_new twl4030_dapm_handsfreermute_control =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+/* Vibra */
+/* Vibra audio path selection */
+static const char *twl4030_vibra_texts[] =
+ {"AudioL1", "AudioR1", "AudioL2", "AudioR2"};
+
+static SOC_ENUM_SINGLE_DECL(twl4030_vibra_enum,
+ TWL4030_REG_VIBRA_CTL, 2,
+ twl4030_vibra_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_vibra_control =
+SOC_DAPM_ENUM("Route", twl4030_vibra_enum);
+
+/* Vibra path selection: local vibrator (PWM) or audio driven */
+static const char *twl4030_vibrapath_texts[] =
+ {"Local vibrator", "Audio"};
+
+static SOC_ENUM_SINGLE_DECL(twl4030_vibrapath_enum,
+ TWL4030_REG_VIBRA_CTL, 4,
+ twl4030_vibrapath_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_vibrapath_control =
+SOC_DAPM_ENUM("Route", twl4030_vibrapath_enum);
+
+/* Left analog microphone selection */
+static const struct snd_kcontrol_new twl4030_dapm_analoglmic_controls[] = {
+ SOC_DAPM_SINGLE("Main Mic Capture Switch",
+ TWL4030_REG_ANAMICL, 0, 1, 0),
+ SOC_DAPM_SINGLE("Headset Mic Capture Switch",
+ TWL4030_REG_ANAMICL, 1, 1, 0),
+ SOC_DAPM_SINGLE("AUXL Capture Switch",
+ TWL4030_REG_ANAMICL, 2, 1, 0),
+ SOC_DAPM_SINGLE("Carkit Mic Capture Switch",
+ TWL4030_REG_ANAMICL, 3, 1, 0),
+};
+
+/* Right analog microphone selection */
+static const struct snd_kcontrol_new twl4030_dapm_analogrmic_controls[] = {
+ SOC_DAPM_SINGLE("Sub Mic Capture Switch", TWL4030_REG_ANAMICR, 0, 1, 0),
+ SOC_DAPM_SINGLE("AUXR Capture Switch", TWL4030_REG_ANAMICR, 2, 1, 0),
+};
+
+/* TX1 L/R Analog/Digital microphone selection */
+static const char *twl4030_micpathtx1_texts[] =
+ {"Analog", "Digimic0"};
+
+static SOC_ENUM_SINGLE_DECL(twl4030_micpathtx1_enum,
+ TWL4030_REG_ADCMICSEL, 0,
+ twl4030_micpathtx1_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_micpathtx1_control =
+SOC_DAPM_ENUM("Route", twl4030_micpathtx1_enum);
+
+/* TX2 L/R Analog/Digital microphone selection */
+static const char *twl4030_micpathtx2_texts[] =
+ {"Analog", "Digimic1"};
+
+static SOC_ENUM_SINGLE_DECL(twl4030_micpathtx2_enum,
+ TWL4030_REG_ADCMICSEL, 2,
+ twl4030_micpathtx2_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control =
+SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum);
+
+/* Analog bypass for AudioR1 */
+static const struct snd_kcontrol_new twl4030_dapm_abypassr1_control =
+ SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR1_APGA_CTL, 2, 1, 0);
+
+/* Analog bypass for AudioL1 */
+static const struct snd_kcontrol_new twl4030_dapm_abypassl1_control =
+ SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL1_APGA_CTL, 2, 1, 0);
+
+/* Analog bypass for AudioR2 */
+static const struct snd_kcontrol_new twl4030_dapm_abypassr2_control =
+ SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR2_APGA_CTL, 2, 1, 0);
+
+/* Analog bypass for AudioL2 */
+static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control =
+ SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0);
+
+/* Analog bypass for Voice */
+static const struct snd_kcontrol_new twl4030_dapm_abypassv_control =
+ SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0);
+
+/* Digital bypass gain, mute instead of -30dB */
+static const unsigned int twl4030_dapm_dbypass_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 1, TLV_DB_SCALE_ITEM(-3000, 600, 1),
+ 2, 3, TLV_DB_SCALE_ITEM(-2400, 0, 0),
+ 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0),
+};
+
+/* Digital bypass left (TX1L -> RX2L) */
+static const struct snd_kcontrol_new twl4030_dapm_dbypassl_control =
+ SOC_DAPM_SINGLE_TLV("Volume",
+ TWL4030_REG_ATX2ARXPGA, 3, 7, 0,
+ twl4030_dapm_dbypass_tlv);
+
+/* Digital bypass right (TX1R -> RX2R) */
+static const struct snd_kcontrol_new twl4030_dapm_dbypassr_control =
+ SOC_DAPM_SINGLE_TLV("Volume",
+ TWL4030_REG_ATX2ARXPGA, 0, 7, 0,
+ twl4030_dapm_dbypass_tlv);
+
+/*
+ * Voice Sidetone GAIN volume control:
+ * from -51 to -10 dB in 1 dB steps (mute instead of -51 dB)
+ */
+static DECLARE_TLV_DB_SCALE(twl4030_dapm_dbypassv_tlv, -5100, 100, 1);
+
+/* Digital bypass voice: sidetone (VUL -> VDL)*/
+static const struct snd_kcontrol_new twl4030_dapm_dbypassv_control =
+ SOC_DAPM_SINGLE_TLV("Volume",
+ TWL4030_REG_VSTPGA, 0, 0x29, 0,
+ twl4030_dapm_dbypassv_tlv);
+
+/*
+ * Output PGA builder:
+ * Handle the muting and unmuting of the given output (turning off the
+ * amplifier associated with the output pin)
+ * On mute bypass the reg_cache and write 0 to the register
+ * On unmute: restore the register content from the reg_cache
+ * Outputs handled in this way: Earpiece, PreDrivL/R, CarkitL/R
+ */
+#define TWL4030_OUTPUT_PGA(pin_name, reg, mask) \
+static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \
+ struct snd_kcontrol *kcontrol, int event) \
+{ \
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); \
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); \
+ \
+ switch (event) { \
+ case SND_SOC_DAPM_POST_PMU: \
+ twl4030->pin_name##_enabled = 1; \
+ twl4030_write(codec, reg, twl4030_read(codec, reg)); \
+ break; \
+ case SND_SOC_DAPM_POST_PMD: \
+ twl4030->pin_name##_enabled = 0; \
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 0, reg); \
+ break; \
+ } \
+ return 0; \
+}
+
+TWL4030_OUTPUT_PGA(earpiece, TWL4030_REG_EAR_CTL, TWL4030_EAR_GAIN);
+TWL4030_OUTPUT_PGA(predrivel, TWL4030_REG_PREDL_CTL, TWL4030_PREDL_GAIN);
+TWL4030_OUTPUT_PGA(predriver, TWL4030_REG_PREDR_CTL, TWL4030_PREDR_GAIN);
+TWL4030_OUTPUT_PGA(carkitl, TWL4030_REG_PRECKL_CTL, TWL4030_PRECKL_GAIN);
+TWL4030_OUTPUT_PGA(carkitr, TWL4030_REG_PRECKR_CTL, TWL4030_PRECKR_GAIN);
+
+static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp)
+{
+ unsigned char hs_ctl;
+
+ hs_ctl = twl4030_read(codec, reg);
+
+ if (ramp) {
+ /* HF ramp-up */
+ hs_ctl |= TWL4030_HF_CTL_REF_EN;
+ twl4030_write(codec, reg, hs_ctl);
+ udelay(10);
+ hs_ctl |= TWL4030_HF_CTL_RAMP_EN;
+ twl4030_write(codec, reg, hs_ctl);
+ udelay(40);
+ hs_ctl |= TWL4030_HF_CTL_LOOP_EN;
+ hs_ctl |= TWL4030_HF_CTL_HB_EN;
+ twl4030_write(codec, reg, hs_ctl);
+ } else {
+ /* HF ramp-down */
+ hs_ctl &= ~TWL4030_HF_CTL_LOOP_EN;
+ hs_ctl &= ~TWL4030_HF_CTL_HB_EN;
+ twl4030_write(codec, reg, hs_ctl);
+ hs_ctl &= ~TWL4030_HF_CTL_RAMP_EN;
+ twl4030_write(codec, reg, hs_ctl);
+ udelay(40);
+ hs_ctl &= ~TWL4030_HF_CTL_REF_EN;
+ twl4030_write(codec, reg, hs_ctl);
+ }
+}
+
+static int handsfreelpga_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_POST_PMU:
+ handsfree_ramp(codec, TWL4030_REG_HFL_CTL, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ handsfree_ramp(codec, TWL4030_REG_HFL_CTL, 0);
+ break;
+ }
+ return 0;
+}
+
+static int handsfreerpga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ handsfree_ramp(codec, TWL4030_REG_HFR_CTL, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ handsfree_ramp(codec, TWL4030_REG_HFR_CTL, 0);
+ break;
+ }
+ return 0;
+}
+
+static int vibramux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ twl4030_write(codec, TWL4030_REG_VIBRA_SET, 0xff);
+ return 0;
+}
+
+static int apll_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ twl4030_apll_enable(codec, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ twl4030_apll_enable(codec, 0);
+ break;
+ }
+ return 0;
+}
+
+static int aif_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);
+ u8 audio_if;
+
+ audio_if = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable AIF */
+ /* enable the PLL before we use it to clock the DAI */
+ twl4030_apll_enable(codec, 1);
+
+ twl4030_write(codec, TWL4030_REG_AUDIO_IF,
+ audio_if | TWL4030_AIF_EN);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* disable the DAI before we stop it's source PLL */
+ twl4030_write(codec, TWL4030_REG_AUDIO_IF,
+ audio_if & ~TWL4030_AIF_EN);
+ twl4030_apll_enable(codec, 0);
+ break;
+ }
+ return 0;
+}
+
+static void headset_ramp(struct snd_soc_codec *codec, int ramp)
+{
+ unsigned char hs_gain, hs_pop;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ struct twl4030_codec_data *pdata = twl4030->pdata;
+ /* Base values for ramp delay calculation: 2^19 - 2^26 */
+ unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304,
+ 8388608, 16777216, 33554432, 67108864};
+ unsigned int delay;
+
+ hs_gain = twl4030_read(codec, TWL4030_REG_HS_GAIN_SET);
+ hs_pop = twl4030_read(codec, TWL4030_REG_HS_POPN_SET);
+ delay = (ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] /
+ twl4030->sysclk) + 1;
+
+ /* Enable external mute control, this dramatically reduces
+ * the pop-noise */
+ if (pdata && pdata->hs_extmute) {
+ if (gpio_is_valid(pdata->hs_extmute_gpio)) {
+ gpio_set_value(pdata->hs_extmute_gpio, 1);
+ } else {
+ hs_pop |= TWL4030_EXTMUTE;
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ }
+ }
+
+ if (ramp) {
+ /* Headset ramp-up according to the TRM */
+ hs_pop |= TWL4030_VMID_EN;
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ /* Actually write to the register */
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain,
+ TWL4030_REG_HS_GAIN_SET);
+ hs_pop |= TWL4030_RAMP_EN;
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ /* Wait ramp delay time + 1, so the VMID can settle */
+ twl4030_wait_ms(delay);
+ } else {
+ /* Headset ramp-down _not_ according to
+ * the TRM, but in a way that it is working */
+ hs_pop &= ~TWL4030_RAMP_EN;
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ /* Wait ramp delay time + 1, so the VMID can settle */
+ twl4030_wait_ms(delay);
+ /* Bypass the reg_cache to mute the headset */
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain & (~0x0f),
+ TWL4030_REG_HS_GAIN_SET);
+
+ hs_pop &= ~TWL4030_VMID_EN;
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ }
+
+ /* Disable external mute */
+ if (pdata && pdata->hs_extmute) {
+ if (gpio_is_valid(pdata->hs_extmute_gpio)) {
+ gpio_set_value(pdata->hs_extmute_gpio, 0);
+ } else {
+ hs_pop &= ~TWL4030_EXTMUTE;
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ }
+ }
+}
+
+static int headsetlpga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Do the ramp-up only once */
+ if (!twl4030->hsr_enabled)
+ headset_ramp(codec, 1);
+
+ twl4030->hsl_enabled = 1;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Do the ramp-down only if both headsetL/R is disabled */
+ if (!twl4030->hsr_enabled)
+ headset_ramp(codec, 0);
+
+ twl4030->hsl_enabled = 0;
+ break;
+ }
+ return 0;
+}
+
+static int headsetrpga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Do the ramp-up only once */
+ if (!twl4030->hsl_enabled)
+ headset_ramp(codec, 1);
+
+ twl4030->hsr_enabled = 1;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Do the ramp-down only if both headsetL/R is disabled */
+ if (!twl4030->hsl_enabled)
+ headset_ramp(codec, 0);
+
+ twl4030->hsr_enabled = 0;
+ break;
+ }
+ return 0;
+}
+
+static int digimic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ struct twl4030_codec_data *pdata = twl4030->pdata;
+
+ if (pdata && pdata->digimic_delay)
+ twl4030_wait_ms(pdata->digimic_delay);
+ return 0;
+}
+
+/*
+ * Some of the gain controls in TWL (mostly those which are associated with
+ * the outputs) are implemented in an interesting way:
+ * 0x0 : Power down (mute)
+ * 0x1 : 6dB
+ * 0x2 : 0 dB
+ * 0x3 : -6 dB
+ * Inverting not going to help with these.
+ * Custom volsw and volsw_2r get/put functions to handle these gain bits.
+ */
+static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int mask = (1 << fls(max)) - 1;
+
+ ucontrol->value.integer.value[0] =
+ (snd_soc_read(codec, reg) >> shift) & mask;
+ if (ucontrol->value.integer.value[0])
+ ucontrol->value.integer.value[0] =
+ max + 1 - ucontrol->value.integer.value[0];
+
+ if (shift != rshift) {
+ ucontrol->value.integer.value[1] =
+ (snd_soc_read(codec, reg) >> rshift) & mask;
+ if (ucontrol->value.integer.value[1])
+ ucontrol->value.integer.value[1] =
+ max + 1 - ucontrol->value.integer.value[1];
+ }
+
+ return 0;
+}
+
+static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int mask = (1 << fls(max)) - 1;
+ unsigned short val, val2, val_mask;
+
+ val = (ucontrol->value.integer.value[0] & mask);
+
+ val_mask = mask << shift;
+ if (val)
+ val = max + 1 - val;
+ val = val << shift;
+ if (shift != rshift) {
+ val2 = (ucontrol->value.integer.value[1] & mask);
+ val_mask |= mask << rshift;
+ if (val2)
+ val2 = max + 1 - val2;
+ val |= val2 << rshift;
+ }
+ return snd_soc_update_bits(codec, reg, val_mask, val);
+}
+
+static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ int mask = (1<<fls(max))-1;
+
+ ucontrol->value.integer.value[0] =
+ (snd_soc_read(codec, reg) >> shift) & mask;
+ ucontrol->value.integer.value[1] =
+ (snd_soc_read(codec, reg2) >> shift) & mask;
+
+ if (ucontrol->value.integer.value[0])
+ ucontrol->value.integer.value[0] =
+ max + 1 - ucontrol->value.integer.value[0];
+ if (ucontrol->value.integer.value[1])
+ ucontrol->value.integer.value[1] =
+ max + 1 - ucontrol->value.integer.value[1];
+
+ return 0;
+}
+
+static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ int mask = (1 << fls(max)) - 1;
+ int err;
+ unsigned short val, val2, val_mask;
+
+ val_mask = mask << shift;
+ val = (ucontrol->value.integer.value[0] & mask);
+ val2 = (ucontrol->value.integer.value[1] & mask);
+
+ if (val)
+ val = max + 1 - val;
+ if (val2)
+ val2 = max + 1 - val2;
+
+ val = val << shift;
+ val2 = val2 << shift;
+
+ err = snd_soc_update_bits(codec, reg, val_mask, val);
+ if (err < 0)
+ return err;
+
+ err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+ return err;
+}
+
+/* Codec operation modes */
+static const char *twl4030_op_modes_texts[] = {
+ "Option 2 (voice/audio)", "Option 1 (audio)"
+};
+
+static SOC_ENUM_SINGLE_DECL(twl4030_op_modes_enum,
+ TWL4030_REG_CODEC_MODE, 0,
+ twl4030_op_modes_texts);
+
+static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+
+ if (twl4030->configured) {
+ dev_err(codec->dev,
+ "operation mode cannot be changed on-the-fly\n");
+ return -EBUSY;
+ }
+
+ return snd_soc_put_enum_double(kcontrol, ucontrol);
+}
+
+/*
+ * FGAIN volume control:
+ * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB)
+ */
+static DECLARE_TLV_DB_SCALE(digital_fine_tlv, -6300, 100, 1);
+
+/*
+ * CGAIN volume control:
+ * 0 dB to 12 dB in 6 dB steps
+ * value 2 and 3 means 12 dB
+ */
+static DECLARE_TLV_DB_SCALE(digital_coarse_tlv, 0, 600, 0);
+
+/*
+ * Voice Downlink GAIN volume control:
+ * from -37 to 12 dB in 1 dB steps (mute instead of -37 dB)
+ */
+static DECLARE_TLV_DB_SCALE(digital_voice_downlink_tlv, -3700, 100, 1);
+
+/*
+ * Analog playback gain
+ * -24 dB to 12 dB in 2 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(analog_tlv, -2400, 200, 0);
+
+/*
+ * Gain controls tied to outputs
+ * -6 dB to 6 dB in 6 dB steps (mute instead of -12)
+ */
+static DECLARE_TLV_DB_SCALE(output_tvl, -1200, 600, 1);
+
+/*
+ * Gain control for earpiece amplifier
+ * 0 dB to 12 dB in 6 dB steps (mute instead of -6)
+ */
+static DECLARE_TLV_DB_SCALE(output_ear_tvl, -600, 600, 1);
+
+/*
+ * Capture gain after the ADCs
+ * from 0 dB to 31 dB in 1 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0);
+
+/*
+ * Gain control for input amplifiers
+ * 0 dB to 30 dB in 6 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
+
+/* AVADC clock priority */
+static const char *twl4030_avadc_clk_priority_texts[] = {
+ "Voice high priority", "HiFi high priority"
+};
+
+static SOC_ENUM_SINGLE_DECL(twl4030_avadc_clk_priority_enum,
+ TWL4030_REG_AVADC_CTL, 2,
+ twl4030_avadc_clk_priority_texts);
+
+static const char *twl4030_rampdelay_texts[] = {
+ "27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms",
+ "437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms",
+ "3495/2581/1748 ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(twl4030_rampdelay_enum,
+ TWL4030_REG_HS_POPN_SET, 2,
+ twl4030_rampdelay_texts);
+
+/* Vibra H-bridge direction mode */
+static const char *twl4030_vibradirmode_texts[] = {
+ "Vibra H-bridge direction", "Audio data MSB",
+};
+
+static SOC_ENUM_SINGLE_DECL(twl4030_vibradirmode_enum,
+ TWL4030_REG_VIBRA_CTL, 5,
+ twl4030_vibradirmode_texts);
+
+/* Vibra H-bridge direction */
+static const char *twl4030_vibradir_texts[] = {
+ "Positive polarity", "Negative polarity",
+};
+
+static SOC_ENUM_SINGLE_DECL(twl4030_vibradir_enum,
+ TWL4030_REG_VIBRA_CTL, 1,
+ twl4030_vibradir_texts);
+
+/* Digimic Left and right swapping */
+static const char *twl4030_digimicswap_texts[] = {
+ "Not swapped", "Swapped",
+};
+
+static SOC_ENUM_SINGLE_DECL(twl4030_digimicswap_enum,
+ TWL4030_REG_MISC_SET_1, 0,
+ twl4030_digimicswap_texts);
+
+static const struct snd_kcontrol_new twl4030_snd_controls[] = {
+ /* Codec operation mode control */
+ SOC_ENUM_EXT("Codec Operation Mode", twl4030_op_modes_enum,
+ snd_soc_get_enum_double,
+ snd_soc_put_twl4030_opmode_enum_double),
+
+ /* Common playback gain controls */
+ SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume",
+ TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
+ 0, 0x3f, 0, digital_fine_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Digital Fine Playback Volume",
+ TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
+ 0, 0x3f, 0, digital_fine_tlv),
+
+ SOC_DOUBLE_R_TLV("DAC1 Digital Coarse Playback Volume",
+ TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
+ 6, 0x2, 0, digital_coarse_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Digital Coarse Playback Volume",
+ TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
+ 6, 0x2, 0, digital_coarse_tlv),
+
+ SOC_DOUBLE_R_TLV("DAC1 Analog Playback Volume",
+ TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL,
+ 3, 0x12, 1, analog_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Analog Playback Volume",
+ TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
+ 3, 0x12, 1, analog_tlv),
+ SOC_DOUBLE_R("DAC1 Analog Playback Switch",
+ TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL,
+ 1, 1, 0),
+ SOC_DOUBLE_R("DAC2 Analog Playback Switch",
+ TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
+ 1, 1, 0),
+
+ /* Common voice downlink gain controls */
+ SOC_SINGLE_TLV("DAC Voice Digital Downlink Volume",
+ TWL4030_REG_VRXPGA, 0, 0x31, 0, digital_voice_downlink_tlv),
+
+ SOC_SINGLE_TLV("DAC Voice Analog Downlink Volume",
+ TWL4030_REG_VDL_APGA_CTL, 3, 0x12, 1, analog_tlv),
+
+ SOC_SINGLE("DAC Voice Analog Downlink Switch",
+ TWL4030_REG_VDL_APGA_CTL, 1, 1, 0),
+
+ /* Separate output gain controls */
+ SOC_DOUBLE_R_EXT_TLV("PreDriv Playback Volume",
+ TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL,
+ 4, 3, 0, snd_soc_get_volsw_r2_twl4030,
+ snd_soc_put_volsw_r2_twl4030, output_tvl),
+
+ SOC_DOUBLE_EXT_TLV("Headset Playback Volume",
+ TWL4030_REG_HS_GAIN_SET, 0, 2, 3, 0, snd_soc_get_volsw_twl4030,
+ snd_soc_put_volsw_twl4030, output_tvl),
+
+ SOC_DOUBLE_R_EXT_TLV("Carkit Playback Volume",
+ TWL4030_REG_PRECKL_CTL, TWL4030_REG_PRECKR_CTL,
+ 4, 3, 0, snd_soc_get_volsw_r2_twl4030,
+ snd_soc_put_volsw_r2_twl4030, output_tvl),
+
+ SOC_SINGLE_EXT_TLV("Earpiece Playback Volume",
+ TWL4030_REG_EAR_CTL, 4, 3, 0, snd_soc_get_volsw_twl4030,
+ snd_soc_put_volsw_twl4030, output_ear_tvl),
+
+ /* Common capture gain controls */
+ SOC_DOUBLE_R_TLV("TX1 Digital Capture Volume",
+ TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA,
+ 0, 0x1f, 0, digital_capture_tlv),
+ SOC_DOUBLE_R_TLV("TX2 Digital Capture Volume",
+ TWL4030_REG_AVTXL2PGA, TWL4030_REG_AVTXR2PGA,
+ 0, 0x1f, 0, digital_capture_tlv),
+
+ SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN,
+ 0, 3, 5, 0, input_gain_tlv),
+
+ SOC_ENUM("AVADC Clock Priority", twl4030_avadc_clk_priority_enum),
+
+ SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum),
+
+ SOC_ENUM("Vibra H-bridge mode", twl4030_vibradirmode_enum),
+ SOC_ENUM("Vibra H-bridge direction", twl4030_vibradir_enum),
+
+ SOC_ENUM("Digimic LR Swap", twl4030_digimicswap_enum),
+};
+
+static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
+ /* Left channel inputs */
+ SND_SOC_DAPM_INPUT("MAINMIC"),
+ SND_SOC_DAPM_INPUT("HSMIC"),
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("CARKITMIC"),
+ /* Right channel inputs */
+ SND_SOC_DAPM_INPUT("SUBMIC"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+ /* Digital microphones (Stereo) */
+ SND_SOC_DAPM_INPUT("DIGIMIC0"),
+ SND_SOC_DAPM_INPUT("DIGIMIC1"),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("EARPIECE"),
+ SND_SOC_DAPM_OUTPUT("PREDRIVEL"),
+ SND_SOC_DAPM_OUTPUT("PREDRIVER"),
+ SND_SOC_DAPM_OUTPUT("HSOL"),
+ SND_SOC_DAPM_OUTPUT("HSOR"),
+ SND_SOC_DAPM_OUTPUT("CARKITL"),
+ SND_SOC_DAPM_OUTPUT("CARKITR"),
+ SND_SOC_DAPM_OUTPUT("HFL"),
+ SND_SOC_DAPM_OUTPUT("HFR"),
+ SND_SOC_DAPM_OUTPUT("VIBRA"),
+
+ /* AIF and APLL clocks for running DAIs (including loopback) */
+ SND_SOC_DAPM_OUTPUT("Virtual HiFi OUT"),
+ SND_SOC_DAPM_INPUT("Virtual HiFi IN"),
+ SND_SOC_DAPM_OUTPUT("Virtual Voice OUT"),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC Right1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC Left1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC Right2", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC Left2", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC Voice", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("VAIFIN", "Voice Playback", 0,
+ TWL4030_REG_VOICE_IF, 6, 0),
+
+ /* Analog bypasses */
+ SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassr1_control),
+ SND_SOC_DAPM_SWITCH("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassl1_control),
+ SND_SOC_DAPM_SWITCH("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassr2_control),
+ SND_SOC_DAPM_SWITCH("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassl2_control),
+ SND_SOC_DAPM_SWITCH("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassv_control),
+
+ /* Master analog loopback switch */
+ SND_SOC_DAPM_SUPPLY("FM Loop Enable", TWL4030_REG_MISC_SET_1, 5, 0,
+ NULL, 0),
+
+ /* Digital bypasses */
+ SND_SOC_DAPM_SWITCH("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_dbypassl_control),
+ SND_SOC_DAPM_SWITCH("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_dbypassr_control),
+ SND_SOC_DAPM_SWITCH("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_dbypassv_control),
+
+ /* Digital mixers, power control for the physical DACs */
+ SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer",
+ TWL4030_REG_AVDAC_CTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Digital L1 Playback Mixer",
+ TWL4030_REG_AVDAC_CTL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Digital R2 Playback Mixer",
+ TWL4030_REG_AVDAC_CTL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Digital L2 Playback Mixer",
+ TWL4030_REG_AVDAC_CTL, 3, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Digital Voice Playback Mixer",
+ TWL4030_REG_AVDAC_CTL, 4, 0, NULL, 0),
+
+ /* Analog mixers, power control for the physical PGAs */
+ SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer",
+ TWL4030_REG_ARXR1_APGA_CTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer",
+ TWL4030_REG_ARXL1_APGA_CTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer",
+ TWL4030_REG_ARXR2_APGA_CTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer",
+ TWL4030_REG_ARXL2_APGA_CTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer",
+ TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event,
+ SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("AIF Enable", SND_SOC_NOPM, 0, 0, aif_event,
+ SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
+
+ /* Output MIXER controls */
+ /* Earpiece */
+ SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_earpiece_controls[0],
+ ARRAY_SIZE(twl4030_dapm_earpiece_controls)),
+ SND_SOC_DAPM_PGA_E("Earpiece PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, earpiecepga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ /* PreDrivL/R */
+ SND_SOC_DAPM_MIXER("PredriveL Mixer", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_predrivel_controls[0],
+ ARRAY_SIZE(twl4030_dapm_predrivel_controls)),
+ SND_SOC_DAPM_PGA_E("PredriveL PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, predrivelpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("PredriveR Mixer", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_predriver_controls[0],
+ ARRAY_SIZE(twl4030_dapm_predriver_controls)),
+ SND_SOC_DAPM_PGA_E("PredriveR PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, predriverpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ /* HeadsetL/R */
+ SND_SOC_DAPM_MIXER("HeadsetL Mixer", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_hsol_controls[0],
+ ARRAY_SIZE(twl4030_dapm_hsol_controls)),
+ SND_SOC_DAPM_PGA_E("HeadsetL PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, headsetlpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("HeadsetR Mixer", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_hsor_controls[0],
+ ARRAY_SIZE(twl4030_dapm_hsor_controls)),
+ SND_SOC_DAPM_PGA_E("HeadsetR PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, headsetrpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ /* CarkitL/R */
+ SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_carkitl_controls[0],
+ ARRAY_SIZE(twl4030_dapm_carkitl_controls)),
+ SND_SOC_DAPM_PGA_E("CarkitL PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, carkitlpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("CarkitR Mixer", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_carkitr_controls[0],
+ ARRAY_SIZE(twl4030_dapm_carkitr_controls)),
+ SND_SOC_DAPM_PGA_E("CarkitR PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, carkitrpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+ /* Output MUX controls */
+ /* HandsfreeL/R */
+ SND_SOC_DAPM_MUX("HandsfreeL Mux", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_handsfreel_control),
+ SND_SOC_DAPM_SWITCH("HandsfreeL", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_handsfreelmute_control),
+ SND_SOC_DAPM_PGA_E("HandsfreeL PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, handsfreelpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("HandsfreeR Mux", SND_SOC_NOPM, 5, 0,
+ &twl4030_dapm_handsfreer_control),
+ SND_SOC_DAPM_SWITCH("HandsfreeR", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_handsfreermute_control),
+ SND_SOC_DAPM_PGA_E("HandsfreeR PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, handsfreerpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ /* Vibra */
+ SND_SOC_DAPM_MUX_E("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
+ &twl4030_dapm_vibra_control, vibramux_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_vibrapath_control),
+
+ /* Introducing four virtual ADC, since TWL4030 have four channel for
+ capture */
+ SND_SOC_DAPM_ADC("ADC Virtual Left1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC Virtual Right1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC Virtual Left2", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC Virtual Right2", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("VAIFOUT", "Voice Capture", 0,
+ TWL4030_REG_VOICE_IF, 5, 0),
+
+ /* Analog/Digital mic path selection.
+ TX1 Left/Right: either analog Left/Right or Digimic0
+ TX2 Left/Right: either analog Left/Right or Digimic1 */
+ SND_SOC_DAPM_MUX("TX1 Capture Route", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_micpathtx1_control),
+ SND_SOC_DAPM_MUX("TX2 Capture Route", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_micpathtx2_control),
+
+ /* Analog input mixers for the capture amplifiers */
+ SND_SOC_DAPM_MIXER("Analog Left",
+ TWL4030_REG_ANAMICL, 4, 0,
+ &twl4030_dapm_analoglmic_controls[0],
+ ARRAY_SIZE(twl4030_dapm_analoglmic_controls)),
+ SND_SOC_DAPM_MIXER("Analog Right",
+ TWL4030_REG_ANAMICR, 4, 0,
+ &twl4030_dapm_analogrmic_controls[0],
+ ARRAY_SIZE(twl4030_dapm_analogrmic_controls)),
+
+ SND_SOC_DAPM_PGA("ADC Physical Left",
+ TWL4030_REG_AVADC_CTL, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC Physical Right",
+ TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_E("Digimic0 Enable",
+ TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0,
+ digimic_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("Digimic1 Enable",
+ TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0,
+ digimic_event, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SUPPLY("micbias1 select", TWL4030_REG_MICBIAS_CTL, 5, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("micbias2 select", TWL4030_REG_MICBIAS_CTL, 6, 0,
+ NULL, 0),
+
+ /* Microphone bias */
+ SND_SOC_DAPM_SUPPLY("Mic Bias 1",
+ TWL4030_REG_MICBIAS_CTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias 2",
+ TWL4030_REG_MICBIAS_CTL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headset Mic Bias",
+ TWL4030_REG_MICBIAS_CTL, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("VIF Enable", TWL4030_REG_VOICE_IF, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ /* Stream -> DAC mapping */
+ {"DAC Right1", NULL, "HiFi Playback"},
+ {"DAC Left1", NULL, "HiFi Playback"},
+ {"DAC Right2", NULL, "HiFi Playback"},
+ {"DAC Left2", NULL, "HiFi Playback"},
+ {"DAC Voice", NULL, "VAIFIN"},
+
+ /* ADC -> Stream mapping */
+ {"HiFi Capture", NULL, "ADC Virtual Left1"},
+ {"HiFi Capture", NULL, "ADC Virtual Right1"},
+ {"HiFi Capture", NULL, "ADC Virtual Left2"},
+ {"HiFi Capture", NULL, "ADC Virtual Right2"},
+ {"VAIFOUT", NULL, "ADC Virtual Left2"},
+ {"VAIFOUT", NULL, "ADC Virtual Right2"},
+ {"VAIFOUT", NULL, "VIF Enable"},
+
+ {"Digital L1 Playback Mixer", NULL, "DAC Left1"},
+ {"Digital R1 Playback Mixer", NULL, "DAC Right1"},
+ {"Digital L2 Playback Mixer", NULL, "DAC Left2"},
+ {"Digital R2 Playback Mixer", NULL, "DAC Right2"},
+ {"Digital Voice Playback Mixer", NULL, "DAC Voice"},
+
+ /* Supply for the digital part (APLL) */
+ {"Digital Voice Playback Mixer", NULL, "APLL Enable"},
+
+ {"DAC Left1", NULL, "AIF Enable"},
+ {"DAC Right1", NULL, "AIF Enable"},
+ {"DAC Left2", NULL, "AIF Enable"},
+ {"DAC Right1", NULL, "AIF Enable"},
+ {"DAC Voice", NULL, "VIF Enable"},
+
+ {"Digital R2 Playback Mixer", NULL, "AIF Enable"},
+ {"Digital L2 Playback Mixer", NULL, "AIF Enable"},
+
+ {"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"},
+ {"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"},
+ {"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"},
+ {"Analog R2 Playback Mixer", NULL, "Digital R2 Playback Mixer"},
+ {"Analog Voice Playback Mixer", NULL, "Digital Voice Playback Mixer"},
+
+ /* Internal playback routings */
+ /* Earpiece */
+ {"Earpiece Mixer", "Voice", "Analog Voice Playback Mixer"},
+ {"Earpiece Mixer", "AudioL1", "Analog L1 Playback Mixer"},
+ {"Earpiece Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+ {"Earpiece Mixer", "AudioR1", "Analog R1 Playback Mixer"},
+ {"Earpiece PGA", NULL, "Earpiece Mixer"},
+ /* PreDrivL */
+ {"PredriveL Mixer", "Voice", "Analog Voice Playback Mixer"},
+ {"PredriveL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
+ {"PredriveL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+ {"PredriveL Mixer", "AudioR2", "Analog R2 Playback Mixer"},
+ {"PredriveL PGA", NULL, "PredriveL Mixer"},
+ /* PreDrivR */
+ {"PredriveR Mixer", "Voice", "Analog Voice Playback Mixer"},
+ {"PredriveR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
+ {"PredriveR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
+ {"PredriveR Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+ {"PredriveR PGA", NULL, "PredriveR Mixer"},
+ /* HeadsetL */
+ {"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"},
+ {"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
+ {"HeadsetL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+ {"HeadsetL PGA", NULL, "HeadsetL Mixer"},
+ /* HeadsetR */
+ {"HeadsetR Mixer", "Voice", "Analog Voice Playback Mixer"},
+ {"HeadsetR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
+ {"HeadsetR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
+ {"HeadsetR PGA", NULL, "HeadsetR Mixer"},
+ /* CarkitL */
+ {"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"},
+ {"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
+ {"CarkitL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+ {"CarkitL PGA", NULL, "CarkitL Mixer"},
+ /* CarkitR */
+ {"CarkitR Mixer", "Voice", "Analog Voice Playback Mixer"},
+ {"CarkitR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
+ {"CarkitR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
+ {"CarkitR PGA", NULL, "CarkitR Mixer"},
+ /* HandsfreeL */
+ {"HandsfreeL Mux", "Voice", "Analog Voice Playback Mixer"},
+ {"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"},
+ {"HandsfreeL Mux", "AudioL2", "Analog L2 Playback Mixer"},
+ {"HandsfreeL Mux", "AudioR2", "Analog R2 Playback Mixer"},
+ {"HandsfreeL", "Switch", "HandsfreeL Mux"},
+ {"HandsfreeL PGA", NULL, "HandsfreeL"},
+ /* HandsfreeR */
+ {"HandsfreeR Mux", "Voice", "Analog Voice Playback Mixer"},
+ {"HandsfreeR Mux", "AudioR1", "Analog R1 Playback Mixer"},
+ {"HandsfreeR Mux", "AudioR2", "Analog R2 Playback Mixer"},
+ {"HandsfreeR Mux", "AudioL2", "Analog L2 Playback Mixer"},
+ {"HandsfreeR", "Switch", "HandsfreeR Mux"},
+ {"HandsfreeR PGA", NULL, "HandsfreeR"},
+ /* Vibra */
+ {"Vibra Mux", "AudioL1", "DAC Left1"},
+ {"Vibra Mux", "AudioR1", "DAC Right1"},
+ {"Vibra Mux", "AudioL2", "DAC Left2"},
+ {"Vibra Mux", "AudioR2", "DAC Right2"},
+
+ /* outputs */
+ /* Must be always connected (for AIF and APLL) */
+ {"Virtual HiFi OUT", NULL, "DAC Left1"},
+ {"Virtual HiFi OUT", NULL, "DAC Right1"},
+ {"Virtual HiFi OUT", NULL, "DAC Left2"},
+ {"Virtual HiFi OUT", NULL, "DAC Right2"},
+ /* Must be always connected (for APLL) */
+ {"Virtual Voice OUT", NULL, "Digital Voice Playback Mixer"},
+ /* Physical outputs */
+ {"EARPIECE", NULL, "Earpiece PGA"},
+ {"PREDRIVEL", NULL, "PredriveL PGA"},
+ {"PREDRIVER", NULL, "PredriveR PGA"},
+ {"HSOL", NULL, "HeadsetL PGA"},
+ {"HSOR", NULL, "HeadsetR PGA"},
+ {"CARKITL", NULL, "CarkitL PGA"},
+ {"CARKITR", NULL, "CarkitR PGA"},
+ {"HFL", NULL, "HandsfreeL PGA"},
+ {"HFR", NULL, "HandsfreeR PGA"},
+ {"Vibra Route", "Audio", "Vibra Mux"},
+ {"VIBRA", NULL, "Vibra Route"},
+
+ /* Capture path */
+ /* Must be always connected (for AIF and APLL) */
+ {"ADC Virtual Left1", NULL, "Virtual HiFi IN"},
+ {"ADC Virtual Right1", NULL, "Virtual HiFi IN"},
+ {"ADC Virtual Left2", NULL, "Virtual HiFi IN"},
+ {"ADC Virtual Right2", NULL, "Virtual HiFi IN"},
+ /* Physical inputs */
+ {"Analog Left", "Main Mic Capture Switch", "MAINMIC"},
+ {"Analog Left", "Headset Mic Capture Switch", "HSMIC"},
+ {"Analog Left", "AUXL Capture Switch", "AUXL"},
+ {"Analog Left", "Carkit Mic Capture Switch", "CARKITMIC"},
+
+ {"Analog Right", "Sub Mic Capture Switch", "SUBMIC"},
+ {"Analog Right", "AUXR Capture Switch", "AUXR"},
+
+ {"ADC Physical Left", NULL, "Analog Left"},
+ {"ADC Physical Right", NULL, "Analog Right"},
+
+ {"Digimic0 Enable", NULL, "DIGIMIC0"},
+ {"Digimic1 Enable", NULL, "DIGIMIC1"},
+
+ {"DIGIMIC0", NULL, "micbias1 select"},
+ {"DIGIMIC1", NULL, "micbias2 select"},
+
+ /* TX1 Left capture path */
+ {"TX1 Capture Route", "Analog", "ADC Physical Left"},
+ {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
+ /* TX1 Right capture path */
+ {"TX1 Capture Route", "Analog", "ADC Physical Right"},
+ {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
+ /* TX2 Left capture path */
+ {"TX2 Capture Route", "Analog", "ADC Physical Left"},
+ {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"},
+ /* TX2 Right capture path */
+ {"TX2 Capture Route", "Analog", "ADC Physical Right"},
+ {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"},
+
+ {"ADC Virtual Left1", NULL, "TX1 Capture Route"},
+ {"ADC Virtual Right1", NULL, "TX1 Capture Route"},
+ {"ADC Virtual Left2", NULL, "TX2 Capture Route"},
+ {"ADC Virtual Right2", NULL, "TX2 Capture Route"},
+
+ {"ADC Virtual Left1", NULL, "AIF Enable"},
+ {"ADC Virtual Right1", NULL, "AIF Enable"},
+ {"ADC Virtual Left2", NULL, "AIF Enable"},
+ {"ADC Virtual Right2", NULL, "AIF Enable"},
+
+ /* Analog bypass routes */
+ {"Right1 Analog Loopback", "Switch", "Analog Right"},
+ {"Left1 Analog Loopback", "Switch", "Analog Left"},
+ {"Right2 Analog Loopback", "Switch", "Analog Right"},
+ {"Left2 Analog Loopback", "Switch", "Analog Left"},
+ {"Voice Analog Loopback", "Switch", "Analog Left"},
+
+ /* Supply for the Analog loopbacks */
+ {"Right1 Analog Loopback", NULL, "FM Loop Enable"},
+ {"Left1 Analog Loopback", NULL, "FM Loop Enable"},
+ {"Right2 Analog Loopback", NULL, "FM Loop Enable"},
+ {"Left2 Analog Loopback", NULL, "FM Loop Enable"},
+ {"Voice Analog Loopback", NULL, "FM Loop Enable"},
+
+ {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"},
+ {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"},
+ {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"},
+ {"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"},
+ {"Analog Voice Playback Mixer", NULL, "Voice Analog Loopback"},
+
+ /* Digital bypass routes */
+ {"Right Digital Loopback", "Volume", "TX1 Capture Route"},
+ {"Left Digital Loopback", "Volume", "TX1 Capture Route"},
+ {"Voice Digital Loopback", "Volume", "TX2 Capture Route"},
+
+ {"Digital R2 Playback Mixer", NULL, "Right Digital Loopback"},
+ {"Digital L2 Playback Mixer", NULL, "Left Digital Loopback"},
+ {"Digital Voice Playback Mixer", NULL, "Voice Digital Loopback"},
+
+};
+
+static int twl4030_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ twl4030_codec_enable(codec, 1);
+ break;
+ case SND_SOC_BIAS_OFF:
+ twl4030_codec_enable(codec, 0);
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static void twl4030_constraints(struct twl4030_priv *twl4030,
+ struct snd_pcm_substream *mst_substream)
+{
+ struct snd_pcm_substream *slv_substream;
+
+ /* Pick the stream, which need to be constrained */
+ if (mst_substream == twl4030->master_substream)
+ slv_substream = twl4030->slave_substream;
+ else if (mst_substream == twl4030->slave_substream)
+ slv_substream = twl4030->master_substream;
+ else /* This should not happen.. */
+ return;
+
+ /* Set the constraints according to the already configured stream */
+ snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ twl4030->rate,
+ twl4030->rate);
+
+ snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ twl4030->sample_bits,
+ twl4030->sample_bits);
+
+ snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ twl4030->channels,
+ twl4030->channels);
+}
+
+/* In case of 4 channel mode, the RX1 L/R for playback and the TX2 L/R for
+ * capture has to be enabled/disabled. */
+static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction,
+ int enable)
+{
+ u8 reg, mask;
+
+ reg = twl4030_read(codec, TWL4030_REG_OPTION);
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+ mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN;
+ else
+ mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN;
+
+ if (enable)
+ reg |= mask;
+ else
+ reg &= ~mask;
+
+ twl4030_write(codec, TWL4030_REG_OPTION, reg);
+}
+
+static int twl4030_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+
+ if (twl4030->master_substream) {
+ twl4030->slave_substream = substream;
+ /* The DAI has one configuration for playback and capture, so
+ * if the DAI has been already configured then constrain this
+ * substream to match it. */
+ if (twl4030->configured)
+ twl4030_constraints(twl4030, twl4030->master_substream);
+ } else {
+ if (!(twl4030_read(codec, TWL4030_REG_CODEC_MODE) &
+ TWL4030_OPTION_1)) {
+ /* In option2 4 channel is not supported, set the
+ * constraint for the first stream for channels, the
+ * second stream will 'inherit' this cosntraint */
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ 2, 2);
+ }
+ twl4030->master_substream = substream;
+ }
+
+ return 0;
+}
+
+static void twl4030_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+
+ if (twl4030->master_substream == substream)
+ twl4030->master_substream = twl4030->slave_substream;
+
+ twl4030->slave_substream = NULL;
+
+ /* If all streams are closed, or the remaining stream has not yet
+ * been configured than set the DAI as not configured. */
+ if (!twl4030->master_substream)
+ twl4030->configured = 0;
+ else if (!twl4030->master_substream->runtime->channels)
+ twl4030->configured = 0;
+
+ /* If the closing substream had 4 channel, do the necessary cleanup */
+ if (substream->runtime->channels == 4)
+ twl4030_tdm_enable(codec, substream->stream, 0);
+}
+
+static int twl4030_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ u8 mode, old_mode, format, old_format;
+
+ /* If the substream has 4 channel, do the necessary setup */
+ if (params_channels(params) == 4) {
+ format = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
+ mode = twl4030_read(codec, TWL4030_REG_CODEC_MODE);
+
+ /* Safety check: are we in the correct operating mode and
+ * the interface is in TDM mode? */
+ if ((mode & TWL4030_OPTION_1) &&
+ ((format & TWL4030_AIF_FORMAT) == TWL4030_AIF_FORMAT_TDM))
+ twl4030_tdm_enable(codec, substream->stream, 1);
+ else
+ return -EINVAL;
+ }
+
+ if (twl4030->configured)
+ /* Ignoring hw_params for already configured DAI */
+ return 0;
+
+ /* bit rate */
+ old_mode = twl4030_read(codec,
+ TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
+ mode = old_mode & ~TWL4030_APLL_RATE;
+
+ switch (params_rate(params)) {
+ case 8000:
+ mode |= TWL4030_APLL_RATE_8000;
+ break;
+ case 11025:
+ mode |= TWL4030_APLL_RATE_11025;
+ break;
+ case 12000:
+ mode |= TWL4030_APLL_RATE_12000;
+ break;
+ case 16000:
+ mode |= TWL4030_APLL_RATE_16000;
+ break;
+ case 22050:
+ mode |= TWL4030_APLL_RATE_22050;
+ break;
+ case 24000:
+ mode |= TWL4030_APLL_RATE_24000;
+ break;
+ case 32000:
+ mode |= TWL4030_APLL_RATE_32000;
+ break;
+ case 44100:
+ mode |= TWL4030_APLL_RATE_44100;
+ break;
+ case 48000:
+ mode |= TWL4030_APLL_RATE_48000;
+ break;
+ case 96000:
+ mode |= TWL4030_APLL_RATE_96000;
+ break;
+ default:
+ dev_err(codec->dev, "%s: unknown rate %d\n", __func__,
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ /* sample size */
+ old_format = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
+ format = old_format;
+ format &= ~TWL4030_DATA_WIDTH;
+ switch (params_width(params)) {
+ case 16:
+ format |= TWL4030_DATA_WIDTH_16S_16W;
+ break;
+ case 32:
+ format |= TWL4030_DATA_WIDTH_32S_24W;
+ break;
+ default:
+ dev_err(codec->dev, "%s: unsupported bits/sample %d\n",
+ __func__, params_width(params));
+ return -EINVAL;
+ }
+
+ if (format != old_format || mode != old_mode) {
+ if (twl4030->codec_powered) {
+ /*
+ * If the codec is powered, than we need to toggle the
+ * codec power.
+ */
+ twl4030_codec_enable(codec, 0);
+ twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+ twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+ twl4030_codec_enable(codec, 1);
+ } else {
+ twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+ twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+ }
+ }
+
+ /* Store the important parameters for the DAI configuration and set
+ * the DAI as configured */
+ twl4030->configured = 1;
+ twl4030->rate = params_rate(params);
+ twl4030->sample_bits = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
+ twl4030->channels = params_channels(params);
+
+ /* If both playback and capture streams are open, and one of them
+ * is setting the hw parameters right now (since we are here), set
+ * constraints to the other stream to match the current one. */
+ if (twl4030->slave_substream)
+ twl4030_constraints(twl4030, substream);
+
+ return 0;
+}
+
+static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+
+ switch (freq) {
+ case 19200000:
+ case 26000000:
+ case 38400000:
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported HFCLKIN: %u\n", freq);
+ return -EINVAL;
+ }
+
+ if ((freq / 1000) != twl4030->sysclk) {
+ dev_err(codec->dev,
+ "Mismatch in HFCLKIN: %u (configured: %u)\n",
+ freq, twl4030->sysclk * 1000);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ u8 old_format, format;
+
+ /* get format */
+ old_format = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
+ format = old_format;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ format &= ~(TWL4030_AIF_SLAVE_EN);
+ format &= ~(TWL4030_CLK256FS_EN);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ format |= TWL4030_AIF_SLAVE_EN;
+ format |= TWL4030_CLK256FS_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ format &= ~TWL4030_AIF_FORMAT;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format |= TWL4030_AIF_FORMAT_CODEC;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format |= TWL4030_AIF_FORMAT_TDM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (format != old_format) {
+ if (twl4030->codec_powered) {
+ /*
+ * If the codec is powered, than we need to toggle the
+ * codec power.
+ */
+ twl4030_codec_enable(codec, 0);
+ twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+ twl4030_codec_enable(codec, 1);
+ } else {
+ twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+ }
+ }
+
+ return 0;
+}
+
+static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 reg = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
+
+ if (tristate)
+ reg |= TWL4030_AIF_TRI_EN;
+ else
+ reg &= ~TWL4030_AIF_TRI_EN;
+
+ return twl4030_write(codec, TWL4030_REG_AUDIO_IF, reg);
+}
+
+/* In case of voice mode, the RX1 L(VRX) for downlink and the TX2 L/R
+ * (VTXL, VTXR) for uplink has to be enabled/disabled. */
+static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction,
+ int enable)
+{
+ u8 reg, mask;
+
+ reg = twl4030_read(codec, TWL4030_REG_OPTION);
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+ mask = TWL4030_ARXL1_VRX_EN;
+ else
+ mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN;
+
+ if (enable)
+ reg |= mask;
+ else
+ reg &= ~mask;
+
+ twl4030_write(codec, TWL4030_REG_OPTION, reg);
+}
+
+static int twl4030_voice_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ u8 mode;
+
+ /* If the system master clock is not 26MHz, the voice PCM interface is
+ * not available.
+ */
+ if (twl4030->sysclk != 26000) {
+ dev_err(codec->dev,
+ "%s: HFCLKIN is %u KHz, voice interface needs 26MHz\n",
+ __func__, twl4030->sysclk);
+ return -EINVAL;
+ }
+
+ /* If the codec mode is not option2, the voice PCM interface is not
+ * available.
+ */
+ mode = twl4030_read(codec, TWL4030_REG_CODEC_MODE)
+ & TWL4030_OPT_MODE;
+
+ if (mode != TWL4030_OPTION_2) {
+ dev_err(codec->dev, "%s: the codec mode is not option2\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void twl4030_voice_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ /* Enable voice digital filters */
+ twl4030_voice_enable(codec, substream->stream, 0);
+}
+
+static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ u8 old_mode, mode;
+
+ /* Enable voice digital filters */
+ twl4030_voice_enable(codec, substream->stream, 1);
+
+ /* bit rate */
+ old_mode = twl4030_read(codec,
+ TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
+ mode = old_mode;
+
+ switch (params_rate(params)) {
+ case 8000:
+ mode &= ~(TWL4030_SEL_16K);
+ break;
+ case 16000:
+ mode |= TWL4030_SEL_16K;
+ break;
+ default:
+ dev_err(codec->dev, "%s: unknown rate %d\n", __func__,
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ if (mode != old_mode) {
+ if (twl4030->codec_powered) {
+ /*
+ * If the codec is powered, than we need to toggle the
+ * codec power.
+ */
+ twl4030_codec_enable(codec, 0);
+ twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+ twl4030_codec_enable(codec, 1);
+ } else {
+ twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+ }
+ }
+
+ return 0;
+}
+
+static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+
+ if (freq != 26000000) {
+ dev_err(codec->dev,
+ "%s: HFCLKIN is %u KHz, voice interface needs 26MHz\n",
+ __func__, freq / 1000);
+ return -EINVAL;
+ }
+ if ((freq / 1000) != twl4030->sysclk) {
+ dev_err(codec->dev,
+ "Mismatch in HFCLKIN: %u (configured: %u)\n",
+ freq, twl4030->sysclk * 1000);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ u8 old_format, format;
+
+ /* get format */
+ old_format = twl4030_read(codec, TWL4030_REG_VOICE_IF);
+ format = old_format;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ format &= ~(TWL4030_VIF_SLAVE_EN);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ format |= TWL4030_VIF_SLAVE_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ format &= ~(TWL4030_VIF_FORMAT);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ format |= TWL4030_VIF_FORMAT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (format != old_format) {
+ if (twl4030->codec_powered) {
+ /*
+ * If the codec is powered, than we need to toggle the
+ * codec power.
+ */
+ twl4030_codec_enable(codec, 0);
+ twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
+ twl4030_codec_enable(codec, 1);
+ } else {
+ twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
+ }
+ }
+
+ return 0;
+}
+
+static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 reg = twl4030_read(codec, TWL4030_REG_VOICE_IF);
+
+ if (tristate)
+ reg |= TWL4030_VIF_TRI_EN;
+ else
+ reg &= ~TWL4030_VIF_TRI_EN;
+
+ return twl4030_write(codec, TWL4030_REG_VOICE_IF, reg);
+}
+
+#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000)
+#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops twl4030_dai_hifi_ops = {
+ .startup = twl4030_startup,
+ .shutdown = twl4030_shutdown,
+ .hw_params = twl4030_hw_params,
+ .set_sysclk = twl4030_set_dai_sysclk,
+ .set_fmt = twl4030_set_dai_fmt,
+ .set_tristate = twl4030_set_tristate,
+};
+
+static const struct snd_soc_dai_ops twl4030_dai_voice_ops = {
+ .startup = twl4030_voice_startup,
+ .shutdown = twl4030_voice_shutdown,
+ .hw_params = twl4030_voice_hw_params,
+ .set_sysclk = twl4030_voice_set_dai_sysclk,
+ .set_fmt = twl4030_voice_set_dai_fmt,
+ .set_tristate = twl4030_voice_set_tristate,
+};
+
+static struct snd_soc_dai_driver twl4030_dai[] = {
+{
+ .name = "twl4030-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = TWL4030_RATES | SNDRV_PCM_RATE_96000,
+ .formats = TWL4030_FORMATS,
+ .sig_bits = 24,},
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = TWL4030_RATES,
+ .formats = TWL4030_FORMATS,
+ .sig_bits = 24,},
+ .ops = &twl4030_dai_hifi_ops,
+},
+{
+ .name = "twl4030-voice",
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "Voice Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &twl4030_dai_voice_ops,
+},
+};
+
+static int twl4030_soc_probe(struct snd_soc_codec *codec)
+{
+ struct twl4030_priv *twl4030;
+
+ twl4030 = devm_kzalloc(codec->dev, sizeof(struct twl4030_priv),
+ GFP_KERNEL);
+ if (!twl4030)
+ return -ENOMEM;
+ snd_soc_codec_set_drvdata(codec, twl4030);
+ /* Set the defaults, and power up the codec */
+ twl4030->sysclk = twl4030_audio_get_mclk() / 1000;
+
+ twl4030_init_chip(codec);
+
+ return 0;
+}
+
+static int twl4030_soc_remove(struct snd_soc_codec *codec)
+{
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ struct twl4030_codec_data *pdata = twl4030->pdata;
+
+ if (pdata && pdata->hs_extmute && gpio_is_valid(pdata->hs_extmute_gpio))
+ gpio_free(pdata->hs_extmute_gpio);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_twl4030 = {
+ .probe = twl4030_soc_probe,
+ .remove = twl4030_soc_remove,
+ .read = twl4030_read,
+ .write = twl4030_write,
+ .set_bias_level = twl4030_set_bias_level,
+ .idle_bias_off = true,
+
+ .controls = twl4030_snd_controls,
+ .num_controls = ARRAY_SIZE(twl4030_snd_controls),
+ .dapm_widgets = twl4030_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(twl4030_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+};
+
+static int twl4030_codec_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl4030,
+ twl4030_dai, ARRAY_SIZE(twl4030_dai));
+}
+
+static int twl4030_codec_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+MODULE_ALIAS("platform:twl4030-codec");
+
+static struct platform_driver twl4030_codec_driver = {
+ .probe = twl4030_codec_probe,
+ .remove = twl4030_codec_remove,
+ .driver = {
+ .name = "twl4030-codec",
+ },
+};
+
+module_platform_driver(twl4030_codec_driver);
+
+MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
+MODULE_AUTHOR("Steve Sakoman");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/twl6040.c b/kernel/sound/soc/codecs/twl6040.c
new file mode 100644
index 000000000..aeec27b6f
--- /dev/null
+++ b/kernel/sound/soc/codecs/twl6040.c
@@ -0,0 +1,1189 @@
+/*
+ * ALSA SoC TWL6040 codec driver
+ *
+ * Author: Misael Lopez Cruz <x0052729@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 <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/twl6040.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 "twl6040.h"
+
+enum twl6040_dai_id {
+ TWL6040_DAI_LEGACY = 0,
+ TWL6040_DAI_UL,
+ TWL6040_DAI_DL1,
+ TWL6040_DAI_DL2,
+ TWL6040_DAI_VIB,
+};
+
+#define TWL6040_RATES SNDRV_PCM_RATE_8000_96000
+#define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TWL6040_OUTHS_0dB 0x00
+#define TWL6040_OUTHS_M30dB 0x0F
+#define TWL6040_OUTHF_0dB 0x03
+#define TWL6040_OUTHF_M52dB 0x1D
+
+#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1)
+
+struct twl6040_jack_data {
+ struct snd_soc_jack *jack;
+ struct delayed_work work;
+ int report;
+};
+
+/* codec private data */
+struct twl6040_data {
+ int plug_irq;
+ int codec_powered;
+ int pll;
+ int pll_power_mode;
+ int hs_power_mode;
+ int hs_power_mode_locked;
+ bool dl1_unmuted;
+ bool dl2_unmuted;
+ u8 dl12_cache[TWL6040_REG_HFRCTL - TWL6040_REG_HSLCTL + 1];
+ unsigned int clk_in;
+ unsigned int sysclk;
+ struct twl6040_jack_data hs_jack;
+ struct snd_soc_codec *codec;
+ struct mutex mutex;
+};
+
+/* set of rates for each pll: low-power and high-performance */
+static const unsigned int lp_rates[] = {
+ 8000,
+ 11250,
+ 16000,
+ 22500,
+ 32000,
+ 44100,
+ 48000,
+ 88200,
+ 96000,
+};
+
+static const unsigned int hp_rates[] = {
+ 8000,
+ 16000,
+ 32000,
+ 48000,
+ 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list sysclk_constraints[] = {
+ { .count = ARRAY_SIZE(lp_rates), .list = lp_rates, },
+ { .count = ARRAY_SIZE(hp_rates), .list = hp_rates, },
+};
+
+static unsigned int twl6040_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct twl6040 *twl6040 = codec->control_data;
+ u8 value;
+
+ if (reg >= TWL6040_CACHEREGNUM)
+ return -EIO;
+
+ switch (reg) {
+ case TWL6040_REG_HSLCTL:
+ case TWL6040_REG_HSRCTL:
+ case TWL6040_REG_EARCTL:
+ case TWL6040_REG_HFLCTL:
+ case TWL6040_REG_HFRCTL:
+ value = priv->dl12_cache[reg - TWL6040_REG_HSLCTL];
+ break;
+ default:
+ value = twl6040_reg_read(twl6040, reg);
+ break;
+ }
+
+ return value;
+}
+
+static bool twl6040_can_write_to_chip(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (reg) {
+ case TWL6040_REG_HSLCTL:
+ case TWL6040_REG_HSRCTL:
+ case TWL6040_REG_EARCTL:
+ /* DL1 path */
+ return priv->dl1_unmuted;
+ case TWL6040_REG_HFLCTL:
+ case TWL6040_REG_HFRCTL:
+ return priv->dl2_unmuted;
+ default:
+ return 1;
+ }
+}
+
+static inline void twl6040_update_dl12_cache(struct snd_soc_codec *codec,
+ u8 reg, u8 value)
+{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (reg) {
+ case TWL6040_REG_HSLCTL:
+ case TWL6040_REG_HSRCTL:
+ case TWL6040_REG_EARCTL:
+ case TWL6040_REG_HFLCTL:
+ case TWL6040_REG_HFRCTL:
+ priv->dl12_cache[reg - TWL6040_REG_HSLCTL] = value;
+ break;
+ default:
+ break;
+ }
+}
+
+static int twl6040_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ struct twl6040 *twl6040 = codec->control_data;
+
+ if (reg >= TWL6040_CACHEREGNUM)
+ return -EIO;
+
+ twl6040_update_dl12_cache(codec, reg, value);
+ if (twl6040_can_write_to_chip(codec, reg))
+ return twl6040_reg_write(twl6040, reg, value);
+ else
+ return 0;
+}
+
+static void twl6040_init_chip(struct snd_soc_codec *codec)
+{
+ twl6040_read(codec, TWL6040_REG_TRIM1);
+ twl6040_read(codec, TWL6040_REG_TRIM2);
+ twl6040_read(codec, TWL6040_REG_TRIM3);
+ twl6040_read(codec, TWL6040_REG_HSOTRIM);
+ twl6040_read(codec, TWL6040_REG_HFOTRIM);
+
+ /* Change chip defaults */
+ /* No imput selected for microphone amplifiers */
+ twl6040_write(codec, TWL6040_REG_MICLCTL, 0x18);
+ twl6040_write(codec, TWL6040_REG_MICRCTL, 0x18);
+
+ /*
+ * We need to lower the default gain values, so the ramp code
+ * can work correctly for the first playback.
+ * This reduces the pop noise heard at the first playback.
+ */
+ twl6040_write(codec, TWL6040_REG_HSGAIN, 0xff);
+ twl6040_write(codec, TWL6040_REG_EARCTL, 0x1e);
+ twl6040_write(codec, TWL6040_REG_HFLGAIN, 0x1d);
+ twl6040_write(codec, TWL6040_REG_HFRGAIN, 0x1d);
+ twl6040_write(codec, TWL6040_REG_LINEGAIN, 0);
+}
+
+/* set headset dac and driver power mode */
+static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
+{
+ int hslctl, hsrctl;
+ int mask = TWL6040_HSDRVMODE | TWL6040_HSDACMODE;
+
+ hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL);
+ hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL);
+
+ if (high_perf) {
+ hslctl &= ~mask;
+ hsrctl &= ~mask;
+ } else {
+ hslctl |= mask;
+ hsrctl |= mask;
+ }
+
+ twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl);
+ twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl);
+
+ return 0;
+}
+
+static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u8 hslctl, hsrctl;
+
+ /*
+ * Workaround for Headset DC offset caused pop noise:
+ * Both HS DAC need to be turned on (before the HS driver) and off at
+ * the same time.
+ */
+ hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL);
+ hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL);
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ hslctl |= TWL6040_HSDACENA;
+ hsrctl |= TWL6040_HSDACENA;
+ } else {
+ hslctl &= ~TWL6040_HSDACENA;
+ hsrctl &= ~TWL6040_HSDACENA;
+ }
+ twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl);
+ twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl);
+
+ msleep(1);
+ return 0;
+}
+
+static int twl6040_ep_drv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Earphone doesn't support low power mode */
+ priv->hs_power_mode_locked = 1;
+ ret = headset_power_mode(codec, 1);
+ } else {
+ priv->hs_power_mode_locked = 0;
+ ret = headset_power_mode(codec, priv->hs_power_mode);
+ }
+
+ msleep(1);
+
+ return ret;
+}
+
+static void twl6040_hs_jack_report(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack, int report)
+{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ int status;
+
+ mutex_lock(&priv->mutex);
+
+ /* Sync status */
+ status = twl6040_read(codec, TWL6040_REG_STATUS);
+ if (status & TWL6040_PLUGCOMP)
+ snd_soc_jack_report(jack, report, report);
+ else
+ snd_soc_jack_report(jack, 0, report);
+
+ mutex_unlock(&priv->mutex);
+}
+
+void twl6040_hs_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack, int report)
+{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct twl6040_jack_data *hs_jack = &priv->hs_jack;
+
+ hs_jack->jack = jack;
+ hs_jack->report = report;
+
+ twl6040_hs_jack_report(codec, hs_jack->jack, hs_jack->report);
+}
+EXPORT_SYMBOL_GPL(twl6040_hs_jack_detect);
+
+static void twl6040_accessory_work(struct work_struct *work)
+{
+ struct twl6040_data *priv = container_of(work,
+ struct twl6040_data, hs_jack.work.work);
+ struct snd_soc_codec *codec = priv->codec;
+ struct twl6040_jack_data *hs_jack = &priv->hs_jack;
+
+ twl6040_hs_jack_report(codec, hs_jack->jack, hs_jack->report);
+}
+
+/* audio interrupt handler */
+static irqreturn_t twl6040_audio_handler(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &priv->hs_jack.work, msecs_to_jiffies(200));
+
+ return IRQ_HANDLED;
+}
+
+static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val;
+
+ /* Do not allow changes while Input/FF efect is running */
+ val = twl6040_read(codec, e->reg);
+ if (val & TWL6040_VIBENA && !(val & TWL6040_VIBSEL))
+ return -EBUSY;
+
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+/*
+ * MICATT volume control:
+ * from -6 to 0 dB in 6 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(mic_preamp_tlv, -600, 600, 0);
+
+/*
+ * MICGAIN volume control:
+ * from 6 to 30 dB in 6 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(mic_amp_tlv, 600, 600, 0);
+
+/*
+ * AFMGAIN volume control:
+ * from -18 to 24 dB in 6 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(afm_amp_tlv, -1800, 600, 0);
+
+/*
+ * HSGAIN volume control:
+ * from -30 to 0 dB in 2 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(hs_tlv, -3000, 200, 0);
+
+/*
+ * HFGAIN volume control:
+ * from -52 to 6 dB in 2 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(hf_tlv, -5200, 200, 0);
+
+/*
+ * EPGAIN volume control:
+ * from -24 to 6 dB in 2 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(ep_tlv, -2400, 200, 0);
+
+/* Left analog microphone selection */
+static const char *twl6040_amicl_texts[] =
+ {"Headset Mic", "Main Mic", "Aux/FM Left", "Off"};
+
+/* Right analog microphone selection */
+static const char *twl6040_amicr_texts[] =
+ {"Headset Mic", "Sub Mic", "Aux/FM Right", "Off"};
+
+static const struct soc_enum twl6040_enum[] = {
+ SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3,
+ ARRAY_SIZE(twl6040_amicl_texts), twl6040_amicl_texts),
+ SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3,
+ ARRAY_SIZE(twl6040_amicr_texts), twl6040_amicr_texts),
+};
+
+static const char *twl6040_hs_texts[] = {
+ "Off", "HS DAC", "Line-In amp"
+};
+
+static const struct soc_enum twl6040_hs_enum[] = {
+ SOC_ENUM_SINGLE(TWL6040_REG_HSLCTL, 5, ARRAY_SIZE(twl6040_hs_texts),
+ twl6040_hs_texts),
+ SOC_ENUM_SINGLE(TWL6040_REG_HSRCTL, 5, ARRAY_SIZE(twl6040_hs_texts),
+ twl6040_hs_texts),
+};
+
+static const char *twl6040_hf_texts[] = {
+ "Off", "HF DAC", "Line-In amp"
+};
+
+static const struct soc_enum twl6040_hf_enum[] = {
+ SOC_ENUM_SINGLE(TWL6040_REG_HFLCTL, 2, ARRAY_SIZE(twl6040_hf_texts),
+ twl6040_hf_texts),
+ SOC_ENUM_SINGLE(TWL6040_REG_HFRCTL, 2, ARRAY_SIZE(twl6040_hf_texts),
+ twl6040_hf_texts),
+};
+
+static const char *twl6040_vibrapath_texts[] = {
+ "Input FF", "Audio PDM"
+};
+
+static const struct soc_enum twl6040_vibra_enum[] = {
+ SOC_ENUM_SINGLE(TWL6040_REG_VIBCTLL, 1,
+ ARRAY_SIZE(twl6040_vibrapath_texts),
+ twl6040_vibrapath_texts),
+ SOC_ENUM_SINGLE(TWL6040_REG_VIBCTLR, 1,
+ ARRAY_SIZE(twl6040_vibrapath_texts),
+ twl6040_vibrapath_texts),
+};
+
+static const struct snd_kcontrol_new amicl_control =
+ SOC_DAPM_ENUM("Route", twl6040_enum[0]);
+
+static const struct snd_kcontrol_new amicr_control =
+ SOC_DAPM_ENUM("Route", twl6040_enum[1]);
+
+/* Headset DAC playback switches */
+static const struct snd_kcontrol_new hsl_mux_controls =
+ SOC_DAPM_ENUM("Route", twl6040_hs_enum[0]);
+
+static const struct snd_kcontrol_new hsr_mux_controls =
+ SOC_DAPM_ENUM("Route", twl6040_hs_enum[1]);
+
+/* Handsfree DAC playback switches */
+static const struct snd_kcontrol_new hfl_mux_controls =
+ SOC_DAPM_ENUM("Route", twl6040_hf_enum[0]);
+
+static const struct snd_kcontrol_new hfr_mux_controls =
+ SOC_DAPM_ENUM("Route", twl6040_hf_enum[1]);
+
+static const struct snd_kcontrol_new ep_path_enable_control =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const struct snd_kcontrol_new auxl_switch_control =
+ SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 6, 1, 0);
+
+static const struct snd_kcontrol_new auxr_switch_control =
+ SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 6, 1, 0);
+
+/* Vibra playback switches */
+static const struct snd_kcontrol_new vibral_mux_controls =
+ SOC_DAPM_ENUM_EXT("Route", twl6040_vibra_enum[0],
+ snd_soc_dapm_get_enum_double,
+ twl6040_soc_dapm_put_vibra_enum);
+
+static const struct snd_kcontrol_new vibrar_mux_controls =
+ SOC_DAPM_ENUM_EXT("Route", twl6040_vibra_enum[1],
+ snd_soc_dapm_get_enum_double,
+ twl6040_soc_dapm_put_vibra_enum);
+
+/* Headset power mode */
+static const char *twl6040_power_mode_texts[] = {
+ "Low-Power", "High-Performance",
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(twl6040_power_mode_enum,
+ twl6040_power_mode_texts);
+
+static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = priv->hs_power_mode;
+
+ return 0;
+}
+
+static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ int high_perf = ucontrol->value.enumerated.item[0];
+ int ret = 0;
+
+ if (!priv->hs_power_mode_locked)
+ ret = headset_power_mode(codec, high_perf);
+
+ if (!ret)
+ priv->hs_power_mode = high_perf;
+
+ return ret;
+}
+
+static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = priv->pll_power_mode;
+
+ return 0;
+}
+
+static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->pll_power_mode = ucontrol->value.enumerated.item[0];
+
+ return 0;
+}
+
+int twl6040_get_dl1_gain(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ if (snd_soc_dapm_get_pin_status(dapm, "EP"))
+ return -1; /* -1dB */
+
+ if (snd_soc_dapm_get_pin_status(dapm, "HSOR") ||
+ snd_soc_dapm_get_pin_status(dapm, "HSOL")) {
+
+ u8 val = snd_soc_read(codec, TWL6040_REG_HSLCTL);
+ if (val & TWL6040_HSDACMODE)
+ /* HSDACL in LP mode */
+ return -8; /* -8dB */
+ else
+ /* HSDACL in HP mode */
+ return -1; /* -1dB */
+ }
+ return 0; /* 0dB */
+}
+EXPORT_SYMBOL_GPL(twl6040_get_dl1_gain);
+
+int twl6040_get_clk_id(struct snd_soc_codec *codec)
+{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ return priv->pll_power_mode;
+}
+EXPORT_SYMBOL_GPL(twl6040_get_clk_id);
+
+int twl6040_get_trim_value(struct snd_soc_codec *codec, enum twl6040_trim trim)
+{
+ if (unlikely(trim >= TWL6040_TRIM_INVAL))
+ return -EINVAL;
+
+ return twl6040_read(codec, TWL6040_REG_TRIM1 + trim);
+}
+EXPORT_SYMBOL_GPL(twl6040_get_trim_value);
+
+int twl6040_get_hs_step_size(struct snd_soc_codec *codec)
+{
+ struct twl6040 *twl6040 = codec->control_data;
+
+ if (twl6040_get_revid(twl6040) < TWL6040_REV_ES1_3)
+ /* For ES under ES_1.3 HS step is 2 mV */
+ return 2;
+ else
+ /* For ES_1.3 HS step is 1 mV */
+ return 1;
+}
+EXPORT_SYMBOL_GPL(twl6040_get_hs_step_size);
+
+static const struct snd_kcontrol_new twl6040_snd_controls[] = {
+ /* Capture gains */
+ SOC_DOUBLE_TLV("Capture Preamplifier Volume",
+ TWL6040_REG_MICGAIN, 6, 7, 1, 1, mic_preamp_tlv),
+ SOC_DOUBLE_TLV("Capture Volume",
+ TWL6040_REG_MICGAIN, 0, 3, 4, 0, mic_amp_tlv),
+
+ /* AFM gains */
+ SOC_DOUBLE_TLV("Aux FM Volume",
+ TWL6040_REG_LINEGAIN, 0, 3, 7, 0, afm_amp_tlv),
+
+ /* Playback gains */
+ SOC_DOUBLE_TLV("Headset Playback Volume",
+ TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv),
+ SOC_DOUBLE_R_TLV("Handsfree Playback Volume",
+ TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv),
+ SOC_SINGLE_TLV("Earphone Playback Volume",
+ TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv),
+
+ SOC_ENUM_EXT("Headset Power Mode", twl6040_power_mode_enum,
+ twl6040_headset_power_get_enum,
+ twl6040_headset_power_put_enum),
+
+ SOC_ENUM_EXT("PLL Selection", twl6040_power_mode_enum,
+ twl6040_pll_get_enum, twl6040_pll_put_enum),
+};
+
+static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MAINMIC"),
+ SND_SOC_DAPM_INPUT("HSMIC"),
+ SND_SOC_DAPM_INPUT("SUBMIC"),
+ SND_SOC_DAPM_INPUT("AFML"),
+ SND_SOC_DAPM_INPUT("AFMR"),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HSOL"),
+ SND_SOC_DAPM_OUTPUT("HSOR"),
+ SND_SOC_DAPM_OUTPUT("HFL"),
+ SND_SOC_DAPM_OUTPUT("HFR"),
+ SND_SOC_DAPM_OUTPUT("EP"),
+ SND_SOC_DAPM_OUTPUT("AUXL"),
+ SND_SOC_DAPM_OUTPUT("AUXR"),
+ SND_SOC_DAPM_OUTPUT("VIBRAL"),
+ SND_SOC_DAPM_OUTPUT("VIBRAR"),
+
+ /* Analog input muxes for the capture amplifiers */
+ SND_SOC_DAPM_MUX("Analog Left Capture Route",
+ SND_SOC_NOPM, 0, 0, &amicl_control),
+ SND_SOC_DAPM_MUX("Analog Right Capture Route",
+ SND_SOC_NOPM, 0, 0, &amicr_control),
+
+ /* Analog capture PGAs */
+ SND_SOC_DAPM_PGA("MicAmpL",
+ TWL6040_REG_MICLCTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MicAmpR",
+ TWL6040_REG_MICRCTL, 0, 0, NULL, 0),
+
+ /* Auxiliary FM PGAs */
+ SND_SOC_DAPM_PGA("AFMAmpL",
+ TWL6040_REG_MICLCTL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AFMAmpR",
+ TWL6040_REG_MICRCTL, 1, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC Left", NULL, TWL6040_REG_MICLCTL, 2, 0),
+ SND_SOC_DAPM_ADC("ADC Right", NULL, TWL6040_REG_MICRCTL, 2, 0),
+
+ /* Microphone bias */
+ SND_SOC_DAPM_SUPPLY("Headset Mic Bias",
+ TWL6040_REG_AMICBCTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Main Mic Bias",
+ TWL6040_REG_AMICBCTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Digital Mic1 Bias",
+ TWL6040_REG_DMICBCTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Digital Mic2 Bias",
+ TWL6040_REG_DMICBCTL, 4, 0, NULL, 0),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("HSDAC Left", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("HSDAC Right", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("HFDAC Left", NULL, TWL6040_REG_HFLCTL, 0, 0),
+ SND_SOC_DAPM_DAC("HFDAC Right", NULL, TWL6040_REG_HFRCTL, 0, 0),
+ /* Virtual DAC for vibra path (DL4 channel) */
+ SND_SOC_DAPM_DAC("VIBRA DAC", NULL, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("Handsfree Left Playback",
+ SND_SOC_NOPM, 0, 0, &hfl_mux_controls),
+ SND_SOC_DAPM_MUX("Handsfree Right Playback",
+ SND_SOC_NOPM, 0, 0, &hfr_mux_controls),
+ /* Analog playback Muxes */
+ SND_SOC_DAPM_MUX("Headset Left Playback",
+ SND_SOC_NOPM, 0, 0, &hsl_mux_controls),
+ SND_SOC_DAPM_MUX("Headset Right Playback",
+ SND_SOC_NOPM, 0, 0, &hsr_mux_controls),
+
+ SND_SOC_DAPM_MUX("Vibra Left Playback", SND_SOC_NOPM, 0, 0,
+ &vibral_mux_controls),
+ SND_SOC_DAPM_MUX("Vibra Right Playback", SND_SOC_NOPM, 0, 0,
+ &vibrar_mux_controls),
+
+ SND_SOC_DAPM_SWITCH("Earphone Playback", SND_SOC_NOPM, 0, 0,
+ &ep_path_enable_control),
+ SND_SOC_DAPM_SWITCH("AUXL Playback", SND_SOC_NOPM, 0, 0,
+ &auxl_switch_control),
+ SND_SOC_DAPM_SWITCH("AUXR Playback", SND_SOC_NOPM, 0, 0,
+ &auxr_switch_control),
+
+ /* Analog playback drivers */
+ SND_SOC_DAPM_OUT_DRV("HF Left Driver",
+ TWL6040_REG_HFLCTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("HF Right Driver",
+ TWL6040_REG_HFRCTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("HS Left Driver",
+ TWL6040_REG_HSLCTL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("HS Right Driver",
+ TWL6040_REG_HSRCTL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV_E("Earphone Driver",
+ TWL6040_REG_EARCTL, 0, 0, NULL, 0,
+ twl6040_ep_drv_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUT_DRV("Vibra Left Driver",
+ TWL6040_REG_VIBCTLL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Vibra Right Driver",
+ TWL6040_REG_VIBCTLR, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Vibra Left Control", TWL6040_REG_VIBCTLL, 2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Vibra Right Control", TWL6040_REG_VIBCTLR, 2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("HSDAC Power", 1, SND_SOC_NOPM, 0, 0,
+ twl6040_hs_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Analog playback PGAs */
+ SND_SOC_DAPM_PGA("HF Left PGA",
+ TWL6040_REG_HFLCTL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HF Right PGA",
+ TWL6040_REG_HFRCTL, 1, 0, NULL, 0),
+
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ /* Stream -> DAC mapping */
+ {"HSDAC Left", NULL, "Legacy Playback"},
+ {"HSDAC Left", NULL, "Headset Playback"},
+ {"HSDAC Right", NULL, "Legacy Playback"},
+ {"HSDAC Right", NULL, "Headset Playback"},
+
+ {"HFDAC Left", NULL, "Legacy Playback"},
+ {"HFDAC Left", NULL, "Handsfree Playback"},
+ {"HFDAC Right", NULL, "Legacy Playback"},
+ {"HFDAC Right", NULL, "Handsfree Playback"},
+
+ {"VIBRA DAC", NULL, "Legacy Playback"},
+ {"VIBRA DAC", NULL, "Vibra Playback"},
+
+ /* ADC -> Stream mapping */
+ {"Legacy Capture" , NULL, "ADC Left"},
+ {"Capture", NULL, "ADC Left"},
+ {"Legacy Capture", NULL, "ADC Right"},
+ {"Capture" , NULL, "ADC Right"},
+
+ /* Capture path */
+ {"Analog Left Capture Route", "Headset Mic", "HSMIC"},
+ {"Analog Left Capture Route", "Main Mic", "MAINMIC"},
+ {"Analog Left Capture Route", "Aux/FM Left", "AFML"},
+
+ {"Analog Right Capture Route", "Headset Mic", "HSMIC"},
+ {"Analog Right Capture Route", "Sub Mic", "SUBMIC"},
+ {"Analog Right Capture Route", "Aux/FM Right", "AFMR"},
+
+ {"MicAmpL", NULL, "Analog Left Capture Route"},
+ {"MicAmpR", NULL, "Analog Right Capture Route"},
+
+ {"ADC Left", NULL, "MicAmpL"},
+ {"ADC Right", NULL, "MicAmpR"},
+
+ /* AFM path */
+ {"AFMAmpL", NULL, "AFML"},
+ {"AFMAmpR", NULL, "AFMR"},
+
+ {"HSDAC Left", NULL, "HSDAC Power"},
+ {"HSDAC Right", NULL, "HSDAC Power"},
+
+ {"Headset Left Playback", "HS DAC", "HSDAC Left"},
+ {"Headset Left Playback", "Line-In amp", "AFMAmpL"},
+
+ {"Headset Right Playback", "HS DAC", "HSDAC Right"},
+ {"Headset Right Playback", "Line-In amp", "AFMAmpR"},
+
+ {"HS Left Driver", NULL, "Headset Left Playback"},
+ {"HS Right Driver", NULL, "Headset Right Playback"},
+
+ {"HSOL", NULL, "HS Left Driver"},
+ {"HSOR", NULL, "HS Right Driver"},
+
+ /* Earphone playback path */
+ {"Earphone Playback", "Switch", "HSDAC Left"},
+ {"Earphone Driver", NULL, "Earphone Playback"},
+ {"EP", NULL, "Earphone Driver"},
+
+ {"Handsfree Left Playback", "HF DAC", "HFDAC Left"},
+ {"Handsfree Left Playback", "Line-In amp", "AFMAmpL"},
+
+ {"Handsfree Right Playback", "HF DAC", "HFDAC Right"},
+ {"Handsfree Right Playback", "Line-In amp", "AFMAmpR"},
+
+ {"HF Left PGA", NULL, "Handsfree Left Playback"},
+ {"HF Right PGA", NULL, "Handsfree Right Playback"},
+
+ {"HF Left Driver", NULL, "HF Left PGA"},
+ {"HF Right Driver", NULL, "HF Right PGA"},
+
+ {"HFL", NULL, "HF Left Driver"},
+ {"HFR", NULL, "HF Right Driver"},
+
+ {"AUXL Playback", "Switch", "HF Left PGA"},
+ {"AUXR Playback", "Switch", "HF Right PGA"},
+
+ {"AUXL", NULL, "AUXL Playback"},
+ {"AUXR", NULL, "AUXR Playback"},
+
+ /* Vibrator paths */
+ {"Vibra Left Playback", "Audio PDM", "VIBRA DAC"},
+ {"Vibra Right Playback", "Audio PDM", "VIBRA DAC"},
+
+ {"Vibra Left Driver", NULL, "Vibra Left Playback"},
+ {"Vibra Right Driver", NULL, "Vibra Right Playback"},
+ {"Vibra Left Driver", NULL, "Vibra Left Control"},
+ {"Vibra Right Driver", NULL, "Vibra Right Control"},
+
+ {"VIBRAL", NULL, "Vibra Left Driver"},
+ {"VIBRAR", NULL, "Vibra Right Driver"},
+};
+
+static int twl6040_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (priv->codec_powered)
+ break;
+
+ ret = twl6040_power(twl6040, 1);
+ if (ret)
+ return ret;
+
+ priv->codec_powered = 1;
+
+ /* Set external boost GPO */
+ twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02);
+ break;
+ case SND_SOC_BIAS_OFF:
+ if (!priv->codec_powered)
+ break;
+
+ twl6040_power(twl6040, 0);
+ priv->codec_powered = 0;
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int twl6040_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &sysclk_constraints[priv->pll_power_mode]);
+
+ return 0;
+}
+
+static int twl6040_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ int rate;
+
+ rate = params_rate(params);
+ switch (rate) {
+ case 11250:
+ case 22500:
+ case 44100:
+ case 88200:
+ /* These rates are not supported when HPPLL is in use */
+ if (unlikely(priv->pll == TWL6040_SYSCLK_SEL_HPPLL)) {
+ dev_err(codec->dev, "HPPLL does not support rate %d\n",
+ rate);
+ return -EINVAL;
+ }
+ priv->sysclk = 17640000;
+ break;
+ case 8000:
+ case 16000:
+ case 32000:
+ case 48000:
+ case 96000:
+ priv->sysclk = 19200000;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported rate %d\n", rate);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int twl6040_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ if (!priv->sysclk) {
+ dev_err(codec->dev,
+ "no mclk configured, call set_sysclk() on init\n");
+ return -EINVAL;
+ }
+
+ ret = twl6040_set_pll(twl6040, priv->pll, priv->clk_in, priv->sysclk);
+ if (ret) {
+ dev_err(codec->dev, "Can not set PLL (%d)\n", ret);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case TWL6040_SYSCLK_SEL_LPPLL:
+ case TWL6040_SYSCLK_SEL_HPPLL:
+ priv->pll = clk_id;
+ priv->clk_in = freq;
+ break;
+ default:
+ dev_err(codec->dev, "unknown clk_id %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id id,
+ int mute)
+{
+ struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ int hslctl, hsrctl, earctl;
+ int hflctl, hfrctl;
+
+ switch (id) {
+ case TWL6040_DAI_DL1:
+ hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL);
+ hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL);
+ earctl = twl6040_read(codec, TWL6040_REG_EARCTL);
+
+ if (mute) {
+ /* Power down drivers and DACs */
+ earctl &= ~0x01;
+ hslctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);
+ hsrctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);
+
+ }
+
+ twl6040_reg_write(twl6040, TWL6040_REG_EARCTL, earctl);
+ twl6040_reg_write(twl6040, TWL6040_REG_HSLCTL, hslctl);
+ twl6040_reg_write(twl6040, TWL6040_REG_HSRCTL, hsrctl);
+ priv->dl1_unmuted = !mute;
+ break;
+ case TWL6040_DAI_DL2:
+ hflctl = twl6040_read(codec, TWL6040_REG_HFLCTL);
+ hfrctl = twl6040_read(codec, TWL6040_REG_HFRCTL);
+
+ if (mute) {
+ /* Power down drivers and DACs */
+ hflctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
+ TWL6040_HFDRVENA);
+ hfrctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
+ TWL6040_HFDRVENA);
+ }
+
+ twl6040_reg_write(twl6040, TWL6040_REG_HFLCTL, hflctl);
+ twl6040_reg_write(twl6040, TWL6040_REG_HFRCTL, hfrctl);
+ priv->dl2_unmuted = !mute;
+ break;
+ default:
+ break;
+ }
+}
+
+static int twl6040_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ switch (dai->id) {
+ case TWL6040_DAI_LEGACY:
+ twl6040_mute_path(dai->codec, TWL6040_DAI_DL1, mute);
+ twl6040_mute_path(dai->codec, TWL6040_DAI_DL2, mute);
+ break;
+ case TWL6040_DAI_DL1:
+ case TWL6040_DAI_DL2:
+ twl6040_mute_path(dai->codec, dai->id, mute);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops twl6040_dai_ops = {
+ .startup = twl6040_startup,
+ .hw_params = twl6040_hw_params,
+ .prepare = twl6040_prepare,
+ .set_sysclk = twl6040_set_dai_sysclk,
+ .digital_mute = twl6040_digital_mute,
+};
+
+static struct snd_soc_dai_driver twl6040_dai[] = {
+{
+ .name = "twl6040-legacy",
+ .id = TWL6040_DAI_LEGACY,
+ .playback = {
+ .stream_name = "Legacy Playback",
+ .channels_min = 1,
+ .channels_max = 5,
+ .rates = TWL6040_RATES,
+ .formats = TWL6040_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Legacy Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TWL6040_RATES,
+ .formats = TWL6040_FORMATS,
+ },
+ .ops = &twl6040_dai_ops,
+},
+{
+ .name = "twl6040-ul",
+ .id = TWL6040_DAI_UL,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TWL6040_RATES,
+ .formats = TWL6040_FORMATS,
+ },
+ .ops = &twl6040_dai_ops,
+},
+{
+ .name = "twl6040-dl1",
+ .id = TWL6040_DAI_DL1,
+ .playback = {
+ .stream_name = "Headset Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TWL6040_RATES,
+ .formats = TWL6040_FORMATS,
+ },
+ .ops = &twl6040_dai_ops,
+},
+{
+ .name = "twl6040-dl2",
+ .id = TWL6040_DAI_DL2,
+ .playback = {
+ .stream_name = "Handsfree Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TWL6040_RATES,
+ .formats = TWL6040_FORMATS,
+ },
+ .ops = &twl6040_dai_ops,
+},
+{
+ .name = "twl6040-vib",
+ .id = TWL6040_DAI_VIB,
+ .playback = {
+ .stream_name = "Vibra Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .formats = TWL6040_FORMATS,
+ },
+ .ops = &twl6040_dai_ops,
+},
+};
+
+static int twl6040_probe(struct snd_soc_codec *codec)
+{
+ struct twl6040_data *priv;
+ struct twl6040 *twl6040 = dev_get_drvdata(codec->dev->parent);
+ struct platform_device *pdev = container_of(codec->dev,
+ struct platform_device, dev);
+ int ret = 0;
+
+ priv = devm_kzalloc(codec->dev, sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ snd_soc_codec_set_drvdata(codec, priv);
+
+ priv->codec = codec;
+ codec->control_data = twl6040;
+
+ priv->plug_irq = platform_get_irq(pdev, 0);
+ if (priv->plug_irq < 0) {
+ dev_err(codec->dev, "invalid irq\n");
+ return -EINVAL;
+ }
+
+ INIT_DELAYED_WORK(&priv->hs_jack.work, twl6040_accessory_work);
+
+ mutex_init(&priv->mutex);
+
+ ret = request_threaded_irq(priv->plug_irq, NULL,
+ twl6040_audio_handler, IRQF_NO_SUSPEND,
+ "twl6040_irq_plug", codec);
+ if (ret) {
+ dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret);
+ return ret;
+ }
+
+ twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ twl6040_init_chip(codec);
+
+ return 0;
+}
+
+static int twl6040_remove(struct snd_soc_codec *codec)
+{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ free_irq(priv->plug_irq, codec);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_twl6040 = {
+ .probe = twl6040_probe,
+ .remove = twl6040_remove,
+ .read = twl6040_read,
+ .write = twl6040_write,
+ .set_bias_level = twl6040_set_bias_level,
+ .suspend_bias_off = true,
+ .ignore_pmdown_time = true,
+
+ .controls = twl6040_snd_controls,
+ .num_controls = ARRAY_SIZE(twl6040_snd_controls),
+ .dapm_widgets = twl6040_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+};
+
+static int twl6040_codec_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl6040,
+ twl6040_dai, ARRAY_SIZE(twl6040_dai));
+}
+
+static int twl6040_codec_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver twl6040_codec_driver = {
+ .driver = {
+ .name = "twl6040-codec",
+ },
+ .probe = twl6040_codec_probe,
+ .remove = twl6040_codec_remove,
+};
+
+module_platform_driver(twl6040_codec_driver);
+
+MODULE_DESCRIPTION("ASoC TWL6040 codec driver");
+MODULE_AUTHOR("Misael Lopez Cruz");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/twl6040.h b/kernel/sound/soc/codecs/twl6040.h
new file mode 100644
index 000000000..0611406ca
--- /dev/null
+++ b/kernel/sound/soc/codecs/twl6040.h
@@ -0,0 +1,44 @@
+/*
+ * ALSA SoC TWL6040 codec driver
+ *
+ * Author: Misael Lopez Cruz <x0052729@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
+ *
+ */
+
+#ifndef __TWL6040_H__
+#define __TWL6040_H__
+
+enum twl6040_trim {
+ TWL6040_TRIM_TRIM1 = 0,
+ TWL6040_TRIM_TRIM2,
+ TWL6040_TRIM_TRIM3,
+ TWL6040_TRIM_HSOTRIM,
+ TWL6040_TRIM_HFOTRIM,
+ TWL6040_TRIM_INVAL,
+};
+
+#define TWL6040_HSF_TRIM_LEFT(x) (x & 0x0f)
+#define TWL6040_HSF_TRIM_RIGHT(x) ((x >> 4) & 0x0f)
+
+int twl6040_get_dl1_gain(struct snd_soc_codec *codec);
+void twl6040_hs_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack, int report);
+int twl6040_get_clk_id(struct snd_soc_codec *codec);
+int twl6040_get_trim_value(struct snd_soc_codec *codec, enum twl6040_trim trim);
+int twl6040_get_hs_step_size(struct snd_soc_codec *codec);
+
+#endif /* End of __TWL6040_H__ */
diff --git a/kernel/sound/soc/codecs/uda134x.c b/kernel/sound/soc/codecs/uda134x.c
new file mode 100644
index 000000000..f883308c0
--- /dev/null
+++ b/kernel/sound/soc/codecs/uda134x.c
@@ -0,0 +1,617 @@
+/*
+ * uda134x.c -- UDA134X ALSA SoC Codec driver
+ *
+ * Modifications by Christian Pellegrin <chripell@evolware.org>
+ *
+ * Copyright 2007 Dension Audio Systems Ltd.
+ * Author: Zoltan Devai
+ *
+ * Based on the WM87xx drivers by Liam Girdwood and 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>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#include <sound/uda134x.h>
+#include <sound/l3.h>
+
+#include "uda134x.h"
+
+
+#define UDA134X_RATES SNDRV_PCM_RATE_8000_48000
+#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
+
+struct uda134x_priv {
+ int sysclk;
+ int dai_fmt;
+
+ struct snd_pcm_substream *master_substream;
+ struct snd_pcm_substream *slave_substream;
+};
+
+/* In-data addresses are hard-coded into the reg-cache values */
+static const char uda134x_reg[UDA134X_REGS_NUM] = {
+ /* Extended address registers */
+ 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* Status, data regs */
+ 0x00, 0x83, 0x00, 0x40, 0x80, 0xC0, 0x00,
+};
+
+/*
+ * The codec has no support for reading its registers except for peak level...
+ */
+static inline unsigned int uda134x_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 *cache = codec->reg_cache;
+
+ if (reg >= UDA134X_REGS_NUM)
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * Write the register cache
+ */
+static inline void uda134x_write_reg_cache(struct snd_soc_codec *codec,
+ u8 reg, unsigned int value)
+{
+ u8 *cache = codec->reg_cache;
+
+ if (reg >= UDA134X_REGS_NUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * Write to the uda134x registers
+ *
+ */
+static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ int ret;
+ u8 addr;
+ u8 data = value;
+ struct uda134x_platform_data *pd = codec->control_data;
+
+ pr_debug("%s reg: %02X, value:%02X\n", __func__, reg, value);
+
+ if (reg >= UDA134X_REGS_NUM) {
+ printk(KERN_ERR "%s unknown register: reg: %u",
+ __func__, reg);
+ return -EINVAL;
+ }
+
+ uda134x_write_reg_cache(codec, reg, value);
+
+ switch (reg) {
+ case UDA134X_STATUS0:
+ case UDA134X_STATUS1:
+ addr = UDA134X_STATUS_ADDR;
+ break;
+ case UDA134X_DATA000:
+ case UDA134X_DATA001:
+ case UDA134X_DATA010:
+ case UDA134X_DATA011:
+ addr = UDA134X_DATA0_ADDR;
+ break;
+ case UDA134X_DATA1:
+ addr = UDA134X_DATA1_ADDR;
+ break;
+ default:
+ /* It's an extended address register */
+ addr = (reg | UDA134X_EXTADDR_PREFIX);
+
+ ret = l3_write(&pd->l3,
+ UDA134X_DATA0_ADDR, &addr, 1);
+ if (ret != 1)
+ return -EIO;
+
+ addr = UDA134X_DATA0_ADDR;
+ data = (value | UDA134X_EXTDATA_PREFIX);
+ break;
+ }
+
+ ret = l3_write(&pd->l3,
+ addr, &data, 1);
+ if (ret != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static inline void uda134x_reset(struct snd_soc_codec *codec)
+{
+ u8 reset_reg = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
+ uda134x_write(codec, UDA134X_STATUS0, reset_reg | (1<<6));
+ msleep(1);
+ uda134x_write(codec, UDA134X_STATUS0, reset_reg & ~(1<<6));
+}
+
+static int uda134x_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 mute_reg = uda134x_read_reg_cache(codec, UDA134X_DATA010);
+
+ pr_debug("%s mute: %d\n", __func__, mute);
+
+ if (mute)
+ mute_reg |= (1<<2);
+ else
+ mute_reg &= ~(1<<2);
+
+ uda134x_write(codec, UDA134X_DATA010, mute_reg);
+
+ return 0;
+}
+
+static int uda134x_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
+ struct snd_pcm_runtime *master_runtime;
+
+ if (uda134x->master_substream) {
+ master_runtime = uda134x->master_substream->runtime;
+
+ pr_debug("%s constraining to %d bits at %d\n", __func__,
+ master_runtime->sample_bits,
+ master_runtime->rate);
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ master_runtime->rate,
+ master_runtime->rate);
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ master_runtime->sample_bits,
+ master_runtime->sample_bits);
+
+ uda134x->slave_substream = substream;
+ } else
+ uda134x->master_substream = substream;
+
+ return 0;
+}
+
+static void uda134x_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
+
+ if (uda134x->master_substream == substream)
+ uda134x->master_substream = uda134x->slave_substream;
+
+ uda134x->slave_substream = NULL;
+}
+
+static int uda134x_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 uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
+ u8 hw_params;
+
+ if (substream == uda134x->slave_substream) {
+ pr_debug("%s ignoring hw_params for slave substream\n",
+ __func__);
+ return 0;
+ }
+
+ hw_params = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
+ hw_params &= STATUS0_SYSCLK_MASK;
+ hw_params &= STATUS0_DAIFMT_MASK;
+
+ pr_debug("%s sysclk: %d, rate:%d\n", __func__,
+ uda134x->sysclk, params_rate(params));
+
+ /* set SYSCLK / fs ratio */
+ switch (uda134x->sysclk / params_rate(params)) {
+ case 512:
+ break;
+ case 384:
+ hw_params |= (1<<4);
+ break;
+ case 256:
+ hw_params |= (1<<5);
+ break;
+ default:
+ printk(KERN_ERR "%s unsupported fs\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s dai_fmt: %d, params_format:%d\n", __func__,
+ uda134x->dai_fmt, params_format(params));
+
+ /* set DAI format and word length */
+ switch (uda134x->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ hw_params |= (1<<1);
+ break;
+ case 18:
+ hw_params |= (1<<2);
+ break;
+ case 20:
+ hw_params |= ((1<<2) | (1<<1));
+ break;
+ default:
+ printk(KERN_ERR "%s unsupported format (right)\n",
+ __func__);
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ hw_params |= (1<<3);
+ break;
+ default:
+ printk(KERN_ERR "%s unsupported format\n", __func__);
+ return -EINVAL;
+ }
+
+ uda134x_write(codec, UDA134X_STATUS0, hw_params);
+
+ return 0;
+}
+
+static int uda134x_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 uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s clk_id: %d, freq: %u, dir: %d\n", __func__,
+ clk_id, freq, dir);
+
+ /* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
+ because the codec is slave. Of course limitations of the clock
+ master (the IIS controller) apply.
+ We'll error out on set_hw_params if it's not OK */
+ if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
+ uda134x->sysclk = freq;
+ return 0;
+ }
+
+ printk(KERN_ERR "%s unsupported sysclk\n", __func__);
+ return -EINVAL;
+}
+
+static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s fmt: %08X\n", __func__, fmt);
+
+ /* codec supports only full slave mode */
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+ printk(KERN_ERR "%s unsupported slave mode\n", __func__);
+ return -EINVAL;
+ }
+
+ /* no support for clock inversion */
+ if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
+ printk(KERN_ERR "%s unsupported clock inversion\n", __func__);
+ return -EINVAL;
+ }
+
+ /* We can't setup DAI format here as it depends on the word bit num */
+ /* so let's just store the value for later */
+ uda134x->dai_fmt = fmt;
+
+ return 0;
+}
+
+static int uda134x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct uda134x_platform_data *pd = codec->control_data;
+ int i;
+ u8 *cache = codec->reg_cache;
+
+ pr_debug("%s bias level %d\n", __func__, level);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* power on */
+ if (pd->power) {
+ pd->power(1);
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(uda134x_reg); i++)
+ codec->driver->write(codec, i, *cache++);
+ }
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* power off */
+ if (pd->power)
+ pd->power(0);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static const char *uda134x_dsp_setting[] = {"Flat", "Minimum1",
+ "Minimum2", "Maximum"};
+static const char *uda134x_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static const char *uda134x_mixmode[] = {"Differential", "Analog1",
+ "Analog2", "Both"};
+
+static const struct soc_enum uda134x_mixer_enum[] = {
+SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting),
+SOC_ENUM_SINGLE(UDA134X_DATA010, 3, 0x04, uda134x_deemph),
+SOC_ENUM_SINGLE(UDA134X_EA010, 0, 0x04, uda134x_mixmode),
+};
+
+static const struct snd_kcontrol_new uda1341_snd_controls[] = {
+SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
+SOC_SINGLE("Capture Volume", UDA134X_EA010, 2, 0x07, 0),
+SOC_SINGLE("Analog1 Volume", UDA134X_EA000, 0, 0x1F, 1),
+SOC_SINGLE("Analog2 Volume", UDA134X_EA001, 0, 0x1F, 1),
+
+SOC_SINGLE("Mic Sensitivity", UDA134X_EA010, 2, 7, 0),
+SOC_SINGLE("Mic Volume", UDA134X_EA101, 0, 0x1F, 0),
+
+SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
+SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
+
+SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
+SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
+SOC_ENUM("Input Mux", uda134x_mixer_enum[2]),
+
+SOC_SINGLE("AGC Switch", UDA134X_EA100, 4, 1, 0),
+SOC_SINGLE("AGC Target Volume", UDA134X_EA110, 0, 0x03, 1),
+SOC_SINGLE("AGC Timing", UDA134X_EA110, 2, 0x07, 0),
+
+SOC_SINGLE("DAC +6dB Switch", UDA134X_STATUS1, 6, 1, 0),
+SOC_SINGLE("ADC +6dB Switch", UDA134X_STATUS1, 5, 1, 0),
+SOC_SINGLE("ADC Polarity Switch", UDA134X_STATUS1, 4, 1, 0),
+SOC_SINGLE("DAC Polarity Switch", UDA134X_STATUS1, 3, 1, 0),
+SOC_SINGLE("Double Speed Playback Switch", UDA134X_STATUS1, 2, 1, 0),
+SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new uda1340_snd_controls[] = {
+SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
+
+SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
+SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
+
+SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
+SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
+
+SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new uda1345_snd_controls[] = {
+SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
+
+SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
+
+SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
+};
+
+/* UDA1341 has the DAC/ADC power down in STATUS1 */
+static const struct snd_soc_dapm_widget uda1341_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_STATUS1, 0, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_STATUS1, 1, 0),
+};
+
+/* UDA1340/4/5 has the DAC/ADC pwoer down in DATA0 11 */
+static const struct snd_soc_dapm_widget uda1340_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_DATA011, 0, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_DATA011, 1, 0),
+};
+
+/* Common DAPM widgets */
+static const struct snd_soc_dapm_widget uda134x_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_OUTPUT("VOUTL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route uda134x_dapm_routes[] = {
+ { "ADC", NULL, "VINL1" },
+ { "ADC", NULL, "VINR1" },
+ { "ADC", NULL, "VINL2" },
+ { "ADC", NULL, "VINR2" },
+ { "VOUTL", NULL, "DAC" },
+ { "VOUTR", NULL, "DAC" },
+};
+
+static const struct snd_soc_dai_ops uda134x_dai_ops = {
+ .startup = uda134x_startup,
+ .shutdown = uda134x_shutdown,
+ .hw_params = uda134x_hw_params,
+ .digital_mute = uda134x_mute,
+ .set_sysclk = uda134x_set_dai_sysclk,
+ .set_fmt = uda134x_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver uda134x_dai = {
+ .name = "uda134x-hifi",
+ /* playback capabilities */
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA134X_RATES,
+ .formats = UDA134X_FORMATS,
+ },
+ /* capture capabilities */
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA134X_RATES,
+ .formats = UDA134X_FORMATS,
+ },
+ /* pcm operations */
+ .ops = &uda134x_dai_ops,
+};
+
+static int uda134x_soc_probe(struct snd_soc_codec *codec)
+{
+ struct uda134x_priv *uda134x;
+ struct uda134x_platform_data *pd = codec->component.card->dev->platform_data;
+ const struct snd_soc_dapm_widget *widgets;
+ unsigned num_widgets;
+
+ int ret;
+
+ printk(KERN_INFO "UDA134X SoC Audio Codec\n");
+
+ if (!pd) {
+ printk(KERN_ERR "UDA134X SoC codec: "
+ "missing L3 bitbang function\n");
+ return -ENODEV;
+ }
+
+ switch (pd->model) {
+ case UDA134X_UDA1340:
+ case UDA134X_UDA1341:
+ case UDA134X_UDA1344:
+ case UDA134X_UDA1345:
+ break;
+ default:
+ printk(KERN_ERR "UDA134X SoC codec: "
+ "unsupported model %d\n",
+ pd->model);
+ return -EINVAL;
+ }
+
+ uda134x = kzalloc(sizeof(struct uda134x_priv), GFP_KERNEL);
+ if (uda134x == NULL)
+ return -ENOMEM;
+ snd_soc_codec_set_drvdata(codec, uda134x);
+
+ codec->control_data = pd;
+
+ if (pd->power)
+ pd->power(1);
+
+ uda134x_reset(codec);
+
+ if (pd->model == UDA134X_UDA1341) {
+ widgets = uda1341_dapm_widgets;
+ num_widgets = ARRAY_SIZE(uda1341_dapm_widgets);
+ } else {
+ widgets = uda1340_dapm_widgets;
+ num_widgets = ARRAY_SIZE(uda1340_dapm_widgets);
+ }
+
+ ret = snd_soc_dapm_new_controls(&codec->dapm, widgets, num_widgets);
+ if (ret) {
+ printk(KERN_ERR "%s failed to register dapm controls: %d",
+ __func__, ret);
+ kfree(uda134x);
+ return ret;
+ }
+
+ switch (pd->model) {
+ case UDA134X_UDA1340:
+ case UDA134X_UDA1344:
+ ret = snd_soc_add_codec_controls(codec, uda1340_snd_controls,
+ ARRAY_SIZE(uda1340_snd_controls));
+ break;
+ case UDA134X_UDA1341:
+ ret = snd_soc_add_codec_controls(codec, uda1341_snd_controls,
+ ARRAY_SIZE(uda1341_snd_controls));
+ break;
+ case UDA134X_UDA1345:
+ ret = snd_soc_add_codec_controls(codec, uda1345_snd_controls,
+ ARRAY_SIZE(uda1345_snd_controls));
+ break;
+ default:
+ printk(KERN_ERR "%s unknown codec type: %d",
+ __func__, pd->model);
+ kfree(uda134x);
+ return -EINVAL;
+ }
+
+ if (ret < 0) {
+ printk(KERN_ERR "UDA134X: failed to register controls\n");
+ kfree(uda134x);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* power down chip */
+static int uda134x_soc_remove(struct snd_soc_codec *codec)
+{
+ struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
+
+ kfree(uda134x);
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
+ .probe = uda134x_soc_probe,
+ .remove = uda134x_soc_remove,
+ .reg_cache_size = sizeof(uda134x_reg),
+ .reg_word_size = sizeof(u8),
+ .reg_cache_default = uda134x_reg,
+ .reg_cache_step = 1,
+ .read = uda134x_read_reg_cache,
+ .set_bias_level = uda134x_set_bias_level,
+ .suspend_bias_off = true,
+
+ .dapm_widgets = uda134x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),
+ .dapm_routes = uda134x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes),
+};
+
+static int uda134x_codec_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_uda134x, &uda134x_dai, 1);
+}
+
+static int uda134x_codec_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver uda134x_codec_driver = {
+ .driver = {
+ .name = "uda134x-codec",
+ },
+ .probe = uda134x_codec_probe,
+ .remove = uda134x_codec_remove,
+};
+
+module_platform_driver(uda134x_codec_driver);
+
+MODULE_DESCRIPTION("UDA134X ALSA soc codec driver");
+MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/uda134x.h b/kernel/sound/soc/codecs/uda134x.h
new file mode 100644
index 000000000..9faae0697
--- /dev/null
+++ b/kernel/sound/soc/codecs/uda134x.h
@@ -0,0 +1,34 @@
+#ifndef _UDA134X_CODEC_H
+#define _UDA134X_CODEC_H
+
+#define UDA134X_L3ADDR 5
+#define UDA134X_DATA0_ADDR ((UDA134X_L3ADDR << 2) | 0)
+#define UDA134X_DATA1_ADDR ((UDA134X_L3ADDR << 2) | 1)
+#define UDA134X_STATUS_ADDR ((UDA134X_L3ADDR << 2) | 2)
+
+#define UDA134X_EXTADDR_PREFIX 0xC0
+#define UDA134X_EXTDATA_PREFIX 0xE0
+
+/* UDA134X registers */
+#define UDA134X_EA000 0
+#define UDA134X_EA001 1
+#define UDA134X_EA010 2
+#define UDA134X_EA011 3
+#define UDA134X_EA100 4
+#define UDA134X_EA101 5
+#define UDA134X_EA110 6
+#define UDA134X_EA111 7
+#define UDA134X_STATUS0 8
+#define UDA134X_STATUS1 9
+#define UDA134X_DATA000 10
+#define UDA134X_DATA001 11
+#define UDA134X_DATA010 12
+#define UDA134X_DATA011 13
+#define UDA134X_DATA1 14
+
+#define UDA134X_REGS_NUM 15
+
+#define STATUS0_DAIFMT_MASK (~(7<<1))
+#define STATUS0_SYSCLK_MASK (~(3<<4))
+
+#endif
diff --git a/kernel/sound/soc/codecs/uda1380.c b/kernel/sound/soc/codecs/uda1380.c
new file mode 100644
index 000000000..c3c33bd0d
--- /dev/null
+++ b/kernel/sound/soc/codecs/uda1380.c
@@ -0,0 +1,847 @@
+/*
+ * uda1380.c - Philips UDA1380 ALSA SoC audio driver
+ *
+ * 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.
+ *
+ * Copyright (c) 2007-2009 Philipp Zabel <philipp.zabel@gmail.com>
+ *
+ * Modified by Richard Purdie <richard@openedhand.com> to fit into SoC
+ * codec model.
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ * Copyright 2005 Openedhand Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/uda1380.h>
+
+#include "uda1380.h"
+
+/* codec private data */
+struct uda1380_priv {
+ struct snd_soc_codec *codec;
+ unsigned int dac_clk;
+ struct work_struct work;
+ void *control_data;
+};
+
+/*
+ * uda1380 register cache
+ */
+static const u16 uda1380_reg[UDA1380_CACHEREGNUM] = {
+ 0x0502, 0x0000, 0x0000, 0x3f3f,
+ 0x0202, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0xff00, 0x0000, 0x4800,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x8000, 0x0002, 0x0000,
+};
+
+static unsigned long uda1380_cache_dirty;
+
+/*
+ * read uda1380 register cache
+ */
+static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg == UDA1380_RESET)
+ return 0;
+ if (reg >= UDA1380_CACHEREGNUM)
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * write uda1380 register cache
+ */
+static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+
+ if (reg >= UDA1380_CACHEREGNUM)
+ return;
+ if ((reg >= 0x10) && (cache[reg] != value))
+ set_bit(reg - 0x10, &uda1380_cache_dirty);
+ cache[reg] = value;
+}
+
+/*
+ * write to the UDA1380 register space
+ */
+static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[3];
+
+ /* data is
+ * data[0] is register offset
+ * data[1] is MS byte
+ * data[2] is LS byte
+ */
+ data[0] = reg;
+ data[1] = (value & 0xff00) >> 8;
+ data[2] = value & 0x00ff;
+
+ uda1380_write_reg_cache(codec, reg, value);
+
+ /* the interpolator & decimator regs must only be written when the
+ * codec DAI is active.
+ */
+ if (!snd_soc_codec_is_active(codec) && (reg >= UDA1380_MVOL))
+ return 0;
+ pr_debug("uda1380: hw write %x val %x\n", reg, value);
+ if (codec->hw_write(codec->control_data, data, 3) == 3) {
+ unsigned int val;
+ i2c_master_send(codec->control_data, data, 1);
+ i2c_master_recv(codec->control_data, data, 2);
+ val = (data[0]<<8) | data[1];
+ if (val != value) {
+ pr_debug("uda1380: READ BACK VAL %x\n",
+ (data[0]<<8) | data[1]);
+ return -EIO;
+ }
+ if (reg >= 0x10)
+ clear_bit(reg - 0x10, &uda1380_cache_dirty);
+ return 0;
+ } else
+ return -EIO;
+}
+
+static void uda1380_sync_cache(struct snd_soc_codec *codec)
+{
+ int reg;
+ u8 data[3];
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (reg = 0; reg < UDA1380_MVOL; reg++) {
+ data[0] = reg;
+ data[1] = (cache[reg] & 0xff00) >> 8;
+ data[2] = cache[reg] & 0x00ff;
+ if (codec->hw_write(codec->control_data, data, 3) != 3)
+ dev_err(codec->dev, "%s: write to reg 0x%x failed\n",
+ __func__, reg);
+ }
+}
+
+static int uda1380_reset(struct snd_soc_codec *codec)
+{
+ struct uda1380_platform_data *pdata = codec->dev->platform_data;
+
+ if (gpio_is_valid(pdata->gpio_reset)) {
+ gpio_set_value(pdata->gpio_reset, 1);
+ mdelay(1);
+ gpio_set_value(pdata->gpio_reset, 0);
+ } else {
+ u8 data[3];
+
+ data[0] = UDA1380_RESET;
+ data[1] = 0;
+ data[2] = 0;
+
+ if (codec->hw_write(codec->control_data, data, 3) != 3) {
+ dev_err(codec->dev, "%s: failed\n", __func__);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static void uda1380_flush_work(struct work_struct *work)
+{
+ struct uda1380_priv *uda1380 = container_of(work, struct uda1380_priv, work);
+ struct snd_soc_codec *uda1380_codec = uda1380->codec;
+ int bit, reg;
+
+ for_each_set_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) {
+ reg = 0x10 + bit;
+ pr_debug("uda1380: flush reg %x val %x:\n", reg,
+ uda1380_read_reg_cache(uda1380_codec, reg));
+ uda1380_write(uda1380_codec, reg,
+ uda1380_read_reg_cache(uda1380_codec, reg));
+ clear_bit(bit, &uda1380_cache_dirty);
+ }
+
+}
+
+/* declarations of ALSA reg_elem_REAL controls */
+static const char *uda1380_deemp[] = {
+ "None",
+ "32kHz",
+ "44.1kHz",
+ "48kHz",
+ "96kHz",
+};
+static const char *uda1380_input_sel[] = {
+ "Line",
+ "Mic + Line R",
+ "Line L",
+ "Mic",
+};
+static const char *uda1380_output_sel[] = {
+ "DAC",
+ "Analog Mixer",
+};
+static const char *uda1380_spf_mode[] = {
+ "Flat",
+ "Minimum1",
+ "Minimum2",
+ "Maximum"
+};
+static const char *uda1380_capture_sel[] = {
+ "ADC",
+ "Digital Mixer"
+};
+static const char *uda1380_sel_ns[] = {
+ "3rd-order",
+ "5th-order"
+};
+static const char *uda1380_mix_control[] = {
+ "off",
+ "PCM only",
+ "before sound processing",
+ "after sound processing"
+};
+static const char *uda1380_sdet_setting[] = {
+ "3200",
+ "4800",
+ "9600",
+ "19200"
+};
+static const char *uda1380_os_setting[] = {
+ "single-speed",
+ "double-speed (no mixing)",
+ "quad-speed (no mixing)"
+};
+
+static const struct soc_enum uda1380_deemp_enum[] = {
+ SOC_ENUM_SINGLE(UDA1380_DEEMP, 8, ARRAY_SIZE(uda1380_deemp),
+ uda1380_deemp),
+ SOC_ENUM_SINGLE(UDA1380_DEEMP, 0, ARRAY_SIZE(uda1380_deemp),
+ uda1380_deemp),
+};
+static SOC_ENUM_SINGLE_DECL(uda1380_input_sel_enum,
+ UDA1380_ADC, 2, uda1380_input_sel); /* SEL_MIC, SEL_LNA */
+static SOC_ENUM_SINGLE_DECL(uda1380_output_sel_enum,
+ UDA1380_PM, 7, uda1380_output_sel); /* R02_EN_AVC */
+static SOC_ENUM_SINGLE_DECL(uda1380_spf_enum,
+ UDA1380_MODE, 14, uda1380_spf_mode); /* M */
+static SOC_ENUM_SINGLE_DECL(uda1380_capture_sel_enum,
+ UDA1380_IFACE, 6, uda1380_capture_sel); /* SEL_SOURCE */
+static SOC_ENUM_SINGLE_DECL(uda1380_sel_ns_enum,
+ UDA1380_MIXER, 14, uda1380_sel_ns); /* SEL_NS */
+static SOC_ENUM_SINGLE_DECL(uda1380_mix_enum,
+ UDA1380_MIXER, 12, uda1380_mix_control); /* MIX, MIX_POS */
+static SOC_ENUM_SINGLE_DECL(uda1380_sdet_enum,
+ UDA1380_MIXER, 4, uda1380_sdet_setting); /* SD_VALUE */
+static SOC_ENUM_SINGLE_DECL(uda1380_os_enum,
+ UDA1380_MIXER, 0, uda1380_os_setting); /* OS */
+
+/*
+ * from -48 dB in 1.5 dB steps (mute instead of -49.5 dB)
+ */
+static DECLARE_TLV_DB_SCALE(amix_tlv, -4950, 150, 1);
+
+/*
+ * from -78 dB in 1 dB steps (3 dB steps, really. LSB are ignored),
+ * from -66 dB in 0.5 dB steps (2 dB steps, really) and
+ * from -52 dB in 0.25 dB steps
+ */
+static const unsigned int mvol_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 15, TLV_DB_SCALE_ITEM(-8200, 100, 1),
+ 16, 43, TLV_DB_SCALE_ITEM(-6600, 50, 0),
+ 44, 252, TLV_DB_SCALE_ITEM(-5200, 25, 0),
+};
+
+/*
+ * from -72 dB in 1.5 dB steps (6 dB steps really),
+ * from -66 dB in 0.75 dB steps (3 dB steps really),
+ * from -60 dB in 0.5 dB steps (2 dB steps really) and
+ * from -46 dB in 0.25 dB steps
+ */
+static const unsigned int vc_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 7, TLV_DB_SCALE_ITEM(-7800, 150, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(-6600, 75, 0),
+ 16, 43, TLV_DB_SCALE_ITEM(-6000, 50, 0),
+ 44, 228, TLV_DB_SCALE_ITEM(-4600, 25, 0),
+};
+
+/* from 0 to 6 dB in 2 dB steps if SPF mode != flat */
+static DECLARE_TLV_DB_SCALE(tr_tlv, 0, 200, 0);
+
+/* from 0 to 24 dB in 2 dB steps, if SPF mode == maximum, otherwise cuts
+ * off at 18 dB max) */
+static DECLARE_TLV_DB_SCALE(bb_tlv, 0, 200, 0);
+
+/* from -63 to 24 dB in 0.5 dB steps (-128...48) */
+static DECLARE_TLV_DB_SCALE(dec_tlv, -6400, 50, 1);
+
+/* from 0 to 24 dB in 3 dB steps */
+static DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
+
+/* from 0 to 30 dB in 2 dB steps */
+static DECLARE_TLV_DB_SCALE(vga_tlv, 0, 200, 0);
+
+static const struct snd_kcontrol_new uda1380_snd_controls[] = {
+ SOC_DOUBLE_TLV("Analog Mixer Volume", UDA1380_AMIX, 0, 8, 44, 1, amix_tlv), /* AVCR, AVCL */
+ SOC_DOUBLE_TLV("Master Playback Volume", UDA1380_MVOL, 0, 8, 252, 1, mvol_tlv), /* MVCL, MVCR */
+ SOC_SINGLE_TLV("ADC Playback Volume", UDA1380_MIXVOL, 8, 228, 1, vc_tlv), /* VC2 */
+ SOC_SINGLE_TLV("PCM Playback Volume", UDA1380_MIXVOL, 0, 228, 1, vc_tlv), /* VC1 */
+ SOC_ENUM("Sound Processing Filter", uda1380_spf_enum), /* M */
+ SOC_DOUBLE_TLV("Tone Control - Treble", UDA1380_MODE, 4, 12, 3, 0, tr_tlv), /* TRL, TRR */
+ SOC_DOUBLE_TLV("Tone Control - Bass", UDA1380_MODE, 0, 8, 15, 0, bb_tlv), /* BBL, BBR */
+/**/ SOC_SINGLE("Master Playback Switch", UDA1380_DEEMP, 14, 1, 1), /* MTM */
+ SOC_SINGLE("ADC Playback Switch", UDA1380_DEEMP, 11, 1, 1), /* MT2 from decimation filter */
+ SOC_ENUM("ADC Playback De-emphasis", uda1380_deemp_enum[0]), /* DE2 */
+ SOC_SINGLE("PCM Playback Switch", UDA1380_DEEMP, 3, 1, 1), /* MT1, from digital data input */
+ SOC_ENUM("PCM Playback De-emphasis", uda1380_deemp_enum[1]), /* DE1 */
+ SOC_SINGLE("DAC Polarity inverting Switch", UDA1380_MIXER, 15, 1, 0), /* DA_POL_INV */
+ SOC_ENUM("Noise Shaper", uda1380_sel_ns_enum), /* SEL_NS */
+ SOC_ENUM("Digital Mixer Signal Control", uda1380_mix_enum), /* MIX_POS, MIX */
+ SOC_SINGLE("Silence Detector Switch", UDA1380_MIXER, 6, 1, 0), /* SDET_ON */
+ SOC_ENUM("Silence Detector Setting", uda1380_sdet_enum), /* SD_VALUE */
+ SOC_ENUM("Oversampling Input", uda1380_os_enum), /* OS */
+ SOC_DOUBLE_S8_TLV("ADC Capture Volume", UDA1380_DEC, -128, 48, dec_tlv), /* ML_DEC, MR_DEC */
+/**/ SOC_SINGLE("ADC Capture Switch", UDA1380_PGA, 15, 1, 1), /* MT_ADC */
+ SOC_DOUBLE_TLV("Line Capture Volume", UDA1380_PGA, 0, 8, 8, 0, pga_tlv), /* PGA_GAINCTRLL, PGA_GAINCTRLR */
+ SOC_SINGLE("ADC Polarity inverting Switch", UDA1380_ADC, 12, 1, 0), /* ADCPOL_INV */
+ SOC_SINGLE_TLV("Mic Capture Volume", UDA1380_ADC, 8, 15, 0, vga_tlv), /* VGA_CTRL */
+ SOC_SINGLE("DC Filter Bypass Switch", UDA1380_ADC, 1, 1, 0), /* SKIP_DCFIL (before decimator) */
+ SOC_SINGLE("DC Filter Enable Switch", UDA1380_ADC, 0, 1, 0), /* EN_DCFIL (at output of decimator) */
+ SOC_SINGLE("AGC Timing", UDA1380_AGC, 8, 7, 0), /* TODO: enum, see table 62 */
+ SOC_SINGLE("AGC Target level", UDA1380_AGC, 2, 3, 1), /* AGC_LEVEL */
+ /* -5.5, -8, -11.5, -14 dBFS */
+ SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0),
+};
+
+/* Input mux */
+static const struct snd_kcontrol_new uda1380_input_mux_control =
+ SOC_DAPM_ENUM("Route", uda1380_input_sel_enum);
+
+/* Output mux */
+static const struct snd_kcontrol_new uda1380_output_mux_control =
+ SOC_DAPM_ENUM("Route", uda1380_output_sel_enum);
+
+/* Capture mux */
+static const struct snd_kcontrol_new uda1380_capture_mux_control =
+ SOC_DAPM_ENUM("Route", uda1380_capture_sel_enum);
+
+
+static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &uda1380_input_mux_control),
+ SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM, 0, 0,
+ &uda1380_output_mux_control),
+ SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0,
+ &uda1380_capture_mux_control),
+ SND_SOC_DAPM_PGA("Left PGA", UDA1380_PM, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right PGA", UDA1380_PM, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic LNA", UDA1380_PM, 4, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", UDA1380_PM, 2, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", UDA1380_PM, 0, 0),
+ SND_SOC_DAPM_INPUT("VINM"),
+ SND_SOC_DAPM_INPUT("VINL"),
+ SND_SOC_DAPM_INPUT("VINR"),
+ SND_SOC_DAPM_MIXER("Analog Mixer", UDA1380_PM, 6, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("VOUTLHP"),
+ SND_SOC_DAPM_OUTPUT("VOUTRHP"),
+ SND_SOC_DAPM_OUTPUT("VOUTL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+ SND_SOC_DAPM_DAC("DAC", "Playback", UDA1380_PM, 10, 0),
+ SND_SOC_DAPM_PGA("HeadPhone Driver", UDA1380_PM, 13, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route uda1380_dapm_routes[] = {
+
+ /* output mux */
+ {"HeadPhone Driver", NULL, "Output Mux"},
+ {"VOUTR", NULL, "Output Mux"},
+ {"VOUTL", NULL, "Output Mux"},
+
+ {"Analog Mixer", NULL, "VINR"},
+ {"Analog Mixer", NULL, "VINL"},
+ {"Analog Mixer", NULL, "DAC"},
+
+ {"Output Mux", "DAC", "DAC"},
+ {"Output Mux", "Analog Mixer", "Analog Mixer"},
+
+ /* {"DAC", "Digital Mixer", "I2S" } */
+
+ /* headphone driver */
+ {"VOUTLHP", NULL, "HeadPhone Driver"},
+ {"VOUTRHP", NULL, "HeadPhone Driver"},
+
+ /* input mux */
+ {"Left ADC", NULL, "Input Mux"},
+ {"Input Mux", "Mic", "Mic LNA"},
+ {"Input Mux", "Mic + Line R", "Mic LNA"},
+ {"Input Mux", "Line L", "Left PGA"},
+ {"Input Mux", "Line", "Left PGA"},
+
+ /* right input */
+ {"Right ADC", "Mic + Line R", "Right PGA"},
+ {"Right ADC", "Line", "Right PGA"},
+
+ /* inputs */
+ {"Mic LNA", NULL, "VINM"},
+ {"Left PGA", NULL, "VINL"},
+ {"Right PGA", NULL, "VINR"},
+};
+
+static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int iface;
+
+ /* set up DAI based upon fmt */
+ iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
+ iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= R01_SFORI_I2S | R01_SFORO_I2S;
+ break;
+ case SND_SOC_DAIFMT_LSB:
+ iface |= R01_SFORI_LSB16 | R01_SFORO_LSB16;
+ break;
+ case SND_SOC_DAIFMT_MSB:
+ iface |= R01_SFORI_MSB | R01_SFORO_MSB;
+ }
+
+ /* DATAI is slave only, so in single-link mode, this has to be slave */
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ return -EINVAL;
+
+ uda1380_write_reg_cache(codec, UDA1380_IFACE, iface);
+
+ return 0;
+}
+
+static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int iface;
+
+ /* set up DAI based upon fmt */
+ iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
+ iface &= ~R01_SFORI_MASK;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= R01_SFORI_I2S;
+ break;
+ case SND_SOC_DAIFMT_LSB:
+ iface |= R01_SFORI_LSB16;
+ break;
+ case SND_SOC_DAIFMT_MSB:
+ iface |= R01_SFORI_MSB;
+ }
+
+ /* DATAI is slave only, so this has to be slave */
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ return -EINVAL;
+
+ uda1380_write(codec, UDA1380_IFACE, iface);
+
+ return 0;
+}
+
+static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int iface;
+
+ /* set up DAI based upon fmt */
+ iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
+ iface &= ~(R01_SIM | R01_SFORO_MASK);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= R01_SFORO_I2S;
+ break;
+ case SND_SOC_DAIFMT_LSB:
+ iface |= R01_SFORO_LSB16;
+ break;
+ case SND_SOC_DAIFMT_MSB:
+ iface |= R01_SFORO_MSB;
+ }
+
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
+ iface |= R01_SIM;
+
+ uda1380_write(codec, UDA1380_IFACE, iface);
+
+ return 0;
+}
+
+static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
+ int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ uda1380_write_reg_cache(codec, UDA1380_MIXER,
+ mixer & ~R14_SILENCE);
+ schedule_work(&uda1380->work);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ uda1380_write_reg_cache(codec, UDA1380_MIXER,
+ mixer | R14_SILENCE);
+ schedule_work(&uda1380->work);
+ break;
+ }
+ return 0;
+}
+
+static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+
+ /* set WSPLL power and divider if running from this clock */
+ if (clk & R00_DAC_CLK) {
+ int rate = params_rate(params);
+ u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+ clk &= ~0x3; /* clear SEL_LOOP_DIV */
+ switch (rate) {
+ case 6250 ... 12500:
+ clk |= 0x0;
+ break;
+ case 12501 ... 25000:
+ clk |= 0x1;
+ break;
+ case 25001 ... 50000:
+ clk |= 0x2;
+ break;
+ case 50001 ... 100000:
+ clk |= 0x3;
+ break;
+ }
+ uda1380_write(codec, UDA1380_PM, R02_PON_PLL | pm);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ clk |= R00_EN_DAC | R00_EN_INT;
+ else
+ clk |= R00_EN_ADC | R00_EN_DEC;
+
+ uda1380_write(codec, UDA1380_CLK, clk);
+ return 0;
+}
+
+static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+
+ /* shut down WSPLL power if running from this clock */
+ if (clk & R00_DAC_CLK) {
+ u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+ uda1380_write(codec, UDA1380_PM, ~R02_PON_PLL & pm);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ clk &= ~(R00_EN_DAC | R00_EN_INT);
+ else
+ clk &= ~(R00_EN_ADC | R00_EN_DEC);
+
+ uda1380_write(codec, UDA1380_CLK, clk);
+}
+
+static int uda1380_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ int pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+ int reg;
+ struct uda1380_platform_data *pdata = codec->dev->platform_data;
+
+ if (codec->dapm.bias_level == level)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ /* ADC, DAC on */
+ uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (gpio_is_valid(pdata->gpio_power)) {
+ gpio_set_value(pdata->gpio_power, 1);
+ mdelay(1);
+ uda1380_reset(codec);
+ }
+
+ uda1380_sync_cache(codec);
+ }
+ uda1380_write(codec, UDA1380_PM, 0x0);
+ break;
+ case SND_SOC_BIAS_OFF:
+ if (!gpio_is_valid(pdata->gpio_power))
+ break;
+
+ gpio_set_value(pdata->gpio_power, 0);
+
+ /* Mark mixer regs cache dirty to sync them with
+ * codec regs on power on.
+ */
+ for (reg = UDA1380_MVOL; reg < UDA1380_CACHEREGNUM; reg++)
+ set_bit(reg - 0x10, &uda1380_cache_dirty);
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define UDA1380_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+static const struct snd_soc_dai_ops uda1380_dai_ops = {
+ .hw_params = uda1380_pcm_hw_params,
+ .shutdown = uda1380_pcm_shutdown,
+ .trigger = uda1380_trigger,
+ .set_fmt = uda1380_set_dai_fmt_both,
+};
+
+static const struct snd_soc_dai_ops uda1380_dai_ops_playback = {
+ .hw_params = uda1380_pcm_hw_params,
+ .shutdown = uda1380_pcm_shutdown,
+ .trigger = uda1380_trigger,
+ .set_fmt = uda1380_set_dai_fmt_playback,
+};
+
+static const struct snd_soc_dai_ops uda1380_dai_ops_capture = {
+ .hw_params = uda1380_pcm_hw_params,
+ .shutdown = uda1380_pcm_shutdown,
+ .trigger = uda1380_trigger,
+ .set_fmt = uda1380_set_dai_fmt_capture,
+};
+
+static struct snd_soc_dai_driver uda1380_dai[] = {
+{
+ .name = "uda1380-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA1380_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA1380_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &uda1380_dai_ops,
+},
+{ /* playback only - dual interface */
+ .name = "uda1380-hifi-playback",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA1380_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &uda1380_dai_ops_playback,
+},
+{ /* capture only - dual interface*/
+ .name = "uda1380-hifi-capture",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA1380_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &uda1380_dai_ops_capture,
+},
+};
+
+static int uda1380_probe(struct snd_soc_codec *codec)
+{
+ struct uda1380_platform_data *pdata =codec->dev->platform_data;
+ struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ uda1380->codec = codec;
+
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ codec->control_data = uda1380->control_data;
+
+ if (!pdata)
+ return -EINVAL;
+
+ if (gpio_is_valid(pdata->gpio_reset)) {
+ ret = gpio_request_one(pdata->gpio_reset, GPIOF_OUT_INIT_LOW,
+ "uda1380 reset");
+ if (ret)
+ goto err_out;
+ }
+
+ if (gpio_is_valid(pdata->gpio_power)) {
+ ret = gpio_request_one(pdata->gpio_power, GPIOF_OUT_INIT_LOW,
+ "uda1380 power");
+ if (ret)
+ goto err_free_gpio;
+ } else {
+ ret = uda1380_reset(codec);
+ if (ret)
+ goto err_free_gpio;
+ }
+
+ INIT_WORK(&uda1380->work, uda1380_flush_work);
+
+ /* set clock input */
+ switch (pdata->dac_clk) {
+ case UDA1380_DAC_CLK_SYSCLK:
+ uda1380_write_reg_cache(codec, UDA1380_CLK, 0);
+ break;
+ case UDA1380_DAC_CLK_WSPLL:
+ uda1380_write_reg_cache(codec, UDA1380_CLK,
+ R00_DAC_CLK);
+ break;
+ }
+
+ return 0;
+
+err_free_gpio:
+ if (gpio_is_valid(pdata->gpio_reset))
+ gpio_free(pdata->gpio_reset);
+err_out:
+ return ret;
+}
+
+/* power down chip */
+static int uda1380_remove(struct snd_soc_codec *codec)
+{
+ struct uda1380_platform_data *pdata =codec->dev->platform_data;
+
+ gpio_free(pdata->gpio_reset);
+ gpio_free(pdata->gpio_power);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_uda1380 = {
+ .probe = uda1380_probe,
+ .remove = uda1380_remove,
+ .read = uda1380_read_reg_cache,
+ .write = uda1380_write,
+ .set_bias_level = uda1380_set_bias_level,
+ .suspend_bias_off = true,
+
+ .reg_cache_size = ARRAY_SIZE(uda1380_reg),
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = uda1380_reg,
+ .reg_cache_step = 1,
+
+ .controls = uda1380_snd_controls,
+ .num_controls = ARRAY_SIZE(uda1380_snd_controls),
+ .dapm_widgets = uda1380_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
+ .dapm_routes = uda1380_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(uda1380_dapm_routes),
+};
+
+#if IS_ENABLED(CONFIG_I2C)
+static int uda1380_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct uda1380_priv *uda1380;
+ int ret;
+
+ uda1380 = devm_kzalloc(&i2c->dev, sizeof(struct uda1380_priv),
+ GFP_KERNEL);
+ if (uda1380 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, uda1380);
+ uda1380->control_data = i2c;
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai));
+ return ret;
+}
+
+static int uda1380_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+ return 0;
+}
+
+static const struct i2c_device_id uda1380_i2c_id[] = {
+ { "uda1380", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
+
+static struct i2c_driver uda1380_i2c_driver = {
+ .driver = {
+ .name = "uda1380-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = uda1380_i2c_probe,
+ .remove = uda1380_i2c_remove,
+ .id_table = uda1380_i2c_id,
+};
+#endif
+
+static int __init uda1380_modinit(void)
+{
+ int ret = 0;
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&uda1380_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register UDA1380 I2C driver: %d\n", ret);
+#endif
+ return ret;
+}
+module_init(uda1380_modinit);
+
+static void __exit uda1380_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&uda1380_i2c_driver);
+#endif
+}
+module_exit(uda1380_exit);
+
+MODULE_AUTHOR("Giorgio Padrin");
+MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/uda1380.h b/kernel/sound/soc/codecs/uda1380.h
new file mode 100644
index 000000000..942e3927c
--- /dev/null
+++ b/kernel/sound/soc/codecs/uda1380.h
@@ -0,0 +1,79 @@
+/*
+ * Audio support for Philips UDA1380
+ *
+ * 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.
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ */
+
+#ifndef _UDA1380_H
+#define _UDA1380_H
+
+#define UDA1380_CLK 0x00
+#define UDA1380_IFACE 0x01
+#define UDA1380_PM 0x02
+#define UDA1380_AMIX 0x03
+#define UDA1380_HP 0x04
+#define UDA1380_MVOL 0x10
+#define UDA1380_MIXVOL 0x11
+#define UDA1380_MODE 0x12
+#define UDA1380_DEEMP 0x13
+#define UDA1380_MIXER 0x14
+#define UDA1380_INTSTAT 0x18
+#define UDA1380_DEC 0x20
+#define UDA1380_PGA 0x21
+#define UDA1380_ADC 0x22
+#define UDA1380_AGC 0x23
+#define UDA1380_DECSTAT 0x28
+#define UDA1380_RESET 0x7f
+
+#define UDA1380_CACHEREGNUM 0x24
+
+/* Register flags */
+#define R00_EN_ADC 0x0800
+#define R00_EN_DEC 0x0400
+#define R00_EN_DAC 0x0200
+#define R00_EN_INT 0x0100
+#define R00_DAC_CLK 0x0010
+#define R01_SFORI_I2S 0x0000
+#define R01_SFORI_LSB16 0x0100
+#define R01_SFORI_LSB18 0x0200
+#define R01_SFORI_LSB20 0x0300
+#define R01_SFORI_MSB 0x0500
+#define R01_SFORI_MASK 0x0700
+#define R01_SFORO_I2S 0x0000
+#define R01_SFORO_LSB16 0x0001
+#define R01_SFORO_LSB18 0x0002
+#define R01_SFORO_LSB20 0x0003
+#define R01_SFORO_LSB24 0x0004
+#define R01_SFORO_MSB 0x0005
+#define R01_SFORO_MASK 0x0007
+#define R01_SEL_SOURCE 0x0040
+#define R01_SIM 0x0010
+#define R02_PON_PLL 0x8000
+#define R02_PON_HP 0x2000
+#define R02_PON_DAC 0x0400
+#define R02_PON_BIAS 0x0100
+#define R02_EN_AVC 0x0080
+#define R02_PON_AVC 0x0040
+#define R02_PON_LNA 0x0010
+#define R02_PON_PGAL 0x0008
+#define R02_PON_ADCL 0x0004
+#define R02_PON_PGAR 0x0002
+#define R02_PON_ADCR 0x0001
+#define R13_MTM 0x4000
+#define R14_SILENCE 0x0080
+#define R14_SDET_ON 0x0040
+#define R21_MT_ADC 0x8000
+#define R22_SEL_LNA 0x0008
+#define R22_SEL_MIC 0x0004
+#define R22_SKIP_DCFIL 0x0002
+#define R23_AGC_EN 0x0001
+
+#define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */
+#define UDA1380_DAI_PLAYBACK 1 /* playback DAI */
+#define UDA1380_DAI_CAPTURE 2 /* capture DAI */
+
+#endif /* _UDA1380_H */
diff --git a/kernel/sound/soc/codecs/wl1273.c b/kernel/sound/soc/codecs/wl1273.c
new file mode 100644
index 000000000..80fb1dc81
--- /dev/null
+++ b/kernel/sound/soc/codecs/wl1273.c
@@ -0,0 +1,523 @@
+/*
+ * ALSA SoC WL1273 codec driver
+ *
+ * Author: Matti Aaltonen, <matti.j.aaltonen@nokia.com>
+ *
+ * Copyright: (C) 2010, 2011 Nokia 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.
+ *
+ * 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/mfd/wl1273-core.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#include "wl1273.h"
+
+enum wl1273_mode { WL1273_MODE_BT, WL1273_MODE_FM_RX, WL1273_MODE_FM_TX };
+
+/* codec private data */
+struct wl1273_priv {
+ enum wl1273_mode mode;
+ struct wl1273_core *core;
+ unsigned int channels;
+};
+
+static int snd_wl1273_fm_set_i2s_mode(struct wl1273_core *core,
+ int rate, int width)
+{
+ struct device *dev = &core->client->dev;
+ int r = 0;
+ u16 mode;
+
+ dev_dbg(dev, "rate: %d\n", rate);
+ dev_dbg(dev, "width: %d\n", width);
+
+ mutex_lock(&core->lock);
+
+ mode = core->i2s_mode & ~WL1273_IS2_WIDTH & ~WL1273_IS2_RATE;
+
+ switch (rate) {
+ case 48000:
+ mode |= WL1273_IS2_RATE_48K;
+ break;
+ case 44100:
+ mode |= WL1273_IS2_RATE_44_1K;
+ break;
+ case 32000:
+ mode |= WL1273_IS2_RATE_32K;
+ break;
+ case 22050:
+ mode |= WL1273_IS2_RATE_22_05K;
+ break;
+ case 16000:
+ mode |= WL1273_IS2_RATE_16K;
+ break;
+ case 12000:
+ mode |= WL1273_IS2_RATE_12K;
+ break;
+ case 11025:
+ mode |= WL1273_IS2_RATE_11_025;
+ break;
+ case 8000:
+ mode |= WL1273_IS2_RATE_8K;
+ break;
+ default:
+ dev_err(dev, "Sampling rate: %d not supported\n", rate);
+ r = -EINVAL;
+ goto out;
+ }
+
+ switch (width) {
+ case 16:
+ mode |= WL1273_IS2_WIDTH_32;
+ break;
+ case 20:
+ mode |= WL1273_IS2_WIDTH_40;
+ break;
+ case 24:
+ mode |= WL1273_IS2_WIDTH_48;
+ break;
+ case 25:
+ mode |= WL1273_IS2_WIDTH_50;
+ break;
+ case 30:
+ mode |= WL1273_IS2_WIDTH_60;
+ break;
+ case 32:
+ mode |= WL1273_IS2_WIDTH_64;
+ break;
+ case 40:
+ mode |= WL1273_IS2_WIDTH_80;
+ break;
+ case 48:
+ mode |= WL1273_IS2_WIDTH_96;
+ break;
+ case 64:
+ mode |= WL1273_IS2_WIDTH_128;
+ break;
+ default:
+ dev_err(dev, "Data width: %d not supported\n", width);
+ r = -EINVAL;
+ goto out;
+ }
+
+ dev_dbg(dev, "WL1273_I2S_DEF_MODE: 0x%04x\n", WL1273_I2S_DEF_MODE);
+ dev_dbg(dev, "core->i2s_mode: 0x%04x\n", core->i2s_mode);
+ dev_dbg(dev, "mode: 0x%04x\n", mode);
+
+ if (core->i2s_mode != mode) {
+ r = core->write(core, WL1273_I2S_MODE_CONFIG_SET, mode);
+ if (r)
+ goto out;
+
+ core->i2s_mode = mode;
+ r = core->write(core, WL1273_AUDIO_ENABLE,
+ WL1273_AUDIO_ENABLE_I2S);
+ if (r)
+ goto out;
+ }
+out:
+ mutex_unlock(&core->lock);
+
+ return r;
+}
+
+static int snd_wl1273_fm_set_channel_number(struct wl1273_core *core,
+ int channel_number)
+{
+ struct device *dev = &core->client->dev;
+ int r = 0;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ mutex_lock(&core->lock);
+
+ if (core->channel_number == channel_number)
+ goto out;
+
+ if (channel_number == 1 && core->mode == WL1273_MODE_RX)
+ r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO);
+ else if (channel_number == 1 && core->mode == WL1273_MODE_TX)
+ r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO);
+ else if (channel_number == 2 && core->mode == WL1273_MODE_RX)
+ r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO);
+ else if (channel_number == 2 && core->mode == WL1273_MODE_TX)
+ r = core->write(core, WL1273_MONO_SET, WL1273_TX_STEREO);
+ else
+ r = -EINVAL;
+out:
+ mutex_unlock(&core->lock);
+
+ return r;
+}
+
+static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wl1273->mode;
+
+ return 0;
+}
+
+/*
+ * TODO: Implement the audio routing in the driver. Now this control
+ * only indicates the setting that has been done elsewhere (in the user
+ * space).
+ */
+static const char * const wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" };
+
+static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
+
+ if (wl1273->mode == ucontrol->value.integer.value[0])
+ return 0;
+
+ /* Do not allow changes while stream is running */
+ if (snd_soc_codec_is_active(codec))
+ return -EPERM;
+
+ if (ucontrol->value.integer.value[0] < 0 ||
+ ucontrol->value.integer.value[0] >= ARRAY_SIZE(wl1273_audio_route))
+ return -EINVAL;
+
+ wl1273->mode = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static SOC_ENUM_SINGLE_EXT_DECL(wl1273_enum, wl1273_audio_route);
+
+static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: enter.\n", __func__);
+
+ ucontrol->value.integer.value[0] = wl1273->core->audio_mode;
+
+ return 0;
+}
+
+static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
+ int val, r = 0;
+
+ dev_dbg(codec->dev, "%s: enter.\n", __func__);
+
+ val = ucontrol->value.integer.value[0];
+ if (wl1273->core->audio_mode == val)
+ return 0;
+
+ r = wl1273->core->set_audio(wl1273->core, val);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static const char * const wl1273_audio_strings[] = { "Digital", "Analog" };
+
+static SOC_ENUM_SINGLE_EXT_DECL(wl1273_audio_enum, wl1273_audio_strings);
+
+static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: enter.\n", __func__);
+
+ ucontrol->value.integer.value[0] = wl1273->core->volume;
+
+ return 0;
+}
+
+static int snd_wl1273_fm_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
+ int r;
+
+ dev_dbg(codec->dev, "%s: enter.\n", __func__);
+
+ r = wl1273->core->set_volume(wl1273->core,
+ ucontrol->value.integer.value[0]);
+ if (r)
+ return r;
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new wl1273_controls[] = {
+ SOC_ENUM_EXT("Codec Mode", wl1273_enum,
+ snd_wl1273_get_audio_route, snd_wl1273_set_audio_route),
+ SOC_ENUM_EXT("Audio Switch", wl1273_audio_enum,
+ snd_wl1273_fm_audio_get, snd_wl1273_fm_audio_put),
+ SOC_SINGLE_EXT("Volume", 0, 0, WL1273_MAX_VOLUME, 0,
+ snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put),
+};
+
+static const struct snd_soc_dapm_widget wl1273_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("RX"),
+
+ SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route wl1273_dapm_routes[] = {
+ { "Capture", NULL, "RX" },
+
+ { "TX", NULL, "Playback" },
+};
+
+static int wl1273_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
+
+ switch (wl1273->mode) {
+ case WL1273_MODE_BT:
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 8000, 8000);
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 1, 1);
+ break;
+ case WL1273_MODE_FM_RX:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ pr_err("Cannot play in RX mode.\n");
+ return -EINVAL;
+ }
+ break;
+ case WL1273_MODE_FM_TX:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ pr_err("Cannot capture in TX mode.\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ return 0;
+}
+
+static int wl1273_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(dai->codec);
+ struct wl1273_core *core = wl1273->core;
+ unsigned int rate, width, r;
+
+ if (params_width(params) != 16) {
+ dev_err(dai->dev, "%d bits/sample not supported\n",
+ params_width(params));
+ return -EINVAL;
+ }
+
+ rate = params_rate(params);
+ width = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
+
+ if (wl1273->mode == WL1273_MODE_BT) {
+ if (rate != 8000) {
+ pr_err("Rate %d not supported.\n", params_rate(params));
+ return -EINVAL;
+ }
+
+ if (params_channels(params) != 1) {
+ pr_err("Only mono supported.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ if (wl1273->mode == WL1273_MODE_FM_TX &&
+ substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ pr_err("Only playback supported with TX.\n");
+ return -EINVAL;
+ }
+
+ if (wl1273->mode == WL1273_MODE_FM_RX &&
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ pr_err("Only capture supported with RX.\n");
+ return -EINVAL;
+ }
+
+ if (wl1273->mode != WL1273_MODE_FM_RX &&
+ wl1273->mode != WL1273_MODE_FM_TX) {
+ pr_err("Unexpected mode: %d.\n", wl1273->mode);
+ return -EINVAL;
+ }
+
+ r = snd_wl1273_fm_set_i2s_mode(core, rate, width);
+ if (r)
+ return r;
+
+ wl1273->channels = params_channels(params);
+ r = snd_wl1273_fm_set_channel_number(core, wl1273->channels);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wl1273_dai_ops = {
+ .startup = wl1273_startup,
+ .hw_params = wl1273_hw_params,
+};
+
+static struct snd_soc_dai_driver wl1273_dai = {
+ .name = "wl1273-fm",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE},
+ .ops = &wl1273_dai_ops,
+};
+
+/* Audio interface format for the soc_card driver */
+int wl1273_get_format(struct snd_soc_codec *codec, unsigned int *fmt)
+{
+ struct wl1273_priv *wl1273;
+
+ if (codec == NULL || fmt == NULL)
+ return -EINVAL;
+
+ wl1273 = snd_soc_codec_get_drvdata(codec);
+
+ switch (wl1273->mode) {
+ case WL1273_MODE_FM_RX:
+ case WL1273_MODE_FM_TX:
+ *fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+
+ break;
+ case WL1273_MODE_BT:
+ *fmt = SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_IB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wl1273_get_format);
+
+static int wl1273_probe(struct snd_soc_codec *codec)
+{
+ struct wl1273_core **core = codec->dev->platform_data;
+ struct wl1273_priv *wl1273;
+
+ dev_dbg(codec->dev, "%s.\n", __func__);
+
+ if (!core) {
+ dev_err(codec->dev, "Platform data is missing.\n");
+ return -EINVAL;
+ }
+
+ wl1273 = kzalloc(sizeof(struct wl1273_priv), GFP_KERNEL);
+ if (!wl1273)
+ return -ENOMEM;
+
+ wl1273->mode = WL1273_MODE_BT;
+ wl1273->core = *core;
+
+ snd_soc_codec_set_drvdata(codec, wl1273);
+
+ return 0;
+}
+
+static int wl1273_remove(struct snd_soc_codec *codec)
+{
+ struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s\n", __func__);
+ kfree(wl1273);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wl1273 = {
+ .probe = wl1273_probe,
+ .remove = wl1273_remove,
+
+ .controls = wl1273_controls,
+ .num_controls = ARRAY_SIZE(wl1273_controls),
+ .dapm_widgets = wl1273_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets),
+ .dapm_routes = wl1273_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wl1273_dapm_routes),
+};
+
+static int wl1273_platform_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wl1273,
+ &wl1273_dai, 1);
+}
+
+static int wl1273_platform_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+MODULE_ALIAS("platform:wl1273-codec");
+
+static struct platform_driver wl1273_platform_driver = {
+ .driver = {
+ .name = "wl1273-codec",
+ },
+ .probe = wl1273_platform_probe,
+ .remove = wl1273_platform_remove,
+};
+
+module_platform_driver(wl1273_platform_driver);
+
+MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>");
+MODULE_DESCRIPTION("ASoC WL1273 codec driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wl1273.h b/kernel/sound/soc/codecs/wl1273.h
new file mode 100644
index 000000000..43ec7e668
--- /dev/null
+++ b/kernel/sound/soc/codecs/wl1273.h
@@ -0,0 +1,30 @@
+/*
+ * sound/soc/codec/wl1273.h
+ *
+ * ALSA SoC WL1273 codec driver
+ *
+ * Copyright (C) Nokia Corporation
+ * Author: Matti Aaltonen <matti.j.aaltonen@nokia.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 __WL1273_CODEC_H__
+#define __WL1273_CODEC_H__
+
+int wl1273_get_format(struct snd_soc_codec *codec, unsigned int *fmt);
+
+#endif /* End of __WL1273_CODEC_H__ */
diff --git a/kernel/sound/soc/codecs/wm0010.c b/kernel/sound/soc/codecs/wm0010.c
new file mode 100644
index 000000000..f37989ec7
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm0010.c
@@ -0,0 +1,1019 @@
+/*
+ * wm0010.c -- WM0010 DSP Driver
+ *
+ * Copyright 2012 Wolfson Microelectronics PLC.
+ *
+ * Authors: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ * Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
+ * Scott Ling <sl@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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/init.h>
+#include <linux/spi/spi.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#include <sound/soc.h>
+#include <sound/wm0010.h>
+
+#define DEVICE_ID_WM0010 10
+
+/* We only support v1 of the .dfw INFO record */
+#define INFO_VERSION 1
+
+enum dfw_cmd {
+ DFW_CMD_FUSE = 0x01,
+ DFW_CMD_CODE_HDR,
+ DFW_CMD_CODE_DATA,
+ DFW_CMD_PLL,
+ DFW_CMD_INFO = 0xff
+};
+
+struct dfw_binrec {
+ u8 command;
+ u32 length:24;
+ u32 address;
+ uint8_t data[0];
+} __packed;
+
+struct dfw_inforec {
+ u8 info_version;
+ u8 tool_major_version;
+ u8 tool_minor_version;
+ u8 dsp_target;
+};
+
+struct dfw_pllrec {
+ u8 command;
+ u32 length:24;
+ u32 address;
+ u32 clkctrl1;
+ u32 clkctrl2;
+ u32 clkctrl3;
+ u32 ldetctrl;
+ u32 uart_div;
+ u32 spi_div;
+} __packed;
+
+static struct pll_clock_map {
+ int max_sysclk;
+ int max_pll_spi_speed;
+ u32 pll_clkctrl1;
+} pll_clock_map[] = { /* Dividers */
+ { 22000000, 26000000, 0x00201f11 }, /* 2,32,2 */
+ { 18000000, 26000000, 0x00203f21 }, /* 2,64,4 */
+ { 14000000, 26000000, 0x00202620 }, /* 1,39,4 */
+ { 10000000, 22000000, 0x00203120 }, /* 1,50,4 */
+ { 6500000, 22000000, 0x00204520 }, /* 1,70,4 */
+ { 5500000, 22000000, 0x00103f10 }, /* 1,64,2 */
+};
+
+enum wm0010_state {
+ WM0010_POWER_OFF,
+ WM0010_OUT_OF_RESET,
+ WM0010_BOOTROM,
+ WM0010_STAGE2,
+ WM0010_FIRMWARE,
+};
+
+struct wm0010_priv {
+ struct snd_soc_codec *codec;
+
+ struct mutex lock;
+ struct device *dev;
+
+ struct wm0010_pdata pdata;
+
+ int gpio_reset;
+ int gpio_reset_value;
+
+ struct regulator_bulk_data core_supplies[2];
+ struct regulator *dbvdd;
+
+ int sysclk;
+
+ enum wm0010_state state;
+ bool boot_failed;
+ bool ready;
+ bool pll_running;
+ int max_spi_freq;
+ int board_max_spi_speed;
+ u32 pll_clkctrl1;
+
+ spinlock_t irq_lock;
+ int irq;
+
+ struct completion boot_completion;
+};
+
+struct wm0010_spi_msg {
+ struct spi_message m;
+ struct spi_transfer t;
+ u8 *tx_buf;
+ u8 *rx_buf;
+ size_t len;
+};
+
+static const struct snd_soc_dapm_widget wm0010_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("CLKIN", SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route wm0010_dapm_routes[] = {
+ { "SDI2 Capture", NULL, "SDI1 Playback" },
+ { "SDI1 Capture", NULL, "SDI2 Playback" },
+
+ { "SDI1 Capture", NULL, "CLKIN" },
+ { "SDI2 Capture", NULL, "CLKIN" },
+ { "SDI1 Playback", NULL, "CLKIN" },
+ { "SDI2 Playback", NULL, "CLKIN" },
+};
+
+static const char *wm0010_state_to_str(enum wm0010_state state)
+{
+ static const char * const state_to_str[] = {
+ "Power off",
+ "Out of reset",
+ "Boot ROM",
+ "Stage2",
+ "Firmware"
+ };
+
+ if (state < 0 || state >= ARRAY_SIZE(state_to_str))
+ return "null";
+ return state_to_str[state];
+}
+
+/* Called with wm0010->lock held */
+static void wm0010_halt(struct snd_soc_codec *codec)
+{
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ unsigned long flags;
+ enum wm0010_state state;
+
+ /* Fetch the wm0010 state */
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ state = wm0010->state;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ switch (state) {
+ case WM0010_POWER_OFF:
+ /* If there's nothing to do, bail out */
+ return;
+ case WM0010_OUT_OF_RESET:
+ case WM0010_BOOTROM:
+ case WM0010_STAGE2:
+ case WM0010_FIRMWARE:
+ /* Remember to put chip back into reset */
+ gpio_set_value_cansleep(wm0010->gpio_reset,
+ wm0010->gpio_reset_value);
+ /* Disable the regulators */
+ regulator_disable(wm0010->dbvdd);
+ regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies),
+ wm0010->core_supplies);
+ break;
+ }
+
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ wm0010->state = WM0010_POWER_OFF;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+}
+
+struct wm0010_boot_xfer {
+ struct list_head list;
+ struct snd_soc_codec *codec;
+ struct completion *done;
+ struct spi_message m;
+ struct spi_transfer t;
+};
+
+/* Called with wm0010->lock held */
+static void wm0010_mark_boot_failure(struct wm0010_priv *wm0010)
+{
+ enum wm0010_state state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ state = wm0010->state;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ dev_err(wm0010->dev, "Failed to transition from `%s' state to `%s' state\n",
+ wm0010_state_to_str(state), wm0010_state_to_str(state + 1));
+
+ wm0010->boot_failed = true;
+}
+
+static void wm0010_boot_xfer_complete(void *data)
+{
+ struct wm0010_boot_xfer *xfer = data;
+ struct snd_soc_codec *codec = xfer->codec;
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ u32 *out32 = xfer->t.rx_buf;
+ int i;
+
+ if (xfer->m.status != 0) {
+ dev_err(codec->dev, "SPI transfer failed: %d\n",
+ xfer->m.status);
+ wm0010_mark_boot_failure(wm0010);
+ if (xfer->done)
+ complete(xfer->done);
+ return;
+ }
+
+ for (i = 0; i < xfer->t.len / 4; i++) {
+ dev_dbg(codec->dev, "%d: %04x\n", i, out32[i]);
+
+ switch (be32_to_cpu(out32[i])) {
+ case 0xe0e0e0e0:
+ dev_err(codec->dev,
+ "%d: ROM error reported in stage 2\n", i);
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x55555555:
+ if (wm0010->state < WM0010_STAGE2)
+ break;
+ dev_err(codec->dev,
+ "%d: ROM bootloader running in stage 2\n", i);
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0000:
+ dev_dbg(codec->dev, "Stage2 loader running\n");
+ break;
+
+ case 0x0fed0007:
+ dev_dbg(codec->dev, "CODE_HDR packet received\n");
+ break;
+
+ case 0x0fed0008:
+ dev_dbg(codec->dev, "CODE_DATA packet received\n");
+ break;
+
+ case 0x0fed0009:
+ dev_dbg(codec->dev, "Download complete\n");
+ break;
+
+ case 0x0fed000c:
+ dev_dbg(codec->dev, "Application start\n");
+ break;
+
+ case 0x0fed000e:
+ dev_dbg(codec->dev, "PLL packet received\n");
+ wm0010->pll_running = true;
+ break;
+
+ case 0x0fed0025:
+ dev_err(codec->dev, "Device reports image too long\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed002c:
+ dev_err(codec->dev, "Device reports bad SPI packet\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0031:
+ dev_err(codec->dev, "Device reports SPI read overflow\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0032:
+ dev_err(codec->dev, "Device reports SPI underclock\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0033:
+ dev_err(codec->dev, "Device reports bad header packet\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0034:
+ dev_err(codec->dev, "Device reports invalid packet type\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0035:
+ dev_err(codec->dev, "Device reports data before header error\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0038:
+ dev_err(codec->dev, "Device reports invalid PLL packet\n");
+ break;
+
+ case 0x0fed003a:
+ dev_err(codec->dev, "Device reports packet alignment error\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ default:
+ dev_err(codec->dev, "Unrecognised return 0x%x\n",
+ be32_to_cpu(out32[i]));
+ wm0010_mark_boot_failure(wm0010);
+ break;
+ }
+
+ if (wm0010->boot_failed)
+ break;
+ }
+
+ if (xfer->done)
+ complete(xfer->done);
+}
+
+static void byte_swap_64(u64 *data_in, u64 *data_out, u32 len)
+{
+ int i;
+
+ for (i = 0; i < len / 8; i++)
+ data_out[i] = cpu_to_be64(le64_to_cpu(data_in[i]));
+}
+
+static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec)
+{
+ struct spi_device *spi = to_spi_device(codec->dev);
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ struct list_head xfer_list;
+ struct wm0010_boot_xfer *xfer;
+ int ret;
+ struct completion done;
+ const struct firmware *fw;
+ const struct dfw_binrec *rec;
+ const struct dfw_inforec *inforec;
+ u64 *img;
+ u8 *out, dsp;
+ u32 len, offset;
+
+ INIT_LIST_HEAD(&xfer_list);
+
+ ret = request_firmware(&fw, name, codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request application(%s): %d\n",
+ name, ret);
+ return ret;
+ }
+
+ rec = (const struct dfw_binrec *)fw->data;
+ inforec = (const struct dfw_inforec *)rec->data;
+ offset = 0;
+ dsp = inforec->dsp_target;
+ wm0010->boot_failed = false;
+ if (WARN_ON(!list_empty(&xfer_list)))
+ return -EINVAL;
+ init_completion(&done);
+
+ /* First record should be INFO */
+ if (rec->command != DFW_CMD_INFO) {
+ dev_err(codec->dev, "First record not INFO\r\n");
+ ret = -EINVAL;
+ goto abort;
+ }
+
+ if (inforec->info_version != INFO_VERSION) {
+ dev_err(codec->dev,
+ "Unsupported version (%02d) of INFO record\r\n",
+ inforec->info_version);
+ ret = -EINVAL;
+ goto abort;
+ }
+
+ dev_dbg(codec->dev, "Version v%02d INFO record found\r\n",
+ inforec->info_version);
+
+ /* Check it's a DSP file */
+ if (dsp != DEVICE_ID_WM0010) {
+ dev_err(codec->dev, "Not a WM0010 firmware file.\r\n");
+ ret = -EINVAL;
+ goto abort;
+ }
+
+ /* Skip the info record as we don't need to send it */
+ offset += ((rec->length) + 8);
+ rec = (void *)&rec->data[rec->length];
+
+ while (offset < fw->size) {
+ dev_dbg(codec->dev,
+ "Packet: command %d, data length = 0x%x\r\n",
+ rec->command, rec->length);
+ len = rec->length + 8;
+
+ xfer = kzalloc(sizeof(*xfer), GFP_KERNEL);
+ if (!xfer) {
+ ret = -ENOMEM;
+ goto abort;
+ }
+
+ xfer->codec = codec;
+ list_add_tail(&xfer->list, &xfer_list);
+
+ out = kzalloc(len, GFP_KERNEL | GFP_DMA);
+ if (!out) {
+ ret = -ENOMEM;
+ goto abort1;
+ }
+ xfer->t.rx_buf = out;
+
+ img = kzalloc(len, GFP_KERNEL | GFP_DMA);
+ if (!img) {
+ ret = -ENOMEM;
+ goto abort1;
+ }
+ xfer->t.tx_buf = img;
+
+ byte_swap_64((u64 *)&rec->command, img, len);
+
+ spi_message_init(&xfer->m);
+ xfer->m.complete = wm0010_boot_xfer_complete;
+ xfer->m.context = xfer;
+ xfer->t.len = len;
+ xfer->t.bits_per_word = 8;
+
+ if (!wm0010->pll_running) {
+ xfer->t.speed_hz = wm0010->sysclk / 6;
+ } else {
+ xfer->t.speed_hz = wm0010->max_spi_freq;
+
+ if (wm0010->board_max_spi_speed &&
+ (wm0010->board_max_spi_speed < wm0010->max_spi_freq))
+ xfer->t.speed_hz = wm0010->board_max_spi_speed;
+ }
+
+ /* Store max usable spi frequency for later use */
+ wm0010->max_spi_freq = xfer->t.speed_hz;
+
+ spi_message_add_tail(&xfer->t, &xfer->m);
+
+ offset += ((rec->length) + 8);
+ rec = (void *)&rec->data[rec->length];
+
+ if (offset >= fw->size) {
+ dev_dbg(codec->dev, "All transfers scheduled\n");
+ xfer->done = &done;
+ }
+
+ ret = spi_async(spi, &xfer->m);
+ if (ret != 0) {
+ dev_err(codec->dev, "Write failed: %d\n", ret);
+ goto abort1;
+ }
+
+ if (wm0010->boot_failed) {
+ dev_dbg(codec->dev, "Boot fail!\n");
+ ret = -EINVAL;
+ goto abort1;
+ }
+ }
+
+ wait_for_completion(&done);
+
+ ret = 0;
+
+abort1:
+ while (!list_empty(&xfer_list)) {
+ xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer,
+ list);
+ kfree(xfer->t.rx_buf);
+ kfree(xfer->t.tx_buf);
+ list_del(&xfer->list);
+ kfree(xfer);
+ }
+
+abort:
+ release_firmware(fw);
+ return ret;
+}
+
+static int wm0010_stage2_load(struct snd_soc_codec *codec)
+{
+ struct spi_device *spi = to_spi_device(codec->dev);
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ const struct firmware *fw;
+ struct spi_message m;
+ struct spi_transfer t;
+ u32 *img;
+ u8 *out;
+ int i;
+ int ret = 0;
+
+ ret = request_firmware(&fw, "wm0010_stage2.bin", codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request stage2 loader: %d\n",
+ ret);
+ return ret;
+ }
+
+ dev_dbg(codec->dev, "Downloading %zu byte stage 2 loader\n", fw->size);
+
+ /* Copy to local buffer first as vmalloc causes problems for dma */
+ img = kzalloc(fw->size, GFP_KERNEL | GFP_DMA);
+ if (!img) {
+ ret = -ENOMEM;
+ goto abort2;
+ }
+
+ out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA);
+ if (!out) {
+ ret = -ENOMEM;
+ goto abort1;
+ }
+
+ memcpy(img, &fw->data[0], fw->size);
+
+ spi_message_init(&m);
+ memset(&t, 0, sizeof(t));
+ t.rx_buf = out;
+ t.tx_buf = img;
+ t.len = fw->size;
+ t.bits_per_word = 8;
+ t.speed_hz = wm0010->sysclk / 10;
+ spi_message_add_tail(&t, &m);
+
+ dev_dbg(codec->dev, "Starting initial download at %dHz\n",
+ t.speed_hz);
+
+ ret = spi_sync(spi, &m);
+ if (ret != 0) {
+ dev_err(codec->dev, "Initial download failed: %d\n", ret);
+ goto abort;
+ }
+
+ /* Look for errors from the boot ROM */
+ for (i = 0; i < fw->size; i++) {
+ if (out[i] != 0x55) {
+ dev_err(codec->dev, "Boot ROM error: %x in %d\n",
+ out[i], i);
+ wm0010_mark_boot_failure(wm0010);
+ ret = -EBUSY;
+ goto abort;
+ }
+ }
+abort:
+ kfree(out);
+abort1:
+ kfree(img);
+abort2:
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int wm0010_boot(struct snd_soc_codec *codec)
+{
+ struct spi_device *spi = to_spi_device(codec->dev);
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ unsigned long flags;
+ int ret;
+ const struct firmware *fw;
+ struct spi_message m;
+ struct spi_transfer t;
+ struct dfw_pllrec pll_rec;
+ u32 *p, len;
+ u64 *img_swap;
+ u8 *out;
+ int i;
+
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ if (wm0010->state != WM0010_POWER_OFF)
+ dev_warn(wm0010->dev, "DSP already powered up!\n");
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ if (wm0010->sysclk > 26000000) {
+ dev_err(codec->dev, "Max DSP clock frequency is 26MHz\n");
+ ret = -ECANCELED;
+ goto err;
+ }
+
+ mutex_lock(&wm0010->lock);
+ wm0010->pll_running = false;
+
+ dev_dbg(codec->dev, "max_spi_freq: %d\n", wm0010->max_spi_freq);
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm0010->core_supplies),
+ wm0010->core_supplies);
+ if (ret != 0) {
+ dev_err(&spi->dev, "Failed to enable core supplies: %d\n",
+ ret);
+ mutex_unlock(&wm0010->lock);
+ goto err;
+ }
+
+ ret = regulator_enable(wm0010->dbvdd);
+ if (ret != 0) {
+ dev_err(&spi->dev, "Failed to enable DBVDD: %d\n", ret);
+ goto err_core;
+ }
+
+ /* Release reset */
+ gpio_set_value_cansleep(wm0010->gpio_reset, !wm0010->gpio_reset_value);
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ wm0010->state = WM0010_OUT_OF_RESET;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ /* First the bootloader */
+ ret = request_firmware(&fw, "wm0010_stage2.bin", codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request stage2 loader: %d\n",
+ ret);
+ goto abort;
+ }
+
+ if (!wait_for_completion_timeout(&wm0010->boot_completion,
+ msecs_to_jiffies(20)))
+ dev_err(codec->dev, "Failed to get interrupt from DSP\n");
+
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ wm0010->state = WM0010_BOOTROM;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ ret = wm0010_stage2_load(codec);
+ if (ret)
+ goto abort;
+
+ if (!wait_for_completion_timeout(&wm0010->boot_completion,
+ msecs_to_jiffies(20)))
+ dev_err(codec->dev, "Failed to get interrupt from DSP loader.\n");
+
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ wm0010->state = WM0010_STAGE2;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ /* Only initialise PLL if max_spi_freq initialised */
+ if (wm0010->max_spi_freq) {
+
+ /* Initialise a PLL record */
+ memset(&pll_rec, 0, sizeof(pll_rec));
+ pll_rec.command = DFW_CMD_PLL;
+ pll_rec.length = (sizeof(pll_rec) - 8);
+
+ /* On wm0010 only the CLKCTRL1 value is used */
+ pll_rec.clkctrl1 = wm0010->pll_clkctrl1;
+
+ ret = -ENOMEM;
+ len = pll_rec.length + 8;
+ out = kzalloc(len, GFP_KERNEL | GFP_DMA);
+ if (!out) {
+ dev_err(codec->dev,
+ "Failed to allocate RX buffer\n");
+ goto abort;
+ }
+
+ img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA);
+ if (!img_swap)
+ goto abort;
+
+ /* We need to re-order for 0010 */
+ byte_swap_64((u64 *)&pll_rec, img_swap, len);
+
+ spi_message_init(&m);
+ memset(&t, 0, sizeof(t));
+ t.rx_buf = out;
+ t.tx_buf = img_swap;
+ t.len = len;
+ t.bits_per_word = 8;
+ t.speed_hz = wm0010->sysclk / 6;
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+ if (ret != 0) {
+ dev_err(codec->dev, "First PLL write failed: %d\n", ret);
+ goto abort;
+ }
+
+ /* Use a second send of the message to get the return status */
+ ret = spi_sync(spi, &m);
+ if (ret != 0) {
+ dev_err(codec->dev, "Second PLL write failed: %d\n", ret);
+ goto abort;
+ }
+
+ p = (u32 *)out;
+
+ /* Look for PLL active code from the DSP */
+ for (i = 0; i < len / 4; i++) {
+ if (*p == 0x0e00ed0f) {
+ dev_dbg(codec->dev, "PLL packet received\n");
+ wm0010->pll_running = true;
+ break;
+ }
+ p++;
+ }
+
+ kfree(img_swap);
+ kfree(out);
+ } else
+ dev_dbg(codec->dev, "Not enabling DSP PLL.");
+
+ ret = wm0010_firmware_load("wm0010.dfw", codec);
+
+ if (ret != 0)
+ goto abort;
+
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ wm0010->state = WM0010_FIRMWARE;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ mutex_unlock(&wm0010->lock);
+
+ return 0;
+
+abort:
+ /* Put the chip back into reset */
+ wm0010_halt(codec);
+ mutex_unlock(&wm0010->lock);
+ return ret;
+
+err_core:
+ mutex_unlock(&wm0010->lock);
+ regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies),
+ wm0010->core_supplies);
+err:
+ return ret;
+}
+
+static int wm0010_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE)
+ wm0010_boot(codec);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
+ mutex_lock(&wm0010->lock);
+ wm0010_halt(codec);
+ mutex_unlock(&wm0010->lock);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int wm0010_set_sysclk(struct snd_soc_codec *codec, int source,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ unsigned int i;
+
+ wm0010->sysclk = freq;
+
+ if (freq < pll_clock_map[ARRAY_SIZE(pll_clock_map)-1].max_sysclk) {
+ wm0010->max_spi_freq = 0;
+ } else {
+ for (i = 0; i < ARRAY_SIZE(pll_clock_map); i++)
+ if (freq >= pll_clock_map[i].max_sysclk) {
+ wm0010->max_spi_freq = pll_clock_map[i].max_pll_spi_speed;
+ wm0010->pll_clkctrl1 = pll_clock_map[i].pll_clkctrl1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int wm0010_probe(struct snd_soc_codec *codec);
+
+static struct snd_soc_codec_driver soc_codec_dev_wm0010 = {
+ .probe = wm0010_probe,
+ .set_bias_level = wm0010_set_bias_level,
+ .set_sysclk = wm0010_set_sysclk,
+ .idle_bias_off = true,
+
+ .dapm_widgets = wm0010_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm0010_dapm_widgets),
+ .dapm_routes = wm0010_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm0010_dapm_routes),
+};
+
+#define WM0010_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define WM0010_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver wm0010_dai[] = {
+ {
+ .name = "wm0010-sdi1",
+ .playback = {
+ .stream_name = "SDI1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM0010_RATES,
+ .formats = WM0010_FORMATS,
+ },
+ .capture = {
+ .stream_name = "SDI1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM0010_RATES,
+ .formats = WM0010_FORMATS,
+ },
+ },
+ {
+ .name = "wm0010-sdi2",
+ .playback = {
+ .stream_name = "SDI2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM0010_RATES,
+ .formats = WM0010_FORMATS,
+ },
+ .capture = {
+ .stream_name = "SDI2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM0010_RATES,
+ .formats = WM0010_FORMATS,
+ },
+ },
+};
+
+static irqreturn_t wm0010_irq(int irq, void *data)
+{
+ struct wm0010_priv *wm0010 = data;
+
+ switch (wm0010->state) {
+ case WM0010_OUT_OF_RESET:
+ case WM0010_BOOTROM:
+ case WM0010_STAGE2:
+ spin_lock(&wm0010->irq_lock);
+ complete(&wm0010->boot_completion);
+ spin_unlock(&wm0010->irq_lock);
+ return IRQ_HANDLED;
+ default:
+ return IRQ_NONE;
+ }
+
+ return IRQ_NONE;
+}
+
+static int wm0010_probe(struct snd_soc_codec *codec)
+{
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+
+ wm0010->codec = codec;
+
+ return 0;
+}
+
+static int wm0010_spi_probe(struct spi_device *spi)
+{
+ unsigned long gpio_flags;
+ int ret;
+ int trigger;
+ int irq;
+ struct wm0010_priv *wm0010;
+
+ wm0010 = devm_kzalloc(&spi->dev, sizeof(*wm0010),
+ GFP_KERNEL);
+ if (!wm0010)
+ return -ENOMEM;
+
+ mutex_init(&wm0010->lock);
+ spin_lock_init(&wm0010->irq_lock);
+
+ spi_set_drvdata(spi, wm0010);
+ wm0010->dev = &spi->dev;
+
+ if (dev_get_platdata(&spi->dev))
+ memcpy(&wm0010->pdata, dev_get_platdata(&spi->dev),
+ sizeof(wm0010->pdata));
+
+ init_completion(&wm0010->boot_completion);
+
+ wm0010->core_supplies[0].supply = "AVDD";
+ wm0010->core_supplies[1].supply = "DCVDD";
+ ret = devm_regulator_bulk_get(wm0010->dev, ARRAY_SIZE(wm0010->core_supplies),
+ wm0010->core_supplies);
+ if (ret != 0) {
+ dev_err(wm0010->dev, "Failed to obtain core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ wm0010->dbvdd = devm_regulator_get(wm0010->dev, "DBVDD");
+ if (IS_ERR(wm0010->dbvdd)) {
+ ret = PTR_ERR(wm0010->dbvdd);
+ dev_err(wm0010->dev, "Failed to obtain DBVDD: %d\n", ret);
+ return ret;
+ }
+
+ if (wm0010->pdata.gpio_reset) {
+ wm0010->gpio_reset = wm0010->pdata.gpio_reset;
+
+ if (wm0010->pdata.reset_active_high)
+ wm0010->gpio_reset_value = 1;
+ else
+ wm0010->gpio_reset_value = 0;
+
+ if (wm0010->gpio_reset_value)
+ gpio_flags = GPIOF_OUT_INIT_HIGH;
+ else
+ gpio_flags = GPIOF_OUT_INIT_LOW;
+
+ ret = devm_gpio_request_one(wm0010->dev, wm0010->gpio_reset,
+ gpio_flags, "wm0010 reset");
+ if (ret < 0) {
+ dev_err(wm0010->dev,
+ "Failed to request GPIO for DSP reset: %d\n",
+ ret);
+ return ret;
+ }
+ } else {
+ dev_err(wm0010->dev, "No reset GPIO configured\n");
+ return -EINVAL;
+ }
+
+ wm0010->state = WM0010_POWER_OFF;
+
+ irq = spi->irq;
+ if (wm0010->pdata.irq_flags)
+ trigger = wm0010->pdata.irq_flags;
+ else
+ trigger = IRQF_TRIGGER_FALLING;
+ trigger |= IRQF_ONESHOT;
+
+ ret = request_threaded_irq(irq, NULL, wm0010_irq, trigger | IRQF_ONESHOT,
+ "wm0010", wm0010);
+ if (ret) {
+ dev_err(wm0010->dev, "Failed to request IRQ %d: %d\n",
+ irq, ret);
+ return ret;
+ }
+ wm0010->irq = irq;
+
+ ret = irq_set_irq_wake(irq, 1);
+ if (ret) {
+ dev_err(wm0010->dev, "Failed to set IRQ %d as wake source: %d\n",
+ irq, ret);
+ return ret;
+ }
+
+ if (spi->max_speed_hz)
+ wm0010->board_max_spi_speed = spi->max_speed_hz;
+ else
+ wm0010->board_max_spi_speed = 0;
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm0010, wm0010_dai,
+ ARRAY_SIZE(wm0010_dai));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int wm0010_spi_remove(struct spi_device *spi)
+{
+ struct wm0010_priv *wm0010 = spi_get_drvdata(spi);
+
+ snd_soc_unregister_codec(&spi->dev);
+
+ gpio_set_value_cansleep(wm0010->gpio_reset,
+ wm0010->gpio_reset_value);
+
+ irq_set_irq_wake(wm0010->irq, 0);
+
+ if (wm0010->irq)
+ free_irq(wm0010->irq, wm0010);
+
+ return 0;
+}
+
+static struct spi_driver wm0010_spi_driver = {
+ .driver = {
+ .name = "wm0010",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm0010_spi_probe,
+ .remove = wm0010_spi_remove,
+};
+
+module_spi_driver(wm0010_spi_driver);
+
+MODULE_DESCRIPTION("ASoC WM0010 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm1250-ev1.c b/kernel/sound/soc/codecs/wm1250-ev1.c
new file mode 100644
index 000000000..8011f75fb
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm1250-ev1.c
@@ -0,0 +1,267 @@
+/*
+ * Driver for the 1250-EV1 audio I/O module
+ *
+ * Copyright 2011 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/wm1250-ev1.h>
+
+static const char *wm1250_gpio_names[WM1250_EV1_NUM_GPIOS] = {
+ "WM1250 CLK_ENA",
+ "WM1250 CLK_SEL0",
+ "WM1250 CLK_SEL1",
+ "WM1250 OSR",
+ "WM1250 MASTER",
+};
+
+struct wm1250_priv {
+ struct gpio gpios[WM1250_EV1_NUM_GPIOS];
+};
+
+static int wm1250_ev1_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm1250_priv *wm1250 = dev_get_drvdata(codec->dev);
+ int ena;
+
+ if (wm1250)
+ ena = wm1250->gpios[WM1250_EV1_GPIO_CLK_ENA].gpio;
+ else
+ ena = -1;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (ena >= 0)
+ gpio_set_value_cansleep(ena, 1);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ if (ena >= 0)
+ gpio_set_value_cansleep(ena, 0);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget wm1250_ev1_dapm_widgets[] = {
+SND_SOC_DAPM_ADC("ADC", "wm1250-ev1 Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_DAC("DAC", "wm1250-ev1 Playback", SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_INPUT("WM1250 Input"),
+SND_SOC_DAPM_OUTPUT("WM1250 Output"),
+};
+
+static const struct snd_soc_dapm_route wm1250_ev1_dapm_routes[] = {
+ { "ADC", NULL, "WM1250 Input" },
+ { "WM1250 Output", NULL, "DAC" },
+};
+
+static int wm1250_ev1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct wm1250_priv *wm1250 = snd_soc_codec_get_drvdata(dai->codec);
+
+ switch (params_rate(params)) {
+ case 8000:
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
+ 1);
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
+ 1);
+ break;
+ case 16000:
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
+ 0);
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
+ 1);
+ break;
+ case 32000:
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
+ 1);
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
+ 0);
+ break;
+ case 64000:
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
+ 0);
+ gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
+ 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wm1250_ev1_ops = {
+ .hw_params = wm1250_ev1_hw_params,
+};
+
+#define WM1250_EV1_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000)
+
+static struct snd_soc_dai_driver wm1250_ev1_dai = {
+ .name = "wm1250-ev1",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM1250_EV1_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM1250_EV1_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &wm1250_ev1_ops,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm1250_ev1 = {
+ .dapm_widgets = wm1250_ev1_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm1250_ev1_dapm_widgets),
+ .dapm_routes = wm1250_ev1_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm1250_ev1_dapm_routes),
+
+ .set_bias_level = wm1250_ev1_set_bias_level,
+ .idle_bias_off = true,
+};
+
+static int wm1250_ev1_pdata(struct i2c_client *i2c)
+{
+ struct wm1250_ev1_pdata *pdata = dev_get_platdata(&i2c->dev);
+ struct wm1250_priv *wm1250;
+ int i, ret;
+
+ if (!pdata)
+ return 0;
+
+ wm1250 = devm_kzalloc(&i2c->dev, sizeof(*wm1250), GFP_KERNEL);
+ if (!wm1250) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm1250->gpios); i++) {
+ wm1250->gpios[i].gpio = pdata->gpios[i];
+ wm1250->gpios[i].label = wm1250_gpio_names[i];
+ wm1250->gpios[i].flags = GPIOF_OUT_INIT_LOW;
+ }
+ wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].flags = GPIOF_OUT_INIT_HIGH;
+ wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].flags = GPIOF_OUT_INIT_HIGH;
+
+ ret = gpio_request_array(wm1250->gpios, ARRAY_SIZE(wm1250->gpios));
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to get GPIOs: %d\n", ret);
+ goto err;
+ }
+
+ dev_set_drvdata(&i2c->dev, wm1250);
+
+ return ret;
+
+err:
+ return ret;
+}
+
+static void wm1250_ev1_free(struct i2c_client *i2c)
+{
+ struct wm1250_priv *wm1250 = dev_get_drvdata(&i2c->dev);
+
+ if (wm1250)
+ gpio_free_array(wm1250->gpios, ARRAY_SIZE(wm1250->gpios));
+}
+
+static int wm1250_ev1_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *i2c_id)
+{
+ int id, board, rev, ret;
+
+ dev_set_drvdata(&i2c->dev, NULL);
+
+ board = i2c_smbus_read_byte_data(i2c, 0);
+ if (board < 0) {
+ dev_err(&i2c->dev, "Failed to read ID: %d\n", board);
+ return board;
+ }
+
+ id = (board & 0xfe) >> 2;
+ rev = board & 0x3;
+
+ if (id != 1) {
+ dev_err(&i2c->dev, "Unknown board ID %d\n", id);
+ return -ENODEV;
+ }
+
+ dev_info(&i2c->dev, "revision %d\n", rev + 1);
+
+ ret = wm1250_ev1_pdata(i2c);
+ if (ret != 0)
+ return ret;
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm1250_ev1,
+ &wm1250_ev1_dai, 1);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+ wm1250_ev1_free(i2c);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wm1250_ev1_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+ wm1250_ev1_free(i2c);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm1250_ev1_i2c_id[] = {
+ { "wm1250-ev1", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm1250_ev1_i2c_id);
+
+static struct i2c_driver wm1250_ev1_i2c_driver = {
+ .driver = {
+ .name = "wm1250-ev1",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm1250_ev1_probe,
+ .remove = wm1250_ev1_remove,
+ .id_table = wm1250_ev1_i2c_id,
+};
+
+module_i2c_driver(wm1250_ev1_i2c_driver);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM1250-EV1 audio I/O module driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm2000.c b/kernel/sound/soc/codecs/wm2000.c
new file mode 100644
index 000000000..21d5402e3
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm2000.c
@@ -0,0 +1,956 @@
+/*
+ * wm2000.c -- WM2000 ALSA Soc Audio driver
+ *
+ * Copyright 2008-2011 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.
+ *
+ * The download image for the WM2000 will be requested as
+ * 'wm2000_anc.bin' by default (overridable via platform data) at
+ * runtime and is expected to be in flat binary format. This is
+ * generated by Wolfson configuration tools and includes
+ * system-specific callibration information. If supplied as a
+ * sequence of ASCII-encoded hexidecimal bytes this can be converted
+ * into a flat binary with a command such as this on the command line:
+ *
+ * perl -e 'while (<>) { s/[\r\n]+// ; printf("%c", hex($_)); }'
+ * < file > wm2000_anc.bin
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/debugfs.h>
+#include <linux/regulator/consumer.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 <sound/wm2000.h>
+
+#include "wm2000.h"
+
+#define WM2000_NUM_SUPPLIES 3
+
+static const char *wm2000_supplies[WM2000_NUM_SUPPLIES] = {
+ "SPKVDD",
+ "DBVDD",
+ "DCVDD",
+};
+
+enum wm2000_anc_mode {
+ ANC_ACTIVE = 0,
+ ANC_BYPASS = 1,
+ ANC_STANDBY = 2,
+ ANC_OFF = 3,
+};
+
+struct wm2000_priv {
+ struct i2c_client *i2c;
+ struct regmap *regmap;
+ struct clk *mclk;
+
+ struct regulator_bulk_data supplies[WM2000_NUM_SUPPLIES];
+
+ enum wm2000_anc_mode anc_mode;
+
+ unsigned int anc_active:1;
+ unsigned int anc_eng_ena:1;
+ unsigned int spk_ena:1;
+
+ unsigned int speech_clarity:1;
+
+ int anc_download_size;
+ char *anc_download;
+
+ struct mutex lock;
+};
+
+static int wm2000_write(struct i2c_client *i2c, unsigned int reg,
+ unsigned int value)
+{
+ struct wm2000_priv *wm2000 = i2c_get_clientdata(i2c);
+ return regmap_write(wm2000->regmap, reg, value);
+}
+
+static unsigned int wm2000_read(struct i2c_client *i2c, unsigned int r)
+{
+ struct wm2000_priv *wm2000 = i2c_get_clientdata(i2c);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(wm2000->regmap, r, &val);
+ if (ret < 0)
+ return -1;
+
+ return val;
+}
+
+static void wm2000_reset(struct wm2000_priv *wm2000)
+{
+ struct i2c_client *i2c = wm2000->i2c;
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_CLR);
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR);
+ wm2000_write(i2c, WM2000_REG_ID1, 0);
+
+ wm2000->anc_mode = ANC_OFF;
+}
+
+static int wm2000_poll_bit(struct i2c_client *i2c,
+ unsigned int reg, u8 mask)
+{
+ int timeout = 4000;
+ int val;
+
+ val = wm2000_read(i2c, reg);
+
+ while (!(val & mask) && --timeout) {
+ msleep(1);
+ val = wm2000_read(i2c, reg);
+ }
+
+ if (timeout == 0)
+ return 0;
+ else
+ return 1;
+}
+
+static int wm2000_power_up(struct i2c_client *i2c, int analogue)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+ unsigned long rate;
+ int ret;
+
+ if (WARN_ON(wm2000->anc_mode != ANC_OFF))
+ return -EINVAL;
+
+ dev_dbg(&i2c->dev, "Beginning power up\n");
+
+ ret = regulator_bulk_enable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ rate = clk_get_rate(wm2000->mclk);
+ if (rate <= 13500000) {
+ dev_dbg(&i2c->dev, "Disabling MCLK divider\n");
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2,
+ WM2000_MCLK_DIV2_ENA_CLR);
+ } else {
+ dev_dbg(&i2c->dev, "Enabling MCLK divider\n");
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2,
+ WM2000_MCLK_DIV2_ENA_SET);
+ }
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_CLR);
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_SET);
+
+ /* Wait for ANC engine to become ready */
+ if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT,
+ WM2000_ANC_ENG_IDLE)) {
+ dev_err(&i2c->dev, "ANC engine failed to reset\n");
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+ return -ETIMEDOUT;
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_BOOT_COMPLETE)) {
+ dev_err(&i2c->dev, "ANC engine failed to initialise\n");
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+ return -ETIMEDOUT;
+ }
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET);
+
+ /* Open code download of the data since it is the only bulk
+ * write we do. */
+ dev_dbg(&i2c->dev, "Downloading %d bytes\n",
+ wm2000->anc_download_size - 2);
+
+ ret = i2c_master_send(i2c, wm2000->anc_download,
+ wm2000->anc_download_size);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "i2c_transfer() failed: %d\n", ret);
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+ return ret;
+ }
+ if (ret != wm2000->anc_download_size) {
+ dev_err(&i2c->dev, "i2c_transfer() failed, %d != %d\n",
+ ret, wm2000->anc_download_size);
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+ return -EIO;
+ }
+
+ dev_dbg(&i2c->dev, "Download complete\n");
+
+ if (analogue) {
+ wm2000_write(i2c, WM2000_REG_ANA_VMID_PU_TIME, 248 / 4);
+
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_ANA_SEQ_INCLUDE |
+ WM2000_MODE_MOUSE_ENABLE |
+ WM2000_MODE_THERMAL_ENABLE);
+ } else {
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_MOUSE_ENABLE |
+ WM2000_MODE_THERMAL_ENABLE);
+ }
+
+ ret = wm2000_read(i2c, WM2000_REG_SPEECH_CLARITY);
+ if (wm2000->speech_clarity)
+ ret |= WM2000_SPEECH_CLARITY;
+ else
+ ret &= ~WM2000_SPEECH_CLARITY;
+ wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, ret);
+
+ wm2000_write(i2c, WM2000_REG_SYS_START0, 0x33);
+ wm2000_write(i2c, WM2000_REG_SYS_START1, 0x02);
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR);
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_MOUSE_ACTIVE)) {
+ dev_err(&i2c->dev, "Timed out waiting for device\n");
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+ return -ETIMEDOUT;
+ }
+
+ dev_dbg(&i2c->dev, "ANC active\n");
+ if (analogue)
+ dev_dbg(&i2c->dev, "Analogue active\n");
+ wm2000->anc_mode = ANC_ACTIVE;
+
+ return 0;
+}
+
+static int wm2000_power_down(struct i2c_client *i2c, int analogue)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+ if (analogue) {
+ wm2000_write(i2c, WM2000_REG_ANA_VMID_PD_TIME, 248 / 4);
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_ANA_SEQ_INCLUDE |
+ WM2000_MODE_POWER_DOWN);
+ } else {
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_POWER_DOWN);
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_POWER_DOWN_COMPLETE)) {
+ dev_err(&i2c->dev, "Timeout waiting for ANC power down\n");
+ return -ETIMEDOUT;
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT,
+ WM2000_ANC_ENG_IDLE)) {
+ dev_err(&i2c->dev, "Timeout waiting for ANC engine idle\n");
+ return -ETIMEDOUT;
+ }
+
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+
+ dev_dbg(&i2c->dev, "powered off\n");
+ wm2000->anc_mode = ANC_OFF;
+
+ return 0;
+}
+
+static int wm2000_enter_bypass(struct i2c_client *i2c, int analogue)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+ if (WARN_ON(wm2000->anc_mode != ANC_ACTIVE))
+ return -EINVAL;
+
+ if (analogue) {
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_ANA_SEQ_INCLUDE |
+ WM2000_MODE_THERMAL_ENABLE |
+ WM2000_MODE_BYPASS_ENTRY);
+ } else {
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_THERMAL_ENABLE |
+ WM2000_MODE_BYPASS_ENTRY);
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_ANC_DISABLED)) {
+ dev_err(&i2c->dev, "Timeout waiting for ANC disable\n");
+ return -ETIMEDOUT;
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT,
+ WM2000_ANC_ENG_IDLE)) {
+ dev_err(&i2c->dev, "Timeout waiting for ANC engine idle\n");
+ return -ETIMEDOUT;
+ }
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL1, WM2000_SYS_STBY);
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR);
+
+ wm2000->anc_mode = ANC_BYPASS;
+ dev_dbg(&i2c->dev, "bypass enabled\n");
+
+ return 0;
+}
+
+static int wm2000_exit_bypass(struct i2c_client *i2c, int analogue)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+ if (WARN_ON(wm2000->anc_mode != ANC_BYPASS))
+ return -EINVAL;
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0);
+
+ if (analogue) {
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_ANA_SEQ_INCLUDE |
+ WM2000_MODE_MOUSE_ENABLE |
+ WM2000_MODE_THERMAL_ENABLE);
+ } else {
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_MOUSE_ENABLE |
+ WM2000_MODE_THERMAL_ENABLE);
+ }
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET);
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR);
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_MOUSE_ACTIVE)) {
+ dev_err(&i2c->dev, "Timed out waiting for MOUSE\n");
+ return -ETIMEDOUT;
+ }
+
+ wm2000->anc_mode = ANC_ACTIVE;
+ dev_dbg(&i2c->dev, "MOUSE active\n");
+
+ return 0;
+}
+
+static int wm2000_enter_standby(struct i2c_client *i2c, int analogue)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+ if (WARN_ON(wm2000->anc_mode != ANC_ACTIVE))
+ return -EINVAL;
+
+ if (analogue) {
+ wm2000_write(i2c, WM2000_REG_ANA_VMID_PD_TIME, 248 / 4);
+
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_ANA_SEQ_INCLUDE |
+ WM2000_MODE_THERMAL_ENABLE |
+ WM2000_MODE_STANDBY_ENTRY);
+ } else {
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_THERMAL_ENABLE |
+ WM2000_MODE_STANDBY_ENTRY);
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_ANC_DISABLED)) {
+ dev_err(&i2c->dev,
+ "Timed out waiting for ANC disable after 1ms\n");
+ return -ETIMEDOUT;
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, WM2000_ANC_ENG_IDLE)) {
+ dev_err(&i2c->dev,
+ "Timed out waiting for standby\n");
+ return -ETIMEDOUT;
+ }
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL1, WM2000_SYS_STBY);
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR);
+
+ wm2000->anc_mode = ANC_STANDBY;
+ dev_dbg(&i2c->dev, "standby\n");
+ if (analogue)
+ dev_dbg(&i2c->dev, "Analogue disabled\n");
+
+ return 0;
+}
+
+static int wm2000_exit_standby(struct i2c_client *i2c, int analogue)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+ if (WARN_ON(wm2000->anc_mode != ANC_STANDBY))
+ return -EINVAL;
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0);
+
+ if (analogue) {
+ wm2000_write(i2c, WM2000_REG_ANA_VMID_PU_TIME, 248 / 4);
+
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_ANA_SEQ_INCLUDE |
+ WM2000_MODE_THERMAL_ENABLE |
+ WM2000_MODE_MOUSE_ENABLE);
+ } else {
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_THERMAL_ENABLE |
+ WM2000_MODE_MOUSE_ENABLE);
+ }
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET);
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR);
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_MOUSE_ACTIVE)) {
+ dev_err(&i2c->dev, "Timed out waiting for MOUSE\n");
+ return -ETIMEDOUT;
+ }
+
+ wm2000->anc_mode = ANC_ACTIVE;
+ dev_dbg(&i2c->dev, "MOUSE active\n");
+ if (analogue)
+ dev_dbg(&i2c->dev, "Analogue enabled\n");
+
+ return 0;
+}
+
+typedef int (*wm2000_mode_fn)(struct i2c_client *i2c, int analogue);
+
+static struct {
+ enum wm2000_anc_mode source;
+ enum wm2000_anc_mode dest;
+ int analogue;
+ wm2000_mode_fn step[2];
+} anc_transitions[] = {
+ {
+ .source = ANC_OFF,
+ .dest = ANC_ACTIVE,
+ .analogue = 1,
+ .step = {
+ wm2000_power_up,
+ },
+ },
+ {
+ .source = ANC_OFF,
+ .dest = ANC_STANDBY,
+ .step = {
+ wm2000_power_up,
+ wm2000_enter_standby,
+ },
+ },
+ {
+ .source = ANC_OFF,
+ .dest = ANC_BYPASS,
+ .analogue = 1,
+ .step = {
+ wm2000_power_up,
+ wm2000_enter_bypass,
+ },
+ },
+ {
+ .source = ANC_ACTIVE,
+ .dest = ANC_BYPASS,
+ .analogue = 1,
+ .step = {
+ wm2000_enter_bypass,
+ },
+ },
+ {
+ .source = ANC_ACTIVE,
+ .dest = ANC_STANDBY,
+ .analogue = 1,
+ .step = {
+ wm2000_enter_standby,
+ },
+ },
+ {
+ .source = ANC_ACTIVE,
+ .dest = ANC_OFF,
+ .analogue = 1,
+ .step = {
+ wm2000_power_down,
+ },
+ },
+ {
+ .source = ANC_BYPASS,
+ .dest = ANC_ACTIVE,
+ .analogue = 1,
+ .step = {
+ wm2000_exit_bypass,
+ },
+ },
+ {
+ .source = ANC_BYPASS,
+ .dest = ANC_STANDBY,
+ .analogue = 1,
+ .step = {
+ wm2000_exit_bypass,
+ wm2000_enter_standby,
+ },
+ },
+ {
+ .source = ANC_BYPASS,
+ .dest = ANC_OFF,
+ .step = {
+ wm2000_exit_bypass,
+ wm2000_power_down,
+ },
+ },
+ {
+ .source = ANC_STANDBY,
+ .dest = ANC_ACTIVE,
+ .analogue = 1,
+ .step = {
+ wm2000_exit_standby,
+ },
+ },
+ {
+ .source = ANC_STANDBY,
+ .dest = ANC_BYPASS,
+ .analogue = 1,
+ .step = {
+ wm2000_exit_standby,
+ wm2000_enter_bypass,
+ },
+ },
+ {
+ .source = ANC_STANDBY,
+ .dest = ANC_OFF,
+ .step = {
+ wm2000_exit_standby,
+ wm2000_power_down,
+ },
+ },
+};
+
+static int wm2000_anc_transition(struct wm2000_priv *wm2000,
+ enum wm2000_anc_mode mode)
+{
+ struct i2c_client *i2c = wm2000->i2c;
+ int i, j;
+ int ret;
+
+ if (wm2000->anc_mode == mode)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(anc_transitions); i++)
+ if (anc_transitions[i].source == wm2000->anc_mode &&
+ anc_transitions[i].dest == mode)
+ break;
+ if (i == ARRAY_SIZE(anc_transitions)) {
+ dev_err(&i2c->dev, "No transition for %d->%d\n",
+ wm2000->anc_mode, mode);
+ return -EINVAL;
+ }
+
+ /* Maintain clock while active */
+ if (anc_transitions[i].source == ANC_OFF) {
+ ret = clk_prepare_enable(wm2000->mclk);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable MCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
+ for (j = 0; j < ARRAY_SIZE(anc_transitions[j].step); j++) {
+ if (!anc_transitions[i].step[j])
+ break;
+ ret = anc_transitions[i].step[j](i2c,
+ anc_transitions[i].analogue);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (anc_transitions[i].dest == ANC_OFF)
+ clk_disable_unprepare(wm2000->mclk);
+
+ return ret;
+}
+
+static int wm2000_anc_set_mode(struct wm2000_priv *wm2000)
+{
+ struct i2c_client *i2c = wm2000->i2c;
+ enum wm2000_anc_mode mode;
+
+ if (wm2000->anc_eng_ena && wm2000->spk_ena)
+ if (wm2000->anc_active)
+ mode = ANC_ACTIVE;
+ else
+ mode = ANC_BYPASS;
+ else
+ mode = ANC_STANDBY;
+
+ dev_dbg(&i2c->dev, "Set mode %d (enabled %d, mute %d, active %d)\n",
+ mode, wm2000->anc_eng_ena, !wm2000->spk_ena,
+ wm2000->anc_active);
+
+ return wm2000_anc_transition(wm2000, mode);
+}
+
+static int wm2000_anc_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+
+ ucontrol->value.integer.value[0] = wm2000->anc_active;
+
+ return 0;
+}
+
+static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+ int anc_active = ucontrol->value.integer.value[0];
+ int ret;
+
+ if (anc_active > 1)
+ return -EINVAL;
+
+ mutex_lock(&wm2000->lock);
+
+ wm2000->anc_active = anc_active;
+
+ ret = wm2000_anc_set_mode(wm2000);
+
+ mutex_unlock(&wm2000->lock);
+
+ return ret;
+}
+
+static int wm2000_speaker_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+
+ ucontrol->value.integer.value[0] = wm2000->spk_ena;
+
+ return 0;
+}
+
+static int wm2000_speaker_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+ int val = ucontrol->value.integer.value[0];
+ int ret;
+
+ if (val > 1)
+ return -EINVAL;
+
+ mutex_lock(&wm2000->lock);
+
+ wm2000->spk_ena = val;
+
+ ret = wm2000_anc_set_mode(wm2000);
+
+ mutex_unlock(&wm2000->lock);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new wm2000_controls[] = {
+ SOC_SINGLE("ANC Volume", WM2000_REG_ANC_GAIN_CTRL, 0, 255, 0),
+ SOC_SINGLE_BOOL_EXT("WM2000 ANC Switch", 0,
+ wm2000_anc_mode_get,
+ wm2000_anc_mode_put),
+ SOC_SINGLE_BOOL_EXT("WM2000 Switch", 0,
+ wm2000_speaker_get,
+ wm2000_speaker_put),
+};
+
+static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+ int ret;
+
+ mutex_lock(&wm2000->lock);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ wm2000->anc_eng_ena = 1;
+
+ if (SND_SOC_DAPM_EVENT_OFF(event))
+ wm2000->anc_eng_ena = 0;
+
+ ret = wm2000_anc_set_mode(wm2000);
+
+ mutex_unlock(&wm2000->lock);
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = {
+/* Externally visible pins */
+SND_SOC_DAPM_OUTPUT("SPKN"),
+SND_SOC_DAPM_OUTPUT("SPKP"),
+
+SND_SOC_DAPM_INPUT("LINN"),
+SND_SOC_DAPM_INPUT("LINP"),
+
+SND_SOC_DAPM_PGA_E("ANC Engine", SND_SOC_NOPM, 0, 0, NULL, 0,
+ wm2000_anc_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+};
+
+/* Target, Path, Source */
+static const struct snd_soc_dapm_route wm2000_audio_map[] = {
+ { "SPKN", NULL, "ANC Engine" },
+ { "SPKP", NULL, "ANC Engine" },
+ { "ANC Engine", NULL, "LINN" },
+ { "ANC Engine", NULL, "LINP" },
+};
+
+#ifdef CONFIG_PM
+static int wm2000_suspend(struct snd_soc_codec *codec)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+
+ return wm2000_anc_transition(wm2000, ANC_OFF);
+}
+
+static int wm2000_resume(struct snd_soc_codec *codec)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+
+ return wm2000_anc_set_mode(wm2000);
+}
+#else
+#define wm2000_suspend NULL
+#define wm2000_resume NULL
+#endif
+
+static bool wm2000_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM2000_REG_SYS_START:
+ case WM2000_REG_ANC_GAIN_CTRL:
+ case WM2000_REG_MSE_TH1:
+ case WM2000_REG_MSE_TH2:
+ case WM2000_REG_SPEECH_CLARITY:
+ case WM2000_REG_SYS_WATCHDOG:
+ case WM2000_REG_ANA_VMID_PD_TIME:
+ case WM2000_REG_ANA_VMID_PU_TIME:
+ case WM2000_REG_CAT_FLTR_INDX:
+ case WM2000_REG_CAT_GAIN_0:
+ case WM2000_REG_SYS_STATUS:
+ case WM2000_REG_SYS_MODE_CNTRL:
+ case WM2000_REG_SYS_START0:
+ case WM2000_REG_SYS_START1:
+ case WM2000_REG_ID1:
+ case WM2000_REG_ID2:
+ case WM2000_REG_REVISON:
+ case WM2000_REG_SYS_CTL1:
+ case WM2000_REG_SYS_CTL2:
+ case WM2000_REG_ANC_STAT:
+ case WM2000_REG_IF_CTL:
+ case WM2000_REG_ANA_MIC_CTL:
+ case WM2000_REG_SPK_CTL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config wm2000_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+
+ .max_register = WM2000_REG_SPK_CTL,
+ .readable_reg = wm2000_readable_reg,
+};
+
+static int wm2000_probe(struct snd_soc_codec *codec)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+
+ /* This will trigger a transition to standby mode by default */
+ wm2000_anc_set_mode(wm2000);
+
+ return 0;
+}
+
+static int wm2000_remove(struct snd_soc_codec *codec)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+
+ return wm2000_anc_transition(wm2000, ANC_OFF);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm2000 = {
+ .probe = wm2000_probe,
+ .remove = wm2000_remove,
+ .suspend = wm2000_suspend,
+ .resume = wm2000_resume,
+
+ .dapm_widgets = wm2000_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm2000_dapm_widgets),
+ .dapm_routes = wm2000_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm2000_audio_map),
+ .controls = wm2000_controls,
+ .num_controls = ARRAY_SIZE(wm2000_controls),
+};
+
+static int wm2000_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *i2c_id)
+{
+ struct wm2000_priv *wm2000;
+ struct wm2000_platform_data *pdata;
+ const char *filename;
+ const struct firmware *fw = NULL;
+ int ret, i;
+ int reg;
+ u16 id;
+
+ wm2000 = devm_kzalloc(&i2c->dev, sizeof(struct wm2000_priv),
+ GFP_KERNEL);
+ if (!wm2000)
+ return -ENOMEM;
+
+ mutex_init(&wm2000->lock);
+
+ dev_set_drvdata(&i2c->dev, wm2000);
+
+ wm2000->regmap = devm_regmap_init_i2c(i2c, &wm2000_regmap);
+ if (IS_ERR(wm2000->regmap)) {
+ ret = PTR_ERR(wm2000->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ goto out;
+ }
+
+ for (i = 0; i < WM2000_NUM_SUPPLIES; i++)
+ wm2000->supplies[i].supply = wm2000_supplies[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, WM2000_NUM_SUPPLIES,
+ wm2000->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to get supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Verify that this is a WM2000 */
+ reg = wm2000_read(i2c, WM2000_REG_ID1);
+ id = reg << 8;
+ reg = wm2000_read(i2c, WM2000_REG_ID2);
+ id |= reg & 0xff;
+
+ if (id != 0x2000) {
+ dev_err(&i2c->dev, "Device is not a WM2000 - ID %x\n", id);
+ ret = -ENODEV;
+ goto err_supplies;
+ }
+
+ reg = wm2000_read(i2c, WM2000_REG_REVISON);
+ dev_info(&i2c->dev, "revision %c\n", reg + 'A');
+
+ wm2000->mclk = devm_clk_get(&i2c->dev, "MCLK");
+ if (IS_ERR(wm2000->mclk)) {
+ ret = PTR_ERR(wm2000->mclk);
+ dev_err(&i2c->dev, "Failed to get MCLK: %d\n", ret);
+ goto err_supplies;
+ }
+
+ filename = "wm2000_anc.bin";
+ pdata = dev_get_platdata(&i2c->dev);
+ if (pdata) {
+ wm2000->speech_clarity = !pdata->speech_enh_disable;
+
+ if (pdata->download_file)
+ filename = pdata->download_file;
+ }
+
+ ret = request_firmware(&fw, filename, &i2c->dev);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to acquire ANC data: %d\n", ret);
+ goto err_supplies;
+ }
+
+ /* Pre-cook the concatenation of the register address onto the image */
+ wm2000->anc_download_size = fw->size + 2;
+ wm2000->anc_download = devm_kzalloc(&i2c->dev,
+ wm2000->anc_download_size,
+ GFP_KERNEL);
+ if (wm2000->anc_download == NULL) {
+ dev_err(&i2c->dev, "Out of memory\n");
+ ret = -ENOMEM;
+ goto err_supplies;
+ }
+
+ wm2000->anc_download[0] = 0x80;
+ wm2000->anc_download[1] = 0x00;
+ memcpy(wm2000->anc_download + 2, fw->data, fw->size);
+
+ wm2000->anc_eng_ena = 1;
+ wm2000->anc_active = 1;
+ wm2000->spk_ena = 1;
+ wm2000->i2c = i2c;
+
+ wm2000_reset(wm2000);
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm2000, NULL, 0);
+
+err_supplies:
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+
+out:
+ release_firmware(fw);
+ return ret;
+}
+
+static int wm2000_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm2000_i2c_id[] = {
+ { "wm2000", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm2000_i2c_id);
+
+static struct i2c_driver wm2000_i2c_driver = {
+ .driver = {
+ .name = "wm2000",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm2000_i2c_probe,
+ .remove = wm2000_i2c_remove,
+ .id_table = wm2000_i2c_id,
+};
+
+module_i2c_driver(wm2000_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM2000 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm2000.h b/kernel/sound/soc/codecs/wm2000.h
new file mode 100644
index 000000000..3870c0e1d
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm2000.h
@@ -0,0 +1,74 @@
+/*
+ * wm2000.h -- WM2000 Soc Audio driver
+ *
+ * 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 _WM2000_H
+#define _WM2000_H
+
+#define WM2000_REG_SYS_START 0x8000
+#define WM2000_REG_ANC_GAIN_CTRL 0x8fa2
+#define WM2000_REG_MSE_TH2 0x8fdf
+#define WM2000_REG_MSE_TH1 0x8fe0
+#define WM2000_REG_SPEECH_CLARITY 0x8fef
+#define WM2000_REG_SYS_WATCHDOG 0x8ff6
+#define WM2000_REG_ANA_VMID_PD_TIME 0x8ff7
+#define WM2000_REG_ANA_VMID_PU_TIME 0x8ff8
+#define WM2000_REG_CAT_FLTR_INDX 0x8ff9
+#define WM2000_REG_CAT_GAIN_0 0x8ffa
+#define WM2000_REG_SYS_STATUS 0x8ffc
+#define WM2000_REG_SYS_MODE_CNTRL 0x8ffd
+#define WM2000_REG_SYS_START0 0x8ffe
+#define WM2000_REG_SYS_START1 0x8fff
+#define WM2000_REG_ID1 0xf000
+#define WM2000_REG_ID2 0xf001
+#define WM2000_REG_REVISON 0xf002
+#define WM2000_REG_SYS_CTL1 0xf003
+#define WM2000_REG_SYS_CTL2 0xf004
+#define WM2000_REG_ANC_STAT 0xf005
+#define WM2000_REG_IF_CTL 0xf006
+#define WM2000_REG_ANA_MIC_CTL 0xf028
+#define WM2000_REG_SPK_CTL 0xf034
+
+/* SPEECH_CLARITY */
+#define WM2000_SPEECH_CLARITY 0x01
+
+/* SYS_STATUS */
+#define WM2000_STATUS_MOUSE_ACTIVE 0x40
+#define WM2000_STATUS_CAT_FREQ_COMPLETE 0x20
+#define WM2000_STATUS_CAT_GAIN_COMPLETE 0x10
+#define WM2000_STATUS_THERMAL_SHUTDOWN_COMPLETE 0x08
+#define WM2000_STATUS_ANC_DISABLED 0x04
+#define WM2000_STATUS_POWER_DOWN_COMPLETE 0x02
+#define WM2000_STATUS_BOOT_COMPLETE 0x01
+
+/* SYS_MODE_CNTRL */
+#define WM2000_MODE_ANA_SEQ_INCLUDE 0x80
+#define WM2000_MODE_MOUSE_ENABLE 0x40
+#define WM2000_MODE_CAT_FREQ_ENABLE 0x20
+#define WM2000_MODE_CAT_GAIN_ENABLE 0x10
+#define WM2000_MODE_BYPASS_ENTRY 0x08
+#define WM2000_MODE_STANDBY_ENTRY 0x04
+#define WM2000_MODE_THERMAL_ENABLE 0x02
+#define WM2000_MODE_POWER_DOWN 0x01
+
+/* SYS_CTL1 */
+#define WM2000_SYS_STBY 0x01
+
+/* SYS_CTL2 */
+#define WM2000_MCLK_DIV2_ENA_CLR 0x80
+#define WM2000_MCLK_DIV2_ENA_SET 0x40
+#define WM2000_ANC_ENG_CLR 0x20
+#define WM2000_ANC_ENG_SET 0x10
+#define WM2000_ANC_INT_N_CLR 0x08
+#define WM2000_ANC_INT_N_SET 0x04
+#define WM2000_RAM_CLR 0x02
+#define WM2000_RAM_SET 0x01
+
+/* ANC_STAT */
+#define WM2000_ANC_ENG_IDLE 0x01
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm2200.c b/kernel/sound/soc/codecs/wm2200.c
new file mode 100644
index 000000000..5a9da28f4
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm2200.c
@@ -0,0 +1,2510 @@
+/*
+ * wm2200.c -- WM2200 ALSA SoC Audio driver
+ *
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/firmware.h>
+#include <linux/gcd.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/fixed.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 <sound/wm2200.h>
+
+#include "wm2200.h"
+#include "wmfw.h"
+#include "wm_adsp.h"
+
+#define WM2200_DSP_CONTROL_1 0x00
+#define WM2200_DSP_CONTROL_2 0x02
+#define WM2200_DSP_CONTROL_3 0x03
+#define WM2200_DSP_CONTROL_4 0x04
+#define WM2200_DSP_CONTROL_5 0x06
+#define WM2200_DSP_CONTROL_6 0x07
+#define WM2200_DSP_CONTROL_7 0x08
+#define WM2200_DSP_CONTROL_8 0x09
+#define WM2200_DSP_CONTROL_9 0x0A
+#define WM2200_DSP_CONTROL_10 0x0B
+#define WM2200_DSP_CONTROL_11 0x0C
+#define WM2200_DSP_CONTROL_12 0x0D
+#define WM2200_DSP_CONTROL_13 0x0F
+#define WM2200_DSP_CONTROL_14 0x10
+#define WM2200_DSP_CONTROL_15 0x11
+#define WM2200_DSP_CONTROL_16 0x12
+#define WM2200_DSP_CONTROL_17 0x13
+#define WM2200_DSP_CONTROL_18 0x14
+#define WM2200_DSP_CONTROL_19 0x16
+#define WM2200_DSP_CONTROL_20 0x17
+#define WM2200_DSP_CONTROL_21 0x18
+#define WM2200_DSP_CONTROL_22 0x1A
+#define WM2200_DSP_CONTROL_23 0x1B
+#define WM2200_DSP_CONTROL_24 0x1C
+#define WM2200_DSP_CONTROL_25 0x1E
+#define WM2200_DSP_CONTROL_26 0x20
+#define WM2200_DSP_CONTROL_27 0x21
+#define WM2200_DSP_CONTROL_28 0x22
+#define WM2200_DSP_CONTROL_29 0x23
+#define WM2200_DSP_CONTROL_30 0x24
+#define WM2200_DSP_CONTROL_31 0x26
+
+/* The code assumes DCVDD is generated internally */
+#define WM2200_NUM_CORE_SUPPLIES 2
+static const char *wm2200_core_supply_names[WM2200_NUM_CORE_SUPPLIES] = {
+ "DBVDD",
+ "LDOVDD",
+};
+
+struct wm2200_fll {
+ int fref;
+ int fout;
+ int src;
+ struct completion lock;
+};
+
+/* codec private data */
+struct wm2200_priv {
+ struct wm_adsp dsp[2];
+ struct regmap *regmap;
+ struct device *dev;
+ struct snd_soc_codec *codec;
+ struct wm2200_pdata pdata;
+ struct regulator_bulk_data core_supplies[WM2200_NUM_CORE_SUPPLIES];
+
+ struct completion fll_lock;
+ int fll_fout;
+ int fll_fref;
+ int fll_src;
+
+ int rev;
+ int sysclk;
+};
+
+#define WM2200_DSP_RANGE_BASE (WM2200_MAX_REGISTER + 1)
+#define WM2200_DSP_SPACING 12288
+
+#define WM2200_DSP1_DM_BASE (WM2200_DSP_RANGE_BASE + (0 * WM2200_DSP_SPACING))
+#define WM2200_DSP1_PM_BASE (WM2200_DSP_RANGE_BASE + (1 * WM2200_DSP_SPACING))
+#define WM2200_DSP1_ZM_BASE (WM2200_DSP_RANGE_BASE + (2 * WM2200_DSP_SPACING))
+#define WM2200_DSP2_DM_BASE (WM2200_DSP_RANGE_BASE + (3 * WM2200_DSP_SPACING))
+#define WM2200_DSP2_PM_BASE (WM2200_DSP_RANGE_BASE + (4 * WM2200_DSP_SPACING))
+#define WM2200_DSP2_ZM_BASE (WM2200_DSP_RANGE_BASE + (5 * WM2200_DSP_SPACING))
+
+static const struct regmap_range_cfg wm2200_ranges[] = {
+ { .name = "DSP1DM", .range_min = WM2200_DSP1_DM_BASE,
+ .range_max = WM2200_DSP1_DM_BASE + 12287,
+ .selector_reg = WM2200_DSP1_CONTROL_3,
+ .selector_mask = WM2200_DSP1_PAGE_BASE_DM_0_MASK,
+ .selector_shift = WM2200_DSP1_PAGE_BASE_DM_0_SHIFT,
+ .window_start = WM2200_DSP1_DM_0, .window_len = 2048, },
+
+ { .name = "DSP1PM", .range_min = WM2200_DSP1_PM_BASE,
+ .range_max = WM2200_DSP1_PM_BASE + 12287,
+ .selector_reg = WM2200_DSP1_CONTROL_2,
+ .selector_mask = WM2200_DSP1_PAGE_BASE_PM_0_MASK,
+ .selector_shift = WM2200_DSP1_PAGE_BASE_PM_0_SHIFT,
+ .window_start = WM2200_DSP1_PM_0, .window_len = 768, },
+
+ { .name = "DSP1ZM", .range_min = WM2200_DSP1_ZM_BASE,
+ .range_max = WM2200_DSP1_ZM_BASE + 2047,
+ .selector_reg = WM2200_DSP1_CONTROL_4,
+ .selector_mask = WM2200_DSP1_PAGE_BASE_ZM_0_MASK,
+ .selector_shift = WM2200_DSP1_PAGE_BASE_ZM_0_SHIFT,
+ .window_start = WM2200_DSP1_ZM_0, .window_len = 1024, },
+
+ { .name = "DSP2DM", .range_min = WM2200_DSP2_DM_BASE,
+ .range_max = WM2200_DSP2_DM_BASE + 4095,
+ .selector_reg = WM2200_DSP2_CONTROL_3,
+ .selector_mask = WM2200_DSP2_PAGE_BASE_DM_0_MASK,
+ .selector_shift = WM2200_DSP2_PAGE_BASE_DM_0_SHIFT,
+ .window_start = WM2200_DSP2_DM_0, .window_len = 2048, },
+
+ { .name = "DSP2PM", .range_min = WM2200_DSP2_PM_BASE,
+ .range_max = WM2200_DSP2_PM_BASE + 11287,
+ .selector_reg = WM2200_DSP2_CONTROL_2,
+ .selector_mask = WM2200_DSP2_PAGE_BASE_PM_0_MASK,
+ .selector_shift = WM2200_DSP2_PAGE_BASE_PM_0_SHIFT,
+ .window_start = WM2200_DSP2_PM_0, .window_len = 768, },
+
+ { .name = "DSP2ZM", .range_min = WM2200_DSP2_ZM_BASE,
+ .range_max = WM2200_DSP2_ZM_BASE + 2047,
+ .selector_reg = WM2200_DSP2_CONTROL_4,
+ .selector_mask = WM2200_DSP2_PAGE_BASE_ZM_0_MASK,
+ .selector_shift = WM2200_DSP2_PAGE_BASE_ZM_0_SHIFT,
+ .window_start = WM2200_DSP2_ZM_0, .window_len = 1024, },
+};
+
+static const struct wm_adsp_region wm2200_dsp1_regions[] = {
+ { .type = WMFW_ADSP1_PM, .base = WM2200_DSP1_PM_BASE },
+ { .type = WMFW_ADSP1_DM, .base = WM2200_DSP1_DM_BASE },
+ { .type = WMFW_ADSP1_ZM, .base = WM2200_DSP1_ZM_BASE },
+};
+
+static const struct wm_adsp_region wm2200_dsp2_regions[] = {
+ { .type = WMFW_ADSP1_PM, .base = WM2200_DSP2_PM_BASE },
+ { .type = WMFW_ADSP1_DM, .base = WM2200_DSP2_DM_BASE },
+ { .type = WMFW_ADSP1_ZM, .base = WM2200_DSP2_ZM_BASE },
+};
+
+static struct reg_default wm2200_reg_defaults[] = {
+ { 0x000B, 0x0000 }, /* R11 - Tone Generator 1 */
+ { 0x0102, 0x0000 }, /* R258 - Clocking 3 */
+ { 0x0103, 0x0011 }, /* R259 - Clocking 4 */
+ { 0x0111, 0x0000 }, /* R273 - FLL Control 1 */
+ { 0x0112, 0x0000 }, /* R274 - FLL Control 2 */
+ { 0x0113, 0x0000 }, /* R275 - FLL Control 3 */
+ { 0x0114, 0x0000 }, /* R276 - FLL Control 4 */
+ { 0x0116, 0x0177 }, /* R278 - FLL Control 6 */
+ { 0x0117, 0x0004 }, /* R279 - FLL Control 7 */
+ { 0x0119, 0x0000 }, /* R281 - FLL EFS 1 */
+ { 0x011A, 0x0002 }, /* R282 - FLL EFS 2 */
+ { 0x0200, 0x0000 }, /* R512 - Mic Charge Pump 1 */
+ { 0x0201, 0x03FF }, /* R513 - Mic Charge Pump 2 */
+ { 0x0202, 0x9BDE }, /* R514 - DM Charge Pump 1 */
+ { 0x020C, 0x0000 }, /* R524 - Mic Bias Ctrl 1 */
+ { 0x020D, 0x0000 }, /* R525 - Mic Bias Ctrl 2 */
+ { 0x020F, 0x0000 }, /* R527 - Ear Piece Ctrl 1 */
+ { 0x0210, 0x0000 }, /* R528 - Ear Piece Ctrl 2 */
+ { 0x0301, 0x0000 }, /* R769 - Input Enables */
+ { 0x0302, 0x2240 }, /* R770 - IN1L Control */
+ { 0x0303, 0x0040 }, /* R771 - IN1R Control */
+ { 0x0304, 0x2240 }, /* R772 - IN2L Control */
+ { 0x0305, 0x0040 }, /* R773 - IN2R Control */
+ { 0x0306, 0x2240 }, /* R774 - IN3L Control */
+ { 0x0307, 0x0040 }, /* R775 - IN3R Control */
+ { 0x030A, 0x0000 }, /* R778 - RXANC_SRC */
+ { 0x030B, 0x0022 }, /* R779 - Input Volume Ramp */
+ { 0x030C, 0x0180 }, /* R780 - ADC Digital Volume 1L */
+ { 0x030D, 0x0180 }, /* R781 - ADC Digital Volume 1R */
+ { 0x030E, 0x0180 }, /* R782 - ADC Digital Volume 2L */
+ { 0x030F, 0x0180 }, /* R783 - ADC Digital Volume 2R */
+ { 0x0310, 0x0180 }, /* R784 - ADC Digital Volume 3L */
+ { 0x0311, 0x0180 }, /* R785 - ADC Digital Volume 3R */
+ { 0x0400, 0x0000 }, /* R1024 - Output Enables */
+ { 0x0401, 0x0000 }, /* R1025 - DAC Volume Limit 1L */
+ { 0x0402, 0x0000 }, /* R1026 - DAC Volume Limit 1R */
+ { 0x0403, 0x0000 }, /* R1027 - DAC Volume Limit 2L */
+ { 0x0404, 0x0000 }, /* R1028 - DAC Volume Limit 2R */
+ { 0x0409, 0x0000 }, /* R1033 - DAC AEC Control 1 */
+ { 0x040A, 0x0022 }, /* R1034 - Output Volume Ramp */
+ { 0x040B, 0x0180 }, /* R1035 - DAC Digital Volume 1L */
+ { 0x040C, 0x0180 }, /* R1036 - DAC Digital Volume 1R */
+ { 0x040D, 0x0180 }, /* R1037 - DAC Digital Volume 2L */
+ { 0x040E, 0x0180 }, /* R1038 - DAC Digital Volume 2R */
+ { 0x0417, 0x0069 }, /* R1047 - PDM 1 */
+ { 0x0418, 0x0000 }, /* R1048 - PDM 2 */
+ { 0x0500, 0x0000 }, /* R1280 - Audio IF 1_1 */
+ { 0x0501, 0x0008 }, /* R1281 - Audio IF 1_2 */
+ { 0x0502, 0x0000 }, /* R1282 - Audio IF 1_3 */
+ { 0x0503, 0x0000 }, /* R1283 - Audio IF 1_4 */
+ { 0x0504, 0x0000 }, /* R1284 - Audio IF 1_5 */
+ { 0x0505, 0x0001 }, /* R1285 - Audio IF 1_6 */
+ { 0x0506, 0x0001 }, /* R1286 - Audio IF 1_7 */
+ { 0x0507, 0x0000 }, /* R1287 - Audio IF 1_8 */
+ { 0x0508, 0x0000 }, /* R1288 - Audio IF 1_9 */
+ { 0x0509, 0x0000 }, /* R1289 - Audio IF 1_10 */
+ { 0x050A, 0x0000 }, /* R1290 - Audio IF 1_11 */
+ { 0x050B, 0x0000 }, /* R1291 - Audio IF 1_12 */
+ { 0x050C, 0x0000 }, /* R1292 - Audio IF 1_13 */
+ { 0x050D, 0x0000 }, /* R1293 - Audio IF 1_14 */
+ { 0x050E, 0x0000 }, /* R1294 - Audio IF 1_15 */
+ { 0x050F, 0x0000 }, /* R1295 - Audio IF 1_16 */
+ { 0x0510, 0x0000 }, /* R1296 - Audio IF 1_17 */
+ { 0x0511, 0x0000 }, /* R1297 - Audio IF 1_18 */
+ { 0x0512, 0x0000 }, /* R1298 - Audio IF 1_19 */
+ { 0x0513, 0x0000 }, /* R1299 - Audio IF 1_20 */
+ { 0x0514, 0x0000 }, /* R1300 - Audio IF 1_21 */
+ { 0x0515, 0x0001 }, /* R1301 - Audio IF 1_22 */
+ { 0x0600, 0x0000 }, /* R1536 - OUT1LMIX Input 1 Source */
+ { 0x0601, 0x0080 }, /* R1537 - OUT1LMIX Input 1 Volume */
+ { 0x0602, 0x0000 }, /* R1538 - OUT1LMIX Input 2 Source */
+ { 0x0603, 0x0080 }, /* R1539 - OUT1LMIX Input 2 Volume */
+ { 0x0604, 0x0000 }, /* R1540 - OUT1LMIX Input 3 Source */
+ { 0x0605, 0x0080 }, /* R1541 - OUT1LMIX Input 3 Volume */
+ { 0x0606, 0x0000 }, /* R1542 - OUT1LMIX Input 4 Source */
+ { 0x0607, 0x0080 }, /* R1543 - OUT1LMIX Input 4 Volume */
+ { 0x0608, 0x0000 }, /* R1544 - OUT1RMIX Input 1 Source */
+ { 0x0609, 0x0080 }, /* R1545 - OUT1RMIX Input 1 Volume */
+ { 0x060A, 0x0000 }, /* R1546 - OUT1RMIX Input 2 Source */
+ { 0x060B, 0x0080 }, /* R1547 - OUT1RMIX Input 2 Volume */
+ { 0x060C, 0x0000 }, /* R1548 - OUT1RMIX Input 3 Source */
+ { 0x060D, 0x0080 }, /* R1549 - OUT1RMIX Input 3 Volume */
+ { 0x060E, 0x0000 }, /* R1550 - OUT1RMIX Input 4 Source */
+ { 0x060F, 0x0080 }, /* R1551 - OUT1RMIX Input 4 Volume */
+ { 0x0610, 0x0000 }, /* R1552 - OUT2LMIX Input 1 Source */
+ { 0x0611, 0x0080 }, /* R1553 - OUT2LMIX Input 1 Volume */
+ { 0x0612, 0x0000 }, /* R1554 - OUT2LMIX Input 2 Source */
+ { 0x0613, 0x0080 }, /* R1555 - OUT2LMIX Input 2 Volume */
+ { 0x0614, 0x0000 }, /* R1556 - OUT2LMIX Input 3 Source */
+ { 0x0615, 0x0080 }, /* R1557 - OUT2LMIX Input 3 Volume */
+ { 0x0616, 0x0000 }, /* R1558 - OUT2LMIX Input 4 Source */
+ { 0x0617, 0x0080 }, /* R1559 - OUT2LMIX Input 4 Volume */
+ { 0x0618, 0x0000 }, /* R1560 - OUT2RMIX Input 1 Source */
+ { 0x0619, 0x0080 }, /* R1561 - OUT2RMIX Input 1 Volume */
+ { 0x061A, 0x0000 }, /* R1562 - OUT2RMIX Input 2 Source */
+ { 0x061B, 0x0080 }, /* R1563 - OUT2RMIX Input 2 Volume */
+ { 0x061C, 0x0000 }, /* R1564 - OUT2RMIX Input 3 Source */
+ { 0x061D, 0x0080 }, /* R1565 - OUT2RMIX Input 3 Volume */
+ { 0x061E, 0x0000 }, /* R1566 - OUT2RMIX Input 4 Source */
+ { 0x061F, 0x0080 }, /* R1567 - OUT2RMIX Input 4 Volume */
+ { 0x0620, 0x0000 }, /* R1568 - AIF1TX1MIX Input 1 Source */
+ { 0x0621, 0x0080 }, /* R1569 - AIF1TX1MIX Input 1 Volume */
+ { 0x0622, 0x0000 }, /* R1570 - AIF1TX1MIX Input 2 Source */
+ { 0x0623, 0x0080 }, /* R1571 - AIF1TX1MIX Input 2 Volume */
+ { 0x0624, 0x0000 }, /* R1572 - AIF1TX1MIX Input 3 Source */
+ { 0x0625, 0x0080 }, /* R1573 - AIF1TX1MIX Input 3 Volume */
+ { 0x0626, 0x0000 }, /* R1574 - AIF1TX1MIX Input 4 Source */
+ { 0x0627, 0x0080 }, /* R1575 - AIF1TX1MIX Input 4 Volume */
+ { 0x0628, 0x0000 }, /* R1576 - AIF1TX2MIX Input 1 Source */
+ { 0x0629, 0x0080 }, /* R1577 - AIF1TX2MIX Input 1 Volume */
+ { 0x062A, 0x0000 }, /* R1578 - AIF1TX2MIX Input 2 Source */
+ { 0x062B, 0x0080 }, /* R1579 - AIF1TX2MIX Input 2 Volume */
+ { 0x062C, 0x0000 }, /* R1580 - AIF1TX2MIX Input 3 Source */
+ { 0x062D, 0x0080 }, /* R1581 - AIF1TX2MIX Input 3 Volume */
+ { 0x062E, 0x0000 }, /* R1582 - AIF1TX2MIX Input 4 Source */
+ { 0x062F, 0x0080 }, /* R1583 - AIF1TX2MIX Input 4 Volume */
+ { 0x0630, 0x0000 }, /* R1584 - AIF1TX3MIX Input 1 Source */
+ { 0x0631, 0x0080 }, /* R1585 - AIF1TX3MIX Input 1 Volume */
+ { 0x0632, 0x0000 }, /* R1586 - AIF1TX3MIX Input 2 Source */
+ { 0x0633, 0x0080 }, /* R1587 - AIF1TX3MIX Input 2 Volume */
+ { 0x0634, 0x0000 }, /* R1588 - AIF1TX3MIX Input 3 Source */
+ { 0x0635, 0x0080 }, /* R1589 - AIF1TX3MIX Input 3 Volume */
+ { 0x0636, 0x0000 }, /* R1590 - AIF1TX3MIX Input 4 Source */
+ { 0x0637, 0x0080 }, /* R1591 - AIF1TX3MIX Input 4 Volume */
+ { 0x0638, 0x0000 }, /* R1592 - AIF1TX4MIX Input 1 Source */
+ { 0x0639, 0x0080 }, /* R1593 - AIF1TX4MIX Input 1 Volume */
+ { 0x063A, 0x0000 }, /* R1594 - AIF1TX4MIX Input 2 Source */
+ { 0x063B, 0x0080 }, /* R1595 - AIF1TX4MIX Input 2 Volume */
+ { 0x063C, 0x0000 }, /* R1596 - AIF1TX4MIX Input 3 Source */
+ { 0x063D, 0x0080 }, /* R1597 - AIF1TX4MIX Input 3 Volume */
+ { 0x063E, 0x0000 }, /* R1598 - AIF1TX4MIX Input 4 Source */
+ { 0x063F, 0x0080 }, /* R1599 - AIF1TX4MIX Input 4 Volume */
+ { 0x0640, 0x0000 }, /* R1600 - AIF1TX5MIX Input 1 Source */
+ { 0x0641, 0x0080 }, /* R1601 - AIF1TX5MIX Input 1 Volume */
+ { 0x0642, 0x0000 }, /* R1602 - AIF1TX5MIX Input 2 Source */
+ { 0x0643, 0x0080 }, /* R1603 - AIF1TX5MIX Input 2 Volume */
+ { 0x0644, 0x0000 }, /* R1604 - AIF1TX5MIX Input 3 Source */
+ { 0x0645, 0x0080 }, /* R1605 - AIF1TX5MIX Input 3 Volume */
+ { 0x0646, 0x0000 }, /* R1606 - AIF1TX5MIX Input 4 Source */
+ { 0x0647, 0x0080 }, /* R1607 - AIF1TX5MIX Input 4 Volume */
+ { 0x0648, 0x0000 }, /* R1608 - AIF1TX6MIX Input 1 Source */
+ { 0x0649, 0x0080 }, /* R1609 - AIF1TX6MIX Input 1 Volume */
+ { 0x064A, 0x0000 }, /* R1610 - AIF1TX6MIX Input 2 Source */
+ { 0x064B, 0x0080 }, /* R1611 - AIF1TX6MIX Input 2 Volume */
+ { 0x064C, 0x0000 }, /* R1612 - AIF1TX6MIX Input 3 Source */
+ { 0x064D, 0x0080 }, /* R1613 - AIF1TX6MIX Input 3 Volume */
+ { 0x064E, 0x0000 }, /* R1614 - AIF1TX6MIX Input 4 Source */
+ { 0x064F, 0x0080 }, /* R1615 - AIF1TX6MIX Input 4 Volume */
+ { 0x0650, 0x0000 }, /* R1616 - EQLMIX Input 1 Source */
+ { 0x0651, 0x0080 }, /* R1617 - EQLMIX Input 1 Volume */
+ { 0x0652, 0x0000 }, /* R1618 - EQLMIX Input 2 Source */
+ { 0x0653, 0x0080 }, /* R1619 - EQLMIX Input 2 Volume */
+ { 0x0654, 0x0000 }, /* R1620 - EQLMIX Input 3 Source */
+ { 0x0655, 0x0080 }, /* R1621 - EQLMIX Input 3 Volume */
+ { 0x0656, 0x0000 }, /* R1622 - EQLMIX Input 4 Source */
+ { 0x0657, 0x0080 }, /* R1623 - EQLMIX Input 4 Volume */
+ { 0x0658, 0x0000 }, /* R1624 - EQRMIX Input 1 Source */
+ { 0x0659, 0x0080 }, /* R1625 - EQRMIX Input 1 Volume */
+ { 0x065A, 0x0000 }, /* R1626 - EQRMIX Input 2 Source */
+ { 0x065B, 0x0080 }, /* R1627 - EQRMIX Input 2 Volume */
+ { 0x065C, 0x0000 }, /* R1628 - EQRMIX Input 3 Source */
+ { 0x065D, 0x0080 }, /* R1629 - EQRMIX Input 3 Volume */
+ { 0x065E, 0x0000 }, /* R1630 - EQRMIX Input 4 Source */
+ { 0x065F, 0x0080 }, /* R1631 - EQRMIX Input 4 Volume */
+ { 0x0660, 0x0000 }, /* R1632 - LHPF1MIX Input 1 Source */
+ { 0x0661, 0x0080 }, /* R1633 - LHPF1MIX Input 1 Volume */
+ { 0x0662, 0x0000 }, /* R1634 - LHPF1MIX Input 2 Source */
+ { 0x0663, 0x0080 }, /* R1635 - LHPF1MIX Input 2 Volume */
+ { 0x0664, 0x0000 }, /* R1636 - LHPF1MIX Input 3 Source */
+ { 0x0665, 0x0080 }, /* R1637 - LHPF1MIX Input 3 Volume */
+ { 0x0666, 0x0000 }, /* R1638 - LHPF1MIX Input 4 Source */
+ { 0x0667, 0x0080 }, /* R1639 - LHPF1MIX Input 4 Volume */
+ { 0x0668, 0x0000 }, /* R1640 - LHPF2MIX Input 1 Source */
+ { 0x0669, 0x0080 }, /* R1641 - LHPF2MIX Input 1 Volume */
+ { 0x066A, 0x0000 }, /* R1642 - LHPF2MIX Input 2 Source */
+ { 0x066B, 0x0080 }, /* R1643 - LHPF2MIX Input 2 Volume */
+ { 0x066C, 0x0000 }, /* R1644 - LHPF2MIX Input 3 Source */
+ { 0x066D, 0x0080 }, /* R1645 - LHPF2MIX Input 3 Volume */
+ { 0x066E, 0x0000 }, /* R1646 - LHPF2MIX Input 4 Source */
+ { 0x066F, 0x0080 }, /* R1647 - LHPF2MIX Input 4 Volume */
+ { 0x0670, 0x0000 }, /* R1648 - DSP1LMIX Input 1 Source */
+ { 0x0671, 0x0080 }, /* R1649 - DSP1LMIX Input 1 Volume */
+ { 0x0672, 0x0000 }, /* R1650 - DSP1LMIX Input 2 Source */
+ { 0x0673, 0x0080 }, /* R1651 - DSP1LMIX Input 2 Volume */
+ { 0x0674, 0x0000 }, /* R1652 - DSP1LMIX Input 3 Source */
+ { 0x0675, 0x0080 }, /* R1653 - DSP1LMIX Input 3 Volume */
+ { 0x0676, 0x0000 }, /* R1654 - DSP1LMIX Input 4 Source */
+ { 0x0677, 0x0080 }, /* R1655 - DSP1LMIX Input 4 Volume */
+ { 0x0678, 0x0000 }, /* R1656 - DSP1RMIX Input 1 Source */
+ { 0x0679, 0x0080 }, /* R1657 - DSP1RMIX Input 1 Volume */
+ { 0x067A, 0x0000 }, /* R1658 - DSP1RMIX Input 2 Source */
+ { 0x067B, 0x0080 }, /* R1659 - DSP1RMIX Input 2 Volume */
+ { 0x067C, 0x0000 }, /* R1660 - DSP1RMIX Input 3 Source */
+ { 0x067D, 0x0080 }, /* R1661 - DSP1RMIX Input 3 Volume */
+ { 0x067E, 0x0000 }, /* R1662 - DSP1RMIX Input 4 Source */
+ { 0x067F, 0x0080 }, /* R1663 - DSP1RMIX Input 4 Volume */
+ { 0x0680, 0x0000 }, /* R1664 - DSP1AUX1MIX Input 1 Source */
+ { 0x0681, 0x0000 }, /* R1665 - DSP1AUX2MIX Input 1 Source */
+ { 0x0682, 0x0000 }, /* R1666 - DSP1AUX3MIX Input 1 Source */
+ { 0x0683, 0x0000 }, /* R1667 - DSP1AUX4MIX Input 1 Source */
+ { 0x0684, 0x0000 }, /* R1668 - DSP1AUX5MIX Input 1 Source */
+ { 0x0685, 0x0000 }, /* R1669 - DSP1AUX6MIX Input 1 Source */
+ { 0x0686, 0x0000 }, /* R1670 - DSP2LMIX Input 1 Source */
+ { 0x0687, 0x0080 }, /* R1671 - DSP2LMIX Input 1 Volume */
+ { 0x0688, 0x0000 }, /* R1672 - DSP2LMIX Input 2 Source */
+ { 0x0689, 0x0080 }, /* R1673 - DSP2LMIX Input 2 Volume */
+ { 0x068A, 0x0000 }, /* R1674 - DSP2LMIX Input 3 Source */
+ { 0x068B, 0x0080 }, /* R1675 - DSP2LMIX Input 3 Volume */
+ { 0x068C, 0x0000 }, /* R1676 - DSP2LMIX Input 4 Source */
+ { 0x068D, 0x0080 }, /* R1677 - DSP2LMIX Input 4 Volume */
+ { 0x068E, 0x0000 }, /* R1678 - DSP2RMIX Input 1 Source */
+ { 0x068F, 0x0080 }, /* R1679 - DSP2RMIX Input 1 Volume */
+ { 0x0690, 0x0000 }, /* R1680 - DSP2RMIX Input 2 Source */
+ { 0x0691, 0x0080 }, /* R1681 - DSP2RMIX Input 2 Volume */
+ { 0x0692, 0x0000 }, /* R1682 - DSP2RMIX Input 3 Source */
+ { 0x0693, 0x0080 }, /* R1683 - DSP2RMIX Input 3 Volume */
+ { 0x0694, 0x0000 }, /* R1684 - DSP2RMIX Input 4 Source */
+ { 0x0695, 0x0080 }, /* R1685 - DSP2RMIX Input 4 Volume */
+ { 0x0696, 0x0000 }, /* R1686 - DSP2AUX1MIX Input 1 Source */
+ { 0x0697, 0x0000 }, /* R1687 - DSP2AUX2MIX Input 1 Source */
+ { 0x0698, 0x0000 }, /* R1688 - DSP2AUX3MIX Input 1 Source */
+ { 0x0699, 0x0000 }, /* R1689 - DSP2AUX4MIX Input 1 Source */
+ { 0x069A, 0x0000 }, /* R1690 - DSP2AUX5MIX Input 1 Source */
+ { 0x069B, 0x0000 }, /* R1691 - DSP2AUX6MIX Input 1 Source */
+ { 0x0700, 0xA101 }, /* R1792 - GPIO CTRL 1 */
+ { 0x0701, 0xA101 }, /* R1793 - GPIO CTRL 2 */
+ { 0x0702, 0xA101 }, /* R1794 - GPIO CTRL 3 */
+ { 0x0703, 0xA101 }, /* R1795 - GPIO CTRL 4 */
+ { 0x0709, 0x0000 }, /* R1801 - Misc Pad Ctrl 1 */
+ { 0x0801, 0x00FF }, /* R2049 - Interrupt Status 1 Mask */
+ { 0x0804, 0xFFFF }, /* R2052 - Interrupt Status 2 Mask */
+ { 0x0808, 0x0000 }, /* R2056 - Interrupt Control */
+ { 0x0900, 0x0000 }, /* R2304 - EQL_1 */
+ { 0x0901, 0x0000 }, /* R2305 - EQL_2 */
+ { 0x0902, 0x0000 }, /* R2306 - EQL_3 */
+ { 0x0903, 0x0000 }, /* R2307 - EQL_4 */
+ { 0x0904, 0x0000 }, /* R2308 - EQL_5 */
+ { 0x0905, 0x0000 }, /* R2309 - EQL_6 */
+ { 0x0906, 0x0000 }, /* R2310 - EQL_7 */
+ { 0x0907, 0x0000 }, /* R2311 - EQL_8 */
+ { 0x0908, 0x0000 }, /* R2312 - EQL_9 */
+ { 0x0909, 0x0000 }, /* R2313 - EQL_10 */
+ { 0x090A, 0x0000 }, /* R2314 - EQL_11 */
+ { 0x090B, 0x0000 }, /* R2315 - EQL_12 */
+ { 0x090C, 0x0000 }, /* R2316 - EQL_13 */
+ { 0x090D, 0x0000 }, /* R2317 - EQL_14 */
+ { 0x090E, 0x0000 }, /* R2318 - EQL_15 */
+ { 0x090F, 0x0000 }, /* R2319 - EQL_16 */
+ { 0x0910, 0x0000 }, /* R2320 - EQL_17 */
+ { 0x0911, 0x0000 }, /* R2321 - EQL_18 */
+ { 0x0912, 0x0000 }, /* R2322 - EQL_19 */
+ { 0x0913, 0x0000 }, /* R2323 - EQL_20 */
+ { 0x0916, 0x0000 }, /* R2326 - EQR_1 */
+ { 0x0917, 0x0000 }, /* R2327 - EQR_2 */
+ { 0x0918, 0x0000 }, /* R2328 - EQR_3 */
+ { 0x0919, 0x0000 }, /* R2329 - EQR_4 */
+ { 0x091A, 0x0000 }, /* R2330 - EQR_5 */
+ { 0x091B, 0x0000 }, /* R2331 - EQR_6 */
+ { 0x091C, 0x0000 }, /* R2332 - EQR_7 */
+ { 0x091D, 0x0000 }, /* R2333 - EQR_8 */
+ { 0x091E, 0x0000 }, /* R2334 - EQR_9 */
+ { 0x091F, 0x0000 }, /* R2335 - EQR_10 */
+ { 0x0920, 0x0000 }, /* R2336 - EQR_11 */
+ { 0x0921, 0x0000 }, /* R2337 - EQR_12 */
+ { 0x0922, 0x0000 }, /* R2338 - EQR_13 */
+ { 0x0923, 0x0000 }, /* R2339 - EQR_14 */
+ { 0x0924, 0x0000 }, /* R2340 - EQR_15 */
+ { 0x0925, 0x0000 }, /* R2341 - EQR_16 */
+ { 0x0926, 0x0000 }, /* R2342 - EQR_17 */
+ { 0x0927, 0x0000 }, /* R2343 - EQR_18 */
+ { 0x0928, 0x0000 }, /* R2344 - EQR_19 */
+ { 0x0929, 0x0000 }, /* R2345 - EQR_20 */
+ { 0x093E, 0x0000 }, /* R2366 - HPLPF1_1 */
+ { 0x093F, 0x0000 }, /* R2367 - HPLPF1_2 */
+ { 0x0942, 0x0000 }, /* R2370 - HPLPF2_1 */
+ { 0x0943, 0x0000 }, /* R2371 - HPLPF2_2 */
+ { 0x0A00, 0x0000 }, /* R2560 - DSP1 Control 1 */
+ { 0x0A02, 0x0000 }, /* R2562 - DSP1 Control 2 */
+ { 0x0A03, 0x0000 }, /* R2563 - DSP1 Control 3 */
+ { 0x0A04, 0x0000 }, /* R2564 - DSP1 Control 4 */
+ { 0x0A06, 0x0000 }, /* R2566 - DSP1 Control 5 */
+ { 0x0A07, 0x0000 }, /* R2567 - DSP1 Control 6 */
+ { 0x0A08, 0x0000 }, /* R2568 - DSP1 Control 7 */
+ { 0x0A09, 0x0000 }, /* R2569 - DSP1 Control 8 */
+ { 0x0A0A, 0x0000 }, /* R2570 - DSP1 Control 9 */
+ { 0x0A0B, 0x0000 }, /* R2571 - DSP1 Control 10 */
+ { 0x0A0C, 0x0000 }, /* R2572 - DSP1 Control 11 */
+ { 0x0A0D, 0x0000 }, /* R2573 - DSP1 Control 12 */
+ { 0x0A0F, 0x0000 }, /* R2575 - DSP1 Control 13 */
+ { 0x0A10, 0x0000 }, /* R2576 - DSP1 Control 14 */
+ { 0x0A11, 0x0000 }, /* R2577 - DSP1 Control 15 */
+ { 0x0A12, 0x0000 }, /* R2578 - DSP1 Control 16 */
+ { 0x0A13, 0x0000 }, /* R2579 - DSP1 Control 17 */
+ { 0x0A14, 0x0000 }, /* R2580 - DSP1 Control 18 */
+ { 0x0A16, 0x0000 }, /* R2582 - DSP1 Control 19 */
+ { 0x0A17, 0x0000 }, /* R2583 - DSP1 Control 20 */
+ { 0x0A18, 0x0000 }, /* R2584 - DSP1 Control 21 */
+ { 0x0A1A, 0x1800 }, /* R2586 - DSP1 Control 22 */
+ { 0x0A1B, 0x1000 }, /* R2587 - DSP1 Control 23 */
+ { 0x0A1C, 0x0400 }, /* R2588 - DSP1 Control 24 */
+ { 0x0A1E, 0x0000 }, /* R2590 - DSP1 Control 25 */
+ { 0x0A20, 0x0000 }, /* R2592 - DSP1 Control 26 */
+ { 0x0A21, 0x0000 }, /* R2593 - DSP1 Control 27 */
+ { 0x0A22, 0x0000 }, /* R2594 - DSP1 Control 28 */
+ { 0x0A23, 0x0000 }, /* R2595 - DSP1 Control 29 */
+ { 0x0A24, 0x0000 }, /* R2596 - DSP1 Control 30 */
+ { 0x0A26, 0x0000 }, /* R2598 - DSP1 Control 31 */
+ { 0x0B00, 0x0000 }, /* R2816 - DSP2 Control 1 */
+ { 0x0B02, 0x0000 }, /* R2818 - DSP2 Control 2 */
+ { 0x0B03, 0x0000 }, /* R2819 - DSP2 Control 3 */
+ { 0x0B04, 0x0000 }, /* R2820 - DSP2 Control 4 */
+ { 0x0B06, 0x0000 }, /* R2822 - DSP2 Control 5 */
+ { 0x0B07, 0x0000 }, /* R2823 - DSP2 Control 6 */
+ { 0x0B08, 0x0000 }, /* R2824 - DSP2 Control 7 */
+ { 0x0B09, 0x0000 }, /* R2825 - DSP2 Control 8 */
+ { 0x0B0A, 0x0000 }, /* R2826 - DSP2 Control 9 */
+ { 0x0B0B, 0x0000 }, /* R2827 - DSP2 Control 10 */
+ { 0x0B0C, 0x0000 }, /* R2828 - DSP2 Control 11 */
+ { 0x0B0D, 0x0000 }, /* R2829 - DSP2 Control 12 */
+ { 0x0B0F, 0x0000 }, /* R2831 - DSP2 Control 13 */
+ { 0x0B10, 0x0000 }, /* R2832 - DSP2 Control 14 */
+ { 0x0B11, 0x0000 }, /* R2833 - DSP2 Control 15 */
+ { 0x0B12, 0x0000 }, /* R2834 - DSP2 Control 16 */
+ { 0x0B13, 0x0000 }, /* R2835 - DSP2 Control 17 */
+ { 0x0B14, 0x0000 }, /* R2836 - DSP2 Control 18 */
+ { 0x0B16, 0x0000 }, /* R2838 - DSP2 Control 19 */
+ { 0x0B17, 0x0000 }, /* R2839 - DSP2 Control 20 */
+ { 0x0B18, 0x0000 }, /* R2840 - DSP2 Control 21 */
+ { 0x0B1A, 0x0800 }, /* R2842 - DSP2 Control 22 */
+ { 0x0B1B, 0x1000 }, /* R2843 - DSP2 Control 23 */
+ { 0x0B1C, 0x0400 }, /* R2844 - DSP2 Control 24 */
+ { 0x0B1E, 0x0000 }, /* R2846 - DSP2 Control 25 */
+ { 0x0B20, 0x0000 }, /* R2848 - DSP2 Control 26 */
+ { 0x0B21, 0x0000 }, /* R2849 - DSP2 Control 27 */
+ { 0x0B22, 0x0000 }, /* R2850 - DSP2 Control 28 */
+ { 0x0B23, 0x0000 }, /* R2851 - DSP2 Control 29 */
+ { 0x0B24, 0x0000 }, /* R2852 - DSP2 Control 30 */
+ { 0x0B26, 0x0000 }, /* R2854 - DSP2 Control 31 */
+};
+
+static bool wm2200_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm2200_ranges); i++)
+ if ((reg >= wm2200_ranges[i].window_start &&
+ reg <= wm2200_ranges[i].window_start +
+ wm2200_ranges[i].window_len) ||
+ (reg >= wm2200_ranges[i].range_min &&
+ reg <= wm2200_ranges[i].range_max))
+ return true;
+
+ switch (reg) {
+ case WM2200_SOFTWARE_RESET:
+ case WM2200_DEVICE_REVISION:
+ case WM2200_ADPS1_IRQ0:
+ case WM2200_ADPS1_IRQ1:
+ case WM2200_INTERRUPT_STATUS_1:
+ case WM2200_INTERRUPT_STATUS_2:
+ case WM2200_INTERRUPT_RAW_STATUS_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wm2200_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm2200_ranges); i++)
+ if ((reg >= wm2200_ranges[i].window_start &&
+ reg <= wm2200_ranges[i].window_start +
+ wm2200_ranges[i].window_len) ||
+ (reg >= wm2200_ranges[i].range_min &&
+ reg <= wm2200_ranges[i].range_max))
+ return true;
+
+ switch (reg) {
+ case WM2200_SOFTWARE_RESET:
+ case WM2200_DEVICE_REVISION:
+ case WM2200_TONE_GENERATOR_1:
+ case WM2200_CLOCKING_3:
+ case WM2200_CLOCKING_4:
+ case WM2200_FLL_CONTROL_1:
+ case WM2200_FLL_CONTROL_2:
+ case WM2200_FLL_CONTROL_3:
+ case WM2200_FLL_CONTROL_4:
+ case WM2200_FLL_CONTROL_6:
+ case WM2200_FLL_CONTROL_7:
+ case WM2200_FLL_EFS_1:
+ case WM2200_FLL_EFS_2:
+ case WM2200_MIC_CHARGE_PUMP_1:
+ case WM2200_MIC_CHARGE_PUMP_2:
+ case WM2200_DM_CHARGE_PUMP_1:
+ case WM2200_MIC_BIAS_CTRL_1:
+ case WM2200_MIC_BIAS_CTRL_2:
+ case WM2200_EAR_PIECE_CTRL_1:
+ case WM2200_EAR_PIECE_CTRL_2:
+ case WM2200_INPUT_ENABLES:
+ case WM2200_IN1L_CONTROL:
+ case WM2200_IN1R_CONTROL:
+ case WM2200_IN2L_CONTROL:
+ case WM2200_IN2R_CONTROL:
+ case WM2200_IN3L_CONTROL:
+ case WM2200_IN3R_CONTROL:
+ case WM2200_RXANC_SRC:
+ case WM2200_INPUT_VOLUME_RAMP:
+ case WM2200_ADC_DIGITAL_VOLUME_1L:
+ case WM2200_ADC_DIGITAL_VOLUME_1R:
+ case WM2200_ADC_DIGITAL_VOLUME_2L:
+ case WM2200_ADC_DIGITAL_VOLUME_2R:
+ case WM2200_ADC_DIGITAL_VOLUME_3L:
+ case WM2200_ADC_DIGITAL_VOLUME_3R:
+ case WM2200_OUTPUT_ENABLES:
+ case WM2200_DAC_VOLUME_LIMIT_1L:
+ case WM2200_DAC_VOLUME_LIMIT_1R:
+ case WM2200_DAC_VOLUME_LIMIT_2L:
+ case WM2200_DAC_VOLUME_LIMIT_2R:
+ case WM2200_DAC_AEC_CONTROL_1:
+ case WM2200_OUTPUT_VOLUME_RAMP:
+ case WM2200_DAC_DIGITAL_VOLUME_1L:
+ case WM2200_DAC_DIGITAL_VOLUME_1R:
+ case WM2200_DAC_DIGITAL_VOLUME_2L:
+ case WM2200_DAC_DIGITAL_VOLUME_2R:
+ case WM2200_PDM_1:
+ case WM2200_PDM_2:
+ case WM2200_AUDIO_IF_1_1:
+ case WM2200_AUDIO_IF_1_2:
+ case WM2200_AUDIO_IF_1_3:
+ case WM2200_AUDIO_IF_1_4:
+ case WM2200_AUDIO_IF_1_5:
+ case WM2200_AUDIO_IF_1_6:
+ case WM2200_AUDIO_IF_1_7:
+ case WM2200_AUDIO_IF_1_8:
+ case WM2200_AUDIO_IF_1_9:
+ case WM2200_AUDIO_IF_1_10:
+ case WM2200_AUDIO_IF_1_11:
+ case WM2200_AUDIO_IF_1_12:
+ case WM2200_AUDIO_IF_1_13:
+ case WM2200_AUDIO_IF_1_14:
+ case WM2200_AUDIO_IF_1_15:
+ case WM2200_AUDIO_IF_1_16:
+ case WM2200_AUDIO_IF_1_17:
+ case WM2200_AUDIO_IF_1_18:
+ case WM2200_AUDIO_IF_1_19:
+ case WM2200_AUDIO_IF_1_20:
+ case WM2200_AUDIO_IF_1_21:
+ case WM2200_AUDIO_IF_1_22:
+ case WM2200_OUT1LMIX_INPUT_1_SOURCE:
+ case WM2200_OUT1LMIX_INPUT_1_VOLUME:
+ case WM2200_OUT1LMIX_INPUT_2_SOURCE:
+ case WM2200_OUT1LMIX_INPUT_2_VOLUME:
+ case WM2200_OUT1LMIX_INPUT_3_SOURCE:
+ case WM2200_OUT1LMIX_INPUT_3_VOLUME:
+ case WM2200_OUT1LMIX_INPUT_4_SOURCE:
+ case WM2200_OUT1LMIX_INPUT_4_VOLUME:
+ case WM2200_OUT1RMIX_INPUT_1_SOURCE:
+ case WM2200_OUT1RMIX_INPUT_1_VOLUME:
+ case WM2200_OUT1RMIX_INPUT_2_SOURCE:
+ case WM2200_OUT1RMIX_INPUT_2_VOLUME:
+ case WM2200_OUT1RMIX_INPUT_3_SOURCE:
+ case WM2200_OUT1RMIX_INPUT_3_VOLUME:
+ case WM2200_OUT1RMIX_INPUT_4_SOURCE:
+ case WM2200_OUT1RMIX_INPUT_4_VOLUME:
+ case WM2200_OUT2LMIX_INPUT_1_SOURCE:
+ case WM2200_OUT2LMIX_INPUT_1_VOLUME:
+ case WM2200_OUT2LMIX_INPUT_2_SOURCE:
+ case WM2200_OUT2LMIX_INPUT_2_VOLUME:
+ case WM2200_OUT2LMIX_INPUT_3_SOURCE:
+ case WM2200_OUT2LMIX_INPUT_3_VOLUME:
+ case WM2200_OUT2LMIX_INPUT_4_SOURCE:
+ case WM2200_OUT2LMIX_INPUT_4_VOLUME:
+ case WM2200_OUT2RMIX_INPUT_1_SOURCE:
+ case WM2200_OUT2RMIX_INPUT_1_VOLUME:
+ case WM2200_OUT2RMIX_INPUT_2_SOURCE:
+ case WM2200_OUT2RMIX_INPUT_2_VOLUME:
+ case WM2200_OUT2RMIX_INPUT_3_SOURCE:
+ case WM2200_OUT2RMIX_INPUT_3_VOLUME:
+ case WM2200_OUT2RMIX_INPUT_4_SOURCE:
+ case WM2200_OUT2RMIX_INPUT_4_VOLUME:
+ case WM2200_AIF1TX1MIX_INPUT_1_SOURCE:
+ case WM2200_AIF1TX1MIX_INPUT_1_VOLUME:
+ case WM2200_AIF1TX1MIX_INPUT_2_SOURCE:
+ case WM2200_AIF1TX1MIX_INPUT_2_VOLUME:
+ case WM2200_AIF1TX1MIX_INPUT_3_SOURCE:
+ case WM2200_AIF1TX1MIX_INPUT_3_VOLUME:
+ case WM2200_AIF1TX1MIX_INPUT_4_SOURCE:
+ case WM2200_AIF1TX1MIX_INPUT_4_VOLUME:
+ case WM2200_AIF1TX2MIX_INPUT_1_SOURCE:
+ case WM2200_AIF1TX2MIX_INPUT_1_VOLUME:
+ case WM2200_AIF1TX2MIX_INPUT_2_SOURCE:
+ case WM2200_AIF1TX2MIX_INPUT_2_VOLUME:
+ case WM2200_AIF1TX2MIX_INPUT_3_SOURCE:
+ case WM2200_AIF1TX2MIX_INPUT_3_VOLUME:
+ case WM2200_AIF1TX2MIX_INPUT_4_SOURCE:
+ case WM2200_AIF1TX2MIX_INPUT_4_VOLUME:
+ case WM2200_AIF1TX3MIX_INPUT_1_SOURCE:
+ case WM2200_AIF1TX3MIX_INPUT_1_VOLUME:
+ case WM2200_AIF1TX3MIX_INPUT_2_SOURCE:
+ case WM2200_AIF1TX3MIX_INPUT_2_VOLUME:
+ case WM2200_AIF1TX3MIX_INPUT_3_SOURCE:
+ case WM2200_AIF1TX3MIX_INPUT_3_VOLUME:
+ case WM2200_AIF1TX3MIX_INPUT_4_SOURCE:
+ case WM2200_AIF1TX3MIX_INPUT_4_VOLUME:
+ case WM2200_AIF1TX4MIX_INPUT_1_SOURCE:
+ case WM2200_AIF1TX4MIX_INPUT_1_VOLUME:
+ case WM2200_AIF1TX4MIX_INPUT_2_SOURCE:
+ case WM2200_AIF1TX4MIX_INPUT_2_VOLUME:
+ case WM2200_AIF1TX4MIX_INPUT_3_SOURCE:
+ case WM2200_AIF1TX4MIX_INPUT_3_VOLUME:
+ case WM2200_AIF1TX4MIX_INPUT_4_SOURCE:
+ case WM2200_AIF1TX4MIX_INPUT_4_VOLUME:
+ case WM2200_AIF1TX5MIX_INPUT_1_SOURCE:
+ case WM2200_AIF1TX5MIX_INPUT_1_VOLUME:
+ case WM2200_AIF1TX5MIX_INPUT_2_SOURCE:
+ case WM2200_AIF1TX5MIX_INPUT_2_VOLUME:
+ case WM2200_AIF1TX5MIX_INPUT_3_SOURCE:
+ case WM2200_AIF1TX5MIX_INPUT_3_VOLUME:
+ case WM2200_AIF1TX5MIX_INPUT_4_SOURCE:
+ case WM2200_AIF1TX5MIX_INPUT_4_VOLUME:
+ case WM2200_AIF1TX6MIX_INPUT_1_SOURCE:
+ case WM2200_AIF1TX6MIX_INPUT_1_VOLUME:
+ case WM2200_AIF1TX6MIX_INPUT_2_SOURCE:
+ case WM2200_AIF1TX6MIX_INPUT_2_VOLUME:
+ case WM2200_AIF1TX6MIX_INPUT_3_SOURCE:
+ case WM2200_AIF1TX6MIX_INPUT_3_VOLUME:
+ case WM2200_AIF1TX6MIX_INPUT_4_SOURCE:
+ case WM2200_AIF1TX6MIX_INPUT_4_VOLUME:
+ case WM2200_EQLMIX_INPUT_1_SOURCE:
+ case WM2200_EQLMIX_INPUT_1_VOLUME:
+ case WM2200_EQLMIX_INPUT_2_SOURCE:
+ case WM2200_EQLMIX_INPUT_2_VOLUME:
+ case WM2200_EQLMIX_INPUT_3_SOURCE:
+ case WM2200_EQLMIX_INPUT_3_VOLUME:
+ case WM2200_EQLMIX_INPUT_4_SOURCE:
+ case WM2200_EQLMIX_INPUT_4_VOLUME:
+ case WM2200_EQRMIX_INPUT_1_SOURCE:
+ case WM2200_EQRMIX_INPUT_1_VOLUME:
+ case WM2200_EQRMIX_INPUT_2_SOURCE:
+ case WM2200_EQRMIX_INPUT_2_VOLUME:
+ case WM2200_EQRMIX_INPUT_3_SOURCE:
+ case WM2200_EQRMIX_INPUT_3_VOLUME:
+ case WM2200_EQRMIX_INPUT_4_SOURCE:
+ case WM2200_EQRMIX_INPUT_4_VOLUME:
+ case WM2200_LHPF1MIX_INPUT_1_SOURCE:
+ case WM2200_LHPF1MIX_INPUT_1_VOLUME:
+ case WM2200_LHPF1MIX_INPUT_2_SOURCE:
+ case WM2200_LHPF1MIX_INPUT_2_VOLUME:
+ case WM2200_LHPF1MIX_INPUT_3_SOURCE:
+ case WM2200_LHPF1MIX_INPUT_3_VOLUME:
+ case WM2200_LHPF1MIX_INPUT_4_SOURCE:
+ case WM2200_LHPF1MIX_INPUT_4_VOLUME:
+ case WM2200_LHPF2MIX_INPUT_1_SOURCE:
+ case WM2200_LHPF2MIX_INPUT_1_VOLUME:
+ case WM2200_LHPF2MIX_INPUT_2_SOURCE:
+ case WM2200_LHPF2MIX_INPUT_2_VOLUME:
+ case WM2200_LHPF2MIX_INPUT_3_SOURCE:
+ case WM2200_LHPF2MIX_INPUT_3_VOLUME:
+ case WM2200_LHPF2MIX_INPUT_4_SOURCE:
+ case WM2200_LHPF2MIX_INPUT_4_VOLUME:
+ case WM2200_DSP1LMIX_INPUT_1_SOURCE:
+ case WM2200_DSP1LMIX_INPUT_1_VOLUME:
+ case WM2200_DSP1LMIX_INPUT_2_SOURCE:
+ case WM2200_DSP1LMIX_INPUT_2_VOLUME:
+ case WM2200_DSP1LMIX_INPUT_3_SOURCE:
+ case WM2200_DSP1LMIX_INPUT_3_VOLUME:
+ case WM2200_DSP1LMIX_INPUT_4_SOURCE:
+ case WM2200_DSP1LMIX_INPUT_4_VOLUME:
+ case WM2200_DSP1RMIX_INPUT_1_SOURCE:
+ case WM2200_DSP1RMIX_INPUT_1_VOLUME:
+ case WM2200_DSP1RMIX_INPUT_2_SOURCE:
+ case WM2200_DSP1RMIX_INPUT_2_VOLUME:
+ case WM2200_DSP1RMIX_INPUT_3_SOURCE:
+ case WM2200_DSP1RMIX_INPUT_3_VOLUME:
+ case WM2200_DSP1RMIX_INPUT_4_SOURCE:
+ case WM2200_DSP1RMIX_INPUT_4_VOLUME:
+ case WM2200_DSP1AUX1MIX_INPUT_1_SOURCE:
+ case WM2200_DSP1AUX2MIX_INPUT_1_SOURCE:
+ case WM2200_DSP1AUX3MIX_INPUT_1_SOURCE:
+ case WM2200_DSP1AUX4MIX_INPUT_1_SOURCE:
+ case WM2200_DSP1AUX5MIX_INPUT_1_SOURCE:
+ case WM2200_DSP1AUX6MIX_INPUT_1_SOURCE:
+ case WM2200_DSP2LMIX_INPUT_1_SOURCE:
+ case WM2200_DSP2LMIX_INPUT_1_VOLUME:
+ case WM2200_DSP2LMIX_INPUT_2_SOURCE:
+ case WM2200_DSP2LMIX_INPUT_2_VOLUME:
+ case WM2200_DSP2LMIX_INPUT_3_SOURCE:
+ case WM2200_DSP2LMIX_INPUT_3_VOLUME:
+ case WM2200_DSP2LMIX_INPUT_4_SOURCE:
+ case WM2200_DSP2LMIX_INPUT_4_VOLUME:
+ case WM2200_DSP2RMIX_INPUT_1_SOURCE:
+ case WM2200_DSP2RMIX_INPUT_1_VOLUME:
+ case WM2200_DSP2RMIX_INPUT_2_SOURCE:
+ case WM2200_DSP2RMIX_INPUT_2_VOLUME:
+ case WM2200_DSP2RMIX_INPUT_3_SOURCE:
+ case WM2200_DSP2RMIX_INPUT_3_VOLUME:
+ case WM2200_DSP2RMIX_INPUT_4_SOURCE:
+ case WM2200_DSP2RMIX_INPUT_4_VOLUME:
+ case WM2200_DSP2AUX1MIX_INPUT_1_SOURCE:
+ case WM2200_DSP2AUX2MIX_INPUT_1_SOURCE:
+ case WM2200_DSP2AUX3MIX_INPUT_1_SOURCE:
+ case WM2200_DSP2AUX4MIX_INPUT_1_SOURCE:
+ case WM2200_DSP2AUX5MIX_INPUT_1_SOURCE:
+ case WM2200_DSP2AUX6MIX_INPUT_1_SOURCE:
+ case WM2200_GPIO_CTRL_1:
+ case WM2200_GPIO_CTRL_2:
+ case WM2200_GPIO_CTRL_3:
+ case WM2200_GPIO_CTRL_4:
+ case WM2200_ADPS1_IRQ0:
+ case WM2200_ADPS1_IRQ1:
+ case WM2200_MISC_PAD_CTRL_1:
+ case WM2200_INTERRUPT_STATUS_1:
+ case WM2200_INTERRUPT_STATUS_1_MASK:
+ case WM2200_INTERRUPT_STATUS_2:
+ case WM2200_INTERRUPT_RAW_STATUS_2:
+ case WM2200_INTERRUPT_STATUS_2_MASK:
+ case WM2200_INTERRUPT_CONTROL:
+ case WM2200_EQL_1:
+ case WM2200_EQL_2:
+ case WM2200_EQL_3:
+ case WM2200_EQL_4:
+ case WM2200_EQL_5:
+ case WM2200_EQL_6:
+ case WM2200_EQL_7:
+ case WM2200_EQL_8:
+ case WM2200_EQL_9:
+ case WM2200_EQL_10:
+ case WM2200_EQL_11:
+ case WM2200_EQL_12:
+ case WM2200_EQL_13:
+ case WM2200_EQL_14:
+ case WM2200_EQL_15:
+ case WM2200_EQL_16:
+ case WM2200_EQL_17:
+ case WM2200_EQL_18:
+ case WM2200_EQL_19:
+ case WM2200_EQL_20:
+ case WM2200_EQR_1:
+ case WM2200_EQR_2:
+ case WM2200_EQR_3:
+ case WM2200_EQR_4:
+ case WM2200_EQR_5:
+ case WM2200_EQR_6:
+ case WM2200_EQR_7:
+ case WM2200_EQR_8:
+ case WM2200_EQR_9:
+ case WM2200_EQR_10:
+ case WM2200_EQR_11:
+ case WM2200_EQR_12:
+ case WM2200_EQR_13:
+ case WM2200_EQR_14:
+ case WM2200_EQR_15:
+ case WM2200_EQR_16:
+ case WM2200_EQR_17:
+ case WM2200_EQR_18:
+ case WM2200_EQR_19:
+ case WM2200_EQR_20:
+ case WM2200_HPLPF1_1:
+ case WM2200_HPLPF1_2:
+ case WM2200_HPLPF2_1:
+ case WM2200_HPLPF2_2:
+ case WM2200_DSP1_CONTROL_1:
+ case WM2200_DSP1_CONTROL_2:
+ case WM2200_DSP1_CONTROL_3:
+ case WM2200_DSP1_CONTROL_4:
+ case WM2200_DSP1_CONTROL_5:
+ case WM2200_DSP1_CONTROL_6:
+ case WM2200_DSP1_CONTROL_7:
+ case WM2200_DSP1_CONTROL_8:
+ case WM2200_DSP1_CONTROL_9:
+ case WM2200_DSP1_CONTROL_10:
+ case WM2200_DSP1_CONTROL_11:
+ case WM2200_DSP1_CONTROL_12:
+ case WM2200_DSP1_CONTROL_13:
+ case WM2200_DSP1_CONTROL_14:
+ case WM2200_DSP1_CONTROL_15:
+ case WM2200_DSP1_CONTROL_16:
+ case WM2200_DSP1_CONTROL_17:
+ case WM2200_DSP1_CONTROL_18:
+ case WM2200_DSP1_CONTROL_19:
+ case WM2200_DSP1_CONTROL_20:
+ case WM2200_DSP1_CONTROL_21:
+ case WM2200_DSP1_CONTROL_22:
+ case WM2200_DSP1_CONTROL_23:
+ case WM2200_DSP1_CONTROL_24:
+ case WM2200_DSP1_CONTROL_25:
+ case WM2200_DSP1_CONTROL_26:
+ case WM2200_DSP1_CONTROL_27:
+ case WM2200_DSP1_CONTROL_28:
+ case WM2200_DSP1_CONTROL_29:
+ case WM2200_DSP1_CONTROL_30:
+ case WM2200_DSP1_CONTROL_31:
+ case WM2200_DSP2_CONTROL_1:
+ case WM2200_DSP2_CONTROL_2:
+ case WM2200_DSP2_CONTROL_3:
+ case WM2200_DSP2_CONTROL_4:
+ case WM2200_DSP2_CONTROL_5:
+ case WM2200_DSP2_CONTROL_6:
+ case WM2200_DSP2_CONTROL_7:
+ case WM2200_DSP2_CONTROL_8:
+ case WM2200_DSP2_CONTROL_9:
+ case WM2200_DSP2_CONTROL_10:
+ case WM2200_DSP2_CONTROL_11:
+ case WM2200_DSP2_CONTROL_12:
+ case WM2200_DSP2_CONTROL_13:
+ case WM2200_DSP2_CONTROL_14:
+ case WM2200_DSP2_CONTROL_15:
+ case WM2200_DSP2_CONTROL_16:
+ case WM2200_DSP2_CONTROL_17:
+ case WM2200_DSP2_CONTROL_18:
+ case WM2200_DSP2_CONTROL_19:
+ case WM2200_DSP2_CONTROL_20:
+ case WM2200_DSP2_CONTROL_21:
+ case WM2200_DSP2_CONTROL_22:
+ case WM2200_DSP2_CONTROL_23:
+ case WM2200_DSP2_CONTROL_24:
+ case WM2200_DSP2_CONTROL_25:
+ case WM2200_DSP2_CONTROL_26:
+ case WM2200_DSP2_CONTROL_27:
+ case WM2200_DSP2_CONTROL_28:
+ case WM2200_DSP2_CONTROL_29:
+ case WM2200_DSP2_CONTROL_30:
+ case WM2200_DSP2_CONTROL_31:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default wm2200_reva_patch[] = {
+ { 0x07, 0x0003 },
+ { 0x102, 0x0200 },
+ { 0x203, 0x0084 },
+ { 0x201, 0x83FF },
+ { 0x20C, 0x0062 },
+ { 0x20D, 0x0062 },
+ { 0x207, 0x2002 },
+ { 0x208, 0x20C0 },
+ { 0x21D, 0x01C0 },
+ { 0x50A, 0x0001 },
+ { 0x50B, 0x0002 },
+ { 0x50C, 0x0003 },
+ { 0x50D, 0x0004 },
+ { 0x50E, 0x0005 },
+ { 0x510, 0x0001 },
+ { 0x511, 0x0002 },
+ { 0x512, 0x0003 },
+ { 0x513, 0x0004 },
+ { 0x514, 0x0005 },
+ { 0x515, 0x0000 },
+ { 0x201, 0x8084 },
+ { 0x202, 0xBBDE },
+ { 0x203, 0x00EC },
+ { 0x500, 0x8000 },
+ { 0x507, 0x1820 },
+ { 0x508, 0x1820 },
+ { 0x505, 0x0300 },
+ { 0x506, 0x0300 },
+ { 0x302, 0x2280 },
+ { 0x303, 0x0080 },
+ { 0x304, 0x2280 },
+ { 0x305, 0x0080 },
+ { 0x306, 0x2280 },
+ { 0x307, 0x0080 },
+ { 0x401, 0x0080 },
+ { 0x402, 0x0080 },
+ { 0x417, 0x3069 },
+ { 0x900, 0x6318 },
+ { 0x901, 0x6300 },
+ { 0x902, 0x0FC8 },
+ { 0x903, 0x03FE },
+ { 0x904, 0x00E0 },
+ { 0x905, 0x1EC4 },
+ { 0x906, 0xF136 },
+ { 0x907, 0x0409 },
+ { 0x908, 0x04CC },
+ { 0x909, 0x1C9B },
+ { 0x90A, 0xF337 },
+ { 0x90B, 0x040B },
+ { 0x90C, 0x0CBB },
+ { 0x90D, 0x16F8 },
+ { 0x90E, 0xF7D9 },
+ { 0x90F, 0x040A },
+ { 0x910, 0x1F14 },
+ { 0x911, 0x058C },
+ { 0x912, 0x0563 },
+ { 0x913, 0x4000 },
+ { 0x916, 0x6318 },
+ { 0x917, 0x6300 },
+ { 0x918, 0x0FC8 },
+ { 0x919, 0x03FE },
+ { 0x91A, 0x00E0 },
+ { 0x91B, 0x1EC4 },
+ { 0x91C, 0xF136 },
+ { 0x91D, 0x0409 },
+ { 0x91E, 0x04CC },
+ { 0x91F, 0x1C9B },
+ { 0x920, 0xF337 },
+ { 0x921, 0x040B },
+ { 0x922, 0x0CBB },
+ { 0x923, 0x16F8 },
+ { 0x924, 0xF7D9 },
+ { 0x925, 0x040A },
+ { 0x926, 0x1F14 },
+ { 0x927, 0x058C },
+ { 0x928, 0x0563 },
+ { 0x929, 0x4000 },
+ { 0x709, 0x2000 },
+ { 0x207, 0x200E },
+ { 0x208, 0x20D4 },
+ { 0x20A, 0x0080 },
+ { 0x07, 0x0000 },
+};
+
+static int wm2200_reset(struct wm2200_priv *wm2200)
+{
+ if (wm2200->pdata.reset) {
+ gpio_set_value_cansleep(wm2200->pdata.reset, 0);
+ gpio_set_value_cansleep(wm2200->pdata.reset, 1);
+
+ return 0;
+ } else {
+ return regmap_write(wm2200->regmap, WM2200_SOFTWARE_RESET,
+ 0x2200);
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(in_tlv, -6300, 100, 0);
+static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
+static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0);
+
+static const char *wm2200_mixer_texts[] = {
+ "None",
+ "Tone Generator",
+ "AEC Loopback",
+ "IN1L",
+ "IN1R",
+ "IN2L",
+ "IN2R",
+ "IN3L",
+ "IN3R",
+ "AIF1RX1",
+ "AIF1RX2",
+ "AIF1RX3",
+ "AIF1RX4",
+ "AIF1RX5",
+ "AIF1RX6",
+ "EQL",
+ "EQR",
+ "LHPF1",
+ "LHPF2",
+ "DSP1.1",
+ "DSP1.2",
+ "DSP1.3",
+ "DSP1.4",
+ "DSP1.5",
+ "DSP1.6",
+ "DSP2.1",
+ "DSP2.2",
+ "DSP2.3",
+ "DSP2.4",
+ "DSP2.5",
+ "DSP2.6",
+};
+
+static int wm2200_mixer_values[] = {
+ 0x00,
+ 0x04, /* Tone */
+ 0x08, /* AEC */
+ 0x10, /* Input */
+ 0x11,
+ 0x12,
+ 0x13,
+ 0x14,
+ 0x15,
+ 0x20, /* AIF */
+ 0x21,
+ 0x22,
+ 0x23,
+ 0x24,
+ 0x25,
+ 0x50, /* EQ */
+ 0x51,
+ 0x60, /* LHPF1 */
+ 0x61, /* LHPF2 */
+ 0x68, /* DSP1 */
+ 0x69,
+ 0x6a,
+ 0x6b,
+ 0x6c,
+ 0x6d,
+ 0x70, /* DSP2 */
+ 0x71,
+ 0x72,
+ 0x73,
+ 0x74,
+ 0x75,
+};
+
+#define WM2200_MIXER_CONTROLS(name, base) \
+ SOC_SINGLE_TLV(name " Input 1 Volume", base + 1 , \
+ WM2200_MIXER_VOL_SHIFT, 80, 0, mixer_tlv), \
+ SOC_SINGLE_TLV(name " Input 2 Volume", base + 3 , \
+ WM2200_MIXER_VOL_SHIFT, 80, 0, mixer_tlv), \
+ SOC_SINGLE_TLV(name " Input 3 Volume", base + 5 , \
+ WM2200_MIXER_VOL_SHIFT, 80, 0, mixer_tlv), \
+ SOC_SINGLE_TLV(name " Input 4 Volume", base + 7 , \
+ WM2200_MIXER_VOL_SHIFT, 80, 0, mixer_tlv)
+
+#define WM2200_MUX_ENUM_DECL(name, reg) \
+ SOC_VALUE_ENUM_SINGLE_DECL(name, reg, 0, 0xff, \
+ wm2200_mixer_texts, wm2200_mixer_values)
+
+#define WM2200_MUX_CTL_DECL(name) \
+ const struct snd_kcontrol_new name##_mux = \
+ SOC_DAPM_ENUM("Route", name##_enum)
+
+#define WM2200_MIXER_ENUMS(name, base_reg) \
+ static WM2200_MUX_ENUM_DECL(name##_in1_enum, base_reg); \
+ static WM2200_MUX_ENUM_DECL(name##_in2_enum, base_reg + 2); \
+ static WM2200_MUX_ENUM_DECL(name##_in3_enum, base_reg + 4); \
+ static WM2200_MUX_ENUM_DECL(name##_in4_enum, base_reg + 6); \
+ static WM2200_MUX_CTL_DECL(name##_in1); \
+ static WM2200_MUX_CTL_DECL(name##_in2); \
+ static WM2200_MUX_CTL_DECL(name##_in3); \
+ static WM2200_MUX_CTL_DECL(name##_in4)
+
+#define WM2200_DSP_ENUMS(name, base_reg) \
+ static WM2200_MUX_ENUM_DECL(name##_aux1_enum, base_reg); \
+ static WM2200_MUX_ENUM_DECL(name##_aux2_enum, base_reg + 1); \
+ static WM2200_MUX_ENUM_DECL(name##_aux3_enum, base_reg + 2); \
+ static WM2200_MUX_ENUM_DECL(name##_aux4_enum, base_reg + 3); \
+ static WM2200_MUX_ENUM_DECL(name##_aux5_enum, base_reg + 4); \
+ static WM2200_MUX_ENUM_DECL(name##_aux6_enum, base_reg + 5); \
+ static WM2200_MUX_CTL_DECL(name##_aux1); \
+ static WM2200_MUX_CTL_DECL(name##_aux2); \
+ static WM2200_MUX_CTL_DECL(name##_aux3); \
+ static WM2200_MUX_CTL_DECL(name##_aux4); \
+ static WM2200_MUX_CTL_DECL(name##_aux5); \
+ static WM2200_MUX_CTL_DECL(name##_aux6);
+
+static const char *wm2200_rxanc_input_sel_texts[] = {
+ "None", "IN1", "IN2", "IN3",
+};
+
+static SOC_ENUM_SINGLE_DECL(wm2200_rxanc_input_sel,
+ WM2200_RXANC_SRC,
+ WM2200_IN_RXANC_SEL_SHIFT,
+ wm2200_rxanc_input_sel_texts);
+
+static const struct snd_kcontrol_new wm2200_snd_controls[] = {
+SOC_SINGLE("IN1 High Performance Switch", WM2200_IN1L_CONTROL,
+ WM2200_IN1_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN2 High Performance Switch", WM2200_IN2L_CONTROL,
+ WM2200_IN2_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN3 High Performance Switch", WM2200_IN3L_CONTROL,
+ WM2200_IN3_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R_TLV("IN1 Volume", WM2200_IN1L_CONTROL, WM2200_IN1R_CONTROL,
+ WM2200_IN1L_PGA_VOL_SHIFT, 0x5f, 0, in_tlv),
+SOC_DOUBLE_R_TLV("IN2 Volume", WM2200_IN2L_CONTROL, WM2200_IN2R_CONTROL,
+ WM2200_IN2L_PGA_VOL_SHIFT, 0x5f, 0, in_tlv),
+SOC_DOUBLE_R_TLV("IN3 Volume", WM2200_IN3L_CONTROL, WM2200_IN3R_CONTROL,
+ WM2200_IN3L_PGA_VOL_SHIFT, 0x5f, 0, in_tlv),
+
+SOC_DOUBLE_R("IN1 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L,
+ WM2200_ADC_DIGITAL_VOLUME_1R, WM2200_IN1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_2L,
+ WM2200_ADC_DIGITAL_VOLUME_2R, WM2200_IN2L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_3L,
+ WM2200_ADC_DIGITAL_VOLUME_3R, WM2200_IN3L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("IN1 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_1L,
+ WM2200_ADC_DIGITAL_VOLUME_1R, WM2200_IN1L_DIG_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("IN2 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_2L,
+ WM2200_ADC_DIGITAL_VOLUME_2R, WM2200_IN2L_DIG_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("IN3 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_3L,
+ WM2200_ADC_DIGITAL_VOLUME_3R, WM2200_IN3L_DIG_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+
+SND_SOC_BYTES_MASK("EQL Coefficients", WM2200_EQL_1, 20, WM2200_EQL_ENA),
+SND_SOC_BYTES_MASK("EQR Coefficients", WM2200_EQR_1, 20, WM2200_EQR_ENA),
+
+SND_SOC_BYTES("LHPF1 Coefficeints", WM2200_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficeints", WM2200_HPLPF2_2, 1),
+
+SOC_SINGLE("OUT1 High Performance Switch", WM2200_DAC_DIGITAL_VOLUME_1L,
+ WM2200_OUT1_OSR_SHIFT, 1, 0),
+SOC_SINGLE("OUT2 High Performance Switch", WM2200_DAC_DIGITAL_VOLUME_2L,
+ WM2200_OUT2_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("OUT1 Digital Switch", WM2200_DAC_DIGITAL_VOLUME_1L,
+ WM2200_DAC_DIGITAL_VOLUME_1R, WM2200_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R_TLV("OUT1 Digital Volume", WM2200_DAC_DIGITAL_VOLUME_1L,
+ WM2200_DAC_DIGITAL_VOLUME_1R, WM2200_OUT1L_VOL_SHIFT, 0x9f, 0,
+ digital_tlv),
+SOC_DOUBLE_R_TLV("OUT1 Volume", WM2200_DAC_VOLUME_LIMIT_1L,
+ WM2200_DAC_VOLUME_LIMIT_1R, WM2200_OUT1L_PGA_VOL_SHIFT,
+ 0x46, 0, out_tlv),
+
+SOC_DOUBLE_R("OUT2 Digital Switch", WM2200_DAC_DIGITAL_VOLUME_2L,
+ WM2200_DAC_DIGITAL_VOLUME_2R, WM2200_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R_TLV("OUT2 Digital Volume", WM2200_DAC_DIGITAL_VOLUME_2L,
+ WM2200_DAC_DIGITAL_VOLUME_2R, WM2200_OUT2L_VOL_SHIFT, 0x9f, 0,
+ digital_tlv),
+SOC_DOUBLE("OUT2 Switch", WM2200_PDM_1, WM2200_SPK1L_MUTE_SHIFT,
+ WM2200_SPK1R_MUTE_SHIFT, 1, 1),
+SOC_ENUM("RxANC Src", wm2200_rxanc_input_sel),
+};
+
+WM2200_MIXER_ENUMS(OUT1L, WM2200_OUT1LMIX_INPUT_1_SOURCE);
+WM2200_MIXER_ENUMS(OUT1R, WM2200_OUT1RMIX_INPUT_1_SOURCE);
+WM2200_MIXER_ENUMS(OUT2L, WM2200_OUT2LMIX_INPUT_1_SOURCE);
+WM2200_MIXER_ENUMS(OUT2R, WM2200_OUT2RMIX_INPUT_1_SOURCE);
+
+WM2200_MIXER_ENUMS(AIF1TX1, WM2200_AIF1TX1MIX_INPUT_1_SOURCE);
+WM2200_MIXER_ENUMS(AIF1TX2, WM2200_AIF1TX2MIX_INPUT_1_SOURCE);
+WM2200_MIXER_ENUMS(AIF1TX3, WM2200_AIF1TX3MIX_INPUT_1_SOURCE);
+WM2200_MIXER_ENUMS(AIF1TX4, WM2200_AIF1TX4MIX_INPUT_1_SOURCE);
+WM2200_MIXER_ENUMS(AIF1TX5, WM2200_AIF1TX5MIX_INPUT_1_SOURCE);
+WM2200_MIXER_ENUMS(AIF1TX6, WM2200_AIF1TX6MIX_INPUT_1_SOURCE);
+
+WM2200_MIXER_ENUMS(EQL, WM2200_EQLMIX_INPUT_1_SOURCE);
+WM2200_MIXER_ENUMS(EQR, WM2200_EQRMIX_INPUT_1_SOURCE);
+
+WM2200_MIXER_ENUMS(DSP1L, WM2200_DSP1LMIX_INPUT_1_SOURCE);
+WM2200_MIXER_ENUMS(DSP1R, WM2200_DSP1RMIX_INPUT_1_SOURCE);
+WM2200_MIXER_ENUMS(DSP2L, WM2200_DSP2LMIX_INPUT_1_SOURCE);
+WM2200_MIXER_ENUMS(DSP2R, WM2200_DSP2RMIX_INPUT_1_SOURCE);
+
+WM2200_DSP_ENUMS(DSP1, WM2200_DSP1AUX1MIX_INPUT_1_SOURCE);
+WM2200_DSP_ENUMS(DSP2, WM2200_DSP2AUX1MIX_INPUT_1_SOURCE);
+
+WM2200_MIXER_ENUMS(LHPF1, WM2200_LHPF1MIX_INPUT_1_SOURCE);
+WM2200_MIXER_ENUMS(LHPF2, WM2200_LHPF2MIX_INPUT_1_SOURCE);
+
+#define WM2200_MUX(name, ctrl) \
+ SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+#define WM2200_MIXER_WIDGETS(name, name_str) \
+ WM2200_MUX(name_str " Input 1", &name##_in1_mux), \
+ WM2200_MUX(name_str " Input 2", &name##_in2_mux), \
+ WM2200_MUX(name_str " Input 3", &name##_in3_mux), \
+ WM2200_MUX(name_str " Input 4", &name##_in4_mux), \
+ SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define WM2200_DSP_WIDGETS(name, name_str) \
+ WM2200_MIXER_WIDGETS(name##L, name_str "L"), \
+ WM2200_MIXER_WIDGETS(name##R, name_str "R"), \
+ WM2200_MUX(name_str " Aux 1", &name##_aux1_mux), \
+ WM2200_MUX(name_str " Aux 2", &name##_aux2_mux), \
+ WM2200_MUX(name_str " Aux 3", &name##_aux3_mux), \
+ WM2200_MUX(name_str " Aux 4", &name##_aux4_mux), \
+ WM2200_MUX(name_str " Aux 5", &name##_aux5_mux), \
+ WM2200_MUX(name_str " Aux 6", &name##_aux6_mux)
+
+#define WM2200_MIXER_INPUT_ROUTES(name) \
+ { name, "Tone Generator", "Tone Generator" }, \
+ { name, "AEC Loopback", "AEC Loopback" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2L PGA" }, \
+ { name, "IN2R", "IN2R PGA" }, \
+ { name, "IN3L", "IN3L PGA" }, \
+ { name, "IN3R", "IN3R PGA" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }, \
+ { name, "DSP2.1", "DSP2" }, \
+ { name, "DSP2.2", "DSP2" }, \
+ { name, "DSP2.3", "DSP2" }, \
+ { name, "DSP2.4", "DSP2" }, \
+ { name, "DSP2.5", "DSP2" }, \
+ { name, "DSP2.6", "DSP2" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "EQL", "EQL" }, \
+ { name, "EQR", "EQR" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }
+
+#define WM2200_MIXER_ROUTES(widget, name) \
+ { widget, NULL, name " Mixer" }, \
+ { name " Mixer", NULL, name " Input 1" }, \
+ { name " Mixer", NULL, name " Input 2" }, \
+ { name " Mixer", NULL, name " Input 3" }, \
+ { name " Mixer", NULL, name " Input 4" }, \
+ WM2200_MIXER_INPUT_ROUTES(name " Input 1"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Input 2"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Input 3"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Input 4")
+
+#define WM2200_DSP_AUX_ROUTES(name) \
+ { name, NULL, name " Aux 1" }, \
+ { name, NULL, name " Aux 2" }, \
+ { name, NULL, name " Aux 3" }, \
+ { name, NULL, name " Aux 4" }, \
+ { name, NULL, name " Aux 5" }, \
+ { name, NULL, name " Aux 6" }, \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 1"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 2"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 3"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 4"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 5"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 6")
+
+static const char *wm2200_aec_loopback_texts[] = {
+ "OUT1L", "OUT1R", "OUT2L", "OUT2R",
+};
+
+static SOC_ENUM_SINGLE_DECL(wm2200_aec_loopback,
+ WM2200_DAC_AEC_CONTROL_1,
+ WM2200_AEC_LOOPBACK_SRC_SHIFT,
+ wm2200_aec_loopback_texts);
+
+static const struct snd_kcontrol_new wm2200_aec_loopback_mux =
+ SOC_DAPM_ENUM("AEC Loopback", wm2200_aec_loopback);
+
+static const struct snd_soc_dapm_widget wm2200_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", WM2200_CLOCKING_3, WM2200_SYSCLK_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_SUPPLY("CP1", WM2200_DM_CHARGE_PUMP_1, WM2200_CPDM_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_SUPPLY("CP2", WM2200_MIC_CHARGE_PUMP_1, WM2200_CPMIC_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1", WM2200_MIC_BIAS_CTRL_1, WM2200_MICB1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", WM2200_MIC_BIAS_CTRL_2, WM2200_MICB2_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("AVDD", 20, 0),
+
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+SND_SOC_DAPM_INPUT("IN3L"),
+SND_SOC_DAPM_INPUT("IN3R"),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_PGA("Tone Generator", WM2200_TONE_GENERATOR_1,
+ WM2200_TONE_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("IN1L PGA", WM2200_INPUT_ENABLES, WM2200_IN1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("IN1R PGA", WM2200_INPUT_ENABLES, WM2200_IN1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("IN2L PGA", WM2200_INPUT_ENABLES, WM2200_IN2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("IN2R PGA", WM2200_INPUT_ENABLES, WM2200_IN2R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("IN3L PGA", WM2200_INPUT_ENABLES, WM2200_IN3L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("IN3R PGA", WM2200_INPUT_ENABLES, WM2200_IN3R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", "Playback", 0,
+ WM2200_AUDIO_IF_1_22, WM2200_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", "Playback", 1,
+ WM2200_AUDIO_IF_1_22, WM2200_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", "Playback", 2,
+ WM2200_AUDIO_IF_1_22, WM2200_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", "Playback", 3,
+ WM2200_AUDIO_IF_1_22, WM2200_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", "Playback", 4,
+ WM2200_AUDIO_IF_1_22, WM2200_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", "Playback", 5,
+ WM2200_AUDIO_IF_1_22, WM2200_AIF1RX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA("EQL", WM2200_EQL_1, WM2200_EQL_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQR", WM2200_EQR_1, WM2200_EQR_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", WM2200_HPLPF1_1, WM2200_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", WM2200_HPLPF2_1, WM2200_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+
+WM_ADSP1("DSP1", 0),
+WM_ADSP1("DSP2", 1),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0,
+ WM2200_AUDIO_IF_1_22, WM2200_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", "Capture", 1,
+ WM2200_AUDIO_IF_1_22, WM2200_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", "Capture", 2,
+ WM2200_AUDIO_IF_1_22, WM2200_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", "Capture", 3,
+ WM2200_AUDIO_IF_1_22, WM2200_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 4,
+ WM2200_AUDIO_IF_1_22, WM2200_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 5,
+ WM2200_AUDIO_IF_1_22, WM2200_AIF1TX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_MUX("AEC Loopback", WM2200_DAC_AEC_CONTROL_1,
+ WM2200_AEC_LOOPBACK_ENA_SHIFT, 0, &wm2200_aec_loopback_mux),
+
+SND_SOC_DAPM_PGA_S("OUT1L", 0, WM2200_OUTPUT_ENABLES,
+ WM2200_OUT1L_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("OUT1R", 0, WM2200_OUTPUT_ENABLES,
+ WM2200_OUT1R_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("EPD_LP", 1, WM2200_EAR_PIECE_CTRL_1,
+ WM2200_EPD_LP_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("EPD_OUTP_LP", 1, WM2200_EAR_PIECE_CTRL_1,
+ WM2200_EPD_OUTP_LP_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("EPD_RMV_SHRT_LP", 1, WM2200_EAR_PIECE_CTRL_1,
+ WM2200_EPD_RMV_SHRT_LP_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("EPD_LN", 1, WM2200_EAR_PIECE_CTRL_1,
+ WM2200_EPD_LN_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("EPD_OUTP_LN", 1, WM2200_EAR_PIECE_CTRL_1,
+ WM2200_EPD_OUTP_LN_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("EPD_RMV_SHRT_LN", 1, WM2200_EAR_PIECE_CTRL_1,
+ WM2200_EPD_RMV_SHRT_LN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("EPD_RP", 1, WM2200_EAR_PIECE_CTRL_2,
+ WM2200_EPD_RP_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("EPD_OUTP_RP", 1, WM2200_EAR_PIECE_CTRL_2,
+ WM2200_EPD_OUTP_RP_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("EPD_RMV_SHRT_RP", 1, WM2200_EAR_PIECE_CTRL_2,
+ WM2200_EPD_RMV_SHRT_RP_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("EPD_RN", 1, WM2200_EAR_PIECE_CTRL_2,
+ WM2200_EPD_RN_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("EPD_OUTP_RN", 1, WM2200_EAR_PIECE_CTRL_2,
+ WM2200_EPD_OUTP_RN_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("EPD_RMV_SHRT_RN", 1, WM2200_EAR_PIECE_CTRL_2,
+ WM2200_EPD_RMV_SHRT_RN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("OUT2L", WM2200_OUTPUT_ENABLES, WM2200_OUT2L_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("OUT2R", WM2200_OUTPUT_ENABLES, WM2200_OUT2R_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("EPOUTLN"),
+SND_SOC_DAPM_OUTPUT("EPOUTLP"),
+SND_SOC_DAPM_OUTPUT("EPOUTRN"),
+SND_SOC_DAPM_OUTPUT("EPOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPK"),
+
+WM2200_MIXER_WIDGETS(EQL, "EQL"),
+WM2200_MIXER_WIDGETS(EQR, "EQR"),
+
+WM2200_MIXER_WIDGETS(LHPF1, "LHPF1"),
+WM2200_MIXER_WIDGETS(LHPF2, "LHPF2"),
+
+WM2200_DSP_WIDGETS(DSP1, "DSP1"),
+WM2200_DSP_WIDGETS(DSP2, "DSP2"),
+
+WM2200_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+WM2200_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+WM2200_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+WM2200_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+WM2200_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+WM2200_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+
+WM2200_MIXER_WIDGETS(OUT1L, "OUT1L"),
+WM2200_MIXER_WIDGETS(OUT1R, "OUT1R"),
+WM2200_MIXER_WIDGETS(OUT2L, "OUT2L"),
+WM2200_MIXER_WIDGETS(OUT2R, "OUT2R"),
+};
+
+static const struct snd_soc_dapm_route wm2200_dapm_routes[] = {
+ /* Everything needs SYSCLK but only hook up things on the edge
+ * of the chip */
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT2L", NULL, "SYSCLK" },
+ { "OUT2R", NULL, "SYSCLK" },
+ { "AIF1RX1", NULL, "SYSCLK" },
+ { "AIF1RX2", NULL, "SYSCLK" },
+ { "AIF1RX3", NULL, "SYSCLK" },
+ { "AIF1RX4", NULL, "SYSCLK" },
+ { "AIF1RX5", NULL, "SYSCLK" },
+ { "AIF1RX6", NULL, "SYSCLK" },
+ { "AIF1TX1", NULL, "SYSCLK" },
+ { "AIF1TX2", NULL, "SYSCLK" },
+ { "AIF1TX3", NULL, "SYSCLK" },
+ { "AIF1TX4", NULL, "SYSCLK" },
+ { "AIF1TX5", NULL, "SYSCLK" },
+ { "AIF1TX6", NULL, "SYSCLK" },
+
+ { "IN1L", NULL, "AVDD" },
+ { "IN1R", NULL, "AVDD" },
+ { "IN2L", NULL, "AVDD" },
+ { "IN2R", NULL, "AVDD" },
+ { "IN3L", NULL, "AVDD" },
+ { "IN3R", NULL, "AVDD" },
+ { "OUT1L", NULL, "AVDD" },
+ { "OUT1R", NULL, "AVDD" },
+
+ { "IN1L PGA", NULL, "IN1L" },
+ { "IN1R PGA", NULL, "IN1R" },
+ { "IN2L PGA", NULL, "IN2L" },
+ { "IN2R PGA", NULL, "IN2R" },
+ { "IN3L PGA", NULL, "IN3L" },
+ { "IN3R PGA", NULL, "IN3R" },
+
+ { "Tone Generator", NULL, "TONE" },
+
+ { "CP2", NULL, "CPVDD" },
+ { "MICBIAS1", NULL, "CP2" },
+ { "MICBIAS2", NULL, "CP2" },
+
+ { "CP1", NULL, "CPVDD" },
+ { "EPD_LN", NULL, "CP1" },
+ { "EPD_LP", NULL, "CP1" },
+ { "EPD_RN", NULL, "CP1" },
+ { "EPD_RP", NULL, "CP1" },
+
+ { "EPD_LP", NULL, "OUT1L" },
+ { "EPD_OUTP_LP", NULL, "EPD_LP" },
+ { "EPD_RMV_SHRT_LP", NULL, "EPD_OUTP_LP" },
+ { "EPOUTLP", NULL, "EPD_RMV_SHRT_LP" },
+
+ { "EPD_LN", NULL, "OUT1L" },
+ { "EPD_OUTP_LN", NULL, "EPD_LN" },
+ { "EPD_RMV_SHRT_LN", NULL, "EPD_OUTP_LN" },
+ { "EPOUTLN", NULL, "EPD_RMV_SHRT_LN" },
+
+ { "EPD_RP", NULL, "OUT1R" },
+ { "EPD_OUTP_RP", NULL, "EPD_RP" },
+ { "EPD_RMV_SHRT_RP", NULL, "EPD_OUTP_RP" },
+ { "EPOUTRP", NULL, "EPD_RMV_SHRT_RP" },
+
+ { "EPD_RN", NULL, "OUT1R" },
+ { "EPD_OUTP_RN", NULL, "EPD_RN" },
+ { "EPD_RMV_SHRT_RN", NULL, "EPD_OUTP_RN" },
+ { "EPOUTRN", NULL, "EPD_RMV_SHRT_RN" },
+
+ { "SPK", NULL, "OUT2L" },
+ { "SPK", NULL, "OUT2R" },
+
+ { "AEC Loopback", "OUT1L", "OUT1L" },
+ { "AEC Loopback", "OUT1R", "OUT1R" },
+ { "AEC Loopback", "OUT2L", "OUT2L" },
+ { "AEC Loopback", "OUT2R", "OUT2R" },
+
+ WM2200_MIXER_ROUTES("DSP1", "DSP1L"),
+ WM2200_MIXER_ROUTES("DSP1", "DSP1R"),
+ WM2200_MIXER_ROUTES("DSP2", "DSP2L"),
+ WM2200_MIXER_ROUTES("DSP2", "DSP2R"),
+
+ WM2200_DSP_AUX_ROUTES("DSP1"),
+ WM2200_DSP_AUX_ROUTES("DSP2"),
+
+ WM2200_MIXER_ROUTES("OUT1L", "OUT1L"),
+ WM2200_MIXER_ROUTES("OUT1R", "OUT1R"),
+ WM2200_MIXER_ROUTES("OUT2L", "OUT2L"),
+ WM2200_MIXER_ROUTES("OUT2R", "OUT2R"),
+
+ WM2200_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ WM2200_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ WM2200_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ WM2200_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ WM2200_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ WM2200_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+
+ WM2200_MIXER_ROUTES("EQL", "EQL"),
+ WM2200_MIXER_ROUTES("EQR", "EQR"),
+
+ WM2200_MIXER_ROUTES("LHPF1", "LHPF1"),
+ WM2200_MIXER_ROUTES("LHPF2", "LHPF2"),
+};
+
+static int wm2200_probe(struct snd_soc_codec *codec)
+{
+ struct wm2200_priv *wm2200 = dev_get_drvdata(codec->dev);
+ int ret;
+
+ wm2200->codec = codec;
+
+ ret = snd_soc_add_codec_controls(codec, wm_adsp1_fw_controls, 2);
+ if (ret != 0)
+ return ret;
+
+ return ret;
+}
+
+static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int lrclk, bclk, fmt_val;
+
+ lrclk = 0;
+ bclk = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ fmt_val = 0;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ fmt_val = 2;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported DAI format %d\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ lrclk |= WM2200_AIF1TX_LRCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ bclk |= WM2200_AIF1_BCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ lrclk |= WM2200_AIF1TX_LRCLK_MSTR;
+ bclk |= WM2200_AIF1_BCLK_MSTR;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported master mode %d\n",
+ fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk |= WM2200_AIF1_BCLK_INV;
+ lrclk |= WM2200_AIF1TX_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk |= WM2200_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk |= WM2200_AIF1TX_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_1, WM2200_AIF1_BCLK_MSTR |
+ WM2200_AIF1_BCLK_INV, bclk);
+ snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_2,
+ WM2200_AIF1TX_LRCLK_MSTR | WM2200_AIF1TX_LRCLK_INV,
+ lrclk);
+ snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_3,
+ WM2200_AIF1TX_LRCLK_MSTR | WM2200_AIF1TX_LRCLK_INV,
+ lrclk);
+ snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_5,
+ WM2200_AIF1_FMT_MASK, fmt_val);
+
+ return 0;
+}
+
+static int wm2200_sr_code[] = {
+ 0,
+ 12000,
+ 24000,
+ 48000,
+ 96000,
+ 192000,
+ 384000,
+ 768000,
+ 0,
+ 11025,
+ 22050,
+ 44100,
+ 88200,
+ 176400,
+ 352800,
+ 705600,
+ 4000,
+ 8000,
+ 16000,
+ 32000,
+ 64000,
+ 128000,
+ 256000,
+ 512000,
+};
+
+#define WM2200_NUM_BCLK_RATES 12
+
+static int wm2200_bclk_rates_dat[WM2200_NUM_BCLK_RATES] = {
+ 6144000,
+ 3072000,
+ 2048000,
+ 1536000,
+ 768000,
+ 512000,
+ 384000,
+ 256000,
+ 192000,
+ 128000,
+ 96000,
+ 64000,
+};
+
+static int wm2200_bclk_rates_cd[WM2200_NUM_BCLK_RATES] = {
+ 5644800,
+ 3763200,
+ 2882400,
+ 1881600,
+ 1411200,
+ 705600,
+ 470400,
+ 352800,
+ 176400,
+ 117600,
+ 88200,
+ 58800,
+};
+
+static int wm2200_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec);
+ int i, bclk, lrclk, wl, fl, sr_code;
+ int *bclk_rates;
+
+ /* Data sizes if not using TDM */
+ wl = snd_pcm_format_width(params_format(params));
+ if (wl < 0)
+ return wl;
+ fl = snd_soc_params_to_frame_size(params);
+ if (fl < 0)
+ return fl;
+
+ dev_dbg(codec->dev, "Word length %d bits, frame length %d bits\n",
+ wl, fl);
+
+ /* Target BCLK rate */
+ bclk = snd_soc_params_to_bclk(params);
+ if (bclk < 0)
+ return bclk;
+
+ if (!wm2200->sysclk) {
+ dev_err(codec->dev, "SYSCLK has no rate set\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm2200_sr_code); i++)
+ if (wm2200_sr_code[i] == params_rate(params))
+ break;
+ if (i == ARRAY_SIZE(wm2200_sr_code)) {
+ dev_err(codec->dev, "Unsupported sample rate: %dHz\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ sr_code = i;
+
+ dev_dbg(codec->dev, "Target BCLK is %dHz, using %dHz SYSCLK\n",
+ bclk, wm2200->sysclk);
+
+ if (wm2200->sysclk % 4000)
+ bclk_rates = wm2200_bclk_rates_cd;
+ else
+ bclk_rates = wm2200_bclk_rates_dat;
+
+ for (i = 0; i < WM2200_NUM_BCLK_RATES; i++)
+ if (bclk_rates[i] >= bclk && (bclk_rates[i] % bclk == 0))
+ break;
+ if (i == WM2200_NUM_BCLK_RATES) {
+ dev_err(codec->dev,
+ "No valid BCLK for %dHz found from %dHz SYSCLK\n",
+ bclk, wm2200->sysclk);
+ return -EINVAL;
+ }
+
+ bclk = i;
+ dev_dbg(codec->dev, "Setting %dHz BCLK\n", bclk_rates[bclk]);
+ snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_1,
+ WM2200_AIF1_BCLK_DIV_MASK, bclk);
+
+ lrclk = bclk_rates[bclk] / params_rate(params);
+ dev_dbg(codec->dev, "Setting %dHz LRCLK\n", bclk_rates[bclk] / lrclk);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ dai->symmetric_rates)
+ snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_7,
+ WM2200_AIF1RX_BCPF_MASK, lrclk);
+ else
+ snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_6,
+ WM2200_AIF1TX_BCPF_MASK, lrclk);
+
+ i = (wl << WM2200_AIF1TX_WL_SHIFT) | wl;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_9,
+ WM2200_AIF1RX_WL_MASK |
+ WM2200_AIF1RX_SLOT_LEN_MASK, i);
+ else
+ snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_8,
+ WM2200_AIF1TX_WL_MASK |
+ WM2200_AIF1TX_SLOT_LEN_MASK, i);
+
+ snd_soc_update_bits(codec, WM2200_CLOCKING_4,
+ WM2200_SAMPLE_RATE_1_MASK, sr_code);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wm2200_dai_ops = {
+ .set_fmt = wm2200_set_fmt,
+ .hw_params = wm2200_hw_params,
+};
+
+static int wm2200_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec);
+ int fval;
+
+ switch (clk_id) {
+ case WM2200_CLK_SYSCLK:
+ break;
+
+ default:
+ dev_err(codec->dev, "Unknown clock %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ switch (source) {
+ case WM2200_CLKSRC_MCLK1:
+ case WM2200_CLKSRC_MCLK2:
+ case WM2200_CLKSRC_FLL:
+ case WM2200_CLKSRC_BCLK1:
+ break;
+ default:
+ dev_err(codec->dev, "Invalid source %d\n", source);
+ return -EINVAL;
+ }
+
+ switch (freq) {
+ case 22579200:
+ case 24576000:
+ fval = 2;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock rate: %d\n", freq);
+ return -EINVAL;
+ }
+
+ /* TODO: Check if MCLKs are in use and enable/disable pulls to
+ * match.
+ */
+
+ snd_soc_update_bits(codec, WM2200_CLOCKING_3, WM2200_SYSCLK_FREQ_MASK |
+ WM2200_SYSCLK_SRC_MASK,
+ fval << WM2200_SYSCLK_FREQ_SHIFT | source);
+
+ wm2200->sysclk = freq;
+
+ return 0;
+}
+
+struct _fll_div {
+ u16 fll_fratio;
+ u16 fll_outdiv;
+ u16 fll_refclk_div;
+ u16 n;
+ u16 theta;
+ u16 lambda;
+};
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fll_fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ unsigned int target;
+ unsigned int div;
+ unsigned int fratio, gcd_fll;
+ int i;
+
+ /* Fref must be <=13.5MHz */
+ div = 1;
+ fll_div->fll_refclk_div = 0;
+ while ((Fref / div) > 13500000) {
+ div *= 2;
+ fll_div->fll_refclk_div++;
+
+ if (div > 8) {
+ pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+ }
+
+ pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout);
+
+ /* Apply the division for our remaining calculations */
+ Fref /= div;
+
+ /* Fvco should be 90-100MHz; don't check the upper bound */
+ div = 2;
+ while (Fout * div < 90000000) {
+ div++;
+ if (div > 64) {
+ pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ target = Fout * div;
+ fll_div->fll_outdiv = div - 1;
+
+ pr_debug("FLL Fvco=%dHz\n", target);
+
+ /* Find an appropraite FLL_FRATIO and factor it out of the target */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+ fratio = fll_fratios[i].ratio;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_fratios)) {
+ pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+ return -EINVAL;
+ }
+
+ fll_div->n = target / (fratio * Fref);
+
+ if (target % Fref == 0) {
+ fll_div->theta = 0;
+ fll_div->lambda = 0;
+ } else {
+ gcd_fll = gcd(target, fratio * Fref);
+
+ fll_div->theta = (target - (fll_div->n * fratio * Fref))
+ / gcd_fll;
+ fll_div->lambda = (fratio * Fref) / gcd_fll;
+ }
+
+ pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n",
+ fll_div->n, fll_div->theta, fll_div->lambda);
+ pr_debug("FLL_FRATIO=%x(%d) FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n",
+ fll_div->fll_fratio, fratio, fll_div->fll_outdiv,
+ fll_div->fll_refclk_div);
+
+ return 0;
+}
+
+static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec);
+ struct _fll_div factors;
+ int ret, i, timeout;
+ unsigned long time_left;
+
+ if (!Fout) {
+ dev_dbg(codec->dev, "FLL disabled");
+
+ if (wm2200->fll_fout)
+ pm_runtime_put(codec->dev);
+
+ wm2200->fll_fout = 0;
+ snd_soc_update_bits(codec, WM2200_FLL_CONTROL_1,
+ WM2200_FLL_ENA, 0);
+ return 0;
+ }
+
+ switch (source) {
+ case WM2200_FLL_SRC_MCLK1:
+ case WM2200_FLL_SRC_MCLK2:
+ case WM2200_FLL_SRC_BCLK:
+ break;
+ default:
+ dev_err(codec->dev, "Invalid FLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = fll_factors(&factors, Fref, Fout);
+ if (ret < 0)
+ return ret;
+
+ /* Disable the FLL while we reconfigure */
+ snd_soc_update_bits(codec, WM2200_FLL_CONTROL_1, WM2200_FLL_ENA, 0);
+
+ snd_soc_update_bits(codec, WM2200_FLL_CONTROL_2,
+ WM2200_FLL_OUTDIV_MASK | WM2200_FLL_FRATIO_MASK,
+ (factors.fll_outdiv << WM2200_FLL_OUTDIV_SHIFT) |
+ factors.fll_fratio);
+ if (factors.theta) {
+ snd_soc_update_bits(codec, WM2200_FLL_CONTROL_3,
+ WM2200_FLL_FRACN_ENA,
+ WM2200_FLL_FRACN_ENA);
+ snd_soc_update_bits(codec, WM2200_FLL_EFS_2,
+ WM2200_FLL_EFS_ENA,
+ WM2200_FLL_EFS_ENA);
+ } else {
+ snd_soc_update_bits(codec, WM2200_FLL_CONTROL_3,
+ WM2200_FLL_FRACN_ENA, 0);
+ snd_soc_update_bits(codec, WM2200_FLL_EFS_2,
+ WM2200_FLL_EFS_ENA, 0);
+ }
+
+ snd_soc_update_bits(codec, WM2200_FLL_CONTROL_4, WM2200_FLL_THETA_MASK,
+ factors.theta);
+ snd_soc_update_bits(codec, WM2200_FLL_CONTROL_6, WM2200_FLL_N_MASK,
+ factors.n);
+ snd_soc_update_bits(codec, WM2200_FLL_CONTROL_7,
+ WM2200_FLL_CLK_REF_DIV_MASK |
+ WM2200_FLL_CLK_REF_SRC_MASK,
+ (factors.fll_refclk_div
+ << WM2200_FLL_CLK_REF_DIV_SHIFT) | source);
+ snd_soc_update_bits(codec, WM2200_FLL_EFS_1,
+ WM2200_FLL_LAMBDA_MASK, factors.lambda);
+
+ /* Clear any pending completions */
+ try_wait_for_completion(&wm2200->fll_lock);
+
+ pm_runtime_get_sync(codec->dev);
+
+ snd_soc_update_bits(codec, WM2200_FLL_CONTROL_1,
+ WM2200_FLL_ENA, WM2200_FLL_ENA);
+
+ if (i2c->irq)
+ timeout = 2;
+ else
+ timeout = 50;
+
+ snd_soc_update_bits(codec, WM2200_CLOCKING_3, WM2200_SYSCLK_ENA,
+ WM2200_SYSCLK_ENA);
+
+ /* Poll for the lock; will use the interrupt to exit quickly */
+ for (i = 0; i < timeout; i++) {
+ if (i2c->irq) {
+ time_left = wait_for_completion_timeout(
+ &wm2200->fll_lock,
+ msecs_to_jiffies(25));
+ if (time_left > 0)
+ break;
+ } else {
+ msleep(1);
+ }
+
+ ret = snd_soc_read(codec,
+ WM2200_INTERRUPT_RAW_STATUS_2);
+ if (ret < 0) {
+ dev_err(codec->dev,
+ "Failed to read FLL status: %d\n",
+ ret);
+ continue;
+ }
+ if (ret & WM2200_FLL_LOCK_STS)
+ break;
+ }
+ if (i == timeout) {
+ dev_err(codec->dev, "FLL lock timed out\n");
+ pm_runtime_put(codec->dev);
+ return -ETIMEDOUT;
+ }
+
+ wm2200->fll_src = source;
+ wm2200->fll_fref = Fref;
+ wm2200->fll_fout = Fout;
+
+ dev_dbg(codec->dev, "FLL running %dHz->%dHz\n", Fref, Fout);
+
+ return 0;
+}
+
+static int wm2200_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int val = 0;
+ int ret;
+
+ ret = snd_soc_read(codec, WM2200_GPIO_CTRL_1);
+ if (ret >= 0) {
+ if ((ret & WM2200_GP1_FN_MASK) != 0) {
+ dai->symmetric_rates = true;
+ val = WM2200_AIF1TX_LRCLK_SRC;
+ }
+ } else {
+ dev_err(codec->dev, "Failed to read GPIO 1 config: %d\n", ret);
+ }
+
+ snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_2,
+ WM2200_AIF1TX_LRCLK_SRC, val);
+
+ return 0;
+}
+
+#define WM2200_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM2200_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver wm2200_dai = {
+ .name = "wm2200",
+ .probe = wm2200_dai_probe,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM2200_RATES,
+ .formats = WM2200_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM2200_RATES,
+ .formats = WM2200_FORMATS,
+ },
+ .ops = &wm2200_dai_ops,
+};
+
+static struct snd_soc_codec_driver soc_codec_wm2200 = {
+ .probe = wm2200_probe,
+
+ .idle_bias_off = true,
+ .ignore_pmdown_time = true,
+ .set_sysclk = wm2200_set_sysclk,
+ .set_pll = wm2200_set_fll,
+
+ .controls = wm2200_snd_controls,
+ .num_controls = ARRAY_SIZE(wm2200_snd_controls),
+ .dapm_widgets = wm2200_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm2200_dapm_widgets),
+ .dapm_routes = wm2200_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm2200_dapm_routes),
+};
+
+static irqreturn_t wm2200_irq(int irq, void *data)
+{
+ struct wm2200_priv *wm2200 = data;
+ unsigned int val, mask;
+ int ret;
+
+ ret = regmap_read(wm2200->regmap, WM2200_INTERRUPT_STATUS_2, &val);
+ if (ret != 0) {
+ dev_err(wm2200->dev, "Failed to read IRQ status: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ ret = regmap_read(wm2200->regmap, WM2200_INTERRUPT_STATUS_2_MASK,
+ &mask);
+ if (ret != 0) {
+ dev_warn(wm2200->dev, "Failed to read IRQ mask: %d\n", ret);
+ mask = 0;
+ }
+
+ val &= ~mask;
+
+ if (val & WM2200_FLL_LOCK_EINT) {
+ dev_dbg(wm2200->dev, "FLL locked\n");
+ complete(&wm2200->fll_lock);
+ }
+
+ if (val) {
+ regmap_write(wm2200->regmap, WM2200_INTERRUPT_STATUS_2, val);
+
+ return IRQ_HANDLED;
+ } else {
+ return IRQ_NONE;
+ }
+}
+
+static const struct regmap_config wm2200_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+
+ .max_register = WM2200_MAX_REGISTER + (ARRAY_SIZE(wm2200_ranges) *
+ WM2200_DSP_SPACING),
+ .reg_defaults = wm2200_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm2200_reg_defaults),
+ .volatile_reg = wm2200_volatile_register,
+ .readable_reg = wm2200_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .ranges = wm2200_ranges,
+ .num_ranges = ARRAY_SIZE(wm2200_ranges),
+};
+
+static const unsigned int wm2200_dig_vu[] = {
+ WM2200_DAC_DIGITAL_VOLUME_1L,
+ WM2200_DAC_DIGITAL_VOLUME_1R,
+ WM2200_DAC_DIGITAL_VOLUME_2L,
+ WM2200_DAC_DIGITAL_VOLUME_2R,
+ WM2200_ADC_DIGITAL_VOLUME_1L,
+ WM2200_ADC_DIGITAL_VOLUME_1R,
+ WM2200_ADC_DIGITAL_VOLUME_2L,
+ WM2200_ADC_DIGITAL_VOLUME_2R,
+ WM2200_ADC_DIGITAL_VOLUME_3L,
+ WM2200_ADC_DIGITAL_VOLUME_3R,
+};
+
+static const unsigned int wm2200_mic_ctrl_reg[] = {
+ WM2200_IN1L_CONTROL,
+ WM2200_IN2L_CONTROL,
+ WM2200_IN3L_CONTROL,
+};
+
+static int wm2200_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm2200_pdata *pdata = dev_get_platdata(&i2c->dev);
+ struct wm2200_priv *wm2200;
+ unsigned int reg;
+ int ret, i;
+ int val;
+
+ wm2200 = devm_kzalloc(&i2c->dev, sizeof(struct wm2200_priv),
+ GFP_KERNEL);
+ if (wm2200 == NULL)
+ return -ENOMEM;
+
+ wm2200->dev = &i2c->dev;
+ init_completion(&wm2200->fll_lock);
+
+ wm2200->regmap = devm_regmap_init_i2c(i2c, &wm2200_regmap);
+ if (IS_ERR(wm2200->regmap)) {
+ ret = PTR_ERR(wm2200->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ for (i = 0; i < 2; i++) {
+ wm2200->dsp[i].type = WMFW_ADSP1;
+ wm2200->dsp[i].part = "wm2200";
+ wm2200->dsp[i].num = i + 1;
+ wm2200->dsp[i].dev = &i2c->dev;
+ wm2200->dsp[i].regmap = wm2200->regmap;
+ wm2200->dsp[i].sysclk_reg = WM2200_CLOCKING_3;
+ wm2200->dsp[i].sysclk_mask = WM2200_SYSCLK_FREQ_MASK;
+ wm2200->dsp[i].sysclk_shift = WM2200_SYSCLK_FREQ_SHIFT;
+ }
+
+ wm2200->dsp[0].base = WM2200_DSP1_CONTROL_1;
+ wm2200->dsp[0].mem = wm2200_dsp1_regions;
+ wm2200->dsp[0].num_mems = ARRAY_SIZE(wm2200_dsp1_regions);
+
+ wm2200->dsp[1].base = WM2200_DSP2_CONTROL_1;
+ wm2200->dsp[1].mem = wm2200_dsp2_regions;
+ wm2200->dsp[1].num_mems = ARRAY_SIZE(wm2200_dsp2_regions);
+
+ for (i = 0; i < ARRAY_SIZE(wm2200->dsp); i++)
+ wm_adsp1_init(&wm2200->dsp[i]);
+
+ if (pdata)
+ wm2200->pdata = *pdata;
+
+ i2c_set_clientdata(i2c, wm2200);
+
+ for (i = 0; i < ARRAY_SIZE(wm2200->core_supplies); i++)
+ wm2200->core_supplies[i].supply = wm2200_core_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev,
+ ARRAY_SIZE(wm2200->core_supplies),
+ wm2200->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm2200->core_supplies),
+ wm2200->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (wm2200->pdata.ldo_ena) {
+ ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.ldo_ena,
+ GPIOF_OUT_INIT_HIGH,
+ "WM2200 LDOENA");
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request LDOENA %d: %d\n",
+ wm2200->pdata.ldo_ena, ret);
+ goto err_enable;
+ }
+ msleep(2);
+ }
+
+ if (wm2200->pdata.reset) {
+ ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.reset,
+ GPIOF_OUT_INIT_HIGH,
+ "WM2200 /RESET");
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request /RESET %d: %d\n",
+ wm2200->pdata.reset, ret);
+ goto err_ldo;
+ }
+ }
+
+ ret = regmap_read(wm2200->regmap, WM2200_SOFTWARE_RESET, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret);
+ goto err_reset;
+ }
+ switch (reg) {
+ case 0x2200:
+ break;
+
+ default:
+ dev_err(&i2c->dev, "Device is not a WM2200, ID is %x\n", reg);
+ ret = -EINVAL;
+ goto err_reset;
+ }
+
+ ret = regmap_read(wm2200->regmap, WM2200_DEVICE_REVISION, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read revision register\n");
+ goto err_reset;
+ }
+
+ wm2200->rev = reg & WM2200_DEVICE_REVISION_MASK;
+
+ dev_info(&i2c->dev, "revision %c\n", wm2200->rev + 'A');
+
+ switch (wm2200->rev) {
+ case 0:
+ case 1:
+ ret = regmap_register_patch(wm2200->regmap, wm2200_reva_patch,
+ ARRAY_SIZE(wm2200_reva_patch));
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register patch: %d\n",
+ ret);
+ }
+ break;
+ default:
+ break;
+ }
+
+ ret = wm2200_reset(wm2200);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to issue reset\n");
+ goto err_reset;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm2200->pdata.gpio_defaults); i++) {
+ if (!wm2200->pdata.gpio_defaults[i])
+ continue;
+
+ regmap_write(wm2200->regmap, WM2200_GPIO_CTRL_1 + i,
+ wm2200->pdata.gpio_defaults[i]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm2200_dig_vu); i++)
+ regmap_update_bits(wm2200->regmap, wm2200_dig_vu[i],
+ WM2200_OUT_VU, WM2200_OUT_VU);
+
+ /* Assign slots 1-6 to channels 1-6 for both TX and RX */
+ for (i = 0; i < 6; i++) {
+ regmap_write(wm2200->regmap, WM2200_AUDIO_IF_1_10 + i, i);
+ regmap_write(wm2200->regmap, WM2200_AUDIO_IF_1_16 + i, i);
+ }
+
+ for (i = 0; i < WM2200_MAX_MICBIAS; i++) {
+ if (!wm2200->pdata.micbias[i].mb_lvl &&
+ !wm2200->pdata.micbias[i].bypass)
+ continue;
+
+ /* Apply default for bypass mode */
+ if (!wm2200->pdata.micbias[i].mb_lvl)
+ wm2200->pdata.micbias[i].mb_lvl
+ = WM2200_MBIAS_LVL_1V5;
+
+ val = (wm2200->pdata.micbias[i].mb_lvl -1)
+ << WM2200_MICB1_LVL_SHIFT;
+
+ if (wm2200->pdata.micbias[i].discharge)
+ val |= WM2200_MICB1_DISCH;
+
+ if (wm2200->pdata.micbias[i].fast_start)
+ val |= WM2200_MICB1_RATE;
+
+ if (wm2200->pdata.micbias[i].bypass)
+ val |= WM2200_MICB1_MODE;
+
+ regmap_update_bits(wm2200->regmap,
+ WM2200_MIC_BIAS_CTRL_1 + i,
+ WM2200_MICB1_LVL_MASK |
+ WM2200_MICB1_DISCH |
+ WM2200_MICB1_MODE |
+ WM2200_MICB1_RATE, val);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm2200->pdata.in_mode); i++) {
+ regmap_update_bits(wm2200->regmap, wm2200_mic_ctrl_reg[i],
+ WM2200_IN1_MODE_MASK |
+ WM2200_IN1_DMIC_SUP_MASK,
+ (wm2200->pdata.in_mode[i] <<
+ WM2200_IN1_MODE_SHIFT) |
+ (wm2200->pdata.dmic_sup[i] <<
+ WM2200_IN1_DMIC_SUP_SHIFT));
+ }
+
+ if (i2c->irq) {
+ ret = request_threaded_irq(i2c->irq, NULL, wm2200_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "wm2200", wm2200);
+ if (ret == 0)
+ regmap_update_bits(wm2200->regmap,
+ WM2200_INTERRUPT_STATUS_2_MASK,
+ WM2200_FLL_LOCK_EINT, 0);
+ else
+ dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n",
+ i2c->irq, ret);
+ }
+
+ pm_runtime_set_active(&i2c->dev);
+ pm_runtime_enable(&i2c->dev);
+ pm_request_idle(&i2c->dev);
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_wm2200,
+ &wm2200_dai, 1);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+ goto err_pm_runtime;
+ }
+
+ return 0;
+
+err_pm_runtime:
+ pm_runtime_disable(&i2c->dev);
+err_reset:
+ if (wm2200->pdata.reset)
+ gpio_set_value_cansleep(wm2200->pdata.reset, 0);
+err_ldo:
+ if (wm2200->pdata.ldo_ena)
+ gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
+ wm2200->core_supplies);
+ return ret;
+}
+
+static int wm2200_i2c_remove(struct i2c_client *i2c)
+{
+ struct wm2200_priv *wm2200 = i2c_get_clientdata(i2c);
+
+ snd_soc_unregister_codec(&i2c->dev);
+ if (i2c->irq)
+ free_irq(i2c->irq, wm2200);
+ if (wm2200->pdata.reset)
+ gpio_set_value_cansleep(wm2200->pdata.reset, 0);
+ if (wm2200->pdata.ldo_ena)
+ gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm2200_runtime_suspend(struct device *dev)
+{
+ struct wm2200_priv *wm2200 = dev_get_drvdata(dev);
+
+ regcache_cache_only(wm2200->regmap, true);
+ regcache_mark_dirty(wm2200->regmap);
+ if (wm2200->pdata.ldo_ena)
+ gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
+ regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
+ wm2200->core_supplies);
+
+ return 0;
+}
+
+static int wm2200_runtime_resume(struct device *dev)
+{
+ struct wm2200_priv *wm2200 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm2200->core_supplies),
+ wm2200->core_supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (wm2200->pdata.ldo_ena) {
+ gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 1);
+ msleep(2);
+ }
+
+ regcache_cache_only(wm2200->regmap, false);
+ regcache_sync(wm2200->regmap);
+
+ return 0;
+}
+#endif
+
+static struct dev_pm_ops wm2200_pm = {
+ SET_RUNTIME_PM_OPS(wm2200_runtime_suspend, wm2200_runtime_resume,
+ NULL)
+};
+
+static const struct i2c_device_id wm2200_i2c_id[] = {
+ { "wm2200", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm2200_i2c_id);
+
+static struct i2c_driver wm2200_i2c_driver = {
+ .driver = {
+ .name = "wm2200",
+ .owner = THIS_MODULE,
+ .pm = &wm2200_pm,
+ },
+ .probe = wm2200_i2c_probe,
+ .remove = wm2200_i2c_remove,
+ .id_table = wm2200_i2c_id,
+};
+
+module_i2c_driver(wm2200_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM2200 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm2200.h b/kernel/sound/soc/codecs/wm2200.h
new file mode 100644
index 000000000..5d719d6b4
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm2200.h
@@ -0,0 +1,3674 @@
+/*
+ * wm2200.h - WM2200 audio codec interface
+ *
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _WM2200_H
+#define _WM2200_H
+
+#define WM2200_CLK_SYSCLK 1
+
+#define WM2200_CLKSRC_MCLK1 0
+#define WM2200_CLKSRC_MCLK2 1
+#define WM2200_CLKSRC_FLL 4
+#define WM2200_CLKSRC_BCLK1 8
+
+#define WM2200_FLL_SRC_MCLK1 0
+#define WM2200_FLL_SRC_MCLK2 1
+#define WM2200_FLL_SRC_BCLK 2
+
+/*
+ * Register values.
+ */
+#define WM2200_SOFTWARE_RESET 0x00
+#define WM2200_DEVICE_REVISION 0x01
+#define WM2200_TONE_GENERATOR_1 0x0B
+#define WM2200_CLOCKING_3 0x102
+#define WM2200_CLOCKING_4 0x103
+#define WM2200_FLL_CONTROL_1 0x111
+#define WM2200_FLL_CONTROL_2 0x112
+#define WM2200_FLL_CONTROL_3 0x113
+#define WM2200_FLL_CONTROL_4 0x114
+#define WM2200_FLL_CONTROL_6 0x116
+#define WM2200_FLL_CONTROL_7 0x117
+#define WM2200_FLL_EFS_1 0x119
+#define WM2200_FLL_EFS_2 0x11A
+#define WM2200_MIC_CHARGE_PUMP_1 0x200
+#define WM2200_MIC_CHARGE_PUMP_2 0x201
+#define WM2200_DM_CHARGE_PUMP_1 0x202
+#define WM2200_MIC_BIAS_CTRL_1 0x20C
+#define WM2200_MIC_BIAS_CTRL_2 0x20D
+#define WM2200_EAR_PIECE_CTRL_1 0x20F
+#define WM2200_EAR_PIECE_CTRL_2 0x210
+#define WM2200_INPUT_ENABLES 0x301
+#define WM2200_IN1L_CONTROL 0x302
+#define WM2200_IN1R_CONTROL 0x303
+#define WM2200_IN2L_CONTROL 0x304
+#define WM2200_IN2R_CONTROL 0x305
+#define WM2200_IN3L_CONTROL 0x306
+#define WM2200_IN3R_CONTROL 0x307
+#define WM2200_RXANC_SRC 0x30A
+#define WM2200_INPUT_VOLUME_RAMP 0x30B
+#define WM2200_ADC_DIGITAL_VOLUME_1L 0x30C
+#define WM2200_ADC_DIGITAL_VOLUME_1R 0x30D
+#define WM2200_ADC_DIGITAL_VOLUME_2L 0x30E
+#define WM2200_ADC_DIGITAL_VOLUME_2R 0x30F
+#define WM2200_ADC_DIGITAL_VOLUME_3L 0x310
+#define WM2200_ADC_DIGITAL_VOLUME_3R 0x311
+#define WM2200_OUTPUT_ENABLES 0x400
+#define WM2200_DAC_VOLUME_LIMIT_1L 0x401
+#define WM2200_DAC_VOLUME_LIMIT_1R 0x402
+#define WM2200_DAC_VOLUME_LIMIT_2L 0x403
+#define WM2200_DAC_VOLUME_LIMIT_2R 0x404
+#define WM2200_DAC_AEC_CONTROL_1 0x409
+#define WM2200_OUTPUT_VOLUME_RAMP 0x40A
+#define WM2200_DAC_DIGITAL_VOLUME_1L 0x40B
+#define WM2200_DAC_DIGITAL_VOLUME_1R 0x40C
+#define WM2200_DAC_DIGITAL_VOLUME_2L 0x40D
+#define WM2200_DAC_DIGITAL_VOLUME_2R 0x40E
+#define WM2200_PDM_1 0x417
+#define WM2200_PDM_2 0x418
+#define WM2200_AUDIO_IF_1_1 0x500
+#define WM2200_AUDIO_IF_1_2 0x501
+#define WM2200_AUDIO_IF_1_3 0x502
+#define WM2200_AUDIO_IF_1_4 0x503
+#define WM2200_AUDIO_IF_1_5 0x504
+#define WM2200_AUDIO_IF_1_6 0x505
+#define WM2200_AUDIO_IF_1_7 0x506
+#define WM2200_AUDIO_IF_1_8 0x507
+#define WM2200_AUDIO_IF_1_9 0x508
+#define WM2200_AUDIO_IF_1_10 0x509
+#define WM2200_AUDIO_IF_1_11 0x50A
+#define WM2200_AUDIO_IF_1_12 0x50B
+#define WM2200_AUDIO_IF_1_13 0x50C
+#define WM2200_AUDIO_IF_1_14 0x50D
+#define WM2200_AUDIO_IF_1_15 0x50E
+#define WM2200_AUDIO_IF_1_16 0x50F
+#define WM2200_AUDIO_IF_1_17 0x510
+#define WM2200_AUDIO_IF_1_18 0x511
+#define WM2200_AUDIO_IF_1_19 0x512
+#define WM2200_AUDIO_IF_1_20 0x513
+#define WM2200_AUDIO_IF_1_21 0x514
+#define WM2200_AUDIO_IF_1_22 0x515
+#define WM2200_OUT1LMIX_INPUT_1_SOURCE 0x600
+#define WM2200_OUT1LMIX_INPUT_1_VOLUME 0x601
+#define WM2200_OUT1LMIX_INPUT_2_SOURCE 0x602
+#define WM2200_OUT1LMIX_INPUT_2_VOLUME 0x603
+#define WM2200_OUT1LMIX_INPUT_3_SOURCE 0x604
+#define WM2200_OUT1LMIX_INPUT_3_VOLUME 0x605
+#define WM2200_OUT1LMIX_INPUT_4_SOURCE 0x606
+#define WM2200_OUT1LMIX_INPUT_4_VOLUME 0x607
+#define WM2200_OUT1RMIX_INPUT_1_SOURCE 0x608
+#define WM2200_OUT1RMIX_INPUT_1_VOLUME 0x609
+#define WM2200_OUT1RMIX_INPUT_2_SOURCE 0x60A
+#define WM2200_OUT1RMIX_INPUT_2_VOLUME 0x60B
+#define WM2200_OUT1RMIX_INPUT_3_SOURCE 0x60C
+#define WM2200_OUT1RMIX_INPUT_3_VOLUME 0x60D
+#define WM2200_OUT1RMIX_INPUT_4_SOURCE 0x60E
+#define WM2200_OUT1RMIX_INPUT_4_VOLUME 0x60F
+#define WM2200_OUT2LMIX_INPUT_1_SOURCE 0x610
+#define WM2200_OUT2LMIX_INPUT_1_VOLUME 0x611
+#define WM2200_OUT2LMIX_INPUT_2_SOURCE 0x612
+#define WM2200_OUT2LMIX_INPUT_2_VOLUME 0x613
+#define WM2200_OUT2LMIX_INPUT_3_SOURCE 0x614
+#define WM2200_OUT2LMIX_INPUT_3_VOLUME 0x615
+#define WM2200_OUT2LMIX_INPUT_4_SOURCE 0x616
+#define WM2200_OUT2LMIX_INPUT_4_VOLUME 0x617
+#define WM2200_OUT2RMIX_INPUT_1_SOURCE 0x618
+#define WM2200_OUT2RMIX_INPUT_1_VOLUME 0x619
+#define WM2200_OUT2RMIX_INPUT_2_SOURCE 0x61A
+#define WM2200_OUT2RMIX_INPUT_2_VOLUME 0x61B
+#define WM2200_OUT2RMIX_INPUT_3_SOURCE 0x61C
+#define WM2200_OUT2RMIX_INPUT_3_VOLUME 0x61D
+#define WM2200_OUT2RMIX_INPUT_4_SOURCE 0x61E
+#define WM2200_OUT2RMIX_INPUT_4_VOLUME 0x61F
+#define WM2200_AIF1TX1MIX_INPUT_1_SOURCE 0x620
+#define WM2200_AIF1TX1MIX_INPUT_1_VOLUME 0x621
+#define WM2200_AIF1TX1MIX_INPUT_2_SOURCE 0x622
+#define WM2200_AIF1TX1MIX_INPUT_2_VOLUME 0x623
+#define WM2200_AIF1TX1MIX_INPUT_3_SOURCE 0x624
+#define WM2200_AIF1TX1MIX_INPUT_3_VOLUME 0x625
+#define WM2200_AIF1TX1MIX_INPUT_4_SOURCE 0x626
+#define WM2200_AIF1TX1MIX_INPUT_4_VOLUME 0x627
+#define WM2200_AIF1TX2MIX_INPUT_1_SOURCE 0x628
+#define WM2200_AIF1TX2MIX_INPUT_1_VOLUME 0x629
+#define WM2200_AIF1TX2MIX_INPUT_2_SOURCE 0x62A
+#define WM2200_AIF1TX2MIX_INPUT_2_VOLUME 0x62B
+#define WM2200_AIF1TX2MIX_INPUT_3_SOURCE 0x62C
+#define WM2200_AIF1TX2MIX_INPUT_3_VOLUME 0x62D
+#define WM2200_AIF1TX2MIX_INPUT_4_SOURCE 0x62E
+#define WM2200_AIF1TX2MIX_INPUT_4_VOLUME 0x62F
+#define WM2200_AIF1TX3MIX_INPUT_1_SOURCE 0x630
+#define WM2200_AIF1TX3MIX_INPUT_1_VOLUME 0x631
+#define WM2200_AIF1TX3MIX_INPUT_2_SOURCE 0x632
+#define WM2200_AIF1TX3MIX_INPUT_2_VOLUME 0x633
+#define WM2200_AIF1TX3MIX_INPUT_3_SOURCE 0x634
+#define WM2200_AIF1TX3MIX_INPUT_3_VOLUME 0x635
+#define WM2200_AIF1TX3MIX_INPUT_4_SOURCE 0x636
+#define WM2200_AIF1TX3MIX_INPUT_4_VOLUME 0x637
+#define WM2200_AIF1TX4MIX_INPUT_1_SOURCE 0x638
+#define WM2200_AIF1TX4MIX_INPUT_1_VOLUME 0x639
+#define WM2200_AIF1TX4MIX_INPUT_2_SOURCE 0x63A
+#define WM2200_AIF1TX4MIX_INPUT_2_VOLUME 0x63B
+#define WM2200_AIF1TX4MIX_INPUT_3_SOURCE 0x63C
+#define WM2200_AIF1TX4MIX_INPUT_3_VOLUME 0x63D
+#define WM2200_AIF1TX4MIX_INPUT_4_SOURCE 0x63E
+#define WM2200_AIF1TX4MIX_INPUT_4_VOLUME 0x63F
+#define WM2200_AIF1TX5MIX_INPUT_1_SOURCE 0x640
+#define WM2200_AIF1TX5MIX_INPUT_1_VOLUME 0x641
+#define WM2200_AIF1TX5MIX_INPUT_2_SOURCE 0x642
+#define WM2200_AIF1TX5MIX_INPUT_2_VOLUME 0x643
+#define WM2200_AIF1TX5MIX_INPUT_3_SOURCE 0x644
+#define WM2200_AIF1TX5MIX_INPUT_3_VOLUME 0x645
+#define WM2200_AIF1TX5MIX_INPUT_4_SOURCE 0x646
+#define WM2200_AIF1TX5MIX_INPUT_4_VOLUME 0x647
+#define WM2200_AIF1TX6MIX_INPUT_1_SOURCE 0x648
+#define WM2200_AIF1TX6MIX_INPUT_1_VOLUME 0x649
+#define WM2200_AIF1TX6MIX_INPUT_2_SOURCE 0x64A
+#define WM2200_AIF1TX6MIX_INPUT_2_VOLUME 0x64B
+#define WM2200_AIF1TX6MIX_INPUT_3_SOURCE 0x64C
+#define WM2200_AIF1TX6MIX_INPUT_3_VOLUME 0x64D
+#define WM2200_AIF1TX6MIX_INPUT_4_SOURCE 0x64E
+#define WM2200_AIF1TX6MIX_INPUT_4_VOLUME 0x64F
+#define WM2200_EQLMIX_INPUT_1_SOURCE 0x650
+#define WM2200_EQLMIX_INPUT_1_VOLUME 0x651
+#define WM2200_EQLMIX_INPUT_2_SOURCE 0x652
+#define WM2200_EQLMIX_INPUT_2_VOLUME 0x653
+#define WM2200_EQLMIX_INPUT_3_SOURCE 0x654
+#define WM2200_EQLMIX_INPUT_3_VOLUME 0x655
+#define WM2200_EQLMIX_INPUT_4_SOURCE 0x656
+#define WM2200_EQLMIX_INPUT_4_VOLUME 0x657
+#define WM2200_EQRMIX_INPUT_1_SOURCE 0x658
+#define WM2200_EQRMIX_INPUT_1_VOLUME 0x659
+#define WM2200_EQRMIX_INPUT_2_SOURCE 0x65A
+#define WM2200_EQRMIX_INPUT_2_VOLUME 0x65B
+#define WM2200_EQRMIX_INPUT_3_SOURCE 0x65C
+#define WM2200_EQRMIX_INPUT_3_VOLUME 0x65D
+#define WM2200_EQRMIX_INPUT_4_SOURCE 0x65E
+#define WM2200_EQRMIX_INPUT_4_VOLUME 0x65F
+#define WM2200_LHPF1MIX_INPUT_1_SOURCE 0x660
+#define WM2200_LHPF1MIX_INPUT_1_VOLUME 0x661
+#define WM2200_LHPF1MIX_INPUT_2_SOURCE 0x662
+#define WM2200_LHPF1MIX_INPUT_2_VOLUME 0x663
+#define WM2200_LHPF1MIX_INPUT_3_SOURCE 0x664
+#define WM2200_LHPF1MIX_INPUT_3_VOLUME 0x665
+#define WM2200_LHPF1MIX_INPUT_4_SOURCE 0x666
+#define WM2200_LHPF1MIX_INPUT_4_VOLUME 0x667
+#define WM2200_LHPF2MIX_INPUT_1_SOURCE 0x668
+#define WM2200_LHPF2MIX_INPUT_1_VOLUME 0x669
+#define WM2200_LHPF2MIX_INPUT_2_SOURCE 0x66A
+#define WM2200_LHPF2MIX_INPUT_2_VOLUME 0x66B
+#define WM2200_LHPF2MIX_INPUT_3_SOURCE 0x66C
+#define WM2200_LHPF2MIX_INPUT_3_VOLUME 0x66D
+#define WM2200_LHPF2MIX_INPUT_4_SOURCE 0x66E
+#define WM2200_LHPF2MIX_INPUT_4_VOLUME 0x66F
+#define WM2200_DSP1LMIX_INPUT_1_SOURCE 0x670
+#define WM2200_DSP1LMIX_INPUT_1_VOLUME 0x671
+#define WM2200_DSP1LMIX_INPUT_2_SOURCE 0x672
+#define WM2200_DSP1LMIX_INPUT_2_VOLUME 0x673
+#define WM2200_DSP1LMIX_INPUT_3_SOURCE 0x674
+#define WM2200_DSP1LMIX_INPUT_3_VOLUME 0x675
+#define WM2200_DSP1LMIX_INPUT_4_SOURCE 0x676
+#define WM2200_DSP1LMIX_INPUT_4_VOLUME 0x677
+#define WM2200_DSP1RMIX_INPUT_1_SOURCE 0x678
+#define WM2200_DSP1RMIX_INPUT_1_VOLUME 0x679
+#define WM2200_DSP1RMIX_INPUT_2_SOURCE 0x67A
+#define WM2200_DSP1RMIX_INPUT_2_VOLUME 0x67B
+#define WM2200_DSP1RMIX_INPUT_3_SOURCE 0x67C
+#define WM2200_DSP1RMIX_INPUT_3_VOLUME 0x67D
+#define WM2200_DSP1RMIX_INPUT_4_SOURCE 0x67E
+#define WM2200_DSP1RMIX_INPUT_4_VOLUME 0x67F
+#define WM2200_DSP1AUX1MIX_INPUT_1_SOURCE 0x680
+#define WM2200_DSP1AUX2MIX_INPUT_1_SOURCE 0x681
+#define WM2200_DSP1AUX3MIX_INPUT_1_SOURCE 0x682
+#define WM2200_DSP1AUX4MIX_INPUT_1_SOURCE 0x683
+#define WM2200_DSP1AUX5MIX_INPUT_1_SOURCE 0x684
+#define WM2200_DSP1AUX6MIX_INPUT_1_SOURCE 0x685
+#define WM2200_DSP2LMIX_INPUT_1_SOURCE 0x686
+#define WM2200_DSP2LMIX_INPUT_1_VOLUME 0x687
+#define WM2200_DSP2LMIX_INPUT_2_SOURCE 0x688
+#define WM2200_DSP2LMIX_INPUT_2_VOLUME 0x689
+#define WM2200_DSP2LMIX_INPUT_3_SOURCE 0x68A
+#define WM2200_DSP2LMIX_INPUT_3_VOLUME 0x68B
+#define WM2200_DSP2LMIX_INPUT_4_SOURCE 0x68C
+#define WM2200_DSP2LMIX_INPUT_4_VOLUME 0x68D
+#define WM2200_DSP2RMIX_INPUT_1_SOURCE 0x68E
+#define WM2200_DSP2RMIX_INPUT_1_VOLUME 0x68F
+#define WM2200_DSP2RMIX_INPUT_2_SOURCE 0x690
+#define WM2200_DSP2RMIX_INPUT_2_VOLUME 0x691
+#define WM2200_DSP2RMIX_INPUT_3_SOURCE 0x692
+#define WM2200_DSP2RMIX_INPUT_3_VOLUME 0x693
+#define WM2200_DSP2RMIX_INPUT_4_SOURCE 0x694
+#define WM2200_DSP2RMIX_INPUT_4_VOLUME 0x695
+#define WM2200_DSP2AUX1MIX_INPUT_1_SOURCE 0x696
+#define WM2200_DSP2AUX2MIX_INPUT_1_SOURCE 0x697
+#define WM2200_DSP2AUX3MIX_INPUT_1_SOURCE 0x698
+#define WM2200_DSP2AUX4MIX_INPUT_1_SOURCE 0x699
+#define WM2200_DSP2AUX5MIX_INPUT_1_SOURCE 0x69A
+#define WM2200_DSP2AUX6MIX_INPUT_1_SOURCE 0x69B
+#define WM2200_GPIO_CTRL_1 0x700
+#define WM2200_GPIO_CTRL_2 0x701
+#define WM2200_GPIO_CTRL_3 0x702
+#define WM2200_GPIO_CTRL_4 0x703
+#define WM2200_ADPS1_IRQ0 0x707
+#define WM2200_ADPS1_IRQ1 0x708
+#define WM2200_MISC_PAD_CTRL_1 0x709
+#define WM2200_INTERRUPT_STATUS_1 0x800
+#define WM2200_INTERRUPT_STATUS_1_MASK 0x801
+#define WM2200_INTERRUPT_STATUS_2 0x802
+#define WM2200_INTERRUPT_RAW_STATUS_2 0x803
+#define WM2200_INTERRUPT_STATUS_2_MASK 0x804
+#define WM2200_INTERRUPT_CONTROL 0x808
+#define WM2200_EQL_1 0x900
+#define WM2200_EQL_2 0x901
+#define WM2200_EQL_3 0x902
+#define WM2200_EQL_4 0x903
+#define WM2200_EQL_5 0x904
+#define WM2200_EQL_6 0x905
+#define WM2200_EQL_7 0x906
+#define WM2200_EQL_8 0x907
+#define WM2200_EQL_9 0x908
+#define WM2200_EQL_10 0x909
+#define WM2200_EQL_11 0x90A
+#define WM2200_EQL_12 0x90B
+#define WM2200_EQL_13 0x90C
+#define WM2200_EQL_14 0x90D
+#define WM2200_EQL_15 0x90E
+#define WM2200_EQL_16 0x90F
+#define WM2200_EQL_17 0x910
+#define WM2200_EQL_18 0x911
+#define WM2200_EQL_19 0x912
+#define WM2200_EQL_20 0x913
+#define WM2200_EQR_1 0x916
+#define WM2200_EQR_2 0x917
+#define WM2200_EQR_3 0x918
+#define WM2200_EQR_4 0x919
+#define WM2200_EQR_5 0x91A
+#define WM2200_EQR_6 0x91B
+#define WM2200_EQR_7 0x91C
+#define WM2200_EQR_8 0x91D
+#define WM2200_EQR_9 0x91E
+#define WM2200_EQR_10 0x91F
+#define WM2200_EQR_11 0x920
+#define WM2200_EQR_12 0x921
+#define WM2200_EQR_13 0x922
+#define WM2200_EQR_14 0x923
+#define WM2200_EQR_15 0x924
+#define WM2200_EQR_16 0x925
+#define WM2200_EQR_17 0x926
+#define WM2200_EQR_18 0x927
+#define WM2200_EQR_19 0x928
+#define WM2200_EQR_20 0x929
+#define WM2200_HPLPF1_1 0x93E
+#define WM2200_HPLPF1_2 0x93F
+#define WM2200_HPLPF2_1 0x942
+#define WM2200_HPLPF2_2 0x943
+#define WM2200_DSP1_CONTROL_1 0xA00
+#define WM2200_DSP1_CONTROL_2 0xA02
+#define WM2200_DSP1_CONTROL_3 0xA03
+#define WM2200_DSP1_CONTROL_4 0xA04
+#define WM2200_DSP1_CONTROL_5 0xA06
+#define WM2200_DSP1_CONTROL_6 0xA07
+#define WM2200_DSP1_CONTROL_7 0xA08
+#define WM2200_DSP1_CONTROL_8 0xA09
+#define WM2200_DSP1_CONTROL_9 0xA0A
+#define WM2200_DSP1_CONTROL_10 0xA0B
+#define WM2200_DSP1_CONTROL_11 0xA0C
+#define WM2200_DSP1_CONTROL_12 0xA0D
+#define WM2200_DSP1_CONTROL_13 0xA0F
+#define WM2200_DSP1_CONTROL_14 0xA10
+#define WM2200_DSP1_CONTROL_15 0xA11
+#define WM2200_DSP1_CONTROL_16 0xA12
+#define WM2200_DSP1_CONTROL_17 0xA13
+#define WM2200_DSP1_CONTROL_18 0xA14
+#define WM2200_DSP1_CONTROL_19 0xA16
+#define WM2200_DSP1_CONTROL_20 0xA17
+#define WM2200_DSP1_CONTROL_21 0xA18
+#define WM2200_DSP1_CONTROL_22 0xA1A
+#define WM2200_DSP1_CONTROL_23 0xA1B
+#define WM2200_DSP1_CONTROL_24 0xA1C
+#define WM2200_DSP1_CONTROL_25 0xA1E
+#define WM2200_DSP1_CONTROL_26 0xA20
+#define WM2200_DSP1_CONTROL_27 0xA21
+#define WM2200_DSP1_CONTROL_28 0xA22
+#define WM2200_DSP1_CONTROL_29 0xA23
+#define WM2200_DSP1_CONTROL_30 0xA24
+#define WM2200_DSP1_CONTROL_31 0xA26
+#define WM2200_DSP2_CONTROL_1 0xB00
+#define WM2200_DSP2_CONTROL_2 0xB02
+#define WM2200_DSP2_CONTROL_3 0xB03
+#define WM2200_DSP2_CONTROL_4 0xB04
+#define WM2200_DSP2_CONTROL_5 0xB06
+#define WM2200_DSP2_CONTROL_6 0xB07
+#define WM2200_DSP2_CONTROL_7 0xB08
+#define WM2200_DSP2_CONTROL_8 0xB09
+#define WM2200_DSP2_CONTROL_9 0xB0A
+#define WM2200_DSP2_CONTROL_10 0xB0B
+#define WM2200_DSP2_CONTROL_11 0xB0C
+#define WM2200_DSP2_CONTROL_12 0xB0D
+#define WM2200_DSP2_CONTROL_13 0xB0F
+#define WM2200_DSP2_CONTROL_14 0xB10
+#define WM2200_DSP2_CONTROL_15 0xB11
+#define WM2200_DSP2_CONTROL_16 0xB12
+#define WM2200_DSP2_CONTROL_17 0xB13
+#define WM2200_DSP2_CONTROL_18 0xB14
+#define WM2200_DSP2_CONTROL_19 0xB16
+#define WM2200_DSP2_CONTROL_20 0xB17
+#define WM2200_DSP2_CONTROL_21 0xB18
+#define WM2200_DSP2_CONTROL_22 0xB1A
+#define WM2200_DSP2_CONTROL_23 0xB1B
+#define WM2200_DSP2_CONTROL_24 0xB1C
+#define WM2200_DSP2_CONTROL_25 0xB1E
+#define WM2200_DSP2_CONTROL_26 0xB20
+#define WM2200_DSP2_CONTROL_27 0xB21
+#define WM2200_DSP2_CONTROL_28 0xB22
+#define WM2200_DSP2_CONTROL_29 0xB23
+#define WM2200_DSP2_CONTROL_30 0xB24
+#define WM2200_DSP2_CONTROL_31 0xB26
+#define WM2200_ANC_CTRL1 0xD00
+#define WM2200_ANC_CTRL2 0xD01
+#define WM2200_ANC_CTRL3 0xD02
+#define WM2200_ANC_CTRL7 0xD08
+#define WM2200_ANC_CTRL8 0xD09
+#define WM2200_ANC_CTRL9 0xD0A
+#define WM2200_ANC_CTRL10 0xD0B
+#define WM2200_ANC_CTRL11 0xD0C
+#define WM2200_ANC_CTRL12 0xD0D
+#define WM2200_ANC_CTRL13 0xD0E
+#define WM2200_ANC_CTRL14 0xD0F
+#define WM2200_ANC_CTRL15 0xD10
+#define WM2200_ANC_CTRL16 0xD11
+#define WM2200_ANC_CTRL17 0xD12
+#define WM2200_ANC_CTRL18 0xD15
+#define WM2200_ANC_CTRL19 0xD16
+#define WM2200_ANC_CTRL20 0xD17
+#define WM2200_ANC_CTRL21 0xD18
+#define WM2200_ANC_CTRL22 0xD19
+#define WM2200_ANC_CTRL23 0xD1A
+#define WM2200_ANC_CTRL24 0xD1B
+#define WM2200_ANC_CTRL25 0xD1C
+#define WM2200_ANC_CTRL26 0xD1D
+#define WM2200_ANC_CTRL27 0xD1E
+#define WM2200_ANC_CTRL28 0xD1F
+#define WM2200_ANC_CTRL29 0xD20
+#define WM2200_ANC_CTRL30 0xD21
+#define WM2200_ANC_CTRL31 0xD23
+#define WM2200_ANC_CTRL32 0xD24
+#define WM2200_ANC_CTRL33 0xD25
+#define WM2200_ANC_CTRL34 0xD27
+#define WM2200_ANC_CTRL35 0xD28
+#define WM2200_ANC_CTRL36 0xD29
+#define WM2200_ANC_CTRL37 0xD2A
+#define WM2200_ANC_CTRL38 0xD2B
+#define WM2200_ANC_CTRL39 0xD2C
+#define WM2200_ANC_CTRL40 0xD2D
+#define WM2200_ANC_CTRL41 0xD2E
+#define WM2200_ANC_CTRL42 0xD2F
+#define WM2200_ANC_CTRL43 0xD30
+#define WM2200_ANC_CTRL44 0xD31
+#define WM2200_ANC_CTRL45 0xD32
+#define WM2200_ANC_CTRL46 0xD33
+#define WM2200_ANC_CTRL47 0xD34
+#define WM2200_ANC_CTRL48 0xD35
+#define WM2200_ANC_CTRL49 0xD36
+#define WM2200_ANC_CTRL50 0xD37
+#define WM2200_ANC_CTRL51 0xD38
+#define WM2200_ANC_CTRL52 0xD39
+#define WM2200_ANC_CTRL53 0xD3A
+#define WM2200_ANC_CTRL54 0xD3B
+#define WM2200_ANC_CTRL55 0xD3C
+#define WM2200_ANC_CTRL56 0xD3D
+#define WM2200_ANC_CTRL57 0xD3E
+#define WM2200_ANC_CTRL58 0xD3F
+#define WM2200_ANC_CTRL59 0xD40
+#define WM2200_ANC_CTRL60 0xD41
+#define WM2200_ANC_CTRL61 0xD42
+#define WM2200_ANC_CTRL62 0xD43
+#define WM2200_ANC_CTRL63 0xD44
+#define WM2200_ANC_CTRL64 0xD45
+#define WM2200_ANC_CTRL65 0xD46
+#define WM2200_ANC_CTRL66 0xD47
+#define WM2200_ANC_CTRL67 0xD48
+#define WM2200_ANC_CTRL68 0xD49
+#define WM2200_ANC_CTRL69 0xD4A
+#define WM2200_ANC_CTRL70 0xD4B
+#define WM2200_ANC_CTRL71 0xD4C
+#define WM2200_ANC_CTRL72 0xD4D
+#define WM2200_ANC_CTRL73 0xD4E
+#define WM2200_ANC_CTRL74 0xD4F
+#define WM2200_ANC_CTRL75 0xD50
+#define WM2200_ANC_CTRL76 0xD51
+#define WM2200_ANC_CTRL77 0xD52
+#define WM2200_ANC_CTRL78 0xD53
+#define WM2200_ANC_CTRL79 0xD54
+#define WM2200_ANC_CTRL80 0xD55
+#define WM2200_ANC_CTRL81 0xD56
+#define WM2200_ANC_CTRL82 0xD57
+#define WM2200_ANC_CTRL83 0xD58
+#define WM2200_ANC_CTRL84 0xD5B
+#define WM2200_ANC_CTRL85 0xD5C
+#define WM2200_ANC_CTRL86 0xD5F
+#define WM2200_ANC_CTRL87 0xD60
+#define WM2200_ANC_CTRL88 0xD61
+#define WM2200_ANC_CTRL89 0xD62
+#define WM2200_ANC_CTRL90 0xD63
+#define WM2200_ANC_CTRL91 0xD64
+#define WM2200_ANC_CTRL92 0xD65
+#define WM2200_ANC_CTRL93 0xD66
+#define WM2200_ANC_CTRL94 0xD67
+#define WM2200_ANC_CTRL95 0xD68
+#define WM2200_ANC_CTRL96 0xD69
+#define WM2200_DSP1_DM_0 0x3000
+#define WM2200_DSP1_DM_1 0x3001
+#define WM2200_DSP1_DM_2 0x3002
+#define WM2200_DSP1_DM_3 0x3003
+#define WM2200_DSP1_DM_2044 0x37FC
+#define WM2200_DSP1_DM_2045 0x37FD
+#define WM2200_DSP1_DM_2046 0x37FE
+#define WM2200_DSP1_DM_2047 0x37FF
+#define WM2200_DSP1_PM_0 0x3800
+#define WM2200_DSP1_PM_1 0x3801
+#define WM2200_DSP1_PM_2 0x3802
+#define WM2200_DSP1_PM_3 0x3803
+#define WM2200_DSP1_PM_4 0x3804
+#define WM2200_DSP1_PM_5 0x3805
+#define WM2200_DSP1_PM_762 0x3AFA
+#define WM2200_DSP1_PM_763 0x3AFB
+#define WM2200_DSP1_PM_764 0x3AFC
+#define WM2200_DSP1_PM_765 0x3AFD
+#define WM2200_DSP1_PM_766 0x3AFE
+#define WM2200_DSP1_PM_767 0x3AFF
+#define WM2200_DSP1_ZM_0 0x3C00
+#define WM2200_DSP1_ZM_1 0x3C01
+#define WM2200_DSP1_ZM_2 0x3C02
+#define WM2200_DSP1_ZM_3 0x3C03
+#define WM2200_DSP1_ZM_1020 0x3FFC
+#define WM2200_DSP1_ZM_1021 0x3FFD
+#define WM2200_DSP1_ZM_1022 0x3FFE
+#define WM2200_DSP1_ZM_1023 0x3FFF
+#define WM2200_DSP2_DM_0 0x4000
+#define WM2200_DSP2_DM_1 0x4001
+#define WM2200_DSP2_DM_2 0x4002
+#define WM2200_DSP2_DM_3 0x4003
+#define WM2200_DSP2_DM_2044 0x47FC
+#define WM2200_DSP2_DM_2045 0x47FD
+#define WM2200_DSP2_DM_2046 0x47FE
+#define WM2200_DSP2_DM_2047 0x47FF
+#define WM2200_DSP2_PM_0 0x4800
+#define WM2200_DSP2_PM_1 0x4801
+#define WM2200_DSP2_PM_2 0x4802
+#define WM2200_DSP2_PM_3 0x4803
+#define WM2200_DSP2_PM_4 0x4804
+#define WM2200_DSP2_PM_5 0x4805
+#define WM2200_DSP2_PM_762 0x4AFA
+#define WM2200_DSP2_PM_763 0x4AFB
+#define WM2200_DSP2_PM_764 0x4AFC
+#define WM2200_DSP2_PM_765 0x4AFD
+#define WM2200_DSP2_PM_766 0x4AFE
+#define WM2200_DSP2_PM_767 0x4AFF
+#define WM2200_DSP2_ZM_0 0x4C00
+#define WM2200_DSP2_ZM_1 0x4C01
+#define WM2200_DSP2_ZM_2 0x4C02
+#define WM2200_DSP2_ZM_3 0x4C03
+#define WM2200_DSP2_ZM_1020 0x4FFC
+#define WM2200_DSP2_ZM_1021 0x4FFD
+#define WM2200_DSP2_ZM_1022 0x4FFE
+#define WM2200_DSP2_ZM_1023 0x4FFF
+
+#define WM2200_REGISTER_COUNT 494
+#define WM2200_MAX_REGISTER 0x4FFF
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - software reset
+ */
+#define WM2200_SW_RESET_CHIP_ID1_MASK 0xFFFF /* SW_RESET_CHIP_ID1 - [15:0] */
+#define WM2200_SW_RESET_CHIP_ID1_SHIFT 0 /* SW_RESET_CHIP_ID1 - [15:0] */
+#define WM2200_SW_RESET_CHIP_ID1_WIDTH 16 /* SW_RESET_CHIP_ID1 - [15:0] */
+
+/*
+ * R1 (0x01) - Device Revision
+ */
+#define WM2200_DEVICE_REVISION_MASK 0x000F /* DEVICE_REVISION - [3:0] */
+#define WM2200_DEVICE_REVISION_SHIFT 0 /* DEVICE_REVISION - [3:0] */
+#define WM2200_DEVICE_REVISION_WIDTH 4 /* DEVICE_REVISION - [3:0] */
+
+/*
+ * R11 (0x0B) - Tone Generator 1
+ */
+#define WM2200_TONE_ENA 0x0001 /* TONE_ENA */
+#define WM2200_TONE_ENA_MASK 0x0001 /* TONE_ENA */
+#define WM2200_TONE_ENA_SHIFT 0 /* TONE_ENA */
+#define WM2200_TONE_ENA_WIDTH 1 /* TONE_ENA */
+
+/*
+ * R258 (0x102) - Clocking 3
+ */
+#define WM2200_SYSCLK_FREQ_MASK 0x0700 /* SYSCLK_FREQ - [10:8] */
+#define WM2200_SYSCLK_FREQ_SHIFT 8 /* SYSCLK_FREQ - [10:8] */
+#define WM2200_SYSCLK_FREQ_WIDTH 3 /* SYSCLK_FREQ - [10:8] */
+#define WM2200_SYSCLK_ENA 0x0040 /* SYSCLK_ENA */
+#define WM2200_SYSCLK_ENA_MASK 0x0040 /* SYSCLK_ENA */
+#define WM2200_SYSCLK_ENA_SHIFT 6 /* SYSCLK_ENA */
+#define WM2200_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */
+#define WM2200_SYSCLK_SRC_MASK 0x000F /* SYSCLK_SRC - [3:0] */
+#define WM2200_SYSCLK_SRC_SHIFT 0 /* SYSCLK_SRC - [3:0] */
+#define WM2200_SYSCLK_SRC_WIDTH 4 /* SYSCLK_SRC - [3:0] */
+
+/*
+ * R259 (0x103) - Clocking 4
+ */
+#define WM2200_SAMPLE_RATE_1_MASK 0x001F /* SAMPLE_RATE_1 - [4:0] */
+#define WM2200_SAMPLE_RATE_1_SHIFT 0 /* SAMPLE_RATE_1 - [4:0] */
+#define WM2200_SAMPLE_RATE_1_WIDTH 5 /* SAMPLE_RATE_1 - [4:0] */
+
+/*
+ * R273 (0x111) - FLL Control 1
+ */
+#define WM2200_FLL_ENA 0x0001 /* FLL_ENA */
+#define WM2200_FLL_ENA_MASK 0x0001 /* FLL_ENA */
+#define WM2200_FLL_ENA_SHIFT 0 /* FLL_ENA */
+#define WM2200_FLL_ENA_WIDTH 1 /* FLL_ENA */
+
+/*
+ * R274 (0x112) - FLL Control 2
+ */
+#define WM2200_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */
+#define WM2200_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */
+#define WM2200_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */
+#define WM2200_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
+#define WM2200_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
+#define WM2200_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
+
+/*
+ * R275 (0x113) - FLL Control 3
+ */
+#define WM2200_FLL_FRACN_ENA 0x0001 /* FLL_FRACN_ENA */
+#define WM2200_FLL_FRACN_ENA_MASK 0x0001 /* FLL_FRACN_ENA */
+#define WM2200_FLL_FRACN_ENA_SHIFT 0 /* FLL_FRACN_ENA */
+#define WM2200_FLL_FRACN_ENA_WIDTH 1 /* FLL_FRACN_ENA */
+
+/*
+ * R276 (0x114) - FLL Control 4
+ */
+#define WM2200_FLL_THETA_MASK 0xFFFF /* FLL_THETA - [15:0] */
+#define WM2200_FLL_THETA_SHIFT 0 /* FLL_THETA - [15:0] */
+#define WM2200_FLL_THETA_WIDTH 16 /* FLL_THETA - [15:0] */
+
+/*
+ * R278 (0x116) - FLL Control 6
+ */
+#define WM2200_FLL_N_MASK 0x03FF /* FLL_N - [9:0] */
+#define WM2200_FLL_N_SHIFT 0 /* FLL_N - [9:0] */
+#define WM2200_FLL_N_WIDTH 10 /* FLL_N - [9:0] */
+
+/*
+ * R279 (0x117) - FLL Control 7
+ */
+#define WM2200_FLL_CLK_REF_DIV_MASK 0x0030 /* FLL_CLK_REF_DIV - [5:4] */
+#define WM2200_FLL_CLK_REF_DIV_SHIFT 4 /* FLL_CLK_REF_DIV - [5:4] */
+#define WM2200_FLL_CLK_REF_DIV_WIDTH 2 /* FLL_CLK_REF_DIV - [5:4] */
+#define WM2200_FLL_CLK_REF_SRC_MASK 0x0003 /* FLL_CLK_REF_SRC - [1:0] */
+#define WM2200_FLL_CLK_REF_SRC_SHIFT 0 /* FLL_CLK_REF_SRC - [1:0] */
+#define WM2200_FLL_CLK_REF_SRC_WIDTH 2 /* FLL_CLK_REF_SRC - [1:0] */
+
+/*
+ * R281 (0x119) - FLL EFS 1
+ */
+#define WM2200_FLL_LAMBDA_MASK 0xFFFF /* FLL_LAMBDA - [15:0] */
+#define WM2200_FLL_LAMBDA_SHIFT 0 /* FLL_LAMBDA - [15:0] */
+#define WM2200_FLL_LAMBDA_WIDTH 16 /* FLL_LAMBDA - [15:0] */
+
+/*
+ * R282 (0x11A) - FLL EFS 2
+ */
+#define WM2200_FLL_EFS_ENA 0x0001 /* FLL_EFS_ENA */
+#define WM2200_FLL_EFS_ENA_MASK 0x0001 /* FLL_EFS_ENA */
+#define WM2200_FLL_EFS_ENA_SHIFT 0 /* FLL_EFS_ENA */
+#define WM2200_FLL_EFS_ENA_WIDTH 1 /* FLL_EFS_ENA */
+
+/*
+ * R512 (0x200) - Mic Charge Pump 1
+ */
+#define WM2200_CPMIC_BYPASS_MODE 0x0020 /* CPMIC_BYPASS_MODE */
+#define WM2200_CPMIC_BYPASS_MODE_MASK 0x0020 /* CPMIC_BYPASS_MODE */
+#define WM2200_CPMIC_BYPASS_MODE_SHIFT 5 /* CPMIC_BYPASS_MODE */
+#define WM2200_CPMIC_BYPASS_MODE_WIDTH 1 /* CPMIC_BYPASS_MODE */
+#define WM2200_CPMIC_ENA 0x0001 /* CPMIC_ENA */
+#define WM2200_CPMIC_ENA_MASK 0x0001 /* CPMIC_ENA */
+#define WM2200_CPMIC_ENA_SHIFT 0 /* CPMIC_ENA */
+#define WM2200_CPMIC_ENA_WIDTH 1 /* CPMIC_ENA */
+
+/*
+ * R513 (0x201) - Mic Charge Pump 2
+ */
+#define WM2200_CPMIC_LDO_VSEL_OVERRIDE_MASK 0xF800 /* CPMIC_LDO_VSEL_OVERRIDE - [15:11] */
+#define WM2200_CPMIC_LDO_VSEL_OVERRIDE_SHIFT 11 /* CPMIC_LDO_VSEL_OVERRIDE - [15:11] */
+#define WM2200_CPMIC_LDO_VSEL_OVERRIDE_WIDTH 5 /* CPMIC_LDO_VSEL_OVERRIDE - [15:11] */
+
+/*
+ * R514 (0x202) - DM Charge Pump 1
+ */
+#define WM2200_CPDM_ENA 0x0001 /* CPDM_ENA */
+#define WM2200_CPDM_ENA_MASK 0x0001 /* CPDM_ENA */
+#define WM2200_CPDM_ENA_SHIFT 0 /* CPDM_ENA */
+#define WM2200_CPDM_ENA_WIDTH 1 /* CPDM_ENA */
+
+/*
+ * R524 (0x20C) - Mic Bias Ctrl 1
+ */
+#define WM2200_MICB1_DISCH 0x0040 /* MICB1_DISCH */
+#define WM2200_MICB1_DISCH_MASK 0x0040 /* MICB1_DISCH */
+#define WM2200_MICB1_DISCH_SHIFT 6 /* MICB1_DISCH */
+#define WM2200_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */
+#define WM2200_MICB1_RATE 0x0020 /* MICB1_RATE */
+#define WM2200_MICB1_RATE_MASK 0x0020 /* MICB1_RATE */
+#define WM2200_MICB1_RATE_SHIFT 5 /* MICB1_RATE */
+#define WM2200_MICB1_RATE_WIDTH 1 /* MICB1_RATE */
+#define WM2200_MICB1_LVL_MASK 0x001C /* MICB1_LVL - [4:2] */
+#define WM2200_MICB1_LVL_SHIFT 2 /* MICB1_LVL - [4:2] */
+#define WM2200_MICB1_LVL_WIDTH 3 /* MICB1_LVL - [4:2] */
+#define WM2200_MICB1_MODE 0x0002 /* MICB1_MODE */
+#define WM2200_MICB1_MODE_MASK 0x0002 /* MICB1_MODE */
+#define WM2200_MICB1_MODE_SHIFT 1 /* MICB1_MODE */
+#define WM2200_MICB1_MODE_WIDTH 1 /* MICB1_MODE */
+#define WM2200_MICB1_ENA 0x0001 /* MICB1_ENA */
+#define WM2200_MICB1_ENA_MASK 0x0001 /* MICB1_ENA */
+#define WM2200_MICB1_ENA_SHIFT 0 /* MICB1_ENA */
+#define WM2200_MICB1_ENA_WIDTH 1 /* MICB1_ENA */
+
+/*
+ * R525 (0x20D) - Mic Bias Ctrl 2
+ */
+#define WM2200_MICB2_DISCH 0x0040 /* MICB2_DISCH */
+#define WM2200_MICB2_DISCH_MASK 0x0040 /* MICB2_DISCH */
+#define WM2200_MICB2_DISCH_SHIFT 6 /* MICB2_DISCH */
+#define WM2200_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */
+#define WM2200_MICB2_RATE 0x0020 /* MICB2_RATE */
+#define WM2200_MICB2_RATE_MASK 0x0020 /* MICB2_RATE */
+#define WM2200_MICB2_RATE_SHIFT 5 /* MICB2_RATE */
+#define WM2200_MICB2_RATE_WIDTH 1 /* MICB2_RATE */
+#define WM2200_MICB2_LVL_MASK 0x001C /* MICB2_LVL - [4:2] */
+#define WM2200_MICB2_LVL_SHIFT 2 /* MICB2_LVL - [4:2] */
+#define WM2200_MICB2_LVL_WIDTH 3 /* MICB2_LVL - [4:2] */
+#define WM2200_MICB2_MODE 0x0002 /* MICB2_MODE */
+#define WM2200_MICB2_MODE_MASK 0x0002 /* MICB2_MODE */
+#define WM2200_MICB2_MODE_SHIFT 1 /* MICB2_MODE */
+#define WM2200_MICB2_MODE_WIDTH 1 /* MICB2_MODE */
+#define WM2200_MICB2_ENA 0x0001 /* MICB2_ENA */
+#define WM2200_MICB2_ENA_MASK 0x0001 /* MICB2_ENA */
+#define WM2200_MICB2_ENA_SHIFT 0 /* MICB2_ENA */
+#define WM2200_MICB2_ENA_WIDTH 1 /* MICB2_ENA */
+
+/*
+ * R527 (0x20F) - Ear Piece Ctrl 1
+ */
+#define WM2200_EPD_LP_ENA 0x4000 /* EPD_LP_ENA */
+#define WM2200_EPD_LP_ENA_MASK 0x4000 /* EPD_LP_ENA */
+#define WM2200_EPD_LP_ENA_SHIFT 14 /* EPD_LP_ENA */
+#define WM2200_EPD_LP_ENA_WIDTH 1 /* EPD_LP_ENA */
+#define WM2200_EPD_OUTP_LP_ENA 0x2000 /* EPD_OUTP_LP_ENA */
+#define WM2200_EPD_OUTP_LP_ENA_MASK 0x2000 /* EPD_OUTP_LP_ENA */
+#define WM2200_EPD_OUTP_LP_ENA_SHIFT 13 /* EPD_OUTP_LP_ENA */
+#define WM2200_EPD_OUTP_LP_ENA_WIDTH 1 /* EPD_OUTP_LP_ENA */
+#define WM2200_EPD_RMV_SHRT_LP 0x1000 /* EPD_RMV_SHRT_LP */
+#define WM2200_EPD_RMV_SHRT_LP_MASK 0x1000 /* EPD_RMV_SHRT_LP */
+#define WM2200_EPD_RMV_SHRT_LP_SHIFT 12 /* EPD_RMV_SHRT_LP */
+#define WM2200_EPD_RMV_SHRT_LP_WIDTH 1 /* EPD_RMV_SHRT_LP */
+#define WM2200_EPD_LN_ENA 0x0800 /* EPD_LN_ENA */
+#define WM2200_EPD_LN_ENA_MASK 0x0800 /* EPD_LN_ENA */
+#define WM2200_EPD_LN_ENA_SHIFT 11 /* EPD_LN_ENA */
+#define WM2200_EPD_LN_ENA_WIDTH 1 /* EPD_LN_ENA */
+#define WM2200_EPD_OUTP_LN_ENA 0x0400 /* EPD_OUTP_LN_ENA */
+#define WM2200_EPD_OUTP_LN_ENA_MASK 0x0400 /* EPD_OUTP_LN_ENA */
+#define WM2200_EPD_OUTP_LN_ENA_SHIFT 10 /* EPD_OUTP_LN_ENA */
+#define WM2200_EPD_OUTP_LN_ENA_WIDTH 1 /* EPD_OUTP_LN_ENA */
+#define WM2200_EPD_RMV_SHRT_LN 0x0200 /* EPD_RMV_SHRT_LN */
+#define WM2200_EPD_RMV_SHRT_LN_MASK 0x0200 /* EPD_RMV_SHRT_LN */
+#define WM2200_EPD_RMV_SHRT_LN_SHIFT 9 /* EPD_RMV_SHRT_LN */
+#define WM2200_EPD_RMV_SHRT_LN_WIDTH 1 /* EPD_RMV_SHRT_LN */
+
+/*
+ * R528 (0x210) - Ear Piece Ctrl 2
+ */
+#define WM2200_EPD_RP_ENA 0x4000 /* EPD_RP_ENA */
+#define WM2200_EPD_RP_ENA_MASK 0x4000 /* EPD_RP_ENA */
+#define WM2200_EPD_RP_ENA_SHIFT 14 /* EPD_RP_ENA */
+#define WM2200_EPD_RP_ENA_WIDTH 1 /* EPD_RP_ENA */
+#define WM2200_EPD_OUTP_RP_ENA 0x2000 /* EPD_OUTP_RP_ENA */
+#define WM2200_EPD_OUTP_RP_ENA_MASK 0x2000 /* EPD_OUTP_RP_ENA */
+#define WM2200_EPD_OUTP_RP_ENA_SHIFT 13 /* EPD_OUTP_RP_ENA */
+#define WM2200_EPD_OUTP_RP_ENA_WIDTH 1 /* EPD_OUTP_RP_ENA */
+#define WM2200_EPD_RMV_SHRT_RP 0x1000 /* EPD_RMV_SHRT_RP */
+#define WM2200_EPD_RMV_SHRT_RP_MASK 0x1000 /* EPD_RMV_SHRT_RP */
+#define WM2200_EPD_RMV_SHRT_RP_SHIFT 12 /* EPD_RMV_SHRT_RP */
+#define WM2200_EPD_RMV_SHRT_RP_WIDTH 1 /* EPD_RMV_SHRT_RP */
+#define WM2200_EPD_RN_ENA 0x0800 /* EPD_RN_ENA */
+#define WM2200_EPD_RN_ENA_MASK 0x0800 /* EPD_RN_ENA */
+#define WM2200_EPD_RN_ENA_SHIFT 11 /* EPD_RN_ENA */
+#define WM2200_EPD_RN_ENA_WIDTH 1 /* EPD_RN_ENA */
+#define WM2200_EPD_OUTP_RN_ENA 0x0400 /* EPD_OUTP_RN_ENA */
+#define WM2200_EPD_OUTP_RN_ENA_MASK 0x0400 /* EPD_OUTP_RN_ENA */
+#define WM2200_EPD_OUTP_RN_ENA_SHIFT 10 /* EPD_OUTP_RN_ENA */
+#define WM2200_EPD_OUTP_RN_ENA_WIDTH 1 /* EPD_OUTP_RN_ENA */
+#define WM2200_EPD_RMV_SHRT_RN 0x0200 /* EPD_RMV_SHRT_RN */
+#define WM2200_EPD_RMV_SHRT_RN_MASK 0x0200 /* EPD_RMV_SHRT_RN */
+#define WM2200_EPD_RMV_SHRT_RN_SHIFT 9 /* EPD_RMV_SHRT_RN */
+#define WM2200_EPD_RMV_SHRT_RN_WIDTH 1 /* EPD_RMV_SHRT_RN */
+
+/*
+ * R769 (0x301) - Input Enables
+ */
+#define WM2200_IN3L_ENA 0x0020 /* IN3L_ENA */
+#define WM2200_IN3L_ENA_MASK 0x0020 /* IN3L_ENA */
+#define WM2200_IN3L_ENA_SHIFT 5 /* IN3L_ENA */
+#define WM2200_IN3L_ENA_WIDTH 1 /* IN3L_ENA */
+#define WM2200_IN3R_ENA 0x0010 /* IN3R_ENA */
+#define WM2200_IN3R_ENA_MASK 0x0010 /* IN3R_ENA */
+#define WM2200_IN3R_ENA_SHIFT 4 /* IN3R_ENA */
+#define WM2200_IN3R_ENA_WIDTH 1 /* IN3R_ENA */
+#define WM2200_IN2L_ENA 0x0008 /* IN2L_ENA */
+#define WM2200_IN2L_ENA_MASK 0x0008 /* IN2L_ENA */
+#define WM2200_IN2L_ENA_SHIFT 3 /* IN2L_ENA */
+#define WM2200_IN2L_ENA_WIDTH 1 /* IN2L_ENA */
+#define WM2200_IN2R_ENA 0x0004 /* IN2R_ENA */
+#define WM2200_IN2R_ENA_MASK 0x0004 /* IN2R_ENA */
+#define WM2200_IN2R_ENA_SHIFT 2 /* IN2R_ENA */
+#define WM2200_IN2R_ENA_WIDTH 1 /* IN2R_ENA */
+#define WM2200_IN1L_ENA 0x0002 /* IN1L_ENA */
+#define WM2200_IN1L_ENA_MASK 0x0002 /* IN1L_ENA */
+#define WM2200_IN1L_ENA_SHIFT 1 /* IN1L_ENA */
+#define WM2200_IN1L_ENA_WIDTH 1 /* IN1L_ENA */
+#define WM2200_IN1R_ENA 0x0001 /* IN1R_ENA */
+#define WM2200_IN1R_ENA_MASK 0x0001 /* IN1R_ENA */
+#define WM2200_IN1R_ENA_SHIFT 0 /* IN1R_ENA */
+#define WM2200_IN1R_ENA_WIDTH 1 /* IN1R_ENA */
+
+/*
+ * R770 (0x302) - IN1L Control
+ */
+#define WM2200_IN1_OSR 0x2000 /* IN1_OSR */
+#define WM2200_IN1_OSR_MASK 0x2000 /* IN1_OSR */
+#define WM2200_IN1_OSR_SHIFT 13 /* IN1_OSR */
+#define WM2200_IN1_OSR_WIDTH 1 /* IN1_OSR */
+#define WM2200_IN1_DMIC_SUP_MASK 0x1800 /* IN1_DMIC_SUP - [12:11] */
+#define WM2200_IN1_DMIC_SUP_SHIFT 11 /* IN1_DMIC_SUP - [12:11] */
+#define WM2200_IN1_DMIC_SUP_WIDTH 2 /* IN1_DMIC_SUP - [12:11] */
+#define WM2200_IN1_MODE_MASK 0x0600 /* IN1_MODE - [10:9] */
+#define WM2200_IN1_MODE_SHIFT 9 /* IN1_MODE - [10:9] */
+#define WM2200_IN1_MODE_WIDTH 2 /* IN1_MODE - [10:9] */
+#define WM2200_IN1L_PGA_VOL_MASK 0x00FE /* IN1L_PGA_VOL - [7:1] */
+#define WM2200_IN1L_PGA_VOL_SHIFT 1 /* IN1L_PGA_VOL - [7:1] */
+#define WM2200_IN1L_PGA_VOL_WIDTH 7 /* IN1L_PGA_VOL - [7:1] */
+
+/*
+ * R771 (0x303) - IN1R Control
+ */
+#define WM2200_IN1R_PGA_VOL_MASK 0x00FE /* IN1R_PGA_VOL - [7:1] */
+#define WM2200_IN1R_PGA_VOL_SHIFT 1 /* IN1R_PGA_VOL - [7:1] */
+#define WM2200_IN1R_PGA_VOL_WIDTH 7 /* IN1R_PGA_VOL - [7:1] */
+
+/*
+ * R772 (0x304) - IN2L Control
+ */
+#define WM2200_IN2_OSR 0x2000 /* IN2_OSR */
+#define WM2200_IN2_OSR_MASK 0x2000 /* IN2_OSR */
+#define WM2200_IN2_OSR_SHIFT 13 /* IN2_OSR */
+#define WM2200_IN2_OSR_WIDTH 1 /* IN2_OSR */
+#define WM2200_IN2_DMIC_SUP_MASK 0x1800 /* IN2_DMIC_SUP - [12:11] */
+#define WM2200_IN2_DMIC_SUP_SHIFT 11 /* IN2_DMIC_SUP - [12:11] */
+#define WM2200_IN2_DMIC_SUP_WIDTH 2 /* IN2_DMIC_SUP - [12:11] */
+#define WM2200_IN2_MODE_MASK 0x0600 /* IN2_MODE - [10:9] */
+#define WM2200_IN2_MODE_SHIFT 9 /* IN2_MODE - [10:9] */
+#define WM2200_IN2_MODE_WIDTH 2 /* IN2_MODE - [10:9] */
+#define WM2200_IN2L_PGA_VOL_MASK 0x00FE /* IN2L_PGA_VOL - [7:1] */
+#define WM2200_IN2L_PGA_VOL_SHIFT 1 /* IN2L_PGA_VOL - [7:1] */
+#define WM2200_IN2L_PGA_VOL_WIDTH 7 /* IN2L_PGA_VOL - [7:1] */
+
+/*
+ * R773 (0x305) - IN2R Control
+ */
+#define WM2200_IN2R_PGA_VOL_MASK 0x00FE /* IN2R_PGA_VOL - [7:1] */
+#define WM2200_IN2R_PGA_VOL_SHIFT 1 /* IN2R_PGA_VOL - [7:1] */
+#define WM2200_IN2R_PGA_VOL_WIDTH 7 /* IN2R_PGA_VOL - [7:1] */
+
+/*
+ * R774 (0x306) - IN3L Control
+ */
+#define WM2200_IN3_OSR 0x2000 /* IN3_OSR */
+#define WM2200_IN3_OSR_MASK 0x2000 /* IN3_OSR */
+#define WM2200_IN3_OSR_SHIFT 13 /* IN3_OSR */
+#define WM2200_IN3_OSR_WIDTH 1 /* IN3_OSR */
+#define WM2200_IN3_DMIC_SUP_MASK 0x1800 /* IN3_DMIC_SUP - [12:11] */
+#define WM2200_IN3_DMIC_SUP_SHIFT 11 /* IN3_DMIC_SUP - [12:11] */
+#define WM2200_IN3_DMIC_SUP_WIDTH 2 /* IN3_DMIC_SUP - [12:11] */
+#define WM2200_IN3_MODE_MASK 0x0600 /* IN3_MODE - [10:9] */
+#define WM2200_IN3_MODE_SHIFT 9 /* IN3_MODE - [10:9] */
+#define WM2200_IN3_MODE_WIDTH 2 /* IN3_MODE - [10:9] */
+#define WM2200_IN3L_PGA_VOL_MASK 0x00FE /* IN3L_PGA_VOL - [7:1] */
+#define WM2200_IN3L_PGA_VOL_SHIFT 1 /* IN3L_PGA_VOL - [7:1] */
+#define WM2200_IN3L_PGA_VOL_WIDTH 7 /* IN3L_PGA_VOL - [7:1] */
+
+/*
+ * R775 (0x307) - IN3R Control
+ */
+#define WM2200_IN3R_PGA_VOL_MASK 0x00FE /* IN3R_PGA_VOL - [7:1] */
+#define WM2200_IN3R_PGA_VOL_SHIFT 1 /* IN3R_PGA_VOL - [7:1] */
+#define WM2200_IN3R_PGA_VOL_WIDTH 7 /* IN3R_PGA_VOL - [7:1] */
+
+/*
+ * R778 (0x30A) - RXANC_SRC
+ */
+#define WM2200_IN_RXANC_SEL_MASK 0x0007 /* IN_RXANC_SEL - [2:0] */
+#define WM2200_IN_RXANC_SEL_SHIFT 0 /* IN_RXANC_SEL - [2:0] */
+#define WM2200_IN_RXANC_SEL_WIDTH 3 /* IN_RXANC_SEL - [2:0] */
+
+/*
+ * R779 (0x30B) - Input Volume Ramp
+ */
+#define WM2200_IN_VD_RAMP_MASK 0x0070 /* IN_VD_RAMP - [6:4] */
+#define WM2200_IN_VD_RAMP_SHIFT 4 /* IN_VD_RAMP - [6:4] */
+#define WM2200_IN_VD_RAMP_WIDTH 3 /* IN_VD_RAMP - [6:4] */
+#define WM2200_IN_VI_RAMP_MASK 0x0007 /* IN_VI_RAMP - [2:0] */
+#define WM2200_IN_VI_RAMP_SHIFT 0 /* IN_VI_RAMP - [2:0] */
+#define WM2200_IN_VI_RAMP_WIDTH 3 /* IN_VI_RAMP - [2:0] */
+
+/*
+ * R780 (0x30C) - ADC Digital Volume 1L
+ */
+#define WM2200_IN_VU 0x0200 /* IN_VU */
+#define WM2200_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM2200_IN_VU_SHIFT 9 /* IN_VU */
+#define WM2200_IN_VU_WIDTH 1 /* IN_VU */
+#define WM2200_IN1L_MUTE 0x0100 /* IN1L_MUTE */
+#define WM2200_IN1L_MUTE_MASK 0x0100 /* IN1L_MUTE */
+#define WM2200_IN1L_MUTE_SHIFT 8 /* IN1L_MUTE */
+#define WM2200_IN1L_MUTE_WIDTH 1 /* IN1L_MUTE */
+#define WM2200_IN1L_DIG_VOL_MASK 0x00FF /* IN1L_DIG_VOL - [7:0] */
+#define WM2200_IN1L_DIG_VOL_SHIFT 0 /* IN1L_DIG_VOL - [7:0] */
+#define WM2200_IN1L_DIG_VOL_WIDTH 8 /* IN1L_DIG_VOL - [7:0] */
+
+/*
+ * R781 (0x30D) - ADC Digital Volume 1R
+ */
+#define WM2200_IN_VU 0x0200 /* IN_VU */
+#define WM2200_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM2200_IN_VU_SHIFT 9 /* IN_VU */
+#define WM2200_IN_VU_WIDTH 1 /* IN_VU */
+#define WM2200_IN1R_MUTE 0x0100 /* IN1R_MUTE */
+#define WM2200_IN1R_MUTE_MASK 0x0100 /* IN1R_MUTE */
+#define WM2200_IN1R_MUTE_SHIFT 8 /* IN1R_MUTE */
+#define WM2200_IN1R_MUTE_WIDTH 1 /* IN1R_MUTE */
+#define WM2200_IN1R_DIG_VOL_MASK 0x00FF /* IN1R_DIG_VOL - [7:0] */
+#define WM2200_IN1R_DIG_VOL_SHIFT 0 /* IN1R_DIG_VOL - [7:0] */
+#define WM2200_IN1R_DIG_VOL_WIDTH 8 /* IN1R_DIG_VOL - [7:0] */
+
+/*
+ * R782 (0x30E) - ADC Digital Volume 2L
+ */
+#define WM2200_IN_VU 0x0200 /* IN_VU */
+#define WM2200_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM2200_IN_VU_SHIFT 9 /* IN_VU */
+#define WM2200_IN_VU_WIDTH 1 /* IN_VU */
+#define WM2200_IN2L_MUTE 0x0100 /* IN2L_MUTE */
+#define WM2200_IN2L_MUTE_MASK 0x0100 /* IN2L_MUTE */
+#define WM2200_IN2L_MUTE_SHIFT 8 /* IN2L_MUTE */
+#define WM2200_IN2L_MUTE_WIDTH 1 /* IN2L_MUTE */
+#define WM2200_IN2L_DIG_VOL_MASK 0x00FF /* IN2L_DIG_VOL - [7:0] */
+#define WM2200_IN2L_DIG_VOL_SHIFT 0 /* IN2L_DIG_VOL - [7:0] */
+#define WM2200_IN2L_DIG_VOL_WIDTH 8 /* IN2L_DIG_VOL - [7:0] */
+
+/*
+ * R783 (0x30F) - ADC Digital Volume 2R
+ */
+#define WM2200_IN_VU 0x0200 /* IN_VU */
+#define WM2200_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM2200_IN_VU_SHIFT 9 /* IN_VU */
+#define WM2200_IN_VU_WIDTH 1 /* IN_VU */
+#define WM2200_IN2R_MUTE 0x0100 /* IN2R_MUTE */
+#define WM2200_IN2R_MUTE_MASK 0x0100 /* IN2R_MUTE */
+#define WM2200_IN2R_MUTE_SHIFT 8 /* IN2R_MUTE */
+#define WM2200_IN2R_MUTE_WIDTH 1 /* IN2R_MUTE */
+#define WM2200_IN2R_DIG_VOL_MASK 0x00FF /* IN2R_DIG_VOL - [7:0] */
+#define WM2200_IN2R_DIG_VOL_SHIFT 0 /* IN2R_DIG_VOL - [7:0] */
+#define WM2200_IN2R_DIG_VOL_WIDTH 8 /* IN2R_DIG_VOL - [7:0] */
+
+/*
+ * R784 (0x310) - ADC Digital Volume 3L
+ */
+#define WM2200_IN_VU 0x0200 /* IN_VU */
+#define WM2200_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM2200_IN_VU_SHIFT 9 /* IN_VU */
+#define WM2200_IN_VU_WIDTH 1 /* IN_VU */
+#define WM2200_IN3L_MUTE 0x0100 /* IN3L_MUTE */
+#define WM2200_IN3L_MUTE_MASK 0x0100 /* IN3L_MUTE */
+#define WM2200_IN3L_MUTE_SHIFT 8 /* IN3L_MUTE */
+#define WM2200_IN3L_MUTE_WIDTH 1 /* IN3L_MUTE */
+#define WM2200_IN3L_DIG_VOL_MASK 0x00FF /* IN3L_DIG_VOL - [7:0] */
+#define WM2200_IN3L_DIG_VOL_SHIFT 0 /* IN3L_DIG_VOL - [7:0] */
+#define WM2200_IN3L_DIG_VOL_WIDTH 8 /* IN3L_DIG_VOL - [7:0] */
+
+/*
+ * R785 (0x311) - ADC Digital Volume 3R
+ */
+#define WM2200_IN_VU 0x0200 /* IN_VU */
+#define WM2200_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM2200_IN_VU_SHIFT 9 /* IN_VU */
+#define WM2200_IN_VU_WIDTH 1 /* IN_VU */
+#define WM2200_IN3R_MUTE 0x0100 /* IN3R_MUTE */
+#define WM2200_IN3R_MUTE_MASK 0x0100 /* IN3R_MUTE */
+#define WM2200_IN3R_MUTE_SHIFT 8 /* IN3R_MUTE */
+#define WM2200_IN3R_MUTE_WIDTH 1 /* IN3R_MUTE */
+#define WM2200_IN3R_DIG_VOL_MASK 0x00FF /* IN3R_DIG_VOL - [7:0] */
+#define WM2200_IN3R_DIG_VOL_SHIFT 0 /* IN3R_DIG_VOL - [7:0] */
+#define WM2200_IN3R_DIG_VOL_WIDTH 8 /* IN3R_DIG_VOL - [7:0] */
+
+/*
+ * R1024 (0x400) - Output Enables
+ */
+#define WM2200_OUT2L_ENA 0x0008 /* OUT2L_ENA */
+#define WM2200_OUT2L_ENA_MASK 0x0008 /* OUT2L_ENA */
+#define WM2200_OUT2L_ENA_SHIFT 3 /* OUT2L_ENA */
+#define WM2200_OUT2L_ENA_WIDTH 1 /* OUT2L_ENA */
+#define WM2200_OUT2R_ENA 0x0004 /* OUT2R_ENA */
+#define WM2200_OUT2R_ENA_MASK 0x0004 /* OUT2R_ENA */
+#define WM2200_OUT2R_ENA_SHIFT 2 /* OUT2R_ENA */
+#define WM2200_OUT2R_ENA_WIDTH 1 /* OUT2R_ENA */
+#define WM2200_OUT1L_ENA 0x0002 /* OUT1L_ENA */
+#define WM2200_OUT1L_ENA_MASK 0x0002 /* OUT1L_ENA */
+#define WM2200_OUT1L_ENA_SHIFT 1 /* OUT1L_ENA */
+#define WM2200_OUT1L_ENA_WIDTH 1 /* OUT1L_ENA */
+#define WM2200_OUT1R_ENA 0x0001 /* OUT1R_ENA */
+#define WM2200_OUT1R_ENA_MASK 0x0001 /* OUT1R_ENA */
+#define WM2200_OUT1R_ENA_SHIFT 0 /* OUT1R_ENA */
+#define WM2200_OUT1R_ENA_WIDTH 1 /* OUT1R_ENA */
+
+/*
+ * R1025 (0x401) - DAC Volume Limit 1L
+ */
+#define WM2200_OUT1_OSR 0x2000 /* OUT1_OSR */
+#define WM2200_OUT1_OSR_MASK 0x2000 /* OUT1_OSR */
+#define WM2200_OUT1_OSR_SHIFT 13 /* OUT1_OSR */
+#define WM2200_OUT1_OSR_WIDTH 1 /* OUT1_OSR */
+#define WM2200_OUT1L_ANC_SRC 0x0800 /* OUT1L_ANC_SRC */
+#define WM2200_OUT1L_ANC_SRC_MASK 0x0800 /* OUT1L_ANC_SRC */
+#define WM2200_OUT1L_ANC_SRC_SHIFT 11 /* OUT1L_ANC_SRC */
+#define WM2200_OUT1L_ANC_SRC_WIDTH 1 /* OUT1L_ANC_SRC */
+#define WM2200_OUT1L_PGA_VOL_MASK 0x00FE /* OUT1L_PGA_VOL - [7:1] */
+#define WM2200_OUT1L_PGA_VOL_SHIFT 1 /* OUT1L_PGA_VOL - [7:1] */
+#define WM2200_OUT1L_PGA_VOL_WIDTH 7 /* OUT1L_PGA_VOL - [7:1] */
+
+/*
+ * R1026 (0x402) - DAC Volume Limit 1R
+ */
+#define WM2200_OUT1R_ANC_SRC 0x0800 /* OUT1R_ANC_SRC */
+#define WM2200_OUT1R_ANC_SRC_MASK 0x0800 /* OUT1R_ANC_SRC */
+#define WM2200_OUT1R_ANC_SRC_SHIFT 11 /* OUT1R_ANC_SRC */
+#define WM2200_OUT1R_ANC_SRC_WIDTH 1 /* OUT1R_ANC_SRC */
+#define WM2200_OUT1R_PGA_VOL_MASK 0x00FE /* OUT1R_PGA_VOL - [7:1] */
+#define WM2200_OUT1R_PGA_VOL_SHIFT 1 /* OUT1R_PGA_VOL - [7:1] */
+#define WM2200_OUT1R_PGA_VOL_WIDTH 7 /* OUT1R_PGA_VOL - [7:1] */
+
+/*
+ * R1027 (0x403) - DAC Volume Limit 2L
+ */
+#define WM2200_OUT2_OSR 0x2000 /* OUT2_OSR */
+#define WM2200_OUT2_OSR_MASK 0x2000 /* OUT2_OSR */
+#define WM2200_OUT2_OSR_SHIFT 13 /* OUT2_OSR */
+#define WM2200_OUT2_OSR_WIDTH 1 /* OUT2_OSR */
+#define WM2200_OUT2L_ANC_SRC 0x0800 /* OUT2L_ANC_SRC */
+#define WM2200_OUT2L_ANC_SRC_MASK 0x0800 /* OUT2L_ANC_SRC */
+#define WM2200_OUT2L_ANC_SRC_SHIFT 11 /* OUT2L_ANC_SRC */
+#define WM2200_OUT2L_ANC_SRC_WIDTH 1 /* OUT2L_ANC_SRC */
+
+/*
+ * R1028 (0x404) - DAC Volume Limit 2R
+ */
+#define WM2200_OUT2R_ANC_SRC 0x0800 /* OUT2R_ANC_SRC */
+#define WM2200_OUT2R_ANC_SRC_MASK 0x0800 /* OUT2R_ANC_SRC */
+#define WM2200_OUT2R_ANC_SRC_SHIFT 11 /* OUT2R_ANC_SRC */
+#define WM2200_OUT2R_ANC_SRC_WIDTH 1 /* OUT2R_ANC_SRC */
+
+/*
+ * R1033 (0x409) - DAC AEC Control 1
+ */
+#define WM2200_AEC_LOOPBACK_ENA 0x0004 /* AEC_LOOPBACK_ENA */
+#define WM2200_AEC_LOOPBACK_ENA_MASK 0x0004 /* AEC_LOOPBACK_ENA */
+#define WM2200_AEC_LOOPBACK_ENA_SHIFT 2 /* AEC_LOOPBACK_ENA */
+#define WM2200_AEC_LOOPBACK_ENA_WIDTH 1 /* AEC_LOOPBACK_ENA */
+#define WM2200_AEC_LOOPBACK_SRC_MASK 0x0003 /* AEC_LOOPBACK_SRC - [1:0] */
+#define WM2200_AEC_LOOPBACK_SRC_SHIFT 0 /* AEC_LOOPBACK_SRC - [1:0] */
+#define WM2200_AEC_LOOPBACK_SRC_WIDTH 2 /* AEC_LOOPBACK_SRC - [1:0] */
+
+/*
+ * R1034 (0x40A) - Output Volume Ramp
+ */
+#define WM2200_OUT_VD_RAMP_MASK 0x0070 /* OUT_VD_RAMP - [6:4] */
+#define WM2200_OUT_VD_RAMP_SHIFT 4 /* OUT_VD_RAMP - [6:4] */
+#define WM2200_OUT_VD_RAMP_WIDTH 3 /* OUT_VD_RAMP - [6:4] */
+#define WM2200_OUT_VI_RAMP_MASK 0x0007 /* OUT_VI_RAMP - [2:0] */
+#define WM2200_OUT_VI_RAMP_SHIFT 0 /* OUT_VI_RAMP - [2:0] */
+#define WM2200_OUT_VI_RAMP_WIDTH 3 /* OUT_VI_RAMP - [2:0] */
+
+/*
+ * R1035 (0x40B) - DAC Digital Volume 1L
+ */
+#define WM2200_OUT_VU 0x0200 /* OUT_VU */
+#define WM2200_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM2200_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM2200_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM2200_OUT1L_MUTE 0x0100 /* OUT1L_MUTE */
+#define WM2200_OUT1L_MUTE_MASK 0x0100 /* OUT1L_MUTE */
+#define WM2200_OUT1L_MUTE_SHIFT 8 /* OUT1L_MUTE */
+#define WM2200_OUT1L_MUTE_WIDTH 1 /* OUT1L_MUTE */
+#define WM2200_OUT1L_VOL_MASK 0x00FF /* OUT1L_VOL - [7:0] */
+#define WM2200_OUT1L_VOL_SHIFT 0 /* OUT1L_VOL - [7:0] */
+#define WM2200_OUT1L_VOL_WIDTH 8 /* OUT1L_VOL - [7:0] */
+
+/*
+ * R1036 (0x40C) - DAC Digital Volume 1R
+ */
+#define WM2200_OUT_VU 0x0200 /* OUT_VU */
+#define WM2200_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM2200_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM2200_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM2200_OUT1R_MUTE 0x0100 /* OUT1R_MUTE */
+#define WM2200_OUT1R_MUTE_MASK 0x0100 /* OUT1R_MUTE */
+#define WM2200_OUT1R_MUTE_SHIFT 8 /* OUT1R_MUTE */
+#define WM2200_OUT1R_MUTE_WIDTH 1 /* OUT1R_MUTE */
+#define WM2200_OUT1R_VOL_MASK 0x00FF /* OUT1R_VOL - [7:0] */
+#define WM2200_OUT1R_VOL_SHIFT 0 /* OUT1R_VOL - [7:0] */
+#define WM2200_OUT1R_VOL_WIDTH 8 /* OUT1R_VOL - [7:0] */
+
+/*
+ * R1037 (0x40D) - DAC Digital Volume 2L
+ */
+#define WM2200_OUT_VU 0x0200 /* OUT_VU */
+#define WM2200_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM2200_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM2200_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM2200_OUT2L_MUTE 0x0100 /* OUT2L_MUTE */
+#define WM2200_OUT2L_MUTE_MASK 0x0100 /* OUT2L_MUTE */
+#define WM2200_OUT2L_MUTE_SHIFT 8 /* OUT2L_MUTE */
+#define WM2200_OUT2L_MUTE_WIDTH 1 /* OUT2L_MUTE */
+#define WM2200_OUT2L_VOL_MASK 0x00FF /* OUT2L_VOL - [7:0] */
+#define WM2200_OUT2L_VOL_SHIFT 0 /* OUT2L_VOL - [7:0] */
+#define WM2200_OUT2L_VOL_WIDTH 8 /* OUT2L_VOL - [7:0] */
+
+/*
+ * R1038 (0x40E) - DAC Digital Volume 2R
+ */
+#define WM2200_OUT_VU 0x0200 /* OUT_VU */
+#define WM2200_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM2200_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM2200_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM2200_OUT2R_MUTE 0x0100 /* OUT2R_MUTE */
+#define WM2200_OUT2R_MUTE_MASK 0x0100 /* OUT2R_MUTE */
+#define WM2200_OUT2R_MUTE_SHIFT 8 /* OUT2R_MUTE */
+#define WM2200_OUT2R_MUTE_WIDTH 1 /* OUT2R_MUTE */
+#define WM2200_OUT2R_VOL_MASK 0x00FF /* OUT2R_VOL - [7:0] */
+#define WM2200_OUT2R_VOL_SHIFT 0 /* OUT2R_VOL - [7:0] */
+#define WM2200_OUT2R_VOL_WIDTH 8 /* OUT2R_VOL - [7:0] */
+
+/*
+ * R1047 (0x417) - PDM 1
+ */
+#define WM2200_SPK1R_MUTE 0x2000 /* SPK1R_MUTE */
+#define WM2200_SPK1R_MUTE_MASK 0x2000 /* SPK1R_MUTE */
+#define WM2200_SPK1R_MUTE_SHIFT 13 /* SPK1R_MUTE */
+#define WM2200_SPK1R_MUTE_WIDTH 1 /* SPK1R_MUTE */
+#define WM2200_SPK1L_MUTE 0x1000 /* SPK1L_MUTE */
+#define WM2200_SPK1L_MUTE_MASK 0x1000 /* SPK1L_MUTE */
+#define WM2200_SPK1L_MUTE_SHIFT 12 /* SPK1L_MUTE */
+#define WM2200_SPK1L_MUTE_WIDTH 1 /* SPK1L_MUTE */
+#define WM2200_SPK1_MUTE_ENDIAN 0x0100 /* SPK1_MUTE_ENDIAN */
+#define WM2200_SPK1_MUTE_ENDIAN_MASK 0x0100 /* SPK1_MUTE_ENDIAN */
+#define WM2200_SPK1_MUTE_ENDIAN_SHIFT 8 /* SPK1_MUTE_ENDIAN */
+#define WM2200_SPK1_MUTE_ENDIAN_WIDTH 1 /* SPK1_MUTE_ENDIAN */
+#define WM2200_SPK1_MUTE_SEQL_MASK 0x00FF /* SPK1_MUTE_SEQL - [7:0] */
+#define WM2200_SPK1_MUTE_SEQL_SHIFT 0 /* SPK1_MUTE_SEQL - [7:0] */
+#define WM2200_SPK1_MUTE_SEQL_WIDTH 8 /* SPK1_MUTE_SEQL - [7:0] */
+
+/*
+ * R1048 (0x418) - PDM 2
+ */
+#define WM2200_SPK1_FMT 0x0001 /* SPK1_FMT */
+#define WM2200_SPK1_FMT_MASK 0x0001 /* SPK1_FMT */
+#define WM2200_SPK1_FMT_SHIFT 0 /* SPK1_FMT */
+#define WM2200_SPK1_FMT_WIDTH 1 /* SPK1_FMT */
+
+/*
+ * R1280 (0x500) - Audio IF 1_1
+ */
+#define WM2200_AIF1_BCLK_INV 0x0040 /* AIF1_BCLK_INV */
+#define WM2200_AIF1_BCLK_INV_MASK 0x0040 /* AIF1_BCLK_INV */
+#define WM2200_AIF1_BCLK_INV_SHIFT 6 /* AIF1_BCLK_INV */
+#define WM2200_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */
+#define WM2200_AIF1_BCLK_FRC 0x0020 /* AIF1_BCLK_FRC */
+#define WM2200_AIF1_BCLK_FRC_MASK 0x0020 /* AIF1_BCLK_FRC */
+#define WM2200_AIF1_BCLK_FRC_SHIFT 5 /* AIF1_BCLK_FRC */
+#define WM2200_AIF1_BCLK_FRC_WIDTH 1 /* AIF1_BCLK_FRC */
+#define WM2200_AIF1_BCLK_MSTR 0x0010 /* AIF1_BCLK_MSTR */
+#define WM2200_AIF1_BCLK_MSTR_MASK 0x0010 /* AIF1_BCLK_MSTR */
+#define WM2200_AIF1_BCLK_MSTR_SHIFT 4 /* AIF1_BCLK_MSTR */
+#define WM2200_AIF1_BCLK_MSTR_WIDTH 1 /* AIF1_BCLK_MSTR */
+#define WM2200_AIF1_BCLK_DIV_MASK 0x000F /* AIF1_BCLK_DIV - [3:0] */
+#define WM2200_AIF1_BCLK_DIV_SHIFT 0 /* AIF1_BCLK_DIV - [3:0] */
+#define WM2200_AIF1_BCLK_DIV_WIDTH 4 /* AIF1_BCLK_DIV - [3:0] */
+
+/*
+ * R1281 (0x501) - Audio IF 1_2
+ */
+#define WM2200_AIF1TX_DAT_TRI 0x0020 /* AIF1TX_DAT_TRI */
+#define WM2200_AIF1TX_DAT_TRI_MASK 0x0020 /* AIF1TX_DAT_TRI */
+#define WM2200_AIF1TX_DAT_TRI_SHIFT 5 /* AIF1TX_DAT_TRI */
+#define WM2200_AIF1TX_DAT_TRI_WIDTH 1 /* AIF1TX_DAT_TRI */
+#define WM2200_AIF1TX_LRCLK_SRC 0x0008 /* AIF1TX_LRCLK_SRC */
+#define WM2200_AIF1TX_LRCLK_SRC_MASK 0x0008 /* AIF1TX_LRCLK_SRC */
+#define WM2200_AIF1TX_LRCLK_SRC_SHIFT 3 /* AIF1TX_LRCLK_SRC */
+#define WM2200_AIF1TX_LRCLK_SRC_WIDTH 1 /* AIF1TX_LRCLK_SRC */
+#define WM2200_AIF1TX_LRCLK_INV 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM2200_AIF1TX_LRCLK_INV_MASK 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM2200_AIF1TX_LRCLK_INV_SHIFT 2 /* AIF1TX_LRCLK_INV */
+#define WM2200_AIF1TX_LRCLK_INV_WIDTH 1 /* AIF1TX_LRCLK_INV */
+#define WM2200_AIF1TX_LRCLK_FRC 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM2200_AIF1TX_LRCLK_FRC_MASK 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM2200_AIF1TX_LRCLK_FRC_SHIFT 1 /* AIF1TX_LRCLK_FRC */
+#define WM2200_AIF1TX_LRCLK_FRC_WIDTH 1 /* AIF1TX_LRCLK_FRC */
+#define WM2200_AIF1TX_LRCLK_MSTR 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM2200_AIF1TX_LRCLK_MSTR_MASK 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM2200_AIF1TX_LRCLK_MSTR_SHIFT 0 /* AIF1TX_LRCLK_MSTR */
+#define WM2200_AIF1TX_LRCLK_MSTR_WIDTH 1 /* AIF1TX_LRCLK_MSTR */
+
+/*
+ * R1282 (0x502) - Audio IF 1_3
+ */
+#define WM2200_AIF1RX_LRCLK_INV 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM2200_AIF1RX_LRCLK_INV_MASK 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM2200_AIF1RX_LRCLK_INV_SHIFT 2 /* AIF1RX_LRCLK_INV */
+#define WM2200_AIF1RX_LRCLK_INV_WIDTH 1 /* AIF1RX_LRCLK_INV */
+#define WM2200_AIF1RX_LRCLK_FRC 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM2200_AIF1RX_LRCLK_FRC_MASK 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM2200_AIF1RX_LRCLK_FRC_SHIFT 1 /* AIF1RX_LRCLK_FRC */
+#define WM2200_AIF1RX_LRCLK_FRC_WIDTH 1 /* AIF1RX_LRCLK_FRC */
+#define WM2200_AIF1RX_LRCLK_MSTR 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM2200_AIF1RX_LRCLK_MSTR_MASK 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM2200_AIF1RX_LRCLK_MSTR_SHIFT 0 /* AIF1RX_LRCLK_MSTR */
+#define WM2200_AIF1RX_LRCLK_MSTR_WIDTH 1 /* AIF1RX_LRCLK_MSTR */
+
+/*
+ * R1283 (0x503) - Audio IF 1_4
+ */
+#define WM2200_AIF1_TRI 0x0040 /* AIF1_TRI */
+#define WM2200_AIF1_TRI_MASK 0x0040 /* AIF1_TRI */
+#define WM2200_AIF1_TRI_SHIFT 6 /* AIF1_TRI */
+#define WM2200_AIF1_TRI_WIDTH 1 /* AIF1_TRI */
+
+/*
+ * R1284 (0x504) - Audio IF 1_5
+ */
+#define WM2200_AIF1_FMT_MASK 0x0007 /* AIF1_FMT - [2:0] */
+#define WM2200_AIF1_FMT_SHIFT 0 /* AIF1_FMT - [2:0] */
+#define WM2200_AIF1_FMT_WIDTH 3 /* AIF1_FMT - [2:0] */
+
+/*
+ * R1285 (0x505) - Audio IF 1_6
+ */
+#define WM2200_AIF1TX_BCPF_MASK 0x07FF /* AIF1TX_BCPF - [10:0] */
+#define WM2200_AIF1TX_BCPF_SHIFT 0 /* AIF1TX_BCPF - [10:0] */
+#define WM2200_AIF1TX_BCPF_WIDTH 11 /* AIF1TX_BCPF - [10:0] */
+
+/*
+ * R1286 (0x506) - Audio IF 1_7
+ */
+#define WM2200_AIF1RX_BCPF_MASK 0x07FF /* AIF1RX_BCPF - [10:0] */
+#define WM2200_AIF1RX_BCPF_SHIFT 0 /* AIF1RX_BCPF - [10:0] */
+#define WM2200_AIF1RX_BCPF_WIDTH 11 /* AIF1RX_BCPF - [10:0] */
+
+/*
+ * R1287 (0x507) - Audio IF 1_8
+ */
+#define WM2200_AIF1TX_WL_MASK 0x3F00 /* AIF1TX_WL - [13:8] */
+#define WM2200_AIF1TX_WL_SHIFT 8 /* AIF1TX_WL - [13:8] */
+#define WM2200_AIF1TX_WL_WIDTH 6 /* AIF1TX_WL - [13:8] */
+#define WM2200_AIF1TX_SLOT_LEN_MASK 0x00FF /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM2200_AIF1TX_SLOT_LEN_SHIFT 0 /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM2200_AIF1TX_SLOT_LEN_WIDTH 8 /* AIF1TX_SLOT_LEN - [7:0] */
+
+/*
+ * R1288 (0x508) - Audio IF 1_9
+ */
+#define WM2200_AIF1RX_WL_MASK 0x3F00 /* AIF1RX_WL - [13:8] */
+#define WM2200_AIF1RX_WL_SHIFT 8 /* AIF1RX_WL - [13:8] */
+#define WM2200_AIF1RX_WL_WIDTH 6 /* AIF1RX_WL - [13:8] */
+#define WM2200_AIF1RX_SLOT_LEN_MASK 0x00FF /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM2200_AIF1RX_SLOT_LEN_SHIFT 0 /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM2200_AIF1RX_SLOT_LEN_WIDTH 8 /* AIF1RX_SLOT_LEN - [7:0] */
+
+/*
+ * R1289 (0x509) - Audio IF 1_10
+ */
+#define WM2200_AIF1TX1_SLOT_MASK 0x003F /* AIF1TX1_SLOT - [5:0] */
+#define WM2200_AIF1TX1_SLOT_SHIFT 0 /* AIF1TX1_SLOT - [5:0] */
+#define WM2200_AIF1TX1_SLOT_WIDTH 6 /* AIF1TX1_SLOT - [5:0] */
+
+/*
+ * R1290 (0x50A) - Audio IF 1_11
+ */
+#define WM2200_AIF1TX2_SLOT_MASK 0x003F /* AIF1TX2_SLOT - [5:0] */
+#define WM2200_AIF1TX2_SLOT_SHIFT 0 /* AIF1TX2_SLOT - [5:0] */
+#define WM2200_AIF1TX2_SLOT_WIDTH 6 /* AIF1TX2_SLOT - [5:0] */
+
+/*
+ * R1291 (0x50B) - Audio IF 1_12
+ */
+#define WM2200_AIF1TX3_SLOT_MASK 0x003F /* AIF1TX3_SLOT - [5:0] */
+#define WM2200_AIF1TX3_SLOT_SHIFT 0 /* AIF1TX3_SLOT - [5:0] */
+#define WM2200_AIF1TX3_SLOT_WIDTH 6 /* AIF1TX3_SLOT - [5:0] */
+
+/*
+ * R1292 (0x50C) - Audio IF 1_13
+ */
+#define WM2200_AIF1TX4_SLOT_MASK 0x003F /* AIF1TX4_SLOT - [5:0] */
+#define WM2200_AIF1TX4_SLOT_SHIFT 0 /* AIF1TX4_SLOT - [5:0] */
+#define WM2200_AIF1TX4_SLOT_WIDTH 6 /* AIF1TX4_SLOT - [5:0] */
+
+/*
+ * R1293 (0x50D) - Audio IF 1_14
+ */
+#define WM2200_AIF1TX5_SLOT_MASK 0x003F /* AIF1TX5_SLOT - [5:0] */
+#define WM2200_AIF1TX5_SLOT_SHIFT 0 /* AIF1TX5_SLOT - [5:0] */
+#define WM2200_AIF1TX5_SLOT_WIDTH 6 /* AIF1TX5_SLOT - [5:0] */
+
+/*
+ * R1294 (0x50E) - Audio IF 1_15
+ */
+#define WM2200_AIF1TX6_SLOT_MASK 0x003F /* AIF1TX6_SLOT - [5:0] */
+#define WM2200_AIF1TX6_SLOT_SHIFT 0 /* AIF1TX6_SLOT - [5:0] */
+#define WM2200_AIF1TX6_SLOT_WIDTH 6 /* AIF1TX6_SLOT - [5:0] */
+
+/*
+ * R1295 (0x50F) - Audio IF 1_16
+ */
+#define WM2200_AIF1RX1_SLOT_MASK 0x003F /* AIF1RX1_SLOT - [5:0] */
+#define WM2200_AIF1RX1_SLOT_SHIFT 0 /* AIF1RX1_SLOT - [5:0] */
+#define WM2200_AIF1RX1_SLOT_WIDTH 6 /* AIF1RX1_SLOT - [5:0] */
+
+/*
+ * R1296 (0x510) - Audio IF 1_17
+ */
+#define WM2200_AIF1RX2_SLOT_MASK 0x003F /* AIF1RX2_SLOT - [5:0] */
+#define WM2200_AIF1RX2_SLOT_SHIFT 0 /* AIF1RX2_SLOT - [5:0] */
+#define WM2200_AIF1RX2_SLOT_WIDTH 6 /* AIF1RX2_SLOT - [5:0] */
+
+/*
+ * R1297 (0x511) - Audio IF 1_18
+ */
+#define WM2200_AIF1RX3_SLOT_MASK 0x003F /* AIF1RX3_SLOT - [5:0] */
+#define WM2200_AIF1RX3_SLOT_SHIFT 0 /* AIF1RX3_SLOT - [5:0] */
+#define WM2200_AIF1RX3_SLOT_WIDTH 6 /* AIF1RX3_SLOT - [5:0] */
+
+/*
+ * R1298 (0x512) - Audio IF 1_19
+ */
+#define WM2200_AIF1RX4_SLOT_MASK 0x003F /* AIF1RX4_SLOT - [5:0] */
+#define WM2200_AIF1RX4_SLOT_SHIFT 0 /* AIF1RX4_SLOT - [5:0] */
+#define WM2200_AIF1RX4_SLOT_WIDTH 6 /* AIF1RX4_SLOT - [5:0] */
+
+/*
+ * R1299 (0x513) - Audio IF 1_20
+ */
+#define WM2200_AIF1RX5_SLOT_MASK 0x003F /* AIF1RX5_SLOT - [5:0] */
+#define WM2200_AIF1RX5_SLOT_SHIFT 0 /* AIF1RX5_SLOT - [5:0] */
+#define WM2200_AIF1RX5_SLOT_WIDTH 6 /* AIF1RX5_SLOT - [5:0] */
+
+/*
+ * R1300 (0x514) - Audio IF 1_21
+ */
+#define WM2200_AIF1RX6_SLOT_MASK 0x003F /* AIF1RX6_SLOT - [5:0] */
+#define WM2200_AIF1RX6_SLOT_SHIFT 0 /* AIF1RX6_SLOT - [5:0] */
+#define WM2200_AIF1RX6_SLOT_WIDTH 6 /* AIF1RX6_SLOT - [5:0] */
+
+/*
+ * R1301 (0x515) - Audio IF 1_22
+ */
+#define WM2200_AIF1RX6_ENA 0x0800 /* AIF1RX6_ENA */
+#define WM2200_AIF1RX6_ENA_MASK 0x0800 /* AIF1RX6_ENA */
+#define WM2200_AIF1RX6_ENA_SHIFT 11 /* AIF1RX6_ENA */
+#define WM2200_AIF1RX6_ENA_WIDTH 1 /* AIF1RX6_ENA */
+#define WM2200_AIF1RX5_ENA 0x0400 /* AIF1RX5_ENA */
+#define WM2200_AIF1RX5_ENA_MASK 0x0400 /* AIF1RX5_ENA */
+#define WM2200_AIF1RX5_ENA_SHIFT 10 /* AIF1RX5_ENA */
+#define WM2200_AIF1RX5_ENA_WIDTH 1 /* AIF1RX5_ENA */
+#define WM2200_AIF1RX4_ENA 0x0200 /* AIF1RX4_ENA */
+#define WM2200_AIF1RX4_ENA_MASK 0x0200 /* AIF1RX4_ENA */
+#define WM2200_AIF1RX4_ENA_SHIFT 9 /* AIF1RX4_ENA */
+#define WM2200_AIF1RX4_ENA_WIDTH 1 /* AIF1RX4_ENA */
+#define WM2200_AIF1RX3_ENA 0x0100 /* AIF1RX3_ENA */
+#define WM2200_AIF1RX3_ENA_MASK 0x0100 /* AIF1RX3_ENA */
+#define WM2200_AIF1RX3_ENA_SHIFT 8 /* AIF1RX3_ENA */
+#define WM2200_AIF1RX3_ENA_WIDTH 1 /* AIF1RX3_ENA */
+#define WM2200_AIF1RX2_ENA 0x0080 /* AIF1RX2_ENA */
+#define WM2200_AIF1RX2_ENA_MASK 0x0080 /* AIF1RX2_ENA */
+#define WM2200_AIF1RX2_ENA_SHIFT 7 /* AIF1RX2_ENA */
+#define WM2200_AIF1RX2_ENA_WIDTH 1 /* AIF1RX2_ENA */
+#define WM2200_AIF1RX1_ENA 0x0040 /* AIF1RX1_ENA */
+#define WM2200_AIF1RX1_ENA_MASK 0x0040 /* AIF1RX1_ENA */
+#define WM2200_AIF1RX1_ENA_SHIFT 6 /* AIF1RX1_ENA */
+#define WM2200_AIF1RX1_ENA_WIDTH 1 /* AIF1RX1_ENA */
+#define WM2200_AIF1TX6_ENA 0x0020 /* AIF1TX6_ENA */
+#define WM2200_AIF1TX6_ENA_MASK 0x0020 /* AIF1TX6_ENA */
+#define WM2200_AIF1TX6_ENA_SHIFT 5 /* AIF1TX6_ENA */
+#define WM2200_AIF1TX6_ENA_WIDTH 1 /* AIF1TX6_ENA */
+#define WM2200_AIF1TX5_ENA 0x0010 /* AIF1TX5_ENA */
+#define WM2200_AIF1TX5_ENA_MASK 0x0010 /* AIF1TX5_ENA */
+#define WM2200_AIF1TX5_ENA_SHIFT 4 /* AIF1TX5_ENA */
+#define WM2200_AIF1TX5_ENA_WIDTH 1 /* AIF1TX5_ENA */
+#define WM2200_AIF1TX4_ENA 0x0008 /* AIF1TX4_ENA */
+#define WM2200_AIF1TX4_ENA_MASK 0x0008 /* AIF1TX4_ENA */
+#define WM2200_AIF1TX4_ENA_SHIFT 3 /* AIF1TX4_ENA */
+#define WM2200_AIF1TX4_ENA_WIDTH 1 /* AIF1TX4_ENA */
+#define WM2200_AIF1TX3_ENA 0x0004 /* AIF1TX3_ENA */
+#define WM2200_AIF1TX3_ENA_MASK 0x0004 /* AIF1TX3_ENA */
+#define WM2200_AIF1TX3_ENA_SHIFT 2 /* AIF1TX3_ENA */
+#define WM2200_AIF1TX3_ENA_WIDTH 1 /* AIF1TX3_ENA */
+#define WM2200_AIF1TX2_ENA 0x0002 /* AIF1TX2_ENA */
+#define WM2200_AIF1TX2_ENA_MASK 0x0002 /* AIF1TX2_ENA */
+#define WM2200_AIF1TX2_ENA_SHIFT 1 /* AIF1TX2_ENA */
+#define WM2200_AIF1TX2_ENA_WIDTH 1 /* AIF1TX2_ENA */
+#define WM2200_AIF1TX1_ENA 0x0001 /* AIF1TX1_ENA */
+#define WM2200_AIF1TX1_ENA_MASK 0x0001 /* AIF1TX1_ENA */
+#define WM2200_AIF1TX1_ENA_SHIFT 0 /* AIF1TX1_ENA */
+#define WM2200_AIF1TX1_ENA_WIDTH 1 /* AIF1TX1_ENA */
+
+/*
+ * R1536 (0x600) - OUT1LMIX Input 1 Source
+ */
+#define WM2200_OUT1LMIX_SRC1_MASK 0x007F /* OUT1LMIX_SRC1 - [6:0] */
+#define WM2200_OUT1LMIX_SRC1_SHIFT 0 /* OUT1LMIX_SRC1 - [6:0] */
+#define WM2200_OUT1LMIX_SRC1_WIDTH 7 /* OUT1LMIX_SRC1 - [6:0] */
+
+/*
+ * R1537 (0x601) - OUT1LMIX Input 1 Volume
+ */
+#define WM2200_OUT1LMIX_VOL1_MASK 0x00FE /* OUT1LMIX_VOL1 - [7:1] */
+#define WM2200_OUT1LMIX_VOL1_SHIFT 1 /* OUT1LMIX_VOL1 - [7:1] */
+#define WM2200_OUT1LMIX_VOL1_WIDTH 7 /* OUT1LMIX_VOL1 - [7:1] */
+
+/*
+ * R1538 (0x602) - OUT1LMIX Input 2 Source
+ */
+#define WM2200_OUT1LMIX_SRC2_MASK 0x007F /* OUT1LMIX_SRC2 - [6:0] */
+#define WM2200_OUT1LMIX_SRC2_SHIFT 0 /* OUT1LMIX_SRC2 - [6:0] */
+#define WM2200_OUT1LMIX_SRC2_WIDTH 7 /* OUT1LMIX_SRC2 - [6:0] */
+
+/*
+ * R1539 (0x603) - OUT1LMIX Input 2 Volume
+ */
+#define WM2200_OUT1LMIX_VOL2_MASK 0x00FE /* OUT1LMIX_VOL2 - [7:1] */
+#define WM2200_OUT1LMIX_VOL2_SHIFT 1 /* OUT1LMIX_VOL2 - [7:1] */
+#define WM2200_OUT1LMIX_VOL2_WIDTH 7 /* OUT1LMIX_VOL2 - [7:1] */
+
+/*
+ * R1540 (0x604) - OUT1LMIX Input 3 Source
+ */
+#define WM2200_OUT1LMIX_SRC3_MASK 0x007F /* OUT1LMIX_SRC3 - [6:0] */
+#define WM2200_OUT1LMIX_SRC3_SHIFT 0 /* OUT1LMIX_SRC3 - [6:0] */
+#define WM2200_OUT1LMIX_SRC3_WIDTH 7 /* OUT1LMIX_SRC3 - [6:0] */
+
+/*
+ * R1541 (0x605) - OUT1LMIX Input 3 Volume
+ */
+#define WM2200_OUT1LMIX_VOL3_MASK 0x00FE /* OUT1LMIX_VOL3 - [7:1] */
+#define WM2200_OUT1LMIX_VOL3_SHIFT 1 /* OUT1LMIX_VOL3 - [7:1] */
+#define WM2200_OUT1LMIX_VOL3_WIDTH 7 /* OUT1LMIX_VOL3 - [7:1] */
+
+/*
+ * R1542 (0x606) - OUT1LMIX Input 4 Source
+ */
+#define WM2200_OUT1LMIX_SRC4_MASK 0x007F /* OUT1LMIX_SRC4 - [6:0] */
+#define WM2200_OUT1LMIX_SRC4_SHIFT 0 /* OUT1LMIX_SRC4 - [6:0] */
+#define WM2200_OUT1LMIX_SRC4_WIDTH 7 /* OUT1LMIX_SRC4 - [6:0] */
+
+/*
+ * R1543 (0x607) - OUT1LMIX Input 4 Volume
+ */
+#define WM2200_OUT1LMIX_VOL4_MASK 0x00FE /* OUT1LMIX_VOL4 - [7:1] */
+#define WM2200_OUT1LMIX_VOL4_SHIFT 1 /* OUT1LMIX_VOL4 - [7:1] */
+#define WM2200_OUT1LMIX_VOL4_WIDTH 7 /* OUT1LMIX_VOL4 - [7:1] */
+
+/*
+ * R1544 (0x608) - OUT1RMIX Input 1 Source
+ */
+#define WM2200_OUT1RMIX_SRC1_MASK 0x007F /* OUT1RMIX_SRC1 - [6:0] */
+#define WM2200_OUT1RMIX_SRC1_SHIFT 0 /* OUT1RMIX_SRC1 - [6:0] */
+#define WM2200_OUT1RMIX_SRC1_WIDTH 7 /* OUT1RMIX_SRC1 - [6:0] */
+
+/*
+ * R1545 (0x609) - OUT1RMIX Input 1 Volume
+ */
+#define WM2200_OUT1RMIX_VOL1_MASK 0x00FE /* OUT1RMIX_VOL1 - [7:1] */
+#define WM2200_OUT1RMIX_VOL1_SHIFT 1 /* OUT1RMIX_VOL1 - [7:1] */
+#define WM2200_OUT1RMIX_VOL1_WIDTH 7 /* OUT1RMIX_VOL1 - [7:1] */
+
+/*
+ * R1546 (0x60A) - OUT1RMIX Input 2 Source
+ */
+#define WM2200_OUT1RMIX_SRC2_MASK 0x007F /* OUT1RMIX_SRC2 - [6:0] */
+#define WM2200_OUT1RMIX_SRC2_SHIFT 0 /* OUT1RMIX_SRC2 - [6:0] */
+#define WM2200_OUT1RMIX_SRC2_WIDTH 7 /* OUT1RMIX_SRC2 - [6:0] */
+
+/*
+ * R1547 (0x60B) - OUT1RMIX Input 2 Volume
+ */
+#define WM2200_OUT1RMIX_VOL2_MASK 0x00FE /* OUT1RMIX_VOL2 - [7:1] */
+#define WM2200_OUT1RMIX_VOL2_SHIFT 1 /* OUT1RMIX_VOL2 - [7:1] */
+#define WM2200_OUT1RMIX_VOL2_WIDTH 7 /* OUT1RMIX_VOL2 - [7:1] */
+
+/*
+ * R1548 (0x60C) - OUT1RMIX Input 3 Source
+ */
+#define WM2200_OUT1RMIX_SRC3_MASK 0x007F /* OUT1RMIX_SRC3 - [6:0] */
+#define WM2200_OUT1RMIX_SRC3_SHIFT 0 /* OUT1RMIX_SRC3 - [6:0] */
+#define WM2200_OUT1RMIX_SRC3_WIDTH 7 /* OUT1RMIX_SRC3 - [6:0] */
+
+/*
+ * R1549 (0x60D) - OUT1RMIX Input 3 Volume
+ */
+#define WM2200_OUT1RMIX_VOL3_MASK 0x00FE /* OUT1RMIX_VOL3 - [7:1] */
+#define WM2200_OUT1RMIX_VOL3_SHIFT 1 /* OUT1RMIX_VOL3 - [7:1] */
+#define WM2200_OUT1RMIX_VOL3_WIDTH 7 /* OUT1RMIX_VOL3 - [7:1] */
+
+/*
+ * R1550 (0x60E) - OUT1RMIX Input 4 Source
+ */
+#define WM2200_OUT1RMIX_SRC4_MASK 0x007F /* OUT1RMIX_SRC4 - [6:0] */
+#define WM2200_OUT1RMIX_SRC4_SHIFT 0 /* OUT1RMIX_SRC4 - [6:0] */
+#define WM2200_OUT1RMIX_SRC4_WIDTH 7 /* OUT1RMIX_SRC4 - [6:0] */
+
+/*
+ * R1551 (0x60F) - OUT1RMIX Input 4 Volume
+ */
+#define WM2200_OUT1RMIX_VOL4_MASK 0x00FE /* OUT1RMIX_VOL4 - [7:1] */
+#define WM2200_OUT1RMIX_VOL4_SHIFT 1 /* OUT1RMIX_VOL4 - [7:1] */
+#define WM2200_OUT1RMIX_VOL4_WIDTH 7 /* OUT1RMIX_VOL4 - [7:1] */
+
+/*
+ * R1552 (0x610) - OUT2LMIX Input 1 Source
+ */
+#define WM2200_OUT2LMIX_SRC1_MASK 0x007F /* OUT2LMIX_SRC1 - [6:0] */
+#define WM2200_OUT2LMIX_SRC1_SHIFT 0 /* OUT2LMIX_SRC1 - [6:0] */
+#define WM2200_OUT2LMIX_SRC1_WIDTH 7 /* OUT2LMIX_SRC1 - [6:0] */
+
+/*
+ * R1553 (0x611) - OUT2LMIX Input 1 Volume
+ */
+#define WM2200_OUT2LMIX_VOL1_MASK 0x00FE /* OUT2LMIX_VOL1 - [7:1] */
+#define WM2200_OUT2LMIX_VOL1_SHIFT 1 /* OUT2LMIX_VOL1 - [7:1] */
+#define WM2200_OUT2LMIX_VOL1_WIDTH 7 /* OUT2LMIX_VOL1 - [7:1] */
+
+/*
+ * R1554 (0x612) - OUT2LMIX Input 2 Source
+ */
+#define WM2200_OUT2LMIX_SRC2_MASK 0x007F /* OUT2LMIX_SRC2 - [6:0] */
+#define WM2200_OUT2LMIX_SRC2_SHIFT 0 /* OUT2LMIX_SRC2 - [6:0] */
+#define WM2200_OUT2LMIX_SRC2_WIDTH 7 /* OUT2LMIX_SRC2 - [6:0] */
+
+/*
+ * R1555 (0x613) - OUT2LMIX Input 2 Volume
+ */
+#define WM2200_OUT2LMIX_VOL2_MASK 0x00FE /* OUT2LMIX_VOL2 - [7:1] */
+#define WM2200_OUT2LMIX_VOL2_SHIFT 1 /* OUT2LMIX_VOL2 - [7:1] */
+#define WM2200_OUT2LMIX_VOL2_WIDTH 7 /* OUT2LMIX_VOL2 - [7:1] */
+
+/*
+ * R1556 (0x614) - OUT2LMIX Input 3 Source
+ */
+#define WM2200_OUT2LMIX_SRC3_MASK 0x007F /* OUT2LMIX_SRC3 - [6:0] */
+#define WM2200_OUT2LMIX_SRC3_SHIFT 0 /* OUT2LMIX_SRC3 - [6:0] */
+#define WM2200_OUT2LMIX_SRC3_WIDTH 7 /* OUT2LMIX_SRC3 - [6:0] */
+
+/*
+ * R1557 (0x615) - OUT2LMIX Input 3 Volume
+ */
+#define WM2200_OUT2LMIX_VOL3_MASK 0x00FE /* OUT2LMIX_VOL3 - [7:1] */
+#define WM2200_OUT2LMIX_VOL3_SHIFT 1 /* OUT2LMIX_VOL3 - [7:1] */
+#define WM2200_OUT2LMIX_VOL3_WIDTH 7 /* OUT2LMIX_VOL3 - [7:1] */
+
+/*
+ * R1558 (0x616) - OUT2LMIX Input 4 Source
+ */
+#define WM2200_OUT2LMIX_SRC4_MASK 0x007F /* OUT2LMIX_SRC4 - [6:0] */
+#define WM2200_OUT2LMIX_SRC4_SHIFT 0 /* OUT2LMIX_SRC4 - [6:0] */
+#define WM2200_OUT2LMIX_SRC4_WIDTH 7 /* OUT2LMIX_SRC4 - [6:0] */
+
+/*
+ * R1559 (0x617) - OUT2LMIX Input 4 Volume
+ */
+#define WM2200_OUT2LMIX_VOL4_MASK 0x00FE /* OUT2LMIX_VOL4 - [7:1] */
+#define WM2200_OUT2LMIX_VOL4_SHIFT 1 /* OUT2LMIX_VOL4 - [7:1] */
+#define WM2200_OUT2LMIX_VOL4_WIDTH 7 /* OUT2LMIX_VOL4 - [7:1] */
+
+/*
+ * R1560 (0x618) - OUT2RMIX Input 1 Source
+ */
+#define WM2200_OUT2RMIX_SRC1_MASK 0x007F /* OUT2RMIX_SRC1 - [6:0] */
+#define WM2200_OUT2RMIX_SRC1_SHIFT 0 /* OUT2RMIX_SRC1 - [6:0] */
+#define WM2200_OUT2RMIX_SRC1_WIDTH 7 /* OUT2RMIX_SRC1 - [6:0] */
+
+/*
+ * R1561 (0x619) - OUT2RMIX Input 1 Volume
+ */
+#define WM2200_OUT2RMIX_VOL1_MASK 0x00FE /* OUT2RMIX_VOL1 - [7:1] */
+#define WM2200_OUT2RMIX_VOL1_SHIFT 1 /* OUT2RMIX_VOL1 - [7:1] */
+#define WM2200_OUT2RMIX_VOL1_WIDTH 7 /* OUT2RMIX_VOL1 - [7:1] */
+
+/*
+ * R1562 (0x61A) - OUT2RMIX Input 2 Source
+ */
+#define WM2200_OUT2RMIX_SRC2_MASK 0x007F /* OUT2RMIX_SRC2 - [6:0] */
+#define WM2200_OUT2RMIX_SRC2_SHIFT 0 /* OUT2RMIX_SRC2 - [6:0] */
+#define WM2200_OUT2RMIX_SRC2_WIDTH 7 /* OUT2RMIX_SRC2 - [6:0] */
+
+/*
+ * R1563 (0x61B) - OUT2RMIX Input 2 Volume
+ */
+#define WM2200_OUT2RMIX_VOL2_MASK 0x00FE /* OUT2RMIX_VOL2 - [7:1] */
+#define WM2200_OUT2RMIX_VOL2_SHIFT 1 /* OUT2RMIX_VOL2 - [7:1] */
+#define WM2200_OUT2RMIX_VOL2_WIDTH 7 /* OUT2RMIX_VOL2 - [7:1] */
+
+/*
+ * R1564 (0x61C) - OUT2RMIX Input 3 Source
+ */
+#define WM2200_OUT2RMIX_SRC3_MASK 0x007F /* OUT2RMIX_SRC3 - [6:0] */
+#define WM2200_OUT2RMIX_SRC3_SHIFT 0 /* OUT2RMIX_SRC3 - [6:0] */
+#define WM2200_OUT2RMIX_SRC3_WIDTH 7 /* OUT2RMIX_SRC3 - [6:0] */
+
+/*
+ * R1565 (0x61D) - OUT2RMIX Input 3 Volume
+ */
+#define WM2200_OUT2RMIX_VOL3_MASK 0x00FE /* OUT2RMIX_VOL3 - [7:1] */
+#define WM2200_OUT2RMIX_VOL3_SHIFT 1 /* OUT2RMIX_VOL3 - [7:1] */
+#define WM2200_OUT2RMIX_VOL3_WIDTH 7 /* OUT2RMIX_VOL3 - [7:1] */
+
+/*
+ * R1566 (0x61E) - OUT2RMIX Input 4 Source
+ */
+#define WM2200_OUT2RMIX_SRC4_MASK 0x007F /* OUT2RMIX_SRC4 - [6:0] */
+#define WM2200_OUT2RMIX_SRC4_SHIFT 0 /* OUT2RMIX_SRC4 - [6:0] */
+#define WM2200_OUT2RMIX_SRC4_WIDTH 7 /* OUT2RMIX_SRC4 - [6:0] */
+
+/*
+ * R1567 (0x61F) - OUT2RMIX Input 4 Volume
+ */
+#define WM2200_OUT2RMIX_VOL4_MASK 0x00FE /* OUT2RMIX_VOL4 - [7:1] */
+#define WM2200_OUT2RMIX_VOL4_SHIFT 1 /* OUT2RMIX_VOL4 - [7:1] */
+#define WM2200_OUT2RMIX_VOL4_WIDTH 7 /* OUT2RMIX_VOL4 - [7:1] */
+
+/*
+ * R1568 (0x620) - AIF1TX1MIX Input 1 Source
+ */
+#define WM2200_AIF1TX1MIX_SRC1_MASK 0x007F /* AIF1TX1MIX_SRC1 - [6:0] */
+#define WM2200_AIF1TX1MIX_SRC1_SHIFT 0 /* AIF1TX1MIX_SRC1 - [6:0] */
+#define WM2200_AIF1TX1MIX_SRC1_WIDTH 7 /* AIF1TX1MIX_SRC1 - [6:0] */
+
+/*
+ * R1569 (0x621) - AIF1TX1MIX Input 1 Volume
+ */
+#define WM2200_AIF1TX1MIX_VOL1_MASK 0x00FE /* AIF1TX1MIX_VOL1 - [7:1] */
+#define WM2200_AIF1TX1MIX_VOL1_SHIFT 1 /* AIF1TX1MIX_VOL1 - [7:1] */
+#define WM2200_AIF1TX1MIX_VOL1_WIDTH 7 /* AIF1TX1MIX_VOL1 - [7:1] */
+
+/*
+ * R1570 (0x622) - AIF1TX1MIX Input 2 Source
+ */
+#define WM2200_AIF1TX1MIX_SRC2_MASK 0x007F /* AIF1TX1MIX_SRC2 - [6:0] */
+#define WM2200_AIF1TX1MIX_SRC2_SHIFT 0 /* AIF1TX1MIX_SRC2 - [6:0] */
+#define WM2200_AIF1TX1MIX_SRC2_WIDTH 7 /* AIF1TX1MIX_SRC2 - [6:0] */
+
+/*
+ * R1571 (0x623) - AIF1TX1MIX Input 2 Volume
+ */
+#define WM2200_AIF1TX1MIX_VOL2_MASK 0x00FE /* AIF1TX1MIX_VOL2 - [7:1] */
+#define WM2200_AIF1TX1MIX_VOL2_SHIFT 1 /* AIF1TX1MIX_VOL2 - [7:1] */
+#define WM2200_AIF1TX1MIX_VOL2_WIDTH 7 /* AIF1TX1MIX_VOL2 - [7:1] */
+
+/*
+ * R1572 (0x624) - AIF1TX1MIX Input 3 Source
+ */
+#define WM2200_AIF1TX1MIX_SRC3_MASK 0x007F /* AIF1TX1MIX_SRC3 - [6:0] */
+#define WM2200_AIF1TX1MIX_SRC3_SHIFT 0 /* AIF1TX1MIX_SRC3 - [6:0] */
+#define WM2200_AIF1TX1MIX_SRC3_WIDTH 7 /* AIF1TX1MIX_SRC3 - [6:0] */
+
+/*
+ * R1573 (0x625) - AIF1TX1MIX Input 3 Volume
+ */
+#define WM2200_AIF1TX1MIX_VOL3_MASK 0x00FE /* AIF1TX1MIX_VOL3 - [7:1] */
+#define WM2200_AIF1TX1MIX_VOL3_SHIFT 1 /* AIF1TX1MIX_VOL3 - [7:1] */
+#define WM2200_AIF1TX1MIX_VOL3_WIDTH 7 /* AIF1TX1MIX_VOL3 - [7:1] */
+
+/*
+ * R1574 (0x626) - AIF1TX1MIX Input 4 Source
+ */
+#define WM2200_AIF1TX1MIX_SRC4_MASK 0x007F /* AIF1TX1MIX_SRC4 - [6:0] */
+#define WM2200_AIF1TX1MIX_SRC4_SHIFT 0 /* AIF1TX1MIX_SRC4 - [6:0] */
+#define WM2200_AIF1TX1MIX_SRC4_WIDTH 7 /* AIF1TX1MIX_SRC4 - [6:0] */
+
+/*
+ * R1575 (0x627) - AIF1TX1MIX Input 4 Volume
+ */
+#define WM2200_AIF1TX1MIX_VOL4_MASK 0x00FE /* AIF1TX1MIX_VOL4 - [7:1] */
+#define WM2200_AIF1TX1MIX_VOL4_SHIFT 1 /* AIF1TX1MIX_VOL4 - [7:1] */
+#define WM2200_AIF1TX1MIX_VOL4_WIDTH 7 /* AIF1TX1MIX_VOL4 - [7:1] */
+
+/*
+ * R1576 (0x628) - AIF1TX2MIX Input 1 Source
+ */
+#define WM2200_AIF1TX2MIX_SRC1_MASK 0x007F /* AIF1TX2MIX_SRC1 - [6:0] */
+#define WM2200_AIF1TX2MIX_SRC1_SHIFT 0 /* AIF1TX2MIX_SRC1 - [6:0] */
+#define WM2200_AIF1TX2MIX_SRC1_WIDTH 7 /* AIF1TX2MIX_SRC1 - [6:0] */
+
+/*
+ * R1577 (0x629) - AIF1TX2MIX Input 1 Volume
+ */
+#define WM2200_AIF1TX2MIX_VOL1_MASK 0x00FE /* AIF1TX2MIX_VOL1 - [7:1] */
+#define WM2200_AIF1TX2MIX_VOL1_SHIFT 1 /* AIF1TX2MIX_VOL1 - [7:1] */
+#define WM2200_AIF1TX2MIX_VOL1_WIDTH 7 /* AIF1TX2MIX_VOL1 - [7:1] */
+
+/*
+ * R1578 (0x62A) - AIF1TX2MIX Input 2 Source
+ */
+#define WM2200_AIF1TX2MIX_SRC2_MASK 0x007F /* AIF1TX2MIX_SRC2 - [6:0] */
+#define WM2200_AIF1TX2MIX_SRC2_SHIFT 0 /* AIF1TX2MIX_SRC2 - [6:0] */
+#define WM2200_AIF1TX2MIX_SRC2_WIDTH 7 /* AIF1TX2MIX_SRC2 - [6:0] */
+
+/*
+ * R1579 (0x62B) - AIF1TX2MIX Input 2 Volume
+ */
+#define WM2200_AIF1TX2MIX_VOL2_MASK 0x00FE /* AIF1TX2MIX_VOL2 - [7:1] */
+#define WM2200_AIF1TX2MIX_VOL2_SHIFT 1 /* AIF1TX2MIX_VOL2 - [7:1] */
+#define WM2200_AIF1TX2MIX_VOL2_WIDTH 7 /* AIF1TX2MIX_VOL2 - [7:1] */
+
+/*
+ * R1580 (0x62C) - AIF1TX2MIX Input 3 Source
+ */
+#define WM2200_AIF1TX2MIX_SRC3_MASK 0x007F /* AIF1TX2MIX_SRC3 - [6:0] */
+#define WM2200_AIF1TX2MIX_SRC3_SHIFT 0 /* AIF1TX2MIX_SRC3 - [6:0] */
+#define WM2200_AIF1TX2MIX_SRC3_WIDTH 7 /* AIF1TX2MIX_SRC3 - [6:0] */
+
+/*
+ * R1581 (0x62D) - AIF1TX2MIX Input 3 Volume
+ */
+#define WM2200_AIF1TX2MIX_VOL3_MASK 0x00FE /* AIF1TX2MIX_VOL3 - [7:1] */
+#define WM2200_AIF1TX2MIX_VOL3_SHIFT 1 /* AIF1TX2MIX_VOL3 - [7:1] */
+#define WM2200_AIF1TX2MIX_VOL3_WIDTH 7 /* AIF1TX2MIX_VOL3 - [7:1] */
+
+/*
+ * R1582 (0x62E) - AIF1TX2MIX Input 4 Source
+ */
+#define WM2200_AIF1TX2MIX_SRC4_MASK 0x007F /* AIF1TX2MIX_SRC4 - [6:0] */
+#define WM2200_AIF1TX2MIX_SRC4_SHIFT 0 /* AIF1TX2MIX_SRC4 - [6:0] */
+#define WM2200_AIF1TX2MIX_SRC4_WIDTH 7 /* AIF1TX2MIX_SRC4 - [6:0] */
+
+/*
+ * R1583 (0x62F) - AIF1TX2MIX Input 4 Volume
+ */
+#define WM2200_AIF1TX2MIX_VOL4_MASK 0x00FE /* AIF1TX2MIX_VOL4 - [7:1] */
+#define WM2200_AIF1TX2MIX_VOL4_SHIFT 1 /* AIF1TX2MIX_VOL4 - [7:1] */
+#define WM2200_AIF1TX2MIX_VOL4_WIDTH 7 /* AIF1TX2MIX_VOL4 - [7:1] */
+
+/*
+ * R1584 (0x630) - AIF1TX3MIX Input 1 Source
+ */
+#define WM2200_AIF1TX3MIX_SRC1_MASK 0x007F /* AIF1TX3MIX_SRC1 - [6:0] */
+#define WM2200_AIF1TX3MIX_SRC1_SHIFT 0 /* AIF1TX3MIX_SRC1 - [6:0] */
+#define WM2200_AIF1TX3MIX_SRC1_WIDTH 7 /* AIF1TX3MIX_SRC1 - [6:0] */
+
+/*
+ * R1585 (0x631) - AIF1TX3MIX Input 1 Volume
+ */
+#define WM2200_AIF1TX3MIX_VOL1_MASK 0x00FE /* AIF1TX3MIX_VOL1 - [7:1] */
+#define WM2200_AIF1TX3MIX_VOL1_SHIFT 1 /* AIF1TX3MIX_VOL1 - [7:1] */
+#define WM2200_AIF1TX3MIX_VOL1_WIDTH 7 /* AIF1TX3MIX_VOL1 - [7:1] */
+
+/*
+ * R1586 (0x632) - AIF1TX3MIX Input 2 Source
+ */
+#define WM2200_AIF1TX3MIX_SRC2_MASK 0x007F /* AIF1TX3MIX_SRC2 - [6:0] */
+#define WM2200_AIF1TX3MIX_SRC2_SHIFT 0 /* AIF1TX3MIX_SRC2 - [6:0] */
+#define WM2200_AIF1TX3MIX_SRC2_WIDTH 7 /* AIF1TX3MIX_SRC2 - [6:0] */
+
+/*
+ * R1587 (0x633) - AIF1TX3MIX Input 2 Volume
+ */
+#define WM2200_AIF1TX3MIX_VOL2_MASK 0x00FE /* AIF1TX3MIX_VOL2 - [7:1] */
+#define WM2200_AIF1TX3MIX_VOL2_SHIFT 1 /* AIF1TX3MIX_VOL2 - [7:1] */
+#define WM2200_AIF1TX3MIX_VOL2_WIDTH 7 /* AIF1TX3MIX_VOL2 - [7:1] */
+
+/*
+ * R1588 (0x634) - AIF1TX3MIX Input 3 Source
+ */
+#define WM2200_AIF1TX3MIX_SRC3_MASK 0x007F /* AIF1TX3MIX_SRC3 - [6:0] */
+#define WM2200_AIF1TX3MIX_SRC3_SHIFT 0 /* AIF1TX3MIX_SRC3 - [6:0] */
+#define WM2200_AIF1TX3MIX_SRC3_WIDTH 7 /* AIF1TX3MIX_SRC3 - [6:0] */
+
+/*
+ * R1589 (0x635) - AIF1TX3MIX Input 3 Volume
+ */
+#define WM2200_AIF1TX3MIX_VOL3_MASK 0x00FE /* AIF1TX3MIX_VOL3 - [7:1] */
+#define WM2200_AIF1TX3MIX_VOL3_SHIFT 1 /* AIF1TX3MIX_VOL3 - [7:1] */
+#define WM2200_AIF1TX3MIX_VOL3_WIDTH 7 /* AIF1TX3MIX_VOL3 - [7:1] */
+
+/*
+ * R1590 (0x636) - AIF1TX3MIX Input 4 Source
+ */
+#define WM2200_AIF1TX3MIX_SRC4_MASK 0x007F /* AIF1TX3MIX_SRC4 - [6:0] */
+#define WM2200_AIF1TX3MIX_SRC4_SHIFT 0 /* AIF1TX3MIX_SRC4 - [6:0] */
+#define WM2200_AIF1TX3MIX_SRC4_WIDTH 7 /* AIF1TX3MIX_SRC4 - [6:0] */
+
+/*
+ * R1591 (0x637) - AIF1TX3MIX Input 4 Volume
+ */
+#define WM2200_AIF1TX3MIX_VOL4_MASK 0x00FE /* AIF1TX3MIX_VOL4 - [7:1] */
+#define WM2200_AIF1TX3MIX_VOL4_SHIFT 1 /* AIF1TX3MIX_VOL4 - [7:1] */
+#define WM2200_AIF1TX3MIX_VOL4_WIDTH 7 /* AIF1TX3MIX_VOL4 - [7:1] */
+
+/*
+ * R1592 (0x638) - AIF1TX4MIX Input 1 Source
+ */
+#define WM2200_AIF1TX4MIX_SRC1_MASK 0x007F /* AIF1TX4MIX_SRC1 - [6:0] */
+#define WM2200_AIF1TX4MIX_SRC1_SHIFT 0 /* AIF1TX4MIX_SRC1 - [6:0] */
+#define WM2200_AIF1TX4MIX_SRC1_WIDTH 7 /* AIF1TX4MIX_SRC1 - [6:0] */
+
+/*
+ * R1593 (0x639) - AIF1TX4MIX Input 1 Volume
+ */
+#define WM2200_AIF1TX4MIX_VOL1_MASK 0x00FE /* AIF1TX4MIX_VOL1 - [7:1] */
+#define WM2200_AIF1TX4MIX_VOL1_SHIFT 1 /* AIF1TX4MIX_VOL1 - [7:1] */
+#define WM2200_AIF1TX4MIX_VOL1_WIDTH 7 /* AIF1TX4MIX_VOL1 - [7:1] */
+
+/*
+ * R1594 (0x63A) - AIF1TX4MIX Input 2 Source
+ */
+#define WM2200_AIF1TX4MIX_SRC2_MASK 0x007F /* AIF1TX4MIX_SRC2 - [6:0] */
+#define WM2200_AIF1TX4MIX_SRC2_SHIFT 0 /* AIF1TX4MIX_SRC2 - [6:0] */
+#define WM2200_AIF1TX4MIX_SRC2_WIDTH 7 /* AIF1TX4MIX_SRC2 - [6:0] */
+
+/*
+ * R1595 (0x63B) - AIF1TX4MIX Input 2 Volume
+ */
+#define WM2200_AIF1TX4MIX_VOL2_MASK 0x00FE /* AIF1TX4MIX_VOL2 - [7:1] */
+#define WM2200_AIF1TX4MIX_VOL2_SHIFT 1 /* AIF1TX4MIX_VOL2 - [7:1] */
+#define WM2200_AIF1TX4MIX_VOL2_WIDTH 7 /* AIF1TX4MIX_VOL2 - [7:1] */
+
+/*
+ * R1596 (0x63C) - AIF1TX4MIX Input 3 Source
+ */
+#define WM2200_AIF1TX4MIX_SRC3_MASK 0x007F /* AIF1TX4MIX_SRC3 - [6:0] */
+#define WM2200_AIF1TX4MIX_SRC3_SHIFT 0 /* AIF1TX4MIX_SRC3 - [6:0] */
+#define WM2200_AIF1TX4MIX_SRC3_WIDTH 7 /* AIF1TX4MIX_SRC3 - [6:0] */
+
+/*
+ * R1597 (0x63D) - AIF1TX4MIX Input 3 Volume
+ */
+#define WM2200_AIF1TX4MIX_VOL3_MASK 0x00FE /* AIF1TX4MIX_VOL3 - [7:1] */
+#define WM2200_AIF1TX4MIX_VOL3_SHIFT 1 /* AIF1TX4MIX_VOL3 - [7:1] */
+#define WM2200_AIF1TX4MIX_VOL3_WIDTH 7 /* AIF1TX4MIX_VOL3 - [7:1] */
+
+/*
+ * R1598 (0x63E) - AIF1TX4MIX Input 4 Source
+ */
+#define WM2200_AIF1TX4MIX_SRC4_MASK 0x007F /* AIF1TX4MIX_SRC4 - [6:0] */
+#define WM2200_AIF1TX4MIX_SRC4_SHIFT 0 /* AIF1TX4MIX_SRC4 - [6:0] */
+#define WM2200_AIF1TX4MIX_SRC4_WIDTH 7 /* AIF1TX4MIX_SRC4 - [6:0] */
+
+/*
+ * R1599 (0x63F) - AIF1TX4MIX Input 4 Volume
+ */
+#define WM2200_AIF1TX4MIX_VOL4_MASK 0x00FE /* AIF1TX4MIX_VOL4 - [7:1] */
+#define WM2200_AIF1TX4MIX_VOL4_SHIFT 1 /* AIF1TX4MIX_VOL4 - [7:1] */
+#define WM2200_AIF1TX4MIX_VOL4_WIDTH 7 /* AIF1TX4MIX_VOL4 - [7:1] */
+
+/*
+ * R1600 (0x640) - AIF1TX5MIX Input 1 Source
+ */
+#define WM2200_AIF1TX5MIX_SRC1_MASK 0x007F /* AIF1TX5MIX_SRC1 - [6:0] */
+#define WM2200_AIF1TX5MIX_SRC1_SHIFT 0 /* AIF1TX5MIX_SRC1 - [6:0] */
+#define WM2200_AIF1TX5MIX_SRC1_WIDTH 7 /* AIF1TX5MIX_SRC1 - [6:0] */
+
+/*
+ * R1601 (0x641) - AIF1TX5MIX Input 1 Volume
+ */
+#define WM2200_AIF1TX5MIX_VOL1_MASK 0x00FE /* AIF1TX5MIX_VOL1 - [7:1] */
+#define WM2200_AIF1TX5MIX_VOL1_SHIFT 1 /* AIF1TX5MIX_VOL1 - [7:1] */
+#define WM2200_AIF1TX5MIX_VOL1_WIDTH 7 /* AIF1TX5MIX_VOL1 - [7:1] */
+
+/*
+ * R1602 (0x642) - AIF1TX5MIX Input 2 Source
+ */
+#define WM2200_AIF1TX5MIX_SRC2_MASK 0x007F /* AIF1TX5MIX_SRC2 - [6:0] */
+#define WM2200_AIF1TX5MIX_SRC2_SHIFT 0 /* AIF1TX5MIX_SRC2 - [6:0] */
+#define WM2200_AIF1TX5MIX_SRC2_WIDTH 7 /* AIF1TX5MIX_SRC2 - [6:0] */
+
+/*
+ * R1603 (0x643) - AIF1TX5MIX Input 2 Volume
+ */
+#define WM2200_AIF1TX5MIX_VOL2_MASK 0x00FE /* AIF1TX5MIX_VOL2 - [7:1] */
+#define WM2200_AIF1TX5MIX_VOL2_SHIFT 1 /* AIF1TX5MIX_VOL2 - [7:1] */
+#define WM2200_AIF1TX5MIX_VOL2_WIDTH 7 /* AIF1TX5MIX_VOL2 - [7:1] */
+
+/*
+ * R1604 (0x644) - AIF1TX5MIX Input 3 Source
+ */
+#define WM2200_AIF1TX5MIX_SRC3_MASK 0x007F /* AIF1TX5MIX_SRC3 - [6:0] */
+#define WM2200_AIF1TX5MIX_SRC3_SHIFT 0 /* AIF1TX5MIX_SRC3 - [6:0] */
+#define WM2200_AIF1TX5MIX_SRC3_WIDTH 7 /* AIF1TX5MIX_SRC3 - [6:0] */
+
+/*
+ * R1605 (0x645) - AIF1TX5MIX Input 3 Volume
+ */
+#define WM2200_AIF1TX5MIX_VOL3_MASK 0x00FE /* AIF1TX5MIX_VOL3 - [7:1] */
+#define WM2200_AIF1TX5MIX_VOL3_SHIFT 1 /* AIF1TX5MIX_VOL3 - [7:1] */
+#define WM2200_AIF1TX5MIX_VOL3_WIDTH 7 /* AIF1TX5MIX_VOL3 - [7:1] */
+
+/*
+ * R1606 (0x646) - AIF1TX5MIX Input 4 Source
+ */
+#define WM2200_AIF1TX5MIX_SRC4_MASK 0x007F /* AIF1TX5MIX_SRC4 - [6:0] */
+#define WM2200_AIF1TX5MIX_SRC4_SHIFT 0 /* AIF1TX5MIX_SRC4 - [6:0] */
+#define WM2200_AIF1TX5MIX_SRC4_WIDTH 7 /* AIF1TX5MIX_SRC4 - [6:0] */
+
+/*
+ * R1607 (0x647) - AIF1TX5MIX Input 4 Volume
+ */
+#define WM2200_AIF1TX5MIX_VOL4_MASK 0x00FE /* AIF1TX5MIX_VOL4 - [7:1] */
+#define WM2200_AIF1TX5MIX_VOL4_SHIFT 1 /* AIF1TX5MIX_VOL4 - [7:1] */
+#define WM2200_AIF1TX5MIX_VOL4_WIDTH 7 /* AIF1TX5MIX_VOL4 - [7:1] */
+
+/*
+ * R1608 (0x648) - AIF1TX6MIX Input 1 Source
+ */
+#define WM2200_AIF1TX6MIX_SRC1_MASK 0x007F /* AIF1TX6MIX_SRC1 - [6:0] */
+#define WM2200_AIF1TX6MIX_SRC1_SHIFT 0 /* AIF1TX6MIX_SRC1 - [6:0] */
+#define WM2200_AIF1TX6MIX_SRC1_WIDTH 7 /* AIF1TX6MIX_SRC1 - [6:0] */
+
+/*
+ * R1609 (0x649) - AIF1TX6MIX Input 1 Volume
+ */
+#define WM2200_AIF1TX6MIX_VOL1_MASK 0x00FE /* AIF1TX6MIX_VOL1 - [7:1] */
+#define WM2200_AIF1TX6MIX_VOL1_SHIFT 1 /* AIF1TX6MIX_VOL1 - [7:1] */
+#define WM2200_AIF1TX6MIX_VOL1_WIDTH 7 /* AIF1TX6MIX_VOL1 - [7:1] */
+
+/*
+ * R1610 (0x64A) - AIF1TX6MIX Input 2 Source
+ */
+#define WM2200_AIF1TX6MIX_SRC2_MASK 0x007F /* AIF1TX6MIX_SRC2 - [6:0] */
+#define WM2200_AIF1TX6MIX_SRC2_SHIFT 0 /* AIF1TX6MIX_SRC2 - [6:0] */
+#define WM2200_AIF1TX6MIX_SRC2_WIDTH 7 /* AIF1TX6MIX_SRC2 - [6:0] */
+
+/*
+ * R1611 (0x64B) - AIF1TX6MIX Input 2 Volume
+ */
+#define WM2200_AIF1TX6MIX_VOL2_MASK 0x00FE /* AIF1TX6MIX_VOL2 - [7:1] */
+#define WM2200_AIF1TX6MIX_VOL2_SHIFT 1 /* AIF1TX6MIX_VOL2 - [7:1] */
+#define WM2200_AIF1TX6MIX_VOL2_WIDTH 7 /* AIF1TX6MIX_VOL2 - [7:1] */
+
+/*
+ * R1612 (0x64C) - AIF1TX6MIX Input 3 Source
+ */
+#define WM2200_AIF1TX6MIX_SRC3_MASK 0x007F /* AIF1TX6MIX_SRC3 - [6:0] */
+#define WM2200_AIF1TX6MIX_SRC3_SHIFT 0 /* AIF1TX6MIX_SRC3 - [6:0] */
+#define WM2200_AIF1TX6MIX_SRC3_WIDTH 7 /* AIF1TX6MIX_SRC3 - [6:0] */
+
+/*
+ * R1613 (0x64D) - AIF1TX6MIX Input 3 Volume
+ */
+#define WM2200_AIF1TX6MIX_VOL3_MASK 0x00FE /* AIF1TX6MIX_VOL3 - [7:1] */
+#define WM2200_AIF1TX6MIX_VOL3_SHIFT 1 /* AIF1TX6MIX_VOL3 - [7:1] */
+#define WM2200_AIF1TX6MIX_VOL3_WIDTH 7 /* AIF1TX6MIX_VOL3 - [7:1] */
+
+/*
+ * R1614 (0x64E) - AIF1TX6MIX Input 4 Source
+ */
+#define WM2200_AIF1TX6MIX_SRC4_MASK 0x007F /* AIF1TX6MIX_SRC4 - [6:0] */
+#define WM2200_AIF1TX6MIX_SRC4_SHIFT 0 /* AIF1TX6MIX_SRC4 - [6:0] */
+#define WM2200_AIF1TX6MIX_SRC4_WIDTH 7 /* AIF1TX6MIX_SRC4 - [6:0] */
+
+/*
+ * R1615 (0x64F) - AIF1TX6MIX Input 4 Volume
+ */
+#define WM2200_AIF1TX6MIX_VOL4_MASK 0x00FE /* AIF1TX6MIX_VOL4 - [7:1] */
+#define WM2200_AIF1TX6MIX_VOL4_SHIFT 1 /* AIF1TX6MIX_VOL4 - [7:1] */
+#define WM2200_AIF1TX6MIX_VOL4_WIDTH 7 /* AIF1TX6MIX_VOL4 - [7:1] */
+
+/*
+ * R1616 (0x650) - EQLMIX Input 1 Source
+ */
+#define WM2200_EQLMIX_SRC1_MASK 0x007F /* EQLMIX_SRC1 - [6:0] */
+#define WM2200_EQLMIX_SRC1_SHIFT 0 /* EQLMIX_SRC1 - [6:0] */
+#define WM2200_EQLMIX_SRC1_WIDTH 7 /* EQLMIX_SRC1 - [6:0] */
+
+/*
+ * R1617 (0x651) - EQLMIX Input 1 Volume
+ */
+#define WM2200_EQLMIX_VOL1_MASK 0x00FE /* EQLMIX_VOL1 - [7:1] */
+#define WM2200_EQLMIX_VOL1_SHIFT 1 /* EQLMIX_VOL1 - [7:1] */
+#define WM2200_EQLMIX_VOL1_WIDTH 7 /* EQLMIX_VOL1 - [7:1] */
+
+/*
+ * R1618 (0x652) - EQLMIX Input 2 Source
+ */
+#define WM2200_EQLMIX_SRC2_MASK 0x007F /* EQLMIX_SRC2 - [6:0] */
+#define WM2200_EQLMIX_SRC2_SHIFT 0 /* EQLMIX_SRC2 - [6:0] */
+#define WM2200_EQLMIX_SRC2_WIDTH 7 /* EQLMIX_SRC2 - [6:0] */
+
+/*
+ * R1619 (0x653) - EQLMIX Input 2 Volume
+ */
+#define WM2200_EQLMIX_VOL2_MASK 0x00FE /* EQLMIX_VOL2 - [7:1] */
+#define WM2200_EQLMIX_VOL2_SHIFT 1 /* EQLMIX_VOL2 - [7:1] */
+#define WM2200_EQLMIX_VOL2_WIDTH 7 /* EQLMIX_VOL2 - [7:1] */
+
+/*
+ * R1620 (0x654) - EQLMIX Input 3 Source
+ */
+#define WM2200_EQLMIX_SRC3_MASK 0x007F /* EQLMIX_SRC3 - [6:0] */
+#define WM2200_EQLMIX_SRC3_SHIFT 0 /* EQLMIX_SRC3 - [6:0] */
+#define WM2200_EQLMIX_SRC3_WIDTH 7 /* EQLMIX_SRC3 - [6:0] */
+
+/*
+ * R1621 (0x655) - EQLMIX Input 3 Volume
+ */
+#define WM2200_EQLMIX_VOL3_MASK 0x00FE /* EQLMIX_VOL3 - [7:1] */
+#define WM2200_EQLMIX_VOL3_SHIFT 1 /* EQLMIX_VOL3 - [7:1] */
+#define WM2200_EQLMIX_VOL3_WIDTH 7 /* EQLMIX_VOL3 - [7:1] */
+
+/*
+ * R1622 (0x656) - EQLMIX Input 4 Source
+ */
+#define WM2200_EQLMIX_SRC4_MASK 0x007F /* EQLMIX_SRC4 - [6:0] */
+#define WM2200_EQLMIX_SRC4_SHIFT 0 /* EQLMIX_SRC4 - [6:0] */
+#define WM2200_EQLMIX_SRC4_WIDTH 7 /* EQLMIX_SRC4 - [6:0] */
+
+/*
+ * R1623 (0x657) - EQLMIX Input 4 Volume
+ */
+#define WM2200_EQLMIX_VOL4_MASK 0x00FE /* EQLMIX_VOL4 - [7:1] */
+#define WM2200_EQLMIX_VOL4_SHIFT 1 /* EQLMIX_VOL4 - [7:1] */
+#define WM2200_EQLMIX_VOL4_WIDTH 7 /* EQLMIX_VOL4 - [7:1] */
+
+/*
+ * R1624 (0x658) - EQRMIX Input 1 Source
+ */
+#define WM2200_EQRMIX_SRC1_MASK 0x007F /* EQRMIX_SRC1 - [6:0] */
+#define WM2200_EQRMIX_SRC1_SHIFT 0 /* EQRMIX_SRC1 - [6:0] */
+#define WM2200_EQRMIX_SRC1_WIDTH 7 /* EQRMIX_SRC1 - [6:0] */
+
+/*
+ * R1625 (0x659) - EQRMIX Input 1 Volume
+ */
+#define WM2200_EQRMIX_VOL1_MASK 0x00FE /* EQRMIX_VOL1 - [7:1] */
+#define WM2200_EQRMIX_VOL1_SHIFT 1 /* EQRMIX_VOL1 - [7:1] */
+#define WM2200_EQRMIX_VOL1_WIDTH 7 /* EQRMIX_VOL1 - [7:1] */
+
+/*
+ * R1626 (0x65A) - EQRMIX Input 2 Source
+ */
+#define WM2200_EQRMIX_SRC2_MASK 0x007F /* EQRMIX_SRC2 - [6:0] */
+#define WM2200_EQRMIX_SRC2_SHIFT 0 /* EQRMIX_SRC2 - [6:0] */
+#define WM2200_EQRMIX_SRC2_WIDTH 7 /* EQRMIX_SRC2 - [6:0] */
+
+/*
+ * R1627 (0x65B) - EQRMIX Input 2 Volume
+ */
+#define WM2200_EQRMIX_VOL2_MASK 0x00FE /* EQRMIX_VOL2 - [7:1] */
+#define WM2200_EQRMIX_VOL2_SHIFT 1 /* EQRMIX_VOL2 - [7:1] */
+#define WM2200_EQRMIX_VOL2_WIDTH 7 /* EQRMIX_VOL2 - [7:1] */
+
+/*
+ * R1628 (0x65C) - EQRMIX Input 3 Source
+ */
+#define WM2200_EQRMIX_SRC3_MASK 0x007F /* EQRMIX_SRC3 - [6:0] */
+#define WM2200_EQRMIX_SRC3_SHIFT 0 /* EQRMIX_SRC3 - [6:0] */
+#define WM2200_EQRMIX_SRC3_WIDTH 7 /* EQRMIX_SRC3 - [6:0] */
+
+/*
+ * R1629 (0x65D) - EQRMIX Input 3 Volume
+ */
+#define WM2200_EQRMIX_VOL3_MASK 0x00FE /* EQRMIX_VOL3 - [7:1] */
+#define WM2200_EQRMIX_VOL3_SHIFT 1 /* EQRMIX_VOL3 - [7:1] */
+#define WM2200_EQRMIX_VOL3_WIDTH 7 /* EQRMIX_VOL3 - [7:1] */
+
+/*
+ * R1630 (0x65E) - EQRMIX Input 4 Source
+ */
+#define WM2200_EQRMIX_SRC4_MASK 0x007F /* EQRMIX_SRC4 - [6:0] */
+#define WM2200_EQRMIX_SRC4_SHIFT 0 /* EQRMIX_SRC4 - [6:0] */
+#define WM2200_EQRMIX_SRC4_WIDTH 7 /* EQRMIX_SRC4 - [6:0] */
+
+/*
+ * R1631 (0x65F) - EQRMIX Input 4 Volume
+ */
+#define WM2200_EQRMIX_VOL4_MASK 0x00FE /* EQRMIX_VOL4 - [7:1] */
+#define WM2200_EQRMIX_VOL4_SHIFT 1 /* EQRMIX_VOL4 - [7:1] */
+#define WM2200_EQRMIX_VOL4_WIDTH 7 /* EQRMIX_VOL4 - [7:1] */
+
+/*
+ * R1632 (0x660) - LHPF1MIX Input 1 Source
+ */
+#define WM2200_LHPF1MIX_SRC1_MASK 0x007F /* LHPF1MIX_SRC1 - [6:0] */
+#define WM2200_LHPF1MIX_SRC1_SHIFT 0 /* LHPF1MIX_SRC1 - [6:0] */
+#define WM2200_LHPF1MIX_SRC1_WIDTH 7 /* LHPF1MIX_SRC1 - [6:0] */
+
+/*
+ * R1633 (0x661) - LHPF1MIX Input 1 Volume
+ */
+#define WM2200_LHPF1MIX_VOL1_MASK 0x00FE /* LHPF1MIX_VOL1 - [7:1] */
+#define WM2200_LHPF1MIX_VOL1_SHIFT 1 /* LHPF1MIX_VOL1 - [7:1] */
+#define WM2200_LHPF1MIX_VOL1_WIDTH 7 /* LHPF1MIX_VOL1 - [7:1] */
+
+/*
+ * R1634 (0x662) - LHPF1MIX Input 2 Source
+ */
+#define WM2200_LHPF1MIX_SRC2_MASK 0x007F /* LHPF1MIX_SRC2 - [6:0] */
+#define WM2200_LHPF1MIX_SRC2_SHIFT 0 /* LHPF1MIX_SRC2 - [6:0] */
+#define WM2200_LHPF1MIX_SRC2_WIDTH 7 /* LHPF1MIX_SRC2 - [6:0] */
+
+/*
+ * R1635 (0x663) - LHPF1MIX Input 2 Volume
+ */
+#define WM2200_LHPF1MIX_VOL2_MASK 0x00FE /* LHPF1MIX_VOL2 - [7:1] */
+#define WM2200_LHPF1MIX_VOL2_SHIFT 1 /* LHPF1MIX_VOL2 - [7:1] */
+#define WM2200_LHPF1MIX_VOL2_WIDTH 7 /* LHPF1MIX_VOL2 - [7:1] */
+
+/*
+ * R1636 (0x664) - LHPF1MIX Input 3 Source
+ */
+#define WM2200_LHPF1MIX_SRC3_MASK 0x007F /* LHPF1MIX_SRC3 - [6:0] */
+#define WM2200_LHPF1MIX_SRC3_SHIFT 0 /* LHPF1MIX_SRC3 - [6:0] */
+#define WM2200_LHPF1MIX_SRC3_WIDTH 7 /* LHPF1MIX_SRC3 - [6:0] */
+
+/*
+ * R1637 (0x665) - LHPF1MIX Input 3 Volume
+ */
+#define WM2200_LHPF1MIX_VOL3_MASK 0x00FE /* LHPF1MIX_VOL3 - [7:1] */
+#define WM2200_LHPF1MIX_VOL3_SHIFT 1 /* LHPF1MIX_VOL3 - [7:1] */
+#define WM2200_LHPF1MIX_VOL3_WIDTH 7 /* LHPF1MIX_VOL3 - [7:1] */
+
+/*
+ * R1638 (0x666) - LHPF1MIX Input 4 Source
+ */
+#define WM2200_LHPF1MIX_SRC4_MASK 0x007F /* LHPF1MIX_SRC4 - [6:0] */
+#define WM2200_LHPF1MIX_SRC4_SHIFT 0 /* LHPF1MIX_SRC4 - [6:0] */
+#define WM2200_LHPF1MIX_SRC4_WIDTH 7 /* LHPF1MIX_SRC4 - [6:0] */
+
+/*
+ * R1639 (0x667) - LHPF1MIX Input 4 Volume
+ */
+#define WM2200_LHPF1MIX_VOL4_MASK 0x00FE /* LHPF1MIX_VOL4 - [7:1] */
+#define WM2200_LHPF1MIX_VOL4_SHIFT 1 /* LHPF1MIX_VOL4 - [7:1] */
+#define WM2200_LHPF1MIX_VOL4_WIDTH 7 /* LHPF1MIX_VOL4 - [7:1] */
+
+/*
+ * R1640 (0x668) - LHPF2MIX Input 1 Source
+ */
+#define WM2200_LHPF2MIX_SRC1_MASK 0x007F /* LHPF2MIX_SRC1 - [6:0] */
+#define WM2200_LHPF2MIX_SRC1_SHIFT 0 /* LHPF2MIX_SRC1 - [6:0] */
+#define WM2200_LHPF2MIX_SRC1_WIDTH 7 /* LHPF2MIX_SRC1 - [6:0] */
+
+/*
+ * R1641 (0x669) - LHPF2MIX Input 1 Volume
+ */
+#define WM2200_LHPF2MIX_VOL1_MASK 0x00FE /* LHPF2MIX_VOL1 - [7:1] */
+#define WM2200_LHPF2MIX_VOL1_SHIFT 1 /* LHPF2MIX_VOL1 - [7:1] */
+#define WM2200_LHPF2MIX_VOL1_WIDTH 7 /* LHPF2MIX_VOL1 - [7:1] */
+
+/*
+ * R1642 (0x66A) - LHPF2MIX Input 2 Source
+ */
+#define WM2200_LHPF2MIX_SRC2_MASK 0x007F /* LHPF2MIX_SRC2 - [6:0] */
+#define WM2200_LHPF2MIX_SRC2_SHIFT 0 /* LHPF2MIX_SRC2 - [6:0] */
+#define WM2200_LHPF2MIX_SRC2_WIDTH 7 /* LHPF2MIX_SRC2 - [6:0] */
+
+/*
+ * R1643 (0x66B) - LHPF2MIX Input 2 Volume
+ */
+#define WM2200_LHPF2MIX_VOL2_MASK 0x00FE /* LHPF2MIX_VOL2 - [7:1] */
+#define WM2200_LHPF2MIX_VOL2_SHIFT 1 /* LHPF2MIX_VOL2 - [7:1] */
+#define WM2200_LHPF2MIX_VOL2_WIDTH 7 /* LHPF2MIX_VOL2 - [7:1] */
+
+/*
+ * R1644 (0x66C) - LHPF2MIX Input 3 Source
+ */
+#define WM2200_LHPF2MIX_SRC3_MASK 0x007F /* LHPF2MIX_SRC3 - [6:0] */
+#define WM2200_LHPF2MIX_SRC3_SHIFT 0 /* LHPF2MIX_SRC3 - [6:0] */
+#define WM2200_LHPF2MIX_SRC3_WIDTH 7 /* LHPF2MIX_SRC3 - [6:0] */
+
+/*
+ * R1645 (0x66D) - LHPF2MIX Input 3 Volume
+ */
+#define WM2200_LHPF2MIX_VOL3_MASK 0x00FE /* LHPF2MIX_VOL3 - [7:1] */
+#define WM2200_LHPF2MIX_VOL3_SHIFT 1 /* LHPF2MIX_VOL3 - [7:1] */
+#define WM2200_LHPF2MIX_VOL3_WIDTH 7 /* LHPF2MIX_VOL3 - [7:1] */
+
+/*
+ * R1646 (0x66E) - LHPF2MIX Input 4 Source
+ */
+#define WM2200_LHPF2MIX_SRC4_MASK 0x007F /* LHPF2MIX_SRC4 - [6:0] */
+#define WM2200_LHPF2MIX_SRC4_SHIFT 0 /* LHPF2MIX_SRC4 - [6:0] */
+#define WM2200_LHPF2MIX_SRC4_WIDTH 7 /* LHPF2MIX_SRC4 - [6:0] */
+
+/*
+ * R1647 (0x66F) - LHPF2MIX Input 4 Volume
+ */
+#define WM2200_LHPF2MIX_VOL4_MASK 0x00FE /* LHPF2MIX_VOL4 - [7:1] */
+#define WM2200_LHPF2MIX_VOL4_SHIFT 1 /* LHPF2MIX_VOL4 - [7:1] */
+#define WM2200_LHPF2MIX_VOL4_WIDTH 7 /* LHPF2MIX_VOL4 - [7:1] */
+
+/*
+ * R1648 (0x670) - DSP1LMIX Input 1 Source
+ */
+#define WM2200_DSP1LMIX_SRC1_MASK 0x007F /* DSP1LMIX_SRC1 - [6:0] */
+#define WM2200_DSP1LMIX_SRC1_SHIFT 0 /* DSP1LMIX_SRC1 - [6:0] */
+#define WM2200_DSP1LMIX_SRC1_WIDTH 7 /* DSP1LMIX_SRC1 - [6:0] */
+
+/*
+ * R1649 (0x671) - DSP1LMIX Input 1 Volume
+ */
+#define WM2200_DSP1LMIX_VOL1_MASK 0x00FE /* DSP1LMIX_VOL1 - [7:1] */
+#define WM2200_DSP1LMIX_VOL1_SHIFT 1 /* DSP1LMIX_VOL1 - [7:1] */
+#define WM2200_DSP1LMIX_VOL1_WIDTH 7 /* DSP1LMIX_VOL1 - [7:1] */
+
+/*
+ * R1650 (0x672) - DSP1LMIX Input 2 Source
+ */
+#define WM2200_DSP1LMIX_SRC2_MASK 0x007F /* DSP1LMIX_SRC2 - [6:0] */
+#define WM2200_DSP1LMIX_SRC2_SHIFT 0 /* DSP1LMIX_SRC2 - [6:0] */
+#define WM2200_DSP1LMIX_SRC2_WIDTH 7 /* DSP1LMIX_SRC2 - [6:0] */
+
+/*
+ * R1651 (0x673) - DSP1LMIX Input 2 Volume
+ */
+#define WM2200_DSP1LMIX_VOL2_MASK 0x00FE /* DSP1LMIX_VOL2 - [7:1] */
+#define WM2200_DSP1LMIX_VOL2_SHIFT 1 /* DSP1LMIX_VOL2 - [7:1] */
+#define WM2200_DSP1LMIX_VOL2_WIDTH 7 /* DSP1LMIX_VOL2 - [7:1] */
+
+/*
+ * R1652 (0x674) - DSP1LMIX Input 3 Source
+ */
+#define WM2200_DSP1LMIX_SRC3_MASK 0x007F /* DSP1LMIX_SRC3 - [6:0] */
+#define WM2200_DSP1LMIX_SRC3_SHIFT 0 /* DSP1LMIX_SRC3 - [6:0] */
+#define WM2200_DSP1LMIX_SRC3_WIDTH 7 /* DSP1LMIX_SRC3 - [6:0] */
+
+/*
+ * R1653 (0x675) - DSP1LMIX Input 3 Volume
+ */
+#define WM2200_DSP1LMIX_VOL3_MASK 0x00FE /* DSP1LMIX_VOL3 - [7:1] */
+#define WM2200_DSP1LMIX_VOL3_SHIFT 1 /* DSP1LMIX_VOL3 - [7:1] */
+#define WM2200_DSP1LMIX_VOL3_WIDTH 7 /* DSP1LMIX_VOL3 - [7:1] */
+
+/*
+ * R1654 (0x676) - DSP1LMIX Input 4 Source
+ */
+#define WM2200_DSP1LMIX_SRC4_MASK 0x007F /* DSP1LMIX_SRC4 - [6:0] */
+#define WM2200_DSP1LMIX_SRC4_SHIFT 0 /* DSP1LMIX_SRC4 - [6:0] */
+#define WM2200_DSP1LMIX_SRC4_WIDTH 7 /* DSP1LMIX_SRC4 - [6:0] */
+
+/*
+ * R1655 (0x677) - DSP1LMIX Input 4 Volume
+ */
+#define WM2200_DSP1LMIX_VOL4_MASK 0x00FE /* DSP1LMIX_VOL4 - [7:1] */
+#define WM2200_DSP1LMIX_VOL4_SHIFT 1 /* DSP1LMIX_VOL4 - [7:1] */
+#define WM2200_DSP1LMIX_VOL4_WIDTH 7 /* DSP1LMIX_VOL4 - [7:1] */
+
+/*
+ * R1656 (0x678) - DSP1RMIX Input 1 Source
+ */
+#define WM2200_DSP1RMIX_SRC1_MASK 0x007F /* DSP1RMIX_SRC1 - [6:0] */
+#define WM2200_DSP1RMIX_SRC1_SHIFT 0 /* DSP1RMIX_SRC1 - [6:0] */
+#define WM2200_DSP1RMIX_SRC1_WIDTH 7 /* DSP1RMIX_SRC1 - [6:0] */
+
+/*
+ * R1657 (0x679) - DSP1RMIX Input 1 Volume
+ */
+#define WM2200_DSP1RMIX_VOL1_MASK 0x00FE /* DSP1RMIX_VOL1 - [7:1] */
+#define WM2200_DSP1RMIX_VOL1_SHIFT 1 /* DSP1RMIX_VOL1 - [7:1] */
+#define WM2200_DSP1RMIX_VOL1_WIDTH 7 /* DSP1RMIX_VOL1 - [7:1] */
+
+/*
+ * R1658 (0x67A) - DSP1RMIX Input 2 Source
+ */
+#define WM2200_DSP1RMIX_SRC2_MASK 0x007F /* DSP1RMIX_SRC2 - [6:0] */
+#define WM2200_DSP1RMIX_SRC2_SHIFT 0 /* DSP1RMIX_SRC2 - [6:0] */
+#define WM2200_DSP1RMIX_SRC2_WIDTH 7 /* DSP1RMIX_SRC2 - [6:0] */
+
+/*
+ * R1659 (0x67B) - DSP1RMIX Input 2 Volume
+ */
+#define WM2200_DSP1RMIX_VOL2_MASK 0x00FE /* DSP1RMIX_VOL2 - [7:1] */
+#define WM2200_DSP1RMIX_VOL2_SHIFT 1 /* DSP1RMIX_VOL2 - [7:1] */
+#define WM2200_DSP1RMIX_VOL2_WIDTH 7 /* DSP1RMIX_VOL2 - [7:1] */
+
+/*
+ * R1660 (0x67C) - DSP1RMIX Input 3 Source
+ */
+#define WM2200_DSP1RMIX_SRC3_MASK 0x007F /* DSP1RMIX_SRC3 - [6:0] */
+#define WM2200_DSP1RMIX_SRC3_SHIFT 0 /* DSP1RMIX_SRC3 - [6:0] */
+#define WM2200_DSP1RMIX_SRC3_WIDTH 7 /* DSP1RMIX_SRC3 - [6:0] */
+
+/*
+ * R1661 (0x67D) - DSP1RMIX Input 3 Volume
+ */
+#define WM2200_DSP1RMIX_VOL3_MASK 0x00FE /* DSP1RMIX_VOL3 - [7:1] */
+#define WM2200_DSP1RMIX_VOL3_SHIFT 1 /* DSP1RMIX_VOL3 - [7:1] */
+#define WM2200_DSP1RMIX_VOL3_WIDTH 7 /* DSP1RMIX_VOL3 - [7:1] */
+
+/*
+ * R1662 (0x67E) - DSP1RMIX Input 4 Source
+ */
+#define WM2200_DSP1RMIX_SRC4_MASK 0x007F /* DSP1RMIX_SRC4 - [6:0] */
+#define WM2200_DSP1RMIX_SRC4_SHIFT 0 /* DSP1RMIX_SRC4 - [6:0] */
+#define WM2200_DSP1RMIX_SRC4_WIDTH 7 /* DSP1RMIX_SRC4 - [6:0] */
+
+/*
+ * R1663 (0x67F) - DSP1RMIX Input 4 Volume
+ */
+#define WM2200_DSP1RMIX_VOL4_MASK 0x00FE /* DSP1RMIX_VOL4 - [7:1] */
+#define WM2200_DSP1RMIX_VOL4_SHIFT 1 /* DSP1RMIX_VOL4 - [7:1] */
+#define WM2200_DSP1RMIX_VOL4_WIDTH 7 /* DSP1RMIX_VOL4 - [7:1] */
+
+/*
+ * R1664 (0x680) - DSP1AUX1MIX Input 1 Source
+ */
+#define WM2200_DSP1AUX1MIX_SRC1_MASK 0x007F /* DSP1AUX1MIX_SRC1 - [6:0] */
+#define WM2200_DSP1AUX1MIX_SRC1_SHIFT 0 /* DSP1AUX1MIX_SRC1 - [6:0] */
+#define WM2200_DSP1AUX1MIX_SRC1_WIDTH 7 /* DSP1AUX1MIX_SRC1 - [6:0] */
+
+/*
+ * R1665 (0x681) - DSP1AUX2MIX Input 1 Source
+ */
+#define WM2200_DSP1AUX2MIX_SRC1_MASK 0x007F /* DSP1AUX2MIX_SRC1 - [6:0] */
+#define WM2200_DSP1AUX2MIX_SRC1_SHIFT 0 /* DSP1AUX2MIX_SRC1 - [6:0] */
+#define WM2200_DSP1AUX2MIX_SRC1_WIDTH 7 /* DSP1AUX2MIX_SRC1 - [6:0] */
+
+/*
+ * R1666 (0x682) - DSP1AUX3MIX Input 1 Source
+ */
+#define WM2200_DSP1AUX3MIX_SRC1_MASK 0x007F /* DSP1AUX3MIX_SRC1 - [6:0] */
+#define WM2200_DSP1AUX3MIX_SRC1_SHIFT 0 /* DSP1AUX3MIX_SRC1 - [6:0] */
+#define WM2200_DSP1AUX3MIX_SRC1_WIDTH 7 /* DSP1AUX3MIX_SRC1 - [6:0] */
+
+/*
+ * R1667 (0x683) - DSP1AUX4MIX Input 1 Source
+ */
+#define WM2200_DSP1AUX4MIX_SRC1_MASK 0x007F /* DSP1AUX4MIX_SRC1 - [6:0] */
+#define WM2200_DSP1AUX4MIX_SRC1_SHIFT 0 /* DSP1AUX4MIX_SRC1 - [6:0] */
+#define WM2200_DSP1AUX4MIX_SRC1_WIDTH 7 /* DSP1AUX4MIX_SRC1 - [6:0] */
+
+/*
+ * R1668 (0x684) - DSP1AUX5MIX Input 1 Source
+ */
+#define WM2200_DSP1AUX5MIX_SRC1_MASK 0x007F /* DSP1AUX5MIX_SRC1 - [6:0] */
+#define WM2200_DSP1AUX5MIX_SRC1_SHIFT 0 /* DSP1AUX5MIX_SRC1 - [6:0] */
+#define WM2200_DSP1AUX5MIX_SRC1_WIDTH 7 /* DSP1AUX5MIX_SRC1 - [6:0] */
+
+/*
+ * R1669 (0x685) - DSP1AUX6MIX Input 1 Source
+ */
+#define WM2200_DSP1AUX6MIX_SRC1_MASK 0x007F /* DSP1AUX6MIX_SRC1 - [6:0] */
+#define WM2200_DSP1AUX6MIX_SRC1_SHIFT 0 /* DSP1AUX6MIX_SRC1 - [6:0] */
+#define WM2200_DSP1AUX6MIX_SRC1_WIDTH 7 /* DSP1AUX6MIX_SRC1 - [6:0] */
+
+/*
+ * R1670 (0x686) - DSP2LMIX Input 1 Source
+ */
+#define WM2200_DSP2LMIX_SRC1_MASK 0x007F /* DSP2LMIX_SRC1 - [6:0] */
+#define WM2200_DSP2LMIX_SRC1_SHIFT 0 /* DSP2LMIX_SRC1 - [6:0] */
+#define WM2200_DSP2LMIX_SRC1_WIDTH 7 /* DSP2LMIX_SRC1 - [6:0] */
+
+/*
+ * R1671 (0x687) - DSP2LMIX Input 1 Volume
+ */
+#define WM2200_DSP2LMIX_VOL1_MASK 0x00FE /* DSP2LMIX_VOL1 - [7:1] */
+#define WM2200_DSP2LMIX_VOL1_SHIFT 1 /* DSP2LMIX_VOL1 - [7:1] */
+#define WM2200_DSP2LMIX_VOL1_WIDTH 7 /* DSP2LMIX_VOL1 - [7:1] */
+
+/*
+ * R1672 (0x688) - DSP2LMIX Input 2 Source
+ */
+#define WM2200_DSP2LMIX_SRC2_MASK 0x007F /* DSP2LMIX_SRC2 - [6:0] */
+#define WM2200_DSP2LMIX_SRC2_SHIFT 0 /* DSP2LMIX_SRC2 - [6:0] */
+#define WM2200_DSP2LMIX_SRC2_WIDTH 7 /* DSP2LMIX_SRC2 - [6:0] */
+
+/*
+ * R1673 (0x689) - DSP2LMIX Input 2 Volume
+ */
+#define WM2200_DSP2LMIX_VOL2_MASK 0x00FE /* DSP2LMIX_VOL2 - [7:1] */
+#define WM2200_DSP2LMIX_VOL2_SHIFT 1 /* DSP2LMIX_VOL2 - [7:1] */
+#define WM2200_DSP2LMIX_VOL2_WIDTH 7 /* DSP2LMIX_VOL2 - [7:1] */
+
+/*
+ * R1674 (0x68A) - DSP2LMIX Input 3 Source
+ */
+#define WM2200_DSP2LMIX_SRC3_MASK 0x007F /* DSP2LMIX_SRC3 - [6:0] */
+#define WM2200_DSP2LMIX_SRC3_SHIFT 0 /* DSP2LMIX_SRC3 - [6:0] */
+#define WM2200_DSP2LMIX_SRC3_WIDTH 7 /* DSP2LMIX_SRC3 - [6:0] */
+
+/*
+ * R1675 (0x68B) - DSP2LMIX Input 3 Volume
+ */
+#define WM2200_DSP2LMIX_VOL3_MASK 0x00FE /* DSP2LMIX_VOL3 - [7:1] */
+#define WM2200_DSP2LMIX_VOL3_SHIFT 1 /* DSP2LMIX_VOL3 - [7:1] */
+#define WM2200_DSP2LMIX_VOL3_WIDTH 7 /* DSP2LMIX_VOL3 - [7:1] */
+
+/*
+ * R1676 (0x68C) - DSP2LMIX Input 4 Source
+ */
+#define WM2200_DSP2LMIX_SRC4_MASK 0x007F /* DSP2LMIX_SRC4 - [6:0] */
+#define WM2200_DSP2LMIX_SRC4_SHIFT 0 /* DSP2LMIX_SRC4 - [6:0] */
+#define WM2200_DSP2LMIX_SRC4_WIDTH 7 /* DSP2LMIX_SRC4 - [6:0] */
+
+/*
+ * R1677 (0x68D) - DSP2LMIX Input 4 Volume
+ */
+#define WM2200_DSP2LMIX_VOL4_MASK 0x00FE /* DSP2LMIX_VOL4 - [7:1] */
+#define WM2200_DSP2LMIX_VOL4_SHIFT 1 /* DSP2LMIX_VOL4 - [7:1] */
+#define WM2200_DSP2LMIX_VOL4_WIDTH 7 /* DSP2LMIX_VOL4 - [7:1] */
+
+/*
+ * R1678 (0x68E) - DSP2RMIX Input 1 Source
+ */
+#define WM2200_DSP2RMIX_SRC1_MASK 0x007F /* DSP2RMIX_SRC1 - [6:0] */
+#define WM2200_DSP2RMIX_SRC1_SHIFT 0 /* DSP2RMIX_SRC1 - [6:0] */
+#define WM2200_DSP2RMIX_SRC1_WIDTH 7 /* DSP2RMIX_SRC1 - [6:0] */
+
+/*
+ * R1679 (0x68F) - DSP2RMIX Input 1 Volume
+ */
+#define WM2200_DSP2RMIX_VOL1_MASK 0x00FE /* DSP2RMIX_VOL1 - [7:1] */
+#define WM2200_DSP2RMIX_VOL1_SHIFT 1 /* DSP2RMIX_VOL1 - [7:1] */
+#define WM2200_DSP2RMIX_VOL1_WIDTH 7 /* DSP2RMIX_VOL1 - [7:1] */
+
+/*
+ * R1680 (0x690) - DSP2RMIX Input 2 Source
+ */
+#define WM2200_DSP2RMIX_SRC2_MASK 0x007F /* DSP2RMIX_SRC2 - [6:0] */
+#define WM2200_DSP2RMIX_SRC2_SHIFT 0 /* DSP2RMIX_SRC2 - [6:0] */
+#define WM2200_DSP2RMIX_SRC2_WIDTH 7 /* DSP2RMIX_SRC2 - [6:0] */
+
+/*
+ * R1681 (0x691) - DSP2RMIX Input 2 Volume
+ */
+#define WM2200_DSP2RMIX_VOL2_MASK 0x00FE /* DSP2RMIX_VOL2 - [7:1] */
+#define WM2200_DSP2RMIX_VOL2_SHIFT 1 /* DSP2RMIX_VOL2 - [7:1] */
+#define WM2200_DSP2RMIX_VOL2_WIDTH 7 /* DSP2RMIX_VOL2 - [7:1] */
+
+/*
+ * R1682 (0x692) - DSP2RMIX Input 3 Source
+ */
+#define WM2200_DSP2RMIX_SRC3_MASK 0x007F /* DSP2RMIX_SRC3 - [6:0] */
+#define WM2200_DSP2RMIX_SRC3_SHIFT 0 /* DSP2RMIX_SRC3 - [6:0] */
+#define WM2200_DSP2RMIX_SRC3_WIDTH 7 /* DSP2RMIX_SRC3 - [6:0] */
+
+/*
+ * R1683 (0x693) - DSP2RMIX Input 3 Volume
+ */
+#define WM2200_DSP2RMIX_VOL3_MASK 0x00FE /* DSP2RMIX_VOL3 - [7:1] */
+#define WM2200_DSP2RMIX_VOL3_SHIFT 1 /* DSP2RMIX_VOL3 - [7:1] */
+#define WM2200_DSP2RMIX_VOL3_WIDTH 7 /* DSP2RMIX_VOL3 - [7:1] */
+
+/*
+ * R1684 (0x694) - DSP2RMIX Input 4 Source
+ */
+#define WM2200_DSP2RMIX_SRC4_MASK 0x007F /* DSP2RMIX_SRC4 - [6:0] */
+#define WM2200_DSP2RMIX_SRC4_SHIFT 0 /* DSP2RMIX_SRC4 - [6:0] */
+#define WM2200_DSP2RMIX_SRC4_WIDTH 7 /* DSP2RMIX_SRC4 - [6:0] */
+
+/*
+ * R1685 (0x695) - DSP2RMIX Input 4 Volume
+ */
+#define WM2200_DSP2RMIX_VOL4_MASK 0x00FE /* DSP2RMIX_VOL4 - [7:1] */
+#define WM2200_DSP2RMIX_VOL4_SHIFT 1 /* DSP2RMIX_VOL4 - [7:1] */
+#define WM2200_DSP2RMIX_VOL4_WIDTH 7 /* DSP2RMIX_VOL4 - [7:1] */
+
+/*
+ * R1686 (0x696) - DSP2AUX1MIX Input 1 Source
+ */
+#define WM2200_DSP2AUX1MIX_SRC1_MASK 0x007F /* DSP2AUX1MIX_SRC1 - [6:0] */
+#define WM2200_DSP2AUX1MIX_SRC1_SHIFT 0 /* DSP2AUX1MIX_SRC1 - [6:0] */
+#define WM2200_DSP2AUX1MIX_SRC1_WIDTH 7 /* DSP2AUX1MIX_SRC1 - [6:0] */
+
+/*
+ * R1687 (0x697) - DSP2AUX2MIX Input 1 Source
+ */
+#define WM2200_DSP2AUX2MIX_SRC1_MASK 0x007F /* DSP2AUX2MIX_SRC1 - [6:0] */
+#define WM2200_DSP2AUX2MIX_SRC1_SHIFT 0 /* DSP2AUX2MIX_SRC1 - [6:0] */
+#define WM2200_DSP2AUX2MIX_SRC1_WIDTH 7 /* DSP2AUX2MIX_SRC1 - [6:0] */
+
+/*
+ * R1688 (0x698) - DSP2AUX3MIX Input 1 Source
+ */
+#define WM2200_DSP2AUX3MIX_SRC1_MASK 0x007F /* DSP2AUX3MIX_SRC1 - [6:0] */
+#define WM2200_DSP2AUX3MIX_SRC1_SHIFT 0 /* DSP2AUX3MIX_SRC1 - [6:0] */
+#define WM2200_DSP2AUX3MIX_SRC1_WIDTH 7 /* DSP2AUX3MIX_SRC1 - [6:0] */
+
+/*
+ * R1689 (0x699) - DSP2AUX4MIX Input 1 Source
+ */
+#define WM2200_DSP2AUX4MIX_SRC1_MASK 0x007F /* DSP2AUX4MIX_SRC1 - [6:0] */
+#define WM2200_DSP2AUX4MIX_SRC1_SHIFT 0 /* DSP2AUX4MIX_SRC1 - [6:0] */
+#define WM2200_DSP2AUX4MIX_SRC1_WIDTH 7 /* DSP2AUX4MIX_SRC1 - [6:0] */
+
+/*
+ * R1690 (0x69A) - DSP2AUX5MIX Input 1 Source
+ */
+#define WM2200_DSP2AUX5MIX_SRC1_MASK 0x007F /* DSP2AUX5MIX_SRC1 - [6:0] */
+#define WM2200_DSP2AUX5MIX_SRC1_SHIFT 0 /* DSP2AUX5MIX_SRC1 - [6:0] */
+#define WM2200_DSP2AUX5MIX_SRC1_WIDTH 7 /* DSP2AUX5MIX_SRC1 - [6:0] */
+
+/*
+ * R1691 (0x69B) - DSP2AUX6MIX Input 1 Source
+ */
+#define WM2200_DSP2AUX6MIX_SRC1_MASK 0x007F /* DSP2AUX6MIX_SRC1 - [6:0] */
+#define WM2200_DSP2AUX6MIX_SRC1_SHIFT 0 /* DSP2AUX6MIX_SRC1 - [6:0] */
+#define WM2200_DSP2AUX6MIX_SRC1_WIDTH 7 /* DSP2AUX6MIX_SRC1 - [6:0] */
+
+/*
+ * R1792 (0x700) - GPIO CTRL 1
+ */
+#define WM2200_GP1_DIR 0x8000 /* GP1_DIR */
+#define WM2200_GP1_DIR_MASK 0x8000 /* GP1_DIR */
+#define WM2200_GP1_DIR_SHIFT 15 /* GP1_DIR */
+#define WM2200_GP1_DIR_WIDTH 1 /* GP1_DIR */
+#define WM2200_GP1_PU 0x4000 /* GP1_PU */
+#define WM2200_GP1_PU_MASK 0x4000 /* GP1_PU */
+#define WM2200_GP1_PU_SHIFT 14 /* GP1_PU */
+#define WM2200_GP1_PU_WIDTH 1 /* GP1_PU */
+#define WM2200_GP1_PD 0x2000 /* GP1_PD */
+#define WM2200_GP1_PD_MASK 0x2000 /* GP1_PD */
+#define WM2200_GP1_PD_SHIFT 13 /* GP1_PD */
+#define WM2200_GP1_PD_WIDTH 1 /* GP1_PD */
+#define WM2200_GP1_POL 0x0400 /* GP1_POL */
+#define WM2200_GP1_POL_MASK 0x0400 /* GP1_POL */
+#define WM2200_GP1_POL_SHIFT 10 /* GP1_POL */
+#define WM2200_GP1_POL_WIDTH 1 /* GP1_POL */
+#define WM2200_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */
+#define WM2200_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */
+#define WM2200_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */
+#define WM2200_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */
+#define WM2200_GP1_DB 0x0100 /* GP1_DB */
+#define WM2200_GP1_DB_MASK 0x0100 /* GP1_DB */
+#define WM2200_GP1_DB_SHIFT 8 /* GP1_DB */
+#define WM2200_GP1_DB_WIDTH 1 /* GP1_DB */
+#define WM2200_GP1_LVL 0x0040 /* GP1_LVL */
+#define WM2200_GP1_LVL_MASK 0x0040 /* GP1_LVL */
+#define WM2200_GP1_LVL_SHIFT 6 /* GP1_LVL */
+#define WM2200_GP1_LVL_WIDTH 1 /* GP1_LVL */
+#define WM2200_GP1_FN_MASK 0x003F /* GP1_FN - [5:0] */
+#define WM2200_GP1_FN_SHIFT 0 /* GP1_FN - [5:0] */
+#define WM2200_GP1_FN_WIDTH 6 /* GP1_FN - [5:0] */
+
+/*
+ * R1793 (0x701) - GPIO CTRL 2
+ */
+#define WM2200_GP2_DIR 0x8000 /* GP2_DIR */
+#define WM2200_GP2_DIR_MASK 0x8000 /* GP2_DIR */
+#define WM2200_GP2_DIR_SHIFT 15 /* GP2_DIR */
+#define WM2200_GP2_DIR_WIDTH 1 /* GP2_DIR */
+#define WM2200_GP2_PU 0x4000 /* GP2_PU */
+#define WM2200_GP2_PU_MASK 0x4000 /* GP2_PU */
+#define WM2200_GP2_PU_SHIFT 14 /* GP2_PU */
+#define WM2200_GP2_PU_WIDTH 1 /* GP2_PU */
+#define WM2200_GP2_PD 0x2000 /* GP2_PD */
+#define WM2200_GP2_PD_MASK 0x2000 /* GP2_PD */
+#define WM2200_GP2_PD_SHIFT 13 /* GP2_PD */
+#define WM2200_GP2_PD_WIDTH 1 /* GP2_PD */
+#define WM2200_GP2_POL 0x0400 /* GP2_POL */
+#define WM2200_GP2_POL_MASK 0x0400 /* GP2_POL */
+#define WM2200_GP2_POL_SHIFT 10 /* GP2_POL */
+#define WM2200_GP2_POL_WIDTH 1 /* GP2_POL */
+#define WM2200_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */
+#define WM2200_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */
+#define WM2200_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */
+#define WM2200_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */
+#define WM2200_GP2_DB 0x0100 /* GP2_DB */
+#define WM2200_GP2_DB_MASK 0x0100 /* GP2_DB */
+#define WM2200_GP2_DB_SHIFT 8 /* GP2_DB */
+#define WM2200_GP2_DB_WIDTH 1 /* GP2_DB */
+#define WM2200_GP2_LVL 0x0040 /* GP2_LVL */
+#define WM2200_GP2_LVL_MASK 0x0040 /* GP2_LVL */
+#define WM2200_GP2_LVL_SHIFT 6 /* GP2_LVL */
+#define WM2200_GP2_LVL_WIDTH 1 /* GP2_LVL */
+#define WM2200_GP2_FN_MASK 0x003F /* GP2_FN - [5:0] */
+#define WM2200_GP2_FN_SHIFT 0 /* GP2_FN - [5:0] */
+#define WM2200_GP2_FN_WIDTH 6 /* GP2_FN - [5:0] */
+
+/*
+ * R1794 (0x702) - GPIO CTRL 3
+ */
+#define WM2200_GP3_DIR 0x8000 /* GP3_DIR */
+#define WM2200_GP3_DIR_MASK 0x8000 /* GP3_DIR */
+#define WM2200_GP3_DIR_SHIFT 15 /* GP3_DIR */
+#define WM2200_GP3_DIR_WIDTH 1 /* GP3_DIR */
+#define WM2200_GP3_PU 0x4000 /* GP3_PU */
+#define WM2200_GP3_PU_MASK 0x4000 /* GP3_PU */
+#define WM2200_GP3_PU_SHIFT 14 /* GP3_PU */
+#define WM2200_GP3_PU_WIDTH 1 /* GP3_PU */
+#define WM2200_GP3_PD 0x2000 /* GP3_PD */
+#define WM2200_GP3_PD_MASK 0x2000 /* GP3_PD */
+#define WM2200_GP3_PD_SHIFT 13 /* GP3_PD */
+#define WM2200_GP3_PD_WIDTH 1 /* GP3_PD */
+#define WM2200_GP3_POL 0x0400 /* GP3_POL */
+#define WM2200_GP3_POL_MASK 0x0400 /* GP3_POL */
+#define WM2200_GP3_POL_SHIFT 10 /* GP3_POL */
+#define WM2200_GP3_POL_WIDTH 1 /* GP3_POL */
+#define WM2200_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */
+#define WM2200_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */
+#define WM2200_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */
+#define WM2200_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */
+#define WM2200_GP3_DB 0x0100 /* GP3_DB */
+#define WM2200_GP3_DB_MASK 0x0100 /* GP3_DB */
+#define WM2200_GP3_DB_SHIFT 8 /* GP3_DB */
+#define WM2200_GP3_DB_WIDTH 1 /* GP3_DB */
+#define WM2200_GP3_LVL 0x0040 /* GP3_LVL */
+#define WM2200_GP3_LVL_MASK 0x0040 /* GP3_LVL */
+#define WM2200_GP3_LVL_SHIFT 6 /* GP3_LVL */
+#define WM2200_GP3_LVL_WIDTH 1 /* GP3_LVL */
+#define WM2200_GP3_FN_MASK 0x003F /* GP3_FN - [5:0] */
+#define WM2200_GP3_FN_SHIFT 0 /* GP3_FN - [5:0] */
+#define WM2200_GP3_FN_WIDTH 6 /* GP3_FN - [5:0] */
+
+/*
+ * R1795 (0x703) - GPIO CTRL 4
+ */
+#define WM2200_GP4_DIR 0x8000 /* GP4_DIR */
+#define WM2200_GP4_DIR_MASK 0x8000 /* GP4_DIR */
+#define WM2200_GP4_DIR_SHIFT 15 /* GP4_DIR */
+#define WM2200_GP4_DIR_WIDTH 1 /* GP4_DIR */
+#define WM2200_GP4_PU 0x4000 /* GP4_PU */
+#define WM2200_GP4_PU_MASK 0x4000 /* GP4_PU */
+#define WM2200_GP4_PU_SHIFT 14 /* GP4_PU */
+#define WM2200_GP4_PU_WIDTH 1 /* GP4_PU */
+#define WM2200_GP4_PD 0x2000 /* GP4_PD */
+#define WM2200_GP4_PD_MASK 0x2000 /* GP4_PD */
+#define WM2200_GP4_PD_SHIFT 13 /* GP4_PD */
+#define WM2200_GP4_PD_WIDTH 1 /* GP4_PD */
+#define WM2200_GP4_POL 0x0400 /* GP4_POL */
+#define WM2200_GP4_POL_MASK 0x0400 /* GP4_POL */
+#define WM2200_GP4_POL_SHIFT 10 /* GP4_POL */
+#define WM2200_GP4_POL_WIDTH 1 /* GP4_POL */
+#define WM2200_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */
+#define WM2200_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */
+#define WM2200_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */
+#define WM2200_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */
+#define WM2200_GP4_DB 0x0100 /* GP4_DB */
+#define WM2200_GP4_DB_MASK 0x0100 /* GP4_DB */
+#define WM2200_GP4_DB_SHIFT 8 /* GP4_DB */
+#define WM2200_GP4_DB_WIDTH 1 /* GP4_DB */
+#define WM2200_GP4_LVL 0x0040 /* GP4_LVL */
+#define WM2200_GP4_LVL_MASK 0x0040 /* GP4_LVL */
+#define WM2200_GP4_LVL_SHIFT 6 /* GP4_LVL */
+#define WM2200_GP4_LVL_WIDTH 1 /* GP4_LVL */
+#define WM2200_GP4_FN_MASK 0x003F /* GP4_FN - [5:0] */
+#define WM2200_GP4_FN_SHIFT 0 /* GP4_FN - [5:0] */
+#define WM2200_GP4_FN_WIDTH 6 /* GP4_FN - [5:0] */
+
+/*
+ * R1799 (0x707) - ADPS1 IRQ0
+ */
+#define WM2200_DSP_IRQ1 0x0002 /* DSP_IRQ1 */
+#define WM2200_DSP_IRQ1_MASK 0x0002 /* DSP_IRQ1 */
+#define WM2200_DSP_IRQ1_SHIFT 1 /* DSP_IRQ1 */
+#define WM2200_DSP_IRQ1_WIDTH 1 /* DSP_IRQ1 */
+#define WM2200_DSP_IRQ0 0x0001 /* DSP_IRQ0 */
+#define WM2200_DSP_IRQ0_MASK 0x0001 /* DSP_IRQ0 */
+#define WM2200_DSP_IRQ0_SHIFT 0 /* DSP_IRQ0 */
+#define WM2200_DSP_IRQ0_WIDTH 1 /* DSP_IRQ0 */
+
+/*
+ * R1800 (0x708) - ADPS1 IRQ1
+ */
+#define WM2200_DSP_IRQ3 0x0002 /* DSP_IRQ3 */
+#define WM2200_DSP_IRQ3_MASK 0x0002 /* DSP_IRQ3 */
+#define WM2200_DSP_IRQ3_SHIFT 1 /* DSP_IRQ3 */
+#define WM2200_DSP_IRQ3_WIDTH 1 /* DSP_IRQ3 */
+#define WM2200_DSP_IRQ2 0x0001 /* DSP_IRQ2 */
+#define WM2200_DSP_IRQ2_MASK 0x0001 /* DSP_IRQ2 */
+#define WM2200_DSP_IRQ2_SHIFT 0 /* DSP_IRQ2 */
+#define WM2200_DSP_IRQ2_WIDTH 1 /* DSP_IRQ2 */
+
+/*
+ * R1801 (0x709) - Misc Pad Ctrl 1
+ */
+#define WM2200_LDO1ENA_PD 0x8000 /* LDO1ENA_PD */
+#define WM2200_LDO1ENA_PD_MASK 0x8000 /* LDO1ENA_PD */
+#define WM2200_LDO1ENA_PD_SHIFT 15 /* LDO1ENA_PD */
+#define WM2200_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */
+#define WM2200_MCLK2_PD 0x2000 /* MCLK2_PD */
+#define WM2200_MCLK2_PD_MASK 0x2000 /* MCLK2_PD */
+#define WM2200_MCLK2_PD_SHIFT 13 /* MCLK2_PD */
+#define WM2200_MCLK2_PD_WIDTH 1 /* MCLK2_PD */
+#define WM2200_MCLK1_PD 0x1000 /* MCLK1_PD */
+#define WM2200_MCLK1_PD_MASK 0x1000 /* MCLK1_PD */
+#define WM2200_MCLK1_PD_SHIFT 12 /* MCLK1_PD */
+#define WM2200_MCLK1_PD_WIDTH 1 /* MCLK1_PD */
+#define WM2200_DACLRCLK1_PU 0x0400 /* DACLRCLK1_PU */
+#define WM2200_DACLRCLK1_PU_MASK 0x0400 /* DACLRCLK1_PU */
+#define WM2200_DACLRCLK1_PU_SHIFT 10 /* DACLRCLK1_PU */
+#define WM2200_DACLRCLK1_PU_WIDTH 1 /* DACLRCLK1_PU */
+#define WM2200_DACLRCLK1_PD 0x0200 /* DACLRCLK1_PD */
+#define WM2200_DACLRCLK1_PD_MASK 0x0200 /* DACLRCLK1_PD */
+#define WM2200_DACLRCLK1_PD_SHIFT 9 /* DACLRCLK1_PD */
+#define WM2200_DACLRCLK1_PD_WIDTH 1 /* DACLRCLK1_PD */
+#define WM2200_BCLK1_PU 0x0100 /* BCLK1_PU */
+#define WM2200_BCLK1_PU_MASK 0x0100 /* BCLK1_PU */
+#define WM2200_BCLK1_PU_SHIFT 8 /* BCLK1_PU */
+#define WM2200_BCLK1_PU_WIDTH 1 /* BCLK1_PU */
+#define WM2200_BCLK1_PD 0x0080 /* BCLK1_PD */
+#define WM2200_BCLK1_PD_MASK 0x0080 /* BCLK1_PD */
+#define WM2200_BCLK1_PD_SHIFT 7 /* BCLK1_PD */
+#define WM2200_BCLK1_PD_WIDTH 1 /* BCLK1_PD */
+#define WM2200_DACDAT1_PU 0x0040 /* DACDAT1_PU */
+#define WM2200_DACDAT1_PU_MASK 0x0040 /* DACDAT1_PU */
+#define WM2200_DACDAT1_PU_SHIFT 6 /* DACDAT1_PU */
+#define WM2200_DACDAT1_PU_WIDTH 1 /* DACDAT1_PU */
+#define WM2200_DACDAT1_PD 0x0020 /* DACDAT1_PD */
+#define WM2200_DACDAT1_PD_MASK 0x0020 /* DACDAT1_PD */
+#define WM2200_DACDAT1_PD_SHIFT 5 /* DACDAT1_PD */
+#define WM2200_DACDAT1_PD_WIDTH 1 /* DACDAT1_PD */
+#define WM2200_DMICDAT3_PD 0x0010 /* DMICDAT3_PD */
+#define WM2200_DMICDAT3_PD_MASK 0x0010 /* DMICDAT3_PD */
+#define WM2200_DMICDAT3_PD_SHIFT 4 /* DMICDAT3_PD */
+#define WM2200_DMICDAT3_PD_WIDTH 1 /* DMICDAT3_PD */
+#define WM2200_DMICDAT2_PD 0x0008 /* DMICDAT2_PD */
+#define WM2200_DMICDAT2_PD_MASK 0x0008 /* DMICDAT2_PD */
+#define WM2200_DMICDAT2_PD_SHIFT 3 /* DMICDAT2_PD */
+#define WM2200_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */
+#define WM2200_DMICDAT1_PD 0x0004 /* DMICDAT1_PD */
+#define WM2200_DMICDAT1_PD_MASK 0x0004 /* DMICDAT1_PD */
+#define WM2200_DMICDAT1_PD_SHIFT 2 /* DMICDAT1_PD */
+#define WM2200_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */
+#define WM2200_RSTB_PU 0x0002 /* RSTB_PU */
+#define WM2200_RSTB_PU_MASK 0x0002 /* RSTB_PU */
+#define WM2200_RSTB_PU_SHIFT 1 /* RSTB_PU */
+#define WM2200_RSTB_PU_WIDTH 1 /* RSTB_PU */
+#define WM2200_ADDR_PD 0x0001 /* ADDR_PD */
+#define WM2200_ADDR_PD_MASK 0x0001 /* ADDR_PD */
+#define WM2200_ADDR_PD_SHIFT 0 /* ADDR_PD */
+#define WM2200_ADDR_PD_WIDTH 1 /* ADDR_PD */
+
+/*
+ * R2048 (0x800) - Interrupt Status 1
+ */
+#define WM2200_DSP_IRQ0_EINT 0x0080 /* DSP_IRQ0_EINT */
+#define WM2200_DSP_IRQ0_EINT_MASK 0x0080 /* DSP_IRQ0_EINT */
+#define WM2200_DSP_IRQ0_EINT_SHIFT 7 /* DSP_IRQ0_EINT */
+#define WM2200_DSP_IRQ0_EINT_WIDTH 1 /* DSP_IRQ0_EINT */
+#define WM2200_DSP_IRQ1_EINT 0x0040 /* DSP_IRQ1_EINT */
+#define WM2200_DSP_IRQ1_EINT_MASK 0x0040 /* DSP_IRQ1_EINT */
+#define WM2200_DSP_IRQ1_EINT_SHIFT 6 /* DSP_IRQ1_EINT */
+#define WM2200_DSP_IRQ1_EINT_WIDTH 1 /* DSP_IRQ1_EINT */
+#define WM2200_DSP_IRQ2_EINT 0x0020 /* DSP_IRQ2_EINT */
+#define WM2200_DSP_IRQ2_EINT_MASK 0x0020 /* DSP_IRQ2_EINT */
+#define WM2200_DSP_IRQ2_EINT_SHIFT 5 /* DSP_IRQ2_EINT */
+#define WM2200_DSP_IRQ2_EINT_WIDTH 1 /* DSP_IRQ2_EINT */
+#define WM2200_DSP_IRQ3_EINT 0x0010 /* DSP_IRQ3_EINT */
+#define WM2200_DSP_IRQ3_EINT_MASK 0x0010 /* DSP_IRQ3_EINT */
+#define WM2200_DSP_IRQ3_EINT_SHIFT 4 /* DSP_IRQ3_EINT */
+#define WM2200_DSP_IRQ3_EINT_WIDTH 1 /* DSP_IRQ3_EINT */
+#define WM2200_GP4_EINT 0x0008 /* GP4_EINT */
+#define WM2200_GP4_EINT_MASK 0x0008 /* GP4_EINT */
+#define WM2200_GP4_EINT_SHIFT 3 /* GP4_EINT */
+#define WM2200_GP4_EINT_WIDTH 1 /* GP4_EINT */
+#define WM2200_GP3_EINT 0x0004 /* GP3_EINT */
+#define WM2200_GP3_EINT_MASK 0x0004 /* GP3_EINT */
+#define WM2200_GP3_EINT_SHIFT 2 /* GP3_EINT */
+#define WM2200_GP3_EINT_WIDTH 1 /* GP3_EINT */
+#define WM2200_GP2_EINT 0x0002 /* GP2_EINT */
+#define WM2200_GP2_EINT_MASK 0x0002 /* GP2_EINT */
+#define WM2200_GP2_EINT_SHIFT 1 /* GP2_EINT */
+#define WM2200_GP2_EINT_WIDTH 1 /* GP2_EINT */
+#define WM2200_GP1_EINT 0x0001 /* GP1_EINT */
+#define WM2200_GP1_EINT_MASK 0x0001 /* GP1_EINT */
+#define WM2200_GP1_EINT_SHIFT 0 /* GP1_EINT */
+#define WM2200_GP1_EINT_WIDTH 1 /* GP1_EINT */
+
+/*
+ * R2049 (0x801) - Interrupt Status 1 Mask
+ */
+#define WM2200_IM_DSP_IRQ0_EINT 0x0080 /* IM_DSP_IRQ0_EINT */
+#define WM2200_IM_DSP_IRQ0_EINT_MASK 0x0080 /* IM_DSP_IRQ0_EINT */
+#define WM2200_IM_DSP_IRQ0_EINT_SHIFT 7 /* IM_DSP_IRQ0_EINT */
+#define WM2200_IM_DSP_IRQ0_EINT_WIDTH 1 /* IM_DSP_IRQ0_EINT */
+#define WM2200_IM_DSP_IRQ1_EINT 0x0040 /* IM_DSP_IRQ1_EINT */
+#define WM2200_IM_DSP_IRQ1_EINT_MASK 0x0040 /* IM_DSP_IRQ1_EINT */
+#define WM2200_IM_DSP_IRQ1_EINT_SHIFT 6 /* IM_DSP_IRQ1_EINT */
+#define WM2200_IM_DSP_IRQ1_EINT_WIDTH 1 /* IM_DSP_IRQ1_EINT */
+#define WM2200_IM_DSP_IRQ2_EINT 0x0020 /* IM_DSP_IRQ2_EINT */
+#define WM2200_IM_DSP_IRQ2_EINT_MASK 0x0020 /* IM_DSP_IRQ2_EINT */
+#define WM2200_IM_DSP_IRQ2_EINT_SHIFT 5 /* IM_DSP_IRQ2_EINT */
+#define WM2200_IM_DSP_IRQ2_EINT_WIDTH 1 /* IM_DSP_IRQ2_EINT */
+#define WM2200_IM_DSP_IRQ3_EINT 0x0010 /* IM_DSP_IRQ3_EINT */
+#define WM2200_IM_DSP_IRQ3_EINT_MASK 0x0010 /* IM_DSP_IRQ3_EINT */
+#define WM2200_IM_DSP_IRQ3_EINT_SHIFT 4 /* IM_DSP_IRQ3_EINT */
+#define WM2200_IM_DSP_IRQ3_EINT_WIDTH 1 /* IM_DSP_IRQ3_EINT */
+#define WM2200_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */
+#define WM2200_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */
+#define WM2200_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */
+#define WM2200_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */
+#define WM2200_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */
+#define WM2200_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */
+#define WM2200_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */
+#define WM2200_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */
+#define WM2200_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */
+#define WM2200_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */
+#define WM2200_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */
+#define WM2200_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */
+#define WM2200_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */
+#define WM2200_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */
+#define WM2200_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */
+#define WM2200_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */
+
+/*
+ * R2050 (0x802) - Interrupt Status 2
+ */
+#define WM2200_WSEQ_BUSY_EINT 0x0100 /* WSEQ_BUSY_EINT */
+#define WM2200_WSEQ_BUSY_EINT_MASK 0x0100 /* WSEQ_BUSY_EINT */
+#define WM2200_WSEQ_BUSY_EINT_SHIFT 8 /* WSEQ_BUSY_EINT */
+#define WM2200_WSEQ_BUSY_EINT_WIDTH 1 /* WSEQ_BUSY_EINT */
+#define WM2200_FLL_LOCK_EINT 0x0002 /* FLL_LOCK_EINT */
+#define WM2200_FLL_LOCK_EINT_MASK 0x0002 /* FLL_LOCK_EINT */
+#define WM2200_FLL_LOCK_EINT_SHIFT 1 /* FLL_LOCK_EINT */
+#define WM2200_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */
+#define WM2200_CLKGEN_EINT 0x0001 /* CLKGEN_EINT */
+#define WM2200_CLKGEN_EINT_MASK 0x0001 /* CLKGEN_EINT */
+#define WM2200_CLKGEN_EINT_SHIFT 0 /* CLKGEN_EINT */
+#define WM2200_CLKGEN_EINT_WIDTH 1 /* CLKGEN_EINT */
+
+/*
+ * R2051 (0x803) - Interrupt Raw Status 2
+ */
+#define WM2200_WSEQ_BUSY_STS 0x0100 /* WSEQ_BUSY_STS */
+#define WM2200_WSEQ_BUSY_STS_MASK 0x0100 /* WSEQ_BUSY_STS */
+#define WM2200_WSEQ_BUSY_STS_SHIFT 8 /* WSEQ_BUSY_STS */
+#define WM2200_WSEQ_BUSY_STS_WIDTH 1 /* WSEQ_BUSY_STS */
+#define WM2200_FLL_LOCK_STS 0x0002 /* FLL_LOCK_STS */
+#define WM2200_FLL_LOCK_STS_MASK 0x0002 /* FLL_LOCK_STS */
+#define WM2200_FLL_LOCK_STS_SHIFT 1 /* FLL_LOCK_STS */
+#define WM2200_FLL_LOCK_STS_WIDTH 1 /* FLL_LOCK_STS */
+#define WM2200_CLKGEN_STS 0x0001 /* CLKGEN_STS */
+#define WM2200_CLKGEN_STS_MASK 0x0001 /* CLKGEN_STS */
+#define WM2200_CLKGEN_STS_SHIFT 0 /* CLKGEN_STS */
+#define WM2200_CLKGEN_STS_WIDTH 1 /* CLKGEN_STS */
+
+/*
+ * R2052 (0x804) - Interrupt Status 2 Mask
+ */
+#define WM2200_IM_WSEQ_BUSY_EINT 0x0100 /* IM_WSEQ_BUSY_EINT */
+#define WM2200_IM_WSEQ_BUSY_EINT_MASK 0x0100 /* IM_WSEQ_BUSY_EINT */
+#define WM2200_IM_WSEQ_BUSY_EINT_SHIFT 8 /* IM_WSEQ_BUSY_EINT */
+#define WM2200_IM_WSEQ_BUSY_EINT_WIDTH 1 /* IM_WSEQ_BUSY_EINT */
+#define WM2200_IM_FLL_LOCK_EINT 0x0002 /* IM_FLL_LOCK_EINT */
+#define WM2200_IM_FLL_LOCK_EINT_MASK 0x0002 /* IM_FLL_LOCK_EINT */
+#define WM2200_IM_FLL_LOCK_EINT_SHIFT 1 /* IM_FLL_LOCK_EINT */
+#define WM2200_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */
+#define WM2200_IM_CLKGEN_EINT 0x0001 /* IM_CLKGEN_EINT */
+#define WM2200_IM_CLKGEN_EINT_MASK 0x0001 /* IM_CLKGEN_EINT */
+#define WM2200_IM_CLKGEN_EINT_SHIFT 0 /* IM_CLKGEN_EINT */
+#define WM2200_IM_CLKGEN_EINT_WIDTH 1 /* IM_CLKGEN_EINT */
+
+/*
+ * R2056 (0x808) - Interrupt Control
+ */
+#define WM2200_IM_IRQ 0x0001 /* IM_IRQ */
+#define WM2200_IM_IRQ_MASK 0x0001 /* IM_IRQ */
+#define WM2200_IM_IRQ_SHIFT 0 /* IM_IRQ */
+#define WM2200_IM_IRQ_WIDTH 1 /* IM_IRQ */
+
+/*
+ * R2304 (0x900) - EQL_1
+ */
+#define WM2200_EQL_B1_GAIN_MASK 0xF800 /* EQL_B1_GAIN - [15:11] */
+#define WM2200_EQL_B1_GAIN_SHIFT 11 /* EQL_B1_GAIN - [15:11] */
+#define WM2200_EQL_B1_GAIN_WIDTH 5 /* EQL_B1_GAIN - [15:11] */
+#define WM2200_EQL_B2_GAIN_MASK 0x07C0 /* EQL_B2_GAIN - [10:6] */
+#define WM2200_EQL_B2_GAIN_SHIFT 6 /* EQL_B2_GAIN - [10:6] */
+#define WM2200_EQL_B2_GAIN_WIDTH 5 /* EQL_B2_GAIN - [10:6] */
+#define WM2200_EQL_B3_GAIN_MASK 0x003E /* EQL_B3_GAIN - [5:1] */
+#define WM2200_EQL_B3_GAIN_SHIFT 1 /* EQL_B3_GAIN - [5:1] */
+#define WM2200_EQL_B3_GAIN_WIDTH 5 /* EQL_B3_GAIN - [5:1] */
+#define WM2200_EQL_ENA 0x0001 /* EQL_ENA */
+#define WM2200_EQL_ENA_MASK 0x0001 /* EQL_ENA */
+#define WM2200_EQL_ENA_SHIFT 0 /* EQL_ENA */
+#define WM2200_EQL_ENA_WIDTH 1 /* EQL_ENA */
+
+/*
+ * R2305 (0x901) - EQL_2
+ */
+#define WM2200_EQL_B4_GAIN_MASK 0xF800 /* EQL_B4_GAIN - [15:11] */
+#define WM2200_EQL_B4_GAIN_SHIFT 11 /* EQL_B4_GAIN - [15:11] */
+#define WM2200_EQL_B4_GAIN_WIDTH 5 /* EQL_B4_GAIN - [15:11] */
+#define WM2200_EQL_B5_GAIN_MASK 0x07C0 /* EQL_B5_GAIN - [10:6] */
+#define WM2200_EQL_B5_GAIN_SHIFT 6 /* EQL_B5_GAIN - [10:6] */
+#define WM2200_EQL_B5_GAIN_WIDTH 5 /* EQL_B5_GAIN - [10:6] */
+
+/*
+ * R2306 (0x902) - EQL_3
+ */
+#define WM2200_EQL_B1_A_MASK 0xFFFF /* EQL_B1_A - [15:0] */
+#define WM2200_EQL_B1_A_SHIFT 0 /* EQL_B1_A - [15:0] */
+#define WM2200_EQL_B1_A_WIDTH 16 /* EQL_B1_A - [15:0] */
+
+/*
+ * R2307 (0x903) - EQL_4
+ */
+#define WM2200_EQL_B1_B_MASK 0xFFFF /* EQL_B1_B - [15:0] */
+#define WM2200_EQL_B1_B_SHIFT 0 /* EQL_B1_B - [15:0] */
+#define WM2200_EQL_B1_B_WIDTH 16 /* EQL_B1_B - [15:0] */
+
+/*
+ * R2308 (0x904) - EQL_5
+ */
+#define WM2200_EQL_B1_PG_MASK 0xFFFF /* EQL_B1_PG - [15:0] */
+#define WM2200_EQL_B1_PG_SHIFT 0 /* EQL_B1_PG - [15:0] */
+#define WM2200_EQL_B1_PG_WIDTH 16 /* EQL_B1_PG - [15:0] */
+
+/*
+ * R2309 (0x905) - EQL_6
+ */
+#define WM2200_EQL_B2_A_MASK 0xFFFF /* EQL_B2_A - [15:0] */
+#define WM2200_EQL_B2_A_SHIFT 0 /* EQL_B2_A - [15:0] */
+#define WM2200_EQL_B2_A_WIDTH 16 /* EQL_B2_A - [15:0] */
+
+/*
+ * R2310 (0x906) - EQL_7
+ */
+#define WM2200_EQL_B2_B_MASK 0xFFFF /* EQL_B2_B - [15:0] */
+#define WM2200_EQL_B2_B_SHIFT 0 /* EQL_B2_B - [15:0] */
+#define WM2200_EQL_B2_B_WIDTH 16 /* EQL_B2_B - [15:0] */
+
+/*
+ * R2311 (0x907) - EQL_8
+ */
+#define WM2200_EQL_B2_C_MASK 0xFFFF /* EQL_B2_C - [15:0] */
+#define WM2200_EQL_B2_C_SHIFT 0 /* EQL_B2_C - [15:0] */
+#define WM2200_EQL_B2_C_WIDTH 16 /* EQL_B2_C - [15:0] */
+
+/*
+ * R2312 (0x908) - EQL_9
+ */
+#define WM2200_EQL_B2_PG_MASK 0xFFFF /* EQL_B2_PG - [15:0] */
+#define WM2200_EQL_B2_PG_SHIFT 0 /* EQL_B2_PG - [15:0] */
+#define WM2200_EQL_B2_PG_WIDTH 16 /* EQL_B2_PG - [15:0] */
+
+/*
+ * R2313 (0x909) - EQL_10
+ */
+#define WM2200_EQL_B3_A_MASK 0xFFFF /* EQL_B3_A - [15:0] */
+#define WM2200_EQL_B3_A_SHIFT 0 /* EQL_B3_A - [15:0] */
+#define WM2200_EQL_B3_A_WIDTH 16 /* EQL_B3_A - [15:0] */
+
+/*
+ * R2314 (0x90A) - EQL_11
+ */
+#define WM2200_EQL_B3_B_MASK 0xFFFF /* EQL_B3_B - [15:0] */
+#define WM2200_EQL_B3_B_SHIFT 0 /* EQL_B3_B - [15:0] */
+#define WM2200_EQL_B3_B_WIDTH 16 /* EQL_B3_B - [15:0] */
+
+/*
+ * R2315 (0x90B) - EQL_12
+ */
+#define WM2200_EQL_B3_C_MASK 0xFFFF /* EQL_B3_C - [15:0] */
+#define WM2200_EQL_B3_C_SHIFT 0 /* EQL_B3_C - [15:0] */
+#define WM2200_EQL_B3_C_WIDTH 16 /* EQL_B3_C - [15:0] */
+
+/*
+ * R2316 (0x90C) - EQL_13
+ */
+#define WM2200_EQL_B3_PG_MASK 0xFFFF /* EQL_B3_PG - [15:0] */
+#define WM2200_EQL_B3_PG_SHIFT 0 /* EQL_B3_PG - [15:0] */
+#define WM2200_EQL_B3_PG_WIDTH 16 /* EQL_B3_PG - [15:0] */
+
+/*
+ * R2317 (0x90D) - EQL_14
+ */
+#define WM2200_EQL_B4_A_MASK 0xFFFF /* EQL_B4_A - [15:0] */
+#define WM2200_EQL_B4_A_SHIFT 0 /* EQL_B4_A - [15:0] */
+#define WM2200_EQL_B4_A_WIDTH 16 /* EQL_B4_A - [15:0] */
+
+/*
+ * R2318 (0x90E) - EQL_15
+ */
+#define WM2200_EQL_B4_B_MASK 0xFFFF /* EQL_B4_B - [15:0] */
+#define WM2200_EQL_B4_B_SHIFT 0 /* EQL_B4_B - [15:0] */
+#define WM2200_EQL_B4_B_WIDTH 16 /* EQL_B4_B - [15:0] */
+
+/*
+ * R2319 (0x90F) - EQL_16
+ */
+#define WM2200_EQL_B4_C_MASK 0xFFFF /* EQL_B4_C - [15:0] */
+#define WM2200_EQL_B4_C_SHIFT 0 /* EQL_B4_C - [15:0] */
+#define WM2200_EQL_B4_C_WIDTH 16 /* EQL_B4_C - [15:0] */
+
+/*
+ * R2320 (0x910) - EQL_17
+ */
+#define WM2200_EQL_B4_PG_MASK 0xFFFF /* EQL_B4_PG - [15:0] */
+#define WM2200_EQL_B4_PG_SHIFT 0 /* EQL_B4_PG - [15:0] */
+#define WM2200_EQL_B4_PG_WIDTH 16 /* EQL_B4_PG - [15:0] */
+
+/*
+ * R2321 (0x911) - EQL_18
+ */
+#define WM2200_EQL_B5_A_MASK 0xFFFF /* EQL_B5_A - [15:0] */
+#define WM2200_EQL_B5_A_SHIFT 0 /* EQL_B5_A - [15:0] */
+#define WM2200_EQL_B5_A_WIDTH 16 /* EQL_B5_A - [15:0] */
+
+/*
+ * R2322 (0x912) - EQL_19
+ */
+#define WM2200_EQL_B5_B_MASK 0xFFFF /* EQL_B5_B - [15:0] */
+#define WM2200_EQL_B5_B_SHIFT 0 /* EQL_B5_B - [15:0] */
+#define WM2200_EQL_B5_B_WIDTH 16 /* EQL_B5_B - [15:0] */
+
+/*
+ * R2323 (0x913) - EQL_20
+ */
+#define WM2200_EQL_B5_PG_MASK 0xFFFF /* EQL_B5_PG - [15:0] */
+#define WM2200_EQL_B5_PG_SHIFT 0 /* EQL_B5_PG - [15:0] */
+#define WM2200_EQL_B5_PG_WIDTH 16 /* EQL_B5_PG - [15:0] */
+
+/*
+ * R2326 (0x916) - EQR_1
+ */
+#define WM2200_EQR_B1_GAIN_MASK 0xF800 /* EQR_B1_GAIN - [15:11] */
+#define WM2200_EQR_B1_GAIN_SHIFT 11 /* EQR_B1_GAIN - [15:11] */
+#define WM2200_EQR_B1_GAIN_WIDTH 5 /* EQR_B1_GAIN - [15:11] */
+#define WM2200_EQR_B2_GAIN_MASK 0x07C0 /* EQR_B2_GAIN - [10:6] */
+#define WM2200_EQR_B2_GAIN_SHIFT 6 /* EQR_B2_GAIN - [10:6] */
+#define WM2200_EQR_B2_GAIN_WIDTH 5 /* EQR_B2_GAIN - [10:6] */
+#define WM2200_EQR_B3_GAIN_MASK 0x003E /* EQR_B3_GAIN - [5:1] */
+#define WM2200_EQR_B3_GAIN_SHIFT 1 /* EQR_B3_GAIN - [5:1] */
+#define WM2200_EQR_B3_GAIN_WIDTH 5 /* EQR_B3_GAIN - [5:1] */
+#define WM2200_EQR_ENA 0x0001 /* EQR_ENA */
+#define WM2200_EQR_ENA_MASK 0x0001 /* EQR_ENA */
+#define WM2200_EQR_ENA_SHIFT 0 /* EQR_ENA */
+#define WM2200_EQR_ENA_WIDTH 1 /* EQR_ENA */
+
+/*
+ * R2327 (0x917) - EQR_2
+ */
+#define WM2200_EQR_B4_GAIN_MASK 0xF800 /* EQR_B4_GAIN - [15:11] */
+#define WM2200_EQR_B4_GAIN_SHIFT 11 /* EQR_B4_GAIN - [15:11] */
+#define WM2200_EQR_B4_GAIN_WIDTH 5 /* EQR_B4_GAIN - [15:11] */
+#define WM2200_EQR_B5_GAIN_MASK 0x07C0 /* EQR_B5_GAIN - [10:6] */
+#define WM2200_EQR_B5_GAIN_SHIFT 6 /* EQR_B5_GAIN - [10:6] */
+#define WM2200_EQR_B5_GAIN_WIDTH 5 /* EQR_B5_GAIN - [10:6] */
+
+/*
+ * R2328 (0x918) - EQR_3
+ */
+#define WM2200_EQR_B1_A_MASK 0xFFFF /* EQR_B1_A - [15:0] */
+#define WM2200_EQR_B1_A_SHIFT 0 /* EQR_B1_A - [15:0] */
+#define WM2200_EQR_B1_A_WIDTH 16 /* EQR_B1_A - [15:0] */
+
+/*
+ * R2329 (0x919) - EQR_4
+ */
+#define WM2200_EQR_B1_B_MASK 0xFFFF /* EQR_B1_B - [15:0] */
+#define WM2200_EQR_B1_B_SHIFT 0 /* EQR_B1_B - [15:0] */
+#define WM2200_EQR_B1_B_WIDTH 16 /* EQR_B1_B - [15:0] */
+
+/*
+ * R2330 (0x91A) - EQR_5
+ */
+#define WM2200_EQR_B1_PG_MASK 0xFFFF /* EQR_B1_PG - [15:0] */
+#define WM2200_EQR_B1_PG_SHIFT 0 /* EQR_B1_PG - [15:0] */
+#define WM2200_EQR_B1_PG_WIDTH 16 /* EQR_B1_PG - [15:0] */
+
+/*
+ * R2331 (0x91B) - EQR_6
+ */
+#define WM2200_EQR_B2_A_MASK 0xFFFF /* EQR_B2_A - [15:0] */
+#define WM2200_EQR_B2_A_SHIFT 0 /* EQR_B2_A - [15:0] */
+#define WM2200_EQR_B2_A_WIDTH 16 /* EQR_B2_A - [15:0] */
+
+/*
+ * R2332 (0x91C) - EQR_7
+ */
+#define WM2200_EQR_B2_B_MASK 0xFFFF /* EQR_B2_B - [15:0] */
+#define WM2200_EQR_B2_B_SHIFT 0 /* EQR_B2_B - [15:0] */
+#define WM2200_EQR_B2_B_WIDTH 16 /* EQR_B2_B - [15:0] */
+
+/*
+ * R2333 (0x91D) - EQR_8
+ */
+#define WM2200_EQR_B2_C_MASK 0xFFFF /* EQR_B2_C - [15:0] */
+#define WM2200_EQR_B2_C_SHIFT 0 /* EQR_B2_C - [15:0] */
+#define WM2200_EQR_B2_C_WIDTH 16 /* EQR_B2_C - [15:0] */
+
+/*
+ * R2334 (0x91E) - EQR_9
+ */
+#define WM2200_EQR_B2_PG_MASK 0xFFFF /* EQR_B2_PG - [15:0] */
+#define WM2200_EQR_B2_PG_SHIFT 0 /* EQR_B2_PG - [15:0] */
+#define WM2200_EQR_B2_PG_WIDTH 16 /* EQR_B2_PG - [15:0] */
+
+/*
+ * R2335 (0x91F) - EQR_10
+ */
+#define WM2200_EQR_B3_A_MASK 0xFFFF /* EQR_B3_A - [15:0] */
+#define WM2200_EQR_B3_A_SHIFT 0 /* EQR_B3_A - [15:0] */
+#define WM2200_EQR_B3_A_WIDTH 16 /* EQR_B3_A - [15:0] */
+
+/*
+ * R2336 (0x920) - EQR_11
+ */
+#define WM2200_EQR_B3_B_MASK 0xFFFF /* EQR_B3_B - [15:0] */
+#define WM2200_EQR_B3_B_SHIFT 0 /* EQR_B3_B - [15:0] */
+#define WM2200_EQR_B3_B_WIDTH 16 /* EQR_B3_B - [15:0] */
+
+/*
+ * R2337 (0x921) - EQR_12
+ */
+#define WM2200_EQR_B3_C_MASK 0xFFFF /* EQR_B3_C - [15:0] */
+#define WM2200_EQR_B3_C_SHIFT 0 /* EQR_B3_C - [15:0] */
+#define WM2200_EQR_B3_C_WIDTH 16 /* EQR_B3_C - [15:0] */
+
+/*
+ * R2338 (0x922) - EQR_13
+ */
+#define WM2200_EQR_B3_PG_MASK 0xFFFF /* EQR_B3_PG - [15:0] */
+#define WM2200_EQR_B3_PG_SHIFT 0 /* EQR_B3_PG - [15:0] */
+#define WM2200_EQR_B3_PG_WIDTH 16 /* EQR_B3_PG - [15:0] */
+
+/*
+ * R2339 (0x923) - EQR_14
+ */
+#define WM2200_EQR_B4_A_MASK 0xFFFF /* EQR_B4_A - [15:0] */
+#define WM2200_EQR_B4_A_SHIFT 0 /* EQR_B4_A - [15:0] */
+#define WM2200_EQR_B4_A_WIDTH 16 /* EQR_B4_A - [15:0] */
+
+/*
+ * R2340 (0x924) - EQR_15
+ */
+#define WM2200_EQR_B4_B_MASK 0xFFFF /* EQR_B4_B - [15:0] */
+#define WM2200_EQR_B4_B_SHIFT 0 /* EQR_B4_B - [15:0] */
+#define WM2200_EQR_B4_B_WIDTH 16 /* EQR_B4_B - [15:0] */
+
+/*
+ * R2341 (0x925) - EQR_16
+ */
+#define WM2200_EQR_B4_C_MASK 0xFFFF /* EQR_B4_C - [15:0] */
+#define WM2200_EQR_B4_C_SHIFT 0 /* EQR_B4_C - [15:0] */
+#define WM2200_EQR_B4_C_WIDTH 16 /* EQR_B4_C - [15:0] */
+
+/*
+ * R2342 (0x926) - EQR_17
+ */
+#define WM2200_EQR_B4_PG_MASK 0xFFFF /* EQR_B4_PG - [15:0] */
+#define WM2200_EQR_B4_PG_SHIFT 0 /* EQR_B4_PG - [15:0] */
+#define WM2200_EQR_B4_PG_WIDTH 16 /* EQR_B4_PG - [15:0] */
+
+/*
+ * R2343 (0x927) - EQR_18
+ */
+#define WM2200_EQR_B5_A_MASK 0xFFFF /* EQR_B5_A - [15:0] */
+#define WM2200_EQR_B5_A_SHIFT 0 /* EQR_B5_A - [15:0] */
+#define WM2200_EQR_B5_A_WIDTH 16 /* EQR_B5_A - [15:0] */
+
+/*
+ * R2344 (0x928) - EQR_19
+ */
+#define WM2200_EQR_B5_B_MASK 0xFFFF /* EQR_B5_B - [15:0] */
+#define WM2200_EQR_B5_B_SHIFT 0 /* EQR_B5_B - [15:0] */
+#define WM2200_EQR_B5_B_WIDTH 16 /* EQR_B5_B - [15:0] */
+
+/*
+ * R2345 (0x929) - EQR_20
+ */
+#define WM2200_EQR_B5_PG_MASK 0xFFFF /* EQR_B5_PG - [15:0] */
+#define WM2200_EQR_B5_PG_SHIFT 0 /* EQR_B5_PG - [15:0] */
+#define WM2200_EQR_B5_PG_WIDTH 16 /* EQR_B5_PG - [15:0] */
+
+/*
+ * R2366 (0x93E) - HPLPF1_1
+ */
+#define WM2200_LHPF1_MODE 0x0002 /* LHPF1_MODE */
+#define WM2200_LHPF1_MODE_MASK 0x0002 /* LHPF1_MODE */
+#define WM2200_LHPF1_MODE_SHIFT 1 /* LHPF1_MODE */
+#define WM2200_LHPF1_MODE_WIDTH 1 /* LHPF1_MODE */
+#define WM2200_LHPF1_ENA 0x0001 /* LHPF1_ENA */
+#define WM2200_LHPF1_ENA_MASK 0x0001 /* LHPF1_ENA */
+#define WM2200_LHPF1_ENA_SHIFT 0 /* LHPF1_ENA */
+#define WM2200_LHPF1_ENA_WIDTH 1 /* LHPF1_ENA */
+
+/*
+ * R2367 (0x93F) - HPLPF1_2
+ */
+#define WM2200_LHPF1_COEFF_MASK 0xFFFF /* LHPF1_COEFF - [15:0] */
+#define WM2200_LHPF1_COEFF_SHIFT 0 /* LHPF1_COEFF - [15:0] */
+#define WM2200_LHPF1_COEFF_WIDTH 16 /* LHPF1_COEFF - [15:0] */
+
+/*
+ * R2370 (0x942) - HPLPF2_1
+ */
+#define WM2200_LHPF2_MODE 0x0002 /* LHPF2_MODE */
+#define WM2200_LHPF2_MODE_MASK 0x0002 /* LHPF2_MODE */
+#define WM2200_LHPF2_MODE_SHIFT 1 /* LHPF2_MODE */
+#define WM2200_LHPF2_MODE_WIDTH 1 /* LHPF2_MODE */
+#define WM2200_LHPF2_ENA 0x0001 /* LHPF2_ENA */
+#define WM2200_LHPF2_ENA_MASK 0x0001 /* LHPF2_ENA */
+#define WM2200_LHPF2_ENA_SHIFT 0 /* LHPF2_ENA */
+#define WM2200_LHPF2_ENA_WIDTH 1 /* LHPF2_ENA */
+
+/*
+ * R2371 (0x943) - HPLPF2_2
+ */
+#define WM2200_LHPF2_COEFF_MASK 0xFFFF /* LHPF2_COEFF - [15:0] */
+#define WM2200_LHPF2_COEFF_SHIFT 0 /* LHPF2_COEFF - [15:0] */
+#define WM2200_LHPF2_COEFF_WIDTH 16 /* LHPF2_COEFF - [15:0] */
+
+/*
+ * R2560 (0xA00) - DSP1 Control 1
+ */
+#define WM2200_DSP1_RW_SEQUENCE_ENA 0x0001 /* DSP1_RW_SEQUENCE_ENA */
+#define WM2200_DSP1_RW_SEQUENCE_ENA_MASK 0x0001 /* DSP1_RW_SEQUENCE_ENA */
+#define WM2200_DSP1_RW_SEQUENCE_ENA_SHIFT 0 /* DSP1_RW_SEQUENCE_ENA */
+#define WM2200_DSP1_RW_SEQUENCE_ENA_WIDTH 1 /* DSP1_RW_SEQUENCE_ENA */
+
+/*
+ * R2562 (0xA02) - DSP1 Control 2
+ */
+#define WM2200_DSP1_PAGE_BASE_PM_0_MASK 0xFF00 /* DSP1_PAGE_BASE_PM - [15:8] */
+#define WM2200_DSP1_PAGE_BASE_PM_0_SHIFT 8 /* DSP1_PAGE_BASE_PM - [15:8] */
+#define WM2200_DSP1_PAGE_BASE_PM_0_WIDTH 8 /* DSP1_PAGE_BASE_PM - [15:8] */
+
+/*
+ * R2563 (0xA03) - DSP1 Control 3
+ */
+#define WM2200_DSP1_PAGE_BASE_DM_0_MASK 0xFF00 /* DSP1_PAGE_BASE_DM - [15:8] */
+#define WM2200_DSP1_PAGE_BASE_DM_0_SHIFT 8 /* DSP1_PAGE_BASE_DM - [15:8] */
+#define WM2200_DSP1_PAGE_BASE_DM_0_WIDTH 8 /* DSP1_PAGE_BASE_DM - [15:8] */
+
+/*
+ * R2564 (0xA04) - DSP1 Control 4
+ */
+#define WM2200_DSP1_PAGE_BASE_ZM_0_MASK 0xFF00 /* DSP1_PAGE_BASE_ZM - [15:8] */
+#define WM2200_DSP1_PAGE_BASE_ZM_0_SHIFT 8 /* DSP1_PAGE_BASE_ZM - [15:8] */
+#define WM2200_DSP1_PAGE_BASE_ZM_0_WIDTH 8 /* DSP1_PAGE_BASE_ZM - [15:8] */
+
+/*
+ * R2566 (0xA06) - DSP1 Control 5
+ */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_0_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_0 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_0_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_0 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_0_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_0 - [13:0] */
+
+/*
+ * R2567 (0xA07) - DSP1 Control 6
+ */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_1_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_1 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_1_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_1 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_1_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_1 - [13:0] */
+
+/*
+ * R2568 (0xA08) - DSP1 Control 7
+ */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_2_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_2 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_2_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_2 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_2_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_2 - [13:0] */
+
+/*
+ * R2569 (0xA09) - DSP1 Control 8
+ */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_3_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_3 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_3_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_3 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_3_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_3 - [13:0] */
+
+/*
+ * R2570 (0xA0A) - DSP1 Control 9
+ */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_4_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_4 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_4_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_4 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_4_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_4 - [13:0] */
+
+/*
+ * R2571 (0xA0B) - DSP1 Control 10
+ */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_5_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_5 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_5_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_5 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_5_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_5 - [13:0] */
+
+/*
+ * R2572 (0xA0C) - DSP1 Control 11
+ */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_6_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_6 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_6_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_6 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_6_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_6 - [13:0] */
+
+/*
+ * R2573 (0xA0D) - DSP1 Control 12
+ */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_7_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_7 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_7_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_7 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_7_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_7 - [13:0] */
+
+/*
+ * R2575 (0xA0F) - DSP1 Control 13
+ */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_0_MASK 0x3FFF /* DSP1_START_ADDRESS_RDMA_BUFFER_0 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_0_SHIFT 0 /* DSP1_START_ADDRESS_RDMA_BUFFER_0 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_0_WIDTH 14 /* DSP1_START_ADDRESS_RDMA_BUFFER_0 - [13:0] */
+
+/*
+ * R2576 (0xA10) - DSP1 Control 14
+ */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_1_MASK 0x3FFF /* DSP1_START_ADDRESS_RDMA_BUFFER_1 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_1_SHIFT 0 /* DSP1_START_ADDRESS_RDMA_BUFFER_1 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_1_WIDTH 14 /* DSP1_START_ADDRESS_RDMA_BUFFER_1 - [13:0] */
+
+/*
+ * R2577 (0xA11) - DSP1 Control 15
+ */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_2_MASK 0x3FFF /* DSP1_START_ADDRESS_RDMA_BUFFER_2 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_2_SHIFT 0 /* DSP1_START_ADDRESS_RDMA_BUFFER_2 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_2_WIDTH 14 /* DSP1_START_ADDRESS_RDMA_BUFFER_2 - [13:0] */
+
+/*
+ * R2578 (0xA12) - DSP1 Control 16
+ */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_3_MASK 0x3FFF /* DSP1_START_ADDRESS_RDMA_BUFFER_3 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_3_SHIFT 0 /* DSP1_START_ADDRESS_RDMA_BUFFER_3 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_3_WIDTH 14 /* DSP1_START_ADDRESS_RDMA_BUFFER_3 - [13:0] */
+
+/*
+ * R2579 (0xA13) - DSP1 Control 17
+ */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_4_MASK 0x3FFF /* DSP1_START_ADDRESS_RDMA_BUFFER_4 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_4_SHIFT 0 /* DSP1_START_ADDRESS_RDMA_BUFFER_4 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_4_WIDTH 14 /* DSP1_START_ADDRESS_RDMA_BUFFER_4 - [13:0] */
+
+/*
+ * R2580 (0xA14) - DSP1 Control 18
+ */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_5_MASK 0x3FFF /* DSP1_START_ADDRESS_RDMA_BUFFER_5 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_5_SHIFT 0 /* DSP1_START_ADDRESS_RDMA_BUFFER_5 - [13:0] */
+#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_5_WIDTH 14 /* DSP1_START_ADDRESS_RDMA_BUFFER_5 - [13:0] */
+
+/*
+ * R2582 (0xA16) - DSP1 Control 19
+ */
+#define WM2200_DSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+#define WM2200_DSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+#define WM2200_DSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+
+/*
+ * R2583 (0xA17) - DSP1 Control 20
+ */
+#define WM2200_DSP1_WDMA_CHANNEL_ENABLE_MASK 0x00FF /* DSP1_WDMA_CHANNEL_ENABLE - [7:0] */
+#define WM2200_DSP1_WDMA_CHANNEL_ENABLE_SHIFT 0 /* DSP1_WDMA_CHANNEL_ENABLE - [7:0] */
+#define WM2200_DSP1_WDMA_CHANNEL_ENABLE_WIDTH 8 /* DSP1_WDMA_CHANNEL_ENABLE - [7:0] */
+
+/*
+ * R2584 (0xA18) - DSP1 Control 21
+ */
+#define WM2200_DSP1_RDMA_CHANNEL_ENABLE_MASK 0x003F /* DSP1_RDMA_CHANNEL_ENABLE - [5:0] */
+#define WM2200_DSP1_RDMA_CHANNEL_ENABLE_SHIFT 0 /* DSP1_RDMA_CHANNEL_ENABLE - [5:0] */
+#define WM2200_DSP1_RDMA_CHANNEL_ENABLE_WIDTH 6 /* DSP1_RDMA_CHANNEL_ENABLE - [5:0] */
+
+/*
+ * R2586 (0xA1A) - DSP1 Control 22
+ */
+#define WM2200_DSP1_DM_SIZE_MASK 0xFFFF /* DSP1_DM_SIZE - [15:0] */
+#define WM2200_DSP1_DM_SIZE_SHIFT 0 /* DSP1_DM_SIZE - [15:0] */
+#define WM2200_DSP1_DM_SIZE_WIDTH 16 /* DSP1_DM_SIZE - [15:0] */
+
+/*
+ * R2587 (0xA1B) - DSP1 Control 23
+ */
+#define WM2200_DSP1_PM_SIZE_MASK 0xFFFF /* DSP1_PM_SIZE - [15:0] */
+#define WM2200_DSP1_PM_SIZE_SHIFT 0 /* DSP1_PM_SIZE - [15:0] */
+#define WM2200_DSP1_PM_SIZE_WIDTH 16 /* DSP1_PM_SIZE - [15:0] */
+
+/*
+ * R2588 (0xA1C) - DSP1 Control 24
+ */
+#define WM2200_DSP1_ZM_SIZE_MASK 0xFFFF /* DSP1_ZM_SIZE - [15:0] */
+#define WM2200_DSP1_ZM_SIZE_SHIFT 0 /* DSP1_ZM_SIZE - [15:0] */
+#define WM2200_DSP1_ZM_SIZE_WIDTH 16 /* DSP1_ZM_SIZE - [15:0] */
+
+/*
+ * R2590 (0xA1E) - DSP1 Control 25
+ */
+#define WM2200_DSP1_PING_FULL 0x8000 /* DSP1_PING_FULL */
+#define WM2200_DSP1_PING_FULL_MASK 0x8000 /* DSP1_PING_FULL */
+#define WM2200_DSP1_PING_FULL_SHIFT 15 /* DSP1_PING_FULL */
+#define WM2200_DSP1_PING_FULL_WIDTH 1 /* DSP1_PING_FULL */
+#define WM2200_DSP1_PONG_FULL 0x4000 /* DSP1_PONG_FULL */
+#define WM2200_DSP1_PONG_FULL_MASK 0x4000 /* DSP1_PONG_FULL */
+#define WM2200_DSP1_PONG_FULL_SHIFT 14 /* DSP1_PONG_FULL */
+#define WM2200_DSP1_PONG_FULL_WIDTH 1 /* DSP1_PONG_FULL */
+#define WM2200_DSP1_WDMA_ACTIVE_CHANNELS_MASK 0x00FF /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */
+#define WM2200_DSP1_WDMA_ACTIVE_CHANNELS_SHIFT 0 /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */
+#define WM2200_DSP1_WDMA_ACTIVE_CHANNELS_WIDTH 8 /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */
+
+/*
+ * R2592 (0xA20) - DSP1 Control 26
+ */
+#define WM2200_DSP1_SCRATCH_0_MASK 0xFFFF /* DSP1_SCRATCH_0 - [15:0] */
+#define WM2200_DSP1_SCRATCH_0_SHIFT 0 /* DSP1_SCRATCH_0 - [15:0] */
+#define WM2200_DSP1_SCRATCH_0_WIDTH 16 /* DSP1_SCRATCH_0 - [15:0] */
+
+/*
+ * R2593 (0xA21) - DSP1 Control 27
+ */
+#define WM2200_DSP1_SCRATCH_1_MASK 0xFFFF /* DSP1_SCRATCH_1 - [15:0] */
+#define WM2200_DSP1_SCRATCH_1_SHIFT 0 /* DSP1_SCRATCH_1 - [15:0] */
+#define WM2200_DSP1_SCRATCH_1_WIDTH 16 /* DSP1_SCRATCH_1 - [15:0] */
+
+/*
+ * R2594 (0xA22) - DSP1 Control 28
+ */
+#define WM2200_DSP1_SCRATCH_2_MASK 0xFFFF /* DSP1_SCRATCH_2 - [15:0] */
+#define WM2200_DSP1_SCRATCH_2_SHIFT 0 /* DSP1_SCRATCH_2 - [15:0] */
+#define WM2200_DSP1_SCRATCH_2_WIDTH 16 /* DSP1_SCRATCH_2 - [15:0] */
+
+/*
+ * R2595 (0xA23) - DSP1 Control 29
+ */
+#define WM2200_DSP1_SCRATCH_3_MASK 0xFFFF /* DSP1_SCRATCH_3 - [15:0] */
+#define WM2200_DSP1_SCRATCH_3_SHIFT 0 /* DSP1_SCRATCH_3 - [15:0] */
+#define WM2200_DSP1_SCRATCH_3_WIDTH 16 /* DSP1_SCRATCH_3 - [15:0] */
+
+/*
+ * R2596 (0xA24) - DSP1 Control 30
+ */
+#define WM2200_DSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
+#define WM2200_DSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
+#define WM2200_DSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
+#define WM2200_DSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
+#define WM2200_DSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
+#define WM2200_DSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
+#define WM2200_DSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
+#define WM2200_DSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
+#define WM2200_DSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
+#define WM2200_DSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
+#define WM2200_DSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
+#define WM2200_DSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
+#define WM2200_DSP1_START 0x0001 /* DSP1_START */
+#define WM2200_DSP1_START_MASK 0x0001 /* DSP1_START */
+#define WM2200_DSP1_START_SHIFT 0 /* DSP1_START */
+#define WM2200_DSP1_START_WIDTH 1 /* DSP1_START */
+
+/*
+ * R2598 (0xA26) - DSP1 Control 31
+ */
+#define WM2200_DSP1_CLK_RATE_MASK 0x0018 /* DSP1_CLK_RATE - [4:3] */
+#define WM2200_DSP1_CLK_RATE_SHIFT 3 /* DSP1_CLK_RATE - [4:3] */
+#define WM2200_DSP1_CLK_RATE_WIDTH 2 /* DSP1_CLK_RATE - [4:3] */
+#define WM2200_DSP1_CLK_AVAIL 0x0004 /* DSP1_CLK_AVAIL */
+#define WM2200_DSP1_CLK_AVAIL_MASK 0x0004 /* DSP1_CLK_AVAIL */
+#define WM2200_DSP1_CLK_AVAIL_SHIFT 2 /* DSP1_CLK_AVAIL */
+#define WM2200_DSP1_CLK_AVAIL_WIDTH 1 /* DSP1_CLK_AVAIL */
+#define WM2200_DSP1_CLK_REQ_MASK 0x0003 /* DSP1_CLK_REQ - [1:0] */
+#define WM2200_DSP1_CLK_REQ_SHIFT 0 /* DSP1_CLK_REQ - [1:0] */
+#define WM2200_DSP1_CLK_REQ_WIDTH 2 /* DSP1_CLK_REQ - [1:0] */
+
+/*
+ * R2816 (0xB00) - DSP2 Control 1
+ */
+#define WM2200_DSP2_RW_SEQUENCE_ENA 0x0001 /* DSP2_RW_SEQUENCE_ENA */
+#define WM2200_DSP2_RW_SEQUENCE_ENA_MASK 0x0001 /* DSP2_RW_SEQUENCE_ENA */
+#define WM2200_DSP2_RW_SEQUENCE_ENA_SHIFT 0 /* DSP2_RW_SEQUENCE_ENA */
+#define WM2200_DSP2_RW_SEQUENCE_ENA_WIDTH 1 /* DSP2_RW_SEQUENCE_ENA */
+
+/*
+ * R2818 (0xB02) - DSP2 Control 2
+ */
+#define WM2200_DSP2_PAGE_BASE_PM_0_MASK 0xFF00 /* DSP2_PAGE_BASE_PM - [15:8] */
+#define WM2200_DSP2_PAGE_BASE_PM_0_SHIFT 8 /* DSP2_PAGE_BASE_PM - [15:8] */
+#define WM2200_DSP2_PAGE_BASE_PM_0_WIDTH 8 /* DSP2_PAGE_BASE_PM - [15:8] */
+
+/*
+ * R2819 (0xB03) - DSP2 Control 3
+ */
+#define WM2200_DSP2_PAGE_BASE_DM_0_MASK 0xFF00 /* DSP2_PAGE_BASE_DM - [15:8] */
+#define WM2200_DSP2_PAGE_BASE_DM_0_SHIFT 8 /* DSP2_PAGE_BASE_DM - [15:8] */
+#define WM2200_DSP2_PAGE_BASE_DM_0_WIDTH 8 /* DSP2_PAGE_BASE_DM - [15:8] */
+
+/*
+ * R2820 (0xB04) - DSP2 Control 4
+ */
+#define WM2200_DSP2_PAGE_BASE_ZM_0_MASK 0xFF00 /* DSP2_PAGE_BASE_ZM - [15:8] */
+#define WM2200_DSP2_PAGE_BASE_ZM_0_SHIFT 8 /* DSP2_PAGE_BASE_ZM - [15:8] */
+#define WM2200_DSP2_PAGE_BASE_ZM_0_WIDTH 8 /* DSP2_PAGE_BASE_ZM - [15:8] */
+
+/*
+ * R2822 (0xB06) - DSP2 Control 5
+ */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_0_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_0 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_0_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_0 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_0_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_0 - [13:0] */
+
+/*
+ * R2823 (0xB07) - DSP2 Control 6
+ */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_1_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_1 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_1_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_1 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_1_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_1 - [13:0] */
+
+/*
+ * R2824 (0xB08) - DSP2 Control 7
+ */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_2_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_2 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_2_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_2 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_2_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_2 - [13:0] */
+
+/*
+ * R2825 (0xB09) - DSP2 Control 8
+ */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_3_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_3 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_3_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_3 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_3_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_3 - [13:0] */
+
+/*
+ * R2826 (0xB0A) - DSP2 Control 9
+ */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_4_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_4 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_4_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_4 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_4_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_4 - [13:0] */
+
+/*
+ * R2827 (0xB0B) - DSP2 Control 10
+ */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_5_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_5 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_5_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_5 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_5_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_5 - [13:0] */
+
+/*
+ * R2828 (0xB0C) - DSP2 Control 11
+ */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_6_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_6 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_6_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_6 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_6_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_6 - [13:0] */
+
+/*
+ * R2829 (0xB0D) - DSP2 Control 12
+ */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_7_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_7 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_7_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_7 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_7_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_7 - [13:0] */
+
+/*
+ * R2831 (0xB0F) - DSP2 Control 13
+ */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_0_MASK 0x3FFF /* DSP2_START_ADDRESS_RDMA_BUFFER_0 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_0_SHIFT 0 /* DSP2_START_ADDRESS_RDMA_BUFFER_0 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_0_WIDTH 14 /* DSP2_START_ADDRESS_RDMA_BUFFER_0 - [13:0] */
+
+/*
+ * R2832 (0xB10) - DSP2 Control 14
+ */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_1_MASK 0x3FFF /* DSP2_START_ADDRESS_RDMA_BUFFER_1 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_1_SHIFT 0 /* DSP2_START_ADDRESS_RDMA_BUFFER_1 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_1_WIDTH 14 /* DSP2_START_ADDRESS_RDMA_BUFFER_1 - [13:0] */
+
+/*
+ * R2833 (0xB11) - DSP2 Control 15
+ */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_2_MASK 0x3FFF /* DSP2_START_ADDRESS_RDMA_BUFFER_2 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_2_SHIFT 0 /* DSP2_START_ADDRESS_RDMA_BUFFER_2 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_2_WIDTH 14 /* DSP2_START_ADDRESS_RDMA_BUFFER_2 - [13:0] */
+
+/*
+ * R2834 (0xB12) - DSP2 Control 16
+ */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_3_MASK 0x3FFF /* DSP2_START_ADDRESS_RDMA_BUFFER_3 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_3_SHIFT 0 /* DSP2_START_ADDRESS_RDMA_BUFFER_3 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_3_WIDTH 14 /* DSP2_START_ADDRESS_RDMA_BUFFER_3 - [13:0] */
+
+/*
+ * R2835 (0xB13) - DSP2 Control 17
+ */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_4_MASK 0x3FFF /* DSP2_START_ADDRESS_RDMA_BUFFER_4 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_4_SHIFT 0 /* DSP2_START_ADDRESS_RDMA_BUFFER_4 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_4_WIDTH 14 /* DSP2_START_ADDRESS_RDMA_BUFFER_4 - [13:0] */
+
+/*
+ * R2836 (0xB14) - DSP2 Control 18
+ */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_5_MASK 0x3FFF /* DSP2_START_ADDRESS_RDMA_BUFFER_5 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_5_SHIFT 0 /* DSP2_START_ADDRESS_RDMA_BUFFER_5 - [13:0] */
+#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_5_WIDTH 14 /* DSP2_START_ADDRESS_RDMA_BUFFER_5 - [13:0] */
+
+/*
+ * R2838 (0xB16) - DSP2 Control 19
+ */
+#define WM2200_DSP2_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP2_WDMA_BUFFER_LENGTH - [7:0] */
+#define WM2200_DSP2_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP2_WDMA_BUFFER_LENGTH - [7:0] */
+#define WM2200_DSP2_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP2_WDMA_BUFFER_LENGTH - [7:0] */
+
+/*
+ * R2839 (0xB17) - DSP2 Control 20
+ */
+#define WM2200_DSP2_WDMA_CHANNEL_ENABLE_MASK 0x00FF /* DSP2_WDMA_CHANNEL_ENABLE - [7:0] */
+#define WM2200_DSP2_WDMA_CHANNEL_ENABLE_SHIFT 0 /* DSP2_WDMA_CHANNEL_ENABLE - [7:0] */
+#define WM2200_DSP2_WDMA_CHANNEL_ENABLE_WIDTH 8 /* DSP2_WDMA_CHANNEL_ENABLE - [7:0] */
+
+/*
+ * R2840 (0xB18) - DSP2 Control 21
+ */
+#define WM2200_DSP2_RDMA_CHANNEL_ENABLE_MASK 0x003F /* DSP2_RDMA_CHANNEL_ENABLE - [5:0] */
+#define WM2200_DSP2_RDMA_CHANNEL_ENABLE_SHIFT 0 /* DSP2_RDMA_CHANNEL_ENABLE - [5:0] */
+#define WM2200_DSP2_RDMA_CHANNEL_ENABLE_WIDTH 6 /* DSP2_RDMA_CHANNEL_ENABLE - [5:0] */
+
+/*
+ * R2842 (0xB1A) - DSP2 Control 22
+ */
+#define WM2200_DSP2_DM_SIZE_MASK 0xFFFF /* DSP2_DM_SIZE - [15:0] */
+#define WM2200_DSP2_DM_SIZE_SHIFT 0 /* DSP2_DM_SIZE - [15:0] */
+#define WM2200_DSP2_DM_SIZE_WIDTH 16 /* DSP2_DM_SIZE - [15:0] */
+
+/*
+ * R2843 (0xB1B) - DSP2 Control 23
+ */
+#define WM2200_DSP2_PM_SIZE_MASK 0xFFFF /* DSP2_PM_SIZE - [15:0] */
+#define WM2200_DSP2_PM_SIZE_SHIFT 0 /* DSP2_PM_SIZE - [15:0] */
+#define WM2200_DSP2_PM_SIZE_WIDTH 16 /* DSP2_PM_SIZE - [15:0] */
+
+/*
+ * R2844 (0xB1C) - DSP2 Control 24
+ */
+#define WM2200_DSP2_ZM_SIZE_MASK 0xFFFF /* DSP2_ZM_SIZE - [15:0] */
+#define WM2200_DSP2_ZM_SIZE_SHIFT 0 /* DSP2_ZM_SIZE - [15:0] */
+#define WM2200_DSP2_ZM_SIZE_WIDTH 16 /* DSP2_ZM_SIZE - [15:0] */
+
+/*
+ * R2846 (0xB1E) - DSP2 Control 25
+ */
+#define WM2200_DSP2_PING_FULL 0x8000 /* DSP2_PING_FULL */
+#define WM2200_DSP2_PING_FULL_MASK 0x8000 /* DSP2_PING_FULL */
+#define WM2200_DSP2_PING_FULL_SHIFT 15 /* DSP2_PING_FULL */
+#define WM2200_DSP2_PING_FULL_WIDTH 1 /* DSP2_PING_FULL */
+#define WM2200_DSP2_PONG_FULL 0x4000 /* DSP2_PONG_FULL */
+#define WM2200_DSP2_PONG_FULL_MASK 0x4000 /* DSP2_PONG_FULL */
+#define WM2200_DSP2_PONG_FULL_SHIFT 14 /* DSP2_PONG_FULL */
+#define WM2200_DSP2_PONG_FULL_WIDTH 1 /* DSP2_PONG_FULL */
+#define WM2200_DSP2_WDMA_ACTIVE_CHANNELS_MASK 0x00FF /* DSP2_WDMA_ACTIVE_CHANNELS - [7:0] */
+#define WM2200_DSP2_WDMA_ACTIVE_CHANNELS_SHIFT 0 /* DSP2_WDMA_ACTIVE_CHANNELS - [7:0] */
+#define WM2200_DSP2_WDMA_ACTIVE_CHANNELS_WIDTH 8 /* DSP2_WDMA_ACTIVE_CHANNELS - [7:0] */
+
+/*
+ * R2848 (0xB20) - DSP2 Control 26
+ */
+#define WM2200_DSP2_SCRATCH_0_MASK 0xFFFF /* DSP2_SCRATCH_0 - [15:0] */
+#define WM2200_DSP2_SCRATCH_0_SHIFT 0 /* DSP2_SCRATCH_0 - [15:0] */
+#define WM2200_DSP2_SCRATCH_0_WIDTH 16 /* DSP2_SCRATCH_0 - [15:0] */
+
+/*
+ * R2849 (0xB21) - DSP2 Control 27
+ */
+#define WM2200_DSP2_SCRATCH_1_MASK 0xFFFF /* DSP2_SCRATCH_1 - [15:0] */
+#define WM2200_DSP2_SCRATCH_1_SHIFT 0 /* DSP2_SCRATCH_1 - [15:0] */
+#define WM2200_DSP2_SCRATCH_1_WIDTH 16 /* DSP2_SCRATCH_1 - [15:0] */
+
+/*
+ * R2850 (0xB22) - DSP2 Control 28
+ */
+#define WM2200_DSP2_SCRATCH_2_MASK 0xFFFF /* DSP2_SCRATCH_2 - [15:0] */
+#define WM2200_DSP2_SCRATCH_2_SHIFT 0 /* DSP2_SCRATCH_2 - [15:0] */
+#define WM2200_DSP2_SCRATCH_2_WIDTH 16 /* DSP2_SCRATCH_2 - [15:0] */
+
+/*
+ * R2851 (0xB23) - DSP2 Control 29
+ */
+#define WM2200_DSP2_SCRATCH_3_MASK 0xFFFF /* DSP2_SCRATCH_3 - [15:0] */
+#define WM2200_DSP2_SCRATCH_3_SHIFT 0 /* DSP2_SCRATCH_3 - [15:0] */
+#define WM2200_DSP2_SCRATCH_3_WIDTH 16 /* DSP2_SCRATCH_3 - [15:0] */
+
+/*
+ * R2852 (0xB24) - DSP2 Control 30
+ */
+#define WM2200_DSP2_DBG_CLK_ENA 0x0008 /* DSP2_DBG_CLK_ENA */
+#define WM2200_DSP2_DBG_CLK_ENA_MASK 0x0008 /* DSP2_DBG_CLK_ENA */
+#define WM2200_DSP2_DBG_CLK_ENA_SHIFT 3 /* DSP2_DBG_CLK_ENA */
+#define WM2200_DSP2_DBG_CLK_ENA_WIDTH 1 /* DSP2_DBG_CLK_ENA */
+#define WM2200_DSP2_SYS_ENA 0x0004 /* DSP2_SYS_ENA */
+#define WM2200_DSP2_SYS_ENA_MASK 0x0004 /* DSP2_SYS_ENA */
+#define WM2200_DSP2_SYS_ENA_SHIFT 2 /* DSP2_SYS_ENA */
+#define WM2200_DSP2_SYS_ENA_WIDTH 1 /* DSP2_SYS_ENA */
+#define WM2200_DSP2_CORE_ENA 0x0002 /* DSP2_CORE_ENA */
+#define WM2200_DSP2_CORE_ENA_MASK 0x0002 /* DSP2_CORE_ENA */
+#define WM2200_DSP2_CORE_ENA_SHIFT 1 /* DSP2_CORE_ENA */
+#define WM2200_DSP2_CORE_ENA_WIDTH 1 /* DSP2_CORE_ENA */
+#define WM2200_DSP2_START 0x0001 /* DSP2_START */
+#define WM2200_DSP2_START_MASK 0x0001 /* DSP2_START */
+#define WM2200_DSP2_START_SHIFT 0 /* DSP2_START */
+#define WM2200_DSP2_START_WIDTH 1 /* DSP2_START */
+
+/*
+ * R2854 (0xB26) - DSP2 Control 31
+ */
+#define WM2200_DSP2_CLK_RATE_MASK 0x0018 /* DSP2_CLK_RATE - [4:3] */
+#define WM2200_DSP2_CLK_RATE_SHIFT 3 /* DSP2_CLK_RATE - [4:3] */
+#define WM2200_DSP2_CLK_RATE_WIDTH 2 /* DSP2_CLK_RATE - [4:3] */
+#define WM2200_DSP2_CLK_AVAIL 0x0004 /* DSP2_CLK_AVAIL */
+#define WM2200_DSP2_CLK_AVAIL_MASK 0x0004 /* DSP2_CLK_AVAIL */
+#define WM2200_DSP2_CLK_AVAIL_SHIFT 2 /* DSP2_CLK_AVAIL */
+#define WM2200_DSP2_CLK_AVAIL_WIDTH 1 /* DSP2_CLK_AVAIL */
+#define WM2200_DSP2_CLK_REQ_MASK 0x0003 /* DSP2_CLK_REQ - [1:0] */
+#define WM2200_DSP2_CLK_REQ_SHIFT 0 /* DSP2_CLK_REQ - [1:0] */
+#define WM2200_DSP2_CLK_REQ_WIDTH 2 /* DSP2_CLK_REQ - [1:0] */
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm5100-tables.c b/kernel/sound/soc/codecs/wm5100-tables.c
new file mode 100644
index 000000000..e239f4bf2
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm5100-tables.c
@@ -0,0 +1,1485 @@
+/*
+ * wm5100-tables.c -- WM5100 ALSA SoC Audio driver data
+ *
+ * Copyright 2011-2 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 "wm5100.h"
+
+bool wm5100_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM5100_SOFTWARE_RESET:
+ case WM5100_DEVICE_REVISION:
+ case WM5100_FX_CTRL:
+ case WM5100_INTERRUPT_STATUS_1:
+ case WM5100_INTERRUPT_STATUS_2:
+ case WM5100_INTERRUPT_STATUS_3:
+ case WM5100_INTERRUPT_STATUS_4:
+ case WM5100_INTERRUPT_RAW_STATUS_2:
+ case WM5100_INTERRUPT_RAW_STATUS_3:
+ case WM5100_INTERRUPT_RAW_STATUS_4:
+ case WM5100_OUTPUT_STATUS_1:
+ case WM5100_OUTPUT_STATUS_2:
+ case WM5100_INPUT_ENABLES_STATUS:
+ case WM5100_MIC_DETECT_3:
+ return 1;
+ default:
+ if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) ||
+ (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) ||
+ (reg >= WM5100_DSP1_DM_0 && reg <= WM5100_DSP1_DM_511) ||
+ (reg >= WM5100_DSP2_PM_0 && reg <= WM5100_DSP2_PM_1535) ||
+ (reg >= WM5100_DSP2_ZM_0 && reg <= WM5100_DSP2_ZM_2047) ||
+ (reg >= WM5100_DSP2_DM_0 && reg <= WM5100_DSP2_DM_511) ||
+ (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) ||
+ (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) ||
+ (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511))
+ return 1;
+ else
+ return 0;
+ }
+}
+
+bool wm5100_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM5100_SOFTWARE_RESET:
+ case WM5100_DEVICE_REVISION:
+ case WM5100_CTRL_IF_1:
+ case WM5100_TONE_GENERATOR_1:
+ case WM5100_PWM_DRIVE_1:
+ case WM5100_PWM_DRIVE_2:
+ case WM5100_PWM_DRIVE_3:
+ case WM5100_CLOCKING_1:
+ case WM5100_CLOCKING_3:
+ case WM5100_CLOCKING_4:
+ case WM5100_CLOCKING_5:
+ case WM5100_CLOCKING_6:
+ case WM5100_CLOCKING_7:
+ case WM5100_CLOCKING_8:
+ case WM5100_ASRC_ENABLE:
+ case WM5100_ASRC_STATUS:
+ case WM5100_ASRC_RATE1:
+ case WM5100_ISRC_1_CTRL_1:
+ case WM5100_ISRC_1_CTRL_2:
+ case WM5100_ISRC_2_CTRL1:
+ case WM5100_ISRC_2_CTRL_2:
+ case WM5100_FLL1_CONTROL_1:
+ case WM5100_FLL1_CONTROL_2:
+ case WM5100_FLL1_CONTROL_3:
+ case WM5100_FLL1_CONTROL_5:
+ case WM5100_FLL1_CONTROL_6:
+ case WM5100_FLL1_EFS_1:
+ case WM5100_FLL2_CONTROL_1:
+ case WM5100_FLL2_CONTROL_2:
+ case WM5100_FLL2_CONTROL_3:
+ case WM5100_FLL2_CONTROL_5:
+ case WM5100_FLL2_CONTROL_6:
+ case WM5100_FLL2_EFS_1:
+ case WM5100_MIC_CHARGE_PUMP_1:
+ case WM5100_MIC_CHARGE_PUMP_2:
+ case WM5100_HP_CHARGE_PUMP_1:
+ case WM5100_LDO1_CONTROL:
+ case WM5100_MIC_BIAS_CTRL_1:
+ case WM5100_MIC_BIAS_CTRL_2:
+ case WM5100_MIC_BIAS_CTRL_3:
+ case WM5100_ACCESSORY_DETECT_MODE_1:
+ case WM5100_HEADPHONE_DETECT_1:
+ case WM5100_HEADPHONE_DETECT_2:
+ case WM5100_MIC_DETECT_1:
+ case WM5100_MIC_DETECT_2:
+ case WM5100_MIC_DETECT_3:
+ case WM5100_MISC_CONTROL:
+ case WM5100_INPUT_ENABLES:
+ case WM5100_INPUT_ENABLES_STATUS:
+ case WM5100_IN1L_CONTROL:
+ case WM5100_IN1R_CONTROL:
+ case WM5100_IN2L_CONTROL:
+ case WM5100_IN2R_CONTROL:
+ case WM5100_IN3L_CONTROL:
+ case WM5100_IN3R_CONTROL:
+ case WM5100_IN4L_CONTROL:
+ case WM5100_IN4R_CONTROL:
+ case WM5100_RXANC_SRC:
+ case WM5100_INPUT_VOLUME_RAMP:
+ case WM5100_ADC_DIGITAL_VOLUME_1L:
+ case WM5100_ADC_DIGITAL_VOLUME_1R:
+ case WM5100_ADC_DIGITAL_VOLUME_2L:
+ case WM5100_ADC_DIGITAL_VOLUME_2R:
+ case WM5100_ADC_DIGITAL_VOLUME_3L:
+ case WM5100_ADC_DIGITAL_VOLUME_3R:
+ case WM5100_ADC_DIGITAL_VOLUME_4L:
+ case WM5100_ADC_DIGITAL_VOLUME_4R:
+ case WM5100_OUTPUT_ENABLES_2:
+ case WM5100_OUTPUT_STATUS_1:
+ case WM5100_OUTPUT_STATUS_2:
+ case WM5100_CHANNEL_ENABLES_1:
+ case WM5100_OUT_VOLUME_1L:
+ case WM5100_OUT_VOLUME_1R:
+ case WM5100_DAC_VOLUME_LIMIT_1L:
+ case WM5100_DAC_VOLUME_LIMIT_1R:
+ case WM5100_OUT_VOLUME_2L:
+ case WM5100_OUT_VOLUME_2R:
+ case WM5100_DAC_VOLUME_LIMIT_2L:
+ case WM5100_DAC_VOLUME_LIMIT_2R:
+ case WM5100_OUT_VOLUME_3L:
+ case WM5100_OUT_VOLUME_3R:
+ case WM5100_DAC_VOLUME_LIMIT_3L:
+ case WM5100_DAC_VOLUME_LIMIT_3R:
+ case WM5100_OUT_VOLUME_4L:
+ case WM5100_OUT_VOLUME_4R:
+ case WM5100_DAC_VOLUME_LIMIT_5L:
+ case WM5100_DAC_VOLUME_LIMIT_5R:
+ case WM5100_DAC_VOLUME_LIMIT_6L:
+ case WM5100_DAC_VOLUME_LIMIT_6R:
+ case WM5100_DAC_AEC_CONTROL_1:
+ case WM5100_OUTPUT_VOLUME_RAMP:
+ case WM5100_DAC_DIGITAL_VOLUME_1L:
+ case WM5100_DAC_DIGITAL_VOLUME_1R:
+ case WM5100_DAC_DIGITAL_VOLUME_2L:
+ case WM5100_DAC_DIGITAL_VOLUME_2R:
+ case WM5100_DAC_DIGITAL_VOLUME_3L:
+ case WM5100_DAC_DIGITAL_VOLUME_3R:
+ case WM5100_DAC_DIGITAL_VOLUME_4L:
+ case WM5100_DAC_DIGITAL_VOLUME_4R:
+ case WM5100_DAC_DIGITAL_VOLUME_5L:
+ case WM5100_DAC_DIGITAL_VOLUME_5R:
+ case WM5100_DAC_DIGITAL_VOLUME_6L:
+ case WM5100_DAC_DIGITAL_VOLUME_6R:
+ case WM5100_PDM_SPK1_CTRL_1:
+ case WM5100_PDM_SPK1_CTRL_2:
+ case WM5100_PDM_SPK2_CTRL_1:
+ case WM5100_PDM_SPK2_CTRL_2:
+ case WM5100_AUDIO_IF_1_1:
+ case WM5100_AUDIO_IF_1_2:
+ case WM5100_AUDIO_IF_1_3:
+ case WM5100_AUDIO_IF_1_4:
+ case WM5100_AUDIO_IF_1_5:
+ case WM5100_AUDIO_IF_1_6:
+ case WM5100_AUDIO_IF_1_7:
+ case WM5100_AUDIO_IF_1_8:
+ case WM5100_AUDIO_IF_1_9:
+ case WM5100_AUDIO_IF_1_10:
+ case WM5100_AUDIO_IF_1_11:
+ case WM5100_AUDIO_IF_1_12:
+ case WM5100_AUDIO_IF_1_13:
+ case WM5100_AUDIO_IF_1_14:
+ case WM5100_AUDIO_IF_1_15:
+ case WM5100_AUDIO_IF_1_16:
+ case WM5100_AUDIO_IF_1_17:
+ case WM5100_AUDIO_IF_1_18:
+ case WM5100_AUDIO_IF_1_19:
+ case WM5100_AUDIO_IF_1_20:
+ case WM5100_AUDIO_IF_1_21:
+ case WM5100_AUDIO_IF_1_22:
+ case WM5100_AUDIO_IF_1_23:
+ case WM5100_AUDIO_IF_1_24:
+ case WM5100_AUDIO_IF_1_25:
+ case WM5100_AUDIO_IF_1_26:
+ case WM5100_AUDIO_IF_1_27:
+ case WM5100_AUDIO_IF_2_1:
+ case WM5100_AUDIO_IF_2_2:
+ case WM5100_AUDIO_IF_2_3:
+ case WM5100_AUDIO_IF_2_4:
+ case WM5100_AUDIO_IF_2_5:
+ case WM5100_AUDIO_IF_2_6:
+ case WM5100_AUDIO_IF_2_7:
+ case WM5100_AUDIO_IF_2_8:
+ case WM5100_AUDIO_IF_2_9:
+ case WM5100_AUDIO_IF_2_10:
+ case WM5100_AUDIO_IF_2_11:
+ case WM5100_AUDIO_IF_2_18:
+ case WM5100_AUDIO_IF_2_19:
+ case WM5100_AUDIO_IF_2_26:
+ case WM5100_AUDIO_IF_2_27:
+ case WM5100_AUDIO_IF_3_1:
+ case WM5100_AUDIO_IF_3_2:
+ case WM5100_AUDIO_IF_3_3:
+ case WM5100_AUDIO_IF_3_4:
+ case WM5100_AUDIO_IF_3_5:
+ case WM5100_AUDIO_IF_3_6:
+ case WM5100_AUDIO_IF_3_7:
+ case WM5100_AUDIO_IF_3_8:
+ case WM5100_AUDIO_IF_3_9:
+ case WM5100_AUDIO_IF_3_10:
+ case WM5100_AUDIO_IF_3_11:
+ case WM5100_AUDIO_IF_3_18:
+ case WM5100_AUDIO_IF_3_19:
+ case WM5100_AUDIO_IF_3_26:
+ case WM5100_AUDIO_IF_3_27:
+ case WM5100_PWM1MIX_INPUT_1_SOURCE:
+ case WM5100_PWM1MIX_INPUT_1_VOLUME:
+ case WM5100_PWM1MIX_INPUT_2_SOURCE:
+ case WM5100_PWM1MIX_INPUT_2_VOLUME:
+ case WM5100_PWM1MIX_INPUT_3_SOURCE:
+ case WM5100_PWM1MIX_INPUT_3_VOLUME:
+ case WM5100_PWM1MIX_INPUT_4_SOURCE:
+ case WM5100_PWM1MIX_INPUT_4_VOLUME:
+ case WM5100_PWM2MIX_INPUT_1_SOURCE:
+ case WM5100_PWM2MIX_INPUT_1_VOLUME:
+ case WM5100_PWM2MIX_INPUT_2_SOURCE:
+ case WM5100_PWM2MIX_INPUT_2_VOLUME:
+ case WM5100_PWM2MIX_INPUT_3_SOURCE:
+ case WM5100_PWM2MIX_INPUT_3_VOLUME:
+ case WM5100_PWM2MIX_INPUT_4_SOURCE:
+ case WM5100_PWM2MIX_INPUT_4_VOLUME:
+ case WM5100_OUT1LMIX_INPUT_1_SOURCE:
+ case WM5100_OUT1LMIX_INPUT_1_VOLUME:
+ case WM5100_OUT1LMIX_INPUT_2_SOURCE:
+ case WM5100_OUT1LMIX_INPUT_2_VOLUME:
+ case WM5100_OUT1LMIX_INPUT_3_SOURCE:
+ case WM5100_OUT1LMIX_INPUT_3_VOLUME:
+ case WM5100_OUT1LMIX_INPUT_4_SOURCE:
+ case WM5100_OUT1LMIX_INPUT_4_VOLUME:
+ case WM5100_OUT1RMIX_INPUT_1_SOURCE:
+ case WM5100_OUT1RMIX_INPUT_1_VOLUME:
+ case WM5100_OUT1RMIX_INPUT_2_SOURCE:
+ case WM5100_OUT1RMIX_INPUT_2_VOLUME:
+ case WM5100_OUT1RMIX_INPUT_3_SOURCE:
+ case WM5100_OUT1RMIX_INPUT_3_VOLUME:
+ case WM5100_OUT1RMIX_INPUT_4_SOURCE:
+ case WM5100_OUT1RMIX_INPUT_4_VOLUME:
+ case WM5100_OUT2LMIX_INPUT_1_SOURCE:
+ case WM5100_OUT2LMIX_INPUT_1_VOLUME:
+ case WM5100_OUT2LMIX_INPUT_2_SOURCE:
+ case WM5100_OUT2LMIX_INPUT_2_VOLUME:
+ case WM5100_OUT2LMIX_INPUT_3_SOURCE:
+ case WM5100_OUT2LMIX_INPUT_3_VOLUME:
+ case WM5100_OUT2LMIX_INPUT_4_SOURCE:
+ case WM5100_OUT2LMIX_INPUT_4_VOLUME:
+ case WM5100_OUT2RMIX_INPUT_1_SOURCE:
+ case WM5100_OUT2RMIX_INPUT_1_VOLUME:
+ case WM5100_OUT2RMIX_INPUT_2_SOURCE:
+ case WM5100_OUT2RMIX_INPUT_2_VOLUME:
+ case WM5100_OUT2RMIX_INPUT_3_SOURCE:
+ case WM5100_OUT2RMIX_INPUT_3_VOLUME:
+ case WM5100_OUT2RMIX_INPUT_4_SOURCE:
+ case WM5100_OUT2RMIX_INPUT_4_VOLUME:
+ case WM5100_OUT3LMIX_INPUT_1_SOURCE:
+ case WM5100_OUT3LMIX_INPUT_1_VOLUME:
+ case WM5100_OUT3LMIX_INPUT_2_SOURCE:
+ case WM5100_OUT3LMIX_INPUT_2_VOLUME:
+ case WM5100_OUT3LMIX_INPUT_3_SOURCE:
+ case WM5100_OUT3LMIX_INPUT_3_VOLUME:
+ case WM5100_OUT3LMIX_INPUT_4_SOURCE:
+ case WM5100_OUT3LMIX_INPUT_4_VOLUME:
+ case WM5100_OUT3RMIX_INPUT_1_SOURCE:
+ case WM5100_OUT3RMIX_INPUT_1_VOLUME:
+ case WM5100_OUT3RMIX_INPUT_2_SOURCE:
+ case WM5100_OUT3RMIX_INPUT_2_VOLUME:
+ case WM5100_OUT3RMIX_INPUT_3_SOURCE:
+ case WM5100_OUT3RMIX_INPUT_3_VOLUME:
+ case WM5100_OUT3RMIX_INPUT_4_SOURCE:
+ case WM5100_OUT3RMIX_INPUT_4_VOLUME:
+ case WM5100_OUT4LMIX_INPUT_1_SOURCE:
+ case WM5100_OUT4LMIX_INPUT_1_VOLUME:
+ case WM5100_OUT4LMIX_INPUT_2_SOURCE:
+ case WM5100_OUT4LMIX_INPUT_2_VOLUME:
+ case WM5100_OUT4LMIX_INPUT_3_SOURCE:
+ case WM5100_OUT4LMIX_INPUT_3_VOLUME:
+ case WM5100_OUT4LMIX_INPUT_4_SOURCE:
+ case WM5100_OUT4LMIX_INPUT_4_VOLUME:
+ case WM5100_OUT4RMIX_INPUT_1_SOURCE:
+ case WM5100_OUT4RMIX_INPUT_1_VOLUME:
+ case WM5100_OUT4RMIX_INPUT_2_SOURCE:
+ case WM5100_OUT4RMIX_INPUT_2_VOLUME:
+ case WM5100_OUT4RMIX_INPUT_3_SOURCE:
+ case WM5100_OUT4RMIX_INPUT_3_VOLUME:
+ case WM5100_OUT4RMIX_INPUT_4_SOURCE:
+ case WM5100_OUT4RMIX_INPUT_4_VOLUME:
+ case WM5100_OUT5LMIX_INPUT_1_SOURCE:
+ case WM5100_OUT5LMIX_INPUT_1_VOLUME:
+ case WM5100_OUT5LMIX_INPUT_2_SOURCE:
+ case WM5100_OUT5LMIX_INPUT_2_VOLUME:
+ case WM5100_OUT5LMIX_INPUT_3_SOURCE:
+ case WM5100_OUT5LMIX_INPUT_3_VOLUME:
+ case WM5100_OUT5LMIX_INPUT_4_SOURCE:
+ case WM5100_OUT5LMIX_INPUT_4_VOLUME:
+ case WM5100_OUT5RMIX_INPUT_1_SOURCE:
+ case WM5100_OUT5RMIX_INPUT_1_VOLUME:
+ case WM5100_OUT5RMIX_INPUT_2_SOURCE:
+ case WM5100_OUT5RMIX_INPUT_2_VOLUME:
+ case WM5100_OUT5RMIX_INPUT_3_SOURCE:
+ case WM5100_OUT5RMIX_INPUT_3_VOLUME:
+ case WM5100_OUT5RMIX_INPUT_4_SOURCE:
+ case WM5100_OUT5RMIX_INPUT_4_VOLUME:
+ case WM5100_OUT6LMIX_INPUT_1_SOURCE:
+ case WM5100_OUT6LMIX_INPUT_1_VOLUME:
+ case WM5100_OUT6LMIX_INPUT_2_SOURCE:
+ case WM5100_OUT6LMIX_INPUT_2_VOLUME:
+ case WM5100_OUT6LMIX_INPUT_3_SOURCE:
+ case WM5100_OUT6LMIX_INPUT_3_VOLUME:
+ case WM5100_OUT6LMIX_INPUT_4_SOURCE:
+ case WM5100_OUT6LMIX_INPUT_4_VOLUME:
+ case WM5100_OUT6RMIX_INPUT_1_SOURCE:
+ case WM5100_OUT6RMIX_INPUT_1_VOLUME:
+ case WM5100_OUT6RMIX_INPUT_2_SOURCE:
+ case WM5100_OUT6RMIX_INPUT_2_VOLUME:
+ case WM5100_OUT6RMIX_INPUT_3_SOURCE:
+ case WM5100_OUT6RMIX_INPUT_3_VOLUME:
+ case WM5100_OUT6RMIX_INPUT_4_SOURCE:
+ case WM5100_OUT6RMIX_INPUT_4_VOLUME:
+ case WM5100_AIF1TX1MIX_INPUT_1_SOURCE:
+ case WM5100_AIF1TX1MIX_INPUT_1_VOLUME:
+ case WM5100_AIF1TX1MIX_INPUT_2_SOURCE:
+ case WM5100_AIF1TX1MIX_INPUT_2_VOLUME:
+ case WM5100_AIF1TX1MIX_INPUT_3_SOURCE:
+ case WM5100_AIF1TX1MIX_INPUT_3_VOLUME:
+ case WM5100_AIF1TX1MIX_INPUT_4_SOURCE:
+ case WM5100_AIF1TX1MIX_INPUT_4_VOLUME:
+ case WM5100_AIF1TX2MIX_INPUT_1_SOURCE:
+ case WM5100_AIF1TX2MIX_INPUT_1_VOLUME:
+ case WM5100_AIF1TX2MIX_INPUT_2_SOURCE:
+ case WM5100_AIF1TX2MIX_INPUT_2_VOLUME:
+ case WM5100_AIF1TX2MIX_INPUT_3_SOURCE:
+ case WM5100_AIF1TX2MIX_INPUT_3_VOLUME:
+ case WM5100_AIF1TX2MIX_INPUT_4_SOURCE:
+ case WM5100_AIF1TX2MIX_INPUT_4_VOLUME:
+ case WM5100_AIF1TX3MIX_INPUT_1_SOURCE:
+ case WM5100_AIF1TX3MIX_INPUT_1_VOLUME:
+ case WM5100_AIF1TX3MIX_INPUT_2_SOURCE:
+ case WM5100_AIF1TX3MIX_INPUT_2_VOLUME:
+ case WM5100_AIF1TX3MIX_INPUT_3_SOURCE:
+ case WM5100_AIF1TX3MIX_INPUT_3_VOLUME:
+ case WM5100_AIF1TX3MIX_INPUT_4_SOURCE:
+ case WM5100_AIF1TX3MIX_INPUT_4_VOLUME:
+ case WM5100_AIF1TX4MIX_INPUT_1_SOURCE:
+ case WM5100_AIF1TX4MIX_INPUT_1_VOLUME:
+ case WM5100_AIF1TX4MIX_INPUT_2_SOURCE:
+ case WM5100_AIF1TX4MIX_INPUT_2_VOLUME:
+ case WM5100_AIF1TX4MIX_INPUT_3_SOURCE:
+ case WM5100_AIF1TX4MIX_INPUT_3_VOLUME:
+ case WM5100_AIF1TX4MIX_INPUT_4_SOURCE:
+ case WM5100_AIF1TX4MIX_INPUT_4_VOLUME:
+ case WM5100_AIF1TX5MIX_INPUT_1_SOURCE:
+ case WM5100_AIF1TX5MIX_INPUT_1_VOLUME:
+ case WM5100_AIF1TX5MIX_INPUT_2_SOURCE:
+ case WM5100_AIF1TX5MIX_INPUT_2_VOLUME:
+ case WM5100_AIF1TX5MIX_INPUT_3_SOURCE:
+ case WM5100_AIF1TX5MIX_INPUT_3_VOLUME:
+ case WM5100_AIF1TX5MIX_INPUT_4_SOURCE:
+ case WM5100_AIF1TX5MIX_INPUT_4_VOLUME:
+ case WM5100_AIF1TX6MIX_INPUT_1_SOURCE:
+ case WM5100_AIF1TX6MIX_INPUT_1_VOLUME:
+ case WM5100_AIF1TX6MIX_INPUT_2_SOURCE:
+ case WM5100_AIF1TX6MIX_INPUT_2_VOLUME:
+ case WM5100_AIF1TX6MIX_INPUT_3_SOURCE:
+ case WM5100_AIF1TX6MIX_INPUT_3_VOLUME:
+ case WM5100_AIF1TX6MIX_INPUT_4_SOURCE:
+ case WM5100_AIF1TX6MIX_INPUT_4_VOLUME:
+ case WM5100_AIF1TX7MIX_INPUT_1_SOURCE:
+ case WM5100_AIF1TX7MIX_INPUT_1_VOLUME:
+ case WM5100_AIF1TX7MIX_INPUT_2_SOURCE:
+ case WM5100_AIF1TX7MIX_INPUT_2_VOLUME:
+ case WM5100_AIF1TX7MIX_INPUT_3_SOURCE:
+ case WM5100_AIF1TX7MIX_INPUT_3_VOLUME:
+ case WM5100_AIF1TX7MIX_INPUT_4_SOURCE:
+ case WM5100_AIF1TX7MIX_INPUT_4_VOLUME:
+ case WM5100_AIF1TX8MIX_INPUT_1_SOURCE:
+ case WM5100_AIF1TX8MIX_INPUT_1_VOLUME:
+ case WM5100_AIF1TX8MIX_INPUT_2_SOURCE:
+ case WM5100_AIF1TX8MIX_INPUT_2_VOLUME:
+ case WM5100_AIF1TX8MIX_INPUT_3_SOURCE:
+ case WM5100_AIF1TX8MIX_INPUT_3_VOLUME:
+ case WM5100_AIF1TX8MIX_INPUT_4_SOURCE:
+ case WM5100_AIF1TX8MIX_INPUT_4_VOLUME:
+ case WM5100_AIF2TX1MIX_INPUT_1_SOURCE:
+ case WM5100_AIF2TX1MIX_INPUT_1_VOLUME:
+ case WM5100_AIF2TX1MIX_INPUT_2_SOURCE:
+ case WM5100_AIF2TX1MIX_INPUT_2_VOLUME:
+ case WM5100_AIF2TX1MIX_INPUT_3_SOURCE:
+ case WM5100_AIF2TX1MIX_INPUT_3_VOLUME:
+ case WM5100_AIF2TX1MIX_INPUT_4_SOURCE:
+ case WM5100_AIF2TX1MIX_INPUT_4_VOLUME:
+ case WM5100_AIF2TX2MIX_INPUT_1_SOURCE:
+ case WM5100_AIF2TX2MIX_INPUT_1_VOLUME:
+ case WM5100_AIF2TX2MIX_INPUT_2_SOURCE:
+ case WM5100_AIF2TX2MIX_INPUT_2_VOLUME:
+ case WM5100_AIF2TX2MIX_INPUT_3_SOURCE:
+ case WM5100_AIF2TX2MIX_INPUT_3_VOLUME:
+ case WM5100_AIF2TX2MIX_INPUT_4_SOURCE:
+ case WM5100_AIF2TX2MIX_INPUT_4_VOLUME:
+ case WM5100_AIF3TX1MIX_INPUT_1_SOURCE:
+ case WM5100_AIF3TX1MIX_INPUT_1_VOLUME:
+ case WM5100_AIF3TX1MIX_INPUT_2_SOURCE:
+ case WM5100_AIF3TX1MIX_INPUT_2_VOLUME:
+ case WM5100_AIF3TX1MIX_INPUT_3_SOURCE:
+ case WM5100_AIF3TX1MIX_INPUT_3_VOLUME:
+ case WM5100_AIF3TX1MIX_INPUT_4_SOURCE:
+ case WM5100_AIF3TX1MIX_INPUT_4_VOLUME:
+ case WM5100_AIF3TX2MIX_INPUT_1_SOURCE:
+ case WM5100_AIF3TX2MIX_INPUT_1_VOLUME:
+ case WM5100_AIF3TX2MIX_INPUT_2_SOURCE:
+ case WM5100_AIF3TX2MIX_INPUT_2_VOLUME:
+ case WM5100_AIF3TX2MIX_INPUT_3_SOURCE:
+ case WM5100_AIF3TX2MIX_INPUT_3_VOLUME:
+ case WM5100_AIF3TX2MIX_INPUT_4_SOURCE:
+ case WM5100_AIF3TX2MIX_INPUT_4_VOLUME:
+ case WM5100_EQ1MIX_INPUT_1_SOURCE:
+ case WM5100_EQ1MIX_INPUT_1_VOLUME:
+ case WM5100_EQ1MIX_INPUT_2_SOURCE:
+ case WM5100_EQ1MIX_INPUT_2_VOLUME:
+ case WM5100_EQ1MIX_INPUT_3_SOURCE:
+ case WM5100_EQ1MIX_INPUT_3_VOLUME:
+ case WM5100_EQ1MIX_INPUT_4_SOURCE:
+ case WM5100_EQ1MIX_INPUT_4_VOLUME:
+ case WM5100_EQ2MIX_INPUT_1_SOURCE:
+ case WM5100_EQ2MIX_INPUT_1_VOLUME:
+ case WM5100_EQ2MIX_INPUT_2_SOURCE:
+ case WM5100_EQ2MIX_INPUT_2_VOLUME:
+ case WM5100_EQ2MIX_INPUT_3_SOURCE:
+ case WM5100_EQ2MIX_INPUT_3_VOLUME:
+ case WM5100_EQ2MIX_INPUT_4_SOURCE:
+ case WM5100_EQ2MIX_INPUT_4_VOLUME:
+ case WM5100_EQ3MIX_INPUT_1_SOURCE:
+ case WM5100_EQ3MIX_INPUT_1_VOLUME:
+ case WM5100_EQ3MIX_INPUT_2_SOURCE:
+ case WM5100_EQ3MIX_INPUT_2_VOLUME:
+ case WM5100_EQ3MIX_INPUT_3_SOURCE:
+ case WM5100_EQ3MIX_INPUT_3_VOLUME:
+ case WM5100_EQ3MIX_INPUT_4_SOURCE:
+ case WM5100_EQ3MIX_INPUT_4_VOLUME:
+ case WM5100_EQ4MIX_INPUT_1_SOURCE:
+ case WM5100_EQ4MIX_INPUT_1_VOLUME:
+ case WM5100_EQ4MIX_INPUT_2_SOURCE:
+ case WM5100_EQ4MIX_INPUT_2_VOLUME:
+ case WM5100_EQ4MIX_INPUT_3_SOURCE:
+ case WM5100_EQ4MIX_INPUT_3_VOLUME:
+ case WM5100_EQ4MIX_INPUT_4_SOURCE:
+ case WM5100_EQ4MIX_INPUT_4_VOLUME:
+ case WM5100_DRC1LMIX_INPUT_1_SOURCE:
+ case WM5100_DRC1LMIX_INPUT_1_VOLUME:
+ case WM5100_DRC1LMIX_INPUT_2_SOURCE:
+ case WM5100_DRC1LMIX_INPUT_2_VOLUME:
+ case WM5100_DRC1LMIX_INPUT_3_SOURCE:
+ case WM5100_DRC1LMIX_INPUT_3_VOLUME:
+ case WM5100_DRC1LMIX_INPUT_4_SOURCE:
+ case WM5100_DRC1LMIX_INPUT_4_VOLUME:
+ case WM5100_DRC1RMIX_INPUT_1_SOURCE:
+ case WM5100_DRC1RMIX_INPUT_1_VOLUME:
+ case WM5100_DRC1RMIX_INPUT_2_SOURCE:
+ case WM5100_DRC1RMIX_INPUT_2_VOLUME:
+ case WM5100_DRC1RMIX_INPUT_3_SOURCE:
+ case WM5100_DRC1RMIX_INPUT_3_VOLUME:
+ case WM5100_DRC1RMIX_INPUT_4_SOURCE:
+ case WM5100_DRC1RMIX_INPUT_4_VOLUME:
+ case WM5100_HPLP1MIX_INPUT_1_SOURCE:
+ case WM5100_HPLP1MIX_INPUT_1_VOLUME:
+ case WM5100_HPLP1MIX_INPUT_2_SOURCE:
+ case WM5100_HPLP1MIX_INPUT_2_VOLUME:
+ case WM5100_HPLP1MIX_INPUT_3_SOURCE:
+ case WM5100_HPLP1MIX_INPUT_3_VOLUME:
+ case WM5100_HPLP1MIX_INPUT_4_SOURCE:
+ case WM5100_HPLP1MIX_INPUT_4_VOLUME:
+ case WM5100_HPLP2MIX_INPUT_1_SOURCE:
+ case WM5100_HPLP2MIX_INPUT_1_VOLUME:
+ case WM5100_HPLP2MIX_INPUT_2_SOURCE:
+ case WM5100_HPLP2MIX_INPUT_2_VOLUME:
+ case WM5100_HPLP2MIX_INPUT_3_SOURCE:
+ case WM5100_HPLP2MIX_INPUT_3_VOLUME:
+ case WM5100_HPLP2MIX_INPUT_4_SOURCE:
+ case WM5100_HPLP2MIX_INPUT_4_VOLUME:
+ case WM5100_HPLP3MIX_INPUT_1_SOURCE:
+ case WM5100_HPLP3MIX_INPUT_1_VOLUME:
+ case WM5100_HPLP3MIX_INPUT_2_SOURCE:
+ case WM5100_HPLP3MIX_INPUT_2_VOLUME:
+ case WM5100_HPLP3MIX_INPUT_3_SOURCE:
+ case WM5100_HPLP3MIX_INPUT_3_VOLUME:
+ case WM5100_HPLP3MIX_INPUT_4_SOURCE:
+ case WM5100_HPLP3MIX_INPUT_4_VOLUME:
+ case WM5100_HPLP4MIX_INPUT_1_SOURCE:
+ case WM5100_HPLP4MIX_INPUT_1_VOLUME:
+ case WM5100_HPLP4MIX_INPUT_2_SOURCE:
+ case WM5100_HPLP4MIX_INPUT_2_VOLUME:
+ case WM5100_HPLP4MIX_INPUT_3_SOURCE:
+ case WM5100_HPLP4MIX_INPUT_3_VOLUME:
+ case WM5100_HPLP4MIX_INPUT_4_SOURCE:
+ case WM5100_HPLP4MIX_INPUT_4_VOLUME:
+ case WM5100_DSP1LMIX_INPUT_1_SOURCE:
+ case WM5100_DSP1LMIX_INPUT_1_VOLUME:
+ case WM5100_DSP1LMIX_INPUT_2_SOURCE:
+ case WM5100_DSP1LMIX_INPUT_2_VOLUME:
+ case WM5100_DSP1LMIX_INPUT_3_SOURCE:
+ case WM5100_DSP1LMIX_INPUT_3_VOLUME:
+ case WM5100_DSP1LMIX_INPUT_4_SOURCE:
+ case WM5100_DSP1LMIX_INPUT_4_VOLUME:
+ case WM5100_DSP1RMIX_INPUT_1_SOURCE:
+ case WM5100_DSP1RMIX_INPUT_1_VOLUME:
+ case WM5100_DSP1RMIX_INPUT_2_SOURCE:
+ case WM5100_DSP1RMIX_INPUT_2_VOLUME:
+ case WM5100_DSP1RMIX_INPUT_3_SOURCE:
+ case WM5100_DSP1RMIX_INPUT_3_VOLUME:
+ case WM5100_DSP1RMIX_INPUT_4_SOURCE:
+ case WM5100_DSP1RMIX_INPUT_4_VOLUME:
+ case WM5100_DSP1AUX1MIX_INPUT_1_SOURCE:
+ case WM5100_DSP1AUX2MIX_INPUT_1_SOURCE:
+ case WM5100_DSP1AUX3MIX_INPUT_1_SOURCE:
+ case WM5100_DSP1AUX4MIX_INPUT_1_SOURCE:
+ case WM5100_DSP1AUX5MIX_INPUT_1_SOURCE:
+ case WM5100_DSP1AUX6MIX_INPUT_1_SOURCE:
+ case WM5100_DSP2LMIX_INPUT_1_SOURCE:
+ case WM5100_DSP2LMIX_INPUT_1_VOLUME:
+ case WM5100_DSP2LMIX_INPUT_2_SOURCE:
+ case WM5100_DSP2LMIX_INPUT_2_VOLUME:
+ case WM5100_DSP2LMIX_INPUT_3_SOURCE:
+ case WM5100_DSP2LMIX_INPUT_3_VOLUME:
+ case WM5100_DSP2LMIX_INPUT_4_SOURCE:
+ case WM5100_DSP2LMIX_INPUT_4_VOLUME:
+ case WM5100_DSP2RMIX_INPUT_1_SOURCE:
+ case WM5100_DSP2RMIX_INPUT_1_VOLUME:
+ case WM5100_DSP2RMIX_INPUT_2_SOURCE:
+ case WM5100_DSP2RMIX_INPUT_2_VOLUME:
+ case WM5100_DSP2RMIX_INPUT_3_SOURCE:
+ case WM5100_DSP2RMIX_INPUT_3_VOLUME:
+ case WM5100_DSP2RMIX_INPUT_4_SOURCE:
+ case WM5100_DSP2RMIX_INPUT_4_VOLUME:
+ case WM5100_DSP2AUX1MIX_INPUT_1_SOURCE:
+ case WM5100_DSP2AUX2MIX_INPUT_1_SOURCE:
+ case WM5100_DSP2AUX3MIX_INPUT_1_SOURCE:
+ case WM5100_DSP2AUX4MIX_INPUT_1_SOURCE:
+ case WM5100_DSP2AUX5MIX_INPUT_1_SOURCE:
+ case WM5100_DSP2AUX6MIX_INPUT_1_SOURCE:
+ case WM5100_DSP3LMIX_INPUT_1_SOURCE:
+ case WM5100_DSP3LMIX_INPUT_1_VOLUME:
+ case WM5100_DSP3LMIX_INPUT_2_SOURCE:
+ case WM5100_DSP3LMIX_INPUT_2_VOLUME:
+ case WM5100_DSP3LMIX_INPUT_3_SOURCE:
+ case WM5100_DSP3LMIX_INPUT_3_VOLUME:
+ case WM5100_DSP3LMIX_INPUT_4_SOURCE:
+ case WM5100_DSP3LMIX_INPUT_4_VOLUME:
+ case WM5100_DSP3RMIX_INPUT_1_SOURCE:
+ case WM5100_DSP3RMIX_INPUT_1_VOLUME:
+ case WM5100_DSP3RMIX_INPUT_2_SOURCE:
+ case WM5100_DSP3RMIX_INPUT_2_VOLUME:
+ case WM5100_DSP3RMIX_INPUT_3_SOURCE:
+ case WM5100_DSP3RMIX_INPUT_3_VOLUME:
+ case WM5100_DSP3RMIX_INPUT_4_SOURCE:
+ case WM5100_DSP3RMIX_INPUT_4_VOLUME:
+ case WM5100_DSP3AUX1MIX_INPUT_1_SOURCE:
+ case WM5100_DSP3AUX2MIX_INPUT_1_SOURCE:
+ case WM5100_DSP3AUX3MIX_INPUT_1_SOURCE:
+ case WM5100_DSP3AUX4MIX_INPUT_1_SOURCE:
+ case WM5100_DSP3AUX5MIX_INPUT_1_SOURCE:
+ case WM5100_DSP3AUX6MIX_INPUT_1_SOURCE:
+ case WM5100_ASRC1LMIX_INPUT_1_SOURCE:
+ case WM5100_ASRC1RMIX_INPUT_1_SOURCE:
+ case WM5100_ASRC2LMIX_INPUT_1_SOURCE:
+ case WM5100_ASRC2RMIX_INPUT_1_SOURCE:
+ case WM5100_ISRC1DEC1MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC1DEC2MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC1DEC3MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC1DEC4MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC1INT1MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC1INT2MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC1INT3MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC1INT4MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC2DEC1MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC2DEC2MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC2DEC3MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC2DEC4MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC2INT1MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC2INT2MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC2INT3MIX_INPUT_1_SOURCE:
+ case WM5100_ISRC2INT4MIX_INPUT_1_SOURCE:
+ case WM5100_GPIO_CTRL_1:
+ case WM5100_GPIO_CTRL_2:
+ case WM5100_GPIO_CTRL_3:
+ case WM5100_GPIO_CTRL_4:
+ case WM5100_GPIO_CTRL_5:
+ case WM5100_GPIO_CTRL_6:
+ case WM5100_MISC_PAD_CTRL_1:
+ case WM5100_MISC_PAD_CTRL_2:
+ case WM5100_MISC_PAD_CTRL_3:
+ case WM5100_MISC_PAD_CTRL_4:
+ case WM5100_MISC_PAD_CTRL_5:
+ case WM5100_MISC_GPIO_1:
+ case WM5100_INTERRUPT_STATUS_1:
+ case WM5100_INTERRUPT_STATUS_2:
+ case WM5100_INTERRUPT_STATUS_3:
+ case WM5100_INTERRUPT_STATUS_4:
+ case WM5100_INTERRUPT_RAW_STATUS_2:
+ case WM5100_INTERRUPT_RAW_STATUS_3:
+ case WM5100_INTERRUPT_RAW_STATUS_4:
+ case WM5100_INTERRUPT_STATUS_1_MASK:
+ case WM5100_INTERRUPT_STATUS_2_MASK:
+ case WM5100_INTERRUPT_STATUS_3_MASK:
+ case WM5100_INTERRUPT_STATUS_4_MASK:
+ case WM5100_INTERRUPT_CONTROL:
+ case WM5100_IRQ_DEBOUNCE_1:
+ case WM5100_IRQ_DEBOUNCE_2:
+ case WM5100_FX_CTRL:
+ case WM5100_EQ1_1:
+ case WM5100_EQ1_2:
+ case WM5100_EQ1_3:
+ case WM5100_EQ1_4:
+ case WM5100_EQ1_5:
+ case WM5100_EQ1_6:
+ case WM5100_EQ1_7:
+ case WM5100_EQ1_8:
+ case WM5100_EQ1_9:
+ case WM5100_EQ1_10:
+ case WM5100_EQ1_11:
+ case WM5100_EQ1_12:
+ case WM5100_EQ1_13:
+ case WM5100_EQ1_14:
+ case WM5100_EQ1_15:
+ case WM5100_EQ1_16:
+ case WM5100_EQ1_17:
+ case WM5100_EQ1_18:
+ case WM5100_EQ1_19:
+ case WM5100_EQ1_20:
+ case WM5100_EQ2_1:
+ case WM5100_EQ2_2:
+ case WM5100_EQ2_3:
+ case WM5100_EQ2_4:
+ case WM5100_EQ2_5:
+ case WM5100_EQ2_6:
+ case WM5100_EQ2_7:
+ case WM5100_EQ2_8:
+ case WM5100_EQ2_9:
+ case WM5100_EQ2_10:
+ case WM5100_EQ2_11:
+ case WM5100_EQ2_12:
+ case WM5100_EQ2_13:
+ case WM5100_EQ2_14:
+ case WM5100_EQ2_15:
+ case WM5100_EQ2_16:
+ case WM5100_EQ2_17:
+ case WM5100_EQ2_18:
+ case WM5100_EQ2_19:
+ case WM5100_EQ2_20:
+ case WM5100_EQ3_1:
+ case WM5100_EQ3_2:
+ case WM5100_EQ3_3:
+ case WM5100_EQ3_4:
+ case WM5100_EQ3_5:
+ case WM5100_EQ3_6:
+ case WM5100_EQ3_7:
+ case WM5100_EQ3_8:
+ case WM5100_EQ3_9:
+ case WM5100_EQ3_10:
+ case WM5100_EQ3_11:
+ case WM5100_EQ3_12:
+ case WM5100_EQ3_13:
+ case WM5100_EQ3_14:
+ case WM5100_EQ3_15:
+ case WM5100_EQ3_16:
+ case WM5100_EQ3_17:
+ case WM5100_EQ3_18:
+ case WM5100_EQ3_19:
+ case WM5100_EQ3_20:
+ case WM5100_EQ4_1:
+ case WM5100_EQ4_2:
+ case WM5100_EQ4_3:
+ case WM5100_EQ4_4:
+ case WM5100_EQ4_5:
+ case WM5100_EQ4_6:
+ case WM5100_EQ4_7:
+ case WM5100_EQ4_8:
+ case WM5100_EQ4_9:
+ case WM5100_EQ4_10:
+ case WM5100_EQ4_11:
+ case WM5100_EQ4_12:
+ case WM5100_EQ4_13:
+ case WM5100_EQ4_14:
+ case WM5100_EQ4_15:
+ case WM5100_EQ4_16:
+ case WM5100_EQ4_17:
+ case WM5100_EQ4_18:
+ case WM5100_EQ4_19:
+ case WM5100_EQ4_20:
+ case WM5100_DRC1_CTRL1:
+ case WM5100_DRC1_CTRL2:
+ case WM5100_DRC1_CTRL3:
+ case WM5100_DRC1_CTRL4:
+ case WM5100_DRC1_CTRL5:
+ case WM5100_HPLPF1_1:
+ case WM5100_HPLPF1_2:
+ case WM5100_HPLPF2_1:
+ case WM5100_HPLPF2_2:
+ case WM5100_HPLPF3_1:
+ case WM5100_HPLPF3_2:
+ case WM5100_HPLPF4_1:
+ case WM5100_HPLPF4_2:
+ case WM5100_DSP1_CONTROL_1:
+ case WM5100_DSP1_CONTROL_2:
+ case WM5100_DSP1_CONTROL_3:
+ case WM5100_DSP1_CONTROL_4:
+ case WM5100_DSP1_CONTROL_5:
+ case WM5100_DSP1_CONTROL_6:
+ case WM5100_DSP1_CONTROL_7:
+ case WM5100_DSP1_CONTROL_8:
+ case WM5100_DSP1_CONTROL_9:
+ case WM5100_DSP1_CONTROL_10:
+ case WM5100_DSP1_CONTROL_11:
+ case WM5100_DSP1_CONTROL_12:
+ case WM5100_DSP1_CONTROL_13:
+ case WM5100_DSP1_CONTROL_14:
+ case WM5100_DSP1_CONTROL_15:
+ case WM5100_DSP1_CONTROL_16:
+ case WM5100_DSP1_CONTROL_17:
+ case WM5100_DSP1_CONTROL_18:
+ case WM5100_DSP1_CONTROL_19:
+ case WM5100_DSP1_CONTROL_20:
+ case WM5100_DSP1_CONTROL_21:
+ case WM5100_DSP1_CONTROL_22:
+ case WM5100_DSP1_CONTROL_23:
+ case WM5100_DSP1_CONTROL_24:
+ case WM5100_DSP1_CONTROL_25:
+ case WM5100_DSP1_CONTROL_26:
+ case WM5100_DSP1_CONTROL_27:
+ case WM5100_DSP1_CONTROL_28:
+ case WM5100_DSP1_CONTROL_29:
+ case WM5100_DSP1_CONTROL_30:
+ case WM5100_DSP2_CONTROL_1:
+ case WM5100_DSP2_CONTROL_2:
+ case WM5100_DSP2_CONTROL_3:
+ case WM5100_DSP2_CONTROL_4:
+ case WM5100_DSP2_CONTROL_5:
+ case WM5100_DSP2_CONTROL_6:
+ case WM5100_DSP2_CONTROL_7:
+ case WM5100_DSP2_CONTROL_8:
+ case WM5100_DSP2_CONTROL_9:
+ case WM5100_DSP2_CONTROL_10:
+ case WM5100_DSP2_CONTROL_11:
+ case WM5100_DSP2_CONTROL_12:
+ case WM5100_DSP2_CONTROL_13:
+ case WM5100_DSP2_CONTROL_14:
+ case WM5100_DSP2_CONTROL_15:
+ case WM5100_DSP2_CONTROL_16:
+ case WM5100_DSP2_CONTROL_17:
+ case WM5100_DSP2_CONTROL_18:
+ case WM5100_DSP2_CONTROL_19:
+ case WM5100_DSP2_CONTROL_20:
+ case WM5100_DSP2_CONTROL_21:
+ case WM5100_DSP2_CONTROL_22:
+ case WM5100_DSP2_CONTROL_23:
+ case WM5100_DSP2_CONTROL_24:
+ case WM5100_DSP2_CONTROL_25:
+ case WM5100_DSP2_CONTROL_26:
+ case WM5100_DSP2_CONTROL_27:
+ case WM5100_DSP2_CONTROL_28:
+ case WM5100_DSP2_CONTROL_29:
+ case WM5100_DSP2_CONTROL_30:
+ case WM5100_DSP3_CONTROL_1:
+ case WM5100_DSP3_CONTROL_2:
+ case WM5100_DSP3_CONTROL_3:
+ case WM5100_DSP3_CONTROL_4:
+ case WM5100_DSP3_CONTROL_5:
+ case WM5100_DSP3_CONTROL_6:
+ case WM5100_DSP3_CONTROL_7:
+ case WM5100_DSP3_CONTROL_8:
+ case WM5100_DSP3_CONTROL_9:
+ case WM5100_DSP3_CONTROL_10:
+ case WM5100_DSP3_CONTROL_11:
+ case WM5100_DSP3_CONTROL_12:
+ case WM5100_DSP3_CONTROL_13:
+ case WM5100_DSP3_CONTROL_14:
+ case WM5100_DSP3_CONTROL_15:
+ case WM5100_DSP3_CONTROL_16:
+ case WM5100_DSP3_CONTROL_17:
+ case WM5100_DSP3_CONTROL_18:
+ case WM5100_DSP3_CONTROL_19:
+ case WM5100_DSP3_CONTROL_20:
+ case WM5100_DSP3_CONTROL_21:
+ case WM5100_DSP3_CONTROL_22:
+ case WM5100_DSP3_CONTROL_23:
+ case WM5100_DSP3_CONTROL_24:
+ case WM5100_DSP3_CONTROL_25:
+ case WM5100_DSP3_CONTROL_26:
+ case WM5100_DSP3_CONTROL_27:
+ case WM5100_DSP3_CONTROL_28:
+ case WM5100_DSP3_CONTROL_29:
+ case WM5100_DSP3_CONTROL_30:
+ return 1;
+ default:
+ if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) ||
+ (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) ||
+ (reg >= WM5100_DSP1_DM_0 && reg <= WM5100_DSP1_DM_511) ||
+ (reg >= WM5100_DSP2_PM_0 && reg <= WM5100_DSP2_PM_1535) ||
+ (reg >= WM5100_DSP2_ZM_0 && reg <= WM5100_DSP2_ZM_2047) ||
+ (reg >= WM5100_DSP2_DM_0 && reg <= WM5100_DSP2_DM_511) ||
+ (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) ||
+ (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) ||
+ (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511))
+ return 1;
+ else
+ return 0;
+ }
+}
+
+struct reg_default wm5100_reg_defaults[WM5100_REGISTER_COUNT] = {
+ { 0x0000, 0x0000 }, /* R0 - software reset */
+ { 0x0001, 0x0000 }, /* R1 - Device Revision */
+ { 0x0010, 0x0801 }, /* R16 - Ctrl IF 1 */
+ { 0x0020, 0x0000 }, /* R32 - Tone Generator 1 */
+ { 0x0030, 0x0000 }, /* R48 - PWM Drive 1 */
+ { 0x0031, 0x0100 }, /* R49 - PWM Drive 2 */
+ { 0x0032, 0x0100 }, /* R50 - PWM Drive 3 */
+ { 0x0100, 0x0002 }, /* R256 - Clocking 1 */
+ { 0x0101, 0x0000 }, /* R257 - Clocking 3 */
+ { 0x0102, 0x0011 }, /* R258 - Clocking 4 */
+ { 0x0103, 0x0011 }, /* R259 - Clocking 5 */
+ { 0x0104, 0x0011 }, /* R260 - Clocking 6 */
+ { 0x0107, 0x0000 }, /* R263 - Clocking 7 */
+ { 0x0108, 0x0000 }, /* R264 - Clocking 8 */
+ { 0x0120, 0x0000 }, /* R288 - ASRC_ENABLE */
+ { 0x0121, 0x0000 }, /* R289 - ASRC_STATUS */
+ { 0x0122, 0x0000 }, /* R290 - ASRC_RATE1 */
+ { 0x0141, 0x8000 }, /* R321 - ISRC 1 CTRL 1 */
+ { 0x0142, 0x0000 }, /* R322 - ISRC 1 CTRL 2 */
+ { 0x0143, 0x8000 }, /* R323 - ISRC 2 CTRL1 */
+ { 0x0144, 0x0000 }, /* R324 - ISRC 2 CTRL 2 */
+ { 0x0182, 0x0000 }, /* R386 - FLL1 Control 1 */
+ { 0x0183, 0x0000 }, /* R387 - FLL1 Control 2 */
+ { 0x0184, 0x0000 }, /* R388 - FLL1 Control 3 */
+ { 0x0186, 0x0177 }, /* R390 - FLL1 Control 5 */
+ { 0x0187, 0x0001 }, /* R391 - FLL1 Control 6 */
+ { 0x0188, 0x0000 }, /* R392 - FLL1 EFS 1 */
+ { 0x01A2, 0x0000 }, /* R418 - FLL2 Control 1 */
+ { 0x01A3, 0x0000 }, /* R419 - FLL2 Control 2 */
+ { 0x01A4, 0x0000 }, /* R420 - FLL2 Control 3 */
+ { 0x01A6, 0x0177 }, /* R422 - FLL2 Control 5 */
+ { 0x01A7, 0x0001 }, /* R423 - FLL2 Control 6 */
+ { 0x01A8, 0x0000 }, /* R424 - FLL2 EFS 1 */
+ { 0x0200, 0x0020 }, /* R512 - Mic Charge Pump 1 */
+ { 0x0201, 0xB084 }, /* R513 - Mic Charge Pump 2 */
+ { 0x0202, 0xBBDE }, /* R514 - HP Charge Pump 1 */
+ { 0x0211, 0x20D4 }, /* R529 - LDO1 Control */
+ { 0x0215, 0x0062 }, /* R533 - Mic Bias Ctrl 1 */
+ { 0x0216, 0x0062 }, /* R534 - Mic Bias Ctrl 2 */
+ { 0x0217, 0x0062 }, /* R535 - Mic Bias Ctrl 3 */
+ { 0x0280, 0x0004 }, /* R640 - Accessory Detect Mode 1 */
+ { 0x0288, 0x0020 }, /* R648 - Headphone Detect 1 */
+ { 0x0289, 0x0000 }, /* R649 - Headphone Detect 2 */
+ { 0x0290, 0x1100 }, /* R656 - Mic Detect 1 */
+ { 0x0291, 0x009F }, /* R657 - Mic Detect 2 */
+ { 0x0292, 0x0000 }, /* R658 - Mic Detect 3 */
+ { 0x0301, 0x0000 }, /* R769 - Input Enables */
+ { 0x0302, 0x0000 }, /* R770 - Input Enables Status */
+ { 0x0310, 0x2280 }, /* R784 - Status */
+ { 0x0311, 0x0080 }, /* R785 - IN1R Control */
+ { 0x0312, 0x2280 }, /* R786 - IN2L Control */
+ { 0x0313, 0x0080 }, /* R787 - IN2R Control */
+ { 0x0314, 0x2280 }, /* R788 - IN3L Control */
+ { 0x0315, 0x0080 }, /* R789 - IN3R Control */
+ { 0x0316, 0x2280 }, /* R790 - IN4L Control */
+ { 0x0317, 0x0080 }, /* R791 - IN4R Control */
+ { 0x0318, 0x0000 }, /* R792 - RXANC_SRC */
+ { 0x0319, 0x0022 }, /* R793 - Input Volume Ramp */
+ { 0x0320, 0x0180 }, /* R800 - ADC Digital Volume 1L */
+ { 0x0321, 0x0180 }, /* R801 - ADC Digital Volume 1R */
+ { 0x0322, 0x0180 }, /* R802 - ADC Digital Volume 2L */
+ { 0x0323, 0x0180 }, /* R803 - ADC Digital Volume 2R */
+ { 0x0324, 0x0180 }, /* R804 - ADC Digital Volume 3L */
+ { 0x0325, 0x0180 }, /* R805 - ADC Digital Volume 3R */
+ { 0x0326, 0x0180 }, /* R806 - ADC Digital Volume 4L */
+ { 0x0327, 0x0180 }, /* R807 - ADC Digital Volume 4R */
+ { 0x0401, 0x0000 }, /* R1025 - Output Enables 2 */
+ { 0x0402, 0x0000 }, /* R1026 - Output Status 1 */
+ { 0x0403, 0x0000 }, /* R1027 - Output Status 2 */
+ { 0x0408, 0x0000 }, /* R1032 - Channel Enables 1 */
+ { 0x0410, 0x0080 }, /* R1040 - Out Volume 1L */
+ { 0x0411, 0x0080 }, /* R1041 - Out Volume 1R */
+ { 0x0412, 0x0080 }, /* R1042 - DAC Volume Limit 1L */
+ { 0x0413, 0x0080 }, /* R1043 - DAC Volume Limit 1R */
+ { 0x0414, 0x0080 }, /* R1044 - Out Volume 2L */
+ { 0x0415, 0x0080 }, /* R1045 - Out Volume 2R */
+ { 0x0416, 0x0080 }, /* R1046 - DAC Volume Limit 2L */
+ { 0x0417, 0x0080 }, /* R1047 - DAC Volume Limit 2R */
+ { 0x0418, 0x0080 }, /* R1048 - Out Volume 3L */
+ { 0x0419, 0x0080 }, /* R1049 - Out Volume 3R */
+ { 0x041A, 0x0080 }, /* R1050 - DAC Volume Limit 3L */
+ { 0x041B, 0x0080 }, /* R1051 - DAC Volume Limit 3R */
+ { 0x041C, 0x0080 }, /* R1052 - Out Volume 4L */
+ { 0x041D, 0x0080 }, /* R1053 - Out Volume 4R */
+ { 0x041E, 0x0080 }, /* R1054 - DAC Volume Limit 5L */
+ { 0x041F, 0x0080 }, /* R1055 - DAC Volume Limit 5R */
+ { 0x0420, 0x0080 }, /* R1056 - DAC Volume Limit 6L */
+ { 0x0421, 0x0080 }, /* R1057 - DAC Volume Limit 6R */
+ { 0x0440, 0x0000 }, /* R1088 - DAC AEC Control 1 */
+ { 0x0441, 0x0022 }, /* R1089 - Output Volume Ramp */
+ { 0x0480, 0x0180 }, /* R1152 - DAC Digital Volume 1L */
+ { 0x0481, 0x0180 }, /* R1153 - DAC Digital Volume 1R */
+ { 0x0482, 0x0180 }, /* R1154 - DAC Digital Volume 2L */
+ { 0x0483, 0x0180 }, /* R1155 - DAC Digital Volume 2R */
+ { 0x0484, 0x0180 }, /* R1156 - DAC Digital Volume 3L */
+ { 0x0485, 0x0180 }, /* R1157 - DAC Digital Volume 3R */
+ { 0x0486, 0x0180 }, /* R1158 - DAC Digital Volume 4L */
+ { 0x0487, 0x0180 }, /* R1159 - DAC Digital Volume 4R */
+ { 0x0488, 0x0180 }, /* R1160 - DAC Digital Volume 5L */
+ { 0x0489, 0x0180 }, /* R1161 - DAC Digital Volume 5R */
+ { 0x048A, 0x0180 }, /* R1162 - DAC Digital Volume 6L */
+ { 0x048B, 0x0180 }, /* R1163 - DAC Digital Volume 6R */
+ { 0x04C0, 0x0069 }, /* R1216 - PDM SPK1 CTRL 1 */
+ { 0x04C1, 0x0000 }, /* R1217 - PDM SPK1 CTRL 2 */
+ { 0x04C2, 0x0069 }, /* R1218 - PDM SPK2 CTRL 1 */
+ { 0x04C3, 0x0000 }, /* R1219 - PDM SPK2 CTRL 2 */
+ { 0x0500, 0x000C }, /* R1280 - Audio IF 1_1 */
+ { 0x0501, 0x0008 }, /* R1281 - Audio IF 1_2 */
+ { 0x0502, 0x0000 }, /* R1282 - Audio IF 1_3 */
+ { 0x0503, 0x0000 }, /* R1283 - Audio IF 1_4 */
+ { 0x0504, 0x0000 }, /* R1284 - Audio IF 1_5 */
+ { 0x0505, 0x0300 }, /* R1285 - Audio IF 1_6 */
+ { 0x0506, 0x0300 }, /* R1286 - Audio IF 1_7 */
+ { 0x0507, 0x1820 }, /* R1287 - Audio IF 1_8 */
+ { 0x0508, 0x1820 }, /* R1288 - Audio IF 1_9 */
+ { 0x0509, 0x0000 }, /* R1289 - Audio IF 1_10 */
+ { 0x050A, 0x0001 }, /* R1290 - Audio IF 1_11 */
+ { 0x050B, 0x0002 }, /* R1291 - Audio IF 1_12 */
+ { 0x050C, 0x0003 }, /* R1292 - Audio IF 1_13 */
+ { 0x050D, 0x0004 }, /* R1293 - Audio IF 1_14 */
+ { 0x050E, 0x0005 }, /* R1294 - Audio IF 1_15 */
+ { 0x050F, 0x0006 }, /* R1295 - Audio IF 1_16 */
+ { 0x0510, 0x0007 }, /* R1296 - Audio IF 1_17 */
+ { 0x0511, 0x0000 }, /* R1297 - Audio IF 1_18 */
+ { 0x0512, 0x0001 }, /* R1298 - Audio IF 1_19 */
+ { 0x0513, 0x0002 }, /* R1299 - Audio IF 1_20 */
+ { 0x0514, 0x0003 }, /* R1300 - Audio IF 1_21 */
+ { 0x0515, 0x0004 }, /* R1301 - Audio IF 1_22 */
+ { 0x0516, 0x0005 }, /* R1302 - Audio IF 1_23 */
+ { 0x0517, 0x0006 }, /* R1303 - Audio IF 1_24 */
+ { 0x0518, 0x0007 }, /* R1304 - Audio IF 1_25 */
+ { 0x0519, 0x0000 }, /* R1305 - Audio IF 1_26 */
+ { 0x051A, 0x0000 }, /* R1306 - Audio IF 1_27 */
+ { 0x0540, 0x000C }, /* R1344 - Audio IF 2_1 */
+ { 0x0541, 0x0008 }, /* R1345 - Audio IF 2_2 */
+ { 0x0542, 0x0000 }, /* R1346 - Audio IF 2_3 */
+ { 0x0543, 0x0000 }, /* R1347 - Audio IF 2_4 */
+ { 0x0544, 0x0000 }, /* R1348 - Audio IF 2_5 */
+ { 0x0545, 0x0300 }, /* R1349 - Audio IF 2_6 */
+ { 0x0546, 0x0300 }, /* R1350 - Audio IF 2_7 */
+ { 0x0547, 0x1820 }, /* R1351 - Audio IF 2_8 */
+ { 0x0548, 0x1820 }, /* R1352 - Audio IF 2_9 */
+ { 0x0549, 0x0000 }, /* R1353 - Audio IF 2_10 */
+ { 0x054A, 0x0001 }, /* R1354 - Audio IF 2_11 */
+ { 0x0551, 0x0000 }, /* R1361 - Audio IF 2_18 */
+ { 0x0552, 0x0001 }, /* R1362 - Audio IF 2_19 */
+ { 0x0559, 0x0000 }, /* R1369 - Audio IF 2_26 */
+ { 0x055A, 0x0000 }, /* R1370 - Audio IF 2_27 */
+ { 0x0580, 0x000C }, /* R1408 - Audio IF 3_1 */
+ { 0x0581, 0x0008 }, /* R1409 - Audio IF 3_2 */
+ { 0x0582, 0x0000 }, /* R1410 - Audio IF 3_3 */
+ { 0x0583, 0x0000 }, /* R1411 - Audio IF 3_4 */
+ { 0x0584, 0x0000 }, /* R1412 - Audio IF 3_5 */
+ { 0x0585, 0x0300 }, /* R1413 - Audio IF 3_6 */
+ { 0x0586, 0x0300 }, /* R1414 - Audio IF 3_7 */
+ { 0x0587, 0x1820 }, /* R1415 - Audio IF 3_8 */
+ { 0x0588, 0x1820 }, /* R1416 - Audio IF 3_9 */
+ { 0x0589, 0x0000 }, /* R1417 - Audio IF 3_10 */
+ { 0x058A, 0x0001 }, /* R1418 - Audio IF 3_11 */
+ { 0x0591, 0x0000 }, /* R1425 - Audio IF 3_18 */
+ { 0x0592, 0x0001 }, /* R1426 - Audio IF 3_19 */
+ { 0x0599, 0x0000 }, /* R1433 - Audio IF 3_26 */
+ { 0x059A, 0x0000 }, /* R1434 - Audio IF 3_27 */
+ { 0x0640, 0x0000 }, /* R1600 - PWM1MIX Input 1 Source */
+ { 0x0641, 0x0080 }, /* R1601 - PWM1MIX Input 1 Volume */
+ { 0x0642, 0x0000 }, /* R1602 - PWM1MIX Input 2 Source */
+ { 0x0643, 0x0080 }, /* R1603 - PWM1MIX Input 2 Volume */
+ { 0x0644, 0x0000 }, /* R1604 - PWM1MIX Input 3 Source */
+ { 0x0645, 0x0080 }, /* R1605 - PWM1MIX Input 3 Volume */
+ { 0x0646, 0x0000 }, /* R1606 - PWM1MIX Input 4 Source */
+ { 0x0647, 0x0080 }, /* R1607 - PWM1MIX Input 4 Volume */
+ { 0x0648, 0x0000 }, /* R1608 - PWM2MIX Input 1 Source */
+ { 0x0649, 0x0080 }, /* R1609 - PWM2MIX Input 1 Volume */
+ { 0x064A, 0x0000 }, /* R1610 - PWM2MIX Input 2 Source */
+ { 0x064B, 0x0080 }, /* R1611 - PWM2MIX Input 2 Volume */
+ { 0x064C, 0x0000 }, /* R1612 - PWM2MIX Input 3 Source */
+ { 0x064D, 0x0080 }, /* R1613 - PWM2MIX Input 3 Volume */
+ { 0x064E, 0x0000 }, /* R1614 - PWM2MIX Input 4 Source */
+ { 0x064F, 0x0080 }, /* R1615 - PWM2MIX Input 4 Volume */
+ { 0x0680, 0x0000 }, /* R1664 - OUT1LMIX Input 1 Source */
+ { 0x0681, 0x0080 }, /* R1665 - OUT1LMIX Input 1 Volume */
+ { 0x0682, 0x0000 }, /* R1666 - OUT1LMIX Input 2 Source */
+ { 0x0683, 0x0080 }, /* R1667 - OUT1LMIX Input 2 Volume */
+ { 0x0684, 0x0000 }, /* R1668 - OUT1LMIX Input 3 Source */
+ { 0x0685, 0x0080 }, /* R1669 - OUT1LMIX Input 3 Volume */
+ { 0x0686, 0x0000 }, /* R1670 - OUT1LMIX Input 4 Source */
+ { 0x0687, 0x0080 }, /* R1671 - OUT1LMIX Input 4 Volume */
+ { 0x0688, 0x0000 }, /* R1672 - OUT1RMIX Input 1 Source */
+ { 0x0689, 0x0080 }, /* R1673 - OUT1RMIX Input 1 Volume */
+ { 0x068A, 0x0000 }, /* R1674 - OUT1RMIX Input 2 Source */
+ { 0x068B, 0x0080 }, /* R1675 - OUT1RMIX Input 2 Volume */
+ { 0x068C, 0x0000 }, /* R1676 - OUT1RMIX Input 3 Source */
+ { 0x068D, 0x0080 }, /* R1677 - OUT1RMIX Input 3 Volume */
+ { 0x068E, 0x0000 }, /* R1678 - OUT1RMIX Input 4 Source */
+ { 0x068F, 0x0080 }, /* R1679 - OUT1RMIX Input 4 Volume */
+ { 0x0690, 0x0000 }, /* R1680 - OUT2LMIX Input 1 Source */
+ { 0x0691, 0x0080 }, /* R1681 - OUT2LMIX Input 1 Volume */
+ { 0x0692, 0x0000 }, /* R1682 - OUT2LMIX Input 2 Source */
+ { 0x0693, 0x0080 }, /* R1683 - OUT2LMIX Input 2 Volume */
+ { 0x0694, 0x0000 }, /* R1684 - OUT2LMIX Input 3 Source */
+ { 0x0695, 0x0080 }, /* R1685 - OUT2LMIX Input 3 Volume */
+ { 0x0696, 0x0000 }, /* R1686 - OUT2LMIX Input 4 Source */
+ { 0x0697, 0x0080 }, /* R1687 - OUT2LMIX Input 4 Volume */
+ { 0x0698, 0x0000 }, /* R1688 - OUT2RMIX Input 1 Source */
+ { 0x0699, 0x0080 }, /* R1689 - OUT2RMIX Input 1 Volume */
+ { 0x069A, 0x0000 }, /* R1690 - OUT2RMIX Input 2 Source */
+ { 0x069B, 0x0080 }, /* R1691 - OUT2RMIX Input 2 Volume */
+ { 0x069C, 0x0000 }, /* R1692 - OUT2RMIX Input 3 Source */
+ { 0x069D, 0x0080 }, /* R1693 - OUT2RMIX Input 3 Volume */
+ { 0x069E, 0x0000 }, /* R1694 - OUT2RMIX Input 4 Source */
+ { 0x069F, 0x0080 }, /* R1695 - OUT2RMIX Input 4 Volume */
+ { 0x06A0, 0x0000 }, /* R1696 - OUT3LMIX Input 1 Source */
+ { 0x06A1, 0x0080 }, /* R1697 - OUT3LMIX Input 1 Volume */
+ { 0x06A2, 0x0000 }, /* R1698 - OUT3LMIX Input 2 Source */
+ { 0x06A3, 0x0080 }, /* R1699 - OUT3LMIX Input 2 Volume */
+ { 0x06A4, 0x0000 }, /* R1700 - OUT3LMIX Input 3 Source */
+ { 0x06A5, 0x0080 }, /* R1701 - OUT3LMIX Input 3 Volume */
+ { 0x06A6, 0x0000 }, /* R1702 - OUT3LMIX Input 4 Source */
+ { 0x06A7, 0x0080 }, /* R1703 - OUT3LMIX Input 4 Volume */
+ { 0x06A8, 0x0000 }, /* R1704 - OUT3RMIX Input 1 Source */
+ { 0x06A9, 0x0080 }, /* R1705 - OUT3RMIX Input 1 Volume */
+ { 0x06AA, 0x0000 }, /* R1706 - OUT3RMIX Input 2 Source */
+ { 0x06AB, 0x0080 }, /* R1707 - OUT3RMIX Input 2 Volume */
+ { 0x06AC, 0x0000 }, /* R1708 - OUT3RMIX Input 3 Source */
+ { 0x06AD, 0x0080 }, /* R1709 - OUT3RMIX Input 3 Volume */
+ { 0x06AE, 0x0000 }, /* R1710 - OUT3RMIX Input 4 Source */
+ { 0x06AF, 0x0080 }, /* R1711 - OUT3RMIX Input 4 Volume */
+ { 0x06B0, 0x0000 }, /* R1712 - OUT4LMIX Input 1 Source */
+ { 0x06B1, 0x0080 }, /* R1713 - OUT4LMIX Input 1 Volume */
+ { 0x06B2, 0x0000 }, /* R1714 - OUT4LMIX Input 2 Source */
+ { 0x06B3, 0x0080 }, /* R1715 - OUT4LMIX Input 2 Volume */
+ { 0x06B4, 0x0000 }, /* R1716 - OUT4LMIX Input 3 Source */
+ { 0x06B5, 0x0080 }, /* R1717 - OUT4LMIX Input 3 Volume */
+ { 0x06B6, 0x0000 }, /* R1718 - OUT4LMIX Input 4 Source */
+ { 0x06B7, 0x0080 }, /* R1719 - OUT4LMIX Input 4 Volume */
+ { 0x06B8, 0x0000 }, /* R1720 - OUT4RMIX Input 1 Source */
+ { 0x06B9, 0x0080 }, /* R1721 - OUT4RMIX Input 1 Volume */
+ { 0x06BA, 0x0000 }, /* R1722 - OUT4RMIX Input 2 Source */
+ { 0x06BB, 0x0080 }, /* R1723 - OUT4RMIX Input 2 Volume */
+ { 0x06BC, 0x0000 }, /* R1724 - OUT4RMIX Input 3 Source */
+ { 0x06BD, 0x0080 }, /* R1725 - OUT4RMIX Input 3 Volume */
+ { 0x06BE, 0x0000 }, /* R1726 - OUT4RMIX Input 4 Source */
+ { 0x06BF, 0x0080 }, /* R1727 - OUT4RMIX Input 4 Volume */
+ { 0x06C0, 0x0000 }, /* R1728 - OUT5LMIX Input 1 Source */
+ { 0x06C1, 0x0080 }, /* R1729 - OUT5LMIX Input 1 Volume */
+ { 0x06C2, 0x0000 }, /* R1730 - OUT5LMIX Input 2 Source */
+ { 0x06C3, 0x0080 }, /* R1731 - OUT5LMIX Input 2 Volume */
+ { 0x06C4, 0x0000 }, /* R1732 - OUT5LMIX Input 3 Source */
+ { 0x06C5, 0x0080 }, /* R1733 - OUT5LMIX Input 3 Volume */
+ { 0x06C6, 0x0000 }, /* R1734 - OUT5LMIX Input 4 Source */
+ { 0x06C7, 0x0080 }, /* R1735 - OUT5LMIX Input 4 Volume */
+ { 0x06C8, 0x0000 }, /* R1736 - OUT5RMIX Input 1 Source */
+ { 0x06C9, 0x0080 }, /* R1737 - OUT5RMIX Input 1 Volume */
+ { 0x06CA, 0x0000 }, /* R1738 - OUT5RMIX Input 2 Source */
+ { 0x06CB, 0x0080 }, /* R1739 - OUT5RMIX Input 2 Volume */
+ { 0x06CC, 0x0000 }, /* R1740 - OUT5RMIX Input 3 Source */
+ { 0x06CD, 0x0080 }, /* R1741 - OUT5RMIX Input 3 Volume */
+ { 0x06CE, 0x0000 }, /* R1742 - OUT5RMIX Input 4 Source */
+ { 0x06CF, 0x0080 }, /* R1743 - OUT5RMIX Input 4 Volume */
+ { 0x06D0, 0x0000 }, /* R1744 - OUT6LMIX Input 1 Source */
+ { 0x06D1, 0x0080 }, /* R1745 - OUT6LMIX Input 1 Volume */
+ { 0x06D2, 0x0000 }, /* R1746 - OUT6LMIX Input 2 Source */
+ { 0x06D3, 0x0080 }, /* R1747 - OUT6LMIX Input 2 Volume */
+ { 0x06D4, 0x0000 }, /* R1748 - OUT6LMIX Input 3 Source */
+ { 0x06D5, 0x0080 }, /* R1749 - OUT6LMIX Input 3 Volume */
+ { 0x06D6, 0x0000 }, /* R1750 - OUT6LMIX Input 4 Source */
+ { 0x06D7, 0x0080 }, /* R1751 - OUT6LMIX Input 4 Volume */
+ { 0x06D8, 0x0000 }, /* R1752 - OUT6RMIX Input 1 Source */
+ { 0x06D9, 0x0080 }, /* R1753 - OUT6RMIX Input 1 Volume */
+ { 0x06DA, 0x0000 }, /* R1754 - OUT6RMIX Input 2 Source */
+ { 0x06DB, 0x0080 }, /* R1755 - OUT6RMIX Input 2 Volume */
+ { 0x06DC, 0x0000 }, /* R1756 - OUT6RMIX Input 3 Source */
+ { 0x06DD, 0x0080 }, /* R1757 - OUT6RMIX Input 3 Volume */
+ { 0x06DE, 0x0000 }, /* R1758 - OUT6RMIX Input 4 Source */
+ { 0x06DF, 0x0080 }, /* R1759 - OUT6RMIX Input 4 Volume */
+ { 0x0700, 0x0000 }, /* R1792 - AIF1TX1MIX Input 1 Source */
+ { 0x0701, 0x0080 }, /* R1793 - AIF1TX1MIX Input 1 Volume */
+ { 0x0702, 0x0000 }, /* R1794 - AIF1TX1MIX Input 2 Source */
+ { 0x0703, 0x0080 }, /* R1795 - AIF1TX1MIX Input 2 Volume */
+ { 0x0704, 0x0000 }, /* R1796 - AIF1TX1MIX Input 3 Source */
+ { 0x0705, 0x0080 }, /* R1797 - AIF1TX1MIX Input 3 Volume */
+ { 0x0706, 0x0000 }, /* R1798 - AIF1TX1MIX Input 4 Source */
+ { 0x0707, 0x0080 }, /* R1799 - AIF1TX1MIX Input 4 Volume */
+ { 0x0708, 0x0000 }, /* R1800 - AIF1TX2MIX Input 1 Source */
+ { 0x0709, 0x0080 }, /* R1801 - AIF1TX2MIX Input 1 Volume */
+ { 0x070A, 0x0000 }, /* R1802 - AIF1TX2MIX Input 2 Source */
+ { 0x070B, 0x0080 }, /* R1803 - AIF1TX2MIX Input 2 Volume */
+ { 0x070C, 0x0000 }, /* R1804 - AIF1TX2MIX Input 3 Source */
+ { 0x070D, 0x0080 }, /* R1805 - AIF1TX2MIX Input 3 Volume */
+ { 0x070E, 0x0000 }, /* R1806 - AIF1TX2MIX Input 4 Source */
+ { 0x070F, 0x0080 }, /* R1807 - AIF1TX2MIX Input 4 Volume */
+ { 0x0710, 0x0000 }, /* R1808 - AIF1TX3MIX Input 1 Source */
+ { 0x0711, 0x0080 }, /* R1809 - AIF1TX3MIX Input 1 Volume */
+ { 0x0712, 0x0000 }, /* R1810 - AIF1TX3MIX Input 2 Source */
+ { 0x0713, 0x0080 }, /* R1811 - AIF1TX3MIX Input 2 Volume */
+ { 0x0714, 0x0000 }, /* R1812 - AIF1TX3MIX Input 3 Source */
+ { 0x0715, 0x0080 }, /* R1813 - AIF1TX3MIX Input 3 Volume */
+ { 0x0716, 0x0000 }, /* R1814 - AIF1TX3MIX Input 4 Source */
+ { 0x0717, 0x0080 }, /* R1815 - AIF1TX3MIX Input 4 Volume */
+ { 0x0718, 0x0000 }, /* R1816 - AIF1TX4MIX Input 1 Source */
+ { 0x0719, 0x0080 }, /* R1817 - AIF1TX4MIX Input 1 Volume */
+ { 0x071A, 0x0000 }, /* R1818 - AIF1TX4MIX Input 2 Source */
+ { 0x071B, 0x0080 }, /* R1819 - AIF1TX4MIX Input 2 Volume */
+ { 0x071C, 0x0000 }, /* R1820 - AIF1TX4MIX Input 3 Source */
+ { 0x071D, 0x0080 }, /* R1821 - AIF1TX4MIX Input 3 Volume */
+ { 0x071E, 0x0000 }, /* R1822 - AIF1TX4MIX Input 4 Source */
+ { 0x071F, 0x0080 }, /* R1823 - AIF1TX4MIX Input 4 Volume */
+ { 0x0720, 0x0000 }, /* R1824 - AIF1TX5MIX Input 1 Source */
+ { 0x0721, 0x0080 }, /* R1825 - AIF1TX5MIX Input 1 Volume */
+ { 0x0722, 0x0000 }, /* R1826 - AIF1TX5MIX Input 2 Source */
+ { 0x0723, 0x0080 }, /* R1827 - AIF1TX5MIX Input 2 Volume */
+ { 0x0724, 0x0000 }, /* R1828 - AIF1TX5MIX Input 3 Source */
+ { 0x0725, 0x0080 }, /* R1829 - AIF1TX5MIX Input 3 Volume */
+ { 0x0726, 0x0000 }, /* R1830 - AIF1TX5MIX Input 4 Source */
+ { 0x0727, 0x0080 }, /* R1831 - AIF1TX5MIX Input 4 Volume */
+ { 0x0728, 0x0000 }, /* R1832 - AIF1TX6MIX Input 1 Source */
+ { 0x0729, 0x0080 }, /* R1833 - AIF1TX6MIX Input 1 Volume */
+ { 0x072A, 0x0000 }, /* R1834 - AIF1TX6MIX Input 2 Source */
+ { 0x072B, 0x0080 }, /* R1835 - AIF1TX6MIX Input 2 Volume */
+ { 0x072C, 0x0000 }, /* R1836 - AIF1TX6MIX Input 3 Source */
+ { 0x072D, 0x0080 }, /* R1837 - AIF1TX6MIX Input 3 Volume */
+ { 0x072E, 0x0000 }, /* R1838 - AIF1TX6MIX Input 4 Source */
+ { 0x072F, 0x0080 }, /* R1839 - AIF1TX6MIX Input 4 Volume */
+ { 0x0730, 0x0000 }, /* R1840 - AIF1TX7MIX Input 1 Source */
+ { 0x0731, 0x0080 }, /* R1841 - AIF1TX7MIX Input 1 Volume */
+ { 0x0732, 0x0000 }, /* R1842 - AIF1TX7MIX Input 2 Source */
+ { 0x0733, 0x0080 }, /* R1843 - AIF1TX7MIX Input 2 Volume */
+ { 0x0734, 0x0000 }, /* R1844 - AIF1TX7MIX Input 3 Source */
+ { 0x0735, 0x0080 }, /* R1845 - AIF1TX7MIX Input 3 Volume */
+ { 0x0736, 0x0000 }, /* R1846 - AIF1TX7MIX Input 4 Source */
+ { 0x0737, 0x0080 }, /* R1847 - AIF1TX7MIX Input 4 Volume */
+ { 0x0738, 0x0000 }, /* R1848 - AIF1TX8MIX Input 1 Source */
+ { 0x0739, 0x0080 }, /* R1849 - AIF1TX8MIX Input 1 Volume */
+ { 0x073A, 0x0000 }, /* R1850 - AIF1TX8MIX Input 2 Source */
+ { 0x073B, 0x0080 }, /* R1851 - AIF1TX8MIX Input 2 Volume */
+ { 0x073C, 0x0000 }, /* R1852 - AIF1TX8MIX Input 3 Source */
+ { 0x073D, 0x0080 }, /* R1853 - AIF1TX8MIX Input 3 Volume */
+ { 0x073E, 0x0000 }, /* R1854 - AIF1TX8MIX Input 4 Source */
+ { 0x073F, 0x0080 }, /* R1855 - AIF1TX8MIX Input 4 Volume */
+ { 0x0740, 0x0000 }, /* R1856 - AIF2TX1MIX Input 1 Source */
+ { 0x0741, 0x0080 }, /* R1857 - AIF2TX1MIX Input 1 Volume */
+ { 0x0742, 0x0000 }, /* R1858 - AIF2TX1MIX Input 2 Source */
+ { 0x0743, 0x0080 }, /* R1859 - AIF2TX1MIX Input 2 Volume */
+ { 0x0744, 0x0000 }, /* R1860 - AIF2TX1MIX Input 3 Source */
+ { 0x0745, 0x0080 }, /* R1861 - AIF2TX1MIX Input 3 Volume */
+ { 0x0746, 0x0000 }, /* R1862 - AIF2TX1MIX Input 4 Source */
+ { 0x0747, 0x0080 }, /* R1863 - AIF2TX1MIX Input 4 Volume */
+ { 0x0748, 0x0000 }, /* R1864 - AIF2TX2MIX Input 1 Source */
+ { 0x0749, 0x0080 }, /* R1865 - AIF2TX2MIX Input 1 Volume */
+ { 0x074A, 0x0000 }, /* R1866 - AIF2TX2MIX Input 2 Source */
+ { 0x074B, 0x0080 }, /* R1867 - AIF2TX2MIX Input 2 Volume */
+ { 0x074C, 0x0000 }, /* R1868 - AIF2TX2MIX Input 3 Source */
+ { 0x074D, 0x0080 }, /* R1869 - AIF2TX2MIX Input 3 Volume */
+ { 0x074E, 0x0000 }, /* R1870 - AIF2TX2MIX Input 4 Source */
+ { 0x074F, 0x0080 }, /* R1871 - AIF2TX2MIX Input 4 Volume */
+ { 0x0780, 0x0000 }, /* R1920 - AIF3TX1MIX Input 1 Source */
+ { 0x0781, 0x0080 }, /* R1921 - AIF3TX1MIX Input 1 Volume */
+ { 0x0782, 0x0000 }, /* R1922 - AIF3TX1MIX Input 2 Source */
+ { 0x0783, 0x0080 }, /* R1923 - AIF3TX1MIX Input 2 Volume */
+ { 0x0784, 0x0000 }, /* R1924 - AIF3TX1MIX Input 3 Source */
+ { 0x0785, 0x0080 }, /* R1925 - AIF3TX1MIX Input 3 Volume */
+ { 0x0786, 0x0000 }, /* R1926 - AIF3TX1MIX Input 4 Source */
+ { 0x0787, 0x0080 }, /* R1927 - AIF3TX1MIX Input 4 Volume */
+ { 0x0788, 0x0000 }, /* R1928 - AIF3TX2MIX Input 1 Source */
+ { 0x0789, 0x0080 }, /* R1929 - AIF3TX2MIX Input 1 Volume */
+ { 0x078A, 0x0000 }, /* R1930 - AIF3TX2MIX Input 2 Source */
+ { 0x078B, 0x0080 }, /* R1931 - AIF3TX2MIX Input 2 Volume */
+ { 0x078C, 0x0000 }, /* R1932 - AIF3TX2MIX Input 3 Source */
+ { 0x078D, 0x0080 }, /* R1933 - AIF3TX2MIX Input 3 Volume */
+ { 0x078E, 0x0000 }, /* R1934 - AIF3TX2MIX Input 4 Source */
+ { 0x078F, 0x0080 }, /* R1935 - AIF3TX2MIX Input 4 Volume */
+ { 0x0880, 0x0000 }, /* R2176 - EQ1MIX Input 1 Source */
+ { 0x0881, 0x0080 }, /* R2177 - EQ1MIX Input 1 Volume */
+ { 0x0882, 0x0000 }, /* R2178 - EQ1MIX Input 2 Source */
+ { 0x0883, 0x0080 }, /* R2179 - EQ1MIX Input 2 Volume */
+ { 0x0884, 0x0000 }, /* R2180 - EQ1MIX Input 3 Source */
+ { 0x0885, 0x0080 }, /* R2181 - EQ1MIX Input 3 Volume */
+ { 0x0886, 0x0000 }, /* R2182 - EQ1MIX Input 4 Source */
+ { 0x0887, 0x0080 }, /* R2183 - EQ1MIX Input 4 Volume */
+ { 0x0888, 0x0000 }, /* R2184 - EQ2MIX Input 1 Source */
+ { 0x0889, 0x0080 }, /* R2185 - EQ2MIX Input 1 Volume */
+ { 0x088A, 0x0000 }, /* R2186 - EQ2MIX Input 2 Source */
+ { 0x088B, 0x0080 }, /* R2187 - EQ2MIX Input 2 Volume */
+ { 0x088C, 0x0000 }, /* R2188 - EQ2MIX Input 3 Source */
+ { 0x088D, 0x0080 }, /* R2189 - EQ2MIX Input 3 Volume */
+ { 0x088E, 0x0000 }, /* R2190 - EQ2MIX Input 4 Source */
+ { 0x088F, 0x0080 }, /* R2191 - EQ2MIX Input 4 Volume */
+ { 0x0890, 0x0000 }, /* R2192 - EQ3MIX Input 1 Source */
+ { 0x0891, 0x0080 }, /* R2193 - EQ3MIX Input 1 Volume */
+ { 0x0892, 0x0000 }, /* R2194 - EQ3MIX Input 2 Source */
+ { 0x0893, 0x0080 }, /* R2195 - EQ3MIX Input 2 Volume */
+ { 0x0894, 0x0000 }, /* R2196 - EQ3MIX Input 3 Source */
+ { 0x0895, 0x0080 }, /* R2197 - EQ3MIX Input 3 Volume */
+ { 0x0896, 0x0000 }, /* R2198 - EQ3MIX Input 4 Source */
+ { 0x0897, 0x0080 }, /* R2199 - EQ3MIX Input 4 Volume */
+ { 0x0898, 0x0000 }, /* R2200 - EQ4MIX Input 1 Source */
+ { 0x0899, 0x0080 }, /* R2201 - EQ4MIX Input 1 Volume */
+ { 0x089A, 0x0000 }, /* R2202 - EQ4MIX Input 2 Source */
+ { 0x089B, 0x0080 }, /* R2203 - EQ4MIX Input 2 Volume */
+ { 0x089C, 0x0000 }, /* R2204 - EQ4MIX Input 3 Source */
+ { 0x089D, 0x0080 }, /* R2205 - EQ4MIX Input 3 Volume */
+ { 0x089E, 0x0000 }, /* R2206 - EQ4MIX Input 4 Source */
+ { 0x089F, 0x0080 }, /* R2207 - EQ4MIX Input 4 Volume */
+ { 0x08C0, 0x0000 }, /* R2240 - DRC1LMIX Input 1 Source */
+ { 0x08C1, 0x0080 }, /* R2241 - DRC1LMIX Input 1 Volume */
+ { 0x08C2, 0x0000 }, /* R2242 - DRC1LMIX Input 2 Source */
+ { 0x08C3, 0x0080 }, /* R2243 - DRC1LMIX Input 2 Volume */
+ { 0x08C4, 0x0000 }, /* R2244 - DRC1LMIX Input 3 Source */
+ { 0x08C5, 0x0080 }, /* R2245 - DRC1LMIX Input 3 Volume */
+ { 0x08C6, 0x0000 }, /* R2246 - DRC1LMIX Input 4 Source */
+ { 0x08C7, 0x0080 }, /* R2247 - DRC1LMIX Input 4 Volume */
+ { 0x08C8, 0x0000 }, /* R2248 - DRC1RMIX Input 1 Source */
+ { 0x08C9, 0x0080 }, /* R2249 - DRC1RMIX Input 1 Volume */
+ { 0x08CA, 0x0000 }, /* R2250 - DRC1RMIX Input 2 Source */
+ { 0x08CB, 0x0080 }, /* R2251 - DRC1RMIX Input 2 Volume */
+ { 0x08CC, 0x0000 }, /* R2252 - DRC1RMIX Input 3 Source */
+ { 0x08CD, 0x0080 }, /* R2253 - DRC1RMIX Input 3 Volume */
+ { 0x08CE, 0x0000 }, /* R2254 - DRC1RMIX Input 4 Source */
+ { 0x08CF, 0x0080 }, /* R2255 - DRC1RMIX Input 4 Volume */
+ { 0x0900, 0x0000 }, /* R2304 - HPLP1MIX Input 1 Source */
+ { 0x0901, 0x0080 }, /* R2305 - HPLP1MIX Input 1 Volume */
+ { 0x0902, 0x0000 }, /* R2306 - HPLP1MIX Input 2 Source */
+ { 0x0903, 0x0080 }, /* R2307 - HPLP1MIX Input 2 Volume */
+ { 0x0904, 0x0000 }, /* R2308 - HPLP1MIX Input 3 Source */
+ { 0x0905, 0x0080 }, /* R2309 - HPLP1MIX Input 3 Volume */
+ { 0x0906, 0x0000 }, /* R2310 - HPLP1MIX Input 4 Source */
+ { 0x0907, 0x0080 }, /* R2311 - HPLP1MIX Input 4 Volume */
+ { 0x0908, 0x0000 }, /* R2312 - HPLP2MIX Input 1 Source */
+ { 0x0909, 0x0080 }, /* R2313 - HPLP2MIX Input 1 Volume */
+ { 0x090A, 0x0000 }, /* R2314 - HPLP2MIX Input 2 Source */
+ { 0x090B, 0x0080 }, /* R2315 - HPLP2MIX Input 2 Volume */
+ { 0x090C, 0x0000 }, /* R2316 - HPLP2MIX Input 3 Source */
+ { 0x090D, 0x0080 }, /* R2317 - HPLP2MIX Input 3 Volume */
+ { 0x090E, 0x0000 }, /* R2318 - HPLP2MIX Input 4 Source */
+ { 0x090F, 0x0080 }, /* R2319 - HPLP2MIX Input 4 Volume */
+ { 0x0910, 0x0000 }, /* R2320 - HPLP3MIX Input 1 Source */
+ { 0x0911, 0x0080 }, /* R2321 - HPLP3MIX Input 1 Volume */
+ { 0x0912, 0x0000 }, /* R2322 - HPLP3MIX Input 2 Source */
+ { 0x0913, 0x0080 }, /* R2323 - HPLP3MIX Input 2 Volume */
+ { 0x0914, 0x0000 }, /* R2324 - HPLP3MIX Input 3 Source */
+ { 0x0915, 0x0080 }, /* R2325 - HPLP3MIX Input 3 Volume */
+ { 0x0916, 0x0000 }, /* R2326 - HPLP3MIX Input 4 Source */
+ { 0x0917, 0x0080 }, /* R2327 - HPLP3MIX Input 4 Volume */
+ { 0x0918, 0x0000 }, /* R2328 - HPLP4MIX Input 1 Source */
+ { 0x0919, 0x0080 }, /* R2329 - HPLP4MIX Input 1 Volume */
+ { 0x091A, 0x0000 }, /* R2330 - HPLP4MIX Input 2 Source */
+ { 0x091B, 0x0080 }, /* R2331 - HPLP4MIX Input 2 Volume */
+ { 0x091C, 0x0000 }, /* R2332 - HPLP4MIX Input 3 Source */
+ { 0x091D, 0x0080 }, /* R2333 - HPLP4MIX Input 3 Volume */
+ { 0x091E, 0x0000 }, /* R2334 - HPLP4MIX Input 4 Source */
+ { 0x091F, 0x0080 }, /* R2335 - HPLP4MIX Input 4 Volume */
+ { 0x0940, 0x0000 }, /* R2368 - DSP1LMIX Input 1 Source */
+ { 0x0941, 0x0080 }, /* R2369 - DSP1LMIX Input 1 Volume */
+ { 0x0942, 0x0000 }, /* R2370 - DSP1LMIX Input 2 Source */
+ { 0x0943, 0x0080 }, /* R2371 - DSP1LMIX Input 2 Volume */
+ { 0x0944, 0x0000 }, /* R2372 - DSP1LMIX Input 3 Source */
+ { 0x0945, 0x0080 }, /* R2373 - DSP1LMIX Input 3 Volume */
+ { 0x0946, 0x0000 }, /* R2374 - DSP1LMIX Input 4 Source */
+ { 0x0947, 0x0080 }, /* R2375 - DSP1LMIX Input 4 Volume */
+ { 0x0948, 0x0000 }, /* R2376 - DSP1RMIX Input 1 Source */
+ { 0x0949, 0x0080 }, /* R2377 - DSP1RMIX Input 1 Volume */
+ { 0x094A, 0x0000 }, /* R2378 - DSP1RMIX Input 2 Source */
+ { 0x094B, 0x0080 }, /* R2379 - DSP1RMIX Input 2 Volume */
+ { 0x094C, 0x0000 }, /* R2380 - DSP1RMIX Input 3 Source */
+ { 0x094D, 0x0080 }, /* R2381 - DSP1RMIX Input 3 Volume */
+ { 0x094E, 0x0000 }, /* R2382 - DSP1RMIX Input 4 Source */
+ { 0x094F, 0x0080 }, /* R2383 - DSP1RMIX Input 4 Volume */
+ { 0x0950, 0x0000 }, /* R2384 - DSP1AUX1MIX Input 1 Source */
+ { 0x0958, 0x0000 }, /* R2392 - DSP1AUX2MIX Input 1 Source */
+ { 0x0960, 0x0000 }, /* R2400 - DSP1AUX3MIX Input 1 Source */
+ { 0x0968, 0x0000 }, /* R2408 - DSP1AUX4MIX Input 1 Source */
+ { 0x0970, 0x0000 }, /* R2416 - DSP1AUX5MIX Input 1 Source */
+ { 0x0978, 0x0000 }, /* R2424 - DSP1AUX6MIX Input 1 Source */
+ { 0x0980, 0x0000 }, /* R2432 - DSP2LMIX Input 1 Source */
+ { 0x0981, 0x0080 }, /* R2433 - DSP2LMIX Input 1 Volume */
+ { 0x0982, 0x0000 }, /* R2434 - DSP2LMIX Input 2 Source */
+ { 0x0983, 0x0080 }, /* R2435 - DSP2LMIX Input 2 Volume */
+ { 0x0984, 0x0000 }, /* R2436 - DSP2LMIX Input 3 Source */
+ { 0x0985, 0x0080 }, /* R2437 - DSP2LMIX Input 3 Volume */
+ { 0x0986, 0x0000 }, /* R2438 - DSP2LMIX Input 4 Source */
+ { 0x0987, 0x0080 }, /* R2439 - DSP2LMIX Input 4 Volume */
+ { 0x0988, 0x0000 }, /* R2440 - DSP2RMIX Input 1 Source */
+ { 0x0989, 0x0080 }, /* R2441 - DSP2RMIX Input 1 Volume */
+ { 0x098A, 0x0000 }, /* R2442 - DSP2RMIX Input 2 Source */
+ { 0x098B, 0x0080 }, /* R2443 - DSP2RMIX Input 2 Volume */
+ { 0x098C, 0x0000 }, /* R2444 - DSP2RMIX Input 3 Source */
+ { 0x098D, 0x0080 }, /* R2445 - DSP2RMIX Input 3 Volume */
+ { 0x098E, 0x0000 }, /* R2446 - DSP2RMIX Input 4 Source */
+ { 0x098F, 0x0080 }, /* R2447 - DSP2RMIX Input 4 Volume */
+ { 0x0990, 0x0000 }, /* R2448 - DSP2AUX1MIX Input 1 Source */
+ { 0x0998, 0x0000 }, /* R2456 - DSP2AUX2MIX Input 1 Source */
+ { 0x09A0, 0x0000 }, /* R2464 - DSP2AUX3MIX Input 1 Source */
+ { 0x09A8, 0x0000 }, /* R2472 - DSP2AUX4MIX Input 1 Source */
+ { 0x09B0, 0x0000 }, /* R2480 - DSP2AUX5MIX Input 1 Source */
+ { 0x09B8, 0x0000 }, /* R2488 - DSP2AUX6MIX Input 1 Source */
+ { 0x09C0, 0x0000 }, /* R2496 - DSP3LMIX Input 1 Source */
+ { 0x09C1, 0x0080 }, /* R2497 - DSP3LMIX Input 1 Volume */
+ { 0x09C2, 0x0000 }, /* R2498 - DSP3LMIX Input 2 Source */
+ { 0x09C3, 0x0080 }, /* R2499 - DSP3LMIX Input 2 Volume */
+ { 0x09C4, 0x0000 }, /* R2500 - DSP3LMIX Input 3 Source */
+ { 0x09C5, 0x0080 }, /* R2501 - DSP3LMIX Input 3 Volume */
+ { 0x09C6, 0x0000 }, /* R2502 - DSP3LMIX Input 4 Source */
+ { 0x09C7, 0x0080 }, /* R2503 - DSP3LMIX Input 4 Volume */
+ { 0x09C8, 0x0000 }, /* R2504 - DSP3RMIX Input 1 Source */
+ { 0x09C9, 0x0080 }, /* R2505 - DSP3RMIX Input 1 Volume */
+ { 0x09CA, 0x0000 }, /* R2506 - DSP3RMIX Input 2 Source */
+ { 0x09CB, 0x0080 }, /* R2507 - DSP3RMIX Input 2 Volume */
+ { 0x09CC, 0x0000 }, /* R2508 - DSP3RMIX Input 3 Source */
+ { 0x09CD, 0x0080 }, /* R2509 - DSP3RMIX Input 3 Volume */
+ { 0x09CE, 0x0000 }, /* R2510 - DSP3RMIX Input 4 Source */
+ { 0x09CF, 0x0080 }, /* R2511 - DSP3RMIX Input 4 Volume */
+ { 0x09D0, 0x0000 }, /* R2512 - DSP3AUX1MIX Input 1 Source */
+ { 0x09D8, 0x0000 }, /* R2520 - DSP3AUX2MIX Input 1 Source */
+ { 0x09E0, 0x0000 }, /* R2528 - DSP3AUX3MIX Input 1 Source */
+ { 0x09E8, 0x0000 }, /* R2536 - DSP3AUX4MIX Input 1 Source */
+ { 0x09F0, 0x0000 }, /* R2544 - DSP3AUX5MIX Input 1 Source */
+ { 0x09F8, 0x0000 }, /* R2552 - DSP3AUX6MIX Input 1 Source */
+ { 0x0A80, 0x0000 }, /* R2688 - ASRC1LMIX Input 1 Source */
+ { 0x0A88, 0x0000 }, /* R2696 - ASRC1RMIX Input 1 Source */
+ { 0x0A90, 0x0000 }, /* R2704 - ASRC2LMIX Input 1 Source */
+ { 0x0A98, 0x0000 }, /* R2712 - ASRC2RMIX Input 1 Source */
+ { 0x0B00, 0x0000 }, /* R2816 - ISRC1DEC1MIX Input 1 Source */
+ { 0x0B08, 0x0000 }, /* R2824 - ISRC1DEC2MIX Input 1 Source */
+ { 0x0B10, 0x0000 }, /* R2832 - ISRC1DEC3MIX Input 1 Source */
+ { 0x0B18, 0x0000 }, /* R2840 - ISRC1DEC4MIX Input 1 Source */
+ { 0x0B20, 0x0000 }, /* R2848 - ISRC1INT1MIX Input 1 Source */
+ { 0x0B28, 0x0000 }, /* R2856 - ISRC1INT2MIX Input 1 Source */
+ { 0x0B30, 0x0000 }, /* R2864 - ISRC1INT3MIX Input 1 Source */
+ { 0x0B38, 0x0000 }, /* R2872 - ISRC1INT4MIX Input 1 Source */
+ { 0x0B40, 0x0000 }, /* R2880 - ISRC2DEC1MIX Input 1 Source */
+ { 0x0B48, 0x0000 }, /* R2888 - ISRC2DEC2MIX Input 1 Source */
+ { 0x0B50, 0x0000 }, /* R2896 - ISRC2DEC3MIX Input 1 Source */
+ { 0x0B58, 0x0000 }, /* R2904 - ISRC2DEC4MIX Input 1 Source */
+ { 0x0B60, 0x0000 }, /* R2912 - ISRC2INT1MIX Input 1 Source */
+ { 0x0B68, 0x0000 }, /* R2920 - ISRC2INT2MIX Input 1 Source */
+ { 0x0B70, 0x0000 }, /* R2928 - ISRC2INT3MIX Input 1 Source */
+ { 0x0B78, 0x0000 }, /* R2936 - ISRC2INT4MIX Input 1 Source */
+ { 0x0C00, 0xA001 }, /* R3072 - GPIO CTRL 1 */
+ { 0x0C01, 0xA001 }, /* R3073 - GPIO CTRL 2 */
+ { 0x0C02, 0xA001 }, /* R3074 - GPIO CTRL 3 */
+ { 0x0C03, 0xA001 }, /* R3075 - GPIO CTRL 4 */
+ { 0x0C04, 0xA001 }, /* R3076 - GPIO CTRL 5 */
+ { 0x0C05, 0xA001 }, /* R3077 - GPIO CTRL 6 */
+ { 0x0C23, 0x4003 }, /* R3107 - Misc Pad Ctrl 1 */
+ { 0x0C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 2 */
+ { 0x0C25, 0x0000 }, /* R3109 - Misc Pad Ctrl 3 */
+ { 0x0C26, 0x0000 }, /* R3110 - Misc Pad Ctrl 4 */
+ { 0x0C27, 0x0000 }, /* R3111 - Misc Pad Ctrl 5 */
+ { 0x0C28, 0x0000 }, /* R3112 - Misc GPIO 1 */
+ { 0x0D00, 0x0000 }, /* R3328 - Interrupt Status 1 */
+ { 0x0D01, 0x0000 }, /* R3329 - Interrupt Status 2 */
+ { 0x0D02, 0x0000 }, /* R3330 - Interrupt Status 3 */
+ { 0x0D03, 0x0000 }, /* R3331 - Interrupt Status 4 */
+ { 0x0D04, 0x0000 }, /* R3332 - Interrupt Raw Status 2 */
+ { 0x0D05, 0x0000 }, /* R3333 - Interrupt Raw Status 3 */
+ { 0x0D06, 0x0000 }, /* R3334 - Interrupt Raw Status 4 */
+ { 0x0D07, 0xFFFF }, /* R3335 - Interrupt Status 1 Mask */
+ { 0x0D08, 0xFFFF }, /* R3336 - Interrupt Status 2 Mask */
+ { 0x0D09, 0xFFFF }, /* R3337 - Interrupt Status 3 Mask */
+ { 0x0D0A, 0xFFFF }, /* R3338 - Interrupt Status 4 Mask */
+ { 0x0D1F, 0x0000 }, /* R3359 - Interrupt Control */
+ { 0x0D20, 0xFFFF }, /* R3360 - IRQ Debounce 1 */
+ { 0x0D21, 0xFFFF }, /* R3361 - IRQ Debounce 2 */
+ { 0x0E00, 0x0000 }, /* R3584 - FX_Ctrl */
+ { 0x0E10, 0x6318 }, /* R3600 - EQ1_1 */
+ { 0x0E11, 0x6300 }, /* R3601 - EQ1_2 */
+ { 0x0E12, 0x0FC8 }, /* R3602 - EQ1_3 */
+ { 0x0E13, 0x03FE }, /* R3603 - EQ1_4 */
+ { 0x0E14, 0x00E0 }, /* R3604 - EQ1_5 */
+ { 0x0E15, 0x1EC4 }, /* R3605 - EQ1_6 */
+ { 0x0E16, 0xF136 }, /* R3606 - EQ1_7 */
+ { 0x0E17, 0x0409 }, /* R3607 - EQ1_8 */
+ { 0x0E18, 0x04CC }, /* R3608 - EQ1_9 */
+ { 0x0E19, 0x1C9B }, /* R3609 - EQ1_10 */
+ { 0x0E1A, 0xF337 }, /* R3610 - EQ1_11 */
+ { 0x0E1B, 0x040B }, /* R3611 - EQ1_12 */
+ { 0x0E1C, 0x0CBB }, /* R3612 - EQ1_13 */
+ { 0x0E1D, 0x16F8 }, /* R3613 - EQ1_14 */
+ { 0x0E1E, 0xF7D9 }, /* R3614 - EQ1_15 */
+ { 0x0E1F, 0x040A }, /* R3615 - EQ1_16 */
+ { 0x0E20, 0x1F14 }, /* R3616 - EQ1_17 */
+ { 0x0E21, 0x058C }, /* R3617 - EQ1_18 */
+ { 0x0E22, 0x0563 }, /* R3618 - EQ1_19 */
+ { 0x0E23, 0x4000 }, /* R3619 - EQ1_20 */
+ { 0x0E26, 0x6318 }, /* R3622 - EQ2_1 */
+ { 0x0E27, 0x6300 }, /* R3623 - EQ2_2 */
+ { 0x0E28, 0x0FC8 }, /* R3624 - EQ2_3 */
+ { 0x0E29, 0x03FE }, /* R3625 - EQ2_4 */
+ { 0x0E2A, 0x00E0 }, /* R3626 - EQ2_5 */
+ { 0x0E2B, 0x1EC4 }, /* R3627 - EQ2_6 */
+ { 0x0E2C, 0xF136 }, /* R3628 - EQ2_7 */
+ { 0x0E2D, 0x0409 }, /* R3629 - EQ2_8 */
+ { 0x0E2E, 0x04CC }, /* R3630 - EQ2_9 */
+ { 0x0E2F, 0x1C9B }, /* R3631 - EQ2_10 */
+ { 0x0E30, 0xF337 }, /* R3632 - EQ2_11 */
+ { 0x0E31, 0x040B }, /* R3633 - EQ2_12 */
+ { 0x0E32, 0x0CBB }, /* R3634 - EQ2_13 */
+ { 0x0E33, 0x16F8 }, /* R3635 - EQ2_14 */
+ { 0x0E34, 0xF7D9 }, /* R3636 - EQ2_15 */
+ { 0x0E35, 0x040A }, /* R3637 - EQ2_16 */
+ { 0x0E36, 0x1F14 }, /* R3638 - EQ2_17 */
+ { 0x0E37, 0x058C }, /* R3639 - EQ2_18 */
+ { 0x0E38, 0x0563 }, /* R3640 - EQ2_19 */
+ { 0x0E39, 0x4000 }, /* R3641 - EQ2_20 */
+ { 0x0E3C, 0x6318 }, /* R3644 - EQ3_1 */
+ { 0x0E3D, 0x6300 }, /* R3645 - EQ3_2 */
+ { 0x0E3E, 0x0FC8 }, /* R3646 - EQ3_3 */
+ { 0x0E3F, 0x03FE }, /* R3647 - EQ3_4 */
+ { 0x0E40, 0x00E0 }, /* R3648 - EQ3_5 */
+ { 0x0E41, 0x1EC4 }, /* R3649 - EQ3_6 */
+ { 0x0E42, 0xF136 }, /* R3650 - EQ3_7 */
+ { 0x0E43, 0x0409 }, /* R3651 - EQ3_8 */
+ { 0x0E44, 0x04CC }, /* R3652 - EQ3_9 */
+ { 0x0E45, 0x1C9B }, /* R3653 - EQ3_10 */
+ { 0x0E46, 0xF337 }, /* R3654 - EQ3_11 */
+ { 0x0E47, 0x040B }, /* R3655 - EQ3_12 */
+ { 0x0E48, 0x0CBB }, /* R3656 - EQ3_13 */
+ { 0x0E49, 0x16F8 }, /* R3657 - EQ3_14 */
+ { 0x0E4A, 0xF7D9 }, /* R3658 - EQ3_15 */
+ { 0x0E4B, 0x040A }, /* R3659 - EQ3_16 */
+ { 0x0E4C, 0x1F14 }, /* R3660 - EQ3_17 */
+ { 0x0E4D, 0x058C }, /* R3661 - EQ3_18 */
+ { 0x0E4E, 0x0563 }, /* R3662 - EQ3_19 */
+ { 0x0E4F, 0x4000 }, /* R3663 - EQ3_20 */
+ { 0x0E52, 0x6318 }, /* R3666 - EQ4_1 */
+ { 0x0E53, 0x6300 }, /* R3667 - EQ4_2 */
+ { 0x0E54, 0x0FC8 }, /* R3668 - EQ4_3 */
+ { 0x0E55, 0x03FE }, /* R3669 - EQ4_4 */
+ { 0x0E56, 0x00E0 }, /* R3670 - EQ4_5 */
+ { 0x0E57, 0x1EC4 }, /* R3671 - EQ4_6 */
+ { 0x0E58, 0xF136 }, /* R3672 - EQ4_7 */
+ { 0x0E59, 0x0409 }, /* R3673 - EQ4_8 */
+ { 0x0E5A, 0x04CC }, /* R3674 - EQ4_9 */
+ { 0x0E5B, 0x1C9B }, /* R3675 - EQ4_10 */
+ { 0x0E5C, 0xF337 }, /* R3676 - EQ4_11 */
+ { 0x0E5D, 0x040B }, /* R3677 - EQ4_12 */
+ { 0x0E5E, 0x0CBB }, /* R3678 - EQ4_13 */
+ { 0x0E5F, 0x16F8 }, /* R3679 - EQ4_14 */
+ { 0x0E60, 0xF7D9 }, /* R3680 - EQ4_15 */
+ { 0x0E61, 0x040A }, /* R3681 - EQ4_16 */
+ { 0x0E62, 0x1F14 }, /* R3682 - EQ4_17 */
+ { 0x0E63, 0x058C }, /* R3683 - EQ4_18 */
+ { 0x0E64, 0x0563 }, /* R3684 - EQ4_19 */
+ { 0x0E65, 0x4000 }, /* R3685 - EQ4_20 */
+ { 0x0E80, 0x0018 }, /* R3712 - DRC1 ctrl1 */
+ { 0x0E81, 0x0933 }, /* R3713 - DRC1 ctrl2 */
+ { 0x0E82, 0x0018 }, /* R3714 - DRC1 ctrl3 */
+ { 0x0E83, 0x0000 }, /* R3715 - DRC1 ctrl4 */
+ { 0x0E84, 0x0000 }, /* R3716 - DRC1 ctrl5 */
+ { 0x0EC0, 0x0000 }, /* R3776 - HPLPF1_1 */
+ { 0x0EC1, 0x0000 }, /* R3777 - HPLPF1_2 */
+ { 0x0EC4, 0x0000 }, /* R3780 - HPLPF2_1 */
+ { 0x0EC5, 0x0000 }, /* R3781 - HPLPF2_2 */
+ { 0x0EC8, 0x0000 }, /* R3784 - HPLPF3_1 */
+ { 0x0EC9, 0x0000 }, /* R3785 - HPLPF3_2 */
+ { 0x0ECC, 0x0000 }, /* R3788 - HPLPF4_1 */
+ { 0x0ECD, 0x0000 }, /* R3789 - HPLPF4_2 */
+ { 0x0F02, 0x0000 }, /* R3842 - DSP1 Control 2 */
+ { 0x0F03, 0x0000 }, /* R3843 - DSP1 Control 3 */
+ { 0x0F04, 0x0000 }, /* R3844 - DSP1 Control 4 */
+ { 0x1002, 0x0000 }, /* R4098 - DSP2 Control 2 */
+ { 0x1003, 0x0000 }, /* R4099 - DSP2 Control 3 */
+ { 0x1004, 0x0000 }, /* R4100 - DSP2 Control 4 */
+ { 0x1102, 0x0000 }, /* R4354 - DSP3 Control 2 */
+ { 0x1103, 0x0000 }, /* R4355 - DSP3 Control 3 */
+ { 0x1104, 0x0000 }, /* R4356 - DSP3 Control 4 */
+};
diff --git a/kernel/sound/soc/codecs/wm5100.c b/kernel/sound/soc/codecs/wm5100.c
new file mode 100644
index 000000000..96740379b
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm5100.c
@@ -0,0 +1,2735 @@
+/*
+ * wm5100.c -- WM5100 ALSA SoC Audio driver
+ *
+ * Copyright 2011-2 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/pm.h>
+#include <linux/gcd.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/fixed.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 <sound/wm5100.h>
+
+#include "wm5100.h"
+
+#define WM5100_NUM_CORE_SUPPLIES 2
+static const char *wm5100_core_supply_names[WM5100_NUM_CORE_SUPPLIES] = {
+ "DBVDD1",
+ "LDOVDD", /* If DCVDD is supplied externally specify as LDOVDD */
+};
+
+#define WM5100_AIFS 3
+#define WM5100_SYNC_SRS 3
+
+struct wm5100_fll {
+ int fref;
+ int fout;
+ int src;
+ struct completion lock;
+};
+
+/* codec private data */
+struct wm5100_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+
+ struct regulator_bulk_data core_supplies[WM5100_NUM_CORE_SUPPLIES];
+
+ int rev;
+
+ int sysclk;
+ int asyncclk;
+
+ bool aif_async[WM5100_AIFS];
+ bool aif_symmetric[WM5100_AIFS];
+ int sr_ref[WM5100_SYNC_SRS];
+
+ bool out_ena[2];
+
+ struct snd_soc_jack *jack;
+ bool jack_detecting;
+ bool jack_mic;
+ int jack_mode;
+ int jack_flips;
+
+ struct wm5100_fll fll[2];
+
+ struct wm5100_pdata pdata;
+
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio_chip;
+#endif
+};
+
+static int wm5100_sr_code[] = {
+ 0,
+ 12000,
+ 24000,
+ 48000,
+ 96000,
+ 192000,
+ 384000,
+ 768000,
+ 0,
+ 11025,
+ 22050,
+ 44100,
+ 88200,
+ 176400,
+ 352800,
+ 705600,
+ 4000,
+ 8000,
+ 16000,
+ 32000,
+ 64000,
+ 128000,
+ 256000,
+ 512000,
+};
+
+static int wm5100_sr_regs[WM5100_SYNC_SRS] = {
+ WM5100_CLOCKING_4,
+ WM5100_CLOCKING_5,
+ WM5100_CLOCKING_6,
+};
+
+static int wm5100_alloc_sr(struct snd_soc_codec *codec, int rate)
+{
+ struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ int sr_code, sr_free, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm5100_sr_code); i++)
+ if (wm5100_sr_code[i] == rate)
+ break;
+ if (i == ARRAY_SIZE(wm5100_sr_code)) {
+ dev_err(codec->dev, "Unsupported sample rate: %dHz\n", rate);
+ return -EINVAL;
+ }
+ sr_code = i;
+
+ if ((wm5100->sysclk % rate) == 0) {
+ /* Is this rate already in use? */
+ sr_free = -1;
+ for (i = 0; i < ARRAY_SIZE(wm5100_sr_regs); i++) {
+ if (!wm5100->sr_ref[i] && sr_free == -1) {
+ sr_free = i;
+ continue;
+ }
+ if ((snd_soc_read(codec, wm5100_sr_regs[i]) &
+ WM5100_SAMPLE_RATE_1_MASK) == sr_code)
+ break;
+ }
+
+ if (i < ARRAY_SIZE(wm5100_sr_regs)) {
+ wm5100->sr_ref[i]++;
+ dev_dbg(codec->dev, "SR %dHz, slot %d, ref %d\n",
+ rate, i, wm5100->sr_ref[i]);
+ return i;
+ }
+
+ if (sr_free == -1) {
+ dev_err(codec->dev, "All SR slots already in use\n");
+ return -EBUSY;
+ }
+
+ dev_dbg(codec->dev, "Allocating SR slot %d for %dHz\n",
+ sr_free, rate);
+ wm5100->sr_ref[sr_free]++;
+ snd_soc_update_bits(codec, wm5100_sr_regs[sr_free],
+ WM5100_SAMPLE_RATE_1_MASK,
+ sr_code);
+
+ return sr_free;
+
+ } else {
+ dev_err(codec->dev,
+ "SR %dHz incompatible with %dHz SYSCLK and %dHz ASYNCCLK\n",
+ rate, wm5100->sysclk, wm5100->asyncclk);
+ return -EINVAL;
+ }
+}
+
+static void wm5100_free_sr(struct snd_soc_codec *codec, int rate)
+{
+ struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ int i, sr_code;
+
+ for (i = 0; i < ARRAY_SIZE(wm5100_sr_code); i++)
+ if (wm5100_sr_code[i] == rate)
+ break;
+ if (i == ARRAY_SIZE(wm5100_sr_code)) {
+ dev_err(codec->dev, "Unsupported sample rate: %dHz\n", rate);
+ return;
+ }
+ sr_code = wm5100_sr_code[i];
+
+ for (i = 0; i < ARRAY_SIZE(wm5100_sr_regs); i++) {
+ if (!wm5100->sr_ref[i])
+ continue;
+
+ if ((snd_soc_read(codec, wm5100_sr_regs[i]) &
+ WM5100_SAMPLE_RATE_1_MASK) == sr_code)
+ break;
+ }
+ if (i < ARRAY_SIZE(wm5100_sr_regs)) {
+ wm5100->sr_ref[i]--;
+ dev_dbg(codec->dev, "Dereference SR %dHz, count now %d\n",
+ rate, wm5100->sr_ref[i]);
+ } else {
+ dev_warn(codec->dev, "Freeing unreferenced sample rate %dHz\n",
+ rate);
+ }
+}
+
+static int wm5100_reset(struct wm5100_priv *wm5100)
+{
+ if (wm5100->pdata.reset) {
+ gpio_set_value_cansleep(wm5100->pdata.reset, 0);
+ gpio_set_value_cansleep(wm5100->pdata.reset, 1);
+
+ return 0;
+ } else {
+ return regmap_write(wm5100->regmap, WM5100_SOFTWARE_RESET, 0);
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(in_tlv, -6300, 100, 0);
+static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(mixer_tlv, -3200, 100, 0);
+static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0);
+static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
+
+static const char *wm5100_mixer_texts[] = {
+ "None",
+ "Tone Generator 1",
+ "Tone Generator 2",
+ "AEC loopback",
+ "IN1L",
+ "IN1R",
+ "IN2L",
+ "IN2R",
+ "IN3L",
+ "IN3R",
+ "IN4L",
+ "IN4R",
+ "AIF1RX1",
+ "AIF1RX2",
+ "AIF1RX3",
+ "AIF1RX4",
+ "AIF1RX5",
+ "AIF1RX6",
+ "AIF1RX7",
+ "AIF1RX8",
+ "AIF2RX1",
+ "AIF2RX2",
+ "AIF3RX1",
+ "AIF3RX2",
+ "EQ1",
+ "EQ2",
+ "EQ3",
+ "EQ4",
+ "DRC1L",
+ "DRC1R",
+ "LHPF1",
+ "LHPF2",
+ "LHPF3",
+ "LHPF4",
+ "DSP1.1",
+ "DSP1.2",
+ "DSP1.3",
+ "DSP1.4",
+ "DSP1.5",
+ "DSP1.6",
+ "DSP2.1",
+ "DSP2.2",
+ "DSP2.3",
+ "DSP2.4",
+ "DSP2.5",
+ "DSP2.6",
+ "DSP3.1",
+ "DSP3.2",
+ "DSP3.3",
+ "DSP3.4",
+ "DSP3.5",
+ "DSP3.6",
+ "ASRC1L",
+ "ASRC1R",
+ "ASRC2L",
+ "ASRC2R",
+ "ISRC1INT1",
+ "ISRC1INT2",
+ "ISRC1INT3",
+ "ISRC1INT4",
+ "ISRC2INT1",
+ "ISRC2INT2",
+ "ISRC2INT3",
+ "ISRC2INT4",
+ "ISRC1DEC1",
+ "ISRC1DEC2",
+ "ISRC1DEC3",
+ "ISRC1DEC4",
+ "ISRC2DEC1",
+ "ISRC2DEC2",
+ "ISRC2DEC3",
+ "ISRC2DEC4",
+};
+
+static int wm5100_mixer_values[] = {
+ 0x00,
+ 0x04, /* Tone */
+ 0x05,
+ 0x08, /* AEC */
+ 0x10, /* Input */
+ 0x11,
+ 0x12,
+ 0x13,
+ 0x14,
+ 0x15,
+ 0x16,
+ 0x17,
+ 0x20, /* AIF */
+ 0x21,
+ 0x22,
+ 0x23,
+ 0x24,
+ 0x25,
+ 0x26,
+ 0x27,
+ 0x28,
+ 0x29,
+ 0x30, /* AIF3 - check */
+ 0x31,
+ 0x50, /* EQ */
+ 0x51,
+ 0x52,
+ 0x53,
+ 0x54,
+ 0x58, /* DRC */
+ 0x59,
+ 0x60, /* LHPF1 */
+ 0x61, /* LHPF2 */
+ 0x62, /* LHPF3 */
+ 0x63, /* LHPF4 */
+ 0x68, /* DSP1 */
+ 0x69,
+ 0x6a,
+ 0x6b,
+ 0x6c,
+ 0x6d,
+ 0x70, /* DSP2 */
+ 0x71,
+ 0x72,
+ 0x73,
+ 0x74,
+ 0x75,
+ 0x78, /* DSP3 */
+ 0x79,
+ 0x7a,
+ 0x7b,
+ 0x7c,
+ 0x7d,
+ 0x90, /* ASRC1 */
+ 0x91,
+ 0x92, /* ASRC2 */
+ 0x93,
+ 0xa0, /* ISRC1DEC1 */
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4, /* ISRC1INT1 */
+ 0xa5,
+ 0xa6,
+ 0xa7,
+ 0xa8, /* ISRC2DEC1 */
+ 0xa9,
+ 0xaa,
+ 0xab,
+ 0xac, /* ISRC2INT1 */
+ 0xad,
+ 0xae,
+ 0xaf,
+};
+
+#define WM5100_MIXER_CONTROLS(name, base) \
+ SOC_SINGLE_TLV(name " Input 1 Volume", base + 1 , \
+ WM5100_MIXER_VOL_SHIFT, 80, 0, mixer_tlv), \
+ SOC_SINGLE_TLV(name " Input 2 Volume", base + 3 , \
+ WM5100_MIXER_VOL_SHIFT, 80, 0, mixer_tlv), \
+ SOC_SINGLE_TLV(name " Input 3 Volume", base + 5 , \
+ WM5100_MIXER_VOL_SHIFT, 80, 0, mixer_tlv), \
+ SOC_SINGLE_TLV(name " Input 4 Volume", base + 7 , \
+ WM5100_MIXER_VOL_SHIFT, 80, 0, mixer_tlv)
+
+#define WM5100_MUX_ENUM_DECL(name, reg) \
+ SOC_VALUE_ENUM_SINGLE_DECL(name, reg, 0, 0xff, \
+ wm5100_mixer_texts, wm5100_mixer_values)
+
+#define WM5100_MUX_CTL_DECL(name) \
+ const struct snd_kcontrol_new name##_mux = \
+ SOC_DAPM_ENUM("Route", name##_enum)
+
+#define WM5100_MIXER_ENUMS(name, base_reg) \
+ static WM5100_MUX_ENUM_DECL(name##_in1_enum, base_reg); \
+ static WM5100_MUX_ENUM_DECL(name##_in2_enum, base_reg + 2); \
+ static WM5100_MUX_ENUM_DECL(name##_in3_enum, base_reg + 4); \
+ static WM5100_MUX_ENUM_DECL(name##_in4_enum, base_reg + 6); \
+ static WM5100_MUX_CTL_DECL(name##_in1); \
+ static WM5100_MUX_CTL_DECL(name##_in2); \
+ static WM5100_MUX_CTL_DECL(name##_in3); \
+ static WM5100_MUX_CTL_DECL(name##_in4)
+
+WM5100_MIXER_ENUMS(HPOUT1L, WM5100_OUT1LMIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(HPOUT1R, WM5100_OUT1RMIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(HPOUT2L, WM5100_OUT2LMIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(HPOUT2R, WM5100_OUT2RMIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(HPOUT3L, WM5100_OUT3LMIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(HPOUT3R, WM5100_OUT3RMIX_INPUT_1_SOURCE);
+
+WM5100_MIXER_ENUMS(SPKOUTL, WM5100_OUT4LMIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(SPKOUTR, WM5100_OUT4RMIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(SPKDAT1L, WM5100_OUT5LMIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(SPKDAT1R, WM5100_OUT5RMIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(SPKDAT2L, WM5100_OUT6LMIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(SPKDAT2R, WM5100_OUT6RMIX_INPUT_1_SOURCE);
+
+WM5100_MIXER_ENUMS(PWM1, WM5100_PWM1MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(PWM2, WM5100_PWM1MIX_INPUT_1_SOURCE);
+
+WM5100_MIXER_ENUMS(AIF1TX1, WM5100_AIF1TX1MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(AIF1TX2, WM5100_AIF1TX2MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(AIF1TX3, WM5100_AIF1TX3MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(AIF1TX4, WM5100_AIF1TX4MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(AIF1TX5, WM5100_AIF1TX5MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(AIF1TX6, WM5100_AIF1TX6MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(AIF1TX7, WM5100_AIF1TX7MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(AIF1TX8, WM5100_AIF1TX8MIX_INPUT_1_SOURCE);
+
+WM5100_MIXER_ENUMS(AIF2TX1, WM5100_AIF2TX1MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(AIF2TX2, WM5100_AIF2TX2MIX_INPUT_1_SOURCE);
+
+WM5100_MIXER_ENUMS(AIF3TX1, WM5100_AIF1TX1MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(AIF3TX2, WM5100_AIF1TX2MIX_INPUT_1_SOURCE);
+
+WM5100_MIXER_ENUMS(EQ1, WM5100_EQ1MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(EQ2, WM5100_EQ2MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(EQ3, WM5100_EQ3MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(EQ4, WM5100_EQ4MIX_INPUT_1_SOURCE);
+
+WM5100_MIXER_ENUMS(DRC1L, WM5100_DRC1LMIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(DRC1R, WM5100_DRC1RMIX_INPUT_1_SOURCE);
+
+WM5100_MIXER_ENUMS(LHPF1, WM5100_HPLP1MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(LHPF2, WM5100_HPLP2MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(LHPF3, WM5100_HPLP3MIX_INPUT_1_SOURCE);
+WM5100_MIXER_ENUMS(LHPF4, WM5100_HPLP4MIX_INPUT_1_SOURCE);
+
+#define WM5100_MUX(name, ctrl) \
+ SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+#define WM5100_MIXER_WIDGETS(name, name_str) \
+ WM5100_MUX(name_str " Input 1", &name##_in1_mux), \
+ WM5100_MUX(name_str " Input 2", &name##_in2_mux), \
+ WM5100_MUX(name_str " Input 3", &name##_in3_mux), \
+ WM5100_MUX(name_str " Input 4", &name##_in4_mux), \
+ SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define WM5100_MIXER_INPUT_ROUTES(name) \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2L PGA" }, \
+ { name, "IN2R", "IN2R PGA" }, \
+ { name, "IN3L", "IN3L PGA" }, \
+ { name, "IN3R", "IN3R PGA" }, \
+ { name, "IN4L", "IN4L PGA" }, \
+ { name, "IN4R", "IN4R PGA" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }
+
+#define WM5100_MIXER_ROUTES(widget, name) \
+ { widget, NULL, name " Mixer" }, \
+ { name " Mixer", NULL, name " Input 1" }, \
+ { name " Mixer", NULL, name " Input 2" }, \
+ { name " Mixer", NULL, name " Input 3" }, \
+ { name " Mixer", NULL, name " Input 4" }, \
+ WM5100_MIXER_INPUT_ROUTES(name " Input 1"), \
+ WM5100_MIXER_INPUT_ROUTES(name " Input 2"), \
+ WM5100_MIXER_INPUT_ROUTES(name " Input 3"), \
+ WM5100_MIXER_INPUT_ROUTES(name " Input 4")
+
+static const char *wm5100_lhpf_mode_text[] = {
+ "Low-pass", "High-pass"
+};
+
+static SOC_ENUM_SINGLE_DECL(wm5100_lhpf1_mode,
+ WM5100_HPLPF1_1, WM5100_LHPF1_MODE_SHIFT,
+ wm5100_lhpf_mode_text);
+
+static SOC_ENUM_SINGLE_DECL(wm5100_lhpf2_mode,
+ WM5100_HPLPF2_1, WM5100_LHPF2_MODE_SHIFT,
+ wm5100_lhpf_mode_text);
+
+static SOC_ENUM_SINGLE_DECL(wm5100_lhpf3_mode,
+ WM5100_HPLPF3_1, WM5100_LHPF3_MODE_SHIFT,
+ wm5100_lhpf_mode_text);
+
+static SOC_ENUM_SINGLE_DECL(wm5100_lhpf4_mode,
+ WM5100_HPLPF4_1, WM5100_LHPF4_MODE_SHIFT,
+ wm5100_lhpf_mode_text);
+
+static const struct snd_kcontrol_new wm5100_snd_controls[] = {
+SOC_SINGLE("IN1 High Performance Switch", WM5100_IN1L_CONTROL,
+ WM5100_IN1_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN2 High Performance Switch", WM5100_IN2L_CONTROL,
+ WM5100_IN2_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN3 High Performance Switch", WM5100_IN3L_CONTROL,
+ WM5100_IN3_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN4 High Performance Switch", WM5100_IN4L_CONTROL,
+ WM5100_IN4_OSR_SHIFT, 1, 0),
+
+/* Only applicable for analogue inputs */
+SOC_DOUBLE_R_TLV("IN1 Volume", WM5100_IN1L_CONTROL, WM5100_IN1R_CONTROL,
+ WM5100_IN1L_PGA_VOL_SHIFT, 94, 0, in_tlv),
+SOC_DOUBLE_R_TLV("IN2 Volume", WM5100_IN2L_CONTROL, WM5100_IN2R_CONTROL,
+ WM5100_IN2L_PGA_VOL_SHIFT, 94, 0, in_tlv),
+SOC_DOUBLE_R_TLV("IN3 Volume", WM5100_IN3L_CONTROL, WM5100_IN3R_CONTROL,
+ WM5100_IN3L_PGA_VOL_SHIFT, 94, 0, in_tlv),
+SOC_DOUBLE_R_TLV("IN4 Volume", WM5100_IN4L_CONTROL, WM5100_IN4R_CONTROL,
+ WM5100_IN4L_PGA_VOL_SHIFT, 94, 0, in_tlv),
+
+SOC_DOUBLE_R_TLV("IN1 Digital Volume", WM5100_ADC_DIGITAL_VOLUME_1L,
+ WM5100_ADC_DIGITAL_VOLUME_1R, WM5100_IN1L_VOL_SHIFT, 191,
+ 0, digital_tlv),
+SOC_DOUBLE_R_TLV("IN2 Digital Volume", WM5100_ADC_DIGITAL_VOLUME_2L,
+ WM5100_ADC_DIGITAL_VOLUME_2R, WM5100_IN2L_VOL_SHIFT, 191,
+ 0, digital_tlv),
+SOC_DOUBLE_R_TLV("IN3 Digital Volume", WM5100_ADC_DIGITAL_VOLUME_3L,
+ WM5100_ADC_DIGITAL_VOLUME_3R, WM5100_IN3L_VOL_SHIFT, 191,
+ 0, digital_tlv),
+SOC_DOUBLE_R_TLV("IN4 Digital Volume", WM5100_ADC_DIGITAL_VOLUME_4L,
+ WM5100_ADC_DIGITAL_VOLUME_4R, WM5100_IN4L_VOL_SHIFT, 191,
+ 0, digital_tlv),
+
+SOC_DOUBLE_R("IN1 Switch", WM5100_ADC_DIGITAL_VOLUME_1L,
+ WM5100_ADC_DIGITAL_VOLUME_1R, WM5100_IN1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("IN2 Switch", WM5100_ADC_DIGITAL_VOLUME_2L,
+ WM5100_ADC_DIGITAL_VOLUME_2R, WM5100_IN2L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("IN3 Switch", WM5100_ADC_DIGITAL_VOLUME_3L,
+ WM5100_ADC_DIGITAL_VOLUME_3R, WM5100_IN3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("IN4 Switch", WM5100_ADC_DIGITAL_VOLUME_4L,
+ WM5100_ADC_DIGITAL_VOLUME_4R, WM5100_IN4L_MUTE_SHIFT, 1, 1),
+
+SND_SOC_BYTES_MASK("EQ1 Coefficients", WM5100_EQ1_1, 20, WM5100_EQ1_ENA),
+SND_SOC_BYTES_MASK("EQ2 Coefficients", WM5100_EQ2_1, 20, WM5100_EQ2_ENA),
+SND_SOC_BYTES_MASK("EQ3 Coefficients", WM5100_EQ3_1, 20, WM5100_EQ3_ENA),
+SND_SOC_BYTES_MASK("EQ4 Coefficients", WM5100_EQ4_1, 20, WM5100_EQ4_ENA),
+
+SND_SOC_BYTES_MASK("DRC Coefficients", WM5100_DRC1_CTRL1, 5,
+ WM5100_DRCL_ENA | WM5100_DRCR_ENA),
+
+SND_SOC_BYTES("LHPF1 Coefficeints", WM5100_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficeints", WM5100_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficeints", WM5100_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficeints", WM5100_HPLPF4_2, 1),
+
+SOC_SINGLE("HPOUT1 High Performance Switch", WM5100_OUT_VOLUME_1L,
+ WM5100_OUT1_OSR_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT2 High Performance Switch", WM5100_OUT_VOLUME_2L,
+ WM5100_OUT2_OSR_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT3 High Performance Switch", WM5100_OUT_VOLUME_3L,
+ WM5100_OUT3_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKOUT High Performance Switch", WM5100_OUT_VOLUME_4L,
+ WM5100_OUT4_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT1 High Performance Switch", WM5100_DAC_VOLUME_LIMIT_5L,
+ WM5100_OUT5_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT2 High Performance Switch", WM5100_DAC_VOLUME_LIMIT_6L,
+ WM5100_OUT6_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", WM5100_DAC_DIGITAL_VOLUME_1L,
+ WM5100_DAC_DIGITAL_VOLUME_1R, WM5100_OUT1L_VOL_SHIFT, 159, 0,
+ digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", WM5100_DAC_DIGITAL_VOLUME_2L,
+ WM5100_DAC_DIGITAL_VOLUME_2R, WM5100_OUT2L_VOL_SHIFT, 159, 0,
+ digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT3 Digital Volume", WM5100_DAC_DIGITAL_VOLUME_3L,
+ WM5100_DAC_DIGITAL_VOLUME_3R, WM5100_OUT3L_VOL_SHIFT, 159, 0,
+ digital_tlv),
+SOC_DOUBLE_R_TLV("SPKOUT Digital Volume", WM5100_DAC_DIGITAL_VOLUME_4L,
+ WM5100_DAC_DIGITAL_VOLUME_4R, WM5100_OUT4L_VOL_SHIFT, 159, 0,
+ digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", WM5100_DAC_DIGITAL_VOLUME_5L,
+ WM5100_DAC_DIGITAL_VOLUME_5R, WM5100_OUT5L_VOL_SHIFT, 159, 0,
+ digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT2 Digital Volume", WM5100_DAC_DIGITAL_VOLUME_6L,
+ WM5100_DAC_DIGITAL_VOLUME_6R, WM5100_OUT6L_VOL_SHIFT, 159, 0,
+ digital_tlv),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", WM5100_DAC_DIGITAL_VOLUME_1L,
+ WM5100_DAC_DIGITAL_VOLUME_1R, WM5100_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT2 Digital Switch", WM5100_DAC_DIGITAL_VOLUME_2L,
+ WM5100_DAC_DIGITAL_VOLUME_2R, WM5100_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT3 Digital Switch", WM5100_DAC_DIGITAL_VOLUME_3L,
+ WM5100_DAC_DIGITAL_VOLUME_3R, WM5100_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKOUT Digital Switch", WM5100_DAC_DIGITAL_VOLUME_4L,
+ WM5100_DAC_DIGITAL_VOLUME_4R, WM5100_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", WM5100_DAC_DIGITAL_VOLUME_5L,
+ WM5100_DAC_DIGITAL_VOLUME_5R, WM5100_OUT5L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT2 Digital Switch", WM5100_DAC_DIGITAL_VOLUME_6L,
+ WM5100_DAC_DIGITAL_VOLUME_6R, WM5100_OUT6L_MUTE_SHIFT, 1, 1),
+
+/* FIXME: Only valid from -12dB to 0dB (52-64) */
+SOC_DOUBLE_R_TLV("HPOUT1 Volume", WM5100_OUT_VOLUME_1L, WM5100_OUT_VOLUME_1R,
+ WM5100_OUT1L_PGA_VOL_SHIFT, 64, 0, out_tlv),
+SOC_DOUBLE_R_TLV("HPOUT2 Volume", WM5100_OUT_VOLUME_2L, WM5100_OUT_VOLUME_2R,
+ WM5100_OUT2L_PGA_VOL_SHIFT, 64, 0, out_tlv),
+SOC_DOUBLE_R_TLV("HPOUT3 Volume", WM5100_OUT_VOLUME_3L, WM5100_OUT_VOLUME_3R,
+ WM5100_OUT2L_PGA_VOL_SHIFT, 64, 0, out_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", WM5100_PDM_SPK1_CTRL_1, WM5100_SPK1L_MUTE_SHIFT,
+ WM5100_SPK1R_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE("SPKDAT2 Switch", WM5100_PDM_SPK2_CTRL_1, WM5100_SPK2L_MUTE_SHIFT,
+ WM5100_SPK2R_MUTE_SHIFT, 1, 1),
+
+SOC_SINGLE_TLV("EQ1 Band 1 Volume", WM5100_EQ1_1, WM5100_EQ1_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 Band 2 Volume", WM5100_EQ1_1, WM5100_EQ1_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 Band 3 Volume", WM5100_EQ1_1, WM5100_EQ1_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 Band 4 Volume", WM5100_EQ1_2, WM5100_EQ1_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 Band 5 Volume", WM5100_EQ1_2, WM5100_EQ1_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ2 Band 1 Volume", WM5100_EQ2_1, WM5100_EQ2_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Band 2 Volume", WM5100_EQ2_1, WM5100_EQ2_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Band 3 Volume", WM5100_EQ2_1, WM5100_EQ2_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Band 4 Volume", WM5100_EQ2_2, WM5100_EQ2_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Band 5 Volume", WM5100_EQ2_2, WM5100_EQ2_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ3 Band 1 Volume", WM5100_EQ1_1, WM5100_EQ3_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Band 2 Volume", WM5100_EQ3_1, WM5100_EQ3_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Band 3 Volume", WM5100_EQ3_1, WM5100_EQ3_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Band 4 Volume", WM5100_EQ3_2, WM5100_EQ3_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Band 5 Volume", WM5100_EQ3_2, WM5100_EQ3_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ4 Band 1 Volume", WM5100_EQ4_1, WM5100_EQ4_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Band 2 Volume", WM5100_EQ4_1, WM5100_EQ4_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Band 3 Volume", WM5100_EQ4_1, WM5100_EQ4_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Band 4 Volume", WM5100_EQ4_2, WM5100_EQ4_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Band 5 Volume", WM5100_EQ4_2, WM5100_EQ4_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SOC_ENUM("LHPF1 Mode", wm5100_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", wm5100_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", wm5100_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", wm5100_lhpf4_mode),
+
+WM5100_MIXER_CONTROLS("HPOUT1L", WM5100_OUT1LMIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("HPOUT1R", WM5100_OUT1RMIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("HPOUT2L", WM5100_OUT2LMIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("HPOUT2R", WM5100_OUT2RMIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("HPOUT3L", WM5100_OUT3LMIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("HPOUT3R", WM5100_OUT3RMIX_INPUT_1_SOURCE),
+
+WM5100_MIXER_CONTROLS("SPKOUTL", WM5100_OUT4LMIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("SPKOUTR", WM5100_OUT4RMIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("SPKDAT1L", WM5100_OUT5LMIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("SPKDAT1R", WM5100_OUT5RMIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("SPKDAT2L", WM5100_OUT6LMIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("SPKDAT2R", WM5100_OUT6RMIX_INPUT_1_SOURCE),
+
+WM5100_MIXER_CONTROLS("PWM1", WM5100_PWM1MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("PWM2", WM5100_PWM2MIX_INPUT_1_SOURCE),
+
+WM5100_MIXER_CONTROLS("AIF1TX1", WM5100_AIF1TX1MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("AIF1TX2", WM5100_AIF1TX2MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("AIF1TX3", WM5100_AIF1TX3MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("AIF1TX4", WM5100_AIF1TX4MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("AIF1TX5", WM5100_AIF1TX5MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("AIF1TX6", WM5100_AIF1TX6MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("AIF1TX7", WM5100_AIF1TX7MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("AIF1TX8", WM5100_AIF1TX8MIX_INPUT_1_SOURCE),
+
+WM5100_MIXER_CONTROLS("AIF2TX1", WM5100_AIF2TX1MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("AIF2TX2", WM5100_AIF2TX2MIX_INPUT_1_SOURCE),
+
+WM5100_MIXER_CONTROLS("AIF3TX1", WM5100_AIF3TX1MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("AIF3TX2", WM5100_AIF3TX2MIX_INPUT_1_SOURCE),
+
+WM5100_MIXER_CONTROLS("EQ1", WM5100_EQ1MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("EQ2", WM5100_EQ2MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("EQ3", WM5100_EQ3MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("EQ4", WM5100_EQ4MIX_INPUT_1_SOURCE),
+
+WM5100_MIXER_CONTROLS("DRC1L", WM5100_DRC1LMIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("DRC1R", WM5100_DRC1RMIX_INPUT_1_SOURCE),
+SND_SOC_BYTES_MASK("DRC", WM5100_DRC1_CTRL1, 5,
+ WM5100_DRCL_ENA | WM5100_DRCR_ENA),
+
+WM5100_MIXER_CONTROLS("LHPF1", WM5100_HPLP1MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("LHPF2", WM5100_HPLP2MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("LHPF3", WM5100_HPLP3MIX_INPUT_1_SOURCE),
+WM5100_MIXER_CONTROLS("LHPF4", WM5100_HPLP4MIX_INPUT_1_SOURCE),
+};
+
+static void wm5100_seq_notifier(struct snd_soc_dapm_context *dapm,
+ enum snd_soc_dapm_type event, int subseq)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ u16 val, expect, i;
+
+ /* Wait for the outputs to flag themselves as enabled */
+ if (wm5100->out_ena[0]) {
+ expect = snd_soc_read(codec, WM5100_CHANNEL_ENABLES_1);
+ for (i = 0; i < 200; i++) {
+ val = snd_soc_read(codec, WM5100_OUTPUT_STATUS_1);
+ if (val == expect) {
+ wm5100->out_ena[0] = false;
+ break;
+ }
+ }
+ if (i == 200) {
+ dev_err(codec->dev, "Timeout waiting for OUTPUT1 %x\n",
+ expect);
+ }
+ }
+
+ if (wm5100->out_ena[1]) {
+ expect = snd_soc_read(codec, WM5100_OUTPUT_ENABLES_2);
+ for (i = 0; i < 200; i++) {
+ val = snd_soc_read(codec, WM5100_OUTPUT_STATUS_2);
+ if (val == expect) {
+ wm5100->out_ena[1] = false;
+ break;
+ }
+ }
+ if (i == 200) {
+ dev_err(codec->dev, "Timeout waiting for OUTPUT2 %x\n",
+ expect);
+ }
+ }
+}
+
+static int wm5100_out_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+
+ switch (w->reg) {
+ case WM5100_CHANNEL_ENABLES_1:
+ wm5100->out_ena[0] = true;
+ break;
+ case WM5100_OUTPUT_ENABLES_2:
+ wm5100->out_ena[0] = true;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void wm5100_log_status3(struct wm5100_priv *wm5100, int val)
+{
+ if (val & WM5100_SPK_SHUTDOWN_WARN_EINT)
+ dev_crit(wm5100->dev, "Speaker shutdown warning\n");
+ if (val & WM5100_SPK_SHUTDOWN_EINT)
+ dev_crit(wm5100->dev, "Speaker shutdown\n");
+ if (val & WM5100_CLKGEN_ERR_EINT)
+ dev_crit(wm5100->dev, "SYSCLK underclocked\n");
+ if (val & WM5100_CLKGEN_ERR_ASYNC_EINT)
+ dev_crit(wm5100->dev, "ASYNCCLK underclocked\n");
+}
+
+static void wm5100_log_status4(struct wm5100_priv *wm5100, int val)
+{
+ if (val & WM5100_AIF3_ERR_EINT)
+ dev_err(wm5100->dev, "AIF3 configuration error\n");
+ if (val & WM5100_AIF2_ERR_EINT)
+ dev_err(wm5100->dev, "AIF2 configuration error\n");
+ if (val & WM5100_AIF1_ERR_EINT)
+ dev_err(wm5100->dev, "AIF1 configuration error\n");
+ if (val & WM5100_CTRLIF_ERR_EINT)
+ dev_err(wm5100->dev, "Control interface error\n");
+ if (val & WM5100_ISRC2_UNDERCLOCKED_EINT)
+ dev_err(wm5100->dev, "ISRC2 underclocked\n");
+ if (val & WM5100_ISRC1_UNDERCLOCKED_EINT)
+ dev_err(wm5100->dev, "ISRC1 underclocked\n");
+ if (val & WM5100_FX_UNDERCLOCKED_EINT)
+ dev_err(wm5100->dev, "FX underclocked\n");
+ if (val & WM5100_AIF3_UNDERCLOCKED_EINT)
+ dev_err(wm5100->dev, "AIF3 underclocked\n");
+ if (val & WM5100_AIF2_UNDERCLOCKED_EINT)
+ dev_err(wm5100->dev, "AIF2 underclocked\n");
+ if (val & WM5100_AIF1_UNDERCLOCKED_EINT)
+ dev_err(wm5100->dev, "AIF1 underclocked\n");
+ if (val & WM5100_ASRC_UNDERCLOCKED_EINT)
+ dev_err(wm5100->dev, "ASRC underclocked\n");
+ if (val & WM5100_DAC_UNDERCLOCKED_EINT)
+ dev_err(wm5100->dev, "DAC underclocked\n");
+ if (val & WM5100_ADC_UNDERCLOCKED_EINT)
+ dev_err(wm5100->dev, "ADC underclocked\n");
+ if (val & WM5100_MIXER_UNDERCLOCKED_EINT)
+ dev_err(wm5100->dev, "Mixer underclocked\n");
+}
+
+static int wm5100_post_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = snd_soc_read(codec, WM5100_INTERRUPT_RAW_STATUS_3);
+ ret &= WM5100_SPK_SHUTDOWN_WARN_STS |
+ WM5100_SPK_SHUTDOWN_STS | WM5100_CLKGEN_ERR_STS |
+ WM5100_CLKGEN_ERR_ASYNC_STS;
+ wm5100_log_status3(wm5100, ret);
+
+ ret = snd_soc_read(codec, WM5100_INTERRUPT_RAW_STATUS_4);
+ wm5100_log_status4(wm5100, ret);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget wm5100_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", WM5100_CLOCKING_3, WM5100_SYSCLK_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", WM5100_CLOCKING_6, WM5100_ASYNC_CLK_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
+
+SND_SOC_DAPM_SUPPLY("CP1", WM5100_HP_CHARGE_PUMP_1, WM5100_CP1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_SUPPLY("CP2", WM5100_MIC_CHARGE_PUMP_1, WM5100_CP2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_SUPPLY("CP2 Active", WM5100_MIC_CHARGE_PUMP_1,
+ WM5100_CP2_BYPASS_SHIFT, 1, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", WM5100_MIC_BIAS_CTRL_1, WM5100_MICB1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", WM5100_MIC_BIAS_CTRL_2, WM5100_MICB2_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", WM5100_MIC_BIAS_CTRL_3, WM5100_MICB3_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+SND_SOC_DAPM_INPUT("IN3L"),
+SND_SOC_DAPM_INPUT("IN3R"),
+SND_SOC_DAPM_INPUT("IN4L"),
+SND_SOC_DAPM_INPUT("IN4R"),
+SND_SOC_DAPM_SIGGEN("TONE"),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", WM5100_INPUT_ENABLES, WM5100_IN1L_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", WM5100_INPUT_ENABLES, WM5100_IN1R_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L PGA", WM5100_INPUT_ENABLES, WM5100_IN2L_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R PGA", WM5100_INPUT_ENABLES, WM5100_IN2R_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3L PGA", WM5100_INPUT_ENABLES, WM5100_IN3L_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3R PGA", WM5100_INPUT_ENABLES, WM5100_IN3R_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4L PGA", WM5100_INPUT_ENABLES, WM5100_IN4L_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4R PGA", WM5100_INPUT_ENABLES, WM5100_IN4R_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", WM5100_TONE_GENERATOR_1,
+ WM5100_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", WM5100_TONE_GENERATOR_1,
+ WM5100_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", "AIF1 Playback", 0,
+ WM5100_AUDIO_IF_1_27, WM5100_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", "AIF1 Playback", 1,
+ WM5100_AUDIO_IF_1_27, WM5100_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", "AIF1 Playback", 2,
+ WM5100_AUDIO_IF_1_27, WM5100_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", "AIF1 Playback", 3,
+ WM5100_AUDIO_IF_1_27, WM5100_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", "AIF1 Playback", 4,
+ WM5100_AUDIO_IF_1_27, WM5100_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", "AIF1 Playback", 5,
+ WM5100_AUDIO_IF_1_27, WM5100_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", "AIF1 Playback", 6,
+ WM5100_AUDIO_IF_1_27, WM5100_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", "AIF1 Playback", 7,
+ WM5100_AUDIO_IF_1_27, WM5100_AIF1RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 0,
+ WM5100_AUDIO_IF_2_27, WM5100_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", "AIF2 Playback", 1,
+ WM5100_AUDIO_IF_2_27, WM5100_AIF2RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", "AIF3 Playback", 0,
+ WM5100_AUDIO_IF_3_27, WM5100_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", "AIF3 Playback", 1,
+ WM5100_AUDIO_IF_3_27, WM5100_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", "AIF1 Capture", 0,
+ WM5100_AUDIO_IF_1_26, WM5100_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", "AIF1 Capture", 1,
+ WM5100_AUDIO_IF_1_26, WM5100_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", "AIF1 Capture", 2,
+ WM5100_AUDIO_IF_1_26, WM5100_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", "AIF1 Capture", 3,
+ WM5100_AUDIO_IF_1_26, WM5100_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", "AIF1 Capture", 4,
+ WM5100_AUDIO_IF_1_26, WM5100_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", "AIF1 Capture", 5,
+ WM5100_AUDIO_IF_1_26, WM5100_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", "AIF1 Capture", 6,
+ WM5100_AUDIO_IF_1_26, WM5100_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", "AIF1 Capture", 7,
+ WM5100_AUDIO_IF_1_26, WM5100_AIF1TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", "AIF2 Capture", 0,
+ WM5100_AUDIO_IF_2_26, WM5100_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", "AIF2 Capture", 1,
+ WM5100_AUDIO_IF_2_26, WM5100_AIF2TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", "AIF3 Capture", 0,
+ WM5100_AUDIO_IF_3_26, WM5100_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", "AIF3 Capture", 1,
+ WM5100_AUDIO_IF_3_26, WM5100_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT6L", WM5100_OUTPUT_ENABLES_2, WM5100_OUT6L_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT6R", WM5100_OUTPUT_ENABLES_2, WM5100_OUT6R_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", WM5100_OUTPUT_ENABLES_2, WM5100_OUT5L_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", WM5100_OUTPUT_ENABLES_2, WM5100_OUT5R_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT4L", WM5100_OUTPUT_ENABLES_2, WM5100_OUT4L_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT4R", WM5100_OUTPUT_ENABLES_2, WM5100_OUT4R_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", WM5100_CHANNEL_ENABLES_1, WM5100_HP3L_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3R", WM5100_CHANNEL_ENABLES_1, WM5100_HP3R_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2L", WM5100_CHANNEL_ENABLES_1, WM5100_HP2L_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", WM5100_CHANNEL_ENABLES_1, WM5100_HP2R_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1L", WM5100_CHANNEL_ENABLES_1, WM5100_HP1L_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", WM5100_CHANNEL_ENABLES_1, WM5100_HP1R_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("PWM1 Driver", WM5100_PWM_DRIVE_1, WM5100_PWM1_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("PWM2 Driver", WM5100_PWM_DRIVE_1, WM5100_PWM2_ENA_SHIFT, 0,
+ NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("EQ1", WM5100_EQ1_1, WM5100_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", WM5100_EQ2_1, WM5100_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", WM5100_EQ3_1, WM5100_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", WM5100_EQ4_1, WM5100_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", WM5100_DRC1_CTRL1, WM5100_DRCL_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", WM5100_DRC1_CTRL1, WM5100_DRCR_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", WM5100_HPLPF1_1, WM5100_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", WM5100_HPLPF2_1, WM5100_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", WM5100_HPLPF3_1, WM5100_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", WM5100_HPLPF4_1, WM5100_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+WM5100_MIXER_WIDGETS(EQ1, "EQ1"),
+WM5100_MIXER_WIDGETS(EQ2, "EQ2"),
+WM5100_MIXER_WIDGETS(EQ3, "EQ3"),
+WM5100_MIXER_WIDGETS(EQ4, "EQ4"),
+
+WM5100_MIXER_WIDGETS(DRC1L, "DRC1L"),
+WM5100_MIXER_WIDGETS(DRC1R, "DRC1R"),
+
+WM5100_MIXER_WIDGETS(LHPF1, "LHPF1"),
+WM5100_MIXER_WIDGETS(LHPF2, "LHPF2"),
+WM5100_MIXER_WIDGETS(LHPF3, "LHPF3"),
+WM5100_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+WM5100_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+WM5100_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+WM5100_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+WM5100_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+WM5100_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+WM5100_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+WM5100_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+WM5100_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+WM5100_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+WM5100_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+
+WM5100_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+WM5100_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+WM5100_MIXER_WIDGETS(HPOUT1L, "HPOUT1L"),
+WM5100_MIXER_WIDGETS(HPOUT1R, "HPOUT1R"),
+WM5100_MIXER_WIDGETS(HPOUT2L, "HPOUT2L"),
+WM5100_MIXER_WIDGETS(HPOUT2R, "HPOUT2R"),
+WM5100_MIXER_WIDGETS(HPOUT3L, "HPOUT3L"),
+WM5100_MIXER_WIDGETS(HPOUT3R, "HPOUT3R"),
+
+WM5100_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
+WM5100_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"),
+WM5100_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+WM5100_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+WM5100_MIXER_WIDGETS(SPKDAT2L, "SPKDAT2L"),
+WM5100_MIXER_WIDGETS(SPKDAT2R, "SPKDAT2R"),
+
+WM5100_MIXER_WIDGETS(PWM1, "PWM1"),
+WM5100_MIXER_WIDGETS(PWM2, "PWM2"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("HPOUT3L"),
+SND_SOC_DAPM_OUTPUT("HPOUT3R"),
+SND_SOC_DAPM_OUTPUT("SPKOUTL"),
+SND_SOC_DAPM_OUTPUT("SPKOUTR"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1"),
+SND_SOC_DAPM_OUTPUT("SPKDAT2"),
+SND_SOC_DAPM_OUTPUT("PWM1"),
+SND_SOC_DAPM_OUTPUT("PWM2"),
+};
+
+/* We register a _POST event if we don't have IRQ support so we can
+ * look at the error status from the CODEC - if we've got the IRQ
+ * hooked up then we will get prompted to look by an interrupt.
+ */
+static const struct snd_soc_dapm_widget wm5100_dapm_widgets_noirq[] = {
+SND_SOC_DAPM_POST("Post", wm5100_post_ev),
+};
+
+static const struct snd_soc_dapm_route wm5100_dapm_routes[] = {
+ { "CP1", NULL, "CPVDD" },
+ { "CP2 Active", NULL, "CPVDD" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+ { "IN4L", NULL, "SYSCLK" },
+ { "IN4R", NULL, "SYSCLK" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT2L", NULL, "SYSCLK" },
+ { "OUT2R", NULL, "SYSCLK" },
+ { "OUT3L", NULL, "SYSCLK" },
+ { "OUT3R", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+ { "OUT4R", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+ { "OUT6L", NULL, "SYSCLK" },
+ { "OUT6R", NULL, "SYSCLK" },
+
+ { "AIF1RX1", NULL, "SYSCLK" },
+ { "AIF1RX2", NULL, "SYSCLK" },
+ { "AIF1RX3", NULL, "SYSCLK" },
+ { "AIF1RX4", NULL, "SYSCLK" },
+ { "AIF1RX5", NULL, "SYSCLK" },
+ { "AIF1RX6", NULL, "SYSCLK" },
+ { "AIF1RX7", NULL, "SYSCLK" },
+ { "AIF1RX8", NULL, "SYSCLK" },
+
+ { "AIF2RX1", NULL, "SYSCLK" },
+ { "AIF2RX1", NULL, "DBVDD2" },
+ { "AIF2RX2", NULL, "SYSCLK" },
+ { "AIF2RX2", NULL, "DBVDD2" },
+
+ { "AIF3RX1", NULL, "SYSCLK" },
+ { "AIF3RX1", NULL, "DBVDD3" },
+ { "AIF3RX2", NULL, "SYSCLK" },
+ { "AIF3RX2", NULL, "DBVDD3" },
+
+ { "AIF1TX1", NULL, "SYSCLK" },
+ { "AIF1TX2", NULL, "SYSCLK" },
+ { "AIF1TX3", NULL, "SYSCLK" },
+ { "AIF1TX4", NULL, "SYSCLK" },
+ { "AIF1TX5", NULL, "SYSCLK" },
+ { "AIF1TX6", NULL, "SYSCLK" },
+ { "AIF1TX7", NULL, "SYSCLK" },
+ { "AIF1TX8", NULL, "SYSCLK" },
+
+ { "AIF2TX1", NULL, "SYSCLK" },
+ { "AIF2TX1", NULL, "DBVDD2" },
+ { "AIF2TX2", NULL, "SYSCLK" },
+ { "AIF2TX2", NULL, "DBVDD2" },
+
+ { "AIF3TX1", NULL, "SYSCLK" },
+ { "AIF3TX1", NULL, "DBVDD3" },
+ { "AIF3TX2", NULL, "SYSCLK" },
+ { "AIF3TX2", NULL, "DBVDD3" },
+
+ { "MICBIAS1", NULL, "CP2" },
+ { "MICBIAS2", NULL, "CP2" },
+ { "MICBIAS3", NULL, "CP2" },
+
+ { "IN1L PGA", NULL, "CP2" },
+ { "IN1R PGA", NULL, "CP2" },
+ { "IN2L PGA", NULL, "CP2" },
+ { "IN2R PGA", NULL, "CP2" },
+ { "IN3L PGA", NULL, "CP2" },
+ { "IN3R PGA", NULL, "CP2" },
+ { "IN4L PGA", NULL, "CP2" },
+ { "IN4R PGA", NULL, "CP2" },
+
+ { "IN1L PGA", NULL, "CP2 Active" },
+ { "IN1R PGA", NULL, "CP2 Active" },
+ { "IN2L PGA", NULL, "CP2 Active" },
+ { "IN2R PGA", NULL, "CP2 Active" },
+ { "IN3L PGA", NULL, "CP2 Active" },
+ { "IN3R PGA", NULL, "CP2 Active" },
+ { "IN4L PGA", NULL, "CP2 Active" },
+ { "IN4R PGA", NULL, "CP2 Active" },
+
+ { "OUT1L", NULL, "CP1" },
+ { "OUT1R", NULL, "CP1" },
+ { "OUT2L", NULL, "CP1" },
+ { "OUT2R", NULL, "CP1" },
+ { "OUT3L", NULL, "CP1" },
+ { "OUT3R", NULL, "CP1" },
+
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "IN1L PGA", NULL, "IN1L" },
+ { "IN1R PGA", NULL, "IN1R" },
+ { "IN2L PGA", NULL, "IN2L" },
+ { "IN2R PGA", NULL, "IN2R" },
+ { "IN3L PGA", NULL, "IN3L" },
+ { "IN3R PGA", NULL, "IN3R" },
+ { "IN4L PGA", NULL, "IN4L" },
+ { "IN4R PGA", NULL, "IN4R" },
+
+ WM5100_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ WM5100_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ WM5100_MIXER_ROUTES("OUT2L", "HPOUT2L"),
+ WM5100_MIXER_ROUTES("OUT2R", "HPOUT2R"),
+ WM5100_MIXER_ROUTES("OUT3L", "HPOUT3L"),
+ WM5100_MIXER_ROUTES("OUT3R", "HPOUT3R"),
+
+ WM5100_MIXER_ROUTES("OUT4L", "SPKOUTL"),
+ WM5100_MIXER_ROUTES("OUT4R", "SPKOUTR"),
+ WM5100_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ WM5100_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+ WM5100_MIXER_ROUTES("OUT6L", "SPKDAT2L"),
+ WM5100_MIXER_ROUTES("OUT6R", "SPKDAT2R"),
+
+ WM5100_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ WM5100_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ WM5100_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ WM5100_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ WM5100_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ WM5100_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ WM5100_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ WM5100_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+ WM5100_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+ WM5100_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ WM5100_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ WM5100_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+
+ WM5100_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ WM5100_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ WM5100_MIXER_ROUTES("EQ1", "EQ1"),
+ WM5100_MIXER_ROUTES("EQ2", "EQ2"),
+ WM5100_MIXER_ROUTES("EQ3", "EQ3"),
+ WM5100_MIXER_ROUTES("EQ4", "EQ4"),
+
+ WM5100_MIXER_ROUTES("DRC1L", "DRC1L"),
+ WM5100_MIXER_ROUTES("DRC1R", "DRC1R"),
+
+ WM5100_MIXER_ROUTES("LHPF1", "LHPF1"),
+ WM5100_MIXER_ROUTES("LHPF2", "LHPF2"),
+ WM5100_MIXER_ROUTES("LHPF3", "LHPF3"),
+ WM5100_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+ { "HPOUT2L", NULL, "OUT2L" },
+ { "HPOUT2R", NULL, "OUT2R" },
+ { "HPOUT3L", NULL, "OUT3L" },
+ { "HPOUT3R", NULL, "OUT3R" },
+ { "SPKOUTL", NULL, "OUT4L" },
+ { "SPKOUTR", NULL, "OUT4R" },
+ { "SPKDAT1", NULL, "OUT5L" },
+ { "SPKDAT1", NULL, "OUT5R" },
+ { "SPKDAT2", NULL, "OUT6L" },
+ { "SPKDAT2", NULL, "OUT6R" },
+ { "PWM1", NULL, "PWM1 Driver" },
+ { "PWM2", NULL, "PWM2 Driver" },
+};
+
+static const struct reg_default wm5100_reva_patches[] = {
+ { WM5100_AUDIO_IF_1_10, 0 },
+ { WM5100_AUDIO_IF_1_11, 1 },
+ { WM5100_AUDIO_IF_1_12, 2 },
+ { WM5100_AUDIO_IF_1_13, 3 },
+ { WM5100_AUDIO_IF_1_14, 4 },
+ { WM5100_AUDIO_IF_1_15, 5 },
+ { WM5100_AUDIO_IF_1_16, 6 },
+ { WM5100_AUDIO_IF_1_17, 7 },
+
+ { WM5100_AUDIO_IF_1_18, 0 },
+ { WM5100_AUDIO_IF_1_19, 1 },
+ { WM5100_AUDIO_IF_1_20, 2 },
+ { WM5100_AUDIO_IF_1_21, 3 },
+ { WM5100_AUDIO_IF_1_22, 4 },
+ { WM5100_AUDIO_IF_1_23, 5 },
+ { WM5100_AUDIO_IF_1_24, 6 },
+ { WM5100_AUDIO_IF_1_25, 7 },
+
+ { WM5100_AUDIO_IF_2_10, 0 },
+ { WM5100_AUDIO_IF_2_11, 1 },
+
+ { WM5100_AUDIO_IF_2_18, 0 },
+ { WM5100_AUDIO_IF_2_19, 1 },
+
+ { WM5100_AUDIO_IF_3_10, 0 },
+ { WM5100_AUDIO_IF_3_11, 1 },
+
+ { WM5100_AUDIO_IF_3_18, 0 },
+ { WM5100_AUDIO_IF_3_19, 1 },
+};
+
+static int wm5100_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int lrclk, bclk, mask, base;
+
+ base = dai->driver->base;
+
+ lrclk = 0;
+ bclk = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ mask = 0;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ mask = 2;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported DAI format %d\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ lrclk |= WM5100_AIF1TX_LRCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ bclk |= WM5100_AIF1_BCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ lrclk |= WM5100_AIF1TX_LRCLK_MSTR;
+ bclk |= WM5100_AIF1_BCLK_MSTR;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported master mode %d\n",
+ fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk |= WM5100_AIF1_BCLK_INV;
+ lrclk |= WM5100_AIF1TX_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk |= WM5100_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk |= WM5100_AIF1TX_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, base + 1, WM5100_AIF1_BCLK_MSTR |
+ WM5100_AIF1_BCLK_INV, bclk);
+ snd_soc_update_bits(codec, base + 2, WM5100_AIF1TX_LRCLK_MSTR |
+ WM5100_AIF1TX_LRCLK_INV, lrclk);
+ snd_soc_update_bits(codec, base + 3, WM5100_AIF1TX_LRCLK_MSTR |
+ WM5100_AIF1TX_LRCLK_INV, lrclk);
+ snd_soc_update_bits(codec, base + 5, WM5100_AIF1_FMT_MASK, mask);
+
+ return 0;
+}
+
+#define WM5100_NUM_BCLK_RATES 19
+
+static int wm5100_bclk_rates_dat[WM5100_NUM_BCLK_RATES] = {
+ 32000,
+ 48000,
+ 64000,
+ 96000,
+ 128000,
+ 192000,
+ 256000,
+ 384000,
+ 512000,
+ 768000,
+ 1024000,
+ 1536000,
+ 2048000,
+ 3072000,
+ 4096000,
+ 6144000,
+ 8192000,
+ 12288000,
+ 24576000,
+};
+
+static int wm5100_bclk_rates_cd[WM5100_NUM_BCLK_RATES] = {
+ 29400,
+ 44100,
+ 58800,
+ 88200,
+ 117600,
+ 176400,
+ 235200,
+ 352800,
+ 470400,
+ 705600,
+ 940800,
+ 1411200,
+ 1881600,
+ 2882400,
+ 3763200,
+ 5644800,
+ 7526400,
+ 11289600,
+ 22579600,
+};
+
+static int wm5100_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ bool async = wm5100->aif_async[dai->id];
+ int i, base, bclk, aif_rate, lrclk, wl, fl, sr;
+ int *bclk_rates;
+
+ base = dai->driver->base;
+
+ /* Data sizes if not using TDM */
+ wl = snd_pcm_format_width(params_format(params));
+ if (wl < 0)
+ return wl;
+ fl = snd_soc_params_to_frame_size(params);
+ if (fl < 0)
+ return fl;
+
+ dev_dbg(codec->dev, "Word length %d bits, frame length %d bits\n",
+ wl, fl);
+
+ /* Target BCLK rate */
+ bclk = snd_soc_params_to_bclk(params);
+ if (bclk < 0)
+ return bclk;
+
+ /* Root for BCLK depends on SYS/ASYNCCLK */
+ if (!async) {
+ aif_rate = wm5100->sysclk;
+ sr = wm5100_alloc_sr(codec, params_rate(params));
+ if (sr < 0)
+ return sr;
+ } else {
+ /* If we're in ASYNCCLK set the ASYNC sample rate */
+ aif_rate = wm5100->asyncclk;
+ sr = 3;
+
+ for (i = 0; i < ARRAY_SIZE(wm5100_sr_code); i++)
+ if (params_rate(params) == wm5100_sr_code[i])
+ break;
+ if (i == ARRAY_SIZE(wm5100_sr_code)) {
+ dev_err(codec->dev, "Invalid rate %dHzn",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ /* TODO: We should really check for symmetry */
+ snd_soc_update_bits(codec, WM5100_CLOCKING_8,
+ WM5100_ASYNC_SAMPLE_RATE_MASK, i);
+ }
+
+ if (!aif_rate) {
+ dev_err(codec->dev, "%s has no rate set\n",
+ async ? "ASYNCCLK" : "SYSCLK");
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "Target BCLK is %dHz, using %dHz %s\n",
+ bclk, aif_rate, async ? "ASYNCCLK" : "SYSCLK");
+
+ if (aif_rate % 4000)
+ bclk_rates = wm5100_bclk_rates_cd;
+ else
+ bclk_rates = wm5100_bclk_rates_dat;
+
+ for (i = 0; i < WM5100_NUM_BCLK_RATES; i++)
+ if (bclk_rates[i] >= bclk && (bclk_rates[i] % bclk == 0))
+ break;
+ if (i == WM5100_NUM_BCLK_RATES) {
+ dev_err(codec->dev,
+ "No valid BCLK for %dHz found from %dHz %s\n",
+ bclk, aif_rate, async ? "ASYNCCLK" : "SYSCLK");
+ return -EINVAL;
+ }
+
+ bclk = i;
+ dev_dbg(codec->dev, "Setting %dHz BCLK\n", bclk_rates[bclk]);
+ snd_soc_update_bits(codec, base + 1, WM5100_AIF1_BCLK_FREQ_MASK, bclk);
+
+ lrclk = bclk_rates[bclk] / params_rate(params);
+ dev_dbg(codec->dev, "Setting %dHz LRCLK\n", bclk_rates[bclk] / lrclk);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ wm5100->aif_symmetric[dai->id])
+ snd_soc_update_bits(codec, base + 7,
+ WM5100_AIF1RX_BCPF_MASK, lrclk);
+ else
+ snd_soc_update_bits(codec, base + 6,
+ WM5100_AIF1TX_BCPF_MASK, lrclk);
+
+ i = (wl << WM5100_AIF1TX_WL_SHIFT) | fl;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_update_bits(codec, base + 9,
+ WM5100_AIF1RX_WL_MASK |
+ WM5100_AIF1RX_SLOT_LEN_MASK, i);
+ else
+ snd_soc_update_bits(codec, base + 8,
+ WM5100_AIF1TX_WL_MASK |
+ WM5100_AIF1TX_SLOT_LEN_MASK, i);
+
+ snd_soc_update_bits(codec, base + 4, WM5100_AIF1_RATE_MASK, sr);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wm5100_dai_ops = {
+ .set_fmt = wm5100_set_fmt,
+ .hw_params = wm5100_hw_params,
+};
+
+static int wm5100_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ int *rate_store;
+ int fval, audio_rate, ret, reg;
+
+ switch (clk_id) {
+ case WM5100_CLK_SYSCLK:
+ reg = WM5100_CLOCKING_3;
+ rate_store = &wm5100->sysclk;
+ break;
+ case WM5100_CLK_ASYNCCLK:
+ reg = WM5100_CLOCKING_7;
+ rate_store = &wm5100->asyncclk;
+ break;
+ case WM5100_CLK_32KHZ:
+ /* The 32kHz clock is slightly different to the others */
+ switch (source) {
+ case WM5100_CLKSRC_MCLK1:
+ case WM5100_CLKSRC_MCLK2:
+ case WM5100_CLKSRC_SYSCLK:
+ snd_soc_update_bits(codec, WM5100_CLOCKING_1,
+ WM5100_CLK_32K_SRC_MASK,
+ source);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+
+ case WM5100_CLK_AIF1:
+ case WM5100_CLK_AIF2:
+ case WM5100_CLK_AIF3:
+ /* Not real clocks, record which clock domain they're in */
+ switch (source) {
+ case WM5100_CLKSRC_SYSCLK:
+ wm5100->aif_async[clk_id - 1] = false;
+ break;
+ case WM5100_CLKSRC_ASYNCCLK:
+ wm5100->aif_async[clk_id - 1] = true;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid source %d\n", source);
+ return -EINVAL;
+ }
+ return 0;
+
+ case WM5100_CLK_OPCLK:
+ switch (freq) {
+ case 5644800:
+ case 6144000:
+ snd_soc_update_bits(codec, WM5100_MISC_GPIO_1,
+ WM5100_OPCLK_SEL_MASK, 0);
+ break;
+ case 11289600:
+ case 12288000:
+ snd_soc_update_bits(codec, WM5100_MISC_GPIO_1,
+ WM5100_OPCLK_SEL_MASK, 0);
+ break;
+ case 22579200:
+ case 24576000:
+ snd_soc_update_bits(codec, WM5100_MISC_GPIO_1,
+ WM5100_OPCLK_SEL_MASK, 0);
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported OPCLK %dHz\n",
+ freq);
+ return -EINVAL;
+ }
+ return 0;
+
+ default:
+ dev_err(codec->dev, "Unknown clock %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ switch (source) {
+ case WM5100_CLKSRC_SYSCLK:
+ case WM5100_CLKSRC_ASYNCCLK:
+ dev_err(codec->dev, "Invalid source %d\n", source);
+ return -EINVAL;
+ }
+
+ switch (freq) {
+ case 5644800:
+ case 6144000:
+ fval = 0;
+ break;
+ case 11289600:
+ case 12288000:
+ fval = 1;
+ break;
+ case 22579200:
+ case 24576000:
+ fval = 2;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock rate: %d\n", freq);
+ return -EINVAL;
+ }
+
+ switch (freq) {
+ case 5644800:
+ case 11289600:
+ case 22579200:
+ audio_rate = 44100;
+ break;
+
+ case 6144000:
+ case 12288000:
+ case 24576000:
+ audio_rate = 48000;
+ break;
+
+ default:
+ BUG();
+ audio_rate = 0;
+ break;
+ }
+
+ /* TODO: Check if MCLKs are in use and enable/disable pulls to
+ * match.
+ */
+
+ snd_soc_update_bits(codec, reg, WM5100_SYSCLK_FREQ_MASK |
+ WM5100_SYSCLK_SRC_MASK,
+ fval << WM5100_SYSCLK_FREQ_SHIFT | source);
+
+ /* If this is SYSCLK then configure the clock rate for the
+ * internal audio functions to the natural sample rate for
+ * this clock rate.
+ */
+ if (clk_id == WM5100_CLK_SYSCLK) {
+ dev_dbg(codec->dev, "Setting primary audio rate to %dHz",
+ audio_rate);
+ if (0 && *rate_store)
+ wm5100_free_sr(codec, audio_rate);
+ ret = wm5100_alloc_sr(codec, audio_rate);
+ if (ret != 0)
+ dev_warn(codec->dev, "Primary audio slot is %d\n",
+ ret);
+ }
+
+ *rate_store = freq;
+
+ return 0;
+}
+
+struct _fll_div {
+ u16 fll_fratio;
+ u16 fll_outdiv;
+ u16 fll_refclk_div;
+ u16 n;
+ u16 theta;
+ u16 lambda;
+};
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fll_fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ unsigned int target;
+ unsigned int div;
+ unsigned int fratio, gcd_fll;
+ int i;
+
+ /* Fref must be <=13.5MHz */
+ div = 1;
+ fll_div->fll_refclk_div = 0;
+ while ((Fref / div) > 13500000) {
+ div *= 2;
+ fll_div->fll_refclk_div++;
+
+ if (div > 8) {
+ pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+ }
+
+ pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout);
+
+ /* Apply the division for our remaining calculations */
+ Fref /= div;
+
+ /* Fvco should be 90-100MHz; don't check the upper bound */
+ div = 2;
+ while (Fout * div < 90000000) {
+ div++;
+ if (div > 64) {
+ pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ target = Fout * div;
+ fll_div->fll_outdiv = div - 1;
+
+ pr_debug("FLL Fvco=%dHz\n", target);
+
+ /* Find an appropraite FLL_FRATIO and factor it out of the target */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+ fratio = fll_fratios[i].ratio;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_fratios)) {
+ pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+ return -EINVAL;
+ }
+
+ fll_div->n = target / (fratio * Fref);
+
+ if (target % Fref == 0) {
+ fll_div->theta = 0;
+ fll_div->lambda = 0;
+ } else {
+ gcd_fll = gcd(target, fratio * Fref);
+
+ fll_div->theta = (target - (fll_div->n * fratio * Fref))
+ / gcd_fll;
+ fll_div->lambda = (fratio * Fref) / gcd_fll;
+ }
+
+ pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n",
+ fll_div->n, fll_div->theta, fll_div->lambda);
+ pr_debug("FLL_FRATIO=%x(%d) FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n",
+ fll_div->fll_fratio, fratio, fll_div->fll_outdiv,
+ fll_div->fll_refclk_div);
+
+ return 0;
+}
+
+static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ struct _fll_div factors;
+ struct wm5100_fll *fll;
+ int ret, base, lock, i, timeout;
+ unsigned long time_left;
+
+ switch (fll_id) {
+ case WM5100_FLL1:
+ fll = &wm5100->fll[0];
+ base = WM5100_FLL1_CONTROL_1 - 1;
+ lock = WM5100_FLL1_LOCK_STS;
+ break;
+ case WM5100_FLL2:
+ fll = &wm5100->fll[1];
+ base = WM5100_FLL2_CONTROL_2 - 1;
+ lock = WM5100_FLL2_LOCK_STS;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown FLL %d\n",fll_id);
+ return -EINVAL;
+ }
+
+ if (!Fout) {
+ dev_dbg(codec->dev, "FLL%d disabled", fll_id);
+ if (fll->fout)
+ pm_runtime_put(codec->dev);
+ fll->fout = 0;
+ snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, 0);
+ return 0;
+ }
+
+ switch (source) {
+ case WM5100_FLL_SRC_MCLK1:
+ case WM5100_FLL_SRC_MCLK2:
+ case WM5100_FLL_SRC_FLL1:
+ case WM5100_FLL_SRC_FLL2:
+ case WM5100_FLL_SRC_AIF1BCLK:
+ case WM5100_FLL_SRC_AIF2BCLK:
+ case WM5100_FLL_SRC_AIF3BCLK:
+ break;
+ default:
+ dev_err(codec->dev, "Invalid FLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = fll_factors(&factors, Fref, Fout);
+ if (ret < 0)
+ return ret;
+
+ /* Disable the FLL while we reconfigure */
+ snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, 0);
+
+ snd_soc_update_bits(codec, base + 2,
+ WM5100_FLL1_OUTDIV_MASK | WM5100_FLL1_FRATIO_MASK,
+ (factors.fll_outdiv << WM5100_FLL1_OUTDIV_SHIFT) |
+ factors.fll_fratio);
+ snd_soc_update_bits(codec, base + 3, WM5100_FLL1_THETA_MASK,
+ factors.theta);
+ snd_soc_update_bits(codec, base + 5, WM5100_FLL1_N_MASK, factors.n);
+ snd_soc_update_bits(codec, base + 6,
+ WM5100_FLL1_REFCLK_DIV_MASK |
+ WM5100_FLL1_REFCLK_SRC_MASK,
+ (factors.fll_refclk_div
+ << WM5100_FLL1_REFCLK_DIV_SHIFT) | source);
+ snd_soc_update_bits(codec, base + 7, WM5100_FLL1_LAMBDA_MASK,
+ factors.lambda);
+
+ /* Clear any pending completions */
+ try_wait_for_completion(&fll->lock);
+
+ pm_runtime_get_sync(codec->dev);
+
+ snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, WM5100_FLL1_ENA);
+
+ if (i2c->irq)
+ timeout = 2;
+ else
+ timeout = 50;
+
+ snd_soc_update_bits(codec, WM5100_CLOCKING_3, WM5100_SYSCLK_ENA,
+ WM5100_SYSCLK_ENA);
+
+ /* Poll for the lock; will use interrupt when we can test */
+ for (i = 0; i < timeout; i++) {
+ if (i2c->irq) {
+ time_left = wait_for_completion_timeout(&fll->lock,
+ msecs_to_jiffies(25));
+ if (time_left > 0)
+ break;
+ } else {
+ msleep(1);
+ }
+
+ ret = snd_soc_read(codec,
+ WM5100_INTERRUPT_RAW_STATUS_3);
+ if (ret < 0) {
+ dev_err(codec->dev,
+ "Failed to read FLL status: %d\n",
+ ret);
+ continue;
+ }
+ if (ret & lock)
+ break;
+ }
+ if (i == timeout) {
+ dev_err(codec->dev, "FLL%d lock timed out\n", fll_id);
+ pm_runtime_put(codec->dev);
+ return -ETIMEDOUT;
+ }
+
+ fll->src = source;
+ fll->fref = Fref;
+ fll->fout = Fout;
+
+ dev_dbg(codec->dev, "FLL%d running %dHz->%dHz\n", fll_id,
+ Fref, Fout);
+
+ return 0;
+}
+
+/* Actually go much higher */
+#define WM5100_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM5100_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver wm5100_dai[] = {
+ {
+ .name = "wm5100-aif1",
+ .base = WM5100_AUDIO_IF_1_1 - 1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM5100_RATES,
+ .formats = WM5100_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM5100_RATES,
+ .formats = WM5100_FORMATS,
+ },
+ .ops = &wm5100_dai_ops,
+ },
+ {
+ .name = "wm5100-aif2",
+ .id = 1,
+ .base = WM5100_AUDIO_IF_2_1 - 1,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM5100_RATES,
+ .formats = WM5100_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM5100_RATES,
+ .formats = WM5100_FORMATS,
+ },
+ .ops = &wm5100_dai_ops,
+ },
+ {
+ .name = "wm5100-aif3",
+ .id = 2,
+ .base = WM5100_AUDIO_IF_3_1 - 1,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM5100_RATES,
+ .formats = WM5100_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM5100_RATES,
+ .formats = WM5100_FORMATS,
+ },
+ .ops = &wm5100_dai_ops,
+ },
+};
+
+static int wm5100_dig_vu[] = {
+ WM5100_ADC_DIGITAL_VOLUME_1L,
+ WM5100_ADC_DIGITAL_VOLUME_1R,
+ WM5100_ADC_DIGITAL_VOLUME_2L,
+ WM5100_ADC_DIGITAL_VOLUME_2R,
+ WM5100_ADC_DIGITAL_VOLUME_3L,
+ WM5100_ADC_DIGITAL_VOLUME_3R,
+ WM5100_ADC_DIGITAL_VOLUME_4L,
+ WM5100_ADC_DIGITAL_VOLUME_4R,
+
+ WM5100_DAC_DIGITAL_VOLUME_1L,
+ WM5100_DAC_DIGITAL_VOLUME_1R,
+ WM5100_DAC_DIGITAL_VOLUME_2L,
+ WM5100_DAC_DIGITAL_VOLUME_2R,
+ WM5100_DAC_DIGITAL_VOLUME_3L,
+ WM5100_DAC_DIGITAL_VOLUME_3R,
+ WM5100_DAC_DIGITAL_VOLUME_4L,
+ WM5100_DAC_DIGITAL_VOLUME_4R,
+ WM5100_DAC_DIGITAL_VOLUME_5L,
+ WM5100_DAC_DIGITAL_VOLUME_5R,
+ WM5100_DAC_DIGITAL_VOLUME_6L,
+ WM5100_DAC_DIGITAL_VOLUME_6R,
+};
+
+static void wm5100_set_detect_mode(struct wm5100_priv *wm5100, int the_mode)
+{
+ struct wm5100_jack_mode *mode = &wm5100->pdata.jack_modes[the_mode];
+
+ if (WARN_ON(the_mode >= ARRAY_SIZE(wm5100->pdata.jack_modes)))
+ return;
+
+ gpio_set_value_cansleep(wm5100->pdata.hp_pol, mode->hp_pol);
+ regmap_update_bits(wm5100->regmap, WM5100_ACCESSORY_DETECT_MODE_1,
+ WM5100_ACCDET_BIAS_SRC_MASK |
+ WM5100_ACCDET_SRC,
+ (mode->bias << WM5100_ACCDET_BIAS_SRC_SHIFT) |
+ mode->micd_src << WM5100_ACCDET_SRC_SHIFT);
+ regmap_update_bits(wm5100->regmap, WM5100_MISC_CONTROL,
+ WM5100_HPCOM_SRC,
+ mode->micd_src << WM5100_HPCOM_SRC_SHIFT);
+
+ wm5100->jack_mode = the_mode;
+
+ dev_dbg(wm5100->dev, "Set microphone polarity to %d\n",
+ wm5100->jack_mode);
+}
+
+static void wm5100_report_headphone(struct wm5100_priv *wm5100)
+{
+ dev_dbg(wm5100->dev, "Headphone detected\n");
+ wm5100->jack_detecting = false;
+ snd_soc_jack_report(wm5100->jack, SND_JACK_HEADPHONE,
+ SND_JACK_HEADPHONE);
+
+ /* Increase the detection rate a bit for responsiveness. */
+ regmap_update_bits(wm5100->regmap, WM5100_MIC_DETECT_1,
+ WM5100_ACCDET_RATE_MASK,
+ 7 << WM5100_ACCDET_RATE_SHIFT);
+}
+
+static void wm5100_micd_irq(struct wm5100_priv *wm5100)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(wm5100->regmap, WM5100_MIC_DETECT_3, &val);
+ if (ret != 0) {
+ dev_err(wm5100->dev, "Failed to read micropone status: %d\n",
+ ret);
+ return;
+ }
+
+ dev_dbg(wm5100->dev, "Microphone event: %x\n", val);
+
+ if (!(val & WM5100_ACCDET_VALID)) {
+ dev_warn(wm5100->dev, "Microphone detection state invalid\n");
+ return;
+ }
+
+ /* No accessory, reset everything and report removal */
+ if (!(val & WM5100_ACCDET_STS)) {
+ dev_dbg(wm5100->dev, "Jack removal detected\n");
+ wm5100->jack_mic = false;
+ wm5100->jack_detecting = true;
+ wm5100->jack_flips = 0;
+ snd_soc_jack_report(wm5100->jack, 0,
+ SND_JACK_LINEOUT | SND_JACK_HEADSET |
+ SND_JACK_BTN_0);
+
+ regmap_update_bits(wm5100->regmap, WM5100_MIC_DETECT_1,
+ WM5100_ACCDET_RATE_MASK,
+ WM5100_ACCDET_RATE_MASK);
+ return;
+ }
+
+ /* If the measurement is very high we've got a microphone,
+ * either we just detected one or if we already reported then
+ * we've got a button release event.
+ */
+ if (val & 0x400) {
+ if (wm5100->jack_detecting) {
+ dev_dbg(wm5100->dev, "Microphone detected\n");
+ wm5100->jack_mic = true;
+ wm5100->jack_detecting = false;
+ snd_soc_jack_report(wm5100->jack,
+ SND_JACK_HEADSET,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+
+ /* Increase poll rate to give better responsiveness
+ * for buttons */
+ regmap_update_bits(wm5100->regmap, WM5100_MIC_DETECT_1,
+ WM5100_ACCDET_RATE_MASK,
+ 5 << WM5100_ACCDET_RATE_SHIFT);
+ } else {
+ dev_dbg(wm5100->dev, "Mic button up\n");
+ snd_soc_jack_report(wm5100->jack, 0, SND_JACK_BTN_0);
+ }
+
+ return;
+ }
+
+ /* If we detected a lower impedence during initial startup
+ * then we probably have the wrong polarity, flip it. Don't
+ * do this for the lowest impedences to speed up detection of
+ * plain headphones and give up if neither polarity looks
+ * sensible.
+ */
+ if (wm5100->jack_detecting && (val & 0x3f8)) {
+ wm5100->jack_flips++;
+
+ if (wm5100->jack_flips > 1)
+ wm5100_report_headphone(wm5100);
+ else
+ wm5100_set_detect_mode(wm5100, !wm5100->jack_mode);
+
+ return;
+ }
+
+ /* Don't distinguish between buttons, just report any low
+ * impedence as BTN_0.
+ */
+ if (val & 0x3fc) {
+ if (wm5100->jack_mic) {
+ dev_dbg(wm5100->dev, "Mic button detected\n");
+ snd_soc_jack_report(wm5100->jack, SND_JACK_BTN_0,
+ SND_JACK_BTN_0);
+ } else if (wm5100->jack_detecting) {
+ wm5100_report_headphone(wm5100);
+ }
+ }
+}
+
+int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+{
+ struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ if (jack) {
+ wm5100->jack = jack;
+ wm5100->jack_detecting = true;
+ wm5100->jack_flips = 0;
+
+ wm5100_set_detect_mode(wm5100, 0);
+
+ /* Slowest detection rate, gives debounce for initial
+ * detection */
+ snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
+ WM5100_ACCDET_BIAS_STARTTIME_MASK |
+ WM5100_ACCDET_RATE_MASK,
+ (7 << WM5100_ACCDET_BIAS_STARTTIME_SHIFT) |
+ WM5100_ACCDET_RATE_MASK);
+
+ /* We need the charge pump to power MICBIAS */
+ snd_soc_dapm_mutex_lock(dapm);
+
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "CP2");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "SYSCLK");
+
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ /* We start off just enabling microphone detection - even a
+ * plain headphone will trigger detection.
+ */
+ snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
+ WM5100_ACCDET_ENA, WM5100_ACCDET_ENA);
+
+ snd_soc_update_bits(codec, WM5100_INTERRUPT_STATUS_3_MASK,
+ WM5100_IM_ACCDET_EINT, 0);
+ } else {
+ snd_soc_update_bits(codec, WM5100_INTERRUPT_STATUS_3_MASK,
+ WM5100_IM_HPDET_EINT |
+ WM5100_IM_ACCDET_EINT,
+ WM5100_IM_HPDET_EINT |
+ WM5100_IM_ACCDET_EINT);
+ snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
+ WM5100_ACCDET_ENA, 0);
+ wm5100->jack = NULL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm5100_detect);
+
+static irqreturn_t wm5100_irq(int irq, void *data)
+{
+ struct wm5100_priv *wm5100 = data;
+ irqreturn_t status = IRQ_NONE;
+ unsigned int irq_val, mask_val;
+ int ret;
+
+ ret = regmap_read(wm5100->regmap, WM5100_INTERRUPT_STATUS_3, &irq_val);
+ if (ret < 0) {
+ dev_err(wm5100->dev, "Failed to read IRQ status 3: %d\n",
+ ret);
+ irq_val = 0;
+ }
+
+ ret = regmap_read(wm5100->regmap, WM5100_INTERRUPT_STATUS_3_MASK,
+ &mask_val);
+ if (ret < 0) {
+ dev_err(wm5100->dev, "Failed to read IRQ mask 3: %d\n",
+ ret);
+ mask_val = 0xffff;
+ }
+
+ irq_val &= ~mask_val;
+
+ regmap_write(wm5100->regmap, WM5100_INTERRUPT_STATUS_3, irq_val);
+
+ if (irq_val)
+ status = IRQ_HANDLED;
+
+ wm5100_log_status3(wm5100, irq_val);
+
+ if (irq_val & WM5100_FLL1_LOCK_EINT) {
+ dev_dbg(wm5100->dev, "FLL1 locked\n");
+ complete(&wm5100->fll[0].lock);
+ }
+ if (irq_val & WM5100_FLL2_LOCK_EINT) {
+ dev_dbg(wm5100->dev, "FLL2 locked\n");
+ complete(&wm5100->fll[1].lock);
+ }
+
+ if (irq_val & WM5100_ACCDET_EINT)
+ wm5100_micd_irq(wm5100);
+
+ ret = regmap_read(wm5100->regmap, WM5100_INTERRUPT_STATUS_4, &irq_val);
+ if (ret < 0) {
+ dev_err(wm5100->dev, "Failed to read IRQ status 4: %d\n",
+ ret);
+ irq_val = 0;
+ }
+
+ ret = regmap_read(wm5100->regmap, WM5100_INTERRUPT_STATUS_4_MASK,
+ &mask_val);
+ if (ret < 0) {
+ dev_err(wm5100->dev, "Failed to read IRQ mask 4: %d\n",
+ ret);
+ mask_val = 0xffff;
+ }
+
+ irq_val &= ~mask_val;
+
+ if (irq_val)
+ status = IRQ_HANDLED;
+
+ regmap_write(wm5100->regmap, WM5100_INTERRUPT_STATUS_4, irq_val);
+
+ wm5100_log_status4(wm5100, irq_val);
+
+ return status;
+}
+
+static irqreturn_t wm5100_edge_irq(int irq, void *data)
+{
+ irqreturn_t ret = IRQ_NONE;
+ irqreturn_t val;
+
+ do {
+ val = wm5100_irq(irq, data);
+ if (val != IRQ_NONE)
+ ret = val;
+ } while (val != IRQ_NONE);
+
+ return ret;
+}
+
+#ifdef CONFIG_GPIOLIB
+static inline struct wm5100_priv *gpio_to_wm5100(struct gpio_chip *chip)
+{
+ return container_of(chip, struct wm5100_priv, gpio_chip);
+}
+
+static void wm5100_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct wm5100_priv *wm5100 = gpio_to_wm5100(chip);
+
+ regmap_update_bits(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset,
+ WM5100_GP1_LVL, !!value << WM5100_GP1_LVL_SHIFT);
+}
+
+static int wm5100_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct wm5100_priv *wm5100 = gpio_to_wm5100(chip);
+ int val, ret;
+
+ val = (1 << WM5100_GP1_FN_SHIFT) | (!!value << WM5100_GP1_LVL_SHIFT);
+
+ ret = regmap_update_bits(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset,
+ WM5100_GP1_FN_MASK | WM5100_GP1_DIR |
+ WM5100_GP1_LVL, val);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+static int wm5100_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm5100_priv *wm5100 = gpio_to_wm5100(chip);
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset, &reg);
+ if (ret < 0)
+ return ret;
+
+ return (reg & WM5100_GP1_LVL) != 0;
+}
+
+static int wm5100_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm5100_priv *wm5100 = gpio_to_wm5100(chip);
+
+ return regmap_update_bits(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset,
+ WM5100_GP1_FN_MASK | WM5100_GP1_DIR,
+ (1 << WM5100_GP1_FN_SHIFT) |
+ (1 << WM5100_GP1_DIR_SHIFT));
+}
+
+static struct gpio_chip wm5100_template_chip = {
+ .label = "wm5100",
+ .owner = THIS_MODULE,
+ .direction_output = wm5100_gpio_direction_out,
+ .set = wm5100_gpio_set,
+ .direction_input = wm5100_gpio_direction_in,
+ .get = wm5100_gpio_get,
+ .can_sleep = 1,
+};
+
+static void wm5100_init_gpio(struct i2c_client *i2c)
+{
+ struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c);
+ int ret;
+
+ wm5100->gpio_chip = wm5100_template_chip;
+ wm5100->gpio_chip.ngpio = 6;
+ wm5100->gpio_chip.dev = &i2c->dev;
+
+ if (wm5100->pdata.gpio_base)
+ wm5100->gpio_chip.base = wm5100->pdata.gpio_base;
+ else
+ wm5100->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&wm5100->gpio_chip);
+ if (ret != 0)
+ dev_err(&i2c->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
+static void wm5100_free_gpio(struct i2c_client *i2c)
+{
+ struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c);
+
+ gpiochip_remove(&wm5100->gpio_chip);
+}
+#else
+static void wm5100_init_gpio(struct i2c_client *i2c)
+{
+}
+
+static void wm5100_free_gpio(struct i2c_client *i2c)
+{
+}
+#endif
+
+static int wm5100_probe(struct snd_soc_codec *codec)
+{
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+ int ret, i;
+
+ wm5100->codec = codec;
+
+ for (i = 0; i < ARRAY_SIZE(wm5100_dig_vu); i++)
+ snd_soc_update_bits(codec, wm5100_dig_vu[i], WM5100_OUT_VU,
+ WM5100_OUT_VU);
+
+ /* Don't debounce interrupts to support use of SYSCLK only */
+ snd_soc_write(codec, WM5100_IRQ_DEBOUNCE_1, 0);
+ snd_soc_write(codec, WM5100_IRQ_DEBOUNCE_2, 0);
+
+ /* TODO: check if we're symmetric */
+
+ if (i2c->irq)
+ snd_soc_dapm_new_controls(&codec->dapm,
+ wm5100_dapm_widgets_noirq,
+ ARRAY_SIZE(wm5100_dapm_widgets_noirq));
+
+ if (wm5100->pdata.hp_pol) {
+ ret = gpio_request_one(wm5100->pdata.hp_pol,
+ GPIOF_OUT_INIT_HIGH, "WM5100 HP_POL");
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request HP_POL %d: %d\n",
+ wm5100->pdata.hp_pol, ret);
+ goto err_gpio;
+ }
+ }
+
+ return 0;
+
+err_gpio:
+
+ return ret;
+}
+
+static int wm5100_remove(struct snd_soc_codec *codec)
+{
+ struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm5100->pdata.hp_pol) {
+ gpio_free(wm5100->pdata.hp_pol);
+ }
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm5100 = {
+ .probe = wm5100_probe,
+ .remove = wm5100_remove,
+
+ .set_sysclk = wm5100_set_sysclk,
+ .set_pll = wm5100_set_fll,
+ .idle_bias_off = 1,
+
+ .seq_notifier = wm5100_seq_notifier,
+ .controls = wm5100_snd_controls,
+ .num_controls = ARRAY_SIZE(wm5100_snd_controls),
+ .dapm_widgets = wm5100_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm5100_dapm_widgets),
+ .dapm_routes = wm5100_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm5100_dapm_routes),
+};
+
+static const struct regmap_config wm5100_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+
+ .max_register = WM5100_MAX_REGISTER,
+ .reg_defaults = wm5100_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm5100_reg_defaults),
+ .volatile_reg = wm5100_volatile_register,
+ .readable_reg = wm5100_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const unsigned int wm5100_mic_ctrl_reg[] = {
+ WM5100_IN1L_CONTROL,
+ WM5100_IN2L_CONTROL,
+ WM5100_IN3L_CONTROL,
+ WM5100_IN4L_CONTROL,
+};
+
+static int wm5100_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm5100_pdata *pdata = dev_get_platdata(&i2c->dev);
+ struct wm5100_priv *wm5100;
+ unsigned int reg;
+ int ret, i, irq_flags;
+
+ wm5100 = devm_kzalloc(&i2c->dev, sizeof(struct wm5100_priv),
+ GFP_KERNEL);
+ if (wm5100 == NULL)
+ return -ENOMEM;
+
+ wm5100->dev = &i2c->dev;
+
+ wm5100->regmap = devm_regmap_init_i2c(i2c, &wm5100_regmap);
+ if (IS_ERR(wm5100->regmap)) {
+ ret = PTR_ERR(wm5100->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm5100->fll); i++)
+ init_completion(&wm5100->fll[i].lock);
+
+ if (pdata)
+ wm5100->pdata = *pdata;
+
+ i2c_set_clientdata(i2c, wm5100);
+
+ for (i = 0; i < ARRAY_SIZE(wm5100->core_supplies); i++)
+ wm5100->core_supplies[i].supply = wm5100_core_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev,
+ ARRAY_SIZE(wm5100->core_supplies),
+ wm5100->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request core supplies: %d\n",
+ ret);
+ goto err;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies),
+ wm5100->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable core supplies: %d\n",
+ ret);
+ goto err;
+ }
+
+ if (wm5100->pdata.ldo_ena) {
+ ret = gpio_request_one(wm5100->pdata.ldo_ena,
+ GPIOF_OUT_INIT_HIGH, "WM5100 LDOENA");
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request LDOENA %d: %d\n",
+ wm5100->pdata.ldo_ena, ret);
+ goto err_enable;
+ }
+ msleep(2);
+ }
+
+ if (wm5100->pdata.reset) {
+ ret = gpio_request_one(wm5100->pdata.reset,
+ GPIOF_OUT_INIT_HIGH, "WM5100 /RESET");
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request /RESET %d: %d\n",
+ wm5100->pdata.reset, ret);
+ goto err_ldo;
+ }
+ }
+
+ ret = regmap_read(wm5100->regmap, WM5100_SOFTWARE_RESET, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret);
+ goto err_reset;
+ }
+ switch (reg) {
+ case 0x8997:
+ case 0x5100:
+ break;
+
+ default:
+ dev_err(&i2c->dev, "Device is not a WM5100, ID is %x\n", reg);
+ ret = -EINVAL;
+ goto err_reset;
+ }
+
+ ret = regmap_read(wm5100->regmap, WM5100_DEVICE_REVISION, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read revision register\n");
+ goto err_reset;
+ }
+ wm5100->rev = reg & WM5100_DEVICE_REVISION_MASK;
+
+ dev_info(&i2c->dev, "revision %c\n", wm5100->rev + 'A');
+
+ ret = wm5100_reset(wm5100);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to issue reset\n");
+ goto err_reset;
+ }
+
+ switch (wm5100->rev) {
+ case 0:
+ ret = regmap_register_patch(wm5100->regmap,
+ wm5100_reva_patches,
+ ARRAY_SIZE(wm5100_reva_patches));
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register patches: %d\n",
+ ret);
+ goto err_reset;
+ }
+ break;
+ default:
+ break;
+ }
+
+
+ wm5100_init_gpio(i2c);
+
+ for (i = 0; i < ARRAY_SIZE(wm5100->pdata.gpio_defaults); i++) {
+ if (!wm5100->pdata.gpio_defaults[i])
+ continue;
+
+ regmap_write(wm5100->regmap, WM5100_GPIO_CTRL_1 + i,
+ wm5100->pdata.gpio_defaults[i]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm5100->pdata.in_mode); i++) {
+ regmap_update_bits(wm5100->regmap, wm5100_mic_ctrl_reg[i],
+ WM5100_IN1_MODE_MASK |
+ WM5100_IN1_DMIC_SUP_MASK,
+ (wm5100->pdata.in_mode[i] <<
+ WM5100_IN1_MODE_SHIFT) |
+ (wm5100->pdata.dmic_sup[i] <<
+ WM5100_IN1_DMIC_SUP_SHIFT));
+ }
+
+ if (i2c->irq) {
+ if (wm5100->pdata.irq_flags)
+ irq_flags = wm5100->pdata.irq_flags;
+ else
+ irq_flags = IRQF_TRIGGER_LOW;
+
+ irq_flags |= IRQF_ONESHOT;
+
+ if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
+ ret = request_threaded_irq(i2c->irq, NULL,
+ wm5100_edge_irq, irq_flags,
+ "wm5100", wm5100);
+ else
+ ret = request_threaded_irq(i2c->irq, NULL, wm5100_irq,
+ irq_flags, "wm5100",
+ wm5100);
+
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n",
+ i2c->irq, ret);
+ } else {
+ /* Enable default interrupts */
+ regmap_update_bits(wm5100->regmap,
+ WM5100_INTERRUPT_STATUS_3_MASK,
+ WM5100_IM_SPK_SHUTDOWN_WARN_EINT |
+ WM5100_IM_SPK_SHUTDOWN_EINT |
+ WM5100_IM_ASRC2_LOCK_EINT |
+ WM5100_IM_ASRC1_LOCK_EINT |
+ WM5100_IM_FLL2_LOCK_EINT |
+ WM5100_IM_FLL1_LOCK_EINT |
+ WM5100_CLKGEN_ERR_EINT |
+ WM5100_CLKGEN_ERR_ASYNC_EINT, 0);
+
+ regmap_update_bits(wm5100->regmap,
+ WM5100_INTERRUPT_STATUS_4_MASK,
+ WM5100_AIF3_ERR_EINT |
+ WM5100_AIF2_ERR_EINT |
+ WM5100_AIF1_ERR_EINT |
+ WM5100_CTRLIF_ERR_EINT |
+ WM5100_ISRC2_UNDERCLOCKED_EINT |
+ WM5100_ISRC1_UNDERCLOCKED_EINT |
+ WM5100_FX_UNDERCLOCKED_EINT |
+ WM5100_AIF3_UNDERCLOCKED_EINT |
+ WM5100_AIF2_UNDERCLOCKED_EINT |
+ WM5100_AIF1_UNDERCLOCKED_EINT |
+ WM5100_ASRC_UNDERCLOCKED_EINT |
+ WM5100_DAC_UNDERCLOCKED_EINT |
+ WM5100_ADC_UNDERCLOCKED_EINT |
+ WM5100_MIXER_UNDERCLOCKED_EINT, 0);
+ }
+ }
+
+ pm_runtime_set_active(&i2c->dev);
+ pm_runtime_enable(&i2c->dev);
+ pm_request_idle(&i2c->dev);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm5100, wm5100_dai,
+ ARRAY_SIZE(wm5100_dai));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register WM5100: %d\n", ret);
+ goto err_reset;
+ }
+
+ return ret;
+
+err_reset:
+ if (i2c->irq)
+ free_irq(i2c->irq, wm5100);
+ wm5100_free_gpio(i2c);
+ if (wm5100->pdata.reset) {
+ gpio_set_value_cansleep(wm5100->pdata.reset, 0);
+ gpio_free(wm5100->pdata.reset);
+ }
+err_ldo:
+ if (wm5100->pdata.ldo_ena) {
+ gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
+ gpio_free(wm5100->pdata.ldo_ena);
+ }
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
+ wm5100->core_supplies);
+err:
+ return ret;
+}
+
+static int wm5100_i2c_remove(struct i2c_client *i2c)
+{
+ struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c);
+
+ snd_soc_unregister_codec(&i2c->dev);
+ if (i2c->irq)
+ free_irq(i2c->irq, wm5100);
+ wm5100_free_gpio(i2c);
+ if (wm5100->pdata.reset) {
+ gpio_set_value_cansleep(wm5100->pdata.reset, 0);
+ gpio_free(wm5100->pdata.reset);
+ }
+ if (wm5100->pdata.ldo_ena) {
+ gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
+ gpio_free(wm5100->pdata.ldo_ena);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm5100_runtime_suspend(struct device *dev)
+{
+ struct wm5100_priv *wm5100 = dev_get_drvdata(dev);
+
+ regcache_cache_only(wm5100->regmap, true);
+ regcache_mark_dirty(wm5100->regmap);
+ if (wm5100->pdata.ldo_ena)
+ gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
+ regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
+ wm5100->core_supplies);
+
+ return 0;
+}
+
+static int wm5100_runtime_resume(struct device *dev)
+{
+ struct wm5100_priv *wm5100 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies),
+ wm5100->core_supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (wm5100->pdata.ldo_ena) {
+ gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 1);
+ msleep(2);
+ }
+
+ regcache_cache_only(wm5100->regmap, false);
+ regcache_sync(wm5100->regmap);
+
+ return 0;
+}
+#endif
+
+static struct dev_pm_ops wm5100_pm = {
+ SET_RUNTIME_PM_OPS(wm5100_runtime_suspend, wm5100_runtime_resume,
+ NULL)
+};
+
+static const struct i2c_device_id wm5100_i2c_id[] = {
+ { "wm5100", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm5100_i2c_id);
+
+static struct i2c_driver wm5100_i2c_driver = {
+ .driver = {
+ .name = "wm5100",
+ .owner = THIS_MODULE,
+ .pm = &wm5100_pm,
+ },
+ .probe = wm5100_i2c_probe,
+ .remove = wm5100_i2c_remove,
+ .id_table = wm5100_i2c_id,
+};
+
+module_i2c_driver(wm5100_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM5100 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm5100.h b/kernel/sound/soc/codecs/wm5100.h
new file mode 100644
index 000000000..935a9b7fb
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm5100.h
@@ -0,0 +1,5315 @@
+/*
+ * wm5100.h -- WM5100 ALSA SoC Audio driver
+ *
+ * Copyright 2011 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 WM5100_ASOC_H
+#define WM5100_ASOC_H
+
+#include <sound/soc.h>
+#include <linux/regmap.h>
+
+int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+
+#define WM5100_CLK_AIF1 1
+#define WM5100_CLK_AIF2 2
+#define WM5100_CLK_AIF3 3
+#define WM5100_CLK_SYSCLK 4
+#define WM5100_CLK_ASYNCCLK 5
+#define WM5100_CLK_32KHZ 6
+#define WM5100_CLK_OPCLK 7
+
+#define WM5100_CLKSRC_MCLK1 0
+#define WM5100_CLKSRC_MCLK2 1
+#define WM5100_CLKSRC_SYSCLK 2
+#define WM5100_CLKSRC_FLL1 4
+#define WM5100_CLKSRC_FLL2 5
+#define WM5100_CLKSRC_AIF1BCLK 8
+#define WM5100_CLKSRC_AIF2BCLK 9
+#define WM5100_CLKSRC_AIF3BCLK 10
+#define WM5100_CLKSRC_ASYNCCLK 0x100
+
+#define WM5100_FLL1 1
+#define WM5100_FLL2 2
+
+#define WM5100_FLL_SRC_MCLK1 0x0
+#define WM5100_FLL_SRC_MCLK2 0x1
+#define WM5100_FLL_SRC_FLL1 0x4
+#define WM5100_FLL_SRC_FLL2 0x5
+#define WM5100_FLL_SRC_AIF1BCLK 0x8
+#define WM5100_FLL_SRC_AIF2BCLK 0x9
+#define WM5100_FLL_SRC_AIF3BCLK 0xa
+
+/*
+ * Register values.
+ */
+#define WM5100_SOFTWARE_RESET 0x00
+#define WM5100_DEVICE_REVISION 0x01
+#define WM5100_CTRL_IF_1 0x10
+#define WM5100_TONE_GENERATOR_1 0x20
+#define WM5100_PWM_DRIVE_1 0x30
+#define WM5100_PWM_DRIVE_2 0x31
+#define WM5100_PWM_DRIVE_3 0x32
+#define WM5100_CLOCKING_1 0x100
+#define WM5100_CLOCKING_3 0x101
+#define WM5100_CLOCKING_4 0x102
+#define WM5100_CLOCKING_5 0x103
+#define WM5100_CLOCKING_6 0x104
+#define WM5100_CLOCKING_7 0x107
+#define WM5100_CLOCKING_8 0x108
+#define WM5100_ASRC_ENABLE 0x120
+#define WM5100_ASRC_STATUS 0x121
+#define WM5100_ASRC_RATE1 0x122
+#define WM5100_ISRC_1_CTRL_1 0x141
+#define WM5100_ISRC_1_CTRL_2 0x142
+#define WM5100_ISRC_2_CTRL1 0x143
+#define WM5100_ISRC_2_CTRL_2 0x144
+#define WM5100_FLL1_CONTROL_1 0x182
+#define WM5100_FLL1_CONTROL_2 0x183
+#define WM5100_FLL1_CONTROL_3 0x184
+#define WM5100_FLL1_CONTROL_5 0x186
+#define WM5100_FLL1_CONTROL_6 0x187
+#define WM5100_FLL1_EFS_1 0x188
+#define WM5100_FLL2_CONTROL_1 0x1A2
+#define WM5100_FLL2_CONTROL_2 0x1A3
+#define WM5100_FLL2_CONTROL_3 0x1A4
+#define WM5100_FLL2_CONTROL_5 0x1A6
+#define WM5100_FLL2_CONTROL_6 0x1A7
+#define WM5100_FLL2_EFS_1 0x1A8
+#define WM5100_MIC_CHARGE_PUMP_1 0x200
+#define WM5100_MIC_CHARGE_PUMP_2 0x201
+#define WM5100_HP_CHARGE_PUMP_1 0x202
+#define WM5100_LDO1_CONTROL 0x211
+#define WM5100_MIC_BIAS_CTRL_1 0x215
+#define WM5100_MIC_BIAS_CTRL_2 0x216
+#define WM5100_MIC_BIAS_CTRL_3 0x217
+#define WM5100_ACCESSORY_DETECT_MODE_1 0x280
+#define WM5100_HEADPHONE_DETECT_1 0x288
+#define WM5100_HEADPHONE_DETECT_2 0x289
+#define WM5100_MIC_DETECT_1 0x290
+#define WM5100_MIC_DETECT_2 0x291
+#define WM5100_MIC_DETECT_3 0x292
+#define WM5100_MISC_CONTROL 0x2BB
+#define WM5100_INPUT_ENABLES 0x301
+#define WM5100_INPUT_ENABLES_STATUS 0x302
+#define WM5100_IN1L_CONTROL 0x310
+#define WM5100_IN1R_CONTROL 0x311
+#define WM5100_IN2L_CONTROL 0x312
+#define WM5100_IN2R_CONTROL 0x313
+#define WM5100_IN3L_CONTROL 0x314
+#define WM5100_IN3R_CONTROL 0x315
+#define WM5100_IN4L_CONTROL 0x316
+#define WM5100_IN4R_CONTROL 0x317
+#define WM5100_RXANC_SRC 0x318
+#define WM5100_INPUT_VOLUME_RAMP 0x319
+#define WM5100_ADC_DIGITAL_VOLUME_1L 0x320
+#define WM5100_ADC_DIGITAL_VOLUME_1R 0x321
+#define WM5100_ADC_DIGITAL_VOLUME_2L 0x322
+#define WM5100_ADC_DIGITAL_VOLUME_2R 0x323
+#define WM5100_ADC_DIGITAL_VOLUME_3L 0x324
+#define WM5100_ADC_DIGITAL_VOLUME_3R 0x325
+#define WM5100_ADC_DIGITAL_VOLUME_4L 0x326
+#define WM5100_ADC_DIGITAL_VOLUME_4R 0x327
+#define WM5100_OUTPUT_ENABLES_2 0x401
+#define WM5100_OUTPUT_STATUS_1 0x402
+#define WM5100_OUTPUT_STATUS_2 0x403
+#define WM5100_CHANNEL_ENABLES_1 0x408
+#define WM5100_OUT_VOLUME_1L 0x410
+#define WM5100_OUT_VOLUME_1R 0x411
+#define WM5100_DAC_VOLUME_LIMIT_1L 0x412
+#define WM5100_DAC_VOLUME_LIMIT_1R 0x413
+#define WM5100_OUT_VOLUME_2L 0x414
+#define WM5100_OUT_VOLUME_2R 0x415
+#define WM5100_DAC_VOLUME_LIMIT_2L 0x416
+#define WM5100_DAC_VOLUME_LIMIT_2R 0x417
+#define WM5100_OUT_VOLUME_3L 0x418
+#define WM5100_OUT_VOLUME_3R 0x419
+#define WM5100_DAC_VOLUME_LIMIT_3L 0x41A
+#define WM5100_DAC_VOLUME_LIMIT_3R 0x41B
+#define WM5100_OUT_VOLUME_4L 0x41C
+#define WM5100_OUT_VOLUME_4R 0x41D
+#define WM5100_DAC_VOLUME_LIMIT_5L 0x41E
+#define WM5100_DAC_VOLUME_LIMIT_5R 0x41F
+#define WM5100_DAC_VOLUME_LIMIT_6L 0x420
+#define WM5100_DAC_VOLUME_LIMIT_6R 0x421
+#define WM5100_DAC_AEC_CONTROL_1 0x440
+#define WM5100_OUTPUT_VOLUME_RAMP 0x441
+#define WM5100_DAC_DIGITAL_VOLUME_1L 0x480
+#define WM5100_DAC_DIGITAL_VOLUME_1R 0x481
+#define WM5100_DAC_DIGITAL_VOLUME_2L 0x482
+#define WM5100_DAC_DIGITAL_VOLUME_2R 0x483
+#define WM5100_DAC_DIGITAL_VOLUME_3L 0x484
+#define WM5100_DAC_DIGITAL_VOLUME_3R 0x485
+#define WM5100_DAC_DIGITAL_VOLUME_4L 0x486
+#define WM5100_DAC_DIGITAL_VOLUME_4R 0x487
+#define WM5100_DAC_DIGITAL_VOLUME_5L 0x488
+#define WM5100_DAC_DIGITAL_VOLUME_5R 0x489
+#define WM5100_DAC_DIGITAL_VOLUME_6L 0x48A
+#define WM5100_DAC_DIGITAL_VOLUME_6R 0x48B
+#define WM5100_PDM_SPK1_CTRL_1 0x4C0
+#define WM5100_PDM_SPK1_CTRL_2 0x4C1
+#define WM5100_PDM_SPK2_CTRL_1 0x4C2
+#define WM5100_PDM_SPK2_CTRL_2 0x4C3
+#define WM5100_AUDIO_IF_1_1 0x500
+#define WM5100_AUDIO_IF_1_2 0x501
+#define WM5100_AUDIO_IF_1_3 0x502
+#define WM5100_AUDIO_IF_1_4 0x503
+#define WM5100_AUDIO_IF_1_5 0x504
+#define WM5100_AUDIO_IF_1_6 0x505
+#define WM5100_AUDIO_IF_1_7 0x506
+#define WM5100_AUDIO_IF_1_8 0x507
+#define WM5100_AUDIO_IF_1_9 0x508
+#define WM5100_AUDIO_IF_1_10 0x509
+#define WM5100_AUDIO_IF_1_11 0x50A
+#define WM5100_AUDIO_IF_1_12 0x50B
+#define WM5100_AUDIO_IF_1_13 0x50C
+#define WM5100_AUDIO_IF_1_14 0x50D
+#define WM5100_AUDIO_IF_1_15 0x50E
+#define WM5100_AUDIO_IF_1_16 0x50F
+#define WM5100_AUDIO_IF_1_17 0x510
+#define WM5100_AUDIO_IF_1_18 0x511
+#define WM5100_AUDIO_IF_1_19 0x512
+#define WM5100_AUDIO_IF_1_20 0x513
+#define WM5100_AUDIO_IF_1_21 0x514
+#define WM5100_AUDIO_IF_1_22 0x515
+#define WM5100_AUDIO_IF_1_23 0x516
+#define WM5100_AUDIO_IF_1_24 0x517
+#define WM5100_AUDIO_IF_1_25 0x518
+#define WM5100_AUDIO_IF_1_26 0x519
+#define WM5100_AUDIO_IF_1_27 0x51A
+#define WM5100_AUDIO_IF_2_1 0x540
+#define WM5100_AUDIO_IF_2_2 0x541
+#define WM5100_AUDIO_IF_2_3 0x542
+#define WM5100_AUDIO_IF_2_4 0x543
+#define WM5100_AUDIO_IF_2_5 0x544
+#define WM5100_AUDIO_IF_2_6 0x545
+#define WM5100_AUDIO_IF_2_7 0x546
+#define WM5100_AUDIO_IF_2_8 0x547
+#define WM5100_AUDIO_IF_2_9 0x548
+#define WM5100_AUDIO_IF_2_10 0x549
+#define WM5100_AUDIO_IF_2_11 0x54A
+#define WM5100_AUDIO_IF_2_18 0x551
+#define WM5100_AUDIO_IF_2_19 0x552
+#define WM5100_AUDIO_IF_2_26 0x559
+#define WM5100_AUDIO_IF_2_27 0x55A
+#define WM5100_AUDIO_IF_3_1 0x580
+#define WM5100_AUDIO_IF_3_2 0x581
+#define WM5100_AUDIO_IF_3_3 0x582
+#define WM5100_AUDIO_IF_3_4 0x583
+#define WM5100_AUDIO_IF_3_5 0x584
+#define WM5100_AUDIO_IF_3_6 0x585
+#define WM5100_AUDIO_IF_3_7 0x586
+#define WM5100_AUDIO_IF_3_8 0x587
+#define WM5100_AUDIO_IF_3_9 0x588
+#define WM5100_AUDIO_IF_3_10 0x589
+#define WM5100_AUDIO_IF_3_11 0x58A
+#define WM5100_AUDIO_IF_3_18 0x591
+#define WM5100_AUDIO_IF_3_19 0x592
+#define WM5100_AUDIO_IF_3_26 0x599
+#define WM5100_AUDIO_IF_3_27 0x59A
+#define WM5100_PWM1MIX_INPUT_1_SOURCE 0x640
+#define WM5100_PWM1MIX_INPUT_1_VOLUME 0x641
+#define WM5100_PWM1MIX_INPUT_2_SOURCE 0x642
+#define WM5100_PWM1MIX_INPUT_2_VOLUME 0x643
+#define WM5100_PWM1MIX_INPUT_3_SOURCE 0x644
+#define WM5100_PWM1MIX_INPUT_3_VOLUME 0x645
+#define WM5100_PWM1MIX_INPUT_4_SOURCE 0x646
+#define WM5100_PWM1MIX_INPUT_4_VOLUME 0x647
+#define WM5100_PWM2MIX_INPUT_1_SOURCE 0x648
+#define WM5100_PWM2MIX_INPUT_1_VOLUME 0x649
+#define WM5100_PWM2MIX_INPUT_2_SOURCE 0x64A
+#define WM5100_PWM2MIX_INPUT_2_VOLUME 0x64B
+#define WM5100_PWM2MIX_INPUT_3_SOURCE 0x64C
+#define WM5100_PWM2MIX_INPUT_3_VOLUME 0x64D
+#define WM5100_PWM2MIX_INPUT_4_SOURCE 0x64E
+#define WM5100_PWM2MIX_INPUT_4_VOLUME 0x64F
+#define WM5100_OUT1LMIX_INPUT_1_SOURCE 0x680
+#define WM5100_OUT1LMIX_INPUT_1_VOLUME 0x681
+#define WM5100_OUT1LMIX_INPUT_2_SOURCE 0x682
+#define WM5100_OUT1LMIX_INPUT_2_VOLUME 0x683
+#define WM5100_OUT1LMIX_INPUT_3_SOURCE 0x684
+#define WM5100_OUT1LMIX_INPUT_3_VOLUME 0x685
+#define WM5100_OUT1LMIX_INPUT_4_SOURCE 0x686
+#define WM5100_OUT1LMIX_INPUT_4_VOLUME 0x687
+#define WM5100_OUT1RMIX_INPUT_1_SOURCE 0x688
+#define WM5100_OUT1RMIX_INPUT_1_VOLUME 0x689
+#define WM5100_OUT1RMIX_INPUT_2_SOURCE 0x68A
+#define WM5100_OUT1RMIX_INPUT_2_VOLUME 0x68B
+#define WM5100_OUT1RMIX_INPUT_3_SOURCE 0x68C
+#define WM5100_OUT1RMIX_INPUT_3_VOLUME 0x68D
+#define WM5100_OUT1RMIX_INPUT_4_SOURCE 0x68E
+#define WM5100_OUT1RMIX_INPUT_4_VOLUME 0x68F
+#define WM5100_OUT2LMIX_INPUT_1_SOURCE 0x690
+#define WM5100_OUT2LMIX_INPUT_1_VOLUME 0x691
+#define WM5100_OUT2LMIX_INPUT_2_SOURCE 0x692
+#define WM5100_OUT2LMIX_INPUT_2_VOLUME 0x693
+#define WM5100_OUT2LMIX_INPUT_3_SOURCE 0x694
+#define WM5100_OUT2LMIX_INPUT_3_VOLUME 0x695
+#define WM5100_OUT2LMIX_INPUT_4_SOURCE 0x696
+#define WM5100_OUT2LMIX_INPUT_4_VOLUME 0x697
+#define WM5100_OUT2RMIX_INPUT_1_SOURCE 0x698
+#define WM5100_OUT2RMIX_INPUT_1_VOLUME 0x699
+#define WM5100_OUT2RMIX_INPUT_2_SOURCE 0x69A
+#define WM5100_OUT2RMIX_INPUT_2_VOLUME 0x69B
+#define WM5100_OUT2RMIX_INPUT_3_SOURCE 0x69C
+#define WM5100_OUT2RMIX_INPUT_3_VOLUME 0x69D
+#define WM5100_OUT2RMIX_INPUT_4_SOURCE 0x69E
+#define WM5100_OUT2RMIX_INPUT_4_VOLUME 0x69F
+#define WM5100_OUT3LMIX_INPUT_1_SOURCE 0x6A0
+#define WM5100_OUT3LMIX_INPUT_1_VOLUME 0x6A1
+#define WM5100_OUT3LMIX_INPUT_2_SOURCE 0x6A2
+#define WM5100_OUT3LMIX_INPUT_2_VOLUME 0x6A3
+#define WM5100_OUT3LMIX_INPUT_3_SOURCE 0x6A4
+#define WM5100_OUT3LMIX_INPUT_3_VOLUME 0x6A5
+#define WM5100_OUT3LMIX_INPUT_4_SOURCE 0x6A6
+#define WM5100_OUT3LMIX_INPUT_4_VOLUME 0x6A7
+#define WM5100_OUT3RMIX_INPUT_1_SOURCE 0x6A8
+#define WM5100_OUT3RMIX_INPUT_1_VOLUME 0x6A9
+#define WM5100_OUT3RMIX_INPUT_2_SOURCE 0x6AA
+#define WM5100_OUT3RMIX_INPUT_2_VOLUME 0x6AB
+#define WM5100_OUT3RMIX_INPUT_3_SOURCE 0x6AC
+#define WM5100_OUT3RMIX_INPUT_3_VOLUME 0x6AD
+#define WM5100_OUT3RMIX_INPUT_4_SOURCE 0x6AE
+#define WM5100_OUT3RMIX_INPUT_4_VOLUME 0x6AF
+#define WM5100_OUT4LMIX_INPUT_1_SOURCE 0x6B0
+#define WM5100_OUT4LMIX_INPUT_1_VOLUME 0x6B1
+#define WM5100_OUT4LMIX_INPUT_2_SOURCE 0x6B2
+#define WM5100_OUT4LMIX_INPUT_2_VOLUME 0x6B3
+#define WM5100_OUT4LMIX_INPUT_3_SOURCE 0x6B4
+#define WM5100_OUT4LMIX_INPUT_3_VOLUME 0x6B5
+#define WM5100_OUT4LMIX_INPUT_4_SOURCE 0x6B6
+#define WM5100_OUT4LMIX_INPUT_4_VOLUME 0x6B7
+#define WM5100_OUT4RMIX_INPUT_1_SOURCE 0x6B8
+#define WM5100_OUT4RMIX_INPUT_1_VOLUME 0x6B9
+#define WM5100_OUT4RMIX_INPUT_2_SOURCE 0x6BA
+#define WM5100_OUT4RMIX_INPUT_2_VOLUME 0x6BB
+#define WM5100_OUT4RMIX_INPUT_3_SOURCE 0x6BC
+#define WM5100_OUT4RMIX_INPUT_3_VOLUME 0x6BD
+#define WM5100_OUT4RMIX_INPUT_4_SOURCE 0x6BE
+#define WM5100_OUT4RMIX_INPUT_4_VOLUME 0x6BF
+#define WM5100_OUT5LMIX_INPUT_1_SOURCE 0x6C0
+#define WM5100_OUT5LMIX_INPUT_1_VOLUME 0x6C1
+#define WM5100_OUT5LMIX_INPUT_2_SOURCE 0x6C2
+#define WM5100_OUT5LMIX_INPUT_2_VOLUME 0x6C3
+#define WM5100_OUT5LMIX_INPUT_3_SOURCE 0x6C4
+#define WM5100_OUT5LMIX_INPUT_3_VOLUME 0x6C5
+#define WM5100_OUT5LMIX_INPUT_4_SOURCE 0x6C6
+#define WM5100_OUT5LMIX_INPUT_4_VOLUME 0x6C7
+#define WM5100_OUT5RMIX_INPUT_1_SOURCE 0x6C8
+#define WM5100_OUT5RMIX_INPUT_1_VOLUME 0x6C9
+#define WM5100_OUT5RMIX_INPUT_2_SOURCE 0x6CA
+#define WM5100_OUT5RMIX_INPUT_2_VOLUME 0x6CB
+#define WM5100_OUT5RMIX_INPUT_3_SOURCE 0x6CC
+#define WM5100_OUT5RMIX_INPUT_3_VOLUME 0x6CD
+#define WM5100_OUT5RMIX_INPUT_4_SOURCE 0x6CE
+#define WM5100_OUT5RMIX_INPUT_4_VOLUME 0x6CF
+#define WM5100_OUT6LMIX_INPUT_1_SOURCE 0x6D0
+#define WM5100_OUT6LMIX_INPUT_1_VOLUME 0x6D1
+#define WM5100_OUT6LMIX_INPUT_2_SOURCE 0x6D2
+#define WM5100_OUT6LMIX_INPUT_2_VOLUME 0x6D3
+#define WM5100_OUT6LMIX_INPUT_3_SOURCE 0x6D4
+#define WM5100_OUT6LMIX_INPUT_3_VOLUME 0x6D5
+#define WM5100_OUT6LMIX_INPUT_4_SOURCE 0x6D6
+#define WM5100_OUT6LMIX_INPUT_4_VOLUME 0x6D7
+#define WM5100_OUT6RMIX_INPUT_1_SOURCE 0x6D8
+#define WM5100_OUT6RMIX_INPUT_1_VOLUME 0x6D9
+#define WM5100_OUT6RMIX_INPUT_2_SOURCE 0x6DA
+#define WM5100_OUT6RMIX_INPUT_2_VOLUME 0x6DB
+#define WM5100_OUT6RMIX_INPUT_3_SOURCE 0x6DC
+#define WM5100_OUT6RMIX_INPUT_3_VOLUME 0x6DD
+#define WM5100_OUT6RMIX_INPUT_4_SOURCE 0x6DE
+#define WM5100_OUT6RMIX_INPUT_4_VOLUME 0x6DF
+#define WM5100_AIF1TX1MIX_INPUT_1_SOURCE 0x700
+#define WM5100_AIF1TX1MIX_INPUT_1_VOLUME 0x701
+#define WM5100_AIF1TX1MIX_INPUT_2_SOURCE 0x702
+#define WM5100_AIF1TX1MIX_INPUT_2_VOLUME 0x703
+#define WM5100_AIF1TX1MIX_INPUT_3_SOURCE 0x704
+#define WM5100_AIF1TX1MIX_INPUT_3_VOLUME 0x705
+#define WM5100_AIF1TX1MIX_INPUT_4_SOURCE 0x706
+#define WM5100_AIF1TX1MIX_INPUT_4_VOLUME 0x707
+#define WM5100_AIF1TX2MIX_INPUT_1_SOURCE 0x708
+#define WM5100_AIF1TX2MIX_INPUT_1_VOLUME 0x709
+#define WM5100_AIF1TX2MIX_INPUT_2_SOURCE 0x70A
+#define WM5100_AIF1TX2MIX_INPUT_2_VOLUME 0x70B
+#define WM5100_AIF1TX2MIX_INPUT_3_SOURCE 0x70C
+#define WM5100_AIF1TX2MIX_INPUT_3_VOLUME 0x70D
+#define WM5100_AIF1TX2MIX_INPUT_4_SOURCE 0x70E
+#define WM5100_AIF1TX2MIX_INPUT_4_VOLUME 0x70F
+#define WM5100_AIF1TX3MIX_INPUT_1_SOURCE 0x710
+#define WM5100_AIF1TX3MIX_INPUT_1_VOLUME 0x711
+#define WM5100_AIF1TX3MIX_INPUT_2_SOURCE 0x712
+#define WM5100_AIF1TX3MIX_INPUT_2_VOLUME 0x713
+#define WM5100_AIF1TX3MIX_INPUT_3_SOURCE 0x714
+#define WM5100_AIF1TX3MIX_INPUT_3_VOLUME 0x715
+#define WM5100_AIF1TX3MIX_INPUT_4_SOURCE 0x716
+#define WM5100_AIF1TX3MIX_INPUT_4_VOLUME 0x717
+#define WM5100_AIF1TX4MIX_INPUT_1_SOURCE 0x718
+#define WM5100_AIF1TX4MIX_INPUT_1_VOLUME 0x719
+#define WM5100_AIF1TX4MIX_INPUT_2_SOURCE 0x71A
+#define WM5100_AIF1TX4MIX_INPUT_2_VOLUME 0x71B
+#define WM5100_AIF1TX4MIX_INPUT_3_SOURCE 0x71C
+#define WM5100_AIF1TX4MIX_INPUT_3_VOLUME 0x71D
+#define WM5100_AIF1TX4MIX_INPUT_4_SOURCE 0x71E
+#define WM5100_AIF1TX4MIX_INPUT_4_VOLUME 0x71F
+#define WM5100_AIF1TX5MIX_INPUT_1_SOURCE 0x720
+#define WM5100_AIF1TX5MIX_INPUT_1_VOLUME 0x721
+#define WM5100_AIF1TX5MIX_INPUT_2_SOURCE 0x722
+#define WM5100_AIF1TX5MIX_INPUT_2_VOLUME 0x723
+#define WM5100_AIF1TX5MIX_INPUT_3_SOURCE 0x724
+#define WM5100_AIF1TX5MIX_INPUT_3_VOLUME 0x725
+#define WM5100_AIF1TX5MIX_INPUT_4_SOURCE 0x726
+#define WM5100_AIF1TX5MIX_INPUT_4_VOLUME 0x727
+#define WM5100_AIF1TX6MIX_INPUT_1_SOURCE 0x728
+#define WM5100_AIF1TX6MIX_INPUT_1_VOLUME 0x729
+#define WM5100_AIF1TX6MIX_INPUT_2_SOURCE 0x72A
+#define WM5100_AIF1TX6MIX_INPUT_2_VOLUME 0x72B
+#define WM5100_AIF1TX6MIX_INPUT_3_SOURCE 0x72C
+#define WM5100_AIF1TX6MIX_INPUT_3_VOLUME 0x72D
+#define WM5100_AIF1TX6MIX_INPUT_4_SOURCE 0x72E
+#define WM5100_AIF1TX6MIX_INPUT_4_VOLUME 0x72F
+#define WM5100_AIF1TX7MIX_INPUT_1_SOURCE 0x730
+#define WM5100_AIF1TX7MIX_INPUT_1_VOLUME 0x731
+#define WM5100_AIF1TX7MIX_INPUT_2_SOURCE 0x732
+#define WM5100_AIF1TX7MIX_INPUT_2_VOLUME 0x733
+#define WM5100_AIF1TX7MIX_INPUT_3_SOURCE 0x734
+#define WM5100_AIF1TX7MIX_INPUT_3_VOLUME 0x735
+#define WM5100_AIF1TX7MIX_INPUT_4_SOURCE 0x736
+#define WM5100_AIF1TX7MIX_INPUT_4_VOLUME 0x737
+#define WM5100_AIF1TX8MIX_INPUT_1_SOURCE 0x738
+#define WM5100_AIF1TX8MIX_INPUT_1_VOLUME 0x739
+#define WM5100_AIF1TX8MIX_INPUT_2_SOURCE 0x73A
+#define WM5100_AIF1TX8MIX_INPUT_2_VOLUME 0x73B
+#define WM5100_AIF1TX8MIX_INPUT_3_SOURCE 0x73C
+#define WM5100_AIF1TX8MIX_INPUT_3_VOLUME 0x73D
+#define WM5100_AIF1TX8MIX_INPUT_4_SOURCE 0x73E
+#define WM5100_AIF1TX8MIX_INPUT_4_VOLUME 0x73F
+#define WM5100_AIF2TX1MIX_INPUT_1_SOURCE 0x740
+#define WM5100_AIF2TX1MIX_INPUT_1_VOLUME 0x741
+#define WM5100_AIF2TX1MIX_INPUT_2_SOURCE 0x742
+#define WM5100_AIF2TX1MIX_INPUT_2_VOLUME 0x743
+#define WM5100_AIF2TX1MIX_INPUT_3_SOURCE 0x744
+#define WM5100_AIF2TX1MIX_INPUT_3_VOLUME 0x745
+#define WM5100_AIF2TX1MIX_INPUT_4_SOURCE 0x746
+#define WM5100_AIF2TX1MIX_INPUT_4_VOLUME 0x747
+#define WM5100_AIF2TX2MIX_INPUT_1_SOURCE 0x748
+#define WM5100_AIF2TX2MIX_INPUT_1_VOLUME 0x749
+#define WM5100_AIF2TX2MIX_INPUT_2_SOURCE 0x74A
+#define WM5100_AIF2TX2MIX_INPUT_2_VOLUME 0x74B
+#define WM5100_AIF2TX2MIX_INPUT_3_SOURCE 0x74C
+#define WM5100_AIF2TX2MIX_INPUT_3_VOLUME 0x74D
+#define WM5100_AIF2TX2MIX_INPUT_4_SOURCE 0x74E
+#define WM5100_AIF2TX2MIX_INPUT_4_VOLUME 0x74F
+#define WM5100_AIF3TX1MIX_INPUT_1_SOURCE 0x780
+#define WM5100_AIF3TX1MIX_INPUT_1_VOLUME 0x781
+#define WM5100_AIF3TX1MIX_INPUT_2_SOURCE 0x782
+#define WM5100_AIF3TX1MIX_INPUT_2_VOLUME 0x783
+#define WM5100_AIF3TX1MIX_INPUT_3_SOURCE 0x784
+#define WM5100_AIF3TX1MIX_INPUT_3_VOLUME 0x785
+#define WM5100_AIF3TX1MIX_INPUT_4_SOURCE 0x786
+#define WM5100_AIF3TX1MIX_INPUT_4_VOLUME 0x787
+#define WM5100_AIF3TX2MIX_INPUT_1_SOURCE 0x788
+#define WM5100_AIF3TX2MIX_INPUT_1_VOLUME 0x789
+#define WM5100_AIF3TX2MIX_INPUT_2_SOURCE 0x78A
+#define WM5100_AIF3TX2MIX_INPUT_2_VOLUME 0x78B
+#define WM5100_AIF3TX2MIX_INPUT_3_SOURCE 0x78C
+#define WM5100_AIF3TX2MIX_INPUT_3_VOLUME 0x78D
+#define WM5100_AIF3TX2MIX_INPUT_4_SOURCE 0x78E
+#define WM5100_AIF3TX2MIX_INPUT_4_VOLUME 0x78F
+#define WM5100_EQ1MIX_INPUT_1_SOURCE 0x880
+#define WM5100_EQ1MIX_INPUT_1_VOLUME 0x881
+#define WM5100_EQ1MIX_INPUT_2_SOURCE 0x882
+#define WM5100_EQ1MIX_INPUT_2_VOLUME 0x883
+#define WM5100_EQ1MIX_INPUT_3_SOURCE 0x884
+#define WM5100_EQ1MIX_INPUT_3_VOLUME 0x885
+#define WM5100_EQ1MIX_INPUT_4_SOURCE 0x886
+#define WM5100_EQ1MIX_INPUT_4_VOLUME 0x887
+#define WM5100_EQ2MIX_INPUT_1_SOURCE 0x888
+#define WM5100_EQ2MIX_INPUT_1_VOLUME 0x889
+#define WM5100_EQ2MIX_INPUT_2_SOURCE 0x88A
+#define WM5100_EQ2MIX_INPUT_2_VOLUME 0x88B
+#define WM5100_EQ2MIX_INPUT_3_SOURCE 0x88C
+#define WM5100_EQ2MIX_INPUT_3_VOLUME 0x88D
+#define WM5100_EQ2MIX_INPUT_4_SOURCE 0x88E
+#define WM5100_EQ2MIX_INPUT_4_VOLUME 0x88F
+#define WM5100_EQ3MIX_INPUT_1_SOURCE 0x890
+#define WM5100_EQ3MIX_INPUT_1_VOLUME 0x891
+#define WM5100_EQ3MIX_INPUT_2_SOURCE 0x892
+#define WM5100_EQ3MIX_INPUT_2_VOLUME 0x893
+#define WM5100_EQ3MIX_INPUT_3_SOURCE 0x894
+#define WM5100_EQ3MIX_INPUT_3_VOLUME 0x895
+#define WM5100_EQ3MIX_INPUT_4_SOURCE 0x896
+#define WM5100_EQ3MIX_INPUT_4_VOLUME 0x897
+#define WM5100_EQ4MIX_INPUT_1_SOURCE 0x898
+#define WM5100_EQ4MIX_INPUT_1_VOLUME 0x899
+#define WM5100_EQ4MIX_INPUT_2_SOURCE 0x89A
+#define WM5100_EQ4MIX_INPUT_2_VOLUME 0x89B
+#define WM5100_EQ4MIX_INPUT_3_SOURCE 0x89C
+#define WM5100_EQ4MIX_INPUT_3_VOLUME 0x89D
+#define WM5100_EQ4MIX_INPUT_4_SOURCE 0x89E
+#define WM5100_EQ4MIX_INPUT_4_VOLUME 0x89F
+#define WM5100_DRC1LMIX_INPUT_1_SOURCE 0x8C0
+#define WM5100_DRC1LMIX_INPUT_1_VOLUME 0x8C1
+#define WM5100_DRC1LMIX_INPUT_2_SOURCE 0x8C2
+#define WM5100_DRC1LMIX_INPUT_2_VOLUME 0x8C3
+#define WM5100_DRC1LMIX_INPUT_3_SOURCE 0x8C4
+#define WM5100_DRC1LMIX_INPUT_3_VOLUME 0x8C5
+#define WM5100_DRC1LMIX_INPUT_4_SOURCE 0x8C6
+#define WM5100_DRC1LMIX_INPUT_4_VOLUME 0x8C7
+#define WM5100_DRC1RMIX_INPUT_1_SOURCE 0x8C8
+#define WM5100_DRC1RMIX_INPUT_1_VOLUME 0x8C9
+#define WM5100_DRC1RMIX_INPUT_2_SOURCE 0x8CA
+#define WM5100_DRC1RMIX_INPUT_2_VOLUME 0x8CB
+#define WM5100_DRC1RMIX_INPUT_3_SOURCE 0x8CC
+#define WM5100_DRC1RMIX_INPUT_3_VOLUME 0x8CD
+#define WM5100_DRC1RMIX_INPUT_4_SOURCE 0x8CE
+#define WM5100_DRC1RMIX_INPUT_4_VOLUME 0x8CF
+#define WM5100_HPLP1MIX_INPUT_1_SOURCE 0x900
+#define WM5100_HPLP1MIX_INPUT_1_VOLUME 0x901
+#define WM5100_HPLP1MIX_INPUT_2_SOURCE 0x902
+#define WM5100_HPLP1MIX_INPUT_2_VOLUME 0x903
+#define WM5100_HPLP1MIX_INPUT_3_SOURCE 0x904
+#define WM5100_HPLP1MIX_INPUT_3_VOLUME 0x905
+#define WM5100_HPLP1MIX_INPUT_4_SOURCE 0x906
+#define WM5100_HPLP1MIX_INPUT_4_VOLUME 0x907
+#define WM5100_HPLP2MIX_INPUT_1_SOURCE 0x908
+#define WM5100_HPLP2MIX_INPUT_1_VOLUME 0x909
+#define WM5100_HPLP2MIX_INPUT_2_SOURCE 0x90A
+#define WM5100_HPLP2MIX_INPUT_2_VOLUME 0x90B
+#define WM5100_HPLP2MIX_INPUT_3_SOURCE 0x90C
+#define WM5100_HPLP2MIX_INPUT_3_VOLUME 0x90D
+#define WM5100_HPLP2MIX_INPUT_4_SOURCE 0x90E
+#define WM5100_HPLP2MIX_INPUT_4_VOLUME 0x90F
+#define WM5100_HPLP3MIX_INPUT_1_SOURCE 0x910
+#define WM5100_HPLP3MIX_INPUT_1_VOLUME 0x911
+#define WM5100_HPLP3MIX_INPUT_2_SOURCE 0x912
+#define WM5100_HPLP3MIX_INPUT_2_VOLUME 0x913
+#define WM5100_HPLP3MIX_INPUT_3_SOURCE 0x914
+#define WM5100_HPLP3MIX_INPUT_3_VOLUME 0x915
+#define WM5100_HPLP3MIX_INPUT_4_SOURCE 0x916
+#define WM5100_HPLP3MIX_INPUT_4_VOLUME 0x917
+#define WM5100_HPLP4MIX_INPUT_1_SOURCE 0x918
+#define WM5100_HPLP4MIX_INPUT_1_VOLUME 0x919
+#define WM5100_HPLP4MIX_INPUT_2_SOURCE 0x91A
+#define WM5100_HPLP4MIX_INPUT_2_VOLUME 0x91B
+#define WM5100_HPLP4MIX_INPUT_3_SOURCE 0x91C
+#define WM5100_HPLP4MIX_INPUT_3_VOLUME 0x91D
+#define WM5100_HPLP4MIX_INPUT_4_SOURCE 0x91E
+#define WM5100_HPLP4MIX_INPUT_4_VOLUME 0x91F
+#define WM5100_DSP1LMIX_INPUT_1_SOURCE 0x940
+#define WM5100_DSP1LMIX_INPUT_1_VOLUME 0x941
+#define WM5100_DSP1LMIX_INPUT_2_SOURCE 0x942
+#define WM5100_DSP1LMIX_INPUT_2_VOLUME 0x943
+#define WM5100_DSP1LMIX_INPUT_3_SOURCE 0x944
+#define WM5100_DSP1LMIX_INPUT_3_VOLUME 0x945
+#define WM5100_DSP1LMIX_INPUT_4_SOURCE 0x946
+#define WM5100_DSP1LMIX_INPUT_4_VOLUME 0x947
+#define WM5100_DSP1RMIX_INPUT_1_SOURCE 0x948
+#define WM5100_DSP1RMIX_INPUT_1_VOLUME 0x949
+#define WM5100_DSP1RMIX_INPUT_2_SOURCE 0x94A
+#define WM5100_DSP1RMIX_INPUT_2_VOLUME 0x94B
+#define WM5100_DSP1RMIX_INPUT_3_SOURCE 0x94C
+#define WM5100_DSP1RMIX_INPUT_3_VOLUME 0x94D
+#define WM5100_DSP1RMIX_INPUT_4_SOURCE 0x94E
+#define WM5100_DSP1RMIX_INPUT_4_VOLUME 0x94F
+#define WM5100_DSP1AUX1MIX_INPUT_1_SOURCE 0x950
+#define WM5100_DSP1AUX2MIX_INPUT_1_SOURCE 0x958
+#define WM5100_DSP1AUX3MIX_INPUT_1_SOURCE 0x960
+#define WM5100_DSP1AUX4MIX_INPUT_1_SOURCE 0x968
+#define WM5100_DSP1AUX5MIX_INPUT_1_SOURCE 0x970
+#define WM5100_DSP1AUX6MIX_INPUT_1_SOURCE 0x978
+#define WM5100_DSP2LMIX_INPUT_1_SOURCE 0x980
+#define WM5100_DSP2LMIX_INPUT_1_VOLUME 0x981
+#define WM5100_DSP2LMIX_INPUT_2_SOURCE 0x982
+#define WM5100_DSP2LMIX_INPUT_2_VOLUME 0x983
+#define WM5100_DSP2LMIX_INPUT_3_SOURCE 0x984
+#define WM5100_DSP2LMIX_INPUT_3_VOLUME 0x985
+#define WM5100_DSP2LMIX_INPUT_4_SOURCE 0x986
+#define WM5100_DSP2LMIX_INPUT_4_VOLUME 0x987
+#define WM5100_DSP2RMIX_INPUT_1_SOURCE 0x988
+#define WM5100_DSP2RMIX_INPUT_1_VOLUME 0x989
+#define WM5100_DSP2RMIX_INPUT_2_SOURCE 0x98A
+#define WM5100_DSP2RMIX_INPUT_2_VOLUME 0x98B
+#define WM5100_DSP2RMIX_INPUT_3_SOURCE 0x98C
+#define WM5100_DSP2RMIX_INPUT_3_VOLUME 0x98D
+#define WM5100_DSP2RMIX_INPUT_4_SOURCE 0x98E
+#define WM5100_DSP2RMIX_INPUT_4_VOLUME 0x98F
+#define WM5100_DSP2AUX1MIX_INPUT_1_SOURCE 0x990
+#define WM5100_DSP2AUX2MIX_INPUT_1_SOURCE 0x998
+#define WM5100_DSP2AUX3MIX_INPUT_1_SOURCE 0x9A0
+#define WM5100_DSP2AUX4MIX_INPUT_1_SOURCE 0x9A8
+#define WM5100_DSP2AUX5MIX_INPUT_1_SOURCE 0x9B0
+#define WM5100_DSP2AUX6MIX_INPUT_1_SOURCE 0x9B8
+#define WM5100_DSP3LMIX_INPUT_1_SOURCE 0x9C0
+#define WM5100_DSP3LMIX_INPUT_1_VOLUME 0x9C1
+#define WM5100_DSP3LMIX_INPUT_2_SOURCE 0x9C2
+#define WM5100_DSP3LMIX_INPUT_2_VOLUME 0x9C3
+#define WM5100_DSP3LMIX_INPUT_3_SOURCE 0x9C4
+#define WM5100_DSP3LMIX_INPUT_3_VOLUME 0x9C5
+#define WM5100_DSP3LMIX_INPUT_4_SOURCE 0x9C6
+#define WM5100_DSP3LMIX_INPUT_4_VOLUME 0x9C7
+#define WM5100_DSP3RMIX_INPUT_1_SOURCE 0x9C8
+#define WM5100_DSP3RMIX_INPUT_1_VOLUME 0x9C9
+#define WM5100_DSP3RMIX_INPUT_2_SOURCE 0x9CA
+#define WM5100_DSP3RMIX_INPUT_2_VOLUME 0x9CB
+#define WM5100_DSP3RMIX_INPUT_3_SOURCE 0x9CC
+#define WM5100_DSP3RMIX_INPUT_3_VOLUME 0x9CD
+#define WM5100_DSP3RMIX_INPUT_4_SOURCE 0x9CE
+#define WM5100_DSP3RMIX_INPUT_4_VOLUME 0x9CF
+#define WM5100_DSP3AUX1MIX_INPUT_1_SOURCE 0x9D0
+#define WM5100_DSP3AUX2MIX_INPUT_1_SOURCE 0x9D8
+#define WM5100_DSP3AUX3MIX_INPUT_1_SOURCE 0x9E0
+#define WM5100_DSP3AUX4MIX_INPUT_1_SOURCE 0x9E8
+#define WM5100_DSP3AUX5MIX_INPUT_1_SOURCE 0x9F0
+#define WM5100_DSP3AUX6MIX_INPUT_1_SOURCE 0x9F8
+#define WM5100_ASRC1LMIX_INPUT_1_SOURCE 0xA80
+#define WM5100_ASRC1RMIX_INPUT_1_SOURCE 0xA88
+#define WM5100_ASRC2LMIX_INPUT_1_SOURCE 0xA90
+#define WM5100_ASRC2RMIX_INPUT_1_SOURCE 0xA98
+#define WM5100_ISRC1DEC1MIX_INPUT_1_SOURCE 0xB00
+#define WM5100_ISRC1DEC2MIX_INPUT_1_SOURCE 0xB08
+#define WM5100_ISRC1DEC3MIX_INPUT_1_SOURCE 0xB10
+#define WM5100_ISRC1DEC4MIX_INPUT_1_SOURCE 0xB18
+#define WM5100_ISRC1INT1MIX_INPUT_1_SOURCE 0xB20
+#define WM5100_ISRC1INT2MIX_INPUT_1_SOURCE 0xB28
+#define WM5100_ISRC1INT3MIX_INPUT_1_SOURCE 0xB30
+#define WM5100_ISRC1INT4MIX_INPUT_1_SOURCE 0xB38
+#define WM5100_ISRC2DEC1MIX_INPUT_1_SOURCE 0xB40
+#define WM5100_ISRC2DEC2MIX_INPUT_1_SOURCE 0xB48
+#define WM5100_ISRC2DEC3MIX_INPUT_1_SOURCE 0xB50
+#define WM5100_ISRC2DEC4MIX_INPUT_1_SOURCE 0xB58
+#define WM5100_ISRC2INT1MIX_INPUT_1_SOURCE 0xB60
+#define WM5100_ISRC2INT2MIX_INPUT_1_SOURCE 0xB68
+#define WM5100_ISRC2INT3MIX_INPUT_1_SOURCE 0xB70
+#define WM5100_ISRC2INT4MIX_INPUT_1_SOURCE 0xB78
+#define WM5100_GPIO_CTRL_1 0xC00
+#define WM5100_GPIO_CTRL_2 0xC01
+#define WM5100_GPIO_CTRL_3 0xC02
+#define WM5100_GPIO_CTRL_4 0xC03
+#define WM5100_GPIO_CTRL_5 0xC04
+#define WM5100_GPIO_CTRL_6 0xC05
+#define WM5100_MISC_PAD_CTRL_1 0xC23
+#define WM5100_MISC_PAD_CTRL_2 0xC24
+#define WM5100_MISC_PAD_CTRL_3 0xC25
+#define WM5100_MISC_PAD_CTRL_4 0xC26
+#define WM5100_MISC_PAD_CTRL_5 0xC27
+#define WM5100_MISC_GPIO_1 0xC28
+#define WM5100_INTERRUPT_STATUS_1 0xD00
+#define WM5100_INTERRUPT_STATUS_2 0xD01
+#define WM5100_INTERRUPT_STATUS_3 0xD02
+#define WM5100_INTERRUPT_STATUS_4 0xD03
+#define WM5100_INTERRUPT_RAW_STATUS_2 0xD04
+#define WM5100_INTERRUPT_RAW_STATUS_3 0xD05
+#define WM5100_INTERRUPT_RAW_STATUS_4 0xD06
+#define WM5100_INTERRUPT_STATUS_1_MASK 0xD07
+#define WM5100_INTERRUPT_STATUS_2_MASK 0xD08
+#define WM5100_INTERRUPT_STATUS_3_MASK 0xD09
+#define WM5100_INTERRUPT_STATUS_4_MASK 0xD0A
+#define WM5100_INTERRUPT_CONTROL 0xD1F
+#define WM5100_IRQ_DEBOUNCE_1 0xD20
+#define WM5100_IRQ_DEBOUNCE_2 0xD21
+#define WM5100_FX_CTRL 0xE00
+#define WM5100_EQ1_1 0xE10
+#define WM5100_EQ1_2 0xE11
+#define WM5100_EQ1_3 0xE12
+#define WM5100_EQ1_4 0xE13
+#define WM5100_EQ1_5 0xE14
+#define WM5100_EQ1_6 0xE15
+#define WM5100_EQ1_7 0xE16
+#define WM5100_EQ1_8 0xE17
+#define WM5100_EQ1_9 0xE18
+#define WM5100_EQ1_10 0xE19
+#define WM5100_EQ1_11 0xE1A
+#define WM5100_EQ1_12 0xE1B
+#define WM5100_EQ1_13 0xE1C
+#define WM5100_EQ1_14 0xE1D
+#define WM5100_EQ1_15 0xE1E
+#define WM5100_EQ1_16 0xE1F
+#define WM5100_EQ1_17 0xE20
+#define WM5100_EQ1_18 0xE21
+#define WM5100_EQ1_19 0xE22
+#define WM5100_EQ1_20 0xE23
+#define WM5100_EQ2_1 0xE26
+#define WM5100_EQ2_2 0xE27
+#define WM5100_EQ2_3 0xE28
+#define WM5100_EQ2_4 0xE29
+#define WM5100_EQ2_5 0xE2A
+#define WM5100_EQ2_6 0xE2B
+#define WM5100_EQ2_7 0xE2C
+#define WM5100_EQ2_8 0xE2D
+#define WM5100_EQ2_9 0xE2E
+#define WM5100_EQ2_10 0xE2F
+#define WM5100_EQ2_11 0xE30
+#define WM5100_EQ2_12 0xE31
+#define WM5100_EQ2_13 0xE32
+#define WM5100_EQ2_14 0xE33
+#define WM5100_EQ2_15 0xE34
+#define WM5100_EQ2_16 0xE35
+#define WM5100_EQ2_17 0xE36
+#define WM5100_EQ2_18 0xE37
+#define WM5100_EQ2_19 0xE38
+#define WM5100_EQ2_20 0xE39
+#define WM5100_EQ3_1 0xE3C
+#define WM5100_EQ3_2 0xE3D
+#define WM5100_EQ3_3 0xE3E
+#define WM5100_EQ3_4 0xE3F
+#define WM5100_EQ3_5 0xE40
+#define WM5100_EQ3_6 0xE41
+#define WM5100_EQ3_7 0xE42
+#define WM5100_EQ3_8 0xE43
+#define WM5100_EQ3_9 0xE44
+#define WM5100_EQ3_10 0xE45
+#define WM5100_EQ3_11 0xE46
+#define WM5100_EQ3_12 0xE47
+#define WM5100_EQ3_13 0xE48
+#define WM5100_EQ3_14 0xE49
+#define WM5100_EQ3_15 0xE4A
+#define WM5100_EQ3_16 0xE4B
+#define WM5100_EQ3_17 0xE4C
+#define WM5100_EQ3_18 0xE4D
+#define WM5100_EQ3_19 0xE4E
+#define WM5100_EQ3_20 0xE4F
+#define WM5100_EQ4_1 0xE52
+#define WM5100_EQ4_2 0xE53
+#define WM5100_EQ4_3 0xE54
+#define WM5100_EQ4_4 0xE55
+#define WM5100_EQ4_5 0xE56
+#define WM5100_EQ4_6 0xE57
+#define WM5100_EQ4_7 0xE58
+#define WM5100_EQ4_8 0xE59
+#define WM5100_EQ4_9 0xE5A
+#define WM5100_EQ4_10 0xE5B
+#define WM5100_EQ4_11 0xE5C
+#define WM5100_EQ4_12 0xE5D
+#define WM5100_EQ4_13 0xE5E
+#define WM5100_EQ4_14 0xE5F
+#define WM5100_EQ4_15 0xE60
+#define WM5100_EQ4_16 0xE61
+#define WM5100_EQ4_17 0xE62
+#define WM5100_EQ4_18 0xE63
+#define WM5100_EQ4_19 0xE64
+#define WM5100_EQ4_20 0xE65
+#define WM5100_DRC1_CTRL1 0xE80
+#define WM5100_DRC1_CTRL2 0xE81
+#define WM5100_DRC1_CTRL3 0xE82
+#define WM5100_DRC1_CTRL4 0xE83
+#define WM5100_DRC1_CTRL5 0xE84
+#define WM5100_HPLPF1_1 0xEC0
+#define WM5100_HPLPF1_2 0xEC1
+#define WM5100_HPLPF2_1 0xEC4
+#define WM5100_HPLPF2_2 0xEC5
+#define WM5100_HPLPF3_1 0xEC8
+#define WM5100_HPLPF3_2 0xEC9
+#define WM5100_HPLPF4_1 0xECC
+#define WM5100_HPLPF4_2 0xECD
+#define WM5100_DSP1_CONTROL_1 0xF00
+#define WM5100_DSP1_CONTROL_2 0xF02
+#define WM5100_DSP1_CONTROL_3 0xF03
+#define WM5100_DSP1_CONTROL_4 0xF04
+#define WM5100_DSP1_CONTROL_5 0xF06
+#define WM5100_DSP1_CONTROL_6 0xF07
+#define WM5100_DSP1_CONTROL_7 0xF08
+#define WM5100_DSP1_CONTROL_8 0xF09
+#define WM5100_DSP1_CONTROL_9 0xF0A
+#define WM5100_DSP1_CONTROL_10 0xF0B
+#define WM5100_DSP1_CONTROL_11 0xF0C
+#define WM5100_DSP1_CONTROL_12 0xF0D
+#define WM5100_DSP1_CONTROL_13 0xF0F
+#define WM5100_DSP1_CONTROL_14 0xF10
+#define WM5100_DSP1_CONTROL_15 0xF11
+#define WM5100_DSP1_CONTROL_16 0xF12
+#define WM5100_DSP1_CONTROL_17 0xF13
+#define WM5100_DSP1_CONTROL_18 0xF14
+#define WM5100_DSP1_CONTROL_19 0xF16
+#define WM5100_DSP1_CONTROL_20 0xF17
+#define WM5100_DSP1_CONTROL_21 0xF18
+#define WM5100_DSP1_CONTROL_22 0xF1A
+#define WM5100_DSP1_CONTROL_23 0xF1B
+#define WM5100_DSP1_CONTROL_24 0xF1C
+#define WM5100_DSP1_CONTROL_25 0xF1E
+#define WM5100_DSP1_CONTROL_26 0xF20
+#define WM5100_DSP1_CONTROL_27 0xF21
+#define WM5100_DSP1_CONTROL_28 0xF22
+#define WM5100_DSP1_CONTROL_29 0xF23
+#define WM5100_DSP1_CONTROL_30 0xF24
+#define WM5100_DSP2_CONTROL_1 0x1000
+#define WM5100_DSP2_CONTROL_2 0x1002
+#define WM5100_DSP2_CONTROL_3 0x1003
+#define WM5100_DSP2_CONTROL_4 0x1004
+#define WM5100_DSP2_CONTROL_5 0x1006
+#define WM5100_DSP2_CONTROL_6 0x1007
+#define WM5100_DSP2_CONTROL_7 0x1008
+#define WM5100_DSP2_CONTROL_8 0x1009
+#define WM5100_DSP2_CONTROL_9 0x100A
+#define WM5100_DSP2_CONTROL_10 0x100B
+#define WM5100_DSP2_CONTROL_11 0x100C
+#define WM5100_DSP2_CONTROL_12 0x100D
+#define WM5100_DSP2_CONTROL_13 0x100F
+#define WM5100_DSP2_CONTROL_14 0x1010
+#define WM5100_DSP2_CONTROL_15 0x1011
+#define WM5100_DSP2_CONTROL_16 0x1012
+#define WM5100_DSP2_CONTROL_17 0x1013
+#define WM5100_DSP2_CONTROL_18 0x1014
+#define WM5100_DSP2_CONTROL_19 0x1016
+#define WM5100_DSP2_CONTROL_20 0x1017
+#define WM5100_DSP2_CONTROL_21 0x1018
+#define WM5100_DSP2_CONTROL_22 0x101A
+#define WM5100_DSP2_CONTROL_23 0x101B
+#define WM5100_DSP2_CONTROL_24 0x101C
+#define WM5100_DSP2_CONTROL_25 0x101E
+#define WM5100_DSP2_CONTROL_26 0x1020
+#define WM5100_DSP2_CONTROL_27 0x1021
+#define WM5100_DSP2_CONTROL_28 0x1022
+#define WM5100_DSP2_CONTROL_29 0x1023
+#define WM5100_DSP2_CONTROL_30 0x1024
+#define WM5100_DSP3_CONTROL_1 0x1100
+#define WM5100_DSP3_CONTROL_2 0x1102
+#define WM5100_DSP3_CONTROL_3 0x1103
+#define WM5100_DSP3_CONTROL_4 0x1104
+#define WM5100_DSP3_CONTROL_5 0x1106
+#define WM5100_DSP3_CONTROL_6 0x1107
+#define WM5100_DSP3_CONTROL_7 0x1108
+#define WM5100_DSP3_CONTROL_8 0x1109
+#define WM5100_DSP3_CONTROL_9 0x110A
+#define WM5100_DSP3_CONTROL_10 0x110B
+#define WM5100_DSP3_CONTROL_11 0x110C
+#define WM5100_DSP3_CONTROL_12 0x110D
+#define WM5100_DSP3_CONTROL_13 0x110F
+#define WM5100_DSP3_CONTROL_14 0x1110
+#define WM5100_DSP3_CONTROL_15 0x1111
+#define WM5100_DSP3_CONTROL_16 0x1112
+#define WM5100_DSP3_CONTROL_17 0x1113
+#define WM5100_DSP3_CONTROL_18 0x1114
+#define WM5100_DSP3_CONTROL_19 0x1116
+#define WM5100_DSP3_CONTROL_20 0x1117
+#define WM5100_DSP3_CONTROL_21 0x1118
+#define WM5100_DSP3_CONTROL_22 0x111A
+#define WM5100_DSP3_CONTROL_23 0x111B
+#define WM5100_DSP3_CONTROL_24 0x111C
+#define WM5100_DSP3_CONTROL_25 0x111E
+#define WM5100_DSP3_CONTROL_26 0x1120
+#define WM5100_DSP3_CONTROL_27 0x1121
+#define WM5100_DSP3_CONTROL_28 0x1122
+#define WM5100_DSP3_CONTROL_29 0x1123
+#define WM5100_DSP3_CONTROL_30 0x1124
+#define WM5100_DSP1_DM_0 0x4000
+#define WM5100_DSP1_DM_1 0x4001
+#define WM5100_DSP1_DM_2 0x4002
+#define WM5100_DSP1_DM_3 0x4003
+#define WM5100_DSP1_DM_508 0x41FC
+#define WM5100_DSP1_DM_509 0x41FD
+#define WM5100_DSP1_DM_510 0x41FE
+#define WM5100_DSP1_DM_511 0x41FF
+#define WM5100_DSP1_PM_0 0x4800
+#define WM5100_DSP1_PM_1 0x4801
+#define WM5100_DSP1_PM_2 0x4802
+#define WM5100_DSP1_PM_3 0x4803
+#define WM5100_DSP1_PM_4 0x4804
+#define WM5100_DSP1_PM_5 0x4805
+#define WM5100_DSP1_PM_1530 0x4DFA
+#define WM5100_DSP1_PM_1531 0x4DFB
+#define WM5100_DSP1_PM_1532 0x4DFC
+#define WM5100_DSP1_PM_1533 0x4DFD
+#define WM5100_DSP1_PM_1534 0x4DFE
+#define WM5100_DSP1_PM_1535 0x4DFF
+#define WM5100_DSP1_ZM_0 0x5000
+#define WM5100_DSP1_ZM_1 0x5001
+#define WM5100_DSP1_ZM_2 0x5002
+#define WM5100_DSP1_ZM_3 0x5003
+#define WM5100_DSP1_ZM_2044 0x57FC
+#define WM5100_DSP1_ZM_2045 0x57FD
+#define WM5100_DSP1_ZM_2046 0x57FE
+#define WM5100_DSP1_ZM_2047 0x57FF
+#define WM5100_DSP2_DM_0 0x6000
+#define WM5100_DSP2_DM_1 0x6001
+#define WM5100_DSP2_DM_2 0x6002
+#define WM5100_DSP2_DM_3 0x6003
+#define WM5100_DSP2_DM_508 0x61FC
+#define WM5100_DSP2_DM_509 0x61FD
+#define WM5100_DSP2_DM_510 0x61FE
+#define WM5100_DSP2_DM_511 0x61FF
+#define WM5100_DSP2_PM_0 0x6800
+#define WM5100_DSP2_PM_1 0x6801
+#define WM5100_DSP2_PM_2 0x6802
+#define WM5100_DSP2_PM_3 0x6803
+#define WM5100_DSP2_PM_4 0x6804
+#define WM5100_DSP2_PM_5 0x6805
+#define WM5100_DSP2_PM_1530 0x6DFA
+#define WM5100_DSP2_PM_1531 0x6DFB
+#define WM5100_DSP2_PM_1532 0x6DFC
+#define WM5100_DSP2_PM_1533 0x6DFD
+#define WM5100_DSP2_PM_1534 0x6DFE
+#define WM5100_DSP2_PM_1535 0x6DFF
+#define WM5100_DSP2_ZM_0 0x7000
+#define WM5100_DSP2_ZM_1 0x7001
+#define WM5100_DSP2_ZM_2 0x7002
+#define WM5100_DSP2_ZM_3 0x7003
+#define WM5100_DSP2_ZM_2044 0x77FC
+#define WM5100_DSP2_ZM_2045 0x77FD
+#define WM5100_DSP2_ZM_2046 0x77FE
+#define WM5100_DSP2_ZM_2047 0x77FF
+#define WM5100_DSP3_DM_0 0x8000
+#define WM5100_DSP3_DM_1 0x8001
+#define WM5100_DSP3_DM_2 0x8002
+#define WM5100_DSP3_DM_3 0x8003
+#define WM5100_DSP3_DM_508 0x81FC
+#define WM5100_DSP3_DM_509 0x81FD
+#define WM5100_DSP3_DM_510 0x81FE
+#define WM5100_DSP3_DM_511 0x81FF
+#define WM5100_DSP3_PM_0 0x8800
+#define WM5100_DSP3_PM_1 0x8801
+#define WM5100_DSP3_PM_2 0x8802
+#define WM5100_DSP3_PM_3 0x8803
+#define WM5100_DSP3_PM_4 0x8804
+#define WM5100_DSP3_PM_5 0x8805
+#define WM5100_DSP3_PM_1530 0x8DFA
+#define WM5100_DSP3_PM_1531 0x8DFB
+#define WM5100_DSP3_PM_1532 0x8DFC
+#define WM5100_DSP3_PM_1533 0x8DFD
+#define WM5100_DSP3_PM_1534 0x8DFE
+#define WM5100_DSP3_PM_1535 0x8DFF
+#define WM5100_DSP3_ZM_0 0x9000
+#define WM5100_DSP3_ZM_1 0x9001
+#define WM5100_DSP3_ZM_2 0x9002
+#define WM5100_DSP3_ZM_3 0x9003
+#define WM5100_DSP3_ZM_2044 0x97FC
+#define WM5100_DSP3_ZM_2045 0x97FD
+#define WM5100_DSP3_ZM_2046 0x97FE
+#define WM5100_DSP3_ZM_2047 0x97FF
+
+#define WM5100_REGISTER_COUNT 1435
+#define WM5100_MAX_REGISTER 0x97FF
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - software reset
+ */
+#define WM5100_SW_RST_DEV_ID1_MASK 0xFFFF /* SW_RST_DEV_ID1 - [15:0] */
+#define WM5100_SW_RST_DEV_ID1_SHIFT 0 /* SW_RST_DEV_ID1 - [15:0] */
+#define WM5100_SW_RST_DEV_ID1_WIDTH 16 /* SW_RST_DEV_ID1 - [15:0] */
+
+/*
+ * R1 (0x01) - Device Revision
+ */
+#define WM5100_DEVICE_REVISION_MASK 0x000F /* DEVICE_REVISION - [3:0] */
+#define WM5100_DEVICE_REVISION_SHIFT 0 /* DEVICE_REVISION - [3:0] */
+#define WM5100_DEVICE_REVISION_WIDTH 4 /* DEVICE_REVISION - [3:0] */
+
+/*
+ * R16 (0x10) - Ctrl IF 1
+ */
+#define WM5100_AUTO_INC 0x0001 /* AUTO_INC */
+#define WM5100_AUTO_INC_MASK 0x0001 /* AUTO_INC */
+#define WM5100_AUTO_INC_SHIFT 0 /* AUTO_INC */
+#define WM5100_AUTO_INC_WIDTH 1 /* AUTO_INC */
+
+/*
+ * R32 (0x20) - Tone Generator 1
+ */
+#define WM5100_TONE_RATE_MASK 0x3000 /* TONE_RATE - [13:12] */
+#define WM5100_TONE_RATE_SHIFT 12 /* TONE_RATE - [13:12] */
+#define WM5100_TONE_RATE_WIDTH 2 /* TONE_RATE - [13:12] */
+#define WM5100_TONE_OFFSET_MASK 0x0300 /* TONE_OFFSET - [9:8] */
+#define WM5100_TONE_OFFSET_SHIFT 8 /* TONE_OFFSET - [9:8] */
+#define WM5100_TONE_OFFSET_WIDTH 2 /* TONE_OFFSET - [9:8] */
+#define WM5100_TONE2_ENA 0x0002 /* TONE2_ENA */
+#define WM5100_TONE2_ENA_MASK 0x0002 /* TONE2_ENA */
+#define WM5100_TONE2_ENA_SHIFT 1 /* TONE2_ENA */
+#define WM5100_TONE2_ENA_WIDTH 1 /* TONE2_ENA */
+#define WM5100_TONE1_ENA 0x0001 /* TONE1_ENA */
+#define WM5100_TONE1_ENA_MASK 0x0001 /* TONE1_ENA */
+#define WM5100_TONE1_ENA_SHIFT 0 /* TONE1_ENA */
+#define WM5100_TONE1_ENA_WIDTH 1 /* TONE1_ENA */
+
+/*
+ * R48 (0x30) - PWM Drive 1
+ */
+#define WM5100_PWM_RATE_MASK 0x3000 /* PWM_RATE - [13:12] */
+#define WM5100_PWM_RATE_SHIFT 12 /* PWM_RATE - [13:12] */
+#define WM5100_PWM_RATE_WIDTH 2 /* PWM_RATE - [13:12] */
+#define WM5100_PWM_CLK_SEL_MASK 0x0300 /* PWM_CLK_SEL - [9:8] */
+#define WM5100_PWM_CLK_SEL_SHIFT 8 /* PWM_CLK_SEL - [9:8] */
+#define WM5100_PWM_CLK_SEL_WIDTH 2 /* PWM_CLK_SEL - [9:8] */
+#define WM5100_PWM2_OVD 0x0020 /* PWM2_OVD */
+#define WM5100_PWM2_OVD_MASK 0x0020 /* PWM2_OVD */
+#define WM5100_PWM2_OVD_SHIFT 5 /* PWM2_OVD */
+#define WM5100_PWM2_OVD_WIDTH 1 /* PWM2_OVD */
+#define WM5100_PWM1_OVD 0x0010 /* PWM1_OVD */
+#define WM5100_PWM1_OVD_MASK 0x0010 /* PWM1_OVD */
+#define WM5100_PWM1_OVD_SHIFT 4 /* PWM1_OVD */
+#define WM5100_PWM1_OVD_WIDTH 1 /* PWM1_OVD */
+#define WM5100_PWM2_ENA 0x0002 /* PWM2_ENA */
+#define WM5100_PWM2_ENA_MASK 0x0002 /* PWM2_ENA */
+#define WM5100_PWM2_ENA_SHIFT 1 /* PWM2_ENA */
+#define WM5100_PWM2_ENA_WIDTH 1 /* PWM2_ENA */
+#define WM5100_PWM1_ENA 0x0001 /* PWM1_ENA */
+#define WM5100_PWM1_ENA_MASK 0x0001 /* PWM1_ENA */
+#define WM5100_PWM1_ENA_SHIFT 0 /* PWM1_ENA */
+#define WM5100_PWM1_ENA_WIDTH 1 /* PWM1_ENA */
+
+/*
+ * R49 (0x31) - PWM Drive 2
+ */
+#define WM5100_PWM1_LVL_MASK 0x03FF /* PWM1_LVL - [9:0] */
+#define WM5100_PWM1_LVL_SHIFT 0 /* PWM1_LVL - [9:0] */
+#define WM5100_PWM1_LVL_WIDTH 10 /* PWM1_LVL - [9:0] */
+
+/*
+ * R50 (0x32) - PWM Drive 3
+ */
+#define WM5100_PWM2_LVL_MASK 0x03FF /* PWM2_LVL - [9:0] */
+#define WM5100_PWM2_LVL_SHIFT 0 /* PWM2_LVL - [9:0] */
+#define WM5100_PWM2_LVL_WIDTH 10 /* PWM2_LVL - [9:0] */
+
+/*
+ * R256 (0x100) - Clocking 1
+ */
+#define WM5100_CLK_32K_SRC_MASK 0x000F /* CLK_32K_SRC - [3:0] */
+#define WM5100_CLK_32K_SRC_SHIFT 0 /* CLK_32K_SRC - [3:0] */
+#define WM5100_CLK_32K_SRC_WIDTH 4 /* CLK_32K_SRC - [3:0] */
+
+/*
+ * R257 (0x101) - Clocking 3
+ */
+#define WM5100_SYSCLK_FREQ_MASK 0x0700 /* SYSCLK_FREQ - [10:8] */
+#define WM5100_SYSCLK_FREQ_SHIFT 8 /* SYSCLK_FREQ - [10:8] */
+#define WM5100_SYSCLK_FREQ_WIDTH 3 /* SYSCLK_FREQ - [10:8] */
+#define WM5100_SYSCLK_ENA 0x0040 /* SYSCLK_ENA */
+#define WM5100_SYSCLK_ENA_MASK 0x0040 /* SYSCLK_ENA */
+#define WM5100_SYSCLK_ENA_SHIFT 6 /* SYSCLK_ENA */
+#define WM5100_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */
+#define WM5100_SYSCLK_SRC_MASK 0x000F /* SYSCLK_SRC - [3:0] */
+#define WM5100_SYSCLK_SRC_SHIFT 0 /* SYSCLK_SRC - [3:0] */
+#define WM5100_SYSCLK_SRC_WIDTH 4 /* SYSCLK_SRC - [3:0] */
+
+/*
+ * R258 (0x102) - Clocking 4
+ */
+#define WM5100_SAMPLE_RATE_1_MASK 0x001F /* SAMPLE_RATE_1 - [4:0] */
+#define WM5100_SAMPLE_RATE_1_SHIFT 0 /* SAMPLE_RATE_1 - [4:0] */
+#define WM5100_SAMPLE_RATE_1_WIDTH 5 /* SAMPLE_RATE_1 - [4:0] */
+
+/*
+ * R259 (0x103) - Clocking 5
+ */
+#define WM5100_SAMPLE_RATE_2_MASK 0x001F /* SAMPLE_RATE_2 - [4:0] */
+#define WM5100_SAMPLE_RATE_2_SHIFT 0 /* SAMPLE_RATE_2 - [4:0] */
+#define WM5100_SAMPLE_RATE_2_WIDTH 5 /* SAMPLE_RATE_2 - [4:0] */
+
+/*
+ * R260 (0x104) - Clocking 6
+ */
+#define WM5100_SAMPLE_RATE_3_MASK 0x001F /* SAMPLE_RATE_3 - [4:0] */
+#define WM5100_SAMPLE_RATE_3_SHIFT 0 /* SAMPLE_RATE_3 - [4:0] */
+#define WM5100_SAMPLE_RATE_3_WIDTH 5 /* SAMPLE_RATE_3 - [4:0] */
+
+/*
+ * R263 (0x107) - Clocking 7
+ */
+#define WM5100_ASYNC_CLK_FREQ_MASK 0x0700 /* ASYNC_CLK_FREQ - [10:8] */
+#define WM5100_ASYNC_CLK_FREQ_SHIFT 8 /* ASYNC_CLK_FREQ - [10:8] */
+#define WM5100_ASYNC_CLK_FREQ_WIDTH 3 /* ASYNC_CLK_FREQ - [10:8] */
+#define WM5100_ASYNC_CLK_ENA 0x0040 /* ASYNC_CLK_ENA */
+#define WM5100_ASYNC_CLK_ENA_MASK 0x0040 /* ASYNC_CLK_ENA */
+#define WM5100_ASYNC_CLK_ENA_SHIFT 6 /* ASYNC_CLK_ENA */
+#define WM5100_ASYNC_CLK_ENA_WIDTH 1 /* ASYNC_CLK_ENA */
+#define WM5100_ASYNC_CLK_SRC_MASK 0x000F /* ASYNC_CLK_SRC - [3:0] */
+#define WM5100_ASYNC_CLK_SRC_SHIFT 0 /* ASYNC_CLK_SRC - [3:0] */
+#define WM5100_ASYNC_CLK_SRC_WIDTH 4 /* ASYNC_CLK_SRC - [3:0] */
+
+/*
+ * R264 (0x108) - Clocking 8
+ */
+#define WM5100_ASYNC_SAMPLE_RATE_MASK 0x001F /* ASYNC_SAMPLE_RATE - [4:0] */
+#define WM5100_ASYNC_SAMPLE_RATE_SHIFT 0 /* ASYNC_SAMPLE_RATE - [4:0] */
+#define WM5100_ASYNC_SAMPLE_RATE_WIDTH 5 /* ASYNC_SAMPLE_RATE - [4:0] */
+
+/*
+ * R288 (0x120) - ASRC_ENABLE
+ */
+#define WM5100_ASRC2L_ENA 0x0008 /* ASRC2L_ENA */
+#define WM5100_ASRC2L_ENA_MASK 0x0008 /* ASRC2L_ENA */
+#define WM5100_ASRC2L_ENA_SHIFT 3 /* ASRC2L_ENA */
+#define WM5100_ASRC2L_ENA_WIDTH 1 /* ASRC2L_ENA */
+#define WM5100_ASRC2R_ENA 0x0004 /* ASRC2R_ENA */
+#define WM5100_ASRC2R_ENA_MASK 0x0004 /* ASRC2R_ENA */
+#define WM5100_ASRC2R_ENA_SHIFT 2 /* ASRC2R_ENA */
+#define WM5100_ASRC2R_ENA_WIDTH 1 /* ASRC2R_ENA */
+#define WM5100_ASRC1L_ENA 0x0002 /* ASRC1L_ENA */
+#define WM5100_ASRC1L_ENA_MASK 0x0002 /* ASRC1L_ENA */
+#define WM5100_ASRC1L_ENA_SHIFT 1 /* ASRC1L_ENA */
+#define WM5100_ASRC1L_ENA_WIDTH 1 /* ASRC1L_ENA */
+#define WM5100_ASRC1R_ENA 0x0001 /* ASRC1R_ENA */
+#define WM5100_ASRC1R_ENA_MASK 0x0001 /* ASRC1R_ENA */
+#define WM5100_ASRC1R_ENA_SHIFT 0 /* ASRC1R_ENA */
+#define WM5100_ASRC1R_ENA_WIDTH 1 /* ASRC1R_ENA */
+
+/*
+ * R289 (0x121) - ASRC_STATUS
+ */
+#define WM5100_ASRC2L_ENA_STS 0x0008 /* ASRC2L_ENA_STS */
+#define WM5100_ASRC2L_ENA_STS_MASK 0x0008 /* ASRC2L_ENA_STS */
+#define WM5100_ASRC2L_ENA_STS_SHIFT 3 /* ASRC2L_ENA_STS */
+#define WM5100_ASRC2L_ENA_STS_WIDTH 1 /* ASRC2L_ENA_STS */
+#define WM5100_ASRC2R_ENA_STS 0x0004 /* ASRC2R_ENA_STS */
+#define WM5100_ASRC2R_ENA_STS_MASK 0x0004 /* ASRC2R_ENA_STS */
+#define WM5100_ASRC2R_ENA_STS_SHIFT 2 /* ASRC2R_ENA_STS */
+#define WM5100_ASRC2R_ENA_STS_WIDTH 1 /* ASRC2R_ENA_STS */
+#define WM5100_ASRC1L_ENA_STS 0x0002 /* ASRC1L_ENA_STS */
+#define WM5100_ASRC1L_ENA_STS_MASK 0x0002 /* ASRC1L_ENA_STS */
+#define WM5100_ASRC1L_ENA_STS_SHIFT 1 /* ASRC1L_ENA_STS */
+#define WM5100_ASRC1L_ENA_STS_WIDTH 1 /* ASRC1L_ENA_STS */
+#define WM5100_ASRC1R_ENA_STS 0x0001 /* ASRC1R_ENA_STS */
+#define WM5100_ASRC1R_ENA_STS_MASK 0x0001 /* ASRC1R_ENA_STS */
+#define WM5100_ASRC1R_ENA_STS_SHIFT 0 /* ASRC1R_ENA_STS */
+#define WM5100_ASRC1R_ENA_STS_WIDTH 1 /* ASRC1R_ENA_STS */
+
+/*
+ * R290 (0x122) - ASRC_RATE1
+ */
+#define WM5100_ASRC_RATE1_MASK 0x0006 /* ASRC_RATE1 - [2:1] */
+#define WM5100_ASRC_RATE1_SHIFT 1 /* ASRC_RATE1 - [2:1] */
+#define WM5100_ASRC_RATE1_WIDTH 2 /* ASRC_RATE1 - [2:1] */
+
+/*
+ * R321 (0x141) - ISRC 1 CTRL 1
+ */
+#define WM5100_ISRC1_DFS_ENA 0x2000 /* ISRC1_DFS_ENA */
+#define WM5100_ISRC1_DFS_ENA_MASK 0x2000 /* ISRC1_DFS_ENA */
+#define WM5100_ISRC1_DFS_ENA_SHIFT 13 /* ISRC1_DFS_ENA */
+#define WM5100_ISRC1_DFS_ENA_WIDTH 1 /* ISRC1_DFS_ENA */
+#define WM5100_ISRC1_CLK_SEL_MASK 0x0300 /* ISRC1_CLK_SEL - [9:8] */
+#define WM5100_ISRC1_CLK_SEL_SHIFT 8 /* ISRC1_CLK_SEL - [9:8] */
+#define WM5100_ISRC1_CLK_SEL_WIDTH 2 /* ISRC1_CLK_SEL - [9:8] */
+#define WM5100_ISRC1_FSH_MASK 0x000C /* ISRC1_FSH - [3:2] */
+#define WM5100_ISRC1_FSH_SHIFT 2 /* ISRC1_FSH - [3:2] */
+#define WM5100_ISRC1_FSH_WIDTH 2 /* ISRC1_FSH - [3:2] */
+#define WM5100_ISRC1_FSL_MASK 0x0003 /* ISRC1_FSL - [1:0] */
+#define WM5100_ISRC1_FSL_SHIFT 0 /* ISRC1_FSL - [1:0] */
+#define WM5100_ISRC1_FSL_WIDTH 2 /* ISRC1_FSL - [1:0] */
+
+/*
+ * R322 (0x142) - ISRC 1 CTRL 2
+ */
+#define WM5100_ISRC1_INT1_ENA 0x8000 /* ISRC1_INT1_ENA */
+#define WM5100_ISRC1_INT1_ENA_MASK 0x8000 /* ISRC1_INT1_ENA */
+#define WM5100_ISRC1_INT1_ENA_SHIFT 15 /* ISRC1_INT1_ENA */
+#define WM5100_ISRC1_INT1_ENA_WIDTH 1 /* ISRC1_INT1_ENA */
+#define WM5100_ISRC1_INT2_ENA 0x4000 /* ISRC1_INT2_ENA */
+#define WM5100_ISRC1_INT2_ENA_MASK 0x4000 /* ISRC1_INT2_ENA */
+#define WM5100_ISRC1_INT2_ENA_SHIFT 14 /* ISRC1_INT2_ENA */
+#define WM5100_ISRC1_INT2_ENA_WIDTH 1 /* ISRC1_INT2_ENA */
+#define WM5100_ISRC1_INT3_ENA 0x2000 /* ISRC1_INT3_ENA */
+#define WM5100_ISRC1_INT3_ENA_MASK 0x2000 /* ISRC1_INT3_ENA */
+#define WM5100_ISRC1_INT3_ENA_SHIFT 13 /* ISRC1_INT3_ENA */
+#define WM5100_ISRC1_INT3_ENA_WIDTH 1 /* ISRC1_INT3_ENA */
+#define WM5100_ISRC1_INT4_ENA 0x1000 /* ISRC1_INT4_ENA */
+#define WM5100_ISRC1_INT4_ENA_MASK 0x1000 /* ISRC1_INT4_ENA */
+#define WM5100_ISRC1_INT4_ENA_SHIFT 12 /* ISRC1_INT4_ENA */
+#define WM5100_ISRC1_INT4_ENA_WIDTH 1 /* ISRC1_INT4_ENA */
+#define WM5100_ISRC1_DEC1_ENA 0x0200 /* ISRC1_DEC1_ENA */
+#define WM5100_ISRC1_DEC1_ENA_MASK 0x0200 /* ISRC1_DEC1_ENA */
+#define WM5100_ISRC1_DEC1_ENA_SHIFT 9 /* ISRC1_DEC1_ENA */
+#define WM5100_ISRC1_DEC1_ENA_WIDTH 1 /* ISRC1_DEC1_ENA */
+#define WM5100_ISRC1_DEC2_ENA 0x0100 /* ISRC1_DEC2_ENA */
+#define WM5100_ISRC1_DEC2_ENA_MASK 0x0100 /* ISRC1_DEC2_ENA */
+#define WM5100_ISRC1_DEC2_ENA_SHIFT 8 /* ISRC1_DEC2_ENA */
+#define WM5100_ISRC1_DEC2_ENA_WIDTH 1 /* ISRC1_DEC2_ENA */
+#define WM5100_ISRC1_DEC3_ENA 0x0080 /* ISRC1_DEC3_ENA */
+#define WM5100_ISRC1_DEC3_ENA_MASK 0x0080 /* ISRC1_DEC3_ENA */
+#define WM5100_ISRC1_DEC3_ENA_SHIFT 7 /* ISRC1_DEC3_ENA */
+#define WM5100_ISRC1_DEC3_ENA_WIDTH 1 /* ISRC1_DEC3_ENA */
+#define WM5100_ISRC1_DEC4_ENA 0x0040 /* ISRC1_DEC4_ENA */
+#define WM5100_ISRC1_DEC4_ENA_MASK 0x0040 /* ISRC1_DEC4_ENA */
+#define WM5100_ISRC1_DEC4_ENA_SHIFT 6 /* ISRC1_DEC4_ENA */
+#define WM5100_ISRC1_DEC4_ENA_WIDTH 1 /* ISRC1_DEC4_ENA */
+#define WM5100_ISRC1_NOTCH_ENA 0x0001 /* ISRC1_NOTCH_ENA */
+#define WM5100_ISRC1_NOTCH_ENA_MASK 0x0001 /* ISRC1_NOTCH_ENA */
+#define WM5100_ISRC1_NOTCH_ENA_SHIFT 0 /* ISRC1_NOTCH_ENA */
+#define WM5100_ISRC1_NOTCH_ENA_WIDTH 1 /* ISRC1_NOTCH_ENA */
+
+/*
+ * R323 (0x143) - ISRC 2 CTRL1
+ */
+#define WM5100_ISRC2_DFS_ENA 0x2000 /* ISRC2_DFS_ENA */
+#define WM5100_ISRC2_DFS_ENA_MASK 0x2000 /* ISRC2_DFS_ENA */
+#define WM5100_ISRC2_DFS_ENA_SHIFT 13 /* ISRC2_DFS_ENA */
+#define WM5100_ISRC2_DFS_ENA_WIDTH 1 /* ISRC2_DFS_ENA */
+#define WM5100_ISRC2_CLK_SEL_MASK 0x0300 /* ISRC2_CLK_SEL - [9:8] */
+#define WM5100_ISRC2_CLK_SEL_SHIFT 8 /* ISRC2_CLK_SEL - [9:8] */
+#define WM5100_ISRC2_CLK_SEL_WIDTH 2 /* ISRC2_CLK_SEL - [9:8] */
+#define WM5100_ISRC2_FSH_MASK 0x000C /* ISRC2_FSH - [3:2] */
+#define WM5100_ISRC2_FSH_SHIFT 2 /* ISRC2_FSH - [3:2] */
+#define WM5100_ISRC2_FSH_WIDTH 2 /* ISRC2_FSH - [3:2] */
+#define WM5100_ISRC2_FSL_MASK 0x0003 /* ISRC2_FSL - [1:0] */
+#define WM5100_ISRC2_FSL_SHIFT 0 /* ISRC2_FSL - [1:0] */
+#define WM5100_ISRC2_FSL_WIDTH 2 /* ISRC2_FSL - [1:0] */
+
+/*
+ * R324 (0x144) - ISRC 2 CTRL 2
+ */
+#define WM5100_ISRC2_INT1_ENA 0x8000 /* ISRC2_INT1_ENA */
+#define WM5100_ISRC2_INT1_ENA_MASK 0x8000 /* ISRC2_INT1_ENA */
+#define WM5100_ISRC2_INT1_ENA_SHIFT 15 /* ISRC2_INT1_ENA */
+#define WM5100_ISRC2_INT1_ENA_WIDTH 1 /* ISRC2_INT1_ENA */
+#define WM5100_ISRC2_INT2_ENA 0x4000 /* ISRC2_INT2_ENA */
+#define WM5100_ISRC2_INT2_ENA_MASK 0x4000 /* ISRC2_INT2_ENA */
+#define WM5100_ISRC2_INT2_ENA_SHIFT 14 /* ISRC2_INT2_ENA */
+#define WM5100_ISRC2_INT2_ENA_WIDTH 1 /* ISRC2_INT2_ENA */
+#define WM5100_ISRC2_INT3_ENA 0x2000 /* ISRC2_INT3_ENA */
+#define WM5100_ISRC2_INT3_ENA_MASK 0x2000 /* ISRC2_INT3_ENA */
+#define WM5100_ISRC2_INT3_ENA_SHIFT 13 /* ISRC2_INT3_ENA */
+#define WM5100_ISRC2_INT3_ENA_WIDTH 1 /* ISRC2_INT3_ENA */
+#define WM5100_ISRC2_INT4_ENA 0x1000 /* ISRC2_INT4_ENA */
+#define WM5100_ISRC2_INT4_ENA_MASK 0x1000 /* ISRC2_INT4_ENA */
+#define WM5100_ISRC2_INT4_ENA_SHIFT 12 /* ISRC2_INT4_ENA */
+#define WM5100_ISRC2_INT4_ENA_WIDTH 1 /* ISRC2_INT4_ENA */
+#define WM5100_ISRC2_DEC1_ENA 0x0200 /* ISRC2_DEC1_ENA */
+#define WM5100_ISRC2_DEC1_ENA_MASK 0x0200 /* ISRC2_DEC1_ENA */
+#define WM5100_ISRC2_DEC1_ENA_SHIFT 9 /* ISRC2_DEC1_ENA */
+#define WM5100_ISRC2_DEC1_ENA_WIDTH 1 /* ISRC2_DEC1_ENA */
+#define WM5100_ISRC2_DEC2_ENA 0x0100 /* ISRC2_DEC2_ENA */
+#define WM5100_ISRC2_DEC2_ENA_MASK 0x0100 /* ISRC2_DEC2_ENA */
+#define WM5100_ISRC2_DEC2_ENA_SHIFT 8 /* ISRC2_DEC2_ENA */
+#define WM5100_ISRC2_DEC2_ENA_WIDTH 1 /* ISRC2_DEC2_ENA */
+#define WM5100_ISRC2_DEC3_ENA 0x0080 /* ISRC2_DEC3_ENA */
+#define WM5100_ISRC2_DEC3_ENA_MASK 0x0080 /* ISRC2_DEC3_ENA */
+#define WM5100_ISRC2_DEC3_ENA_SHIFT 7 /* ISRC2_DEC3_ENA */
+#define WM5100_ISRC2_DEC3_ENA_WIDTH 1 /* ISRC2_DEC3_ENA */
+#define WM5100_ISRC2_DEC4_ENA 0x0040 /* ISRC2_DEC4_ENA */
+#define WM5100_ISRC2_DEC4_ENA_MASK 0x0040 /* ISRC2_DEC4_ENA */
+#define WM5100_ISRC2_DEC4_ENA_SHIFT 6 /* ISRC2_DEC4_ENA */
+#define WM5100_ISRC2_DEC4_ENA_WIDTH 1 /* ISRC2_DEC4_ENA */
+#define WM5100_ISRC2_NOTCH_ENA 0x0001 /* ISRC2_NOTCH_ENA */
+#define WM5100_ISRC2_NOTCH_ENA_MASK 0x0001 /* ISRC2_NOTCH_ENA */
+#define WM5100_ISRC2_NOTCH_ENA_SHIFT 0 /* ISRC2_NOTCH_ENA */
+#define WM5100_ISRC2_NOTCH_ENA_WIDTH 1 /* ISRC2_NOTCH_ENA */
+
+/*
+ * R386 (0x182) - FLL1 Control 1
+ */
+#define WM5100_FLL1_ENA 0x0001 /* FLL1_ENA */
+#define WM5100_FLL1_ENA_MASK 0x0001 /* FLL1_ENA */
+#define WM5100_FLL1_ENA_SHIFT 0 /* FLL1_ENA */
+#define WM5100_FLL1_ENA_WIDTH 1 /* FLL1_ENA */
+
+/*
+ * R387 (0x183) - FLL1 Control 2
+ */
+#define WM5100_FLL1_OUTDIV_MASK 0x3F00 /* FLL1_OUTDIV - [13:8] */
+#define WM5100_FLL1_OUTDIV_SHIFT 8 /* FLL1_OUTDIV - [13:8] */
+#define WM5100_FLL1_OUTDIV_WIDTH 6 /* FLL1_OUTDIV - [13:8] */
+#define WM5100_FLL1_FRATIO_MASK 0x0007 /* FLL1_FRATIO - [2:0] */
+#define WM5100_FLL1_FRATIO_SHIFT 0 /* FLL1_FRATIO - [2:0] */
+#define WM5100_FLL1_FRATIO_WIDTH 3 /* FLL1_FRATIO - [2:0] */
+
+/*
+ * R388 (0x184) - FLL1 Control 3
+ */
+#define WM5100_FLL1_THETA_MASK 0xFFFF /* FLL1_THETA - [15:0] */
+#define WM5100_FLL1_THETA_SHIFT 0 /* FLL1_THETA - [15:0] */
+#define WM5100_FLL1_THETA_WIDTH 16 /* FLL1_THETA - [15:0] */
+
+/*
+ * R390 (0x186) - FLL1 Control 5
+ */
+#define WM5100_FLL1_N_MASK 0x03FF /* FLL1_N - [9:0] */
+#define WM5100_FLL1_N_SHIFT 0 /* FLL1_N - [9:0] */
+#define WM5100_FLL1_N_WIDTH 10 /* FLL1_N - [9:0] */
+
+/*
+ * R391 (0x187) - FLL1 Control 6
+ */
+#define WM5100_FLL1_REFCLK_DIV_MASK 0x00C0 /* FLL1_REFCLK_DIV - [7:6] */
+#define WM5100_FLL1_REFCLK_DIV_SHIFT 6 /* FLL1_REFCLK_DIV - [7:6] */
+#define WM5100_FLL1_REFCLK_DIV_WIDTH 2 /* FLL1_REFCLK_DIV - [7:6] */
+#define WM5100_FLL1_REFCLK_SRC_MASK 0x000F /* FLL1_REFCLK_SRC - [3:0] */
+#define WM5100_FLL1_REFCLK_SRC_SHIFT 0 /* FLL1_REFCLK_SRC - [3:0] */
+#define WM5100_FLL1_REFCLK_SRC_WIDTH 4 /* FLL1_REFCLK_SRC - [3:0] */
+
+/*
+ * R392 (0x188) - FLL1 EFS 1
+ */
+#define WM5100_FLL1_LAMBDA_MASK 0xFFFF /* FLL1_LAMBDA - [15:0] */
+#define WM5100_FLL1_LAMBDA_SHIFT 0 /* FLL1_LAMBDA - [15:0] */
+#define WM5100_FLL1_LAMBDA_WIDTH 16 /* FLL1_LAMBDA - [15:0] */
+
+/*
+ * R418 (0x1A2) - FLL2 Control 1
+ */
+#define WM5100_FLL2_ENA 0x0001 /* FLL2_ENA */
+#define WM5100_FLL2_ENA_MASK 0x0001 /* FLL2_ENA */
+#define WM5100_FLL2_ENA_SHIFT 0 /* FLL2_ENA */
+#define WM5100_FLL2_ENA_WIDTH 1 /* FLL2_ENA */
+
+/*
+ * R419 (0x1A3) - FLL2 Control 2
+ */
+#define WM5100_FLL2_OUTDIV_MASK 0x3F00 /* FLL2_OUTDIV - [13:8] */
+#define WM5100_FLL2_OUTDIV_SHIFT 8 /* FLL2_OUTDIV - [13:8] */
+#define WM5100_FLL2_OUTDIV_WIDTH 6 /* FLL2_OUTDIV - [13:8] */
+#define WM5100_FLL2_FRATIO_MASK 0x0007 /* FLL2_FRATIO - [2:0] */
+#define WM5100_FLL2_FRATIO_SHIFT 0 /* FLL2_FRATIO - [2:0] */
+#define WM5100_FLL2_FRATIO_WIDTH 3 /* FLL2_FRATIO - [2:0] */
+
+/*
+ * R420 (0x1A4) - FLL2 Control 3
+ */
+#define WM5100_FLL2_THETA_MASK 0xFFFF /* FLL2_THETA - [15:0] */
+#define WM5100_FLL2_THETA_SHIFT 0 /* FLL2_THETA - [15:0] */
+#define WM5100_FLL2_THETA_WIDTH 16 /* FLL2_THETA - [15:0] */
+
+/*
+ * R422 (0x1A6) - FLL2 Control 5
+ */
+#define WM5100_FLL2_N_MASK 0x03FF /* FLL2_N - [9:0] */
+#define WM5100_FLL2_N_SHIFT 0 /* FLL2_N - [9:0] */
+#define WM5100_FLL2_N_WIDTH 10 /* FLL2_N - [9:0] */
+
+/*
+ * R423 (0x1A7) - FLL2 Control 6
+ */
+#define WM5100_FLL2_REFCLK_DIV_MASK 0x00C0 /* FLL2_REFCLK_DIV - [7:6] */
+#define WM5100_FLL2_REFCLK_DIV_SHIFT 6 /* FLL2_REFCLK_DIV - [7:6] */
+#define WM5100_FLL2_REFCLK_DIV_WIDTH 2 /* FLL2_REFCLK_DIV - [7:6] */
+#define WM5100_FLL2_REFCLK_SRC_MASK 0x000F /* FLL2_REFCLK_SRC - [3:0] */
+#define WM5100_FLL2_REFCLK_SRC_SHIFT 0 /* FLL2_REFCLK_SRC - [3:0] */
+#define WM5100_FLL2_REFCLK_SRC_WIDTH 4 /* FLL2_REFCLK_SRC - [3:0] */
+
+/*
+ * R424 (0x1A8) - FLL2 EFS 1
+ */
+#define WM5100_FLL2_LAMBDA_MASK 0xFFFF /* FLL2_LAMBDA - [15:0] */
+#define WM5100_FLL2_LAMBDA_SHIFT 0 /* FLL2_LAMBDA - [15:0] */
+#define WM5100_FLL2_LAMBDA_WIDTH 16 /* FLL2_LAMBDA - [15:0] */
+
+/*
+ * R512 (0x200) - Mic Charge Pump 1
+ */
+#define WM5100_CP2_BYPASS 0x0020 /* CP2_BYPASS */
+#define WM5100_CP2_BYPASS_MASK 0x0020 /* CP2_BYPASS */
+#define WM5100_CP2_BYPASS_SHIFT 5 /* CP2_BYPASS */
+#define WM5100_CP2_BYPASS_WIDTH 1 /* CP2_BYPASS */
+#define WM5100_CP2_ENA 0x0001 /* CP2_ENA */
+#define WM5100_CP2_ENA_MASK 0x0001 /* CP2_ENA */
+#define WM5100_CP2_ENA_SHIFT 0 /* CP2_ENA */
+#define WM5100_CP2_ENA_WIDTH 1 /* CP2_ENA */
+
+/*
+ * R513 (0x201) - Mic Charge Pump 2
+ */
+#define WM5100_LDO2_VSEL_MASK 0xF800 /* LDO2_VSEL - [15:11] */
+#define WM5100_LDO2_VSEL_SHIFT 11 /* LDO2_VSEL - [15:11] */
+#define WM5100_LDO2_VSEL_WIDTH 5 /* LDO2_VSEL - [15:11] */
+
+/*
+ * R514 (0x202) - HP Charge Pump 1
+ */
+#define WM5100_CP1_ENA 0x0001 /* CP1_ENA */
+#define WM5100_CP1_ENA_MASK 0x0001 /* CP1_ENA */
+#define WM5100_CP1_ENA_SHIFT 0 /* CP1_ENA */
+#define WM5100_CP1_ENA_WIDTH 1 /* CP1_ENA */
+
+/*
+ * R529 (0x211) - LDO1 Control
+ */
+#define WM5100_LDO1_BYPASS 0x0002 /* LDO1_BYPASS */
+#define WM5100_LDO1_BYPASS_MASK 0x0002 /* LDO1_BYPASS */
+#define WM5100_LDO1_BYPASS_SHIFT 1 /* LDO1_BYPASS */
+#define WM5100_LDO1_BYPASS_WIDTH 1 /* LDO1_BYPASS */
+
+/*
+ * R533 (0x215) - Mic Bias Ctrl 1
+ */
+#define WM5100_MICB1_DISCH 0x0040 /* MICB1_DISCH */
+#define WM5100_MICB1_DISCH_MASK 0x0040 /* MICB1_DISCH */
+#define WM5100_MICB1_DISCH_SHIFT 6 /* MICB1_DISCH */
+#define WM5100_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */
+#define WM5100_MICB1_RATE 0x0020 /* MICB1_RATE */
+#define WM5100_MICB1_RATE_MASK 0x0020 /* MICB1_RATE */
+#define WM5100_MICB1_RATE_SHIFT 5 /* MICB1_RATE */
+#define WM5100_MICB1_RATE_WIDTH 1 /* MICB1_RATE */
+#define WM5100_MICB1_LVL_MASK 0x001C /* MICB1_LVL - [4:2] */
+#define WM5100_MICB1_LVL_SHIFT 2 /* MICB1_LVL - [4:2] */
+#define WM5100_MICB1_LVL_WIDTH 3 /* MICB1_LVL - [4:2] */
+#define WM5100_MICB1_BYPASS 0x0002 /* MICB1_BYPASS */
+#define WM5100_MICB1_BYPASS_MASK 0x0002 /* MICB1_BYPASS */
+#define WM5100_MICB1_BYPASS_SHIFT 1 /* MICB1_BYPASS */
+#define WM5100_MICB1_BYPASS_WIDTH 1 /* MICB1_BYPASS */
+#define WM5100_MICB1_ENA 0x0001 /* MICB1_ENA */
+#define WM5100_MICB1_ENA_MASK 0x0001 /* MICB1_ENA */
+#define WM5100_MICB1_ENA_SHIFT 0 /* MICB1_ENA */
+#define WM5100_MICB1_ENA_WIDTH 1 /* MICB1_ENA */
+
+/*
+ * R534 (0x216) - Mic Bias Ctrl 2
+ */
+#define WM5100_MICB2_DISCH 0x0040 /* MICB2_DISCH */
+#define WM5100_MICB2_DISCH_MASK 0x0040 /* MICB2_DISCH */
+#define WM5100_MICB2_DISCH_SHIFT 6 /* MICB2_DISCH */
+#define WM5100_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */
+#define WM5100_MICB2_RATE 0x0020 /* MICB2_RATE */
+#define WM5100_MICB2_RATE_MASK 0x0020 /* MICB2_RATE */
+#define WM5100_MICB2_RATE_SHIFT 5 /* MICB2_RATE */
+#define WM5100_MICB2_RATE_WIDTH 1 /* MICB2_RATE */
+#define WM5100_MICB2_LVL_MASK 0x001C /* MICB2_LVL - [4:2] */
+#define WM5100_MICB2_LVL_SHIFT 2 /* MICB2_LVL - [4:2] */
+#define WM5100_MICB2_LVL_WIDTH 3 /* MICB2_LVL - [4:2] */
+#define WM5100_MICB2_BYPASS 0x0002 /* MICB2_BYPASS */
+#define WM5100_MICB2_BYPASS_MASK 0x0002 /* MICB2_BYPASS */
+#define WM5100_MICB2_BYPASS_SHIFT 1 /* MICB2_BYPASS */
+#define WM5100_MICB2_BYPASS_WIDTH 1 /* MICB2_BYPASS */
+#define WM5100_MICB2_ENA 0x0001 /* MICB2_ENA */
+#define WM5100_MICB2_ENA_MASK 0x0001 /* MICB2_ENA */
+#define WM5100_MICB2_ENA_SHIFT 0 /* MICB2_ENA */
+#define WM5100_MICB2_ENA_WIDTH 1 /* MICB2_ENA */
+
+/*
+ * R535 (0x217) - Mic Bias Ctrl 3
+ */
+#define WM5100_MICB3_DISCH 0x0040 /* MICB3_DISCH */
+#define WM5100_MICB3_DISCH_MASK 0x0040 /* MICB3_DISCH */
+#define WM5100_MICB3_DISCH_SHIFT 6 /* MICB3_DISCH */
+#define WM5100_MICB3_DISCH_WIDTH 1 /* MICB3_DISCH */
+#define WM5100_MICB3_RATE 0x0020 /* MICB3_RATE */
+#define WM5100_MICB3_RATE_MASK 0x0020 /* MICB3_RATE */
+#define WM5100_MICB3_RATE_SHIFT 5 /* MICB3_RATE */
+#define WM5100_MICB3_RATE_WIDTH 1 /* MICB3_RATE */
+#define WM5100_MICB3_LVL_MASK 0x001C /* MICB3_LVL - [4:2] */
+#define WM5100_MICB3_LVL_SHIFT 2 /* MICB3_LVL - [4:2] */
+#define WM5100_MICB3_LVL_WIDTH 3 /* MICB3_LVL - [4:2] */
+#define WM5100_MICB3_BYPASS 0x0002 /* MICB3_BYPASS */
+#define WM5100_MICB3_BYPASS_MASK 0x0002 /* MICB3_BYPASS */
+#define WM5100_MICB3_BYPASS_SHIFT 1 /* MICB3_BYPASS */
+#define WM5100_MICB3_BYPASS_WIDTH 1 /* MICB3_BYPASS */
+#define WM5100_MICB3_ENA 0x0001 /* MICB3_ENA */
+#define WM5100_MICB3_ENA_MASK 0x0001 /* MICB3_ENA */
+#define WM5100_MICB3_ENA_SHIFT 0 /* MICB3_ENA */
+#define WM5100_MICB3_ENA_WIDTH 1 /* MICB3_ENA */
+
+/*
+ * R640 (0x280) - Accessory Detect Mode 1
+ */
+#define WM5100_ACCDET_BIAS_SRC_MASK 0xC000 /* ACCDET_BIAS_SRC - [15:14] */
+#define WM5100_ACCDET_BIAS_SRC_SHIFT 14 /* ACCDET_BIAS_SRC - [15:14] */
+#define WM5100_ACCDET_BIAS_SRC_WIDTH 2 /* ACCDET_BIAS_SRC - [15:14] */
+#define WM5100_ACCDET_SRC 0x2000 /* ACCDET_SRC */
+#define WM5100_ACCDET_SRC_MASK 0x2000 /* ACCDET_SRC */
+#define WM5100_ACCDET_SRC_SHIFT 13 /* ACCDET_SRC */
+#define WM5100_ACCDET_SRC_WIDTH 1 /* ACCDET_SRC */
+#define WM5100_ACCDET_MODE_MASK 0x0003 /* ACCDET_MODE - [1:0] */
+#define WM5100_ACCDET_MODE_SHIFT 0 /* ACCDET_MODE - [1:0] */
+#define WM5100_ACCDET_MODE_WIDTH 2 /* ACCDET_MODE - [1:0] */
+
+/*
+ * R648 (0x288) - Headphone Detect 1
+ */
+#define WM5100_HP_HOLDTIME_MASK 0x00E0 /* HP_HOLDTIME - [7:5] */
+#define WM5100_HP_HOLDTIME_SHIFT 5 /* HP_HOLDTIME - [7:5] */
+#define WM5100_HP_HOLDTIME_WIDTH 3 /* HP_HOLDTIME - [7:5] */
+#define WM5100_HP_CLK_DIV_MASK 0x0018 /* HP_CLK_DIV - [4:3] */
+#define WM5100_HP_CLK_DIV_SHIFT 3 /* HP_CLK_DIV - [4:3] */
+#define WM5100_HP_CLK_DIV_WIDTH 2 /* HP_CLK_DIV - [4:3] */
+#define WM5100_HP_STEP_SIZE 0x0002 /* HP_STEP_SIZE */
+#define WM5100_HP_STEP_SIZE_MASK 0x0002 /* HP_STEP_SIZE */
+#define WM5100_HP_STEP_SIZE_SHIFT 1 /* HP_STEP_SIZE */
+#define WM5100_HP_STEP_SIZE_WIDTH 1 /* HP_STEP_SIZE */
+#define WM5100_HP_POLL 0x0001 /* HP_POLL */
+#define WM5100_HP_POLL_MASK 0x0001 /* HP_POLL */
+#define WM5100_HP_POLL_SHIFT 0 /* HP_POLL */
+#define WM5100_HP_POLL_WIDTH 1 /* HP_POLL */
+
+/*
+ * R649 (0x289) - Headphone Detect 2
+ */
+#define WM5100_HP_DONE 0x0080 /* HP_DONE */
+#define WM5100_HP_DONE_MASK 0x0080 /* HP_DONE */
+#define WM5100_HP_DONE_SHIFT 7 /* HP_DONE */
+#define WM5100_HP_DONE_WIDTH 1 /* HP_DONE */
+#define WM5100_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */
+#define WM5100_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */
+#define WM5100_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */
+
+/*
+ * R656 (0x290) - Mic Detect 1
+ */
+#define WM5100_ACCDET_BIAS_STARTTIME_MASK 0xF000 /* ACCDET_BIAS_STARTTIME - [15:12] */
+#define WM5100_ACCDET_BIAS_STARTTIME_SHIFT 12 /* ACCDET_BIAS_STARTTIME - [15:12] */
+#define WM5100_ACCDET_BIAS_STARTTIME_WIDTH 4 /* ACCDET_BIAS_STARTTIME - [15:12] */
+#define WM5100_ACCDET_RATE_MASK 0x0F00 /* ACCDET_RATE - [11:8] */
+#define WM5100_ACCDET_RATE_SHIFT 8 /* ACCDET_RATE - [11:8] */
+#define WM5100_ACCDET_RATE_WIDTH 4 /* ACCDET_RATE - [11:8] */
+#define WM5100_ACCDET_DBTIME 0x0002 /* ACCDET_DBTIME */
+#define WM5100_ACCDET_DBTIME_MASK 0x0002 /* ACCDET_DBTIME */
+#define WM5100_ACCDET_DBTIME_SHIFT 1 /* ACCDET_DBTIME */
+#define WM5100_ACCDET_DBTIME_WIDTH 1 /* ACCDET_DBTIME */
+#define WM5100_ACCDET_ENA 0x0001 /* ACCDET_ENA */
+#define WM5100_ACCDET_ENA_MASK 0x0001 /* ACCDET_ENA */
+#define WM5100_ACCDET_ENA_SHIFT 0 /* ACCDET_ENA */
+#define WM5100_ACCDET_ENA_WIDTH 1 /* ACCDET_ENA */
+
+/*
+ * R657 (0x291) - Mic Detect 2
+ */
+#define WM5100_ACCDET_LVL_SEL_MASK 0x00FF /* ACCDET_LVL_SEL - [7:0] */
+#define WM5100_ACCDET_LVL_SEL_SHIFT 0 /* ACCDET_LVL_SEL - [7:0] */
+#define WM5100_ACCDET_LVL_SEL_WIDTH 8 /* ACCDET_LVL_SEL - [7:0] */
+
+/*
+ * R658 (0x292) - Mic Detect 3
+ */
+#define WM5100_ACCDET_LVL_MASK 0x07FC /* ACCDET_LVL - [10:2] */
+#define WM5100_ACCDET_LVL_SHIFT 2 /* ACCDET_LVL - [10:2] */
+#define WM5100_ACCDET_LVL_WIDTH 9 /* ACCDET_LVL - [10:2] */
+#define WM5100_ACCDET_VALID 0x0002 /* ACCDET_VALID */
+#define WM5100_ACCDET_VALID_MASK 0x0002 /* ACCDET_VALID */
+#define WM5100_ACCDET_VALID_SHIFT 1 /* ACCDET_VALID */
+#define WM5100_ACCDET_VALID_WIDTH 1 /* ACCDET_VALID */
+#define WM5100_ACCDET_STS 0x0001 /* ACCDET_STS */
+#define WM5100_ACCDET_STS_MASK 0x0001 /* ACCDET_STS */
+#define WM5100_ACCDET_STS_SHIFT 0 /* ACCDET_STS */
+#define WM5100_ACCDET_STS_WIDTH 1 /* ACCDET_STS */
+
+/*
+ * R699 (0x2BB) - Misc Control
+ */
+#define WM5100_HPCOM_SRC 0x200 /* HPCOM_SRC */
+#define WM5100_HPCOM_SRC_SHIFT 9 /* HPCOM_SRC */
+
+/*
+ * R769 (0x301) - Input Enables
+ */
+#define WM5100_IN4L_ENA 0x0080 /* IN4L_ENA */
+#define WM5100_IN4L_ENA_MASK 0x0080 /* IN4L_ENA */
+#define WM5100_IN4L_ENA_SHIFT 7 /* IN4L_ENA */
+#define WM5100_IN4L_ENA_WIDTH 1 /* IN4L_ENA */
+#define WM5100_IN4R_ENA 0x0040 /* IN4R_ENA */
+#define WM5100_IN4R_ENA_MASK 0x0040 /* IN4R_ENA */
+#define WM5100_IN4R_ENA_SHIFT 6 /* IN4R_ENA */
+#define WM5100_IN4R_ENA_WIDTH 1 /* IN4R_ENA */
+#define WM5100_IN3L_ENA 0x0020 /* IN3L_ENA */
+#define WM5100_IN3L_ENA_MASK 0x0020 /* IN3L_ENA */
+#define WM5100_IN3L_ENA_SHIFT 5 /* IN3L_ENA */
+#define WM5100_IN3L_ENA_WIDTH 1 /* IN3L_ENA */
+#define WM5100_IN3R_ENA 0x0010 /* IN3R_ENA */
+#define WM5100_IN3R_ENA_MASK 0x0010 /* IN3R_ENA */
+#define WM5100_IN3R_ENA_SHIFT 4 /* IN3R_ENA */
+#define WM5100_IN3R_ENA_WIDTH 1 /* IN3R_ENA */
+#define WM5100_IN2L_ENA 0x0008 /* IN2L_ENA */
+#define WM5100_IN2L_ENA_MASK 0x0008 /* IN2L_ENA */
+#define WM5100_IN2L_ENA_SHIFT 3 /* IN2L_ENA */
+#define WM5100_IN2L_ENA_WIDTH 1 /* IN2L_ENA */
+#define WM5100_IN2R_ENA 0x0004 /* IN2R_ENA */
+#define WM5100_IN2R_ENA_MASK 0x0004 /* IN2R_ENA */
+#define WM5100_IN2R_ENA_SHIFT 2 /* IN2R_ENA */
+#define WM5100_IN2R_ENA_WIDTH 1 /* IN2R_ENA */
+#define WM5100_IN1L_ENA 0x0002 /* IN1L_ENA */
+#define WM5100_IN1L_ENA_MASK 0x0002 /* IN1L_ENA */
+#define WM5100_IN1L_ENA_SHIFT 1 /* IN1L_ENA */
+#define WM5100_IN1L_ENA_WIDTH 1 /* IN1L_ENA */
+#define WM5100_IN1R_ENA 0x0001 /* IN1R_ENA */
+#define WM5100_IN1R_ENA_MASK 0x0001 /* IN1R_ENA */
+#define WM5100_IN1R_ENA_SHIFT 0 /* IN1R_ENA */
+#define WM5100_IN1R_ENA_WIDTH 1 /* IN1R_ENA */
+
+/*
+ * R770 (0x302) - Input Enables Status
+ */
+#define WM5100_IN4L_ENA_STS 0x0080 /* IN4L_ENA_STS */
+#define WM5100_IN4L_ENA_STS_MASK 0x0080 /* IN4L_ENA_STS */
+#define WM5100_IN4L_ENA_STS_SHIFT 7 /* IN4L_ENA_STS */
+#define WM5100_IN4L_ENA_STS_WIDTH 1 /* IN4L_ENA_STS */
+#define WM5100_IN4R_ENA_STS 0x0040 /* IN4R_ENA_STS */
+#define WM5100_IN4R_ENA_STS_MASK 0x0040 /* IN4R_ENA_STS */
+#define WM5100_IN4R_ENA_STS_SHIFT 6 /* IN4R_ENA_STS */
+#define WM5100_IN4R_ENA_STS_WIDTH 1 /* IN4R_ENA_STS */
+#define WM5100_IN3L_ENA_STS 0x0020 /* IN3L_ENA_STS */
+#define WM5100_IN3L_ENA_STS_MASK 0x0020 /* IN3L_ENA_STS */
+#define WM5100_IN3L_ENA_STS_SHIFT 5 /* IN3L_ENA_STS */
+#define WM5100_IN3L_ENA_STS_WIDTH 1 /* IN3L_ENA_STS */
+#define WM5100_IN3R_ENA_STS 0x0010 /* IN3R_ENA_STS */
+#define WM5100_IN3R_ENA_STS_MASK 0x0010 /* IN3R_ENA_STS */
+#define WM5100_IN3R_ENA_STS_SHIFT 4 /* IN3R_ENA_STS */
+#define WM5100_IN3R_ENA_STS_WIDTH 1 /* IN3R_ENA_STS */
+#define WM5100_IN2L_ENA_STS 0x0008 /* IN2L_ENA_STS */
+#define WM5100_IN2L_ENA_STS_MASK 0x0008 /* IN2L_ENA_STS */
+#define WM5100_IN2L_ENA_STS_SHIFT 3 /* IN2L_ENA_STS */
+#define WM5100_IN2L_ENA_STS_WIDTH 1 /* IN2L_ENA_STS */
+#define WM5100_IN2R_ENA_STS 0x0004 /* IN2R_ENA_STS */
+#define WM5100_IN2R_ENA_STS_MASK 0x0004 /* IN2R_ENA_STS */
+#define WM5100_IN2R_ENA_STS_SHIFT 2 /* IN2R_ENA_STS */
+#define WM5100_IN2R_ENA_STS_WIDTH 1 /* IN2R_ENA_STS */
+#define WM5100_IN1L_ENA_STS 0x0002 /* IN1L_ENA_STS */
+#define WM5100_IN1L_ENA_STS_MASK 0x0002 /* IN1L_ENA_STS */
+#define WM5100_IN1L_ENA_STS_SHIFT 1 /* IN1L_ENA_STS */
+#define WM5100_IN1L_ENA_STS_WIDTH 1 /* IN1L_ENA_STS */
+#define WM5100_IN1R_ENA_STS 0x0001 /* IN1R_ENA_STS */
+#define WM5100_IN1R_ENA_STS_MASK 0x0001 /* IN1R_ENA_STS */
+#define WM5100_IN1R_ENA_STS_SHIFT 0 /* IN1R_ENA_STS */
+#define WM5100_IN1R_ENA_STS_WIDTH 1 /* IN1R_ENA_STS */
+
+/*
+ * R784 (0x310) - IN1L Control
+ */
+#define WM5100_IN_RATE_MASK 0xC000 /* IN_RATE - [15:14] */
+#define WM5100_IN_RATE_SHIFT 14 /* IN_RATE - [15:14] */
+#define WM5100_IN_RATE_WIDTH 2 /* IN_RATE - [15:14] */
+#define WM5100_IN1_OSR 0x2000 /* IN1_OSR */
+#define WM5100_IN1_OSR_MASK 0x2000 /* IN1_OSR */
+#define WM5100_IN1_OSR_SHIFT 13 /* IN1_OSR */
+#define WM5100_IN1_OSR_WIDTH 1 /* IN1_OSR */
+#define WM5100_IN1_DMIC_SUP_MASK 0x1800 /* IN1_DMIC_SUP - [12:11] */
+#define WM5100_IN1_DMIC_SUP_SHIFT 11 /* IN1_DMIC_SUP - [12:11] */
+#define WM5100_IN1_DMIC_SUP_WIDTH 2 /* IN1_DMIC_SUP - [12:11] */
+#define WM5100_IN1_MODE_MASK 0x0600 /* IN1_MODE - [10:9] */
+#define WM5100_IN1_MODE_SHIFT 9 /* IN1_MODE - [10:9] */
+#define WM5100_IN1_MODE_WIDTH 2 /* IN1_MODE - [10:9] */
+#define WM5100_IN1L_PGA_VOL_MASK 0x00FE /* IN1L_PGA_VOL - [7:1] */
+#define WM5100_IN1L_PGA_VOL_SHIFT 1 /* IN1L_PGA_VOL - [7:1] */
+#define WM5100_IN1L_PGA_VOL_WIDTH 7 /* IN1L_PGA_VOL - [7:1] */
+
+/*
+ * R785 (0x311) - IN1R Control
+ */
+#define WM5100_IN1R_PGA_VOL_MASK 0x00FE /* IN1R_PGA_VOL - [7:1] */
+#define WM5100_IN1R_PGA_VOL_SHIFT 1 /* IN1R_PGA_VOL - [7:1] */
+#define WM5100_IN1R_PGA_VOL_WIDTH 7 /* IN1R_PGA_VOL - [7:1] */
+
+/*
+ * R786 (0x312) - IN2L Control
+ */
+#define WM5100_IN2_OSR 0x2000 /* IN2_OSR */
+#define WM5100_IN2_OSR_MASK 0x2000 /* IN2_OSR */
+#define WM5100_IN2_OSR_SHIFT 13 /* IN2_OSR */
+#define WM5100_IN2_OSR_WIDTH 1 /* IN2_OSR */
+#define WM5100_IN2_DMIC_SUP_MASK 0x1800 /* IN2_DMIC_SUP - [12:11] */
+#define WM5100_IN2_DMIC_SUP_SHIFT 11 /* IN2_DMIC_SUP - [12:11] */
+#define WM5100_IN2_DMIC_SUP_WIDTH 2 /* IN2_DMIC_SUP - [12:11] */
+#define WM5100_IN2_MODE_MASK 0x0600 /* IN2_MODE - [10:9] */
+#define WM5100_IN2_MODE_SHIFT 9 /* IN2_MODE - [10:9] */
+#define WM5100_IN2_MODE_WIDTH 2 /* IN2_MODE - [10:9] */
+#define WM5100_IN2L_PGA_VOL_MASK 0x00FE /* IN2L_PGA_VOL - [7:1] */
+#define WM5100_IN2L_PGA_VOL_SHIFT 1 /* IN2L_PGA_VOL - [7:1] */
+#define WM5100_IN2L_PGA_VOL_WIDTH 7 /* IN2L_PGA_VOL - [7:1] */
+
+/*
+ * R787 (0x313) - IN2R Control
+ */
+#define WM5100_IN2R_PGA_VOL_MASK 0x00FE /* IN2R_PGA_VOL - [7:1] */
+#define WM5100_IN2R_PGA_VOL_SHIFT 1 /* IN2R_PGA_VOL - [7:1] */
+#define WM5100_IN2R_PGA_VOL_WIDTH 7 /* IN2R_PGA_VOL - [7:1] */
+
+/*
+ * R788 (0x314) - IN3L Control
+ */
+#define WM5100_IN3_OSR 0x2000 /* IN3_OSR */
+#define WM5100_IN3_OSR_MASK 0x2000 /* IN3_OSR */
+#define WM5100_IN3_OSR_SHIFT 13 /* IN3_OSR */
+#define WM5100_IN3_OSR_WIDTH 1 /* IN3_OSR */
+#define WM5100_IN3_DMIC_SUP_MASK 0x1800 /* IN3_DMIC_SUP - [12:11] */
+#define WM5100_IN3_DMIC_SUP_SHIFT 11 /* IN3_DMIC_SUP - [12:11] */
+#define WM5100_IN3_DMIC_SUP_WIDTH 2 /* IN3_DMIC_SUP - [12:11] */
+#define WM5100_IN3_MODE_MASK 0x0600 /* IN3_MODE - [10:9] */
+#define WM5100_IN3_MODE_SHIFT 9 /* IN3_MODE - [10:9] */
+#define WM5100_IN3_MODE_WIDTH 2 /* IN3_MODE - [10:9] */
+#define WM5100_IN3L_PGA_VOL_MASK 0x00FE /* IN3L_PGA_VOL - [7:1] */
+#define WM5100_IN3L_PGA_VOL_SHIFT 1 /* IN3L_PGA_VOL - [7:1] */
+#define WM5100_IN3L_PGA_VOL_WIDTH 7 /* IN3L_PGA_VOL - [7:1] */
+
+/*
+ * R789 (0x315) - IN3R Control
+ */
+#define WM5100_IN3R_PGA_VOL_MASK 0x00FE /* IN3R_PGA_VOL - [7:1] */
+#define WM5100_IN3R_PGA_VOL_SHIFT 1 /* IN3R_PGA_VOL - [7:1] */
+#define WM5100_IN3R_PGA_VOL_WIDTH 7 /* IN3R_PGA_VOL - [7:1] */
+
+/*
+ * R790 (0x316) - IN4L Control
+ */
+#define WM5100_IN4_OSR 0x2000 /* IN4_OSR */
+#define WM5100_IN4_OSR_MASK 0x2000 /* IN4_OSR */
+#define WM5100_IN4_OSR_SHIFT 13 /* IN4_OSR */
+#define WM5100_IN4_OSR_WIDTH 1 /* IN4_OSR */
+#define WM5100_IN4_DMIC_SUP_MASK 0x1800 /* IN4_DMIC_SUP - [12:11] */
+#define WM5100_IN4_DMIC_SUP_SHIFT 11 /* IN4_DMIC_SUP - [12:11] */
+#define WM5100_IN4_DMIC_SUP_WIDTH 2 /* IN4_DMIC_SUP - [12:11] */
+#define WM5100_IN4_MODE_MASK 0x0600 /* IN4_MODE - [10:9] */
+#define WM5100_IN4_MODE_SHIFT 9 /* IN4_MODE - [10:9] */
+#define WM5100_IN4_MODE_WIDTH 2 /* IN4_MODE - [10:9] */
+#define WM5100_IN4L_PGA_VOL_MASK 0x00FE /* IN4L_PGA_VOL - [7:1] */
+#define WM5100_IN4L_PGA_VOL_SHIFT 1 /* IN4L_PGA_VOL - [7:1] */
+#define WM5100_IN4L_PGA_VOL_WIDTH 7 /* IN4L_PGA_VOL - [7:1] */
+
+/*
+ * R791 (0x317) - IN4R Control
+ */
+#define WM5100_IN4R_PGA_VOL_MASK 0x00FE /* IN4R_PGA_VOL - [7:1] */
+#define WM5100_IN4R_PGA_VOL_SHIFT 1 /* IN4R_PGA_VOL - [7:1] */
+#define WM5100_IN4R_PGA_VOL_WIDTH 7 /* IN4R_PGA_VOL - [7:1] */
+
+/*
+ * R792 (0x318) - RXANC_SRC
+ */
+#define WM5100_IN_RXANC_SEL_MASK 0x0007 /* IN_RXANC_SEL - [2:0] */
+#define WM5100_IN_RXANC_SEL_SHIFT 0 /* IN_RXANC_SEL - [2:0] */
+#define WM5100_IN_RXANC_SEL_WIDTH 3 /* IN_RXANC_SEL - [2:0] */
+
+/*
+ * R793 (0x319) - Input Volume Ramp
+ */
+#define WM5100_IN_VD_RAMP_MASK 0x0070 /* IN_VD_RAMP - [6:4] */
+#define WM5100_IN_VD_RAMP_SHIFT 4 /* IN_VD_RAMP - [6:4] */
+#define WM5100_IN_VD_RAMP_WIDTH 3 /* IN_VD_RAMP - [6:4] */
+#define WM5100_IN_VI_RAMP_MASK 0x0007 /* IN_VI_RAMP - [2:0] */
+#define WM5100_IN_VI_RAMP_SHIFT 0 /* IN_VI_RAMP - [2:0] */
+#define WM5100_IN_VI_RAMP_WIDTH 3 /* IN_VI_RAMP - [2:0] */
+
+/*
+ * R800 (0x320) - ADC Digital Volume 1L
+ */
+#define WM5100_IN_VU 0x0200 /* IN_VU */
+#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM5100_IN_VU_SHIFT 9 /* IN_VU */
+#define WM5100_IN_VU_WIDTH 1 /* IN_VU */
+#define WM5100_IN1L_MUTE 0x0100 /* IN1L_MUTE */
+#define WM5100_IN1L_MUTE_MASK 0x0100 /* IN1L_MUTE */
+#define WM5100_IN1L_MUTE_SHIFT 8 /* IN1L_MUTE */
+#define WM5100_IN1L_MUTE_WIDTH 1 /* IN1L_MUTE */
+#define WM5100_IN1L_VOL_MASK 0x00FF /* IN1L_VOL - [7:0] */
+#define WM5100_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [7:0] */
+#define WM5100_IN1L_VOL_WIDTH 8 /* IN1L_VOL - [7:0] */
+
+/*
+ * R801 (0x321) - ADC Digital Volume 1R
+ */
+#define WM5100_IN_VU 0x0200 /* IN_VU */
+#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM5100_IN_VU_SHIFT 9 /* IN_VU */
+#define WM5100_IN_VU_WIDTH 1 /* IN_VU */
+#define WM5100_IN1R_MUTE 0x0100 /* IN1R_MUTE */
+#define WM5100_IN1R_MUTE_MASK 0x0100 /* IN1R_MUTE */
+#define WM5100_IN1R_MUTE_SHIFT 8 /* IN1R_MUTE */
+#define WM5100_IN1R_MUTE_WIDTH 1 /* IN1R_MUTE */
+#define WM5100_IN1R_VOL_MASK 0x00FF /* IN1R_VOL - [7:0] */
+#define WM5100_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [7:0] */
+#define WM5100_IN1R_VOL_WIDTH 8 /* IN1R_VOL - [7:0] */
+
+/*
+ * R802 (0x322) - ADC Digital Volume 2L
+ */
+#define WM5100_IN_VU 0x0200 /* IN_VU */
+#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM5100_IN_VU_SHIFT 9 /* IN_VU */
+#define WM5100_IN_VU_WIDTH 1 /* IN_VU */
+#define WM5100_IN2L_MUTE 0x0100 /* IN2L_MUTE */
+#define WM5100_IN2L_MUTE_MASK 0x0100 /* IN2L_MUTE */
+#define WM5100_IN2L_MUTE_SHIFT 8 /* IN2L_MUTE */
+#define WM5100_IN2L_MUTE_WIDTH 1 /* IN2L_MUTE */
+#define WM5100_IN2L_VOL_MASK 0x00FF /* IN2L_VOL - [7:0] */
+#define WM5100_IN2L_VOL_SHIFT 0 /* IN2L_VOL - [7:0] */
+#define WM5100_IN2L_VOL_WIDTH 8 /* IN2L_VOL - [7:0] */
+
+/*
+ * R803 (0x323) - ADC Digital Volume 2R
+ */
+#define WM5100_IN_VU 0x0200 /* IN_VU */
+#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM5100_IN_VU_SHIFT 9 /* IN_VU */
+#define WM5100_IN_VU_WIDTH 1 /* IN_VU */
+#define WM5100_IN2R_MUTE 0x0100 /* IN2R_MUTE */
+#define WM5100_IN2R_MUTE_MASK 0x0100 /* IN2R_MUTE */
+#define WM5100_IN2R_MUTE_SHIFT 8 /* IN2R_MUTE */
+#define WM5100_IN2R_MUTE_WIDTH 1 /* IN2R_MUTE */
+#define WM5100_IN2R_VOL_MASK 0x00FF /* IN2R_VOL - [7:0] */
+#define WM5100_IN2R_VOL_SHIFT 0 /* IN2R_VOL - [7:0] */
+#define WM5100_IN2R_VOL_WIDTH 8 /* IN2R_VOL - [7:0] */
+
+/*
+ * R804 (0x324) - ADC Digital Volume 3L
+ */
+#define WM5100_IN_VU 0x0200 /* IN_VU */
+#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM5100_IN_VU_SHIFT 9 /* IN_VU */
+#define WM5100_IN_VU_WIDTH 1 /* IN_VU */
+#define WM5100_IN3L_MUTE 0x0100 /* IN3L_MUTE */
+#define WM5100_IN3L_MUTE_MASK 0x0100 /* IN3L_MUTE */
+#define WM5100_IN3L_MUTE_SHIFT 8 /* IN3L_MUTE */
+#define WM5100_IN3L_MUTE_WIDTH 1 /* IN3L_MUTE */
+#define WM5100_IN3L_VOL_MASK 0x00FF /* IN3L_VOL - [7:0] */
+#define WM5100_IN3L_VOL_SHIFT 0 /* IN3L_VOL - [7:0] */
+#define WM5100_IN3L_VOL_WIDTH 8 /* IN3L_VOL - [7:0] */
+
+/*
+ * R805 (0x325) - ADC Digital Volume 3R
+ */
+#define WM5100_IN_VU 0x0200 /* IN_VU */
+#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM5100_IN_VU_SHIFT 9 /* IN_VU */
+#define WM5100_IN_VU_WIDTH 1 /* IN_VU */
+#define WM5100_IN3R_MUTE 0x0100 /* IN3R_MUTE */
+#define WM5100_IN3R_MUTE_MASK 0x0100 /* IN3R_MUTE */
+#define WM5100_IN3R_MUTE_SHIFT 8 /* IN3R_MUTE */
+#define WM5100_IN3R_MUTE_WIDTH 1 /* IN3R_MUTE */
+#define WM5100_IN3R_VOL_MASK 0x00FF /* IN3R_VOL - [7:0] */
+#define WM5100_IN3R_VOL_SHIFT 0 /* IN3R_VOL - [7:0] */
+#define WM5100_IN3R_VOL_WIDTH 8 /* IN3R_VOL - [7:0] */
+
+/*
+ * R806 (0x326) - ADC Digital Volume 4L
+ */
+#define WM5100_IN_VU 0x0200 /* IN_VU */
+#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM5100_IN_VU_SHIFT 9 /* IN_VU */
+#define WM5100_IN_VU_WIDTH 1 /* IN_VU */
+#define WM5100_IN4L_MUTE 0x0100 /* IN4L_MUTE */
+#define WM5100_IN4L_MUTE_MASK 0x0100 /* IN4L_MUTE */
+#define WM5100_IN4L_MUTE_SHIFT 8 /* IN4L_MUTE */
+#define WM5100_IN4L_MUTE_WIDTH 1 /* IN4L_MUTE */
+#define WM5100_IN4L_VOL_MASK 0x00FF /* IN4L_VOL - [7:0] */
+#define WM5100_IN4L_VOL_SHIFT 0 /* IN4L_VOL - [7:0] */
+#define WM5100_IN4L_VOL_WIDTH 8 /* IN4L_VOL - [7:0] */
+
+/*
+ * R807 (0x327) - ADC Digital Volume 4R
+ */
+#define WM5100_IN_VU 0x0200 /* IN_VU */
+#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */
+#define WM5100_IN_VU_SHIFT 9 /* IN_VU */
+#define WM5100_IN_VU_WIDTH 1 /* IN_VU */
+#define WM5100_IN4R_MUTE 0x0100 /* IN4R_MUTE */
+#define WM5100_IN4R_MUTE_MASK 0x0100 /* IN4R_MUTE */
+#define WM5100_IN4R_MUTE_SHIFT 8 /* IN4R_MUTE */
+#define WM5100_IN4R_MUTE_WIDTH 1 /* IN4R_MUTE */
+#define WM5100_IN4R_VOL_MASK 0x00FF /* IN4R_VOL - [7:0] */
+#define WM5100_IN4R_VOL_SHIFT 0 /* IN4R_VOL - [7:0] */
+#define WM5100_IN4R_VOL_WIDTH 8 /* IN4R_VOL - [7:0] */
+
+/*
+ * R1025 (0x401) - Output Enables 2
+ */
+#define WM5100_OUT6L_ENA 0x0800 /* OUT6L_ENA */
+#define WM5100_OUT6L_ENA_MASK 0x0800 /* OUT6L_ENA */
+#define WM5100_OUT6L_ENA_SHIFT 11 /* OUT6L_ENA */
+#define WM5100_OUT6L_ENA_WIDTH 1 /* OUT6L_ENA */
+#define WM5100_OUT6R_ENA 0x0400 /* OUT6R_ENA */
+#define WM5100_OUT6R_ENA_MASK 0x0400 /* OUT6R_ENA */
+#define WM5100_OUT6R_ENA_SHIFT 10 /* OUT6R_ENA */
+#define WM5100_OUT6R_ENA_WIDTH 1 /* OUT6R_ENA */
+#define WM5100_OUT5L_ENA 0x0200 /* OUT5L_ENA */
+#define WM5100_OUT5L_ENA_MASK 0x0200 /* OUT5L_ENA */
+#define WM5100_OUT5L_ENA_SHIFT 9 /* OUT5L_ENA */
+#define WM5100_OUT5L_ENA_WIDTH 1 /* OUT5L_ENA */
+#define WM5100_OUT5R_ENA 0x0100 /* OUT5R_ENA */
+#define WM5100_OUT5R_ENA_MASK 0x0100 /* OUT5R_ENA */
+#define WM5100_OUT5R_ENA_SHIFT 8 /* OUT5R_ENA */
+#define WM5100_OUT5R_ENA_WIDTH 1 /* OUT5R_ENA */
+#define WM5100_OUT4L_ENA 0x0080 /* OUT4L_ENA */
+#define WM5100_OUT4L_ENA_MASK 0x0080 /* OUT4L_ENA */
+#define WM5100_OUT4L_ENA_SHIFT 7 /* OUT4L_ENA */
+#define WM5100_OUT4L_ENA_WIDTH 1 /* OUT4L_ENA */
+#define WM5100_OUT4R_ENA 0x0040 /* OUT4R_ENA */
+#define WM5100_OUT4R_ENA_MASK 0x0040 /* OUT4R_ENA */
+#define WM5100_OUT4R_ENA_SHIFT 6 /* OUT4R_ENA */
+#define WM5100_OUT4R_ENA_WIDTH 1 /* OUT4R_ENA */
+
+/*
+ * R1026 (0x402) - Output Status 1
+ */
+#define WM5100_OUT3L_ENA_STS 0x0020 /* OUT3L_ENA_STS */
+#define WM5100_OUT3L_ENA_STS_MASK 0x0020 /* OUT3L_ENA_STS */
+#define WM5100_OUT3L_ENA_STS_SHIFT 5 /* OUT3L_ENA_STS */
+#define WM5100_OUT3L_ENA_STS_WIDTH 1 /* OUT3L_ENA_STS */
+#define WM5100_OUT3R_ENA_STS 0x0010 /* OUT3R_ENA_STS */
+#define WM5100_OUT3R_ENA_STS_MASK 0x0010 /* OUT3R_ENA_STS */
+#define WM5100_OUT3R_ENA_STS_SHIFT 4 /* OUT3R_ENA_STS */
+#define WM5100_OUT3R_ENA_STS_WIDTH 1 /* OUT3R_ENA_STS */
+#define WM5100_OUT2L_ENA_STS 0x0008 /* OUT2L_ENA_STS */
+#define WM5100_OUT2L_ENA_STS_MASK 0x0008 /* OUT2L_ENA_STS */
+#define WM5100_OUT2L_ENA_STS_SHIFT 3 /* OUT2L_ENA_STS */
+#define WM5100_OUT2L_ENA_STS_WIDTH 1 /* OUT2L_ENA_STS */
+#define WM5100_OUT2R_ENA_STS 0x0004 /* OUT2R_ENA_STS */
+#define WM5100_OUT2R_ENA_STS_MASK 0x0004 /* OUT2R_ENA_STS */
+#define WM5100_OUT2R_ENA_STS_SHIFT 2 /* OUT2R_ENA_STS */
+#define WM5100_OUT2R_ENA_STS_WIDTH 1 /* OUT2R_ENA_STS */
+#define WM5100_OUT1L_ENA_STS 0x0002 /* OUT1L_ENA_STS */
+#define WM5100_OUT1L_ENA_STS_MASK 0x0002 /* OUT1L_ENA_STS */
+#define WM5100_OUT1L_ENA_STS_SHIFT 1 /* OUT1L_ENA_STS */
+#define WM5100_OUT1L_ENA_STS_WIDTH 1 /* OUT1L_ENA_STS */
+#define WM5100_OUT1R_ENA_STS 0x0001 /* OUT1R_ENA_STS */
+#define WM5100_OUT1R_ENA_STS_MASK 0x0001 /* OUT1R_ENA_STS */
+#define WM5100_OUT1R_ENA_STS_SHIFT 0 /* OUT1R_ENA_STS */
+#define WM5100_OUT1R_ENA_STS_WIDTH 1 /* OUT1R_ENA_STS */
+
+/*
+ * R1027 (0x403) - Output Status 2
+ */
+#define WM5100_OUT6L_ENA_STS 0x0800 /* OUT6L_ENA_STS */
+#define WM5100_OUT6L_ENA_STS_MASK 0x0800 /* OUT6L_ENA_STS */
+#define WM5100_OUT6L_ENA_STS_SHIFT 11 /* OUT6L_ENA_STS */
+#define WM5100_OUT6L_ENA_STS_WIDTH 1 /* OUT6L_ENA_STS */
+#define WM5100_OUT6R_ENA_STS 0x0400 /* OUT6R_ENA_STS */
+#define WM5100_OUT6R_ENA_STS_MASK 0x0400 /* OUT6R_ENA_STS */
+#define WM5100_OUT6R_ENA_STS_SHIFT 10 /* OUT6R_ENA_STS */
+#define WM5100_OUT6R_ENA_STS_WIDTH 1 /* OUT6R_ENA_STS */
+#define WM5100_OUT5L_ENA_STS 0x0200 /* OUT5L_ENA_STS */
+#define WM5100_OUT5L_ENA_STS_MASK 0x0200 /* OUT5L_ENA_STS */
+#define WM5100_OUT5L_ENA_STS_SHIFT 9 /* OUT5L_ENA_STS */
+#define WM5100_OUT5L_ENA_STS_WIDTH 1 /* OUT5L_ENA_STS */
+#define WM5100_OUT5R_ENA_STS 0x0100 /* OUT5R_ENA_STS */
+#define WM5100_OUT5R_ENA_STS_MASK 0x0100 /* OUT5R_ENA_STS */
+#define WM5100_OUT5R_ENA_STS_SHIFT 8 /* OUT5R_ENA_STS */
+#define WM5100_OUT5R_ENA_STS_WIDTH 1 /* OUT5R_ENA_STS */
+#define WM5100_OUT4L_ENA_STS 0x0080 /* OUT4L_ENA_STS */
+#define WM5100_OUT4L_ENA_STS_MASK 0x0080 /* OUT4L_ENA_STS */
+#define WM5100_OUT4L_ENA_STS_SHIFT 7 /* OUT4L_ENA_STS */
+#define WM5100_OUT4L_ENA_STS_WIDTH 1 /* OUT4L_ENA_STS */
+#define WM5100_OUT4R_ENA_STS 0x0040 /* OUT4R_ENA_STS */
+#define WM5100_OUT4R_ENA_STS_MASK 0x0040 /* OUT4R_ENA_STS */
+#define WM5100_OUT4R_ENA_STS_SHIFT 6 /* OUT4R_ENA_STS */
+#define WM5100_OUT4R_ENA_STS_WIDTH 1 /* OUT4R_ENA_STS */
+
+/*
+ * R1032 (0x408) - Channel Enables 1
+ */
+#define WM5100_HP3L_ENA 0x0020 /* HP3L_ENA */
+#define WM5100_HP3L_ENA_MASK 0x0020 /* HP3L_ENA */
+#define WM5100_HP3L_ENA_SHIFT 5 /* HP3L_ENA */
+#define WM5100_HP3L_ENA_WIDTH 1 /* HP3L_ENA */
+#define WM5100_HP3R_ENA 0x0010 /* HP3R_ENA */
+#define WM5100_HP3R_ENA_MASK 0x0010 /* HP3R_ENA */
+#define WM5100_HP3R_ENA_SHIFT 4 /* HP3R_ENA */
+#define WM5100_HP3R_ENA_WIDTH 1 /* HP3R_ENA */
+#define WM5100_HP2L_ENA 0x0008 /* HP2L_ENA */
+#define WM5100_HP2L_ENA_MASK 0x0008 /* HP2L_ENA */
+#define WM5100_HP2L_ENA_SHIFT 3 /* HP2L_ENA */
+#define WM5100_HP2L_ENA_WIDTH 1 /* HP2L_ENA */
+#define WM5100_HP2R_ENA 0x0004 /* HP2R_ENA */
+#define WM5100_HP2R_ENA_MASK 0x0004 /* HP2R_ENA */
+#define WM5100_HP2R_ENA_SHIFT 2 /* HP2R_ENA */
+#define WM5100_HP2R_ENA_WIDTH 1 /* HP2R_ENA */
+#define WM5100_HP1L_ENA 0x0002 /* HP1L_ENA */
+#define WM5100_HP1L_ENA_MASK 0x0002 /* HP1L_ENA */
+#define WM5100_HP1L_ENA_SHIFT 1 /* HP1L_ENA */
+#define WM5100_HP1L_ENA_WIDTH 1 /* HP1L_ENA */
+#define WM5100_HP1R_ENA 0x0001 /* HP1R_ENA */
+#define WM5100_HP1R_ENA_MASK 0x0001 /* HP1R_ENA */
+#define WM5100_HP1R_ENA_SHIFT 0 /* HP1R_ENA */
+#define WM5100_HP1R_ENA_WIDTH 1 /* HP1R_ENA */
+
+/*
+ * R1040 (0x410) - Out Volume 1L
+ */
+#define WM5100_OUT_RATE_MASK 0xC000 /* OUT_RATE - [15:14] */
+#define WM5100_OUT_RATE_SHIFT 14 /* OUT_RATE - [15:14] */
+#define WM5100_OUT_RATE_WIDTH 2 /* OUT_RATE - [15:14] */
+#define WM5100_OUT1_OSR 0x2000 /* OUT1_OSR */
+#define WM5100_OUT1_OSR_MASK 0x2000 /* OUT1_OSR */
+#define WM5100_OUT1_OSR_SHIFT 13 /* OUT1_OSR */
+#define WM5100_OUT1_OSR_WIDTH 1 /* OUT1_OSR */
+#define WM5100_OUT1_MONO 0x1000 /* OUT1_MONO */
+#define WM5100_OUT1_MONO_MASK 0x1000 /* OUT1_MONO */
+#define WM5100_OUT1_MONO_SHIFT 12 /* OUT1_MONO */
+#define WM5100_OUT1_MONO_WIDTH 1 /* OUT1_MONO */
+#define WM5100_OUT1L_ANC_SRC 0x0800 /* OUT1L_ANC_SRC */
+#define WM5100_OUT1L_ANC_SRC_MASK 0x0800 /* OUT1L_ANC_SRC */
+#define WM5100_OUT1L_ANC_SRC_SHIFT 11 /* OUT1L_ANC_SRC */
+#define WM5100_OUT1L_ANC_SRC_WIDTH 1 /* OUT1L_ANC_SRC */
+#define WM5100_OUT1L_PGA_VOL_MASK 0x00FE /* OUT1L_PGA_VOL - [7:1] */
+#define WM5100_OUT1L_PGA_VOL_SHIFT 1 /* OUT1L_PGA_VOL - [7:1] */
+#define WM5100_OUT1L_PGA_VOL_WIDTH 7 /* OUT1L_PGA_VOL - [7:1] */
+
+/*
+ * R1041 (0x411) - Out Volume 1R
+ */
+#define WM5100_OUT1R_ANC_SRC 0x0800 /* OUT1R_ANC_SRC */
+#define WM5100_OUT1R_ANC_SRC_MASK 0x0800 /* OUT1R_ANC_SRC */
+#define WM5100_OUT1R_ANC_SRC_SHIFT 11 /* OUT1R_ANC_SRC */
+#define WM5100_OUT1R_ANC_SRC_WIDTH 1 /* OUT1R_ANC_SRC */
+#define WM5100_OUT1R_PGA_VOL_MASK 0x00FE /* OUT1R_PGA_VOL - [7:1] */
+#define WM5100_OUT1R_PGA_VOL_SHIFT 1 /* OUT1R_PGA_VOL - [7:1] */
+#define WM5100_OUT1R_PGA_VOL_WIDTH 7 /* OUT1R_PGA_VOL - [7:1] */
+
+/*
+ * R1042 (0x412) - DAC Volume Limit 1L
+ */
+#define WM5100_OUT1L_VOL_LIM_MASK 0x00FF /* OUT1L_VOL_LIM - [7:0] */
+#define WM5100_OUT1L_VOL_LIM_SHIFT 0 /* OUT1L_VOL_LIM - [7:0] */
+#define WM5100_OUT1L_VOL_LIM_WIDTH 8 /* OUT1L_VOL_LIM - [7:0] */
+
+/*
+ * R1043 (0x413) - DAC Volume Limit 1R
+ */
+#define WM5100_OUT1R_VOL_LIM_MASK 0x00FF /* OUT1R_VOL_LIM - [7:0] */
+#define WM5100_OUT1R_VOL_LIM_SHIFT 0 /* OUT1R_VOL_LIM - [7:0] */
+#define WM5100_OUT1R_VOL_LIM_WIDTH 8 /* OUT1R_VOL_LIM - [7:0] */
+
+/*
+ * R1044 (0x414) - Out Volume 2L
+ */
+#define WM5100_OUT2_OSR 0x2000 /* OUT2_OSR */
+#define WM5100_OUT2_OSR_MASK 0x2000 /* OUT2_OSR */
+#define WM5100_OUT2_OSR_SHIFT 13 /* OUT2_OSR */
+#define WM5100_OUT2_OSR_WIDTH 1 /* OUT2_OSR */
+#define WM5100_OUT2_MONO 0x1000 /* OUT2_MONO */
+#define WM5100_OUT2_MONO_MASK 0x1000 /* OUT2_MONO */
+#define WM5100_OUT2_MONO_SHIFT 12 /* OUT2_MONO */
+#define WM5100_OUT2_MONO_WIDTH 1 /* OUT2_MONO */
+#define WM5100_OUT2L_ANC_SRC 0x0800 /* OUT2L_ANC_SRC */
+#define WM5100_OUT2L_ANC_SRC_MASK 0x0800 /* OUT2L_ANC_SRC */
+#define WM5100_OUT2L_ANC_SRC_SHIFT 11 /* OUT2L_ANC_SRC */
+#define WM5100_OUT2L_ANC_SRC_WIDTH 1 /* OUT2L_ANC_SRC */
+#define WM5100_OUT2L_PGA_VOL_MASK 0x00FE /* OUT2L_PGA_VOL - [7:1] */
+#define WM5100_OUT2L_PGA_VOL_SHIFT 1 /* OUT2L_PGA_VOL - [7:1] */
+#define WM5100_OUT2L_PGA_VOL_WIDTH 7 /* OUT2L_PGA_VOL - [7:1] */
+
+/*
+ * R1045 (0x415) - Out Volume 2R
+ */
+#define WM5100_OUT2R_ANC_SRC 0x0800 /* OUT2R_ANC_SRC */
+#define WM5100_OUT2R_ANC_SRC_MASK 0x0800 /* OUT2R_ANC_SRC */
+#define WM5100_OUT2R_ANC_SRC_SHIFT 11 /* OUT2R_ANC_SRC */
+#define WM5100_OUT2R_ANC_SRC_WIDTH 1 /* OUT2R_ANC_SRC */
+#define WM5100_OUT2R_PGA_VOL_MASK 0x00FE /* OUT2R_PGA_VOL - [7:1] */
+#define WM5100_OUT2R_PGA_VOL_SHIFT 1 /* OUT2R_PGA_VOL - [7:1] */
+#define WM5100_OUT2R_PGA_VOL_WIDTH 7 /* OUT2R_PGA_VOL - [7:1] */
+
+/*
+ * R1046 (0x416) - DAC Volume Limit 2L
+ */
+#define WM5100_OUT2L_VOL_LIM_MASK 0x00FF /* OUT2L_VOL_LIM - [7:0] */
+#define WM5100_OUT2L_VOL_LIM_SHIFT 0 /* OUT2L_VOL_LIM - [7:0] */
+#define WM5100_OUT2L_VOL_LIM_WIDTH 8 /* OUT2L_VOL_LIM - [7:0] */
+
+/*
+ * R1047 (0x417) - DAC Volume Limit 2R
+ */
+#define WM5100_OUT2R_VOL_LIM_MASK 0x00FF /* OUT2R_VOL_LIM - [7:0] */
+#define WM5100_OUT2R_VOL_LIM_SHIFT 0 /* OUT2R_VOL_LIM - [7:0] */
+#define WM5100_OUT2R_VOL_LIM_WIDTH 8 /* OUT2R_VOL_LIM - [7:0] */
+
+/*
+ * R1048 (0x418) - Out Volume 3L
+ */
+#define WM5100_OUT3_OSR 0x2000 /* OUT3_OSR */
+#define WM5100_OUT3_OSR_MASK 0x2000 /* OUT3_OSR */
+#define WM5100_OUT3_OSR_SHIFT 13 /* OUT3_OSR */
+#define WM5100_OUT3_OSR_WIDTH 1 /* OUT3_OSR */
+#define WM5100_OUT3_MONO 0x1000 /* OUT3_MONO */
+#define WM5100_OUT3_MONO_MASK 0x1000 /* OUT3_MONO */
+#define WM5100_OUT3_MONO_SHIFT 12 /* OUT3_MONO */
+#define WM5100_OUT3_MONO_WIDTH 1 /* OUT3_MONO */
+#define WM5100_OUT3L_ANC_SRC 0x0800 /* OUT3L_ANC_SRC */
+#define WM5100_OUT3L_ANC_SRC_MASK 0x0800 /* OUT3L_ANC_SRC */
+#define WM5100_OUT3L_ANC_SRC_SHIFT 11 /* OUT3L_ANC_SRC */
+#define WM5100_OUT3L_ANC_SRC_WIDTH 1 /* OUT3L_ANC_SRC */
+#define WM5100_OUT3L_PGA_VOL_MASK 0x00FE /* OUT3L_PGA_VOL - [7:1] */
+#define WM5100_OUT3L_PGA_VOL_SHIFT 1 /* OUT3L_PGA_VOL - [7:1] */
+#define WM5100_OUT3L_PGA_VOL_WIDTH 7 /* OUT3L_PGA_VOL - [7:1] */
+
+/*
+ * R1049 (0x419) - Out Volume 3R
+ */
+#define WM5100_OUT3R_ANC_SRC 0x0800 /* OUT3R_ANC_SRC */
+#define WM5100_OUT3R_ANC_SRC_MASK 0x0800 /* OUT3R_ANC_SRC */
+#define WM5100_OUT3R_ANC_SRC_SHIFT 11 /* OUT3R_ANC_SRC */
+#define WM5100_OUT3R_ANC_SRC_WIDTH 1 /* OUT3R_ANC_SRC */
+#define WM5100_OUT3R_PGA_VOL_MASK 0x00FE /* OUT3R_PGA_VOL - [7:1] */
+#define WM5100_OUT3R_PGA_VOL_SHIFT 1 /* OUT3R_PGA_VOL - [7:1] */
+#define WM5100_OUT3R_PGA_VOL_WIDTH 7 /* OUT3R_PGA_VOL - [7:1] */
+
+/*
+ * R1050 (0x41A) - DAC Volume Limit 3L
+ */
+#define WM5100_OUT3L_VOL_LIM_MASK 0x00FF /* OUT3L_VOL_LIM - [7:0] */
+#define WM5100_OUT3L_VOL_LIM_SHIFT 0 /* OUT3L_VOL_LIM - [7:0] */
+#define WM5100_OUT3L_VOL_LIM_WIDTH 8 /* OUT3L_VOL_LIM - [7:0] */
+
+/*
+ * R1051 (0x41B) - DAC Volume Limit 3R
+ */
+#define WM5100_OUT3R_VOL_LIM_MASK 0x00FF /* OUT3R_VOL_LIM - [7:0] */
+#define WM5100_OUT3R_VOL_LIM_SHIFT 0 /* OUT3R_VOL_LIM - [7:0] */
+#define WM5100_OUT3R_VOL_LIM_WIDTH 8 /* OUT3R_VOL_LIM - [7:0] */
+
+/*
+ * R1052 (0x41C) - Out Volume 4L
+ */
+#define WM5100_OUT4_OSR 0x2000 /* OUT4_OSR */
+#define WM5100_OUT4_OSR_MASK 0x2000 /* OUT4_OSR */
+#define WM5100_OUT4_OSR_SHIFT 13 /* OUT4_OSR */
+#define WM5100_OUT4_OSR_WIDTH 1 /* OUT4_OSR */
+#define WM5100_OUT4L_ANC_SRC 0x0800 /* OUT4L_ANC_SRC */
+#define WM5100_OUT4L_ANC_SRC_MASK 0x0800 /* OUT4L_ANC_SRC */
+#define WM5100_OUT4L_ANC_SRC_SHIFT 11 /* OUT4L_ANC_SRC */
+#define WM5100_OUT4L_ANC_SRC_WIDTH 1 /* OUT4L_ANC_SRC */
+#define WM5100_OUT4L_VOL_LIM_MASK 0x00FF /* OUT4L_VOL_LIM - [7:0] */
+#define WM5100_OUT4L_VOL_LIM_SHIFT 0 /* OUT4L_VOL_LIM - [7:0] */
+#define WM5100_OUT4L_VOL_LIM_WIDTH 8 /* OUT4L_VOL_LIM - [7:0] */
+
+/*
+ * R1053 (0x41D) - Out Volume 4R
+ */
+#define WM5100_OUT4R_ANC_SRC 0x0800 /* OUT4R_ANC_SRC */
+#define WM5100_OUT4R_ANC_SRC_MASK 0x0800 /* OUT4R_ANC_SRC */
+#define WM5100_OUT4R_ANC_SRC_SHIFT 11 /* OUT4R_ANC_SRC */
+#define WM5100_OUT4R_ANC_SRC_WIDTH 1 /* OUT4R_ANC_SRC */
+#define WM5100_OUT4R_VOL_LIM_MASK 0x00FF /* OUT4R_VOL_LIM - [7:0] */
+#define WM5100_OUT4R_VOL_LIM_SHIFT 0 /* OUT4R_VOL_LIM - [7:0] */
+#define WM5100_OUT4R_VOL_LIM_WIDTH 8 /* OUT4R_VOL_LIM - [7:0] */
+
+/*
+ * R1054 (0x41E) - DAC Volume Limit 5L
+ */
+#define WM5100_OUT5_OSR 0x2000 /* OUT5_OSR */
+#define WM5100_OUT5_OSR_MASK 0x2000 /* OUT5_OSR */
+#define WM5100_OUT5_OSR_SHIFT 13 /* OUT5_OSR */
+#define WM5100_OUT5_OSR_WIDTH 1 /* OUT5_OSR */
+#define WM5100_OUT5L_ANC_SRC 0x0800 /* OUT5L_ANC_SRC */
+#define WM5100_OUT5L_ANC_SRC_MASK 0x0800 /* OUT5L_ANC_SRC */
+#define WM5100_OUT5L_ANC_SRC_SHIFT 11 /* OUT5L_ANC_SRC */
+#define WM5100_OUT5L_ANC_SRC_WIDTH 1 /* OUT5L_ANC_SRC */
+#define WM5100_OUT5L_VOL_LIM_MASK 0x00FF /* OUT5L_VOL_LIM - [7:0] */
+#define WM5100_OUT5L_VOL_LIM_SHIFT 0 /* OUT5L_VOL_LIM - [7:0] */
+#define WM5100_OUT5L_VOL_LIM_WIDTH 8 /* OUT5L_VOL_LIM - [7:0] */
+
+/*
+ * R1055 (0x41F) - DAC Volume Limit 5R
+ */
+#define WM5100_OUT5R_ANC_SRC 0x0800 /* OUT5R_ANC_SRC */
+#define WM5100_OUT5R_ANC_SRC_MASK 0x0800 /* OUT5R_ANC_SRC */
+#define WM5100_OUT5R_ANC_SRC_SHIFT 11 /* OUT5R_ANC_SRC */
+#define WM5100_OUT5R_ANC_SRC_WIDTH 1 /* OUT5R_ANC_SRC */
+#define WM5100_OUT5R_VOL_LIM_MASK 0x00FF /* OUT5R_VOL_LIM - [7:0] */
+#define WM5100_OUT5R_VOL_LIM_SHIFT 0 /* OUT5R_VOL_LIM - [7:0] */
+#define WM5100_OUT5R_VOL_LIM_WIDTH 8 /* OUT5R_VOL_LIM - [7:0] */
+
+/*
+ * R1056 (0x420) - DAC Volume Limit 6L
+ */
+#define WM5100_OUT6_OSR 0x2000 /* OUT6_OSR */
+#define WM5100_OUT6_OSR_MASK 0x2000 /* OUT6_OSR */
+#define WM5100_OUT6_OSR_SHIFT 13 /* OUT6_OSR */
+#define WM5100_OUT6_OSR_WIDTH 1 /* OUT6_OSR */
+#define WM5100_OUT6L_ANC_SRC 0x0800 /* OUT6L_ANC_SRC */
+#define WM5100_OUT6L_ANC_SRC_MASK 0x0800 /* OUT6L_ANC_SRC */
+#define WM5100_OUT6L_ANC_SRC_SHIFT 11 /* OUT6L_ANC_SRC */
+#define WM5100_OUT6L_ANC_SRC_WIDTH 1 /* OUT6L_ANC_SRC */
+#define WM5100_OUT6L_VOL_LIM_MASK 0x00FF /* OUT6L_VOL_LIM - [7:0] */
+#define WM5100_OUT6L_VOL_LIM_SHIFT 0 /* OUT6L_VOL_LIM - [7:0] */
+#define WM5100_OUT6L_VOL_LIM_WIDTH 8 /* OUT6L_VOL_LIM - [7:0] */
+
+/*
+ * R1057 (0x421) - DAC Volume Limit 6R
+ */
+#define WM5100_OUT6R_ANC_SRC 0x0800 /* OUT6R_ANC_SRC */
+#define WM5100_OUT6R_ANC_SRC_MASK 0x0800 /* OUT6R_ANC_SRC */
+#define WM5100_OUT6R_ANC_SRC_SHIFT 11 /* OUT6R_ANC_SRC */
+#define WM5100_OUT6R_ANC_SRC_WIDTH 1 /* OUT6R_ANC_SRC */
+#define WM5100_OUT6R_VOL_LIM_MASK 0x00FF /* OUT6R_VOL_LIM - [7:0] */
+#define WM5100_OUT6R_VOL_LIM_SHIFT 0 /* OUT6R_VOL_LIM - [7:0] */
+#define WM5100_OUT6R_VOL_LIM_WIDTH 8 /* OUT6R_VOL_LIM - [7:0] */
+
+/*
+ * R1088 (0x440) - DAC AEC Control 1
+ */
+#define WM5100_AEC_LOOPBACK_SRC_MASK 0x003C /* AEC_LOOPBACK_SRC - [5:2] */
+#define WM5100_AEC_LOOPBACK_SRC_SHIFT 2 /* AEC_LOOPBACK_SRC - [5:2] */
+#define WM5100_AEC_LOOPBACK_SRC_WIDTH 4 /* AEC_LOOPBACK_SRC - [5:2] */
+#define WM5100_AEC_ENA_STS 0x0002 /* AEC_ENA_STS */
+#define WM5100_AEC_ENA_STS_MASK 0x0002 /* AEC_ENA_STS */
+#define WM5100_AEC_ENA_STS_SHIFT 1 /* AEC_ENA_STS */
+#define WM5100_AEC_ENA_STS_WIDTH 1 /* AEC_ENA_STS */
+#define WM5100_AEC_LOOPBACK_ENA 0x0001 /* AEC_LOOPBACK_ENA */
+#define WM5100_AEC_LOOPBACK_ENA_MASK 0x0001 /* AEC_LOOPBACK_ENA */
+#define WM5100_AEC_LOOPBACK_ENA_SHIFT 0 /* AEC_LOOPBACK_ENA */
+#define WM5100_AEC_LOOPBACK_ENA_WIDTH 1 /* AEC_LOOPBACK_ENA */
+
+/*
+ * R1089 (0x441) - Output Volume Ramp
+ */
+#define WM5100_OUT_VD_RAMP_MASK 0x0070 /* OUT_VD_RAMP - [6:4] */
+#define WM5100_OUT_VD_RAMP_SHIFT 4 /* OUT_VD_RAMP - [6:4] */
+#define WM5100_OUT_VD_RAMP_WIDTH 3 /* OUT_VD_RAMP - [6:4] */
+#define WM5100_OUT_VI_RAMP_MASK 0x0007 /* OUT_VI_RAMP - [2:0] */
+#define WM5100_OUT_VI_RAMP_SHIFT 0 /* OUT_VI_RAMP - [2:0] */
+#define WM5100_OUT_VI_RAMP_WIDTH 3 /* OUT_VI_RAMP - [2:0] */
+
+/*
+ * R1152 (0x480) - DAC Digital Volume 1L
+ */
+#define WM5100_OUT_VU 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM5100_OUT1L_MUTE 0x0100 /* OUT1L_MUTE */
+#define WM5100_OUT1L_MUTE_MASK 0x0100 /* OUT1L_MUTE */
+#define WM5100_OUT1L_MUTE_SHIFT 8 /* OUT1L_MUTE */
+#define WM5100_OUT1L_MUTE_WIDTH 1 /* OUT1L_MUTE */
+#define WM5100_OUT1L_VOL_MASK 0x00FF /* OUT1L_VOL - [7:0] */
+#define WM5100_OUT1L_VOL_SHIFT 0 /* OUT1L_VOL - [7:0] */
+#define WM5100_OUT1L_VOL_WIDTH 8 /* OUT1L_VOL - [7:0] */
+
+/*
+ * R1153 (0x481) - DAC Digital Volume 1R
+ */
+#define WM5100_OUT_VU 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM5100_OUT1R_MUTE 0x0100 /* OUT1R_MUTE */
+#define WM5100_OUT1R_MUTE_MASK 0x0100 /* OUT1R_MUTE */
+#define WM5100_OUT1R_MUTE_SHIFT 8 /* OUT1R_MUTE */
+#define WM5100_OUT1R_MUTE_WIDTH 1 /* OUT1R_MUTE */
+#define WM5100_OUT1R_VOL_MASK 0x00FF /* OUT1R_VOL - [7:0] */
+#define WM5100_OUT1R_VOL_SHIFT 0 /* OUT1R_VOL - [7:0] */
+#define WM5100_OUT1R_VOL_WIDTH 8 /* OUT1R_VOL - [7:0] */
+
+/*
+ * R1154 (0x482) - DAC Digital Volume 2L
+ */
+#define WM5100_OUT_VU 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM5100_OUT2L_MUTE 0x0100 /* OUT2L_MUTE */
+#define WM5100_OUT2L_MUTE_MASK 0x0100 /* OUT2L_MUTE */
+#define WM5100_OUT2L_MUTE_SHIFT 8 /* OUT2L_MUTE */
+#define WM5100_OUT2L_MUTE_WIDTH 1 /* OUT2L_MUTE */
+#define WM5100_OUT2L_VOL_MASK 0x00FF /* OUT2L_VOL - [7:0] */
+#define WM5100_OUT2L_VOL_SHIFT 0 /* OUT2L_VOL - [7:0] */
+#define WM5100_OUT2L_VOL_WIDTH 8 /* OUT2L_VOL - [7:0] */
+
+/*
+ * R1155 (0x483) - DAC Digital Volume 2R
+ */
+#define WM5100_OUT_VU 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM5100_OUT2R_MUTE 0x0100 /* OUT2R_MUTE */
+#define WM5100_OUT2R_MUTE_MASK 0x0100 /* OUT2R_MUTE */
+#define WM5100_OUT2R_MUTE_SHIFT 8 /* OUT2R_MUTE */
+#define WM5100_OUT2R_MUTE_WIDTH 1 /* OUT2R_MUTE */
+#define WM5100_OUT2R_VOL_MASK 0x00FF /* OUT2R_VOL - [7:0] */
+#define WM5100_OUT2R_VOL_SHIFT 0 /* OUT2R_VOL - [7:0] */
+#define WM5100_OUT2R_VOL_WIDTH 8 /* OUT2R_VOL - [7:0] */
+
+/*
+ * R1156 (0x484) - DAC Digital Volume 3L
+ */
+#define WM5100_OUT_VU 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM5100_OUT3L_MUTE 0x0100 /* OUT3L_MUTE */
+#define WM5100_OUT3L_MUTE_MASK 0x0100 /* OUT3L_MUTE */
+#define WM5100_OUT3L_MUTE_SHIFT 8 /* OUT3L_MUTE */
+#define WM5100_OUT3L_MUTE_WIDTH 1 /* OUT3L_MUTE */
+#define WM5100_OUT3L_VOL_MASK 0x00FF /* OUT3L_VOL - [7:0] */
+#define WM5100_OUT3L_VOL_SHIFT 0 /* OUT3L_VOL - [7:0] */
+#define WM5100_OUT3L_VOL_WIDTH 8 /* OUT3L_VOL - [7:0] */
+
+/*
+ * R1157 (0x485) - DAC Digital Volume 3R
+ */
+#define WM5100_OUT_VU 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM5100_OUT3R_MUTE 0x0100 /* OUT3R_MUTE */
+#define WM5100_OUT3R_MUTE_MASK 0x0100 /* OUT3R_MUTE */
+#define WM5100_OUT3R_MUTE_SHIFT 8 /* OUT3R_MUTE */
+#define WM5100_OUT3R_MUTE_WIDTH 1 /* OUT3R_MUTE */
+#define WM5100_OUT3R_VOL_MASK 0x00FF /* OUT3R_VOL - [7:0] */
+#define WM5100_OUT3R_VOL_SHIFT 0 /* OUT3R_VOL - [7:0] */
+#define WM5100_OUT3R_VOL_WIDTH 8 /* OUT3R_VOL - [7:0] */
+
+/*
+ * R1158 (0x486) - DAC Digital Volume 4L
+ */
+#define WM5100_OUT_VU 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM5100_OUT4L_MUTE 0x0100 /* OUT4L_MUTE */
+#define WM5100_OUT4L_MUTE_MASK 0x0100 /* OUT4L_MUTE */
+#define WM5100_OUT4L_MUTE_SHIFT 8 /* OUT4L_MUTE */
+#define WM5100_OUT4L_MUTE_WIDTH 1 /* OUT4L_MUTE */
+#define WM5100_OUT4L_VOL_MASK 0x00FF /* OUT4L_VOL - [7:0] */
+#define WM5100_OUT4L_VOL_SHIFT 0 /* OUT4L_VOL - [7:0] */
+#define WM5100_OUT4L_VOL_WIDTH 8 /* OUT4L_VOL - [7:0] */
+
+/*
+ * R1159 (0x487) - DAC Digital Volume 4R
+ */
+#define WM5100_OUT_VU 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM5100_OUT4R_MUTE 0x0100 /* OUT4R_MUTE */
+#define WM5100_OUT4R_MUTE_MASK 0x0100 /* OUT4R_MUTE */
+#define WM5100_OUT4R_MUTE_SHIFT 8 /* OUT4R_MUTE */
+#define WM5100_OUT4R_MUTE_WIDTH 1 /* OUT4R_MUTE */
+#define WM5100_OUT4R_VOL_MASK 0x00FF /* OUT4R_VOL - [7:0] */
+#define WM5100_OUT4R_VOL_SHIFT 0 /* OUT4R_VOL - [7:0] */
+#define WM5100_OUT4R_VOL_WIDTH 8 /* OUT4R_VOL - [7:0] */
+
+/*
+ * R1160 (0x488) - DAC Digital Volume 5L
+ */
+#define WM5100_OUT_VU 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM5100_OUT5L_MUTE 0x0100 /* OUT5L_MUTE */
+#define WM5100_OUT5L_MUTE_MASK 0x0100 /* OUT5L_MUTE */
+#define WM5100_OUT5L_MUTE_SHIFT 8 /* OUT5L_MUTE */
+#define WM5100_OUT5L_MUTE_WIDTH 1 /* OUT5L_MUTE */
+#define WM5100_OUT5L_VOL_MASK 0x00FF /* OUT5L_VOL - [7:0] */
+#define WM5100_OUT5L_VOL_SHIFT 0 /* OUT5L_VOL - [7:0] */
+#define WM5100_OUT5L_VOL_WIDTH 8 /* OUT5L_VOL - [7:0] */
+
+/*
+ * R1161 (0x489) - DAC Digital Volume 5R
+ */
+#define WM5100_OUT_VU 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM5100_OUT5R_MUTE 0x0100 /* OUT5R_MUTE */
+#define WM5100_OUT5R_MUTE_MASK 0x0100 /* OUT5R_MUTE */
+#define WM5100_OUT5R_MUTE_SHIFT 8 /* OUT5R_MUTE */
+#define WM5100_OUT5R_MUTE_WIDTH 1 /* OUT5R_MUTE */
+#define WM5100_OUT5R_VOL_MASK 0x00FF /* OUT5R_VOL - [7:0] */
+#define WM5100_OUT5R_VOL_SHIFT 0 /* OUT5R_VOL - [7:0] */
+#define WM5100_OUT5R_VOL_WIDTH 8 /* OUT5R_VOL - [7:0] */
+
+/*
+ * R1162 (0x48A) - DAC Digital Volume 6L
+ */
+#define WM5100_OUT_VU 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM5100_OUT6L_MUTE 0x0100 /* OUT6L_MUTE */
+#define WM5100_OUT6L_MUTE_MASK 0x0100 /* OUT6L_MUTE */
+#define WM5100_OUT6L_MUTE_SHIFT 8 /* OUT6L_MUTE */
+#define WM5100_OUT6L_MUTE_WIDTH 1 /* OUT6L_MUTE */
+#define WM5100_OUT6L_VOL_MASK 0x00FF /* OUT6L_VOL - [7:0] */
+#define WM5100_OUT6L_VOL_SHIFT 0 /* OUT6L_VOL - [7:0] */
+#define WM5100_OUT6L_VOL_WIDTH 8 /* OUT6L_VOL - [7:0] */
+
+/*
+ * R1163 (0x48B) - DAC Digital Volume 6R
+ */
+#define WM5100_OUT_VU 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */
+#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */
+#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */
+#define WM5100_OUT6R_MUTE 0x0100 /* OUT6R_MUTE */
+#define WM5100_OUT6R_MUTE_MASK 0x0100 /* OUT6R_MUTE */
+#define WM5100_OUT6R_MUTE_SHIFT 8 /* OUT6R_MUTE */
+#define WM5100_OUT6R_MUTE_WIDTH 1 /* OUT6R_MUTE */
+#define WM5100_OUT6R_VOL_MASK 0x00FF /* OUT6R_VOL - [7:0] */
+#define WM5100_OUT6R_VOL_SHIFT 0 /* OUT6R_VOL - [7:0] */
+#define WM5100_OUT6R_VOL_WIDTH 8 /* OUT6R_VOL - [7:0] */
+
+/*
+ * R1216 (0x4C0) - PDM SPK1 CTRL 1
+ */
+#define WM5100_SPK1R_MUTE 0x2000 /* SPK1R_MUTE */
+#define WM5100_SPK1R_MUTE_MASK 0x2000 /* SPK1R_MUTE */
+#define WM5100_SPK1R_MUTE_SHIFT 13 /* SPK1R_MUTE */
+#define WM5100_SPK1R_MUTE_WIDTH 1 /* SPK1R_MUTE */
+#define WM5100_SPK1L_MUTE 0x1000 /* SPK1L_MUTE */
+#define WM5100_SPK1L_MUTE_MASK 0x1000 /* SPK1L_MUTE */
+#define WM5100_SPK1L_MUTE_SHIFT 12 /* SPK1L_MUTE */
+#define WM5100_SPK1L_MUTE_WIDTH 1 /* SPK1L_MUTE */
+#define WM5100_SPK1_MUTE_ENDIAN 0x0100 /* SPK1_MUTE_ENDIAN */
+#define WM5100_SPK1_MUTE_ENDIAN_MASK 0x0100 /* SPK1_MUTE_ENDIAN */
+#define WM5100_SPK1_MUTE_ENDIAN_SHIFT 8 /* SPK1_MUTE_ENDIAN */
+#define WM5100_SPK1_MUTE_ENDIAN_WIDTH 1 /* SPK1_MUTE_ENDIAN */
+#define WM5100_SPK1_MUTE_SEQ1_MASK 0x00FF /* SPK1_MUTE_SEQ1 - [7:0] */
+#define WM5100_SPK1_MUTE_SEQ1_SHIFT 0 /* SPK1_MUTE_SEQ1 - [7:0] */
+#define WM5100_SPK1_MUTE_SEQ1_WIDTH 8 /* SPK1_MUTE_SEQ1 - [7:0] */
+
+/*
+ * R1217 (0x4C1) - PDM SPK1 CTRL 2
+ */
+#define WM5100_SPK1_FMT 0x0001 /* SPK1_FMT */
+#define WM5100_SPK1_FMT_MASK 0x0001 /* SPK1_FMT */
+#define WM5100_SPK1_FMT_SHIFT 0 /* SPK1_FMT */
+#define WM5100_SPK1_FMT_WIDTH 1 /* SPK1_FMT */
+
+/*
+ * R1218 (0x4C2) - PDM SPK2 CTRL 1
+ */
+#define WM5100_SPK2R_MUTE 0x2000 /* SPK2R_MUTE */
+#define WM5100_SPK2R_MUTE_MASK 0x2000 /* SPK2R_MUTE */
+#define WM5100_SPK2R_MUTE_SHIFT 13 /* SPK2R_MUTE */
+#define WM5100_SPK2R_MUTE_WIDTH 1 /* SPK2R_MUTE */
+#define WM5100_SPK2L_MUTE 0x1000 /* SPK2L_MUTE */
+#define WM5100_SPK2L_MUTE_MASK 0x1000 /* SPK2L_MUTE */
+#define WM5100_SPK2L_MUTE_SHIFT 12 /* SPK2L_MUTE */
+#define WM5100_SPK2L_MUTE_WIDTH 1 /* SPK2L_MUTE */
+#define WM5100_SPK2_MUTE_ENDIAN 0x0100 /* SPK2_MUTE_ENDIAN */
+#define WM5100_SPK2_MUTE_ENDIAN_MASK 0x0100 /* SPK2_MUTE_ENDIAN */
+#define WM5100_SPK2_MUTE_ENDIAN_SHIFT 8 /* SPK2_MUTE_ENDIAN */
+#define WM5100_SPK2_MUTE_ENDIAN_WIDTH 1 /* SPK2_MUTE_ENDIAN */
+#define WM5100_SPK2_MUTE_SEQ1_MASK 0x00FF /* SPK2_MUTE_SEQ1 - [7:0] */
+#define WM5100_SPK2_MUTE_SEQ1_SHIFT 0 /* SPK2_MUTE_SEQ1 - [7:0] */
+#define WM5100_SPK2_MUTE_SEQ1_WIDTH 8 /* SPK2_MUTE_SEQ1 - [7:0] */
+
+/*
+ * R1219 (0x4C3) - PDM SPK2 CTRL 2
+ */
+#define WM5100_SPK2_FMT 0x0001 /* SPK2_FMT */
+#define WM5100_SPK2_FMT_MASK 0x0001 /* SPK2_FMT */
+#define WM5100_SPK2_FMT_SHIFT 0 /* SPK2_FMT */
+#define WM5100_SPK2_FMT_WIDTH 1 /* SPK2_FMT */
+
+/*
+ * R1280 (0x500) - Audio IF 1_1
+ */
+#define WM5100_AIF1_BCLK_INV 0x0080 /* AIF1_BCLK_INV */
+#define WM5100_AIF1_BCLK_INV_MASK 0x0080 /* AIF1_BCLK_INV */
+#define WM5100_AIF1_BCLK_INV_SHIFT 7 /* AIF1_BCLK_INV */
+#define WM5100_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */
+#define WM5100_AIF1_BCLK_FRC 0x0040 /* AIF1_BCLK_FRC */
+#define WM5100_AIF1_BCLK_FRC_MASK 0x0040 /* AIF1_BCLK_FRC */
+#define WM5100_AIF1_BCLK_FRC_SHIFT 6 /* AIF1_BCLK_FRC */
+#define WM5100_AIF1_BCLK_FRC_WIDTH 1 /* AIF1_BCLK_FRC */
+#define WM5100_AIF1_BCLK_MSTR 0x0020 /* AIF1_BCLK_MSTR */
+#define WM5100_AIF1_BCLK_MSTR_MASK 0x0020 /* AIF1_BCLK_MSTR */
+#define WM5100_AIF1_BCLK_MSTR_SHIFT 5 /* AIF1_BCLK_MSTR */
+#define WM5100_AIF1_BCLK_MSTR_WIDTH 1 /* AIF1_BCLK_MSTR */
+#define WM5100_AIF1_BCLK_FREQ_MASK 0x001F /* AIF1_BCLK_FREQ - [4:0] */
+#define WM5100_AIF1_BCLK_FREQ_SHIFT 0 /* AIF1_BCLK_FREQ - [4:0] */
+#define WM5100_AIF1_BCLK_FREQ_WIDTH 5 /* AIF1_BCLK_FREQ - [4:0] */
+
+/*
+ * R1281 (0x501) - Audio IF 1_2
+ */
+#define WM5100_AIF1TX_DAT_TRI 0x0020 /* AIF1TX_DAT_TRI */
+#define WM5100_AIF1TX_DAT_TRI_MASK 0x0020 /* AIF1TX_DAT_TRI */
+#define WM5100_AIF1TX_DAT_TRI_SHIFT 5 /* AIF1TX_DAT_TRI */
+#define WM5100_AIF1TX_DAT_TRI_WIDTH 1 /* AIF1TX_DAT_TRI */
+#define WM5100_AIF1TX_LRCLK_SRC 0x0008 /* AIF1TX_LRCLK_SRC */
+#define WM5100_AIF1TX_LRCLK_SRC_MASK 0x0008 /* AIF1TX_LRCLK_SRC */
+#define WM5100_AIF1TX_LRCLK_SRC_SHIFT 3 /* AIF1TX_LRCLK_SRC */
+#define WM5100_AIF1TX_LRCLK_SRC_WIDTH 1 /* AIF1TX_LRCLK_SRC */
+#define WM5100_AIF1TX_LRCLK_INV 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM5100_AIF1TX_LRCLK_INV_MASK 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM5100_AIF1TX_LRCLK_INV_SHIFT 2 /* AIF1TX_LRCLK_INV */
+#define WM5100_AIF1TX_LRCLK_INV_WIDTH 1 /* AIF1TX_LRCLK_INV */
+#define WM5100_AIF1TX_LRCLK_FRC 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM5100_AIF1TX_LRCLK_FRC_MASK 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM5100_AIF1TX_LRCLK_FRC_SHIFT 1 /* AIF1TX_LRCLK_FRC */
+#define WM5100_AIF1TX_LRCLK_FRC_WIDTH 1 /* AIF1TX_LRCLK_FRC */
+#define WM5100_AIF1TX_LRCLK_MSTR 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM5100_AIF1TX_LRCLK_MSTR_MASK 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM5100_AIF1TX_LRCLK_MSTR_SHIFT 0 /* AIF1TX_LRCLK_MSTR */
+#define WM5100_AIF1TX_LRCLK_MSTR_WIDTH 1 /* AIF1TX_LRCLK_MSTR */
+
+/*
+ * R1282 (0x502) - Audio IF 1_3
+ */
+#define WM5100_AIF1RX_LRCLK_INV 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM5100_AIF1RX_LRCLK_INV_MASK 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM5100_AIF1RX_LRCLK_INV_SHIFT 2 /* AIF1RX_LRCLK_INV */
+#define WM5100_AIF1RX_LRCLK_INV_WIDTH 1 /* AIF1RX_LRCLK_INV */
+#define WM5100_AIF1RX_LRCLK_FRC 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM5100_AIF1RX_LRCLK_FRC_MASK 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM5100_AIF1RX_LRCLK_FRC_SHIFT 1 /* AIF1RX_LRCLK_FRC */
+#define WM5100_AIF1RX_LRCLK_FRC_WIDTH 1 /* AIF1RX_LRCLK_FRC */
+#define WM5100_AIF1RX_LRCLK_MSTR 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM5100_AIF1RX_LRCLK_MSTR_MASK 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM5100_AIF1RX_LRCLK_MSTR_SHIFT 0 /* AIF1RX_LRCLK_MSTR */
+#define WM5100_AIF1RX_LRCLK_MSTR_WIDTH 1 /* AIF1RX_LRCLK_MSTR */
+
+/*
+ * R1283 (0x503) - Audio IF 1_4
+ */
+#define WM5100_AIF1_TRI 0x0040 /* AIF1_TRI */
+#define WM5100_AIF1_TRI_MASK 0x0040 /* AIF1_TRI */
+#define WM5100_AIF1_TRI_SHIFT 6 /* AIF1_TRI */
+#define WM5100_AIF1_TRI_WIDTH 1 /* AIF1_TRI */
+#define WM5100_AIF1_RATE_MASK 0x0003 /* AIF1_RATE - [1:0] */
+#define WM5100_AIF1_RATE_SHIFT 0 /* AIF1_RATE - [1:0] */
+#define WM5100_AIF1_RATE_WIDTH 2 /* AIF1_RATE - [1:0] */
+
+/*
+ * R1284 (0x504) - Audio IF 1_5
+ */
+#define WM5100_AIF1_FMT_MASK 0x0007 /* AIF1_FMT - [2:0] */
+#define WM5100_AIF1_FMT_SHIFT 0 /* AIF1_FMT - [2:0] */
+#define WM5100_AIF1_FMT_WIDTH 3 /* AIF1_FMT - [2:0] */
+
+/*
+ * R1285 (0x505) - Audio IF 1_6
+ */
+#define WM5100_AIF1TX_BCPF_MASK 0x1FFF /* AIF1TX_BCPF - [12:0] */
+#define WM5100_AIF1TX_BCPF_SHIFT 0 /* AIF1TX_BCPF - [12:0] */
+#define WM5100_AIF1TX_BCPF_WIDTH 13 /* AIF1TX_BCPF - [12:0] */
+
+/*
+ * R1286 (0x506) - Audio IF 1_7
+ */
+#define WM5100_AIF1RX_BCPF_MASK 0x1FFF /* AIF1RX_BCPF - [12:0] */
+#define WM5100_AIF1RX_BCPF_SHIFT 0 /* AIF1RX_BCPF - [12:0] */
+#define WM5100_AIF1RX_BCPF_WIDTH 13 /* AIF1RX_BCPF - [12:0] */
+
+/*
+ * R1287 (0x507) - Audio IF 1_8
+ */
+#define WM5100_AIF1TX_WL_MASK 0x3F00 /* AIF1TX_WL - [13:8] */
+#define WM5100_AIF1TX_WL_SHIFT 8 /* AIF1TX_WL - [13:8] */
+#define WM5100_AIF1TX_WL_WIDTH 6 /* AIF1TX_WL - [13:8] */
+#define WM5100_AIF1TX_SLOT_LEN_MASK 0x00FF /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM5100_AIF1TX_SLOT_LEN_SHIFT 0 /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM5100_AIF1TX_SLOT_LEN_WIDTH 8 /* AIF1TX_SLOT_LEN - [7:0] */
+
+/*
+ * R1288 (0x508) - Audio IF 1_9
+ */
+#define WM5100_AIF1RX_WL_MASK 0x3F00 /* AIF1RX_WL - [13:8] */
+#define WM5100_AIF1RX_WL_SHIFT 8 /* AIF1RX_WL - [13:8] */
+#define WM5100_AIF1RX_WL_WIDTH 6 /* AIF1RX_WL - [13:8] */
+#define WM5100_AIF1RX_SLOT_LEN_MASK 0x00FF /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM5100_AIF1RX_SLOT_LEN_SHIFT 0 /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM5100_AIF1RX_SLOT_LEN_WIDTH 8 /* AIF1RX_SLOT_LEN - [7:0] */
+
+/*
+ * R1289 (0x509) - Audio IF 1_10
+ */
+#define WM5100_AIF1TX1_SLOT_MASK 0x003F /* AIF1TX1_SLOT - [5:0] */
+#define WM5100_AIF1TX1_SLOT_SHIFT 0 /* AIF1TX1_SLOT - [5:0] */
+#define WM5100_AIF1TX1_SLOT_WIDTH 6 /* AIF1TX1_SLOT - [5:0] */
+
+/*
+ * R1290 (0x50A) - Audio IF 1_11
+ */
+#define WM5100_AIF1TX2_SLOT_MASK 0x003F /* AIF1TX2_SLOT - [5:0] */
+#define WM5100_AIF1TX2_SLOT_SHIFT 0 /* AIF1TX2_SLOT - [5:0] */
+#define WM5100_AIF1TX2_SLOT_WIDTH 6 /* AIF1TX2_SLOT - [5:0] */
+
+/*
+ * R1291 (0x50B) - Audio IF 1_12
+ */
+#define WM5100_AIF1TX3_SLOT_MASK 0x003F /* AIF1TX3_SLOT - [5:0] */
+#define WM5100_AIF1TX3_SLOT_SHIFT 0 /* AIF1TX3_SLOT - [5:0] */
+#define WM5100_AIF1TX3_SLOT_WIDTH 6 /* AIF1TX3_SLOT - [5:0] */
+
+/*
+ * R1292 (0x50C) - Audio IF 1_13
+ */
+#define WM5100_AIF1TX4_SLOT_MASK 0x003F /* AIF1TX4_SLOT - [5:0] */
+#define WM5100_AIF1TX4_SLOT_SHIFT 0 /* AIF1TX4_SLOT - [5:0] */
+#define WM5100_AIF1TX4_SLOT_WIDTH 6 /* AIF1TX4_SLOT - [5:0] */
+
+/*
+ * R1293 (0x50D) - Audio IF 1_14
+ */
+#define WM5100_AIF1TX5_SLOT_MASK 0x003F /* AIF1TX5_SLOT - [5:0] */
+#define WM5100_AIF1TX5_SLOT_SHIFT 0 /* AIF1TX5_SLOT - [5:0] */
+#define WM5100_AIF1TX5_SLOT_WIDTH 6 /* AIF1TX5_SLOT - [5:0] */
+
+/*
+ * R1294 (0x50E) - Audio IF 1_15
+ */
+#define WM5100_AIF1TX6_SLOT_MASK 0x003F /* AIF1TX6_SLOT - [5:0] */
+#define WM5100_AIF1TX6_SLOT_SHIFT 0 /* AIF1TX6_SLOT - [5:0] */
+#define WM5100_AIF1TX6_SLOT_WIDTH 6 /* AIF1TX6_SLOT - [5:0] */
+
+/*
+ * R1295 (0x50F) - Audio IF 1_16
+ */
+#define WM5100_AIF1TX7_SLOT_MASK 0x003F /* AIF1TX7_SLOT - [5:0] */
+#define WM5100_AIF1TX7_SLOT_SHIFT 0 /* AIF1TX7_SLOT - [5:0] */
+#define WM5100_AIF1TX7_SLOT_WIDTH 6 /* AIF1TX7_SLOT - [5:0] */
+
+/*
+ * R1296 (0x510) - Audio IF 1_17
+ */
+#define WM5100_AIF1TX8_SLOT_MASK 0x003F /* AIF1TX8_SLOT - [5:0] */
+#define WM5100_AIF1TX8_SLOT_SHIFT 0 /* AIF1TX8_SLOT - [5:0] */
+#define WM5100_AIF1TX8_SLOT_WIDTH 6 /* AIF1TX8_SLOT - [5:0] */
+
+/*
+ * R1297 (0x511) - Audio IF 1_18
+ */
+#define WM5100_AIF1RX1_SLOT_MASK 0x003F /* AIF1RX1_SLOT - [5:0] */
+#define WM5100_AIF1RX1_SLOT_SHIFT 0 /* AIF1RX1_SLOT - [5:0] */
+#define WM5100_AIF1RX1_SLOT_WIDTH 6 /* AIF1RX1_SLOT - [5:0] */
+
+/*
+ * R1298 (0x512) - Audio IF 1_19
+ */
+#define WM5100_AIF1RX2_SLOT_MASK 0x003F /* AIF1RX2_SLOT - [5:0] */
+#define WM5100_AIF1RX2_SLOT_SHIFT 0 /* AIF1RX2_SLOT - [5:0] */
+#define WM5100_AIF1RX2_SLOT_WIDTH 6 /* AIF1RX2_SLOT - [5:0] */
+
+/*
+ * R1299 (0x513) - Audio IF 1_20
+ */
+#define WM5100_AIF1RX3_SLOT_MASK 0x003F /* AIF1RX3_SLOT - [5:0] */
+#define WM5100_AIF1RX3_SLOT_SHIFT 0 /* AIF1RX3_SLOT - [5:0] */
+#define WM5100_AIF1RX3_SLOT_WIDTH 6 /* AIF1RX3_SLOT - [5:0] */
+
+/*
+ * R1300 (0x514) - Audio IF 1_21
+ */
+#define WM5100_AIF1RX4_SLOT_MASK 0x003F /* AIF1RX4_SLOT - [5:0] */
+#define WM5100_AIF1RX4_SLOT_SHIFT 0 /* AIF1RX4_SLOT - [5:0] */
+#define WM5100_AIF1RX4_SLOT_WIDTH 6 /* AIF1RX4_SLOT - [5:0] */
+
+/*
+ * R1301 (0x515) - Audio IF 1_22
+ */
+#define WM5100_AIF1RX5_SLOT_MASK 0x003F /* AIF1RX5_SLOT - [5:0] */
+#define WM5100_AIF1RX5_SLOT_SHIFT 0 /* AIF1RX5_SLOT - [5:0] */
+#define WM5100_AIF1RX5_SLOT_WIDTH 6 /* AIF1RX5_SLOT - [5:0] */
+
+/*
+ * R1302 (0x516) - Audio IF 1_23
+ */
+#define WM5100_AIF1RX6_SLOT_MASK 0x003F /* AIF1RX6_SLOT - [5:0] */
+#define WM5100_AIF1RX6_SLOT_SHIFT 0 /* AIF1RX6_SLOT - [5:0] */
+#define WM5100_AIF1RX6_SLOT_WIDTH 6 /* AIF1RX6_SLOT - [5:0] */
+
+/*
+ * R1303 (0x517) - Audio IF 1_24
+ */
+#define WM5100_AIF1RX7_SLOT_MASK 0x003F /* AIF1RX7_SLOT - [5:0] */
+#define WM5100_AIF1RX7_SLOT_SHIFT 0 /* AIF1RX7_SLOT - [5:0] */
+#define WM5100_AIF1RX7_SLOT_WIDTH 6 /* AIF1RX7_SLOT - [5:0] */
+
+/*
+ * R1304 (0x518) - Audio IF 1_25
+ */
+#define WM5100_AIF1RX8_SLOT_MASK 0x003F /* AIF1RX8_SLOT - [5:0] */
+#define WM5100_AIF1RX8_SLOT_SHIFT 0 /* AIF1RX8_SLOT - [5:0] */
+#define WM5100_AIF1RX8_SLOT_WIDTH 6 /* AIF1RX8_SLOT - [5:0] */
+
+/*
+ * R1305 (0x519) - Audio IF 1_26
+ */
+#define WM5100_AIF1TX8_ENA 0x0080 /* AIF1TX8_ENA */
+#define WM5100_AIF1TX8_ENA_MASK 0x0080 /* AIF1TX8_ENA */
+#define WM5100_AIF1TX8_ENA_SHIFT 7 /* AIF1TX8_ENA */
+#define WM5100_AIF1TX8_ENA_WIDTH 1 /* AIF1TX8_ENA */
+#define WM5100_AIF1TX7_ENA 0x0040 /* AIF1TX7_ENA */
+#define WM5100_AIF1TX7_ENA_MASK 0x0040 /* AIF1TX7_ENA */
+#define WM5100_AIF1TX7_ENA_SHIFT 6 /* AIF1TX7_ENA */
+#define WM5100_AIF1TX7_ENA_WIDTH 1 /* AIF1TX7_ENA */
+#define WM5100_AIF1TX6_ENA 0x0020 /* AIF1TX6_ENA */
+#define WM5100_AIF1TX6_ENA_MASK 0x0020 /* AIF1TX6_ENA */
+#define WM5100_AIF1TX6_ENA_SHIFT 5 /* AIF1TX6_ENA */
+#define WM5100_AIF1TX6_ENA_WIDTH 1 /* AIF1TX6_ENA */
+#define WM5100_AIF1TX5_ENA 0x0010 /* AIF1TX5_ENA */
+#define WM5100_AIF1TX5_ENA_MASK 0x0010 /* AIF1TX5_ENA */
+#define WM5100_AIF1TX5_ENA_SHIFT 4 /* AIF1TX5_ENA */
+#define WM5100_AIF1TX5_ENA_WIDTH 1 /* AIF1TX5_ENA */
+#define WM5100_AIF1TX4_ENA 0x0008 /* AIF1TX4_ENA */
+#define WM5100_AIF1TX4_ENA_MASK 0x0008 /* AIF1TX4_ENA */
+#define WM5100_AIF1TX4_ENA_SHIFT 3 /* AIF1TX4_ENA */
+#define WM5100_AIF1TX4_ENA_WIDTH 1 /* AIF1TX4_ENA */
+#define WM5100_AIF1TX3_ENA 0x0004 /* AIF1TX3_ENA */
+#define WM5100_AIF1TX3_ENA_MASK 0x0004 /* AIF1TX3_ENA */
+#define WM5100_AIF1TX3_ENA_SHIFT 2 /* AIF1TX3_ENA */
+#define WM5100_AIF1TX3_ENA_WIDTH 1 /* AIF1TX3_ENA */
+#define WM5100_AIF1TX2_ENA 0x0002 /* AIF1TX2_ENA */
+#define WM5100_AIF1TX2_ENA_MASK 0x0002 /* AIF1TX2_ENA */
+#define WM5100_AIF1TX2_ENA_SHIFT 1 /* AIF1TX2_ENA */
+#define WM5100_AIF1TX2_ENA_WIDTH 1 /* AIF1TX2_ENA */
+#define WM5100_AIF1TX1_ENA 0x0001 /* AIF1TX1_ENA */
+#define WM5100_AIF1TX1_ENA_MASK 0x0001 /* AIF1TX1_ENA */
+#define WM5100_AIF1TX1_ENA_SHIFT 0 /* AIF1TX1_ENA */
+#define WM5100_AIF1TX1_ENA_WIDTH 1 /* AIF1TX1_ENA */
+
+/*
+ * R1306 (0x51A) - Audio IF 1_27
+ */
+#define WM5100_AIF1RX8_ENA 0x0080 /* AIF1RX8_ENA */
+#define WM5100_AIF1RX8_ENA_MASK 0x0080 /* AIF1RX8_ENA */
+#define WM5100_AIF1RX8_ENA_SHIFT 7 /* AIF1RX8_ENA */
+#define WM5100_AIF1RX8_ENA_WIDTH 1 /* AIF1RX8_ENA */
+#define WM5100_AIF1RX7_ENA 0x0040 /* AIF1RX7_ENA */
+#define WM5100_AIF1RX7_ENA_MASK 0x0040 /* AIF1RX7_ENA */
+#define WM5100_AIF1RX7_ENA_SHIFT 6 /* AIF1RX7_ENA */
+#define WM5100_AIF1RX7_ENA_WIDTH 1 /* AIF1RX7_ENA */
+#define WM5100_AIF1RX6_ENA 0x0020 /* AIF1RX6_ENA */
+#define WM5100_AIF1RX6_ENA_MASK 0x0020 /* AIF1RX6_ENA */
+#define WM5100_AIF1RX6_ENA_SHIFT 5 /* AIF1RX6_ENA */
+#define WM5100_AIF1RX6_ENA_WIDTH 1 /* AIF1RX6_ENA */
+#define WM5100_AIF1RX5_ENA 0x0010 /* AIF1RX5_ENA */
+#define WM5100_AIF1RX5_ENA_MASK 0x0010 /* AIF1RX5_ENA */
+#define WM5100_AIF1RX5_ENA_SHIFT 4 /* AIF1RX5_ENA */
+#define WM5100_AIF1RX5_ENA_WIDTH 1 /* AIF1RX5_ENA */
+#define WM5100_AIF1RX4_ENA 0x0008 /* AIF1RX4_ENA */
+#define WM5100_AIF1RX4_ENA_MASK 0x0008 /* AIF1RX4_ENA */
+#define WM5100_AIF1RX4_ENA_SHIFT 3 /* AIF1RX4_ENA */
+#define WM5100_AIF1RX4_ENA_WIDTH 1 /* AIF1RX4_ENA */
+#define WM5100_AIF1RX3_ENA 0x0004 /* AIF1RX3_ENA */
+#define WM5100_AIF1RX3_ENA_MASK 0x0004 /* AIF1RX3_ENA */
+#define WM5100_AIF1RX3_ENA_SHIFT 2 /* AIF1RX3_ENA */
+#define WM5100_AIF1RX3_ENA_WIDTH 1 /* AIF1RX3_ENA */
+#define WM5100_AIF1RX2_ENA 0x0002 /* AIF1RX2_ENA */
+#define WM5100_AIF1RX2_ENA_MASK 0x0002 /* AIF1RX2_ENA */
+#define WM5100_AIF1RX2_ENA_SHIFT 1 /* AIF1RX2_ENA */
+#define WM5100_AIF1RX2_ENA_WIDTH 1 /* AIF1RX2_ENA */
+#define WM5100_AIF1RX1_ENA 0x0001 /* AIF1RX1_ENA */
+#define WM5100_AIF1RX1_ENA_MASK 0x0001 /* AIF1RX1_ENA */
+#define WM5100_AIF1RX1_ENA_SHIFT 0 /* AIF1RX1_ENA */
+#define WM5100_AIF1RX1_ENA_WIDTH 1 /* AIF1RX1_ENA */
+
+/*
+ * R1344 (0x540) - Audio IF 2_1
+ */
+#define WM5100_AIF2_BCLK_INV 0x0080 /* AIF2_BCLK_INV */
+#define WM5100_AIF2_BCLK_INV_MASK 0x0080 /* AIF2_BCLK_INV */
+#define WM5100_AIF2_BCLK_INV_SHIFT 7 /* AIF2_BCLK_INV */
+#define WM5100_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */
+#define WM5100_AIF2_BCLK_FRC 0x0040 /* AIF2_BCLK_FRC */
+#define WM5100_AIF2_BCLK_FRC_MASK 0x0040 /* AIF2_BCLK_FRC */
+#define WM5100_AIF2_BCLK_FRC_SHIFT 6 /* AIF2_BCLK_FRC */
+#define WM5100_AIF2_BCLK_FRC_WIDTH 1 /* AIF2_BCLK_FRC */
+#define WM5100_AIF2_BCLK_MSTR 0x0020 /* AIF2_BCLK_MSTR */
+#define WM5100_AIF2_BCLK_MSTR_MASK 0x0020 /* AIF2_BCLK_MSTR */
+#define WM5100_AIF2_BCLK_MSTR_SHIFT 5 /* AIF2_BCLK_MSTR */
+#define WM5100_AIF2_BCLK_MSTR_WIDTH 1 /* AIF2_BCLK_MSTR */
+#define WM5100_AIF2_BCLK_FREQ_MASK 0x001F /* AIF2_BCLK_FREQ - [4:0] */
+#define WM5100_AIF2_BCLK_FREQ_SHIFT 0 /* AIF2_BCLK_FREQ - [4:0] */
+#define WM5100_AIF2_BCLK_FREQ_WIDTH 5 /* AIF2_BCLK_FREQ - [4:0] */
+
+/*
+ * R1345 (0x541) - Audio IF 2_2
+ */
+#define WM5100_AIF2TX_DAT_TRI 0x0020 /* AIF2TX_DAT_TRI */
+#define WM5100_AIF2TX_DAT_TRI_MASK 0x0020 /* AIF2TX_DAT_TRI */
+#define WM5100_AIF2TX_DAT_TRI_SHIFT 5 /* AIF2TX_DAT_TRI */
+#define WM5100_AIF2TX_DAT_TRI_WIDTH 1 /* AIF2TX_DAT_TRI */
+#define WM5100_AIF2TX_LRCLK_SRC 0x0008 /* AIF2TX_LRCLK_SRC */
+#define WM5100_AIF2TX_LRCLK_SRC_MASK 0x0008 /* AIF2TX_LRCLK_SRC */
+#define WM5100_AIF2TX_LRCLK_SRC_SHIFT 3 /* AIF2TX_LRCLK_SRC */
+#define WM5100_AIF2TX_LRCLK_SRC_WIDTH 1 /* AIF2TX_LRCLK_SRC */
+#define WM5100_AIF2TX_LRCLK_INV 0x0004 /* AIF2TX_LRCLK_INV */
+#define WM5100_AIF2TX_LRCLK_INV_MASK 0x0004 /* AIF2TX_LRCLK_INV */
+#define WM5100_AIF2TX_LRCLK_INV_SHIFT 2 /* AIF2TX_LRCLK_INV */
+#define WM5100_AIF2TX_LRCLK_INV_WIDTH 1 /* AIF2TX_LRCLK_INV */
+#define WM5100_AIF2TX_LRCLK_FRC 0x0002 /* AIF2TX_LRCLK_FRC */
+#define WM5100_AIF2TX_LRCLK_FRC_MASK 0x0002 /* AIF2TX_LRCLK_FRC */
+#define WM5100_AIF2TX_LRCLK_FRC_SHIFT 1 /* AIF2TX_LRCLK_FRC */
+#define WM5100_AIF2TX_LRCLK_FRC_WIDTH 1 /* AIF2TX_LRCLK_FRC */
+#define WM5100_AIF2TX_LRCLK_MSTR 0x0001 /* AIF2TX_LRCLK_MSTR */
+#define WM5100_AIF2TX_LRCLK_MSTR_MASK 0x0001 /* AIF2TX_LRCLK_MSTR */
+#define WM5100_AIF2TX_LRCLK_MSTR_SHIFT 0 /* AIF2TX_LRCLK_MSTR */
+#define WM5100_AIF2TX_LRCLK_MSTR_WIDTH 1 /* AIF2TX_LRCLK_MSTR */
+
+/*
+ * R1346 (0x542) - Audio IF 2_3
+ */
+#define WM5100_AIF2RX_LRCLK_INV 0x0004 /* AIF2RX_LRCLK_INV */
+#define WM5100_AIF2RX_LRCLK_INV_MASK 0x0004 /* AIF2RX_LRCLK_INV */
+#define WM5100_AIF2RX_LRCLK_INV_SHIFT 2 /* AIF2RX_LRCLK_INV */
+#define WM5100_AIF2RX_LRCLK_INV_WIDTH 1 /* AIF2RX_LRCLK_INV */
+#define WM5100_AIF2RX_LRCLK_FRC 0x0002 /* AIF2RX_LRCLK_FRC */
+#define WM5100_AIF2RX_LRCLK_FRC_MASK 0x0002 /* AIF2RX_LRCLK_FRC */
+#define WM5100_AIF2RX_LRCLK_FRC_SHIFT 1 /* AIF2RX_LRCLK_FRC */
+#define WM5100_AIF2RX_LRCLK_FRC_WIDTH 1 /* AIF2RX_LRCLK_FRC */
+#define WM5100_AIF2RX_LRCLK_MSTR 0x0001 /* AIF2RX_LRCLK_MSTR */
+#define WM5100_AIF2RX_LRCLK_MSTR_MASK 0x0001 /* AIF2RX_LRCLK_MSTR */
+#define WM5100_AIF2RX_LRCLK_MSTR_SHIFT 0 /* AIF2RX_LRCLK_MSTR */
+#define WM5100_AIF2RX_LRCLK_MSTR_WIDTH 1 /* AIF2RX_LRCLK_MSTR */
+
+/*
+ * R1347 (0x543) - Audio IF 2_4
+ */
+#define WM5100_AIF2_TRI 0x0040 /* AIF2_TRI */
+#define WM5100_AIF2_TRI_MASK 0x0040 /* AIF2_TRI */
+#define WM5100_AIF2_TRI_SHIFT 6 /* AIF2_TRI */
+#define WM5100_AIF2_TRI_WIDTH 1 /* AIF2_TRI */
+#define WM5100_AIF2_RATE_MASK 0x0003 /* AIF2_RATE - [1:0] */
+#define WM5100_AIF2_RATE_SHIFT 0 /* AIF2_RATE - [1:0] */
+#define WM5100_AIF2_RATE_WIDTH 2 /* AIF2_RATE - [1:0] */
+
+/*
+ * R1348 (0x544) - Audio IF 2_5
+ */
+#define WM5100_AIF2_FMT_MASK 0x0007 /* AIF2_FMT - [2:0] */
+#define WM5100_AIF2_FMT_SHIFT 0 /* AIF2_FMT - [2:0] */
+#define WM5100_AIF2_FMT_WIDTH 3 /* AIF2_FMT - [2:0] */
+
+/*
+ * R1349 (0x545) - Audio IF 2_6
+ */
+#define WM5100_AIF2TX_BCPF_MASK 0x1FFF /* AIF2TX_BCPF - [12:0] */
+#define WM5100_AIF2TX_BCPF_SHIFT 0 /* AIF2TX_BCPF - [12:0] */
+#define WM5100_AIF2TX_BCPF_WIDTH 13 /* AIF2TX_BCPF - [12:0] */
+
+/*
+ * R1350 (0x546) - Audio IF 2_7
+ */
+#define WM5100_AIF2RX_BCPF_MASK 0x1FFF /* AIF2RX_BCPF - [12:0] */
+#define WM5100_AIF2RX_BCPF_SHIFT 0 /* AIF2RX_BCPF - [12:0] */
+#define WM5100_AIF2RX_BCPF_WIDTH 13 /* AIF2RX_BCPF - [12:0] */
+
+/*
+ * R1351 (0x547) - Audio IF 2_8
+ */
+#define WM5100_AIF2TX_WL_MASK 0x3F00 /* AIF2TX_WL - [13:8] */
+#define WM5100_AIF2TX_WL_SHIFT 8 /* AIF2TX_WL - [13:8] */
+#define WM5100_AIF2TX_WL_WIDTH 6 /* AIF2TX_WL - [13:8] */
+#define WM5100_AIF2TX_SLOT_LEN_MASK 0x00FF /* AIF2TX_SLOT_LEN - [7:0] */
+#define WM5100_AIF2TX_SLOT_LEN_SHIFT 0 /* AIF2TX_SLOT_LEN - [7:0] */
+#define WM5100_AIF2TX_SLOT_LEN_WIDTH 8 /* AIF2TX_SLOT_LEN - [7:0] */
+
+/*
+ * R1352 (0x548) - Audio IF 2_9
+ */
+#define WM5100_AIF2RX_WL_MASK 0x3F00 /* AIF2RX_WL - [13:8] */
+#define WM5100_AIF2RX_WL_SHIFT 8 /* AIF2RX_WL - [13:8] */
+#define WM5100_AIF2RX_WL_WIDTH 6 /* AIF2RX_WL - [13:8] */
+#define WM5100_AIF2RX_SLOT_LEN_MASK 0x00FF /* AIF2RX_SLOT_LEN - [7:0] */
+#define WM5100_AIF2RX_SLOT_LEN_SHIFT 0 /* AIF2RX_SLOT_LEN - [7:0] */
+#define WM5100_AIF2RX_SLOT_LEN_WIDTH 8 /* AIF2RX_SLOT_LEN - [7:0] */
+
+/*
+ * R1353 (0x549) - Audio IF 2_10
+ */
+#define WM5100_AIF2TX1_SLOT_MASK 0x003F /* AIF2TX1_SLOT - [5:0] */
+#define WM5100_AIF2TX1_SLOT_SHIFT 0 /* AIF2TX1_SLOT - [5:0] */
+#define WM5100_AIF2TX1_SLOT_WIDTH 6 /* AIF2TX1_SLOT - [5:0] */
+
+/*
+ * R1354 (0x54A) - Audio IF 2_11
+ */
+#define WM5100_AIF2TX2_SLOT_MASK 0x003F /* AIF2TX2_SLOT - [5:0] */
+#define WM5100_AIF2TX2_SLOT_SHIFT 0 /* AIF2TX2_SLOT - [5:0] */
+#define WM5100_AIF2TX2_SLOT_WIDTH 6 /* AIF2TX2_SLOT - [5:0] */
+
+/*
+ * R1361 (0x551) - Audio IF 2_18
+ */
+#define WM5100_AIF2RX1_SLOT_MASK 0x003F /* AIF2RX1_SLOT - [5:0] */
+#define WM5100_AIF2RX1_SLOT_SHIFT 0 /* AIF2RX1_SLOT - [5:0] */
+#define WM5100_AIF2RX1_SLOT_WIDTH 6 /* AIF2RX1_SLOT - [5:0] */
+
+/*
+ * R1362 (0x552) - Audio IF 2_19
+ */
+#define WM5100_AIF2RX2_SLOT_MASK 0x003F /* AIF2RX2_SLOT - [5:0] */
+#define WM5100_AIF2RX2_SLOT_SHIFT 0 /* AIF2RX2_SLOT - [5:0] */
+#define WM5100_AIF2RX2_SLOT_WIDTH 6 /* AIF2RX2_SLOT - [5:0] */
+
+/*
+ * R1369 (0x559) - Audio IF 2_26
+ */
+#define WM5100_AIF2TX2_ENA 0x0002 /* AIF2TX2_ENA */
+#define WM5100_AIF2TX2_ENA_MASK 0x0002 /* AIF2TX2_ENA */
+#define WM5100_AIF2TX2_ENA_SHIFT 1 /* AIF2TX2_ENA */
+#define WM5100_AIF2TX2_ENA_WIDTH 1 /* AIF2TX2_ENA */
+#define WM5100_AIF2TX1_ENA 0x0001 /* AIF2TX1_ENA */
+#define WM5100_AIF2TX1_ENA_MASK 0x0001 /* AIF2TX1_ENA */
+#define WM5100_AIF2TX1_ENA_SHIFT 0 /* AIF2TX1_ENA */
+#define WM5100_AIF2TX1_ENA_WIDTH 1 /* AIF2TX1_ENA */
+
+/*
+ * R1370 (0x55A) - Audio IF 2_27
+ */
+#define WM5100_AIF2RX2_ENA 0x0002 /* AIF2RX2_ENA */
+#define WM5100_AIF2RX2_ENA_MASK 0x0002 /* AIF2RX2_ENA */
+#define WM5100_AIF2RX2_ENA_SHIFT 1 /* AIF2RX2_ENA */
+#define WM5100_AIF2RX2_ENA_WIDTH 1 /* AIF2RX2_ENA */
+#define WM5100_AIF2RX1_ENA 0x0001 /* AIF2RX1_ENA */
+#define WM5100_AIF2RX1_ENA_MASK 0x0001 /* AIF2RX1_ENA */
+#define WM5100_AIF2RX1_ENA_SHIFT 0 /* AIF2RX1_ENA */
+#define WM5100_AIF2RX1_ENA_WIDTH 1 /* AIF2RX1_ENA */
+
+/*
+ * R1408 (0x580) - Audio IF 3_1
+ */
+#define WM5100_AIF3_BCLK_INV 0x0080 /* AIF3_BCLK_INV */
+#define WM5100_AIF3_BCLK_INV_MASK 0x0080 /* AIF3_BCLK_INV */
+#define WM5100_AIF3_BCLK_INV_SHIFT 7 /* AIF3_BCLK_INV */
+#define WM5100_AIF3_BCLK_INV_WIDTH 1 /* AIF3_BCLK_INV */
+#define WM5100_AIF3_BCLK_FRC 0x0040 /* AIF3_BCLK_FRC */
+#define WM5100_AIF3_BCLK_FRC_MASK 0x0040 /* AIF3_BCLK_FRC */
+#define WM5100_AIF3_BCLK_FRC_SHIFT 6 /* AIF3_BCLK_FRC */
+#define WM5100_AIF3_BCLK_FRC_WIDTH 1 /* AIF3_BCLK_FRC */
+#define WM5100_AIF3_BCLK_MSTR 0x0020 /* AIF3_BCLK_MSTR */
+#define WM5100_AIF3_BCLK_MSTR_MASK 0x0020 /* AIF3_BCLK_MSTR */
+#define WM5100_AIF3_BCLK_MSTR_SHIFT 5 /* AIF3_BCLK_MSTR */
+#define WM5100_AIF3_BCLK_MSTR_WIDTH 1 /* AIF3_BCLK_MSTR */
+#define WM5100_AIF3_BCLK_FREQ_MASK 0x001F /* AIF3_BCLK_FREQ - [4:0] */
+#define WM5100_AIF3_BCLK_FREQ_SHIFT 0 /* AIF3_BCLK_FREQ - [4:0] */
+#define WM5100_AIF3_BCLK_FREQ_WIDTH 5 /* AIF3_BCLK_FREQ - [4:0] */
+
+/*
+ * R1409 (0x581) - Audio IF 3_2
+ */
+#define WM5100_AIF3TX_DAT_TRI 0x0020 /* AIF3TX_DAT_TRI */
+#define WM5100_AIF3TX_DAT_TRI_MASK 0x0020 /* AIF3TX_DAT_TRI */
+#define WM5100_AIF3TX_DAT_TRI_SHIFT 5 /* AIF3TX_DAT_TRI */
+#define WM5100_AIF3TX_DAT_TRI_WIDTH 1 /* AIF3TX_DAT_TRI */
+#define WM5100_AIF3TX_LRCLK_SRC 0x0008 /* AIF3TX_LRCLK_SRC */
+#define WM5100_AIF3TX_LRCLK_SRC_MASK 0x0008 /* AIF3TX_LRCLK_SRC */
+#define WM5100_AIF3TX_LRCLK_SRC_SHIFT 3 /* AIF3TX_LRCLK_SRC */
+#define WM5100_AIF3TX_LRCLK_SRC_WIDTH 1 /* AIF3TX_LRCLK_SRC */
+#define WM5100_AIF3TX_LRCLK_INV 0x0004 /* AIF3TX_LRCLK_INV */
+#define WM5100_AIF3TX_LRCLK_INV_MASK 0x0004 /* AIF3TX_LRCLK_INV */
+#define WM5100_AIF3TX_LRCLK_INV_SHIFT 2 /* AIF3TX_LRCLK_INV */
+#define WM5100_AIF3TX_LRCLK_INV_WIDTH 1 /* AIF3TX_LRCLK_INV */
+#define WM5100_AIF3TX_LRCLK_FRC 0x0002 /* AIF3TX_LRCLK_FRC */
+#define WM5100_AIF3TX_LRCLK_FRC_MASK 0x0002 /* AIF3TX_LRCLK_FRC */
+#define WM5100_AIF3TX_LRCLK_FRC_SHIFT 1 /* AIF3TX_LRCLK_FRC */
+#define WM5100_AIF3TX_LRCLK_FRC_WIDTH 1 /* AIF3TX_LRCLK_FRC */
+#define WM5100_AIF3TX_LRCLK_MSTR 0x0001 /* AIF3TX_LRCLK_MSTR */
+#define WM5100_AIF3TX_LRCLK_MSTR_MASK 0x0001 /* AIF3TX_LRCLK_MSTR */
+#define WM5100_AIF3TX_LRCLK_MSTR_SHIFT 0 /* AIF3TX_LRCLK_MSTR */
+#define WM5100_AIF3TX_LRCLK_MSTR_WIDTH 1 /* AIF3TX_LRCLK_MSTR */
+
+/*
+ * R1410 (0x582) - Audio IF 3_3
+ */
+#define WM5100_AIF3RX_LRCLK_INV 0x0004 /* AIF3RX_LRCLK_INV */
+#define WM5100_AIF3RX_LRCLK_INV_MASK 0x0004 /* AIF3RX_LRCLK_INV */
+#define WM5100_AIF3RX_LRCLK_INV_SHIFT 2 /* AIF3RX_LRCLK_INV */
+#define WM5100_AIF3RX_LRCLK_INV_WIDTH 1 /* AIF3RX_LRCLK_INV */
+#define WM5100_AIF3RX_LRCLK_FRC 0x0002 /* AIF3RX_LRCLK_FRC */
+#define WM5100_AIF3RX_LRCLK_FRC_MASK 0x0002 /* AIF3RX_LRCLK_FRC */
+#define WM5100_AIF3RX_LRCLK_FRC_SHIFT 1 /* AIF3RX_LRCLK_FRC */
+#define WM5100_AIF3RX_LRCLK_FRC_WIDTH 1 /* AIF3RX_LRCLK_FRC */
+#define WM5100_AIF3RX_LRCLK_MSTR 0x0001 /* AIF3RX_LRCLK_MSTR */
+#define WM5100_AIF3RX_LRCLK_MSTR_MASK 0x0001 /* AIF3RX_LRCLK_MSTR */
+#define WM5100_AIF3RX_LRCLK_MSTR_SHIFT 0 /* AIF3RX_LRCLK_MSTR */
+#define WM5100_AIF3RX_LRCLK_MSTR_WIDTH 1 /* AIF3RX_LRCLK_MSTR */
+
+/*
+ * R1411 (0x583) - Audio IF 3_4
+ */
+#define WM5100_AIF3_TRI 0x0040 /* AIF3_TRI */
+#define WM5100_AIF3_TRI_MASK 0x0040 /* AIF3_TRI */
+#define WM5100_AIF3_TRI_SHIFT 6 /* AIF3_TRI */
+#define WM5100_AIF3_TRI_WIDTH 1 /* AIF3_TRI */
+#define WM5100_AIF3_RATE_MASK 0x0003 /* AIF3_RATE - [1:0] */
+#define WM5100_AIF3_RATE_SHIFT 0 /* AIF3_RATE - [1:0] */
+#define WM5100_AIF3_RATE_WIDTH 2 /* AIF3_RATE - [1:0] */
+
+/*
+ * R1412 (0x584) - Audio IF 3_5
+ */
+#define WM5100_AIF3_FMT_MASK 0x0007 /* AIF3_FMT - [2:0] */
+#define WM5100_AIF3_FMT_SHIFT 0 /* AIF3_FMT - [2:0] */
+#define WM5100_AIF3_FMT_WIDTH 3 /* AIF3_FMT - [2:0] */
+
+/*
+ * R1413 (0x585) - Audio IF 3_6
+ */
+#define WM5100_AIF3TX_BCPF_MASK 0x1FFF /* AIF3TX_BCPF - [12:0] */
+#define WM5100_AIF3TX_BCPF_SHIFT 0 /* AIF3TX_BCPF - [12:0] */
+#define WM5100_AIF3TX_BCPF_WIDTH 13 /* AIF3TX_BCPF - [12:0] */
+
+/*
+ * R1414 (0x586) - Audio IF 3_7
+ */
+#define WM5100_AIF3RX_BCPF_MASK 0x1FFF /* AIF3RX_BCPF - [12:0] */
+#define WM5100_AIF3RX_BCPF_SHIFT 0 /* AIF3RX_BCPF - [12:0] */
+#define WM5100_AIF3RX_BCPF_WIDTH 13 /* AIF3RX_BCPF - [12:0] */
+
+/*
+ * R1415 (0x587) - Audio IF 3_8
+ */
+#define WM5100_AIF3TX_WL_MASK 0x3F00 /* AIF3TX_WL - [13:8] */
+#define WM5100_AIF3TX_WL_SHIFT 8 /* AIF3TX_WL - [13:8] */
+#define WM5100_AIF3TX_WL_WIDTH 6 /* AIF3TX_WL - [13:8] */
+#define WM5100_AIF3TX_SLOT_LEN_MASK 0x00FF /* AIF3TX_SLOT_LEN - [7:0] */
+#define WM5100_AIF3TX_SLOT_LEN_SHIFT 0 /* AIF3TX_SLOT_LEN - [7:0] */
+#define WM5100_AIF3TX_SLOT_LEN_WIDTH 8 /* AIF3TX_SLOT_LEN - [7:0] */
+
+/*
+ * R1416 (0x588) - Audio IF 3_9
+ */
+#define WM5100_AIF3RX_WL_MASK 0x3F00 /* AIF3RX_WL - [13:8] */
+#define WM5100_AIF3RX_WL_SHIFT 8 /* AIF3RX_WL - [13:8] */
+#define WM5100_AIF3RX_WL_WIDTH 6 /* AIF3RX_WL - [13:8] */
+#define WM5100_AIF3RX_SLOT_LEN_MASK 0x00FF /* AIF3RX_SLOT_LEN - [7:0] */
+#define WM5100_AIF3RX_SLOT_LEN_SHIFT 0 /* AIF3RX_SLOT_LEN - [7:0] */
+#define WM5100_AIF3RX_SLOT_LEN_WIDTH 8 /* AIF3RX_SLOT_LEN - [7:0] */
+
+/*
+ * R1417 (0x589) - Audio IF 3_10
+ */
+#define WM5100_AIF3TX1_SLOT_MASK 0x003F /* AIF3TX1_SLOT - [5:0] */
+#define WM5100_AIF3TX1_SLOT_SHIFT 0 /* AIF3TX1_SLOT - [5:0] */
+#define WM5100_AIF3TX1_SLOT_WIDTH 6 /* AIF3TX1_SLOT - [5:0] */
+
+/*
+ * R1418 (0x58A) - Audio IF 3_11
+ */
+#define WM5100_AIF3TX2_SLOT_MASK 0x003F /* AIF3TX2_SLOT - [5:0] */
+#define WM5100_AIF3TX2_SLOT_SHIFT 0 /* AIF3TX2_SLOT - [5:0] */
+#define WM5100_AIF3TX2_SLOT_WIDTH 6 /* AIF3TX2_SLOT - [5:0] */
+
+/*
+ * R1425 (0x591) - Audio IF 3_18
+ */
+#define WM5100_AIF3RX1_SLOT_MASK 0x003F /* AIF3RX1_SLOT - [5:0] */
+#define WM5100_AIF3RX1_SLOT_SHIFT 0 /* AIF3RX1_SLOT - [5:0] */
+#define WM5100_AIF3RX1_SLOT_WIDTH 6 /* AIF3RX1_SLOT - [5:0] */
+
+/*
+ * R1426 (0x592) - Audio IF 3_19
+ */
+#define WM5100_AIF3RX2_SLOT_MASK 0x003F /* AIF3RX2_SLOT - [5:0] */
+#define WM5100_AIF3RX2_SLOT_SHIFT 0 /* AIF3RX2_SLOT - [5:0] */
+#define WM5100_AIF3RX2_SLOT_WIDTH 6 /* AIF3RX2_SLOT - [5:0] */
+
+/*
+ * R1433 (0x599) - Audio IF 3_26
+ */
+#define WM5100_AIF3TX2_ENA 0x0002 /* AIF3TX2_ENA */
+#define WM5100_AIF3TX2_ENA_MASK 0x0002 /* AIF3TX2_ENA */
+#define WM5100_AIF3TX2_ENA_SHIFT 1 /* AIF3TX2_ENA */
+#define WM5100_AIF3TX2_ENA_WIDTH 1 /* AIF3TX2_ENA */
+#define WM5100_AIF3TX1_ENA 0x0001 /* AIF3TX1_ENA */
+#define WM5100_AIF3TX1_ENA_MASK 0x0001 /* AIF3TX1_ENA */
+#define WM5100_AIF3TX1_ENA_SHIFT 0 /* AIF3TX1_ENA */
+#define WM5100_AIF3TX1_ENA_WIDTH 1 /* AIF3TX1_ENA */
+
+/*
+ * R1434 (0x59A) - Audio IF 3_27
+ */
+#define WM5100_AIF3RX2_ENA 0x0002 /* AIF3RX2_ENA */
+#define WM5100_AIF3RX2_ENA_MASK 0x0002 /* AIF3RX2_ENA */
+#define WM5100_AIF3RX2_ENA_SHIFT 1 /* AIF3RX2_ENA */
+#define WM5100_AIF3RX2_ENA_WIDTH 1 /* AIF3RX2_ENA */
+#define WM5100_AIF3RX1_ENA 0x0001 /* AIF3RX1_ENA */
+#define WM5100_AIF3RX1_ENA_MASK 0x0001 /* AIF3RX1_ENA */
+#define WM5100_AIF3RX1_ENA_SHIFT 0 /* AIF3RX1_ENA */
+#define WM5100_AIF3RX1_ENA_WIDTH 1 /* AIF3RX1_ENA */
+
+#define WM5100_MIXER_VOL_MASK 0x00FE /* MIXER_VOL - [7:1] */
+#define WM5100_MIXER_VOL_SHIFT 1 /* MIXER_VOL - [7:1] */
+#define WM5100_MIXER_VOL_WIDTH 7 /* MIXER_VOL - [7:1] */
+
+/*
+ * R3072 (0xC00) - GPIO CTRL 1
+ */
+#define WM5100_GP1_DIR 0x8000 /* GP1_DIR */
+#define WM5100_GP1_DIR_MASK 0x8000 /* GP1_DIR */
+#define WM5100_GP1_DIR_SHIFT 15 /* GP1_DIR */
+#define WM5100_GP1_DIR_WIDTH 1 /* GP1_DIR */
+#define WM5100_GP1_PU 0x4000 /* GP1_PU */
+#define WM5100_GP1_PU_MASK 0x4000 /* GP1_PU */
+#define WM5100_GP1_PU_SHIFT 14 /* GP1_PU */
+#define WM5100_GP1_PU_WIDTH 1 /* GP1_PU */
+#define WM5100_GP1_PD 0x2000 /* GP1_PD */
+#define WM5100_GP1_PD_MASK 0x2000 /* GP1_PD */
+#define WM5100_GP1_PD_SHIFT 13 /* GP1_PD */
+#define WM5100_GP1_PD_WIDTH 1 /* GP1_PD */
+#define WM5100_GP1_POL 0x0400 /* GP1_POL */
+#define WM5100_GP1_POL_MASK 0x0400 /* GP1_POL */
+#define WM5100_GP1_POL_SHIFT 10 /* GP1_POL */
+#define WM5100_GP1_POL_WIDTH 1 /* GP1_POL */
+#define WM5100_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */
+#define WM5100_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */
+#define WM5100_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */
+#define WM5100_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */
+#define WM5100_GP1_DB 0x0100 /* GP1_DB */
+#define WM5100_GP1_DB_MASK 0x0100 /* GP1_DB */
+#define WM5100_GP1_DB_SHIFT 8 /* GP1_DB */
+#define WM5100_GP1_DB_WIDTH 1 /* GP1_DB */
+#define WM5100_GP1_LVL 0x0040 /* GP1_LVL */
+#define WM5100_GP1_LVL_MASK 0x0040 /* GP1_LVL */
+#define WM5100_GP1_LVL_SHIFT 6 /* GP1_LVL */
+#define WM5100_GP1_LVL_WIDTH 1 /* GP1_LVL */
+#define WM5100_GP1_FN_MASK 0x003F /* GP1_FN - [5:0] */
+#define WM5100_GP1_FN_SHIFT 0 /* GP1_FN - [5:0] */
+#define WM5100_GP1_FN_WIDTH 6 /* GP1_FN - [5:0] */
+
+/*
+ * R3073 (0xC01) - GPIO CTRL 2
+ */
+#define WM5100_GP2_DIR 0x8000 /* GP2_DIR */
+#define WM5100_GP2_DIR_MASK 0x8000 /* GP2_DIR */
+#define WM5100_GP2_DIR_SHIFT 15 /* GP2_DIR */
+#define WM5100_GP2_DIR_WIDTH 1 /* GP2_DIR */
+#define WM5100_GP2_PU 0x4000 /* GP2_PU */
+#define WM5100_GP2_PU_MASK 0x4000 /* GP2_PU */
+#define WM5100_GP2_PU_SHIFT 14 /* GP2_PU */
+#define WM5100_GP2_PU_WIDTH 1 /* GP2_PU */
+#define WM5100_GP2_PD 0x2000 /* GP2_PD */
+#define WM5100_GP2_PD_MASK 0x2000 /* GP2_PD */
+#define WM5100_GP2_PD_SHIFT 13 /* GP2_PD */
+#define WM5100_GP2_PD_WIDTH 1 /* GP2_PD */
+#define WM5100_GP2_POL 0x0400 /* GP2_POL */
+#define WM5100_GP2_POL_MASK 0x0400 /* GP2_POL */
+#define WM5100_GP2_POL_SHIFT 10 /* GP2_POL */
+#define WM5100_GP2_POL_WIDTH 1 /* GP2_POL */
+#define WM5100_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */
+#define WM5100_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */
+#define WM5100_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */
+#define WM5100_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */
+#define WM5100_GP2_DB 0x0100 /* GP2_DB */
+#define WM5100_GP2_DB_MASK 0x0100 /* GP2_DB */
+#define WM5100_GP2_DB_SHIFT 8 /* GP2_DB */
+#define WM5100_GP2_DB_WIDTH 1 /* GP2_DB */
+#define WM5100_GP2_LVL 0x0040 /* GP2_LVL */
+#define WM5100_GP2_LVL_MASK 0x0040 /* GP2_LVL */
+#define WM5100_GP2_LVL_SHIFT 6 /* GP2_LVL */
+#define WM5100_GP2_LVL_WIDTH 1 /* GP2_LVL */
+#define WM5100_GP2_FN_MASK 0x003F /* GP2_FN - [5:0] */
+#define WM5100_GP2_FN_SHIFT 0 /* GP2_FN - [5:0] */
+#define WM5100_GP2_FN_WIDTH 6 /* GP2_FN - [5:0] */
+
+/*
+ * R3074 (0xC02) - GPIO CTRL 3
+ */
+#define WM5100_GP3_DIR 0x8000 /* GP3_DIR */
+#define WM5100_GP3_DIR_MASK 0x8000 /* GP3_DIR */
+#define WM5100_GP3_DIR_SHIFT 15 /* GP3_DIR */
+#define WM5100_GP3_DIR_WIDTH 1 /* GP3_DIR */
+#define WM5100_GP3_PU 0x4000 /* GP3_PU */
+#define WM5100_GP3_PU_MASK 0x4000 /* GP3_PU */
+#define WM5100_GP3_PU_SHIFT 14 /* GP3_PU */
+#define WM5100_GP3_PU_WIDTH 1 /* GP3_PU */
+#define WM5100_GP3_PD 0x2000 /* GP3_PD */
+#define WM5100_GP3_PD_MASK 0x2000 /* GP3_PD */
+#define WM5100_GP3_PD_SHIFT 13 /* GP3_PD */
+#define WM5100_GP3_PD_WIDTH 1 /* GP3_PD */
+#define WM5100_GP3_POL 0x0400 /* GP3_POL */
+#define WM5100_GP3_POL_MASK 0x0400 /* GP3_POL */
+#define WM5100_GP3_POL_SHIFT 10 /* GP3_POL */
+#define WM5100_GP3_POL_WIDTH 1 /* GP3_POL */
+#define WM5100_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */
+#define WM5100_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */
+#define WM5100_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */
+#define WM5100_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */
+#define WM5100_GP3_DB 0x0100 /* GP3_DB */
+#define WM5100_GP3_DB_MASK 0x0100 /* GP3_DB */
+#define WM5100_GP3_DB_SHIFT 8 /* GP3_DB */
+#define WM5100_GP3_DB_WIDTH 1 /* GP3_DB */
+#define WM5100_GP3_LVL 0x0040 /* GP3_LVL */
+#define WM5100_GP3_LVL_MASK 0x0040 /* GP3_LVL */
+#define WM5100_GP3_LVL_SHIFT 6 /* GP3_LVL */
+#define WM5100_GP3_LVL_WIDTH 1 /* GP3_LVL */
+#define WM5100_GP3_FN_MASK 0x003F /* GP3_FN - [5:0] */
+#define WM5100_GP3_FN_SHIFT 0 /* GP3_FN - [5:0] */
+#define WM5100_GP3_FN_WIDTH 6 /* GP3_FN - [5:0] */
+
+/*
+ * R3075 (0xC03) - GPIO CTRL 4
+ */
+#define WM5100_GP4_DIR 0x8000 /* GP4_DIR */
+#define WM5100_GP4_DIR_MASK 0x8000 /* GP4_DIR */
+#define WM5100_GP4_DIR_SHIFT 15 /* GP4_DIR */
+#define WM5100_GP4_DIR_WIDTH 1 /* GP4_DIR */
+#define WM5100_GP4_PU 0x4000 /* GP4_PU */
+#define WM5100_GP4_PU_MASK 0x4000 /* GP4_PU */
+#define WM5100_GP4_PU_SHIFT 14 /* GP4_PU */
+#define WM5100_GP4_PU_WIDTH 1 /* GP4_PU */
+#define WM5100_GP4_PD 0x2000 /* GP4_PD */
+#define WM5100_GP4_PD_MASK 0x2000 /* GP4_PD */
+#define WM5100_GP4_PD_SHIFT 13 /* GP4_PD */
+#define WM5100_GP4_PD_WIDTH 1 /* GP4_PD */
+#define WM5100_GP4_POL 0x0400 /* GP4_POL */
+#define WM5100_GP4_POL_MASK 0x0400 /* GP4_POL */
+#define WM5100_GP4_POL_SHIFT 10 /* GP4_POL */
+#define WM5100_GP4_POL_WIDTH 1 /* GP4_POL */
+#define WM5100_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */
+#define WM5100_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */
+#define WM5100_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */
+#define WM5100_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */
+#define WM5100_GP4_DB 0x0100 /* GP4_DB */
+#define WM5100_GP4_DB_MASK 0x0100 /* GP4_DB */
+#define WM5100_GP4_DB_SHIFT 8 /* GP4_DB */
+#define WM5100_GP4_DB_WIDTH 1 /* GP4_DB */
+#define WM5100_GP4_LVL 0x0040 /* GP4_LVL */
+#define WM5100_GP4_LVL_MASK 0x0040 /* GP4_LVL */
+#define WM5100_GP4_LVL_SHIFT 6 /* GP4_LVL */
+#define WM5100_GP4_LVL_WIDTH 1 /* GP4_LVL */
+#define WM5100_GP4_FN_MASK 0x003F /* GP4_FN - [5:0] */
+#define WM5100_GP4_FN_SHIFT 0 /* GP4_FN - [5:0] */
+#define WM5100_GP4_FN_WIDTH 6 /* GP4_FN - [5:0] */
+
+/*
+ * R3076 (0xC04) - GPIO CTRL 5
+ */
+#define WM5100_GP5_DIR 0x8000 /* GP5_DIR */
+#define WM5100_GP5_DIR_MASK 0x8000 /* GP5_DIR */
+#define WM5100_GP5_DIR_SHIFT 15 /* GP5_DIR */
+#define WM5100_GP5_DIR_WIDTH 1 /* GP5_DIR */
+#define WM5100_GP5_PU 0x4000 /* GP5_PU */
+#define WM5100_GP5_PU_MASK 0x4000 /* GP5_PU */
+#define WM5100_GP5_PU_SHIFT 14 /* GP5_PU */
+#define WM5100_GP5_PU_WIDTH 1 /* GP5_PU */
+#define WM5100_GP5_PD 0x2000 /* GP5_PD */
+#define WM5100_GP5_PD_MASK 0x2000 /* GP5_PD */
+#define WM5100_GP5_PD_SHIFT 13 /* GP5_PD */
+#define WM5100_GP5_PD_WIDTH 1 /* GP5_PD */
+#define WM5100_GP5_POL 0x0400 /* GP5_POL */
+#define WM5100_GP5_POL_MASK 0x0400 /* GP5_POL */
+#define WM5100_GP5_POL_SHIFT 10 /* GP5_POL */
+#define WM5100_GP5_POL_WIDTH 1 /* GP5_POL */
+#define WM5100_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */
+#define WM5100_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */
+#define WM5100_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */
+#define WM5100_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */
+#define WM5100_GP5_DB 0x0100 /* GP5_DB */
+#define WM5100_GP5_DB_MASK 0x0100 /* GP5_DB */
+#define WM5100_GP5_DB_SHIFT 8 /* GP5_DB */
+#define WM5100_GP5_DB_WIDTH 1 /* GP5_DB */
+#define WM5100_GP5_LVL 0x0040 /* GP5_LVL */
+#define WM5100_GP5_LVL_MASK 0x0040 /* GP5_LVL */
+#define WM5100_GP5_LVL_SHIFT 6 /* GP5_LVL */
+#define WM5100_GP5_LVL_WIDTH 1 /* GP5_LVL */
+#define WM5100_GP5_FN_MASK 0x003F /* GP5_FN - [5:0] */
+#define WM5100_GP5_FN_SHIFT 0 /* GP5_FN - [5:0] */
+#define WM5100_GP5_FN_WIDTH 6 /* GP5_FN - [5:0] */
+
+/*
+ * R3077 (0xC05) - GPIO CTRL 6
+ */
+#define WM5100_GP6_DIR 0x8000 /* GP6_DIR */
+#define WM5100_GP6_DIR_MASK 0x8000 /* GP6_DIR */
+#define WM5100_GP6_DIR_SHIFT 15 /* GP6_DIR */
+#define WM5100_GP6_DIR_WIDTH 1 /* GP6_DIR */
+#define WM5100_GP6_PU 0x4000 /* GP6_PU */
+#define WM5100_GP6_PU_MASK 0x4000 /* GP6_PU */
+#define WM5100_GP6_PU_SHIFT 14 /* GP6_PU */
+#define WM5100_GP6_PU_WIDTH 1 /* GP6_PU */
+#define WM5100_GP6_PD 0x2000 /* GP6_PD */
+#define WM5100_GP6_PD_MASK 0x2000 /* GP6_PD */
+#define WM5100_GP6_PD_SHIFT 13 /* GP6_PD */
+#define WM5100_GP6_PD_WIDTH 1 /* GP6_PD */
+#define WM5100_GP6_POL 0x0400 /* GP6_POL */
+#define WM5100_GP6_POL_MASK 0x0400 /* GP6_POL */
+#define WM5100_GP6_POL_SHIFT 10 /* GP6_POL */
+#define WM5100_GP6_POL_WIDTH 1 /* GP6_POL */
+#define WM5100_GP6_OP_CFG 0x0200 /* GP6_OP_CFG */
+#define WM5100_GP6_OP_CFG_MASK 0x0200 /* GP6_OP_CFG */
+#define WM5100_GP6_OP_CFG_SHIFT 9 /* GP6_OP_CFG */
+#define WM5100_GP6_OP_CFG_WIDTH 1 /* GP6_OP_CFG */
+#define WM5100_GP6_DB 0x0100 /* GP6_DB */
+#define WM5100_GP6_DB_MASK 0x0100 /* GP6_DB */
+#define WM5100_GP6_DB_SHIFT 8 /* GP6_DB */
+#define WM5100_GP6_DB_WIDTH 1 /* GP6_DB */
+#define WM5100_GP6_LVL 0x0040 /* GP6_LVL */
+#define WM5100_GP6_LVL_MASK 0x0040 /* GP6_LVL */
+#define WM5100_GP6_LVL_SHIFT 6 /* GP6_LVL */
+#define WM5100_GP6_LVL_WIDTH 1 /* GP6_LVL */
+#define WM5100_GP6_FN_MASK 0x003F /* GP6_FN - [5:0] */
+#define WM5100_GP6_FN_SHIFT 0 /* GP6_FN - [5:0] */
+#define WM5100_GP6_FN_WIDTH 6 /* GP6_FN - [5:0] */
+
+/*
+ * R3107 (0xC23) - Misc Pad Ctrl 1
+ */
+#define WM5100_LDO1ENA_PD 0x8000 /* LDO1ENA_PD */
+#define WM5100_LDO1ENA_PD_MASK 0x8000 /* LDO1ENA_PD */
+#define WM5100_LDO1ENA_PD_SHIFT 15 /* LDO1ENA_PD */
+#define WM5100_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */
+#define WM5100_MCLK2_PD 0x2000 /* MCLK2_PD */
+#define WM5100_MCLK2_PD_MASK 0x2000 /* MCLK2_PD */
+#define WM5100_MCLK2_PD_SHIFT 13 /* MCLK2_PD */
+#define WM5100_MCLK2_PD_WIDTH 1 /* MCLK2_PD */
+#define WM5100_MCLK1_PD 0x1000 /* MCLK1_PD */
+#define WM5100_MCLK1_PD_MASK 0x1000 /* MCLK1_PD */
+#define WM5100_MCLK1_PD_SHIFT 12 /* MCLK1_PD */
+#define WM5100_MCLK1_PD_WIDTH 1 /* MCLK1_PD */
+#define WM5100_RESET_PU 0x0002 /* RESET_PU */
+#define WM5100_RESET_PU_MASK 0x0002 /* RESET_PU */
+#define WM5100_RESET_PU_SHIFT 1 /* RESET_PU */
+#define WM5100_RESET_PU_WIDTH 1 /* RESET_PU */
+#define WM5100_ADDR_PD 0x0001 /* ADDR_PD */
+#define WM5100_ADDR_PD_MASK 0x0001 /* ADDR_PD */
+#define WM5100_ADDR_PD_SHIFT 0 /* ADDR_PD */
+#define WM5100_ADDR_PD_WIDTH 1 /* ADDR_PD */
+
+/*
+ * R3108 (0xC24) - Misc Pad Ctrl 2
+ */
+#define WM5100_DMICDAT4_PD 0x0008 /* DMICDAT4_PD */
+#define WM5100_DMICDAT4_PD_MASK 0x0008 /* DMICDAT4_PD */
+#define WM5100_DMICDAT4_PD_SHIFT 3 /* DMICDAT4_PD */
+#define WM5100_DMICDAT4_PD_WIDTH 1 /* DMICDAT4_PD */
+#define WM5100_DMICDAT3_PD 0x0004 /* DMICDAT3_PD */
+#define WM5100_DMICDAT3_PD_MASK 0x0004 /* DMICDAT3_PD */
+#define WM5100_DMICDAT3_PD_SHIFT 2 /* DMICDAT3_PD */
+#define WM5100_DMICDAT3_PD_WIDTH 1 /* DMICDAT3_PD */
+#define WM5100_DMICDAT2_PD 0x0002 /* DMICDAT2_PD */
+#define WM5100_DMICDAT2_PD_MASK 0x0002 /* DMICDAT2_PD */
+#define WM5100_DMICDAT2_PD_SHIFT 1 /* DMICDAT2_PD */
+#define WM5100_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */
+#define WM5100_DMICDAT1_PD 0x0001 /* DMICDAT1_PD */
+#define WM5100_DMICDAT1_PD_MASK 0x0001 /* DMICDAT1_PD */
+#define WM5100_DMICDAT1_PD_SHIFT 0 /* DMICDAT1_PD */
+#define WM5100_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */
+
+/*
+ * R3109 (0xC25) - Misc Pad Ctrl 3
+ */
+#define WM5100_AIF1RXLRCLK_PU 0x0020 /* AIF1RXLRCLK_PU */
+#define WM5100_AIF1RXLRCLK_PU_MASK 0x0020 /* AIF1RXLRCLK_PU */
+#define WM5100_AIF1RXLRCLK_PU_SHIFT 5 /* AIF1RXLRCLK_PU */
+#define WM5100_AIF1RXLRCLK_PU_WIDTH 1 /* AIF1RXLRCLK_PU */
+#define WM5100_AIF1RXLRCLK_PD 0x0010 /* AIF1RXLRCLK_PD */
+#define WM5100_AIF1RXLRCLK_PD_MASK 0x0010 /* AIF1RXLRCLK_PD */
+#define WM5100_AIF1RXLRCLK_PD_SHIFT 4 /* AIF1RXLRCLK_PD */
+#define WM5100_AIF1RXLRCLK_PD_WIDTH 1 /* AIF1RXLRCLK_PD */
+#define WM5100_AIF1BCLK_PU 0x0008 /* AIF1BCLK_PU */
+#define WM5100_AIF1BCLK_PU_MASK 0x0008 /* AIF1BCLK_PU */
+#define WM5100_AIF1BCLK_PU_SHIFT 3 /* AIF1BCLK_PU */
+#define WM5100_AIF1BCLK_PU_WIDTH 1 /* AIF1BCLK_PU */
+#define WM5100_AIF1BCLK_PD 0x0004 /* AIF1BCLK_PD */
+#define WM5100_AIF1BCLK_PD_MASK 0x0004 /* AIF1BCLK_PD */
+#define WM5100_AIF1BCLK_PD_SHIFT 2 /* AIF1BCLK_PD */
+#define WM5100_AIF1BCLK_PD_WIDTH 1 /* AIF1BCLK_PD */
+#define WM5100_AIF1RXDAT_PU 0x0002 /* AIF1RXDAT_PU */
+#define WM5100_AIF1RXDAT_PU_MASK 0x0002 /* AIF1RXDAT_PU */
+#define WM5100_AIF1RXDAT_PU_SHIFT 1 /* AIF1RXDAT_PU */
+#define WM5100_AIF1RXDAT_PU_WIDTH 1 /* AIF1RXDAT_PU */
+#define WM5100_AIF1RXDAT_PD 0x0001 /* AIF1RXDAT_PD */
+#define WM5100_AIF1RXDAT_PD_MASK 0x0001 /* AIF1RXDAT_PD */
+#define WM5100_AIF1RXDAT_PD_SHIFT 0 /* AIF1RXDAT_PD */
+#define WM5100_AIF1RXDAT_PD_WIDTH 1 /* AIF1RXDAT_PD */
+
+/*
+ * R3110 (0xC26) - Misc Pad Ctrl 4
+ */
+#define WM5100_AIF2RXLRCLK_PU 0x0020 /* AIF2RXLRCLK_PU */
+#define WM5100_AIF2RXLRCLK_PU_MASK 0x0020 /* AIF2RXLRCLK_PU */
+#define WM5100_AIF2RXLRCLK_PU_SHIFT 5 /* AIF2RXLRCLK_PU */
+#define WM5100_AIF2RXLRCLK_PU_WIDTH 1 /* AIF2RXLRCLK_PU */
+#define WM5100_AIF2RXLRCLK_PD 0x0010 /* AIF2RXLRCLK_PD */
+#define WM5100_AIF2RXLRCLK_PD_MASK 0x0010 /* AIF2RXLRCLK_PD */
+#define WM5100_AIF2RXLRCLK_PD_SHIFT 4 /* AIF2RXLRCLK_PD */
+#define WM5100_AIF2RXLRCLK_PD_WIDTH 1 /* AIF2RXLRCLK_PD */
+#define WM5100_AIF2BCLK_PU 0x0008 /* AIF2BCLK_PU */
+#define WM5100_AIF2BCLK_PU_MASK 0x0008 /* AIF2BCLK_PU */
+#define WM5100_AIF2BCLK_PU_SHIFT 3 /* AIF2BCLK_PU */
+#define WM5100_AIF2BCLK_PU_WIDTH 1 /* AIF2BCLK_PU */
+#define WM5100_AIF2BCLK_PD 0x0004 /* AIF2BCLK_PD */
+#define WM5100_AIF2BCLK_PD_MASK 0x0004 /* AIF2BCLK_PD */
+#define WM5100_AIF2BCLK_PD_SHIFT 2 /* AIF2BCLK_PD */
+#define WM5100_AIF2BCLK_PD_WIDTH 1 /* AIF2BCLK_PD */
+#define WM5100_AIF2RXDAT_PU 0x0002 /* AIF2RXDAT_PU */
+#define WM5100_AIF2RXDAT_PU_MASK 0x0002 /* AIF2RXDAT_PU */
+#define WM5100_AIF2RXDAT_PU_SHIFT 1 /* AIF2RXDAT_PU */
+#define WM5100_AIF2RXDAT_PU_WIDTH 1 /* AIF2RXDAT_PU */
+#define WM5100_AIF2RXDAT_PD 0x0001 /* AIF2RXDAT_PD */
+#define WM5100_AIF2RXDAT_PD_MASK 0x0001 /* AIF2RXDAT_PD */
+#define WM5100_AIF2RXDAT_PD_SHIFT 0 /* AIF2RXDAT_PD */
+#define WM5100_AIF2RXDAT_PD_WIDTH 1 /* AIF2RXDAT_PD */
+
+/*
+ * R3111 (0xC27) - Misc Pad Ctrl 5
+ */
+#define WM5100_AIF3RXLRCLK_PU 0x0020 /* AIF3RXLRCLK_PU */
+#define WM5100_AIF3RXLRCLK_PU_MASK 0x0020 /* AIF3RXLRCLK_PU */
+#define WM5100_AIF3RXLRCLK_PU_SHIFT 5 /* AIF3RXLRCLK_PU */
+#define WM5100_AIF3RXLRCLK_PU_WIDTH 1 /* AIF3RXLRCLK_PU */
+#define WM5100_AIF3RXLRCLK_PD 0x0010 /* AIF3RXLRCLK_PD */
+#define WM5100_AIF3RXLRCLK_PD_MASK 0x0010 /* AIF3RXLRCLK_PD */
+#define WM5100_AIF3RXLRCLK_PD_SHIFT 4 /* AIF3RXLRCLK_PD */
+#define WM5100_AIF3RXLRCLK_PD_WIDTH 1 /* AIF3RXLRCLK_PD */
+#define WM5100_AIF3BCLK_PU 0x0008 /* AIF3BCLK_PU */
+#define WM5100_AIF3BCLK_PU_MASK 0x0008 /* AIF3BCLK_PU */
+#define WM5100_AIF3BCLK_PU_SHIFT 3 /* AIF3BCLK_PU */
+#define WM5100_AIF3BCLK_PU_WIDTH 1 /* AIF3BCLK_PU */
+#define WM5100_AIF3BCLK_PD 0x0004 /* AIF3BCLK_PD */
+#define WM5100_AIF3BCLK_PD_MASK 0x0004 /* AIF3BCLK_PD */
+#define WM5100_AIF3BCLK_PD_SHIFT 2 /* AIF3BCLK_PD */
+#define WM5100_AIF3BCLK_PD_WIDTH 1 /* AIF3BCLK_PD */
+#define WM5100_AIF3RXDAT_PU 0x0002 /* AIF3RXDAT_PU */
+#define WM5100_AIF3RXDAT_PU_MASK 0x0002 /* AIF3RXDAT_PU */
+#define WM5100_AIF3RXDAT_PU_SHIFT 1 /* AIF3RXDAT_PU */
+#define WM5100_AIF3RXDAT_PU_WIDTH 1 /* AIF3RXDAT_PU */
+#define WM5100_AIF3RXDAT_PD 0x0001 /* AIF3RXDAT_PD */
+#define WM5100_AIF3RXDAT_PD_MASK 0x0001 /* AIF3RXDAT_PD */
+#define WM5100_AIF3RXDAT_PD_SHIFT 0 /* AIF3RXDAT_PD */
+#define WM5100_AIF3RXDAT_PD_WIDTH 1 /* AIF3RXDAT_PD */
+
+/*
+ * R3112 (0xC28) - Misc GPIO 1
+ */
+#define WM5100_OPCLK_SEL_MASK 0x0003 /* OPCLK_SEL - [1:0] */
+#define WM5100_OPCLK_SEL_SHIFT 0 /* OPCLK_SEL - [1:0] */
+#define WM5100_OPCLK_SEL_WIDTH 2 /* OPCLK_SEL - [1:0] */
+
+/*
+ * R3328 (0xD00) - Interrupt Status 1
+ */
+#define WM5100_GP6_EINT 0x0020 /* GP6_EINT */
+#define WM5100_GP6_EINT_MASK 0x0020 /* GP6_EINT */
+#define WM5100_GP6_EINT_SHIFT 5 /* GP6_EINT */
+#define WM5100_GP6_EINT_WIDTH 1 /* GP6_EINT */
+#define WM5100_GP5_EINT 0x0010 /* GP5_EINT */
+#define WM5100_GP5_EINT_MASK 0x0010 /* GP5_EINT */
+#define WM5100_GP5_EINT_SHIFT 4 /* GP5_EINT */
+#define WM5100_GP5_EINT_WIDTH 1 /* GP5_EINT */
+#define WM5100_GP4_EINT 0x0008 /* GP4_EINT */
+#define WM5100_GP4_EINT_MASK 0x0008 /* GP4_EINT */
+#define WM5100_GP4_EINT_SHIFT 3 /* GP4_EINT */
+#define WM5100_GP4_EINT_WIDTH 1 /* GP4_EINT */
+#define WM5100_GP3_EINT 0x0004 /* GP3_EINT */
+#define WM5100_GP3_EINT_MASK 0x0004 /* GP3_EINT */
+#define WM5100_GP3_EINT_SHIFT 2 /* GP3_EINT */
+#define WM5100_GP3_EINT_WIDTH 1 /* GP3_EINT */
+#define WM5100_GP2_EINT 0x0002 /* GP2_EINT */
+#define WM5100_GP2_EINT_MASK 0x0002 /* GP2_EINT */
+#define WM5100_GP2_EINT_SHIFT 1 /* GP2_EINT */
+#define WM5100_GP2_EINT_WIDTH 1 /* GP2_EINT */
+#define WM5100_GP1_EINT 0x0001 /* GP1_EINT */
+#define WM5100_GP1_EINT_MASK 0x0001 /* GP1_EINT */
+#define WM5100_GP1_EINT_SHIFT 0 /* GP1_EINT */
+#define WM5100_GP1_EINT_WIDTH 1 /* GP1_EINT */
+
+/*
+ * R3329 (0xD01) - Interrupt Status 2
+ */
+#define WM5100_DSP_IRQ6_EINT 0x0020 /* DSP_IRQ6_EINT */
+#define WM5100_DSP_IRQ6_EINT_MASK 0x0020 /* DSP_IRQ6_EINT */
+#define WM5100_DSP_IRQ6_EINT_SHIFT 5 /* DSP_IRQ6_EINT */
+#define WM5100_DSP_IRQ6_EINT_WIDTH 1 /* DSP_IRQ6_EINT */
+#define WM5100_DSP_IRQ5_EINT 0x0010 /* DSP_IRQ5_EINT */
+#define WM5100_DSP_IRQ5_EINT_MASK 0x0010 /* DSP_IRQ5_EINT */
+#define WM5100_DSP_IRQ5_EINT_SHIFT 4 /* DSP_IRQ5_EINT */
+#define WM5100_DSP_IRQ5_EINT_WIDTH 1 /* DSP_IRQ5_EINT */
+#define WM5100_DSP_IRQ4_EINT 0x0008 /* DSP_IRQ4_EINT */
+#define WM5100_DSP_IRQ4_EINT_MASK 0x0008 /* DSP_IRQ4_EINT */
+#define WM5100_DSP_IRQ4_EINT_SHIFT 3 /* DSP_IRQ4_EINT */
+#define WM5100_DSP_IRQ4_EINT_WIDTH 1 /* DSP_IRQ4_EINT */
+#define WM5100_DSP_IRQ3_EINT 0x0004 /* DSP_IRQ3_EINT */
+#define WM5100_DSP_IRQ3_EINT_MASK 0x0004 /* DSP_IRQ3_EINT */
+#define WM5100_DSP_IRQ3_EINT_SHIFT 2 /* DSP_IRQ3_EINT */
+#define WM5100_DSP_IRQ3_EINT_WIDTH 1 /* DSP_IRQ3_EINT */
+#define WM5100_DSP_IRQ2_EINT 0x0002 /* DSP_IRQ2_EINT */
+#define WM5100_DSP_IRQ2_EINT_MASK 0x0002 /* DSP_IRQ2_EINT */
+#define WM5100_DSP_IRQ2_EINT_SHIFT 1 /* DSP_IRQ2_EINT */
+#define WM5100_DSP_IRQ2_EINT_WIDTH 1 /* DSP_IRQ2_EINT */
+#define WM5100_DSP_IRQ1_EINT 0x0001 /* DSP_IRQ1_EINT */
+#define WM5100_DSP_IRQ1_EINT_MASK 0x0001 /* DSP_IRQ1_EINT */
+#define WM5100_DSP_IRQ1_EINT_SHIFT 0 /* DSP_IRQ1_EINT */
+#define WM5100_DSP_IRQ1_EINT_WIDTH 1 /* DSP_IRQ1_EINT */
+
+/*
+ * R3330 (0xD02) - Interrupt Status 3
+ */
+#define WM5100_SPK_SHUTDOWN_WARN_EINT 0x8000 /* SPK_SHUTDOWN_WARN_EINT */
+#define WM5100_SPK_SHUTDOWN_WARN_EINT_MASK 0x8000 /* SPK_SHUTDOWN_WARN_EINT */
+#define WM5100_SPK_SHUTDOWN_WARN_EINT_SHIFT 15 /* SPK_SHUTDOWN_WARN_EINT */
+#define WM5100_SPK_SHUTDOWN_WARN_EINT_WIDTH 1 /* SPK_SHUTDOWN_WARN_EINT */
+#define WM5100_SPK_SHUTDOWN_EINT 0x4000 /* SPK_SHUTDOWN_EINT */
+#define WM5100_SPK_SHUTDOWN_EINT_MASK 0x4000 /* SPK_SHUTDOWN_EINT */
+#define WM5100_SPK_SHUTDOWN_EINT_SHIFT 14 /* SPK_SHUTDOWN_EINT */
+#define WM5100_SPK_SHUTDOWN_EINT_WIDTH 1 /* SPK_SHUTDOWN_EINT */
+#define WM5100_HPDET_EINT 0x2000 /* HPDET_EINT */
+#define WM5100_HPDET_EINT_MASK 0x2000 /* HPDET_EINT */
+#define WM5100_HPDET_EINT_SHIFT 13 /* HPDET_EINT */
+#define WM5100_HPDET_EINT_WIDTH 1 /* HPDET_EINT */
+#define WM5100_ACCDET_EINT 0x1000 /* ACCDET_EINT */
+#define WM5100_ACCDET_EINT_MASK 0x1000 /* ACCDET_EINT */
+#define WM5100_ACCDET_EINT_SHIFT 12 /* ACCDET_EINT */
+#define WM5100_ACCDET_EINT_WIDTH 1 /* ACCDET_EINT */
+#define WM5100_DRC_SIG_DET_EINT 0x0200 /* DRC_SIG_DET_EINT */
+#define WM5100_DRC_SIG_DET_EINT_MASK 0x0200 /* DRC_SIG_DET_EINT */
+#define WM5100_DRC_SIG_DET_EINT_SHIFT 9 /* DRC_SIG_DET_EINT */
+#define WM5100_DRC_SIG_DET_EINT_WIDTH 1 /* DRC_SIG_DET_EINT */
+#define WM5100_ASRC2_LOCK_EINT 0x0100 /* ASRC2_LOCK_EINT */
+#define WM5100_ASRC2_LOCK_EINT_MASK 0x0100 /* ASRC2_LOCK_EINT */
+#define WM5100_ASRC2_LOCK_EINT_SHIFT 8 /* ASRC2_LOCK_EINT */
+#define WM5100_ASRC2_LOCK_EINT_WIDTH 1 /* ASRC2_LOCK_EINT */
+#define WM5100_ASRC1_LOCK_EINT 0x0080 /* ASRC1_LOCK_EINT */
+#define WM5100_ASRC1_LOCK_EINT_MASK 0x0080 /* ASRC1_LOCK_EINT */
+#define WM5100_ASRC1_LOCK_EINT_SHIFT 7 /* ASRC1_LOCK_EINT */
+#define WM5100_ASRC1_LOCK_EINT_WIDTH 1 /* ASRC1_LOCK_EINT */
+#define WM5100_FLL2_LOCK_EINT 0x0008 /* FLL2_LOCK_EINT */
+#define WM5100_FLL2_LOCK_EINT_MASK 0x0008 /* FLL2_LOCK_EINT */
+#define WM5100_FLL2_LOCK_EINT_SHIFT 3 /* FLL2_LOCK_EINT */
+#define WM5100_FLL2_LOCK_EINT_WIDTH 1 /* FLL2_LOCK_EINT */
+#define WM5100_FLL1_LOCK_EINT 0x0004 /* FLL1_LOCK_EINT */
+#define WM5100_FLL1_LOCK_EINT_MASK 0x0004 /* FLL1_LOCK_EINT */
+#define WM5100_FLL1_LOCK_EINT_SHIFT 2 /* FLL1_LOCK_EINT */
+#define WM5100_FLL1_LOCK_EINT_WIDTH 1 /* FLL1_LOCK_EINT */
+#define WM5100_CLKGEN_ERR_EINT 0x0002 /* CLKGEN_ERR_EINT */
+#define WM5100_CLKGEN_ERR_EINT_MASK 0x0002 /* CLKGEN_ERR_EINT */
+#define WM5100_CLKGEN_ERR_EINT_SHIFT 1 /* CLKGEN_ERR_EINT */
+#define WM5100_CLKGEN_ERR_EINT_WIDTH 1 /* CLKGEN_ERR_EINT */
+#define WM5100_CLKGEN_ERR_ASYNC_EINT 0x0001 /* CLKGEN_ERR_ASYNC_EINT */
+#define WM5100_CLKGEN_ERR_ASYNC_EINT_MASK 0x0001 /* CLKGEN_ERR_ASYNC_EINT */
+#define WM5100_CLKGEN_ERR_ASYNC_EINT_SHIFT 0 /* CLKGEN_ERR_ASYNC_EINT */
+#define WM5100_CLKGEN_ERR_ASYNC_EINT_WIDTH 1 /* CLKGEN_ERR_ASYNC_EINT */
+
+/*
+ * R3331 (0xD03) - Interrupt Status 4
+ */
+#define WM5100_AIF3_ERR_EINT 0x2000 /* AIF3_ERR_EINT */
+#define WM5100_AIF3_ERR_EINT_MASK 0x2000 /* AIF3_ERR_EINT */
+#define WM5100_AIF3_ERR_EINT_SHIFT 13 /* AIF3_ERR_EINT */
+#define WM5100_AIF3_ERR_EINT_WIDTH 1 /* AIF3_ERR_EINT */
+#define WM5100_AIF2_ERR_EINT 0x1000 /* AIF2_ERR_EINT */
+#define WM5100_AIF2_ERR_EINT_MASK 0x1000 /* AIF2_ERR_EINT */
+#define WM5100_AIF2_ERR_EINT_SHIFT 12 /* AIF2_ERR_EINT */
+#define WM5100_AIF2_ERR_EINT_WIDTH 1 /* AIF2_ERR_EINT */
+#define WM5100_AIF1_ERR_EINT 0x0800 /* AIF1_ERR_EINT */
+#define WM5100_AIF1_ERR_EINT_MASK 0x0800 /* AIF1_ERR_EINT */
+#define WM5100_AIF1_ERR_EINT_SHIFT 11 /* AIF1_ERR_EINT */
+#define WM5100_AIF1_ERR_EINT_WIDTH 1 /* AIF1_ERR_EINT */
+#define WM5100_CTRLIF_ERR_EINT 0x0400 /* CTRLIF_ERR_EINT */
+#define WM5100_CTRLIF_ERR_EINT_MASK 0x0400 /* CTRLIF_ERR_EINT */
+#define WM5100_CTRLIF_ERR_EINT_SHIFT 10 /* CTRLIF_ERR_EINT */
+#define WM5100_CTRLIF_ERR_EINT_WIDTH 1 /* CTRLIF_ERR_EINT */
+#define WM5100_ISRC2_UNDERCLOCKED_EINT 0x0200 /* ISRC2_UNDERCLOCKED_EINT */
+#define WM5100_ISRC2_UNDERCLOCKED_EINT_MASK 0x0200 /* ISRC2_UNDERCLOCKED_EINT */
+#define WM5100_ISRC2_UNDERCLOCKED_EINT_SHIFT 9 /* ISRC2_UNDERCLOCKED_EINT */
+#define WM5100_ISRC2_UNDERCLOCKED_EINT_WIDTH 1 /* ISRC2_UNDERCLOCKED_EINT */
+#define WM5100_ISRC1_UNDERCLOCKED_EINT 0x0100 /* ISRC1_UNDERCLOCKED_EINT */
+#define WM5100_ISRC1_UNDERCLOCKED_EINT_MASK 0x0100 /* ISRC1_UNDERCLOCKED_EINT */
+#define WM5100_ISRC1_UNDERCLOCKED_EINT_SHIFT 8 /* ISRC1_UNDERCLOCKED_EINT */
+#define WM5100_ISRC1_UNDERCLOCKED_EINT_WIDTH 1 /* ISRC1_UNDERCLOCKED_EINT */
+#define WM5100_FX_UNDERCLOCKED_EINT 0x0080 /* FX_UNDERCLOCKED_EINT */
+#define WM5100_FX_UNDERCLOCKED_EINT_MASK 0x0080 /* FX_UNDERCLOCKED_EINT */
+#define WM5100_FX_UNDERCLOCKED_EINT_SHIFT 7 /* FX_UNDERCLOCKED_EINT */
+#define WM5100_FX_UNDERCLOCKED_EINT_WIDTH 1 /* FX_UNDERCLOCKED_EINT */
+#define WM5100_AIF3_UNDERCLOCKED_EINT 0x0040 /* AIF3_UNDERCLOCKED_EINT */
+#define WM5100_AIF3_UNDERCLOCKED_EINT_MASK 0x0040 /* AIF3_UNDERCLOCKED_EINT */
+#define WM5100_AIF3_UNDERCLOCKED_EINT_SHIFT 6 /* AIF3_UNDERCLOCKED_EINT */
+#define WM5100_AIF3_UNDERCLOCKED_EINT_WIDTH 1 /* AIF3_UNDERCLOCKED_EINT */
+#define WM5100_AIF2_UNDERCLOCKED_EINT 0x0020 /* AIF2_UNDERCLOCKED_EINT */
+#define WM5100_AIF2_UNDERCLOCKED_EINT_MASK 0x0020 /* AIF2_UNDERCLOCKED_EINT */
+#define WM5100_AIF2_UNDERCLOCKED_EINT_SHIFT 5 /* AIF2_UNDERCLOCKED_EINT */
+#define WM5100_AIF2_UNDERCLOCKED_EINT_WIDTH 1 /* AIF2_UNDERCLOCKED_EINT */
+#define WM5100_AIF1_UNDERCLOCKED_EINT 0x0010 /* AIF1_UNDERCLOCKED_EINT */
+#define WM5100_AIF1_UNDERCLOCKED_EINT_MASK 0x0010 /* AIF1_UNDERCLOCKED_EINT */
+#define WM5100_AIF1_UNDERCLOCKED_EINT_SHIFT 4 /* AIF1_UNDERCLOCKED_EINT */
+#define WM5100_AIF1_UNDERCLOCKED_EINT_WIDTH 1 /* AIF1_UNDERCLOCKED_EINT */
+#define WM5100_ASRC_UNDERCLOCKED_EINT 0x0008 /* ASRC_UNDERCLOCKED_EINT */
+#define WM5100_ASRC_UNDERCLOCKED_EINT_MASK 0x0008 /* ASRC_UNDERCLOCKED_EINT */
+#define WM5100_ASRC_UNDERCLOCKED_EINT_SHIFT 3 /* ASRC_UNDERCLOCKED_EINT */
+#define WM5100_ASRC_UNDERCLOCKED_EINT_WIDTH 1 /* ASRC_UNDERCLOCKED_EINT */
+#define WM5100_DAC_UNDERCLOCKED_EINT 0x0004 /* DAC_UNDERCLOCKED_EINT */
+#define WM5100_DAC_UNDERCLOCKED_EINT_MASK 0x0004 /* DAC_UNDERCLOCKED_EINT */
+#define WM5100_DAC_UNDERCLOCKED_EINT_SHIFT 2 /* DAC_UNDERCLOCKED_EINT */
+#define WM5100_DAC_UNDERCLOCKED_EINT_WIDTH 1 /* DAC_UNDERCLOCKED_EINT */
+#define WM5100_ADC_UNDERCLOCKED_EINT 0x0002 /* ADC_UNDERCLOCKED_EINT */
+#define WM5100_ADC_UNDERCLOCKED_EINT_MASK 0x0002 /* ADC_UNDERCLOCKED_EINT */
+#define WM5100_ADC_UNDERCLOCKED_EINT_SHIFT 1 /* ADC_UNDERCLOCKED_EINT */
+#define WM5100_ADC_UNDERCLOCKED_EINT_WIDTH 1 /* ADC_UNDERCLOCKED_EINT */
+#define WM5100_MIXER_UNDERCLOCKED_EINT 0x0001 /* MIXER_UNDERCLOCKED_EINT */
+#define WM5100_MIXER_UNDERCLOCKED_EINT_MASK 0x0001 /* MIXER_UNDERCLOCKED_EINT */
+#define WM5100_MIXER_UNDERCLOCKED_EINT_SHIFT 0 /* MIXER_UNDERCLOCKED_EINT */
+#define WM5100_MIXER_UNDERCLOCKED_EINT_WIDTH 1 /* MIXER_UNDERCLOCKED_EINT */
+
+/*
+ * R3332 (0xD04) - Interrupt Raw Status 2
+ */
+#define WM5100_DSP_IRQ6_STS 0x0020 /* DSP_IRQ6_STS */
+#define WM5100_DSP_IRQ6_STS_MASK 0x0020 /* DSP_IRQ6_STS */
+#define WM5100_DSP_IRQ6_STS_SHIFT 5 /* DSP_IRQ6_STS */
+#define WM5100_DSP_IRQ6_STS_WIDTH 1 /* DSP_IRQ6_STS */
+#define WM5100_DSP_IRQ5_STS 0x0010 /* DSP_IRQ5_STS */
+#define WM5100_DSP_IRQ5_STS_MASK 0x0010 /* DSP_IRQ5_STS */
+#define WM5100_DSP_IRQ5_STS_SHIFT 4 /* DSP_IRQ5_STS */
+#define WM5100_DSP_IRQ5_STS_WIDTH 1 /* DSP_IRQ5_STS */
+#define WM5100_DSP_IRQ4_STS 0x0008 /* DSP_IRQ4_STS */
+#define WM5100_DSP_IRQ4_STS_MASK 0x0008 /* DSP_IRQ4_STS */
+#define WM5100_DSP_IRQ4_STS_SHIFT 3 /* DSP_IRQ4_STS */
+#define WM5100_DSP_IRQ4_STS_WIDTH 1 /* DSP_IRQ4_STS */
+#define WM5100_DSP_IRQ3_STS 0x0004 /* DSP_IRQ3_STS */
+#define WM5100_DSP_IRQ3_STS_MASK 0x0004 /* DSP_IRQ3_STS */
+#define WM5100_DSP_IRQ3_STS_SHIFT 2 /* DSP_IRQ3_STS */
+#define WM5100_DSP_IRQ3_STS_WIDTH 1 /* DSP_IRQ3_STS */
+#define WM5100_DSP_IRQ2_STS 0x0002 /* DSP_IRQ2_STS */
+#define WM5100_DSP_IRQ2_STS_MASK 0x0002 /* DSP_IRQ2_STS */
+#define WM5100_DSP_IRQ2_STS_SHIFT 1 /* DSP_IRQ2_STS */
+#define WM5100_DSP_IRQ2_STS_WIDTH 1 /* DSP_IRQ2_STS */
+#define WM5100_DSP_IRQ1_STS 0x0001 /* DSP_IRQ1_STS */
+#define WM5100_DSP_IRQ1_STS_MASK 0x0001 /* DSP_IRQ1_STS */
+#define WM5100_DSP_IRQ1_STS_SHIFT 0 /* DSP_IRQ1_STS */
+#define WM5100_DSP_IRQ1_STS_WIDTH 1 /* DSP_IRQ1_STS */
+
+/*
+ * R3333 (0xD05) - Interrupt Raw Status 3
+ */
+#define WM5100_SPK_SHUTDOWN_WARN_STS 0x8000 /* SPK_SHUTDOWN_WARN_STS */
+#define WM5100_SPK_SHUTDOWN_WARN_STS_MASK 0x8000 /* SPK_SHUTDOWN_WARN_STS */
+#define WM5100_SPK_SHUTDOWN_WARN_STS_SHIFT 15 /* SPK_SHUTDOWN_WARN_STS */
+#define WM5100_SPK_SHUTDOWN_WARN_STS_WIDTH 1 /* SPK_SHUTDOWN_WARN_STS */
+#define WM5100_SPK_SHUTDOWN_STS 0x4000 /* SPK_SHUTDOWN_STS */
+#define WM5100_SPK_SHUTDOWN_STS_MASK 0x4000 /* SPK_SHUTDOWN_STS */
+#define WM5100_SPK_SHUTDOWN_STS_SHIFT 14 /* SPK_SHUTDOWN_STS */
+#define WM5100_SPK_SHUTDOWN_STS_WIDTH 1 /* SPK_SHUTDOWN_STS */
+#define WM5100_HPDET_STS 0x2000 /* HPDET_STS */
+#define WM5100_HPDET_STS_MASK 0x2000 /* HPDET_STS */
+#define WM5100_HPDET_STS_SHIFT 13 /* HPDET_STS */
+#define WM5100_HPDET_STS_WIDTH 1 /* HPDET_STS */
+#define WM5100_DRC_SID_DET_STS 0x0200 /* DRC_SID_DET_STS */
+#define WM5100_DRC_SID_DET_STS_MASK 0x0200 /* DRC_SID_DET_STS */
+#define WM5100_DRC_SID_DET_STS_SHIFT 9 /* DRC_SID_DET_STS */
+#define WM5100_DRC_SID_DET_STS_WIDTH 1 /* DRC_SID_DET_STS */
+#define WM5100_ASRC2_LOCK_STS 0x0100 /* ASRC2_LOCK_STS */
+#define WM5100_ASRC2_LOCK_STS_MASK 0x0100 /* ASRC2_LOCK_STS */
+#define WM5100_ASRC2_LOCK_STS_SHIFT 8 /* ASRC2_LOCK_STS */
+#define WM5100_ASRC2_LOCK_STS_WIDTH 1 /* ASRC2_LOCK_STS */
+#define WM5100_ASRC1_LOCK_STS 0x0080 /* ASRC1_LOCK_STS */
+#define WM5100_ASRC1_LOCK_STS_MASK 0x0080 /* ASRC1_LOCK_STS */
+#define WM5100_ASRC1_LOCK_STS_SHIFT 7 /* ASRC1_LOCK_STS */
+#define WM5100_ASRC1_LOCK_STS_WIDTH 1 /* ASRC1_LOCK_STS */
+#define WM5100_FLL2_LOCK_STS 0x0008 /* FLL2_LOCK_STS */
+#define WM5100_FLL2_LOCK_STS_MASK 0x0008 /* FLL2_LOCK_STS */
+#define WM5100_FLL2_LOCK_STS_SHIFT 3 /* FLL2_LOCK_STS */
+#define WM5100_FLL2_LOCK_STS_WIDTH 1 /* FLL2_LOCK_STS */
+#define WM5100_FLL1_LOCK_STS 0x0004 /* FLL1_LOCK_STS */
+#define WM5100_FLL1_LOCK_STS_MASK 0x0004 /* FLL1_LOCK_STS */
+#define WM5100_FLL1_LOCK_STS_SHIFT 2 /* FLL1_LOCK_STS */
+#define WM5100_FLL1_LOCK_STS_WIDTH 1 /* FLL1_LOCK_STS */
+#define WM5100_CLKGEN_ERR_STS 0x0002 /* CLKGEN_ERR_STS */
+#define WM5100_CLKGEN_ERR_STS_MASK 0x0002 /* CLKGEN_ERR_STS */
+#define WM5100_CLKGEN_ERR_STS_SHIFT 1 /* CLKGEN_ERR_STS */
+#define WM5100_CLKGEN_ERR_STS_WIDTH 1 /* CLKGEN_ERR_STS */
+#define WM5100_CLKGEN_ERR_ASYNC_STS 0x0001 /* CLKGEN_ERR_ASYNC_STS */
+#define WM5100_CLKGEN_ERR_ASYNC_STS_MASK 0x0001 /* CLKGEN_ERR_ASYNC_STS */
+#define WM5100_CLKGEN_ERR_ASYNC_STS_SHIFT 0 /* CLKGEN_ERR_ASYNC_STS */
+#define WM5100_CLKGEN_ERR_ASYNC_STS_WIDTH 1 /* CLKGEN_ERR_ASYNC_STS */
+
+/*
+ * R3334 (0xD06) - Interrupt Raw Status 4
+ */
+#define WM5100_AIF3_ERR_STS 0x2000 /* AIF3_ERR_STS */
+#define WM5100_AIF3_ERR_STS_MASK 0x2000 /* AIF3_ERR_STS */
+#define WM5100_AIF3_ERR_STS_SHIFT 13 /* AIF3_ERR_STS */
+#define WM5100_AIF3_ERR_STS_WIDTH 1 /* AIF3_ERR_STS */
+#define WM5100_AIF2_ERR_STS 0x1000 /* AIF2_ERR_STS */
+#define WM5100_AIF2_ERR_STS_MASK 0x1000 /* AIF2_ERR_STS */
+#define WM5100_AIF2_ERR_STS_SHIFT 12 /* AIF2_ERR_STS */
+#define WM5100_AIF2_ERR_STS_WIDTH 1 /* AIF2_ERR_STS */
+#define WM5100_AIF1_ERR_STS 0x0800 /* AIF1_ERR_STS */
+#define WM5100_AIF1_ERR_STS_MASK 0x0800 /* AIF1_ERR_STS */
+#define WM5100_AIF1_ERR_STS_SHIFT 11 /* AIF1_ERR_STS */
+#define WM5100_AIF1_ERR_STS_WIDTH 1 /* AIF1_ERR_STS */
+#define WM5100_CTRLIF_ERR_STS 0x0400 /* CTRLIF_ERR_STS */
+#define WM5100_CTRLIF_ERR_STS_MASK 0x0400 /* CTRLIF_ERR_STS */
+#define WM5100_CTRLIF_ERR_STS_SHIFT 10 /* CTRLIF_ERR_STS */
+#define WM5100_CTRLIF_ERR_STS_WIDTH 1 /* CTRLIF_ERR_STS */
+#define WM5100_ISRC2_UNDERCLOCKED_STS 0x0200 /* ISRC2_UNDERCLOCKED_STS */
+#define WM5100_ISRC2_UNDERCLOCKED_STS_MASK 0x0200 /* ISRC2_UNDERCLOCKED_STS */
+#define WM5100_ISRC2_UNDERCLOCKED_STS_SHIFT 9 /* ISRC2_UNDERCLOCKED_STS */
+#define WM5100_ISRC2_UNDERCLOCKED_STS_WIDTH 1 /* ISRC2_UNDERCLOCKED_STS */
+#define WM5100_ISRC1_UNDERCLOCKED_STS 0x0100 /* ISRC1_UNDERCLOCKED_STS */
+#define WM5100_ISRC1_UNDERCLOCKED_STS_MASK 0x0100 /* ISRC1_UNDERCLOCKED_STS */
+#define WM5100_ISRC1_UNDERCLOCKED_STS_SHIFT 8 /* ISRC1_UNDERCLOCKED_STS */
+#define WM5100_ISRC1_UNDERCLOCKED_STS_WIDTH 1 /* ISRC1_UNDERCLOCKED_STS */
+#define WM5100_FX_UNDERCLOCKED_STS 0x0080 /* FX_UNDERCLOCKED_STS */
+#define WM5100_FX_UNDERCLOCKED_STS_MASK 0x0080 /* FX_UNDERCLOCKED_STS */
+#define WM5100_FX_UNDERCLOCKED_STS_SHIFT 7 /* FX_UNDERCLOCKED_STS */
+#define WM5100_FX_UNDERCLOCKED_STS_WIDTH 1 /* FX_UNDERCLOCKED_STS */
+#define WM5100_AIF3_UNDERCLOCKED_STS 0x0040 /* AIF3_UNDERCLOCKED_STS */
+#define WM5100_AIF3_UNDERCLOCKED_STS_MASK 0x0040 /* AIF3_UNDERCLOCKED_STS */
+#define WM5100_AIF3_UNDERCLOCKED_STS_SHIFT 6 /* AIF3_UNDERCLOCKED_STS */
+#define WM5100_AIF3_UNDERCLOCKED_STS_WIDTH 1 /* AIF3_UNDERCLOCKED_STS */
+#define WM5100_AIF2_UNDERCLOCKED_STS 0x0020 /* AIF2_UNDERCLOCKED_STS */
+#define WM5100_AIF2_UNDERCLOCKED_STS_MASK 0x0020 /* AIF2_UNDERCLOCKED_STS */
+#define WM5100_AIF2_UNDERCLOCKED_STS_SHIFT 5 /* AIF2_UNDERCLOCKED_STS */
+#define WM5100_AIF2_UNDERCLOCKED_STS_WIDTH 1 /* AIF2_UNDERCLOCKED_STS */
+#define WM5100_AIF1_UNDERCLOCKED_STS 0x0010 /* AIF1_UNDERCLOCKED_STS */
+#define WM5100_AIF1_UNDERCLOCKED_STS_MASK 0x0010 /* AIF1_UNDERCLOCKED_STS */
+#define WM5100_AIF1_UNDERCLOCKED_STS_SHIFT 4 /* AIF1_UNDERCLOCKED_STS */
+#define WM5100_AIF1_UNDERCLOCKED_STS_WIDTH 1 /* AIF1_UNDERCLOCKED_STS */
+#define WM5100_ASRC_UNDERCLOCKED_STS 0x0008 /* ASRC_UNDERCLOCKED_STS */
+#define WM5100_ASRC_UNDERCLOCKED_STS_MASK 0x0008 /* ASRC_UNDERCLOCKED_STS */
+#define WM5100_ASRC_UNDERCLOCKED_STS_SHIFT 3 /* ASRC_UNDERCLOCKED_STS */
+#define WM5100_ASRC_UNDERCLOCKED_STS_WIDTH 1 /* ASRC_UNDERCLOCKED_STS */
+#define WM5100_DAC_UNDERCLOCKED_STS 0x0004 /* DAC_UNDERCLOCKED_STS */
+#define WM5100_DAC_UNDERCLOCKED_STS_MASK 0x0004 /* DAC_UNDERCLOCKED_STS */
+#define WM5100_DAC_UNDERCLOCKED_STS_SHIFT 2 /* DAC_UNDERCLOCKED_STS */
+#define WM5100_DAC_UNDERCLOCKED_STS_WIDTH 1 /* DAC_UNDERCLOCKED_STS */
+#define WM5100_ADC_UNDERCLOCKED_STS 0x0002 /* ADC_UNDERCLOCKED_STS */
+#define WM5100_ADC_UNDERCLOCKED_STS_MASK 0x0002 /* ADC_UNDERCLOCKED_STS */
+#define WM5100_ADC_UNDERCLOCKED_STS_SHIFT 1 /* ADC_UNDERCLOCKED_STS */
+#define WM5100_ADC_UNDERCLOCKED_STS_WIDTH 1 /* ADC_UNDERCLOCKED_STS */
+#define WM5100_MIXER_UNDERCLOCKED_STS 0x0001 /* MIXER_UNDERCLOCKED_STS */
+#define WM5100_MIXER_UNDERCLOCKED_STS_MASK 0x0001 /* MIXER_UNDERCLOCKED_STS */
+#define WM5100_MIXER_UNDERCLOCKED_STS_SHIFT 0 /* MIXER_UNDERCLOCKED_STS */
+#define WM5100_MIXER_UNDERCLOCKED_STS_WIDTH 1 /* MIXER_UNDERCLOCKED_STS */
+
+/*
+ * R3335 (0xD07) - Interrupt Status 1 Mask
+ */
+#define WM5100_IM_GP6_EINT 0x0020 /* IM_GP6_EINT */
+#define WM5100_IM_GP6_EINT_MASK 0x0020 /* IM_GP6_EINT */
+#define WM5100_IM_GP6_EINT_SHIFT 5 /* IM_GP6_EINT */
+#define WM5100_IM_GP6_EINT_WIDTH 1 /* IM_GP6_EINT */
+#define WM5100_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */
+#define WM5100_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */
+#define WM5100_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */
+#define WM5100_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */
+#define WM5100_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */
+#define WM5100_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */
+#define WM5100_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */
+#define WM5100_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */
+#define WM5100_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */
+#define WM5100_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */
+#define WM5100_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */
+#define WM5100_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */
+#define WM5100_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */
+#define WM5100_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */
+#define WM5100_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */
+#define WM5100_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */
+#define WM5100_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */
+#define WM5100_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */
+#define WM5100_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */
+#define WM5100_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */
+
+/*
+ * R3336 (0xD08) - Interrupt Status 2 Mask
+ */
+#define WM5100_IM_DSP_IRQ6_EINT 0x0020 /* IM_DSP_IRQ6_EINT */
+#define WM5100_IM_DSP_IRQ6_EINT_MASK 0x0020 /* IM_DSP_IRQ6_EINT */
+#define WM5100_IM_DSP_IRQ6_EINT_SHIFT 5 /* IM_DSP_IRQ6_EINT */
+#define WM5100_IM_DSP_IRQ6_EINT_WIDTH 1 /* IM_DSP_IRQ6_EINT */
+#define WM5100_IM_DSP_IRQ5_EINT 0x0010 /* IM_DSP_IRQ5_EINT */
+#define WM5100_IM_DSP_IRQ5_EINT_MASK 0x0010 /* IM_DSP_IRQ5_EINT */
+#define WM5100_IM_DSP_IRQ5_EINT_SHIFT 4 /* IM_DSP_IRQ5_EINT */
+#define WM5100_IM_DSP_IRQ5_EINT_WIDTH 1 /* IM_DSP_IRQ5_EINT */
+#define WM5100_IM_DSP_IRQ4_EINT 0x0008 /* IM_DSP_IRQ4_EINT */
+#define WM5100_IM_DSP_IRQ4_EINT_MASK 0x0008 /* IM_DSP_IRQ4_EINT */
+#define WM5100_IM_DSP_IRQ4_EINT_SHIFT 3 /* IM_DSP_IRQ4_EINT */
+#define WM5100_IM_DSP_IRQ4_EINT_WIDTH 1 /* IM_DSP_IRQ4_EINT */
+#define WM5100_IM_DSP_IRQ3_EINT 0x0004 /* IM_DSP_IRQ3_EINT */
+#define WM5100_IM_DSP_IRQ3_EINT_MASK 0x0004 /* IM_DSP_IRQ3_EINT */
+#define WM5100_IM_DSP_IRQ3_EINT_SHIFT 2 /* IM_DSP_IRQ3_EINT */
+#define WM5100_IM_DSP_IRQ3_EINT_WIDTH 1 /* IM_DSP_IRQ3_EINT */
+#define WM5100_IM_DSP_IRQ2_EINT 0x0002 /* IM_DSP_IRQ2_EINT */
+#define WM5100_IM_DSP_IRQ2_EINT_MASK 0x0002 /* IM_DSP_IRQ2_EINT */
+#define WM5100_IM_DSP_IRQ2_EINT_SHIFT 1 /* IM_DSP_IRQ2_EINT */
+#define WM5100_IM_DSP_IRQ2_EINT_WIDTH 1 /* IM_DSP_IRQ2_EINT */
+#define WM5100_IM_DSP_IRQ1_EINT 0x0001 /* IM_DSP_IRQ1_EINT */
+#define WM5100_IM_DSP_IRQ1_EINT_MASK 0x0001 /* IM_DSP_IRQ1_EINT */
+#define WM5100_IM_DSP_IRQ1_EINT_SHIFT 0 /* IM_DSP_IRQ1_EINT */
+#define WM5100_IM_DSP_IRQ1_EINT_WIDTH 1 /* IM_DSP_IRQ1_EINT */
+
+/*
+ * R3337 (0xD09) - Interrupt Status 3 Mask
+ */
+#define WM5100_IM_SPK_SHUTDOWN_WARN_EINT 0x8000 /* IM_SPK_SHUTDOWN_WARN_EINT */
+#define WM5100_IM_SPK_SHUTDOWN_WARN_EINT_MASK 0x8000 /* IM_SPK_SHUTDOWN_WARN_EINT */
+#define WM5100_IM_SPK_SHUTDOWN_WARN_EINT_SHIFT 15 /* IM_SPK_SHUTDOWN_WARN_EINT */
+#define WM5100_IM_SPK_SHUTDOWN_WARN_EINT_WIDTH 1 /* IM_SPK_SHUTDOWN_WARN_EINT */
+#define WM5100_IM_SPK_SHUTDOWN_EINT 0x4000 /* IM_SPK_SHUTDOWN_EINT */
+#define WM5100_IM_SPK_SHUTDOWN_EINT_MASK 0x4000 /* IM_SPK_SHUTDOWN_EINT */
+#define WM5100_IM_SPK_SHUTDOWN_EINT_SHIFT 14 /* IM_SPK_SHUTDOWN_EINT */
+#define WM5100_IM_SPK_SHUTDOWN_EINT_WIDTH 1 /* IM_SPK_SHUTDOWN_EINT */
+#define WM5100_IM_HPDET_EINT 0x2000 /* IM_HPDET_EINT */
+#define WM5100_IM_HPDET_EINT_MASK 0x2000 /* IM_HPDET_EINT */
+#define WM5100_IM_HPDET_EINT_SHIFT 13 /* IM_HPDET_EINT */
+#define WM5100_IM_HPDET_EINT_WIDTH 1 /* IM_HPDET_EINT */
+#define WM5100_IM_ACCDET_EINT 0x1000 /* IM_ACCDET_EINT */
+#define WM5100_IM_ACCDET_EINT_MASK 0x1000 /* IM_ACCDET_EINT */
+#define WM5100_IM_ACCDET_EINT_SHIFT 12 /* IM_ACCDET_EINT */
+#define WM5100_IM_ACCDET_EINT_WIDTH 1 /* IM_ACCDET_EINT */
+#define WM5100_IM_DRC_SIG_DET_EINT 0x0200 /* IM_DRC_SIG_DET_EINT */
+#define WM5100_IM_DRC_SIG_DET_EINT_MASK 0x0200 /* IM_DRC_SIG_DET_EINT */
+#define WM5100_IM_DRC_SIG_DET_EINT_SHIFT 9 /* IM_DRC_SIG_DET_EINT */
+#define WM5100_IM_DRC_SIG_DET_EINT_WIDTH 1 /* IM_DRC_SIG_DET_EINT */
+#define WM5100_IM_ASRC2_LOCK_EINT 0x0100 /* IM_ASRC2_LOCK_EINT */
+#define WM5100_IM_ASRC2_LOCK_EINT_MASK 0x0100 /* IM_ASRC2_LOCK_EINT */
+#define WM5100_IM_ASRC2_LOCK_EINT_SHIFT 8 /* IM_ASRC2_LOCK_EINT */
+#define WM5100_IM_ASRC2_LOCK_EINT_WIDTH 1 /* IM_ASRC2_LOCK_EINT */
+#define WM5100_IM_ASRC1_LOCK_EINT 0x0080 /* IM_ASRC1_LOCK_EINT */
+#define WM5100_IM_ASRC1_LOCK_EINT_MASK 0x0080 /* IM_ASRC1_LOCK_EINT */
+#define WM5100_IM_ASRC1_LOCK_EINT_SHIFT 7 /* IM_ASRC1_LOCK_EINT */
+#define WM5100_IM_ASRC1_LOCK_EINT_WIDTH 1 /* IM_ASRC1_LOCK_EINT */
+#define WM5100_IM_FLL2_LOCK_EINT 0x0008 /* IM_FLL2_LOCK_EINT */
+#define WM5100_IM_FLL2_LOCK_EINT_MASK 0x0008 /* IM_FLL2_LOCK_EINT */
+#define WM5100_IM_FLL2_LOCK_EINT_SHIFT 3 /* IM_FLL2_LOCK_EINT */
+#define WM5100_IM_FLL2_LOCK_EINT_WIDTH 1 /* IM_FLL2_LOCK_EINT */
+#define WM5100_IM_FLL1_LOCK_EINT 0x0004 /* IM_FLL1_LOCK_EINT */
+#define WM5100_IM_FLL1_LOCK_EINT_MASK 0x0004 /* IM_FLL1_LOCK_EINT */
+#define WM5100_IM_FLL1_LOCK_EINT_SHIFT 2 /* IM_FLL1_LOCK_EINT */
+#define WM5100_IM_FLL1_LOCK_EINT_WIDTH 1 /* IM_FLL1_LOCK_EINT */
+#define WM5100_IM_CLKGEN_ERR_EINT 0x0002 /* IM_CLKGEN_ERR_EINT */
+#define WM5100_IM_CLKGEN_ERR_EINT_MASK 0x0002 /* IM_CLKGEN_ERR_EINT */
+#define WM5100_IM_CLKGEN_ERR_EINT_SHIFT 1 /* IM_CLKGEN_ERR_EINT */
+#define WM5100_IM_CLKGEN_ERR_EINT_WIDTH 1 /* IM_CLKGEN_ERR_EINT */
+#define WM5100_IM_CLKGEN_ERR_ASYNC_EINT 0x0001 /* IM_CLKGEN_ERR_ASYNC_EINT */
+#define WM5100_IM_CLKGEN_ERR_ASYNC_EINT_MASK 0x0001 /* IM_CLKGEN_ERR_ASYNC_EINT */
+#define WM5100_IM_CLKGEN_ERR_ASYNC_EINT_SHIFT 0 /* IM_CLKGEN_ERR_ASYNC_EINT */
+#define WM5100_IM_CLKGEN_ERR_ASYNC_EINT_WIDTH 1 /* IM_CLKGEN_ERR_ASYNC_EINT */
+
+/*
+ * R3338 (0xD0A) - Interrupt Status 4 Mask
+ */
+#define WM5100_IM_AIF3_ERR_EINT 0x2000 /* IM_AIF3_ERR_EINT */
+#define WM5100_IM_AIF3_ERR_EINT_MASK 0x2000 /* IM_AIF3_ERR_EINT */
+#define WM5100_IM_AIF3_ERR_EINT_SHIFT 13 /* IM_AIF3_ERR_EINT */
+#define WM5100_IM_AIF3_ERR_EINT_WIDTH 1 /* IM_AIF3_ERR_EINT */
+#define WM5100_IM_AIF2_ERR_EINT 0x1000 /* IM_AIF2_ERR_EINT */
+#define WM5100_IM_AIF2_ERR_EINT_MASK 0x1000 /* IM_AIF2_ERR_EINT */
+#define WM5100_IM_AIF2_ERR_EINT_SHIFT 12 /* IM_AIF2_ERR_EINT */
+#define WM5100_IM_AIF2_ERR_EINT_WIDTH 1 /* IM_AIF2_ERR_EINT */
+#define WM5100_IM_AIF1_ERR_EINT 0x0800 /* IM_AIF1_ERR_EINT */
+#define WM5100_IM_AIF1_ERR_EINT_MASK 0x0800 /* IM_AIF1_ERR_EINT */
+#define WM5100_IM_AIF1_ERR_EINT_SHIFT 11 /* IM_AIF1_ERR_EINT */
+#define WM5100_IM_AIF1_ERR_EINT_WIDTH 1 /* IM_AIF1_ERR_EINT */
+#define WM5100_IM_CTRLIF_ERR_EINT 0x0400 /* IM_CTRLIF_ERR_EINT */
+#define WM5100_IM_CTRLIF_ERR_EINT_MASK 0x0400 /* IM_CTRLIF_ERR_EINT */
+#define WM5100_IM_CTRLIF_ERR_EINT_SHIFT 10 /* IM_CTRLIF_ERR_EINT */
+#define WM5100_IM_CTRLIF_ERR_EINT_WIDTH 1 /* IM_CTRLIF_ERR_EINT */
+#define WM5100_IM_ISRC2_UNDERCLOCKED_EINT 0x0200 /* IM_ISRC2_UNDERCLOCKED_EINT */
+#define WM5100_IM_ISRC2_UNDERCLOCKED_EINT_MASK 0x0200 /* IM_ISRC2_UNDERCLOCKED_EINT */
+#define WM5100_IM_ISRC2_UNDERCLOCKED_EINT_SHIFT 9 /* IM_ISRC2_UNDERCLOCKED_EINT */
+#define WM5100_IM_ISRC2_UNDERCLOCKED_EINT_WIDTH 1 /* IM_ISRC2_UNDERCLOCKED_EINT */
+#define WM5100_IM_ISRC1_UNDERCLOCKED_EINT 0x0100 /* IM_ISRC1_UNDERCLOCKED_EINT */
+#define WM5100_IM_ISRC1_UNDERCLOCKED_EINT_MASK 0x0100 /* IM_ISRC1_UNDERCLOCKED_EINT */
+#define WM5100_IM_ISRC1_UNDERCLOCKED_EINT_SHIFT 8 /* IM_ISRC1_UNDERCLOCKED_EINT */
+#define WM5100_IM_ISRC1_UNDERCLOCKED_EINT_WIDTH 1 /* IM_ISRC1_UNDERCLOCKED_EINT */
+#define WM5100_IM_FX_UNDERCLOCKED_EINT 0x0080 /* IM_FX_UNDERCLOCKED_EINT */
+#define WM5100_IM_FX_UNDERCLOCKED_EINT_MASK 0x0080 /* IM_FX_UNDERCLOCKED_EINT */
+#define WM5100_IM_FX_UNDERCLOCKED_EINT_SHIFT 7 /* IM_FX_UNDERCLOCKED_EINT */
+#define WM5100_IM_FX_UNDERCLOCKED_EINT_WIDTH 1 /* IM_FX_UNDERCLOCKED_EINT */
+#define WM5100_IM_AIF3_UNDERCLOCKED_EINT 0x0040 /* IM_AIF3_UNDERCLOCKED_EINT */
+#define WM5100_IM_AIF3_UNDERCLOCKED_EINT_MASK 0x0040 /* IM_AIF3_UNDERCLOCKED_EINT */
+#define WM5100_IM_AIF3_UNDERCLOCKED_EINT_SHIFT 6 /* IM_AIF3_UNDERCLOCKED_EINT */
+#define WM5100_IM_AIF3_UNDERCLOCKED_EINT_WIDTH 1 /* IM_AIF3_UNDERCLOCKED_EINT */
+#define WM5100_IM_AIF2_UNDERCLOCKED_EINT 0x0020 /* IM_AIF2_UNDERCLOCKED_EINT */
+#define WM5100_IM_AIF2_UNDERCLOCKED_EINT_MASK 0x0020 /* IM_AIF2_UNDERCLOCKED_EINT */
+#define WM5100_IM_AIF2_UNDERCLOCKED_EINT_SHIFT 5 /* IM_AIF2_UNDERCLOCKED_EINT */
+#define WM5100_IM_AIF2_UNDERCLOCKED_EINT_WIDTH 1 /* IM_AIF2_UNDERCLOCKED_EINT */
+#define WM5100_IM_AIF1_UNDERCLOCKED_EINT 0x0010 /* IM_AIF1_UNDERCLOCKED_EINT */
+#define WM5100_IM_AIF1_UNDERCLOCKED_EINT_MASK 0x0010 /* IM_AIF1_UNDERCLOCKED_EINT */
+#define WM5100_IM_AIF1_UNDERCLOCKED_EINT_SHIFT 4 /* IM_AIF1_UNDERCLOCKED_EINT */
+#define WM5100_IM_AIF1_UNDERCLOCKED_EINT_WIDTH 1 /* IM_AIF1_UNDERCLOCKED_EINT */
+#define WM5100_IM_ASRC_UNDERCLOCKED_EINT 0x0008 /* IM_ASRC_UNDERCLOCKED_EINT */
+#define WM5100_IM_ASRC_UNDERCLOCKED_EINT_MASK 0x0008 /* IM_ASRC_UNDERCLOCKED_EINT */
+#define WM5100_IM_ASRC_UNDERCLOCKED_EINT_SHIFT 3 /* IM_ASRC_UNDERCLOCKED_EINT */
+#define WM5100_IM_ASRC_UNDERCLOCKED_EINT_WIDTH 1 /* IM_ASRC_UNDERCLOCKED_EINT */
+#define WM5100_IM_DAC_UNDERCLOCKED_EINT 0x0004 /* IM_DAC_UNDERCLOCKED_EINT */
+#define WM5100_IM_DAC_UNDERCLOCKED_EINT_MASK 0x0004 /* IM_DAC_UNDERCLOCKED_EINT */
+#define WM5100_IM_DAC_UNDERCLOCKED_EINT_SHIFT 2 /* IM_DAC_UNDERCLOCKED_EINT */
+#define WM5100_IM_DAC_UNDERCLOCKED_EINT_WIDTH 1 /* IM_DAC_UNDERCLOCKED_EINT */
+#define WM5100_IM_ADC_UNDERCLOCKED_EINT 0x0002 /* IM_ADC_UNDERCLOCKED_EINT */
+#define WM5100_IM_ADC_UNDERCLOCKED_EINT_MASK 0x0002 /* IM_ADC_UNDERCLOCKED_EINT */
+#define WM5100_IM_ADC_UNDERCLOCKED_EINT_SHIFT 1 /* IM_ADC_UNDERCLOCKED_EINT */
+#define WM5100_IM_ADC_UNDERCLOCKED_EINT_WIDTH 1 /* IM_ADC_UNDERCLOCKED_EINT */
+#define WM5100_IM_MIXER_UNDERCLOCKED_EINT 0x0001 /* IM_MIXER_UNDERCLOCKED_EINT */
+#define WM5100_IM_MIXER_UNDERCLOCKED_EINT_MASK 0x0001 /* IM_MIXER_UNDERCLOCKED_EINT */
+#define WM5100_IM_MIXER_UNDERCLOCKED_EINT_SHIFT 0 /* IM_MIXER_UNDERCLOCKED_EINT */
+#define WM5100_IM_MIXER_UNDERCLOCKED_EINT_WIDTH 1 /* IM_MIXER_UNDERCLOCKED_EINT */
+
+/*
+ * R3359 (0xD1F) - Interrupt Control
+ */
+#define WM5100_IM_IRQ 0x0001 /* IM_IRQ */
+#define WM5100_IM_IRQ_MASK 0x0001 /* IM_IRQ */
+#define WM5100_IM_IRQ_SHIFT 0 /* IM_IRQ */
+#define WM5100_IM_IRQ_WIDTH 1 /* IM_IRQ */
+
+/*
+ * R3360 (0xD20) - IRQ Debounce 1
+ */
+#define WM5100_SPK_SHUTDOWN_WARN_DB 0x0200 /* SPK_SHUTDOWN_WARN_DB */
+#define WM5100_SPK_SHUTDOWN_WARN_DB_MASK 0x0200 /* SPK_SHUTDOWN_WARN_DB */
+#define WM5100_SPK_SHUTDOWN_WARN_DB_SHIFT 9 /* SPK_SHUTDOWN_WARN_DB */
+#define WM5100_SPK_SHUTDOWN_WARN_DB_WIDTH 1 /* SPK_SHUTDOWN_WARN_DB */
+#define WM5100_SPK_SHUTDOWN_DB 0x0100 /* SPK_SHUTDOWN_DB */
+#define WM5100_SPK_SHUTDOWN_DB_MASK 0x0100 /* SPK_SHUTDOWN_DB */
+#define WM5100_SPK_SHUTDOWN_DB_SHIFT 8 /* SPK_SHUTDOWN_DB */
+#define WM5100_SPK_SHUTDOWN_DB_WIDTH 1 /* SPK_SHUTDOWN_DB */
+#define WM5100_FLL1_LOCK_IRQ_DB 0x0008 /* FLL1_LOCK_IRQ_DB */
+#define WM5100_FLL1_LOCK_IRQ_DB_MASK 0x0008 /* FLL1_LOCK_IRQ_DB */
+#define WM5100_FLL1_LOCK_IRQ_DB_SHIFT 3 /* FLL1_LOCK_IRQ_DB */
+#define WM5100_FLL1_LOCK_IRQ_DB_WIDTH 1 /* FLL1_LOCK_IRQ_DB */
+#define WM5100_FLL2_LOCK_IRQ_DB 0x0004 /* FLL2_LOCK_IRQ_DB */
+#define WM5100_FLL2_LOCK_IRQ_DB_MASK 0x0004 /* FLL2_LOCK_IRQ_DB */
+#define WM5100_FLL2_LOCK_IRQ_DB_SHIFT 2 /* FLL2_LOCK_IRQ_DB */
+#define WM5100_FLL2_LOCK_IRQ_DB_WIDTH 1 /* FLL2_LOCK_IRQ_DB */
+#define WM5100_CLKGEN_ERR_IRQ_DB 0x0002 /* CLKGEN_ERR_IRQ_DB */
+#define WM5100_CLKGEN_ERR_IRQ_DB_MASK 0x0002 /* CLKGEN_ERR_IRQ_DB */
+#define WM5100_CLKGEN_ERR_IRQ_DB_SHIFT 1 /* CLKGEN_ERR_IRQ_DB */
+#define WM5100_CLKGEN_ERR_IRQ_DB_WIDTH 1 /* CLKGEN_ERR_IRQ_DB */
+#define WM5100_CLKGEN_ERR_ASYNC_IRQ_DB 0x0001 /* CLKGEN_ERR_ASYNC_IRQ_DB */
+#define WM5100_CLKGEN_ERR_ASYNC_IRQ_DB_MASK 0x0001 /* CLKGEN_ERR_ASYNC_IRQ_DB */
+#define WM5100_CLKGEN_ERR_ASYNC_IRQ_DB_SHIFT 0 /* CLKGEN_ERR_ASYNC_IRQ_DB */
+#define WM5100_CLKGEN_ERR_ASYNC_IRQ_DB_WIDTH 1 /* CLKGEN_ERR_ASYNC_IRQ_DB */
+
+/*
+ * R3361 (0xD21) - IRQ Debounce 2
+ */
+#define WM5100_AIF_ERR_DB 0x0001 /* AIF_ERR_DB */
+#define WM5100_AIF_ERR_DB_MASK 0x0001 /* AIF_ERR_DB */
+#define WM5100_AIF_ERR_DB_SHIFT 0 /* AIF_ERR_DB */
+#define WM5100_AIF_ERR_DB_WIDTH 1 /* AIF_ERR_DB */
+
+/*
+ * R3584 (0xE00) - FX_Ctrl
+ */
+#define WM5100_FX_STS_MASK 0xFFC0 /* FX_STS - [15:6] */
+#define WM5100_FX_STS_SHIFT 6 /* FX_STS - [15:6] */
+#define WM5100_FX_STS_WIDTH 10 /* FX_STS - [15:6] */
+#define WM5100_FX_RATE_MASK 0x0003 /* FX_RATE - [1:0] */
+#define WM5100_FX_RATE_SHIFT 0 /* FX_RATE - [1:0] */
+#define WM5100_FX_RATE_WIDTH 2 /* FX_RATE - [1:0] */
+
+/*
+ * R3600 (0xE10) - EQ1_1
+ */
+#define WM5100_EQ1_B1_GAIN_MASK 0xF800 /* EQ1_B1_GAIN - [15:11] */
+#define WM5100_EQ1_B1_GAIN_SHIFT 11 /* EQ1_B1_GAIN - [15:11] */
+#define WM5100_EQ1_B1_GAIN_WIDTH 5 /* EQ1_B1_GAIN - [15:11] */
+#define WM5100_EQ1_B2_GAIN_MASK 0x07C0 /* EQ1_B2_GAIN - [10:6] */
+#define WM5100_EQ1_B2_GAIN_SHIFT 6 /* EQ1_B2_GAIN - [10:6] */
+#define WM5100_EQ1_B2_GAIN_WIDTH 5 /* EQ1_B2_GAIN - [10:6] */
+#define WM5100_EQ1_B3_GAIN_MASK 0x003E /* EQ1_B3_GAIN - [5:1] */
+#define WM5100_EQ1_B3_GAIN_SHIFT 1 /* EQ1_B3_GAIN - [5:1] */
+#define WM5100_EQ1_B3_GAIN_WIDTH 5 /* EQ1_B3_GAIN - [5:1] */
+#define WM5100_EQ1_ENA 0x0001 /* EQ1_ENA */
+#define WM5100_EQ1_ENA_MASK 0x0001 /* EQ1_ENA */
+#define WM5100_EQ1_ENA_SHIFT 0 /* EQ1_ENA */
+#define WM5100_EQ1_ENA_WIDTH 1 /* EQ1_ENA */
+
+/*
+ * R3601 (0xE11) - EQ1_2
+ */
+#define WM5100_EQ1_B4_GAIN_MASK 0xF800 /* EQ1_B4_GAIN - [15:11] */
+#define WM5100_EQ1_B4_GAIN_SHIFT 11 /* EQ1_B4_GAIN - [15:11] */
+#define WM5100_EQ1_B4_GAIN_WIDTH 5 /* EQ1_B4_GAIN - [15:11] */
+#define WM5100_EQ1_B5_GAIN_MASK 0x07C0 /* EQ1_B5_GAIN - [10:6] */
+#define WM5100_EQ1_B5_GAIN_SHIFT 6 /* EQ1_B5_GAIN - [10:6] */
+#define WM5100_EQ1_B5_GAIN_WIDTH 5 /* EQ1_B5_GAIN - [10:6] */
+
+/*
+ * R3602 (0xE12) - EQ1_3
+ */
+#define WM5100_EQ1_B1_A_MASK 0xFFFF /* EQ1_B1_A - [15:0] */
+#define WM5100_EQ1_B1_A_SHIFT 0 /* EQ1_B1_A - [15:0] */
+#define WM5100_EQ1_B1_A_WIDTH 16 /* EQ1_B1_A - [15:0] */
+
+/*
+ * R3603 (0xE13) - EQ1_4
+ */
+#define WM5100_EQ1_B1_B_MASK 0xFFFF /* EQ1_B1_B - [15:0] */
+#define WM5100_EQ1_B1_B_SHIFT 0 /* EQ1_B1_B - [15:0] */
+#define WM5100_EQ1_B1_B_WIDTH 16 /* EQ1_B1_B - [15:0] */
+
+/*
+ * R3604 (0xE14) - EQ1_5
+ */
+#define WM5100_EQ1_B1_PG_MASK 0xFFFF /* EQ1_B1_PG - [15:0] */
+#define WM5100_EQ1_B1_PG_SHIFT 0 /* EQ1_B1_PG - [15:0] */
+#define WM5100_EQ1_B1_PG_WIDTH 16 /* EQ1_B1_PG - [15:0] */
+
+/*
+ * R3605 (0xE15) - EQ1_6
+ */
+#define WM5100_EQ1_B2_A_MASK 0xFFFF /* EQ1_B2_A - [15:0] */
+#define WM5100_EQ1_B2_A_SHIFT 0 /* EQ1_B2_A - [15:0] */
+#define WM5100_EQ1_B2_A_WIDTH 16 /* EQ1_B2_A - [15:0] */
+
+/*
+ * R3606 (0xE16) - EQ1_7
+ */
+#define WM5100_EQ1_B2_B_MASK 0xFFFF /* EQ1_B2_B - [15:0] */
+#define WM5100_EQ1_B2_B_SHIFT 0 /* EQ1_B2_B - [15:0] */
+#define WM5100_EQ1_B2_B_WIDTH 16 /* EQ1_B2_B - [15:0] */
+
+/*
+ * R3607 (0xE17) - EQ1_8
+ */
+#define WM5100_EQ1_B2_C_MASK 0xFFFF /* EQ1_B2_C - [15:0] */
+#define WM5100_EQ1_B2_C_SHIFT 0 /* EQ1_B2_C - [15:0] */
+#define WM5100_EQ1_B2_C_WIDTH 16 /* EQ1_B2_C - [15:0] */
+
+/*
+ * R3608 (0xE18) - EQ1_9
+ */
+#define WM5100_EQ1_B2_PG_MASK 0xFFFF /* EQ1_B2_PG - [15:0] */
+#define WM5100_EQ1_B2_PG_SHIFT 0 /* EQ1_B2_PG - [15:0] */
+#define WM5100_EQ1_B2_PG_WIDTH 16 /* EQ1_B2_PG - [15:0] */
+
+/*
+ * R3609 (0xE19) - EQ1_10
+ */
+#define WM5100_EQ1_B3_A_MASK 0xFFFF /* EQ1_B3_A - [15:0] */
+#define WM5100_EQ1_B3_A_SHIFT 0 /* EQ1_B3_A - [15:0] */
+#define WM5100_EQ1_B3_A_WIDTH 16 /* EQ1_B3_A - [15:0] */
+
+/*
+ * R3610 (0xE1A) - EQ1_11
+ */
+#define WM5100_EQ1_B3_B_MASK 0xFFFF /* EQ1_B3_B - [15:0] */
+#define WM5100_EQ1_B3_B_SHIFT 0 /* EQ1_B3_B - [15:0] */
+#define WM5100_EQ1_B3_B_WIDTH 16 /* EQ1_B3_B - [15:0] */
+
+/*
+ * R3611 (0xE1B) - EQ1_12
+ */
+#define WM5100_EQ1_B3_C_MASK 0xFFFF /* EQ1_B3_C - [15:0] */
+#define WM5100_EQ1_B3_C_SHIFT 0 /* EQ1_B3_C - [15:0] */
+#define WM5100_EQ1_B3_C_WIDTH 16 /* EQ1_B3_C - [15:0] */
+
+/*
+ * R3612 (0xE1C) - EQ1_13
+ */
+#define WM5100_EQ1_B3_PG_MASK 0xFFFF /* EQ1_B3_PG - [15:0] */
+#define WM5100_EQ1_B3_PG_SHIFT 0 /* EQ1_B3_PG - [15:0] */
+#define WM5100_EQ1_B3_PG_WIDTH 16 /* EQ1_B3_PG - [15:0] */
+
+/*
+ * R3613 (0xE1D) - EQ1_14
+ */
+#define WM5100_EQ1_B4_A_MASK 0xFFFF /* EQ1_B4_A - [15:0] */
+#define WM5100_EQ1_B4_A_SHIFT 0 /* EQ1_B4_A - [15:0] */
+#define WM5100_EQ1_B4_A_WIDTH 16 /* EQ1_B4_A - [15:0] */
+
+/*
+ * R3614 (0xE1E) - EQ1_15
+ */
+#define WM5100_EQ1_B4_B_MASK 0xFFFF /* EQ1_B4_B - [15:0] */
+#define WM5100_EQ1_B4_B_SHIFT 0 /* EQ1_B4_B - [15:0] */
+#define WM5100_EQ1_B4_B_WIDTH 16 /* EQ1_B4_B - [15:0] */
+
+/*
+ * R3615 (0xE1F) - EQ1_16
+ */
+#define WM5100_EQ1_B4_C_MASK 0xFFFF /* EQ1_B4_C - [15:0] */
+#define WM5100_EQ1_B4_C_SHIFT 0 /* EQ1_B4_C - [15:0] */
+#define WM5100_EQ1_B4_C_WIDTH 16 /* EQ1_B4_C - [15:0] */
+
+/*
+ * R3616 (0xE20) - EQ1_17
+ */
+#define WM5100_EQ1_B4_PG_MASK 0xFFFF /* EQ1_B4_PG - [15:0] */
+#define WM5100_EQ1_B4_PG_SHIFT 0 /* EQ1_B4_PG - [15:0] */
+#define WM5100_EQ1_B4_PG_WIDTH 16 /* EQ1_B4_PG - [15:0] */
+
+/*
+ * R3617 (0xE21) - EQ1_18
+ */
+#define WM5100_EQ1_B5_A_MASK 0xFFFF /* EQ1_B5_A - [15:0] */
+#define WM5100_EQ1_B5_A_SHIFT 0 /* EQ1_B5_A - [15:0] */
+#define WM5100_EQ1_B5_A_WIDTH 16 /* EQ1_B5_A - [15:0] */
+
+/*
+ * R3618 (0xE22) - EQ1_19
+ */
+#define WM5100_EQ1_B5_B_MASK 0xFFFF /* EQ1_B5_B - [15:0] */
+#define WM5100_EQ1_B5_B_SHIFT 0 /* EQ1_B5_B - [15:0] */
+#define WM5100_EQ1_B5_B_WIDTH 16 /* EQ1_B5_B - [15:0] */
+
+/*
+ * R3619 (0xE23) - EQ1_20
+ */
+#define WM5100_EQ1_B5_PG_MASK 0xFFFF /* EQ1_B5_PG - [15:0] */
+#define WM5100_EQ1_B5_PG_SHIFT 0 /* EQ1_B5_PG - [15:0] */
+#define WM5100_EQ1_B5_PG_WIDTH 16 /* EQ1_B5_PG - [15:0] */
+
+/*
+ * R3622 (0xE26) - EQ2_1
+ */
+#define WM5100_EQ2_B1_GAIN_MASK 0xF800 /* EQ2_B1_GAIN - [15:11] */
+#define WM5100_EQ2_B1_GAIN_SHIFT 11 /* EQ2_B1_GAIN - [15:11] */
+#define WM5100_EQ2_B1_GAIN_WIDTH 5 /* EQ2_B1_GAIN - [15:11] */
+#define WM5100_EQ2_B2_GAIN_MASK 0x07C0 /* EQ2_B2_GAIN - [10:6] */
+#define WM5100_EQ2_B2_GAIN_SHIFT 6 /* EQ2_B2_GAIN - [10:6] */
+#define WM5100_EQ2_B2_GAIN_WIDTH 5 /* EQ2_B2_GAIN - [10:6] */
+#define WM5100_EQ2_B3_GAIN_MASK 0x003E /* EQ2_B3_GAIN - [5:1] */
+#define WM5100_EQ2_B3_GAIN_SHIFT 1 /* EQ2_B3_GAIN - [5:1] */
+#define WM5100_EQ2_B3_GAIN_WIDTH 5 /* EQ2_B3_GAIN - [5:1] */
+#define WM5100_EQ2_ENA 0x0001 /* EQ2_ENA */
+#define WM5100_EQ2_ENA_MASK 0x0001 /* EQ2_ENA */
+#define WM5100_EQ2_ENA_SHIFT 0 /* EQ2_ENA */
+#define WM5100_EQ2_ENA_WIDTH 1 /* EQ2_ENA */
+
+/*
+ * R3623 (0xE27) - EQ2_2
+ */
+#define WM5100_EQ2_B4_GAIN_MASK 0xF800 /* EQ2_B4_GAIN - [15:11] */
+#define WM5100_EQ2_B4_GAIN_SHIFT 11 /* EQ2_B4_GAIN - [15:11] */
+#define WM5100_EQ2_B4_GAIN_WIDTH 5 /* EQ2_B4_GAIN - [15:11] */
+#define WM5100_EQ2_B5_GAIN_MASK 0x07C0 /* EQ2_B5_GAIN - [10:6] */
+#define WM5100_EQ2_B5_GAIN_SHIFT 6 /* EQ2_B5_GAIN - [10:6] */
+#define WM5100_EQ2_B5_GAIN_WIDTH 5 /* EQ2_B5_GAIN - [10:6] */
+
+/*
+ * R3624 (0xE28) - EQ2_3
+ */
+#define WM5100_EQ2_B1_A_MASK 0xFFFF /* EQ2_B1_A - [15:0] */
+#define WM5100_EQ2_B1_A_SHIFT 0 /* EQ2_B1_A - [15:0] */
+#define WM5100_EQ2_B1_A_WIDTH 16 /* EQ2_B1_A - [15:0] */
+
+/*
+ * R3625 (0xE29) - EQ2_4
+ */
+#define WM5100_EQ2_B1_B_MASK 0xFFFF /* EQ2_B1_B - [15:0] */
+#define WM5100_EQ2_B1_B_SHIFT 0 /* EQ2_B1_B - [15:0] */
+#define WM5100_EQ2_B1_B_WIDTH 16 /* EQ2_B1_B - [15:0] */
+
+/*
+ * R3626 (0xE2A) - EQ2_5
+ */
+#define WM5100_EQ2_B1_PG_MASK 0xFFFF /* EQ2_B1_PG - [15:0] */
+#define WM5100_EQ2_B1_PG_SHIFT 0 /* EQ2_B1_PG - [15:0] */
+#define WM5100_EQ2_B1_PG_WIDTH 16 /* EQ2_B1_PG - [15:0] */
+
+/*
+ * R3627 (0xE2B) - EQ2_6
+ */
+#define WM5100_EQ2_B2_A_MASK 0xFFFF /* EQ2_B2_A - [15:0] */
+#define WM5100_EQ2_B2_A_SHIFT 0 /* EQ2_B2_A - [15:0] */
+#define WM5100_EQ2_B2_A_WIDTH 16 /* EQ2_B2_A - [15:0] */
+
+/*
+ * R3628 (0xE2C) - EQ2_7
+ */
+#define WM5100_EQ2_B2_B_MASK 0xFFFF /* EQ2_B2_B - [15:0] */
+#define WM5100_EQ2_B2_B_SHIFT 0 /* EQ2_B2_B - [15:0] */
+#define WM5100_EQ2_B2_B_WIDTH 16 /* EQ2_B2_B - [15:0] */
+
+/*
+ * R3629 (0xE2D) - EQ2_8
+ */
+#define WM5100_EQ2_B2_C_MASK 0xFFFF /* EQ2_B2_C - [15:0] */
+#define WM5100_EQ2_B2_C_SHIFT 0 /* EQ2_B2_C - [15:0] */
+#define WM5100_EQ2_B2_C_WIDTH 16 /* EQ2_B2_C - [15:0] */
+
+/*
+ * R3630 (0xE2E) - EQ2_9
+ */
+#define WM5100_EQ2_B2_PG_MASK 0xFFFF /* EQ2_B2_PG - [15:0] */
+#define WM5100_EQ2_B2_PG_SHIFT 0 /* EQ2_B2_PG - [15:0] */
+#define WM5100_EQ2_B2_PG_WIDTH 16 /* EQ2_B2_PG - [15:0] */
+
+/*
+ * R3631 (0xE2F) - EQ2_10
+ */
+#define WM5100_EQ2_B3_A_MASK 0xFFFF /* EQ2_B3_A - [15:0] */
+#define WM5100_EQ2_B3_A_SHIFT 0 /* EQ2_B3_A - [15:0] */
+#define WM5100_EQ2_B3_A_WIDTH 16 /* EQ2_B3_A - [15:0] */
+
+/*
+ * R3632 (0xE30) - EQ2_11
+ */
+#define WM5100_EQ2_B3_B_MASK 0xFFFF /* EQ2_B3_B - [15:0] */
+#define WM5100_EQ2_B3_B_SHIFT 0 /* EQ2_B3_B - [15:0] */
+#define WM5100_EQ2_B3_B_WIDTH 16 /* EQ2_B3_B - [15:0] */
+
+/*
+ * R3633 (0xE31) - EQ2_12
+ */
+#define WM5100_EQ2_B3_C_MASK 0xFFFF /* EQ2_B3_C - [15:0] */
+#define WM5100_EQ2_B3_C_SHIFT 0 /* EQ2_B3_C - [15:0] */
+#define WM5100_EQ2_B3_C_WIDTH 16 /* EQ2_B3_C - [15:0] */
+
+/*
+ * R3634 (0xE32) - EQ2_13
+ */
+#define WM5100_EQ2_B3_PG_MASK 0xFFFF /* EQ2_B3_PG - [15:0] */
+#define WM5100_EQ2_B3_PG_SHIFT 0 /* EQ2_B3_PG - [15:0] */
+#define WM5100_EQ2_B3_PG_WIDTH 16 /* EQ2_B3_PG - [15:0] */
+
+/*
+ * R3635 (0xE33) - EQ2_14
+ */
+#define WM5100_EQ2_B4_A_MASK 0xFFFF /* EQ2_B4_A - [15:0] */
+#define WM5100_EQ2_B4_A_SHIFT 0 /* EQ2_B4_A - [15:0] */
+#define WM5100_EQ2_B4_A_WIDTH 16 /* EQ2_B4_A - [15:0] */
+
+/*
+ * R3636 (0xE34) - EQ2_15
+ */
+#define WM5100_EQ2_B4_B_MASK 0xFFFF /* EQ2_B4_B - [15:0] */
+#define WM5100_EQ2_B4_B_SHIFT 0 /* EQ2_B4_B - [15:0] */
+#define WM5100_EQ2_B4_B_WIDTH 16 /* EQ2_B4_B - [15:0] */
+
+/*
+ * R3637 (0xE35) - EQ2_16
+ */
+#define WM5100_EQ2_B4_C_MASK 0xFFFF /* EQ2_B4_C - [15:0] */
+#define WM5100_EQ2_B4_C_SHIFT 0 /* EQ2_B4_C - [15:0] */
+#define WM5100_EQ2_B4_C_WIDTH 16 /* EQ2_B4_C - [15:0] */
+
+/*
+ * R3638 (0xE36) - EQ2_17
+ */
+#define WM5100_EQ2_B4_PG_MASK 0xFFFF /* EQ2_B4_PG - [15:0] */
+#define WM5100_EQ2_B4_PG_SHIFT 0 /* EQ2_B4_PG - [15:0] */
+#define WM5100_EQ2_B4_PG_WIDTH 16 /* EQ2_B4_PG - [15:0] */
+
+/*
+ * R3639 (0xE37) - EQ2_18
+ */
+#define WM5100_EQ2_B5_A_MASK 0xFFFF /* EQ2_B5_A - [15:0] */
+#define WM5100_EQ2_B5_A_SHIFT 0 /* EQ2_B5_A - [15:0] */
+#define WM5100_EQ2_B5_A_WIDTH 16 /* EQ2_B5_A - [15:0] */
+
+/*
+ * R3640 (0xE38) - EQ2_19
+ */
+#define WM5100_EQ2_B5_B_MASK 0xFFFF /* EQ2_B5_B - [15:0] */
+#define WM5100_EQ2_B5_B_SHIFT 0 /* EQ2_B5_B - [15:0] */
+#define WM5100_EQ2_B5_B_WIDTH 16 /* EQ2_B5_B - [15:0] */
+
+/*
+ * R3641 (0xE39) - EQ2_20
+ */
+#define WM5100_EQ2_B5_PG_MASK 0xFFFF /* EQ2_B5_PG - [15:0] */
+#define WM5100_EQ2_B5_PG_SHIFT 0 /* EQ2_B5_PG - [15:0] */
+#define WM5100_EQ2_B5_PG_WIDTH 16 /* EQ2_B5_PG - [15:0] */
+
+/*
+ * R3644 (0xE3C) - EQ3_1
+ */
+#define WM5100_EQ3_B1_GAIN_MASK 0xF800 /* EQ3_B1_GAIN - [15:11] */
+#define WM5100_EQ3_B1_GAIN_SHIFT 11 /* EQ3_B1_GAIN - [15:11] */
+#define WM5100_EQ3_B1_GAIN_WIDTH 5 /* EQ3_B1_GAIN - [15:11] */
+#define WM5100_EQ3_B2_GAIN_MASK 0x07C0 /* EQ3_B2_GAIN - [10:6] */
+#define WM5100_EQ3_B2_GAIN_SHIFT 6 /* EQ3_B2_GAIN - [10:6] */
+#define WM5100_EQ3_B2_GAIN_WIDTH 5 /* EQ3_B2_GAIN - [10:6] */
+#define WM5100_EQ3_B3_GAIN_MASK 0x003E /* EQ3_B3_GAIN - [5:1] */
+#define WM5100_EQ3_B3_GAIN_SHIFT 1 /* EQ3_B3_GAIN - [5:1] */
+#define WM5100_EQ3_B3_GAIN_WIDTH 5 /* EQ3_B3_GAIN - [5:1] */
+#define WM5100_EQ3_ENA 0x0001 /* EQ3_ENA */
+#define WM5100_EQ3_ENA_MASK 0x0001 /* EQ3_ENA */
+#define WM5100_EQ3_ENA_SHIFT 0 /* EQ3_ENA */
+#define WM5100_EQ3_ENA_WIDTH 1 /* EQ3_ENA */
+
+/*
+ * R3645 (0xE3D) - EQ3_2
+ */
+#define WM5100_EQ3_B4_GAIN_MASK 0xF800 /* EQ3_B4_GAIN - [15:11] */
+#define WM5100_EQ3_B4_GAIN_SHIFT 11 /* EQ3_B4_GAIN - [15:11] */
+#define WM5100_EQ3_B4_GAIN_WIDTH 5 /* EQ3_B4_GAIN - [15:11] */
+#define WM5100_EQ3_B5_GAIN_MASK 0x07C0 /* EQ3_B5_GAIN - [10:6] */
+#define WM5100_EQ3_B5_GAIN_SHIFT 6 /* EQ3_B5_GAIN - [10:6] */
+#define WM5100_EQ3_B5_GAIN_WIDTH 5 /* EQ3_B5_GAIN - [10:6] */
+
+/*
+ * R3646 (0xE3E) - EQ3_3
+ */
+#define WM5100_EQ3_B1_A_MASK 0xFFFF /* EQ3_B1_A - [15:0] */
+#define WM5100_EQ3_B1_A_SHIFT 0 /* EQ3_B1_A - [15:0] */
+#define WM5100_EQ3_B1_A_WIDTH 16 /* EQ3_B1_A - [15:0] */
+
+/*
+ * R3647 (0xE3F) - EQ3_4
+ */
+#define WM5100_EQ3_B1_B_MASK 0xFFFF /* EQ3_B1_B - [15:0] */
+#define WM5100_EQ3_B1_B_SHIFT 0 /* EQ3_B1_B - [15:0] */
+#define WM5100_EQ3_B1_B_WIDTH 16 /* EQ3_B1_B - [15:0] */
+
+/*
+ * R3648 (0xE40) - EQ3_5
+ */
+#define WM5100_EQ3_B1_PG_MASK 0xFFFF /* EQ3_B1_PG - [15:0] */
+#define WM5100_EQ3_B1_PG_SHIFT 0 /* EQ3_B1_PG - [15:0] */
+#define WM5100_EQ3_B1_PG_WIDTH 16 /* EQ3_B1_PG - [15:0] */
+
+/*
+ * R3649 (0xE41) - EQ3_6
+ */
+#define WM5100_EQ3_B2_A_MASK 0xFFFF /* EQ3_B2_A - [15:0] */
+#define WM5100_EQ3_B2_A_SHIFT 0 /* EQ3_B2_A - [15:0] */
+#define WM5100_EQ3_B2_A_WIDTH 16 /* EQ3_B2_A - [15:0] */
+
+/*
+ * R3650 (0xE42) - EQ3_7
+ */
+#define WM5100_EQ3_B2_B_MASK 0xFFFF /* EQ3_B2_B - [15:0] */
+#define WM5100_EQ3_B2_B_SHIFT 0 /* EQ3_B2_B - [15:0] */
+#define WM5100_EQ3_B2_B_WIDTH 16 /* EQ3_B2_B - [15:0] */
+
+/*
+ * R3651 (0xE43) - EQ3_8
+ */
+#define WM5100_EQ3_B2_C_MASK 0xFFFF /* EQ3_B2_C - [15:0] */
+#define WM5100_EQ3_B2_C_SHIFT 0 /* EQ3_B2_C - [15:0] */
+#define WM5100_EQ3_B2_C_WIDTH 16 /* EQ3_B2_C - [15:0] */
+
+/*
+ * R3652 (0xE44) - EQ3_9
+ */
+#define WM5100_EQ3_B2_PG_MASK 0xFFFF /* EQ3_B2_PG - [15:0] */
+#define WM5100_EQ3_B2_PG_SHIFT 0 /* EQ3_B2_PG - [15:0] */
+#define WM5100_EQ3_B2_PG_WIDTH 16 /* EQ3_B2_PG - [15:0] */
+
+/*
+ * R3653 (0xE45) - EQ3_10
+ */
+#define WM5100_EQ3_B3_A_MASK 0xFFFF /* EQ3_B3_A - [15:0] */
+#define WM5100_EQ3_B3_A_SHIFT 0 /* EQ3_B3_A - [15:0] */
+#define WM5100_EQ3_B3_A_WIDTH 16 /* EQ3_B3_A - [15:0] */
+
+/*
+ * R3654 (0xE46) - EQ3_11
+ */
+#define WM5100_EQ3_B3_B_MASK 0xFFFF /* EQ3_B3_B - [15:0] */
+#define WM5100_EQ3_B3_B_SHIFT 0 /* EQ3_B3_B - [15:0] */
+#define WM5100_EQ3_B3_B_WIDTH 16 /* EQ3_B3_B - [15:0] */
+
+/*
+ * R3655 (0xE47) - EQ3_12
+ */
+#define WM5100_EQ3_B3_C_MASK 0xFFFF /* EQ3_B3_C - [15:0] */
+#define WM5100_EQ3_B3_C_SHIFT 0 /* EQ3_B3_C - [15:0] */
+#define WM5100_EQ3_B3_C_WIDTH 16 /* EQ3_B3_C - [15:0] */
+
+/*
+ * R3656 (0xE48) - EQ3_13
+ */
+#define WM5100_EQ3_B3_PG_MASK 0xFFFF /* EQ3_B3_PG - [15:0] */
+#define WM5100_EQ3_B3_PG_SHIFT 0 /* EQ3_B3_PG - [15:0] */
+#define WM5100_EQ3_B3_PG_WIDTH 16 /* EQ3_B3_PG - [15:0] */
+
+/*
+ * R3657 (0xE49) - EQ3_14
+ */
+#define WM5100_EQ3_B4_A_MASK 0xFFFF /* EQ3_B4_A - [15:0] */
+#define WM5100_EQ3_B4_A_SHIFT 0 /* EQ3_B4_A - [15:0] */
+#define WM5100_EQ3_B4_A_WIDTH 16 /* EQ3_B4_A - [15:0] */
+
+/*
+ * R3658 (0xE4A) - EQ3_15
+ */
+#define WM5100_EQ3_B4_B_MASK 0xFFFF /* EQ3_B4_B - [15:0] */
+#define WM5100_EQ3_B4_B_SHIFT 0 /* EQ3_B4_B - [15:0] */
+#define WM5100_EQ3_B4_B_WIDTH 16 /* EQ3_B4_B - [15:0] */
+
+/*
+ * R3659 (0xE4B) - EQ3_16
+ */
+#define WM5100_EQ3_B4_C_MASK 0xFFFF /* EQ3_B4_C - [15:0] */
+#define WM5100_EQ3_B4_C_SHIFT 0 /* EQ3_B4_C - [15:0] */
+#define WM5100_EQ3_B4_C_WIDTH 16 /* EQ3_B4_C - [15:0] */
+
+/*
+ * R3660 (0xE4C) - EQ3_17
+ */
+#define WM5100_EQ3_B4_PG_MASK 0xFFFF /* EQ3_B4_PG - [15:0] */
+#define WM5100_EQ3_B4_PG_SHIFT 0 /* EQ3_B4_PG - [15:0] */
+#define WM5100_EQ3_B4_PG_WIDTH 16 /* EQ3_B4_PG - [15:0] */
+
+/*
+ * R3661 (0xE4D) - EQ3_18
+ */
+#define WM5100_EQ3_B5_A_MASK 0xFFFF /* EQ3_B5_A - [15:0] */
+#define WM5100_EQ3_B5_A_SHIFT 0 /* EQ3_B5_A - [15:0] */
+#define WM5100_EQ3_B5_A_WIDTH 16 /* EQ3_B5_A - [15:0] */
+
+/*
+ * R3662 (0xE4E) - EQ3_19
+ */
+#define WM5100_EQ3_B5_B_MASK 0xFFFF /* EQ3_B5_B - [15:0] */
+#define WM5100_EQ3_B5_B_SHIFT 0 /* EQ3_B5_B - [15:0] */
+#define WM5100_EQ3_B5_B_WIDTH 16 /* EQ3_B5_B - [15:0] */
+
+/*
+ * R3663 (0xE4F) - EQ3_20
+ */
+#define WM5100_EQ3_B5_PG_MASK 0xFFFF /* EQ3_B5_PG - [15:0] */
+#define WM5100_EQ3_B5_PG_SHIFT 0 /* EQ3_B5_PG - [15:0] */
+#define WM5100_EQ3_B5_PG_WIDTH 16 /* EQ3_B5_PG - [15:0] */
+
+/*
+ * R3666 (0xE52) - EQ4_1
+ */
+#define WM5100_EQ4_B1_GAIN_MASK 0xF800 /* EQ4_B1_GAIN - [15:11] */
+#define WM5100_EQ4_B1_GAIN_SHIFT 11 /* EQ4_B1_GAIN - [15:11] */
+#define WM5100_EQ4_B1_GAIN_WIDTH 5 /* EQ4_B1_GAIN - [15:11] */
+#define WM5100_EQ4_B2_GAIN_MASK 0x07C0 /* EQ4_B2_GAIN - [10:6] */
+#define WM5100_EQ4_B2_GAIN_SHIFT 6 /* EQ4_B2_GAIN - [10:6] */
+#define WM5100_EQ4_B2_GAIN_WIDTH 5 /* EQ4_B2_GAIN - [10:6] */
+#define WM5100_EQ4_B3_GAIN_MASK 0x003E /* EQ4_B3_GAIN - [5:1] */
+#define WM5100_EQ4_B3_GAIN_SHIFT 1 /* EQ4_B3_GAIN - [5:1] */
+#define WM5100_EQ4_B3_GAIN_WIDTH 5 /* EQ4_B3_GAIN - [5:1] */
+#define WM5100_EQ4_ENA 0x0001 /* EQ4_ENA */
+#define WM5100_EQ4_ENA_MASK 0x0001 /* EQ4_ENA */
+#define WM5100_EQ4_ENA_SHIFT 0 /* EQ4_ENA */
+#define WM5100_EQ4_ENA_WIDTH 1 /* EQ4_ENA */
+
+/*
+ * R3667 (0xE53) - EQ4_2
+ */
+#define WM5100_EQ4_B4_GAIN_MASK 0xF800 /* EQ4_B4_GAIN - [15:11] */
+#define WM5100_EQ4_B4_GAIN_SHIFT 11 /* EQ4_B4_GAIN - [15:11] */
+#define WM5100_EQ4_B4_GAIN_WIDTH 5 /* EQ4_B4_GAIN - [15:11] */
+#define WM5100_EQ4_B5_GAIN_MASK 0x07C0 /* EQ4_B5_GAIN - [10:6] */
+#define WM5100_EQ4_B5_GAIN_SHIFT 6 /* EQ4_B5_GAIN - [10:6] */
+#define WM5100_EQ4_B5_GAIN_WIDTH 5 /* EQ4_B5_GAIN - [10:6] */
+
+/*
+ * R3668 (0xE54) - EQ4_3
+ */
+#define WM5100_EQ4_B1_A_MASK 0xFFFF /* EQ4_B1_A - [15:0] */
+#define WM5100_EQ4_B1_A_SHIFT 0 /* EQ4_B1_A - [15:0] */
+#define WM5100_EQ4_B1_A_WIDTH 16 /* EQ4_B1_A - [15:0] */
+
+/*
+ * R3669 (0xE55) - EQ4_4
+ */
+#define WM5100_EQ4_B1_B_MASK 0xFFFF /* EQ4_B1_B - [15:0] */
+#define WM5100_EQ4_B1_B_SHIFT 0 /* EQ4_B1_B - [15:0] */
+#define WM5100_EQ4_B1_B_WIDTH 16 /* EQ4_B1_B - [15:0] */
+
+/*
+ * R3670 (0xE56) - EQ4_5
+ */
+#define WM5100_EQ4_B1_PG_MASK 0xFFFF /* EQ4_B1_PG - [15:0] */
+#define WM5100_EQ4_B1_PG_SHIFT 0 /* EQ4_B1_PG - [15:0] */
+#define WM5100_EQ4_B1_PG_WIDTH 16 /* EQ4_B1_PG - [15:0] */
+
+/*
+ * R3671 (0xE57) - EQ4_6
+ */
+#define WM5100_EQ4_B2_A_MASK 0xFFFF /* EQ4_B2_A - [15:0] */
+#define WM5100_EQ4_B2_A_SHIFT 0 /* EQ4_B2_A - [15:0] */
+#define WM5100_EQ4_B2_A_WIDTH 16 /* EQ4_B2_A - [15:0] */
+
+/*
+ * R3672 (0xE58) - EQ4_7
+ */
+#define WM5100_EQ4_B2_B_MASK 0xFFFF /* EQ4_B2_B - [15:0] */
+#define WM5100_EQ4_B2_B_SHIFT 0 /* EQ4_B2_B - [15:0] */
+#define WM5100_EQ4_B2_B_WIDTH 16 /* EQ4_B2_B - [15:0] */
+
+/*
+ * R3673 (0xE59) - EQ4_8
+ */
+#define WM5100_EQ4_B2_C_MASK 0xFFFF /* EQ4_B2_C - [15:0] */
+#define WM5100_EQ4_B2_C_SHIFT 0 /* EQ4_B2_C - [15:0] */
+#define WM5100_EQ4_B2_C_WIDTH 16 /* EQ4_B2_C - [15:0] */
+
+/*
+ * R3674 (0xE5A) - EQ4_9
+ */
+#define WM5100_EQ4_B2_PG_MASK 0xFFFF /* EQ4_B2_PG - [15:0] */
+#define WM5100_EQ4_B2_PG_SHIFT 0 /* EQ4_B2_PG - [15:0] */
+#define WM5100_EQ4_B2_PG_WIDTH 16 /* EQ4_B2_PG - [15:0] */
+
+/*
+ * R3675 (0xE5B) - EQ4_10
+ */
+#define WM5100_EQ4_B3_A_MASK 0xFFFF /* EQ4_B3_A - [15:0] */
+#define WM5100_EQ4_B3_A_SHIFT 0 /* EQ4_B3_A - [15:0] */
+#define WM5100_EQ4_B3_A_WIDTH 16 /* EQ4_B3_A - [15:0] */
+
+/*
+ * R3676 (0xE5C) - EQ4_11
+ */
+#define WM5100_EQ4_B3_B_MASK 0xFFFF /* EQ4_B3_B - [15:0] */
+#define WM5100_EQ4_B3_B_SHIFT 0 /* EQ4_B3_B - [15:0] */
+#define WM5100_EQ4_B3_B_WIDTH 16 /* EQ4_B3_B - [15:0] */
+
+/*
+ * R3677 (0xE5D) - EQ4_12
+ */
+#define WM5100_EQ4_B3_C_MASK 0xFFFF /* EQ4_B3_C - [15:0] */
+#define WM5100_EQ4_B3_C_SHIFT 0 /* EQ4_B3_C - [15:0] */
+#define WM5100_EQ4_B3_C_WIDTH 16 /* EQ4_B3_C - [15:0] */
+
+/*
+ * R3678 (0xE5E) - EQ4_13
+ */
+#define WM5100_EQ4_B3_PG_MASK 0xFFFF /* EQ4_B3_PG - [15:0] */
+#define WM5100_EQ4_B3_PG_SHIFT 0 /* EQ4_B3_PG - [15:0] */
+#define WM5100_EQ4_B3_PG_WIDTH 16 /* EQ4_B3_PG - [15:0] */
+
+/*
+ * R3679 (0xE5F) - EQ4_14
+ */
+#define WM5100_EQ4_B4_A_MASK 0xFFFF /* EQ4_B4_A - [15:0] */
+#define WM5100_EQ4_B4_A_SHIFT 0 /* EQ4_B4_A - [15:0] */
+#define WM5100_EQ4_B4_A_WIDTH 16 /* EQ4_B4_A - [15:0] */
+
+/*
+ * R3680 (0xE60) - EQ4_15
+ */
+#define WM5100_EQ4_B4_B_MASK 0xFFFF /* EQ4_B4_B - [15:0] */
+#define WM5100_EQ4_B4_B_SHIFT 0 /* EQ4_B4_B - [15:0] */
+#define WM5100_EQ4_B4_B_WIDTH 16 /* EQ4_B4_B - [15:0] */
+
+/*
+ * R3681 (0xE61) - EQ4_16
+ */
+#define WM5100_EQ4_B4_C_MASK 0xFFFF /* EQ4_B4_C - [15:0] */
+#define WM5100_EQ4_B4_C_SHIFT 0 /* EQ4_B4_C - [15:0] */
+#define WM5100_EQ4_B4_C_WIDTH 16 /* EQ4_B4_C - [15:0] */
+
+/*
+ * R3682 (0xE62) - EQ4_17
+ */
+#define WM5100_EQ4_B4_PG_MASK 0xFFFF /* EQ4_B4_PG - [15:0] */
+#define WM5100_EQ4_B4_PG_SHIFT 0 /* EQ4_B4_PG - [15:0] */
+#define WM5100_EQ4_B4_PG_WIDTH 16 /* EQ4_B4_PG - [15:0] */
+
+/*
+ * R3683 (0xE63) - EQ4_18
+ */
+#define WM5100_EQ4_B5_A_MASK 0xFFFF /* EQ4_B5_A - [15:0] */
+#define WM5100_EQ4_B5_A_SHIFT 0 /* EQ4_B5_A - [15:0] */
+#define WM5100_EQ4_B5_A_WIDTH 16 /* EQ4_B5_A - [15:0] */
+
+/*
+ * R3684 (0xE64) - EQ4_19
+ */
+#define WM5100_EQ4_B5_B_MASK 0xFFFF /* EQ4_B5_B - [15:0] */
+#define WM5100_EQ4_B5_B_SHIFT 0 /* EQ4_B5_B - [15:0] */
+#define WM5100_EQ4_B5_B_WIDTH 16 /* EQ4_B5_B - [15:0] */
+
+/*
+ * R3685 (0xE65) - EQ4_20
+ */
+#define WM5100_EQ4_B5_PG_MASK 0xFFFF /* EQ4_B5_PG - [15:0] */
+#define WM5100_EQ4_B5_PG_SHIFT 0 /* EQ4_B5_PG - [15:0] */
+#define WM5100_EQ4_B5_PG_WIDTH 16 /* EQ4_B5_PG - [15:0] */
+
+/*
+ * R3712 (0xE80) - DRC1 ctrl1
+ */
+#define WM5100_DRC_SIG_DET_RMS_MASK 0xF800 /* DRC_SIG_DET_RMS - [15:11] */
+#define WM5100_DRC_SIG_DET_RMS_SHIFT 11 /* DRC_SIG_DET_RMS - [15:11] */
+#define WM5100_DRC_SIG_DET_RMS_WIDTH 5 /* DRC_SIG_DET_RMS - [15:11] */
+#define WM5100_DRC_SIG_DET_PK_MASK 0x0600 /* DRC_SIG_DET_PK - [10:9] */
+#define WM5100_DRC_SIG_DET_PK_SHIFT 9 /* DRC_SIG_DET_PK - [10:9] */
+#define WM5100_DRC_SIG_DET_PK_WIDTH 2 /* DRC_SIG_DET_PK - [10:9] */
+#define WM5100_DRC_NG_ENA 0x0100 /* DRC_NG_ENA */
+#define WM5100_DRC_NG_ENA_MASK 0x0100 /* DRC_NG_ENA */
+#define WM5100_DRC_NG_ENA_SHIFT 8 /* DRC_NG_ENA */
+#define WM5100_DRC_NG_ENA_WIDTH 1 /* DRC_NG_ENA */
+#define WM5100_DRC_SIG_DET_MODE 0x0080 /* DRC_SIG_DET_MODE */
+#define WM5100_DRC_SIG_DET_MODE_MASK 0x0080 /* DRC_SIG_DET_MODE */
+#define WM5100_DRC_SIG_DET_MODE_SHIFT 7 /* DRC_SIG_DET_MODE */
+#define WM5100_DRC_SIG_DET_MODE_WIDTH 1 /* DRC_SIG_DET_MODE */
+#define WM5100_DRC_SIG_DET 0x0040 /* DRC_SIG_DET */
+#define WM5100_DRC_SIG_DET_MASK 0x0040 /* DRC_SIG_DET */
+#define WM5100_DRC_SIG_DET_SHIFT 6 /* DRC_SIG_DET */
+#define WM5100_DRC_SIG_DET_WIDTH 1 /* DRC_SIG_DET */
+#define WM5100_DRC_KNEE2_OP_ENA 0x0020 /* DRC_KNEE2_OP_ENA */
+#define WM5100_DRC_KNEE2_OP_ENA_MASK 0x0020 /* DRC_KNEE2_OP_ENA */
+#define WM5100_DRC_KNEE2_OP_ENA_SHIFT 5 /* DRC_KNEE2_OP_ENA */
+#define WM5100_DRC_KNEE2_OP_ENA_WIDTH 1 /* DRC_KNEE2_OP_ENA */
+#define WM5100_DRC_QR 0x0010 /* DRC_QR */
+#define WM5100_DRC_QR_MASK 0x0010 /* DRC_QR */
+#define WM5100_DRC_QR_SHIFT 4 /* DRC_QR */
+#define WM5100_DRC_QR_WIDTH 1 /* DRC_QR */
+#define WM5100_DRC_ANTICLIP 0x0008 /* DRC_ANTICLIP */
+#define WM5100_DRC_ANTICLIP_MASK 0x0008 /* DRC_ANTICLIP */
+#define WM5100_DRC_ANTICLIP_SHIFT 3 /* DRC_ANTICLIP */
+#define WM5100_DRC_ANTICLIP_WIDTH 1 /* DRC_ANTICLIP */
+#define WM5100_DRCL_ENA 0x0002 /* DRCL_ENA */
+#define WM5100_DRCL_ENA_MASK 0x0002 /* DRCL_ENA */
+#define WM5100_DRCL_ENA_SHIFT 1 /* DRCL_ENA */
+#define WM5100_DRCL_ENA_WIDTH 1 /* DRCL_ENA */
+#define WM5100_DRCR_ENA 0x0001 /* DRCR_ENA */
+#define WM5100_DRCR_ENA_MASK 0x0001 /* DRCR_ENA */
+#define WM5100_DRCR_ENA_SHIFT 0 /* DRCR_ENA */
+#define WM5100_DRCR_ENA_WIDTH 1 /* DRCR_ENA */
+
+/*
+ * R3713 (0xE81) - DRC1 ctrl2
+ */
+#define WM5100_DRC_ATK_MASK 0x1E00 /* DRC_ATK - [12:9] */
+#define WM5100_DRC_ATK_SHIFT 9 /* DRC_ATK - [12:9] */
+#define WM5100_DRC_ATK_WIDTH 4 /* DRC_ATK - [12:9] */
+#define WM5100_DRC_DCY_MASK 0x01E0 /* DRC_DCY - [8:5] */
+#define WM5100_DRC_DCY_SHIFT 5 /* DRC_DCY - [8:5] */
+#define WM5100_DRC_DCY_WIDTH 4 /* DRC_DCY - [8:5] */
+#define WM5100_DRC_MINGAIN_MASK 0x001C /* DRC_MINGAIN - [4:2] */
+#define WM5100_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [4:2] */
+#define WM5100_DRC_MINGAIN_WIDTH 3 /* DRC_MINGAIN - [4:2] */
+#define WM5100_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */
+#define WM5100_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */
+#define WM5100_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R3714 (0xE82) - DRC1 ctrl3
+ */
+#define WM5100_DRC_NG_MINGAIN_MASK 0xF000 /* DRC_NG_MINGAIN - [15:12] */
+#define WM5100_DRC_NG_MINGAIN_SHIFT 12 /* DRC_NG_MINGAIN - [15:12] */
+#define WM5100_DRC_NG_MINGAIN_WIDTH 4 /* DRC_NG_MINGAIN - [15:12] */
+#define WM5100_DRC_NG_EXP_MASK 0x0C00 /* DRC_NG_EXP - [11:10] */
+#define WM5100_DRC_NG_EXP_SHIFT 10 /* DRC_NG_EXP - [11:10] */
+#define WM5100_DRC_NG_EXP_WIDTH 2 /* DRC_NG_EXP - [11:10] */
+#define WM5100_DRC_QR_THR_MASK 0x0300 /* DRC_QR_THR - [9:8] */
+#define WM5100_DRC_QR_THR_SHIFT 8 /* DRC_QR_THR - [9:8] */
+#define WM5100_DRC_QR_THR_WIDTH 2 /* DRC_QR_THR - [9:8] */
+#define WM5100_DRC_QR_DCY_MASK 0x00C0 /* DRC_QR_DCY - [7:6] */
+#define WM5100_DRC_QR_DCY_SHIFT 6 /* DRC_QR_DCY - [7:6] */
+#define WM5100_DRC_QR_DCY_WIDTH 2 /* DRC_QR_DCY - [7:6] */
+#define WM5100_DRC_HI_COMP_MASK 0x0038 /* DRC_HI_COMP - [5:3] */
+#define WM5100_DRC_HI_COMP_SHIFT 3 /* DRC_HI_COMP - [5:3] */
+#define WM5100_DRC_HI_COMP_WIDTH 3 /* DRC_HI_COMP - [5:3] */
+#define WM5100_DRC_LO_COMP_MASK 0x0007 /* DRC_LO_COMP - [2:0] */
+#define WM5100_DRC_LO_COMP_SHIFT 0 /* DRC_LO_COMP - [2:0] */
+#define WM5100_DRC_LO_COMP_WIDTH 3 /* DRC_LO_COMP - [2:0] */
+
+/*
+ * R3715 (0xE83) - DRC1 ctrl4
+ */
+#define WM5100_DRC_KNEE_IP_MASK 0x07E0 /* DRC_KNEE_IP - [10:5] */
+#define WM5100_DRC_KNEE_IP_SHIFT 5 /* DRC_KNEE_IP - [10:5] */
+#define WM5100_DRC_KNEE_IP_WIDTH 6 /* DRC_KNEE_IP - [10:5] */
+#define WM5100_DRC_KNEE_OP_MASK 0x001F /* DRC_KNEE_OP - [4:0] */
+#define WM5100_DRC_KNEE_OP_SHIFT 0 /* DRC_KNEE_OP - [4:0] */
+#define WM5100_DRC_KNEE_OP_WIDTH 5 /* DRC_KNEE_OP - [4:0] */
+
+/*
+ * R3716 (0xE84) - DRC1 ctrl5
+ */
+#define WM5100_DRC_KNEE2_IP_MASK 0x03E0 /* DRC_KNEE2_IP - [9:5] */
+#define WM5100_DRC_KNEE2_IP_SHIFT 5 /* DRC_KNEE2_IP - [9:5] */
+#define WM5100_DRC_KNEE2_IP_WIDTH 5 /* DRC_KNEE2_IP - [9:5] */
+#define WM5100_DRC_KNEE2_OP_MASK 0x001F /* DRC_KNEE2_OP - [4:0] */
+#define WM5100_DRC_KNEE2_OP_SHIFT 0 /* DRC_KNEE2_OP - [4:0] */
+#define WM5100_DRC_KNEE2_OP_WIDTH 5 /* DRC_KNEE2_OP - [4:0] */
+
+/*
+ * R3776 (0xEC0) - HPLPF1_1
+ */
+#define WM5100_LHPF1_MODE 0x0002 /* LHPF1_MODE */
+#define WM5100_LHPF1_MODE_MASK 0x0002 /* LHPF1_MODE */
+#define WM5100_LHPF1_MODE_SHIFT 1 /* LHPF1_MODE */
+#define WM5100_LHPF1_MODE_WIDTH 1 /* LHPF1_MODE */
+#define WM5100_LHPF1_ENA 0x0001 /* LHPF1_ENA */
+#define WM5100_LHPF1_ENA_MASK 0x0001 /* LHPF1_ENA */
+#define WM5100_LHPF1_ENA_SHIFT 0 /* LHPF1_ENA */
+#define WM5100_LHPF1_ENA_WIDTH 1 /* LHPF1_ENA */
+
+/*
+ * R3777 (0xEC1) - HPLPF1_2
+ */
+#define WM5100_LHPF1_COEFF_MASK 0xFFFF /* LHPF1_COEFF - [15:0] */
+#define WM5100_LHPF1_COEFF_SHIFT 0 /* LHPF1_COEFF - [15:0] */
+#define WM5100_LHPF1_COEFF_WIDTH 16 /* LHPF1_COEFF - [15:0] */
+
+/*
+ * R3780 (0xEC4) - HPLPF2_1
+ */
+#define WM5100_LHPF2_MODE 0x0002 /* LHPF2_MODE */
+#define WM5100_LHPF2_MODE_MASK 0x0002 /* LHPF2_MODE */
+#define WM5100_LHPF2_MODE_SHIFT 1 /* LHPF2_MODE */
+#define WM5100_LHPF2_MODE_WIDTH 1 /* LHPF2_MODE */
+#define WM5100_LHPF2_ENA 0x0001 /* LHPF2_ENA */
+#define WM5100_LHPF2_ENA_MASK 0x0001 /* LHPF2_ENA */
+#define WM5100_LHPF2_ENA_SHIFT 0 /* LHPF2_ENA */
+#define WM5100_LHPF2_ENA_WIDTH 1 /* LHPF2_ENA */
+
+/*
+ * R3781 (0xEC5) - HPLPF2_2
+ */
+#define WM5100_LHPF2_COEFF_MASK 0xFFFF /* LHPF2_COEFF - [15:0] */
+#define WM5100_LHPF2_COEFF_SHIFT 0 /* LHPF2_COEFF - [15:0] */
+#define WM5100_LHPF2_COEFF_WIDTH 16 /* LHPF2_COEFF - [15:0] */
+
+/*
+ * R3784 (0xEC8) - HPLPF3_1
+ */
+#define WM5100_LHPF3_MODE 0x0002 /* LHPF3_MODE */
+#define WM5100_LHPF3_MODE_MASK 0x0002 /* LHPF3_MODE */
+#define WM5100_LHPF3_MODE_SHIFT 1 /* LHPF3_MODE */
+#define WM5100_LHPF3_MODE_WIDTH 1 /* LHPF3_MODE */
+#define WM5100_LHPF3_ENA 0x0001 /* LHPF3_ENA */
+#define WM5100_LHPF3_ENA_MASK 0x0001 /* LHPF3_ENA */
+#define WM5100_LHPF3_ENA_SHIFT 0 /* LHPF3_ENA */
+#define WM5100_LHPF3_ENA_WIDTH 1 /* LHPF3_ENA */
+
+/*
+ * R3785 (0xEC9) - HPLPF3_2
+ */
+#define WM5100_LHPF3_COEFF_MASK 0xFFFF /* LHPF3_COEFF - [15:0] */
+#define WM5100_LHPF3_COEFF_SHIFT 0 /* LHPF3_COEFF - [15:0] */
+#define WM5100_LHPF3_COEFF_WIDTH 16 /* LHPF3_COEFF - [15:0] */
+
+/*
+ * R3788 (0xECC) - HPLPF4_1
+ */
+#define WM5100_LHPF4_MODE 0x0002 /* LHPF4_MODE */
+#define WM5100_LHPF4_MODE_MASK 0x0002 /* LHPF4_MODE */
+#define WM5100_LHPF4_MODE_SHIFT 1 /* LHPF4_MODE */
+#define WM5100_LHPF4_MODE_WIDTH 1 /* LHPF4_MODE */
+#define WM5100_LHPF4_ENA 0x0001 /* LHPF4_ENA */
+#define WM5100_LHPF4_ENA_MASK 0x0001 /* LHPF4_ENA */
+#define WM5100_LHPF4_ENA_SHIFT 0 /* LHPF4_ENA */
+#define WM5100_LHPF4_ENA_WIDTH 1 /* LHPF4_ENA */
+
+/*
+ * R3789 (0xECD) - HPLPF4_2
+ */
+#define WM5100_LHPF4_COEFF_MASK 0xFFFF /* LHPF4_COEFF - [15:0] */
+#define WM5100_LHPF4_COEFF_SHIFT 0 /* LHPF4_COEFF - [15:0] */
+#define WM5100_LHPF4_COEFF_WIDTH 16 /* LHPF4_COEFF - [15:0] */
+
+/*
+ * R4132 (0x1024) - DSP2 Control 30
+ */
+#define WM5100_DSP2_RATE_MASK 0xC000 /* DSP2_RATE - [15:14] */
+#define WM5100_DSP2_RATE_SHIFT 14 /* DSP2_RATE - [15:14] */
+#define WM5100_DSP2_RATE_WIDTH 2 /* DSP2_RATE - [15:14] */
+#define WM5100_DSP2_DBG_CLK_ENA 0x0008 /* DSP2_DBG_CLK_ENA */
+#define WM5100_DSP2_DBG_CLK_ENA_MASK 0x0008 /* DSP2_DBG_CLK_ENA */
+#define WM5100_DSP2_DBG_CLK_ENA_SHIFT 3 /* DSP2_DBG_CLK_ENA */
+#define WM5100_DSP2_DBG_CLK_ENA_WIDTH 1 /* DSP2_DBG_CLK_ENA */
+#define WM5100_DSP2_SYS_ENA 0x0004 /* DSP2_SYS_ENA */
+#define WM5100_DSP2_SYS_ENA_MASK 0x0004 /* DSP2_SYS_ENA */
+#define WM5100_DSP2_SYS_ENA_SHIFT 2 /* DSP2_SYS_ENA */
+#define WM5100_DSP2_SYS_ENA_WIDTH 1 /* DSP2_SYS_ENA */
+#define WM5100_DSP2_CORE_ENA 0x0002 /* DSP2_CORE_ENA */
+#define WM5100_DSP2_CORE_ENA_MASK 0x0002 /* DSP2_CORE_ENA */
+#define WM5100_DSP2_CORE_ENA_SHIFT 1 /* DSP2_CORE_ENA */
+#define WM5100_DSP2_CORE_ENA_WIDTH 1 /* DSP2_CORE_ENA */
+#define WM5100_DSP2_START 0x0001 /* DSP2_START */
+#define WM5100_DSP2_START_MASK 0x0001 /* DSP2_START */
+#define WM5100_DSP2_START_SHIFT 0 /* DSP2_START */
+#define WM5100_DSP2_START_WIDTH 1 /* DSP2_START */
+
+/*
+ * R3876 (0xF24) - DSP1 Control 30
+ */
+#define WM5100_DSP1_RATE_MASK 0xC000 /* DSP1_RATE - [15:14] */
+#define WM5100_DSP1_RATE_SHIFT 14 /* DSP1_RATE - [15:14] */
+#define WM5100_DSP1_RATE_WIDTH 2 /* DSP1_RATE - [15:14] */
+#define WM5100_DSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
+#define WM5100_DSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
+#define WM5100_DSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
+#define WM5100_DSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
+#define WM5100_DSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
+#define WM5100_DSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
+#define WM5100_DSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
+#define WM5100_DSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
+#define WM5100_DSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
+#define WM5100_DSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
+#define WM5100_DSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
+#define WM5100_DSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
+#define WM5100_DSP1_START 0x0001 /* DSP1_START */
+#define WM5100_DSP1_START_MASK 0x0001 /* DSP1_START */
+#define WM5100_DSP1_START_SHIFT 0 /* DSP1_START */
+#define WM5100_DSP1_START_WIDTH 1 /* DSP1_START */
+
+/*
+ * R4388 (0x1124) - DSP3 Control 30
+ */
+#define WM5100_DSP3_RATE_MASK 0xC000 /* DSP3_RATE - [15:14] */
+#define WM5100_DSP3_RATE_SHIFT 14 /* DSP3_RATE - [15:14] */
+#define WM5100_DSP3_RATE_WIDTH 2 /* DSP3_RATE - [15:14] */
+#define WM5100_DSP3_DBG_CLK_ENA 0x0008 /* DSP3_DBG_CLK_ENA */
+#define WM5100_DSP3_DBG_CLK_ENA_MASK 0x0008 /* DSP3_DBG_CLK_ENA */
+#define WM5100_DSP3_DBG_CLK_ENA_SHIFT 3 /* DSP3_DBG_CLK_ENA */
+#define WM5100_DSP3_DBG_CLK_ENA_WIDTH 1 /* DSP3_DBG_CLK_ENA */
+#define WM5100_DSP3_SYS_ENA 0x0004 /* DSP3_SYS_ENA */
+#define WM5100_DSP3_SYS_ENA_MASK 0x0004 /* DSP3_SYS_ENA */
+#define WM5100_DSP3_SYS_ENA_SHIFT 2 /* DSP3_SYS_ENA */
+#define WM5100_DSP3_SYS_ENA_WIDTH 1 /* DSP3_SYS_ENA */
+#define WM5100_DSP3_CORE_ENA 0x0002 /* DSP3_CORE_ENA */
+#define WM5100_DSP3_CORE_ENA_MASK 0x0002 /* DSP3_CORE_ENA */
+#define WM5100_DSP3_CORE_ENA_SHIFT 1 /* DSP3_CORE_ENA */
+#define WM5100_DSP3_CORE_ENA_WIDTH 1 /* DSP3_CORE_ENA */
+#define WM5100_DSP3_START 0x0001 /* DSP3_START */
+#define WM5100_DSP3_START_MASK 0x0001 /* DSP3_START */
+#define WM5100_DSP3_START_SHIFT 0 /* DSP3_START */
+#define WM5100_DSP3_START_WIDTH 1 /* DSP3_START */
+
+/*
+ * R16384 (0x4000) - DSP1 DM 0
+ */
+#define WM5100_DSP1_DM_START_1_MASK 0x00FF /* DSP1_DM_START - [7:0] */
+#define WM5100_DSP1_DM_START_1_SHIFT 0 /* DSP1_DM_START - [7:0] */
+#define WM5100_DSP1_DM_START_1_WIDTH 8 /* DSP1_DM_START - [7:0] */
+
+/*
+ * R16385 (0x4001) - DSP1 DM 1
+ */
+#define WM5100_DSP1_DM_START_MASK 0xFFFF /* DSP1_DM_START - [15:0] */
+#define WM5100_DSP1_DM_START_SHIFT 0 /* DSP1_DM_START - [15:0] */
+#define WM5100_DSP1_DM_START_WIDTH 16 /* DSP1_DM_START - [15:0] */
+
+/*
+ * R16386 (0x4002) - DSP1 DM 2
+ */
+#define WM5100_DSP1_DM_1_1_MASK 0x00FF /* DSP1_DM_1 - [7:0] */
+#define WM5100_DSP1_DM_1_1_SHIFT 0 /* DSP1_DM_1 - [7:0] */
+#define WM5100_DSP1_DM_1_1_WIDTH 8 /* DSP1_DM_1 - [7:0] */
+
+/*
+ * R16387 (0x4003) - DSP1 DM 3
+ */
+#define WM5100_DSP1_DM_1_MASK 0xFFFF /* DSP1_DM_1 - [15:0] */
+#define WM5100_DSP1_DM_1_SHIFT 0 /* DSP1_DM_1 - [15:0] */
+#define WM5100_DSP1_DM_1_WIDTH 16 /* DSP1_DM_1 - [15:0] */
+
+/*
+ * R16892 (0x41FC) - DSP1 DM 508
+ */
+#define WM5100_DSP1_DM_254_1_MASK 0x00FF /* DSP1_DM_254 - [7:0] */
+#define WM5100_DSP1_DM_254_1_SHIFT 0 /* DSP1_DM_254 - [7:0] */
+#define WM5100_DSP1_DM_254_1_WIDTH 8 /* DSP1_DM_254 - [7:0] */
+
+/*
+ * R16893 (0x41FD) - DSP1 DM 509
+ */
+#define WM5100_DSP1_DM_254_MASK 0xFFFF /* DSP1_DM_254 - [15:0] */
+#define WM5100_DSP1_DM_254_SHIFT 0 /* DSP1_DM_254 - [15:0] */
+#define WM5100_DSP1_DM_254_WIDTH 16 /* DSP1_DM_254 - [15:0] */
+
+/*
+ * R16894 (0x41FE) - DSP1 DM 510
+ */
+#define WM5100_DSP1_DM_END_1_MASK 0x00FF /* DSP1_DM_END - [7:0] */
+#define WM5100_DSP1_DM_END_1_SHIFT 0 /* DSP1_DM_END - [7:0] */
+#define WM5100_DSP1_DM_END_1_WIDTH 8 /* DSP1_DM_END - [7:0] */
+
+/*
+ * R16895 (0x41FF) - DSP1 DM 511
+ */
+#define WM5100_DSP1_DM_END_MASK 0xFFFF /* DSP1_DM_END - [15:0] */
+#define WM5100_DSP1_DM_END_SHIFT 0 /* DSP1_DM_END - [15:0] */
+#define WM5100_DSP1_DM_END_WIDTH 16 /* DSP1_DM_END - [15:0] */
+
+/*
+ * R18432 (0x4800) - DSP1 PM 0
+ */
+#define WM5100_DSP1_PM_START_2_MASK 0x00FF /* DSP1_PM_START - [7:0] */
+#define WM5100_DSP1_PM_START_2_SHIFT 0 /* DSP1_PM_START - [7:0] */
+#define WM5100_DSP1_PM_START_2_WIDTH 8 /* DSP1_PM_START - [7:0] */
+
+/*
+ * R18433 (0x4801) - DSP1 PM 1
+ */
+#define WM5100_DSP1_PM_START_1_MASK 0xFFFF /* DSP1_PM_START - [15:0] */
+#define WM5100_DSP1_PM_START_1_SHIFT 0 /* DSP1_PM_START - [15:0] */
+#define WM5100_DSP1_PM_START_1_WIDTH 16 /* DSP1_PM_START - [15:0] */
+
+/*
+ * R18434 (0x4802) - DSP1 PM 2
+ */
+#define WM5100_DSP1_PM_START_MASK 0xFFFF /* DSP1_PM_START - [15:0] */
+#define WM5100_DSP1_PM_START_SHIFT 0 /* DSP1_PM_START - [15:0] */
+#define WM5100_DSP1_PM_START_WIDTH 16 /* DSP1_PM_START - [15:0] */
+
+/*
+ * R18435 (0x4803) - DSP1 PM 3
+ */
+#define WM5100_DSP1_PM_1_2_MASK 0x00FF /* DSP1_PM_1 - [7:0] */
+#define WM5100_DSP1_PM_1_2_SHIFT 0 /* DSP1_PM_1 - [7:0] */
+#define WM5100_DSP1_PM_1_2_WIDTH 8 /* DSP1_PM_1 - [7:0] */
+
+/*
+ * R18436 (0x4804) - DSP1 PM 4
+ */
+#define WM5100_DSP1_PM_1_1_MASK 0xFFFF /* DSP1_PM_1 - [15:0] */
+#define WM5100_DSP1_PM_1_1_SHIFT 0 /* DSP1_PM_1 - [15:0] */
+#define WM5100_DSP1_PM_1_1_WIDTH 16 /* DSP1_PM_1 - [15:0] */
+
+/*
+ * R18437 (0x4805) - DSP1 PM 5
+ */
+#define WM5100_DSP1_PM_1_MASK 0xFFFF /* DSP1_PM_1 - [15:0] */
+#define WM5100_DSP1_PM_1_SHIFT 0 /* DSP1_PM_1 - [15:0] */
+#define WM5100_DSP1_PM_1_WIDTH 16 /* DSP1_PM_1 - [15:0] */
+
+/*
+ * R19962 (0x4DFA) - DSP1 PM 1530
+ */
+#define WM5100_DSP1_PM_510_2_MASK 0x00FF /* DSP1_PM_510 - [7:0] */
+#define WM5100_DSP1_PM_510_2_SHIFT 0 /* DSP1_PM_510 - [7:0] */
+#define WM5100_DSP1_PM_510_2_WIDTH 8 /* DSP1_PM_510 - [7:0] */
+
+/*
+ * R19963 (0x4DFB) - DSP1 PM 1531
+ */
+#define WM5100_DSP1_PM_510_1_MASK 0xFFFF /* DSP1_PM_510 - [15:0] */
+#define WM5100_DSP1_PM_510_1_SHIFT 0 /* DSP1_PM_510 - [15:0] */
+#define WM5100_DSP1_PM_510_1_WIDTH 16 /* DSP1_PM_510 - [15:0] */
+
+/*
+ * R19964 (0x4DFC) - DSP1 PM 1532
+ */
+#define WM5100_DSP1_PM_510_MASK 0xFFFF /* DSP1_PM_510 - [15:0] */
+#define WM5100_DSP1_PM_510_SHIFT 0 /* DSP1_PM_510 - [15:0] */
+#define WM5100_DSP1_PM_510_WIDTH 16 /* DSP1_PM_510 - [15:0] */
+
+/*
+ * R19965 (0x4DFD) - DSP1 PM 1533
+ */
+#define WM5100_DSP1_PM_END_2_MASK 0x00FF /* DSP1_PM_END - [7:0] */
+#define WM5100_DSP1_PM_END_2_SHIFT 0 /* DSP1_PM_END - [7:0] */
+#define WM5100_DSP1_PM_END_2_WIDTH 8 /* DSP1_PM_END - [7:0] */
+
+/*
+ * R19966 (0x4DFE) - DSP1 PM 1534
+ */
+#define WM5100_DSP1_PM_END_1_MASK 0xFFFF /* DSP1_PM_END - [15:0] */
+#define WM5100_DSP1_PM_END_1_SHIFT 0 /* DSP1_PM_END - [15:0] */
+#define WM5100_DSP1_PM_END_1_WIDTH 16 /* DSP1_PM_END - [15:0] */
+
+/*
+ * R19967 (0x4DFF) - DSP1 PM 1535
+ */
+#define WM5100_DSP1_PM_END_MASK 0xFFFF /* DSP1_PM_END - [15:0] */
+#define WM5100_DSP1_PM_END_SHIFT 0 /* DSP1_PM_END - [15:0] */
+#define WM5100_DSP1_PM_END_WIDTH 16 /* DSP1_PM_END - [15:0] */
+
+/*
+ * R20480 (0x5000) - DSP1 ZM 0
+ */
+#define WM5100_DSP1_ZM_START_1_MASK 0x00FF /* DSP1_ZM_START - [7:0] */
+#define WM5100_DSP1_ZM_START_1_SHIFT 0 /* DSP1_ZM_START - [7:0] */
+#define WM5100_DSP1_ZM_START_1_WIDTH 8 /* DSP1_ZM_START - [7:0] */
+
+/*
+ * R20481 (0x5001) - DSP1 ZM 1
+ */
+#define WM5100_DSP1_ZM_START_MASK 0xFFFF /* DSP1_ZM_START - [15:0] */
+#define WM5100_DSP1_ZM_START_SHIFT 0 /* DSP1_ZM_START - [15:0] */
+#define WM5100_DSP1_ZM_START_WIDTH 16 /* DSP1_ZM_START - [15:0] */
+
+/*
+ * R20482 (0x5002) - DSP1 ZM 2
+ */
+#define WM5100_DSP1_ZM_1_1_MASK 0x00FF /* DSP1_ZM_1 - [7:0] */
+#define WM5100_DSP1_ZM_1_1_SHIFT 0 /* DSP1_ZM_1 - [7:0] */
+#define WM5100_DSP1_ZM_1_1_WIDTH 8 /* DSP1_ZM_1 - [7:0] */
+
+/*
+ * R20483 (0x5003) - DSP1 ZM 3
+ */
+#define WM5100_DSP1_ZM_1_MASK 0xFFFF /* DSP1_ZM_1 - [15:0] */
+#define WM5100_DSP1_ZM_1_SHIFT 0 /* DSP1_ZM_1 - [15:0] */
+#define WM5100_DSP1_ZM_1_WIDTH 16 /* DSP1_ZM_1 - [15:0] */
+
+/*
+ * R22524 (0x57FC) - DSP1 ZM 2044
+ */
+#define WM5100_DSP1_ZM_1022_1_MASK 0x00FF /* DSP1_ZM_1022 - [7:0] */
+#define WM5100_DSP1_ZM_1022_1_SHIFT 0 /* DSP1_ZM_1022 - [7:0] */
+#define WM5100_DSP1_ZM_1022_1_WIDTH 8 /* DSP1_ZM_1022 - [7:0] */
+
+/*
+ * R22525 (0x57FD) - DSP1 ZM 2045
+ */
+#define WM5100_DSP1_ZM_1022_MASK 0xFFFF /* DSP1_ZM_1022 - [15:0] */
+#define WM5100_DSP1_ZM_1022_SHIFT 0 /* DSP1_ZM_1022 - [15:0] */
+#define WM5100_DSP1_ZM_1022_WIDTH 16 /* DSP1_ZM_1022 - [15:0] */
+
+/*
+ * R22526 (0x57FE) - DSP1 ZM 2046
+ */
+#define WM5100_DSP1_ZM_END_1_MASK 0x00FF /* DSP1_ZM_END - [7:0] */
+#define WM5100_DSP1_ZM_END_1_SHIFT 0 /* DSP1_ZM_END - [7:0] */
+#define WM5100_DSP1_ZM_END_1_WIDTH 8 /* DSP1_ZM_END - [7:0] */
+
+/*
+ * R22527 (0x57FF) - DSP1 ZM 2047
+ */
+#define WM5100_DSP1_ZM_END_MASK 0xFFFF /* DSP1_ZM_END - [15:0] */
+#define WM5100_DSP1_ZM_END_SHIFT 0 /* DSP1_ZM_END - [15:0] */
+#define WM5100_DSP1_ZM_END_WIDTH 16 /* DSP1_ZM_END - [15:0] */
+
+/*
+ * R24576 (0x6000) - DSP2 DM 0
+ */
+#define WM5100_DSP2_DM_START_1_MASK 0x00FF /* DSP2_DM_START - [7:0] */
+#define WM5100_DSP2_DM_START_1_SHIFT 0 /* DSP2_DM_START - [7:0] */
+#define WM5100_DSP2_DM_START_1_WIDTH 8 /* DSP2_DM_START - [7:0] */
+
+/*
+ * R24577 (0x6001) - DSP2 DM 1
+ */
+#define WM5100_DSP2_DM_START_MASK 0xFFFF /* DSP2_DM_START - [15:0] */
+#define WM5100_DSP2_DM_START_SHIFT 0 /* DSP2_DM_START - [15:0] */
+#define WM5100_DSP2_DM_START_WIDTH 16 /* DSP2_DM_START - [15:0] */
+
+/*
+ * R24578 (0x6002) - DSP2 DM 2
+ */
+#define WM5100_DSP2_DM_1_1_MASK 0x00FF /* DSP2_DM_1 - [7:0] */
+#define WM5100_DSP2_DM_1_1_SHIFT 0 /* DSP2_DM_1 - [7:0] */
+#define WM5100_DSP2_DM_1_1_WIDTH 8 /* DSP2_DM_1 - [7:0] */
+
+/*
+ * R24579 (0x6003) - DSP2 DM 3
+ */
+#define WM5100_DSP2_DM_1_MASK 0xFFFF /* DSP2_DM_1 - [15:0] */
+#define WM5100_DSP2_DM_1_SHIFT 0 /* DSP2_DM_1 - [15:0] */
+#define WM5100_DSP2_DM_1_WIDTH 16 /* DSP2_DM_1 - [15:0] */
+
+/*
+ * R25084 (0x61FC) - DSP2 DM 508
+ */
+#define WM5100_DSP2_DM_254_1_MASK 0x00FF /* DSP2_DM_254 - [7:0] */
+#define WM5100_DSP2_DM_254_1_SHIFT 0 /* DSP2_DM_254 - [7:0] */
+#define WM5100_DSP2_DM_254_1_WIDTH 8 /* DSP2_DM_254 - [7:0] */
+
+/*
+ * R25085 (0x61FD) - DSP2 DM 509
+ */
+#define WM5100_DSP2_DM_254_MASK 0xFFFF /* DSP2_DM_254 - [15:0] */
+#define WM5100_DSP2_DM_254_SHIFT 0 /* DSP2_DM_254 - [15:0] */
+#define WM5100_DSP2_DM_254_WIDTH 16 /* DSP2_DM_254 - [15:0] */
+
+/*
+ * R25086 (0x61FE) - DSP2 DM 510
+ */
+#define WM5100_DSP2_DM_END_1_MASK 0x00FF /* DSP2_DM_END - [7:0] */
+#define WM5100_DSP2_DM_END_1_SHIFT 0 /* DSP2_DM_END - [7:0] */
+#define WM5100_DSP2_DM_END_1_WIDTH 8 /* DSP2_DM_END - [7:0] */
+
+/*
+ * R25087 (0x61FF) - DSP2 DM 511
+ */
+#define WM5100_DSP2_DM_END_MASK 0xFFFF /* DSP2_DM_END - [15:0] */
+#define WM5100_DSP2_DM_END_SHIFT 0 /* DSP2_DM_END - [15:0] */
+#define WM5100_DSP2_DM_END_WIDTH 16 /* DSP2_DM_END - [15:0] */
+
+/*
+ * R26624 (0x6800) - DSP2 PM 0
+ */
+#define WM5100_DSP2_PM_START_2_MASK 0x00FF /* DSP2_PM_START - [7:0] */
+#define WM5100_DSP2_PM_START_2_SHIFT 0 /* DSP2_PM_START - [7:0] */
+#define WM5100_DSP2_PM_START_2_WIDTH 8 /* DSP2_PM_START - [7:0] */
+
+/*
+ * R26625 (0x6801) - DSP2 PM 1
+ */
+#define WM5100_DSP2_PM_START_1_MASK 0xFFFF /* DSP2_PM_START - [15:0] */
+#define WM5100_DSP2_PM_START_1_SHIFT 0 /* DSP2_PM_START - [15:0] */
+#define WM5100_DSP2_PM_START_1_WIDTH 16 /* DSP2_PM_START - [15:0] */
+
+/*
+ * R26626 (0x6802) - DSP2 PM 2
+ */
+#define WM5100_DSP2_PM_START_MASK 0xFFFF /* DSP2_PM_START - [15:0] */
+#define WM5100_DSP2_PM_START_SHIFT 0 /* DSP2_PM_START - [15:0] */
+#define WM5100_DSP2_PM_START_WIDTH 16 /* DSP2_PM_START - [15:0] */
+
+/*
+ * R26627 (0x6803) - DSP2 PM 3
+ */
+#define WM5100_DSP2_PM_1_2_MASK 0x00FF /* DSP2_PM_1 - [7:0] */
+#define WM5100_DSP2_PM_1_2_SHIFT 0 /* DSP2_PM_1 - [7:0] */
+#define WM5100_DSP2_PM_1_2_WIDTH 8 /* DSP2_PM_1 - [7:0] */
+
+/*
+ * R26628 (0x6804) - DSP2 PM 4
+ */
+#define WM5100_DSP2_PM_1_1_MASK 0xFFFF /* DSP2_PM_1 - [15:0] */
+#define WM5100_DSP2_PM_1_1_SHIFT 0 /* DSP2_PM_1 - [15:0] */
+#define WM5100_DSP2_PM_1_1_WIDTH 16 /* DSP2_PM_1 - [15:0] */
+
+/*
+ * R26629 (0x6805) - DSP2 PM 5
+ */
+#define WM5100_DSP2_PM_1_MASK 0xFFFF /* DSP2_PM_1 - [15:0] */
+#define WM5100_DSP2_PM_1_SHIFT 0 /* DSP2_PM_1 - [15:0] */
+#define WM5100_DSP2_PM_1_WIDTH 16 /* DSP2_PM_1 - [15:0] */
+
+/*
+ * R28154 (0x6DFA) - DSP2 PM 1530
+ */
+#define WM5100_DSP2_PM_510_2_MASK 0x00FF /* DSP2_PM_510 - [7:0] */
+#define WM5100_DSP2_PM_510_2_SHIFT 0 /* DSP2_PM_510 - [7:0] */
+#define WM5100_DSP2_PM_510_2_WIDTH 8 /* DSP2_PM_510 - [7:0] */
+
+/*
+ * R28155 (0x6DFB) - DSP2 PM 1531
+ */
+#define WM5100_DSP2_PM_510_1_MASK 0xFFFF /* DSP2_PM_510 - [15:0] */
+#define WM5100_DSP2_PM_510_1_SHIFT 0 /* DSP2_PM_510 - [15:0] */
+#define WM5100_DSP2_PM_510_1_WIDTH 16 /* DSP2_PM_510 - [15:0] */
+
+/*
+ * R28156 (0x6DFC) - DSP2 PM 1532
+ */
+#define WM5100_DSP2_PM_510_MASK 0xFFFF /* DSP2_PM_510 - [15:0] */
+#define WM5100_DSP2_PM_510_SHIFT 0 /* DSP2_PM_510 - [15:0] */
+#define WM5100_DSP2_PM_510_WIDTH 16 /* DSP2_PM_510 - [15:0] */
+
+/*
+ * R28157 (0x6DFD) - DSP2 PM 1533
+ */
+#define WM5100_DSP2_PM_END_2_MASK 0x00FF /* DSP2_PM_END - [7:0] */
+#define WM5100_DSP2_PM_END_2_SHIFT 0 /* DSP2_PM_END - [7:0] */
+#define WM5100_DSP2_PM_END_2_WIDTH 8 /* DSP2_PM_END - [7:0] */
+
+/*
+ * R28158 (0x6DFE) - DSP2 PM 1534
+ */
+#define WM5100_DSP2_PM_END_1_MASK 0xFFFF /* DSP2_PM_END - [15:0] */
+#define WM5100_DSP2_PM_END_1_SHIFT 0 /* DSP2_PM_END - [15:0] */
+#define WM5100_DSP2_PM_END_1_WIDTH 16 /* DSP2_PM_END - [15:0] */
+
+/*
+ * R28159 (0x6DFF) - DSP2 PM 1535
+ */
+#define WM5100_DSP2_PM_END_MASK 0xFFFF /* DSP2_PM_END - [15:0] */
+#define WM5100_DSP2_PM_END_SHIFT 0 /* DSP2_PM_END - [15:0] */
+#define WM5100_DSP2_PM_END_WIDTH 16 /* DSP2_PM_END - [15:0] */
+
+/*
+ * R28672 (0x7000) - DSP2 ZM 0
+ */
+#define WM5100_DSP2_ZM_START_1_MASK 0x00FF /* DSP2_ZM_START - [7:0] */
+#define WM5100_DSP2_ZM_START_1_SHIFT 0 /* DSP2_ZM_START - [7:0] */
+#define WM5100_DSP2_ZM_START_1_WIDTH 8 /* DSP2_ZM_START - [7:0] */
+
+/*
+ * R28673 (0x7001) - DSP2 ZM 1
+ */
+#define WM5100_DSP2_ZM_START_MASK 0xFFFF /* DSP2_ZM_START - [15:0] */
+#define WM5100_DSP2_ZM_START_SHIFT 0 /* DSP2_ZM_START - [15:0] */
+#define WM5100_DSP2_ZM_START_WIDTH 16 /* DSP2_ZM_START - [15:0] */
+
+/*
+ * R28674 (0x7002) - DSP2 ZM 2
+ */
+#define WM5100_DSP2_ZM_1_1_MASK 0x00FF /* DSP2_ZM_1 - [7:0] */
+#define WM5100_DSP2_ZM_1_1_SHIFT 0 /* DSP2_ZM_1 - [7:0] */
+#define WM5100_DSP2_ZM_1_1_WIDTH 8 /* DSP2_ZM_1 - [7:0] */
+
+/*
+ * R28675 (0x7003) - DSP2 ZM 3
+ */
+#define WM5100_DSP2_ZM_1_MASK 0xFFFF /* DSP2_ZM_1 - [15:0] */
+#define WM5100_DSP2_ZM_1_SHIFT 0 /* DSP2_ZM_1 - [15:0] */
+#define WM5100_DSP2_ZM_1_WIDTH 16 /* DSP2_ZM_1 - [15:0] */
+
+/*
+ * R30716 (0x77FC) - DSP2 ZM 2044
+ */
+#define WM5100_DSP2_ZM_1022_1_MASK 0x00FF /* DSP2_ZM_1022 - [7:0] */
+#define WM5100_DSP2_ZM_1022_1_SHIFT 0 /* DSP2_ZM_1022 - [7:0] */
+#define WM5100_DSP2_ZM_1022_1_WIDTH 8 /* DSP2_ZM_1022 - [7:0] */
+
+/*
+ * R30717 (0x77FD) - DSP2 ZM 2045
+ */
+#define WM5100_DSP2_ZM_1022_MASK 0xFFFF /* DSP2_ZM_1022 - [15:0] */
+#define WM5100_DSP2_ZM_1022_SHIFT 0 /* DSP2_ZM_1022 - [15:0] */
+#define WM5100_DSP2_ZM_1022_WIDTH 16 /* DSP2_ZM_1022 - [15:0] */
+
+/*
+ * R30718 (0x77FE) - DSP2 ZM 2046
+ */
+#define WM5100_DSP2_ZM_END_1_MASK 0x00FF /* DSP2_ZM_END - [7:0] */
+#define WM5100_DSP2_ZM_END_1_SHIFT 0 /* DSP2_ZM_END - [7:0] */
+#define WM5100_DSP2_ZM_END_1_WIDTH 8 /* DSP2_ZM_END - [7:0] */
+
+/*
+ * R30719 (0x77FF) - DSP2 ZM 2047
+ */
+#define WM5100_DSP2_ZM_END_MASK 0xFFFF /* DSP2_ZM_END - [15:0] */
+#define WM5100_DSP2_ZM_END_SHIFT 0 /* DSP2_ZM_END - [15:0] */
+#define WM5100_DSP2_ZM_END_WIDTH 16 /* DSP2_ZM_END - [15:0] */
+
+/*
+ * R32768 (0x8000) - DSP3 DM 0
+ */
+#define WM5100_DSP3_DM_START_1_MASK 0x00FF /* DSP3_DM_START - [7:0] */
+#define WM5100_DSP3_DM_START_1_SHIFT 0 /* DSP3_DM_START - [7:0] */
+#define WM5100_DSP3_DM_START_1_WIDTH 8 /* DSP3_DM_START - [7:0] */
+
+/*
+ * R32769 (0x8001) - DSP3 DM 1
+ */
+#define WM5100_DSP3_DM_START_MASK 0xFFFF /* DSP3_DM_START - [15:0] */
+#define WM5100_DSP3_DM_START_SHIFT 0 /* DSP3_DM_START - [15:0] */
+#define WM5100_DSP3_DM_START_WIDTH 16 /* DSP3_DM_START - [15:0] */
+
+/*
+ * R32770 (0x8002) - DSP3 DM 2
+ */
+#define WM5100_DSP3_DM_1_1_MASK 0x00FF /* DSP3_DM_1 - [7:0] */
+#define WM5100_DSP3_DM_1_1_SHIFT 0 /* DSP3_DM_1 - [7:0] */
+#define WM5100_DSP3_DM_1_1_WIDTH 8 /* DSP3_DM_1 - [7:0] */
+
+/*
+ * R32771 (0x8003) - DSP3 DM 3
+ */
+#define WM5100_DSP3_DM_1_MASK 0xFFFF /* DSP3_DM_1 - [15:0] */
+#define WM5100_DSP3_DM_1_SHIFT 0 /* DSP3_DM_1 - [15:0] */
+#define WM5100_DSP3_DM_1_WIDTH 16 /* DSP3_DM_1 - [15:0] */
+
+/*
+ * R33276 (0x81FC) - DSP3 DM 508
+ */
+#define WM5100_DSP3_DM_254_1_MASK 0x00FF /* DSP3_DM_254 - [7:0] */
+#define WM5100_DSP3_DM_254_1_SHIFT 0 /* DSP3_DM_254 - [7:0] */
+#define WM5100_DSP3_DM_254_1_WIDTH 8 /* DSP3_DM_254 - [7:0] */
+
+/*
+ * R33277 (0x81FD) - DSP3 DM 509
+ */
+#define WM5100_DSP3_DM_254_MASK 0xFFFF /* DSP3_DM_254 - [15:0] */
+#define WM5100_DSP3_DM_254_SHIFT 0 /* DSP3_DM_254 - [15:0] */
+#define WM5100_DSP3_DM_254_WIDTH 16 /* DSP3_DM_254 - [15:0] */
+
+/*
+ * R33278 (0x81FE) - DSP3 DM 510
+ */
+#define WM5100_DSP3_DM_END_1_MASK 0x00FF /* DSP3_DM_END - [7:0] */
+#define WM5100_DSP3_DM_END_1_SHIFT 0 /* DSP3_DM_END - [7:0] */
+#define WM5100_DSP3_DM_END_1_WIDTH 8 /* DSP3_DM_END - [7:0] */
+
+/*
+ * R33279 (0x81FF) - DSP3 DM 511
+ */
+#define WM5100_DSP3_DM_END_MASK 0xFFFF /* DSP3_DM_END - [15:0] */
+#define WM5100_DSP3_DM_END_SHIFT 0 /* DSP3_DM_END - [15:0] */
+#define WM5100_DSP3_DM_END_WIDTH 16 /* DSP3_DM_END - [15:0] */
+
+/*
+ * R34816 (0x8800) - DSP3 PM 0
+ */
+#define WM5100_DSP3_PM_START_2_MASK 0x00FF /* DSP3_PM_START - [7:0] */
+#define WM5100_DSP3_PM_START_2_SHIFT 0 /* DSP3_PM_START - [7:0] */
+#define WM5100_DSP3_PM_START_2_WIDTH 8 /* DSP3_PM_START - [7:0] */
+
+/*
+ * R34817 (0x8801) - DSP3 PM 1
+ */
+#define WM5100_DSP3_PM_START_1_MASK 0xFFFF /* DSP3_PM_START - [15:0] */
+#define WM5100_DSP3_PM_START_1_SHIFT 0 /* DSP3_PM_START - [15:0] */
+#define WM5100_DSP3_PM_START_1_WIDTH 16 /* DSP3_PM_START - [15:0] */
+
+/*
+ * R34818 (0x8802) - DSP3 PM 2
+ */
+#define WM5100_DSP3_PM_START_MASK 0xFFFF /* DSP3_PM_START - [15:0] */
+#define WM5100_DSP3_PM_START_SHIFT 0 /* DSP3_PM_START - [15:0] */
+#define WM5100_DSP3_PM_START_WIDTH 16 /* DSP3_PM_START - [15:0] */
+
+/*
+ * R34819 (0x8803) - DSP3 PM 3
+ */
+#define WM5100_DSP3_PM_1_2_MASK 0x00FF /* DSP3_PM_1 - [7:0] */
+#define WM5100_DSP3_PM_1_2_SHIFT 0 /* DSP3_PM_1 - [7:0] */
+#define WM5100_DSP3_PM_1_2_WIDTH 8 /* DSP3_PM_1 - [7:0] */
+
+/*
+ * R34820 (0x8804) - DSP3 PM 4
+ */
+#define WM5100_DSP3_PM_1_1_MASK 0xFFFF /* DSP3_PM_1 - [15:0] */
+#define WM5100_DSP3_PM_1_1_SHIFT 0 /* DSP3_PM_1 - [15:0] */
+#define WM5100_DSP3_PM_1_1_WIDTH 16 /* DSP3_PM_1 - [15:0] */
+
+/*
+ * R34821 (0x8805) - DSP3 PM 5
+ */
+#define WM5100_DSP3_PM_1_MASK 0xFFFF /* DSP3_PM_1 - [15:0] */
+#define WM5100_DSP3_PM_1_SHIFT 0 /* DSP3_PM_1 - [15:0] */
+#define WM5100_DSP3_PM_1_WIDTH 16 /* DSP3_PM_1 - [15:0] */
+
+/*
+ * R36346 (0x8DFA) - DSP3 PM 1530
+ */
+#define WM5100_DSP3_PM_510_2_MASK 0x00FF /* DSP3_PM_510 - [7:0] */
+#define WM5100_DSP3_PM_510_2_SHIFT 0 /* DSP3_PM_510 - [7:0] */
+#define WM5100_DSP3_PM_510_2_WIDTH 8 /* DSP3_PM_510 - [7:0] */
+
+/*
+ * R36347 (0x8DFB) - DSP3 PM 1531
+ */
+#define WM5100_DSP3_PM_510_1_MASK 0xFFFF /* DSP3_PM_510 - [15:0] */
+#define WM5100_DSP3_PM_510_1_SHIFT 0 /* DSP3_PM_510 - [15:0] */
+#define WM5100_DSP3_PM_510_1_WIDTH 16 /* DSP3_PM_510 - [15:0] */
+
+/*
+ * R36348 (0x8DFC) - DSP3 PM 1532
+ */
+#define WM5100_DSP3_PM_510_MASK 0xFFFF /* DSP3_PM_510 - [15:0] */
+#define WM5100_DSP3_PM_510_SHIFT 0 /* DSP3_PM_510 - [15:0] */
+#define WM5100_DSP3_PM_510_WIDTH 16 /* DSP3_PM_510 - [15:0] */
+
+/*
+ * R36349 (0x8DFD) - DSP3 PM 1533
+ */
+#define WM5100_DSP3_PM_END_2_MASK 0x00FF /* DSP3_PM_END - [7:0] */
+#define WM5100_DSP3_PM_END_2_SHIFT 0 /* DSP3_PM_END - [7:0] */
+#define WM5100_DSP3_PM_END_2_WIDTH 8 /* DSP3_PM_END - [7:0] */
+
+/*
+ * R36350 (0x8DFE) - DSP3 PM 1534
+ */
+#define WM5100_DSP3_PM_END_1_MASK 0xFFFF /* DSP3_PM_END - [15:0] */
+#define WM5100_DSP3_PM_END_1_SHIFT 0 /* DSP3_PM_END - [15:0] */
+#define WM5100_DSP3_PM_END_1_WIDTH 16 /* DSP3_PM_END - [15:0] */
+
+/*
+ * R36351 (0x8DFF) - DSP3 PM 1535
+ */
+#define WM5100_DSP3_PM_END_MASK 0xFFFF /* DSP3_PM_END - [15:0] */
+#define WM5100_DSP3_PM_END_SHIFT 0 /* DSP3_PM_END - [15:0] */
+#define WM5100_DSP3_PM_END_WIDTH 16 /* DSP3_PM_END - [15:0] */
+
+/*
+ * R36864 (0x9000) - DSP3 ZM 0
+ */
+#define WM5100_DSP3_ZM_START_1_MASK 0x00FF /* DSP3_ZM_START - [7:0] */
+#define WM5100_DSP3_ZM_START_1_SHIFT 0 /* DSP3_ZM_START - [7:0] */
+#define WM5100_DSP3_ZM_START_1_WIDTH 8 /* DSP3_ZM_START - [7:0] */
+
+/*
+ * R36865 (0x9001) - DSP3 ZM 1
+ */
+#define WM5100_DSP3_ZM_START_MASK 0xFFFF /* DSP3_ZM_START - [15:0] */
+#define WM5100_DSP3_ZM_START_SHIFT 0 /* DSP3_ZM_START - [15:0] */
+#define WM5100_DSP3_ZM_START_WIDTH 16 /* DSP3_ZM_START - [15:0] */
+
+/*
+ * R36866 (0x9002) - DSP3 ZM 2
+ */
+#define WM5100_DSP3_ZM_1_1_MASK 0x00FF /* DSP3_ZM_1 - [7:0] */
+#define WM5100_DSP3_ZM_1_1_SHIFT 0 /* DSP3_ZM_1 - [7:0] */
+#define WM5100_DSP3_ZM_1_1_WIDTH 8 /* DSP3_ZM_1 - [7:0] */
+
+/*
+ * R36867 (0x9003) - DSP3 ZM 3
+ */
+#define WM5100_DSP3_ZM_1_MASK 0xFFFF /* DSP3_ZM_1 - [15:0] */
+#define WM5100_DSP3_ZM_1_SHIFT 0 /* DSP3_ZM_1 - [15:0] */
+#define WM5100_DSP3_ZM_1_WIDTH 16 /* DSP3_ZM_1 - [15:0] */
+
+/*
+ * R38908 (0x97FC) - DSP3 ZM 2044
+ */
+#define WM5100_DSP3_ZM_1022_1_MASK 0x00FF /* DSP3_ZM_1022 - [7:0] */
+#define WM5100_DSP3_ZM_1022_1_SHIFT 0 /* DSP3_ZM_1022 - [7:0] */
+#define WM5100_DSP3_ZM_1022_1_WIDTH 8 /* DSP3_ZM_1022 - [7:0] */
+
+/*
+ * R38909 (0x97FD) - DSP3 ZM 2045
+ */
+#define WM5100_DSP3_ZM_1022_MASK 0xFFFF /* DSP3_ZM_1022 - [15:0] */
+#define WM5100_DSP3_ZM_1022_SHIFT 0 /* DSP3_ZM_1022 - [15:0] */
+#define WM5100_DSP3_ZM_1022_WIDTH 16 /* DSP3_ZM_1022 - [15:0] */
+
+/*
+ * R38910 (0x97FE) - DSP3 ZM 2046
+ */
+#define WM5100_DSP3_ZM_END_1_MASK 0x00FF /* DSP3_ZM_END - [7:0] */
+#define WM5100_DSP3_ZM_END_1_SHIFT 0 /* DSP3_ZM_END - [7:0] */
+#define WM5100_DSP3_ZM_END_1_WIDTH 8 /* DSP3_ZM_END - [7:0] */
+
+/*
+ * R38911 (0x97FF) - DSP3 ZM 2047
+ */
+#define WM5100_DSP3_ZM_END_MASK 0xFFFF /* DSP3_ZM_END - [15:0] */
+#define WM5100_DSP3_ZM_END_SHIFT 0 /* DSP3_ZM_END - [15:0] */
+#define WM5100_DSP3_ZM_END_WIDTH 16 /* DSP3_ZM_END - [15:0] */
+
+bool wm5100_readable_register(struct device *dev, unsigned int reg);
+bool wm5100_volatile_register(struct device *dev, unsigned int reg);
+
+extern struct reg_default wm5100_reg_defaults[WM5100_REGISTER_COUNT];
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm5102.c b/kernel/sound/soc/codecs/wm5102.c
new file mode 100644
index 000000000..0c6d1bc05
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm5102.c
@@ -0,0 +1,1977 @@
+/*
+ * wm5102.c -- WM5102 ALSA SoC Audio driver
+ *
+ * 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/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 <asm/unaligned.h>
+
+#include "arizona.h"
+#include "wm5102.h"
+#include "wm_adsp.h"
+
+struct wm5102_priv {
+ struct arizona_priv core;
+ struct arizona_fll fll[2];
+};
+
+static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
+static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+
+static const struct wm_adsp_region wm5102_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x100000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x180000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x190000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x1a8000 },
+};
+
+static const struct reg_default wm5102_sysclk_reva_patch[] = {
+ { 0x3000, 0x2225 },
+ { 0x3001, 0x3a03 },
+ { 0x3002, 0x0225 },
+ { 0x3003, 0x0801 },
+ { 0x3004, 0x6249 },
+ { 0x3005, 0x0c04 },
+ { 0x3006, 0x0225 },
+ { 0x3007, 0x5901 },
+ { 0x3008, 0xe249 },
+ { 0x3009, 0x030d },
+ { 0x300a, 0x0249 },
+ { 0x300b, 0x2c01 },
+ { 0x300c, 0xe249 },
+ { 0x300d, 0x4342 },
+ { 0x300e, 0xe249 },
+ { 0x300f, 0x73c0 },
+ { 0x3010, 0x4249 },
+ { 0x3011, 0x0c00 },
+ { 0x3012, 0x0225 },
+ { 0x3013, 0x1f01 },
+ { 0x3014, 0x0225 },
+ { 0x3015, 0x1e01 },
+ { 0x3016, 0x0225 },
+ { 0x3017, 0xfa00 },
+ { 0x3018, 0x0000 },
+ { 0x3019, 0xf000 },
+ { 0x301a, 0x0000 },
+ { 0x301b, 0xf000 },
+ { 0x301c, 0x0000 },
+ { 0x301d, 0xf000 },
+ { 0x301e, 0x0000 },
+ { 0x301f, 0xf000 },
+ { 0x3020, 0x0000 },
+ { 0x3021, 0xf000 },
+ { 0x3022, 0x0000 },
+ { 0x3023, 0xf000 },
+ { 0x3024, 0x0000 },
+ { 0x3025, 0xf000 },
+ { 0x3026, 0x0000 },
+ { 0x3027, 0xf000 },
+ { 0x3028, 0x0000 },
+ { 0x3029, 0xf000 },
+ { 0x302a, 0x0000 },
+ { 0x302b, 0xf000 },
+ { 0x302c, 0x0000 },
+ { 0x302d, 0xf000 },
+ { 0x302e, 0x0000 },
+ { 0x302f, 0xf000 },
+ { 0x3030, 0x0225 },
+ { 0x3031, 0x1a01 },
+ { 0x3032, 0x0225 },
+ { 0x3033, 0x1e00 },
+ { 0x3034, 0x0225 },
+ { 0x3035, 0x1f00 },
+ { 0x3036, 0x6225 },
+ { 0x3037, 0xf800 },
+ { 0x3038, 0x0000 },
+ { 0x3039, 0xf000 },
+ { 0x303a, 0x0000 },
+ { 0x303b, 0xf000 },
+ { 0x303c, 0x0000 },
+ { 0x303d, 0xf000 },
+ { 0x303e, 0x0000 },
+ { 0x303f, 0xf000 },
+ { 0x3040, 0x2226 },
+ { 0x3041, 0x3a03 },
+ { 0x3042, 0x0226 },
+ { 0x3043, 0x0801 },
+ { 0x3044, 0x6249 },
+ { 0x3045, 0x0c06 },
+ { 0x3046, 0x0226 },
+ { 0x3047, 0x5901 },
+ { 0x3048, 0xe249 },
+ { 0x3049, 0x030d },
+ { 0x304a, 0x0249 },
+ { 0x304b, 0x2c01 },
+ { 0x304c, 0xe249 },
+ { 0x304d, 0x4342 },
+ { 0x304e, 0xe249 },
+ { 0x304f, 0x73c0 },
+ { 0x3050, 0x4249 },
+ { 0x3051, 0x0c00 },
+ { 0x3052, 0x0226 },
+ { 0x3053, 0x1f01 },
+ { 0x3054, 0x0226 },
+ { 0x3055, 0x1e01 },
+ { 0x3056, 0x0226 },
+ { 0x3057, 0xfa00 },
+ { 0x3058, 0x0000 },
+ { 0x3059, 0xf000 },
+ { 0x305a, 0x0000 },
+ { 0x305b, 0xf000 },
+ { 0x305c, 0x0000 },
+ { 0x305d, 0xf000 },
+ { 0x305e, 0x0000 },
+ { 0x305f, 0xf000 },
+ { 0x3060, 0x0000 },
+ { 0x3061, 0xf000 },
+ { 0x3062, 0x0000 },
+ { 0x3063, 0xf000 },
+ { 0x3064, 0x0000 },
+ { 0x3065, 0xf000 },
+ { 0x3066, 0x0000 },
+ { 0x3067, 0xf000 },
+ { 0x3068, 0x0000 },
+ { 0x3069, 0xf000 },
+ { 0x306a, 0x0000 },
+ { 0x306b, 0xf000 },
+ { 0x306c, 0x0000 },
+ { 0x306d, 0xf000 },
+ { 0x306e, 0x0000 },
+ { 0x306f, 0xf000 },
+ { 0x3070, 0x0226 },
+ { 0x3071, 0x1a01 },
+ { 0x3072, 0x0226 },
+ { 0x3073, 0x1e00 },
+ { 0x3074, 0x0226 },
+ { 0x3075, 0x1f00 },
+ { 0x3076, 0x6226 },
+ { 0x3077, 0xf800 },
+ { 0x3078, 0x0000 },
+ { 0x3079, 0xf000 },
+ { 0x307a, 0x0000 },
+ { 0x307b, 0xf000 },
+ { 0x307c, 0x0000 },
+ { 0x307d, 0xf000 },
+ { 0x307e, 0x0000 },
+ { 0x307f, 0xf000 },
+ { 0x3080, 0x2227 },
+ { 0x3081, 0x3a03 },
+ { 0x3082, 0x0227 },
+ { 0x3083, 0x0801 },
+ { 0x3084, 0x6255 },
+ { 0x3085, 0x0c04 },
+ { 0x3086, 0x0227 },
+ { 0x3087, 0x5901 },
+ { 0x3088, 0xe255 },
+ { 0x3089, 0x030d },
+ { 0x308a, 0x0255 },
+ { 0x308b, 0x2c01 },
+ { 0x308c, 0xe255 },
+ { 0x308d, 0x4342 },
+ { 0x308e, 0xe255 },
+ { 0x308f, 0x73c0 },
+ { 0x3090, 0x4255 },
+ { 0x3091, 0x0c00 },
+ { 0x3092, 0x0227 },
+ { 0x3093, 0x1f01 },
+ { 0x3094, 0x0227 },
+ { 0x3095, 0x1e01 },
+ { 0x3096, 0x0227 },
+ { 0x3097, 0xfa00 },
+ { 0x3098, 0x0000 },
+ { 0x3099, 0xf000 },
+ { 0x309a, 0x0000 },
+ { 0x309b, 0xf000 },
+ { 0x309c, 0x0000 },
+ { 0x309d, 0xf000 },
+ { 0x309e, 0x0000 },
+ { 0x309f, 0xf000 },
+ { 0x30a0, 0x0000 },
+ { 0x30a1, 0xf000 },
+ { 0x30a2, 0x0000 },
+ { 0x30a3, 0xf000 },
+ { 0x30a4, 0x0000 },
+ { 0x30a5, 0xf000 },
+ { 0x30a6, 0x0000 },
+ { 0x30a7, 0xf000 },
+ { 0x30a8, 0x0000 },
+ { 0x30a9, 0xf000 },
+ { 0x30aa, 0x0000 },
+ { 0x30ab, 0xf000 },
+ { 0x30ac, 0x0000 },
+ { 0x30ad, 0xf000 },
+ { 0x30ae, 0x0000 },
+ { 0x30af, 0xf000 },
+ { 0x30b0, 0x0227 },
+ { 0x30b1, 0x1a01 },
+ { 0x30b2, 0x0227 },
+ { 0x30b3, 0x1e00 },
+ { 0x30b4, 0x0227 },
+ { 0x30b5, 0x1f00 },
+ { 0x30b6, 0x6227 },
+ { 0x30b7, 0xf800 },
+ { 0x30b8, 0x0000 },
+ { 0x30b9, 0xf000 },
+ { 0x30ba, 0x0000 },
+ { 0x30bb, 0xf000 },
+ { 0x30bc, 0x0000 },
+ { 0x30bd, 0xf000 },
+ { 0x30be, 0x0000 },
+ { 0x30bf, 0xf000 },
+ { 0x30c0, 0x2228 },
+ { 0x30c1, 0x3a03 },
+ { 0x30c2, 0x0228 },
+ { 0x30c3, 0x0801 },
+ { 0x30c4, 0x6255 },
+ { 0x30c5, 0x0c06 },
+ { 0x30c6, 0x0228 },
+ { 0x30c7, 0x5901 },
+ { 0x30c8, 0xe255 },
+ { 0x30c9, 0x030d },
+ { 0x30ca, 0x0255 },
+ { 0x30cb, 0x2c01 },
+ { 0x30cc, 0xe255 },
+ { 0x30cd, 0x4342 },
+ { 0x30ce, 0xe255 },
+ { 0x30cf, 0x73c0 },
+ { 0x30d0, 0x4255 },
+ { 0x30d1, 0x0c00 },
+ { 0x30d2, 0x0228 },
+ { 0x30d3, 0x1f01 },
+ { 0x30d4, 0x0228 },
+ { 0x30d5, 0x1e01 },
+ { 0x30d6, 0x0228 },
+ { 0x30d7, 0xfa00 },
+ { 0x30d8, 0x0000 },
+ { 0x30d9, 0xf000 },
+ { 0x30da, 0x0000 },
+ { 0x30db, 0xf000 },
+ { 0x30dc, 0x0000 },
+ { 0x30dd, 0xf000 },
+ { 0x30de, 0x0000 },
+ { 0x30df, 0xf000 },
+ { 0x30e0, 0x0000 },
+ { 0x30e1, 0xf000 },
+ { 0x30e2, 0x0000 },
+ { 0x30e3, 0xf000 },
+ { 0x30e4, 0x0000 },
+ { 0x30e5, 0xf000 },
+ { 0x30e6, 0x0000 },
+ { 0x30e7, 0xf000 },
+ { 0x30e8, 0x0000 },
+ { 0x30e9, 0xf000 },
+ { 0x30ea, 0x0000 },
+ { 0x30eb, 0xf000 },
+ { 0x30ec, 0x0000 },
+ { 0x30ed, 0xf000 },
+ { 0x30ee, 0x0000 },
+ { 0x30ef, 0xf000 },
+ { 0x30f0, 0x0228 },
+ { 0x30f1, 0x1a01 },
+ { 0x30f2, 0x0228 },
+ { 0x30f3, 0x1e00 },
+ { 0x30f4, 0x0228 },
+ { 0x30f5, 0x1f00 },
+ { 0x30f6, 0x6228 },
+ { 0x30f7, 0xf800 },
+ { 0x30f8, 0x0000 },
+ { 0x30f9, 0xf000 },
+ { 0x30fa, 0x0000 },
+ { 0x30fb, 0xf000 },
+ { 0x30fc, 0x0000 },
+ { 0x30fd, 0xf000 },
+ { 0x30fe, 0x0000 },
+ { 0x30ff, 0xf000 },
+ { 0x3100, 0x222b },
+ { 0x3101, 0x3a03 },
+ { 0x3102, 0x222b },
+ { 0x3103, 0x5803 },
+ { 0x3104, 0xe26f },
+ { 0x3105, 0x030d },
+ { 0x3106, 0x626f },
+ { 0x3107, 0x2c01 },
+ { 0x3108, 0xe26f },
+ { 0x3109, 0x4342 },
+ { 0x310a, 0xe26f },
+ { 0x310b, 0x73c0 },
+ { 0x310c, 0x026f },
+ { 0x310d, 0x0c00 },
+ { 0x310e, 0x022b },
+ { 0x310f, 0x1f01 },
+ { 0x3110, 0x022b },
+ { 0x3111, 0x1e01 },
+ { 0x3112, 0x022b },
+ { 0x3113, 0xfa00 },
+ { 0x3114, 0x0000 },
+ { 0x3115, 0xf000 },
+ { 0x3116, 0x0000 },
+ { 0x3117, 0xf000 },
+ { 0x3118, 0x0000 },
+ { 0x3119, 0xf000 },
+ { 0x311a, 0x0000 },
+ { 0x311b, 0xf000 },
+ { 0x311c, 0x0000 },
+ { 0x311d, 0xf000 },
+ { 0x311e, 0x0000 },
+ { 0x311f, 0xf000 },
+ { 0x3120, 0x022b },
+ { 0x3121, 0x0a01 },
+ { 0x3122, 0x022b },
+ { 0x3123, 0x1e00 },
+ { 0x3124, 0x022b },
+ { 0x3125, 0x1f00 },
+ { 0x3126, 0x622b },
+ { 0x3127, 0xf800 },
+ { 0x3128, 0x0000 },
+ { 0x3129, 0xf000 },
+ { 0x312a, 0x0000 },
+ { 0x312b, 0xf000 },
+ { 0x312c, 0x0000 },
+ { 0x312d, 0xf000 },
+ { 0x312e, 0x0000 },
+ { 0x312f, 0xf000 },
+ { 0x3130, 0x0000 },
+ { 0x3131, 0xf000 },
+ { 0x3132, 0x0000 },
+ { 0x3133, 0xf000 },
+ { 0x3134, 0x0000 },
+ { 0x3135, 0xf000 },
+ { 0x3136, 0x0000 },
+ { 0x3137, 0xf000 },
+ { 0x3138, 0x0000 },
+ { 0x3139, 0xf000 },
+ { 0x313a, 0x0000 },
+ { 0x313b, 0xf000 },
+ { 0x313c, 0x0000 },
+ { 0x313d, 0xf000 },
+ { 0x313e, 0x0000 },
+ { 0x313f, 0xf000 },
+ { 0x3140, 0x0000 },
+ { 0x3141, 0xf000 },
+ { 0x3142, 0x0000 },
+ { 0x3143, 0xf000 },
+ { 0x3144, 0x0000 },
+ { 0x3145, 0xf000 },
+ { 0x3146, 0x0000 },
+ { 0x3147, 0xf000 },
+ { 0x3148, 0x0000 },
+ { 0x3149, 0xf000 },
+ { 0x314a, 0x0000 },
+ { 0x314b, 0xf000 },
+ { 0x314c, 0x0000 },
+ { 0x314d, 0xf000 },
+ { 0x314e, 0x0000 },
+ { 0x314f, 0xf000 },
+ { 0x3150, 0x0000 },
+ { 0x3151, 0xf000 },
+ { 0x3152, 0x0000 },
+ { 0x3153, 0xf000 },
+ { 0x3154, 0x0000 },
+ { 0x3155, 0xf000 },
+ { 0x3156, 0x0000 },
+ { 0x3157, 0xf000 },
+ { 0x3158, 0x0000 },
+ { 0x3159, 0xf000 },
+ { 0x315a, 0x0000 },
+ { 0x315b, 0xf000 },
+ { 0x315c, 0x0000 },
+ { 0x315d, 0xf000 },
+ { 0x315e, 0x0000 },
+ { 0x315f, 0xf000 },
+ { 0x3160, 0x0000 },
+ { 0x3161, 0xf000 },
+ { 0x3162, 0x0000 },
+ { 0x3163, 0xf000 },
+ { 0x3164, 0x0000 },
+ { 0x3165, 0xf000 },
+ { 0x3166, 0x0000 },
+ { 0x3167, 0xf000 },
+ { 0x3168, 0x0000 },
+ { 0x3169, 0xf000 },
+ { 0x316a, 0x0000 },
+ { 0x316b, 0xf000 },
+ { 0x316c, 0x0000 },
+ { 0x316d, 0xf000 },
+ { 0x316e, 0x0000 },
+ { 0x316f, 0xf000 },
+ { 0x3170, 0x0000 },
+ { 0x3171, 0xf000 },
+ { 0x3172, 0x0000 },
+ { 0x3173, 0xf000 },
+ { 0x3174, 0x0000 },
+ { 0x3175, 0xf000 },
+ { 0x3176, 0x0000 },
+ { 0x3177, 0xf000 },
+ { 0x3178, 0x0000 },
+ { 0x3179, 0xf000 },
+ { 0x317a, 0x0000 },
+ { 0x317b, 0xf000 },
+ { 0x317c, 0x0000 },
+ { 0x317d, 0xf000 },
+ { 0x317e, 0x0000 },
+ { 0x317f, 0xf000 },
+ { 0x3180, 0x2001 },
+ { 0x3181, 0xf101 },
+ { 0x3182, 0x0000 },
+ { 0x3183, 0xf000 },
+ { 0x3184, 0x0000 },
+ { 0x3185, 0xf000 },
+ { 0x3186, 0x0000 },
+ { 0x3187, 0xf000 },
+ { 0x3188, 0x0000 },
+ { 0x3189, 0xf000 },
+ { 0x318a, 0x0000 },
+ { 0x318b, 0xf000 },
+ { 0x318c, 0x0000 },
+ { 0x318d, 0xf000 },
+ { 0x318e, 0x0000 },
+ { 0x318f, 0xf000 },
+ { 0x3190, 0x0000 },
+ { 0x3191, 0xf000 },
+ { 0x3192, 0x0000 },
+ { 0x3193, 0xf000 },
+ { 0x3194, 0x0000 },
+ { 0x3195, 0xf000 },
+ { 0x3196, 0x0000 },
+ { 0x3197, 0xf000 },
+ { 0x3198, 0x0000 },
+ { 0x3199, 0xf000 },
+ { 0x319a, 0x0000 },
+ { 0x319b, 0xf000 },
+ { 0x319c, 0x0000 },
+ { 0x319d, 0xf000 },
+ { 0x319e, 0x0000 },
+ { 0x319f, 0xf000 },
+ { 0x31a0, 0x0000 },
+ { 0x31a1, 0xf000 },
+ { 0x31a2, 0x0000 },
+ { 0x31a3, 0xf000 },
+ { 0x31a4, 0x0000 },
+ { 0x31a5, 0xf000 },
+ { 0x31a6, 0x0000 },
+ { 0x31a7, 0xf000 },
+ { 0x31a8, 0x0000 },
+ { 0x31a9, 0xf000 },
+ { 0x31aa, 0x0000 },
+ { 0x31ab, 0xf000 },
+ { 0x31ac, 0x0000 },
+ { 0x31ad, 0xf000 },
+ { 0x31ae, 0x0000 },
+ { 0x31af, 0xf000 },
+ { 0x31b0, 0x0000 },
+ { 0x31b1, 0xf000 },
+ { 0x31b2, 0x0000 },
+ { 0x31b3, 0xf000 },
+ { 0x31b4, 0x0000 },
+ { 0x31b5, 0xf000 },
+ { 0x31b6, 0x0000 },
+ { 0x31b7, 0xf000 },
+ { 0x31b8, 0x0000 },
+ { 0x31b9, 0xf000 },
+ { 0x31ba, 0x0000 },
+ { 0x31bb, 0xf000 },
+ { 0x31bc, 0x0000 },
+ { 0x31bd, 0xf000 },
+ { 0x31be, 0x0000 },
+ { 0x31bf, 0xf000 },
+ { 0x31c0, 0x0000 },
+ { 0x31c1, 0xf000 },
+ { 0x31c2, 0x0000 },
+ { 0x31c3, 0xf000 },
+ { 0x31c4, 0x0000 },
+ { 0x31c5, 0xf000 },
+ { 0x31c6, 0x0000 },
+ { 0x31c7, 0xf000 },
+ { 0x31c8, 0x0000 },
+ { 0x31c9, 0xf000 },
+ { 0x31ca, 0x0000 },
+ { 0x31cb, 0xf000 },
+ { 0x31cc, 0x0000 },
+ { 0x31cd, 0xf000 },
+ { 0x31ce, 0x0000 },
+ { 0x31cf, 0xf000 },
+ { 0x31d0, 0x0000 },
+ { 0x31d1, 0xf000 },
+ { 0x31d2, 0x0000 },
+ { 0x31d3, 0xf000 },
+ { 0x31d4, 0x0000 },
+ { 0x31d5, 0xf000 },
+ { 0x31d6, 0x0000 },
+ { 0x31d7, 0xf000 },
+ { 0x31d8, 0x0000 },
+ { 0x31d9, 0xf000 },
+ { 0x31da, 0x0000 },
+ { 0x31db, 0xf000 },
+ { 0x31dc, 0x0000 },
+ { 0x31dd, 0xf000 },
+ { 0x31de, 0x0000 },
+ { 0x31df, 0xf000 },
+ { 0x31e0, 0x0000 },
+ { 0x31e1, 0xf000 },
+ { 0x31e2, 0x0000 },
+ { 0x31e3, 0xf000 },
+ { 0x31e4, 0x0000 },
+ { 0x31e5, 0xf000 },
+ { 0x31e6, 0x0000 },
+ { 0x31e7, 0xf000 },
+ { 0x31e8, 0x0000 },
+ { 0x31e9, 0xf000 },
+ { 0x31ea, 0x0000 },
+ { 0x31eb, 0xf000 },
+ { 0x31ec, 0x0000 },
+ { 0x31ed, 0xf000 },
+ { 0x31ee, 0x0000 },
+ { 0x31ef, 0xf000 },
+ { 0x31f0, 0x0000 },
+ { 0x31f1, 0xf000 },
+ { 0x31f2, 0x0000 },
+ { 0x31f3, 0xf000 },
+ { 0x31f4, 0x0000 },
+ { 0x31f5, 0xf000 },
+ { 0x31f6, 0x0000 },
+ { 0x31f7, 0xf000 },
+ { 0x31f8, 0x0000 },
+ { 0x31f9, 0xf000 },
+ { 0x31fa, 0x0000 },
+ { 0x31fb, 0xf000 },
+ { 0x31fc, 0x0000 },
+ { 0x31fd, 0xf000 },
+ { 0x31fe, 0x0000 },
+ { 0x31ff, 0xf000 },
+ { 0x024d, 0xff50 },
+ { 0x0252, 0xff50 },
+ { 0x0259, 0x0112 },
+ { 0x025e, 0x0112 },
+};
+
+static const struct reg_default wm5102_sysclk_revb_patch[] = {
+ { 0x3081, 0x08FE },
+ { 0x3083, 0x00ED },
+ { 0x30C1, 0x08FE },
+ { 0x30C3, 0x00ED },
+};
+
+static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ struct regmap *regmap = arizona->regmap;
+ const struct reg_default *patch = NULL;
+ int i, patch_size;
+
+ switch (arizona->rev) {
+ case 0:
+ patch = wm5102_sysclk_reva_patch;
+ patch_size = ARRAY_SIZE(wm5102_sysclk_reva_patch);
+ break;
+ default:
+ patch = wm5102_sysclk_revb_patch;
+ patch_size = ARRAY_SIZE(wm5102_sysclk_revb_patch);
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (patch)
+ for (i = 0; i < patch_size; i++)
+ regmap_write_async(regmap, patch[i].reg,
+ patch[i].def);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+
+ mutex_lock(&arizona->dac_comp_lock);
+ put_unaligned_be16(arizona->dac_comp_coeff,
+ ucontrol->value.bytes.data);
+ mutex_unlock(&arizona->dac_comp_lock);
+
+ return 0;
+}
+
+static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+
+ mutex_lock(&arizona->dac_comp_lock);
+ memcpy(&arizona->dac_comp_coeff, ucontrol->value.bytes.data,
+ sizeof(arizona->dac_comp_coeff));
+ arizona->dac_comp_coeff = be16_to_cpu(arizona->dac_comp_coeff);
+ mutex_unlock(&arizona->dac_comp_lock);
+
+ return 0;
+}
+
+static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+
+ mutex_lock(&arizona->dac_comp_lock);
+ ucontrol->value.integer.value[0] = arizona->dac_comp_enabled;
+ mutex_unlock(&arizona->dac_comp_lock);
+
+ return 0;
+}
+
+static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+
+ mutex_lock(&arizona->dac_comp_lock);
+ arizona->dac_comp_enabled = ucontrol->value.integer.value[0];
+ mutex_unlock(&arizona->dac_comp_lock);
+
+ return 0;
+}
+
+static const char *wm5102_osr_text[] = {
+ "Low power", "Normal", "High performance",
+};
+
+static const unsigned int wm5102_osr_val[] = {
+ 0x0, 0x3, 0x5,
+};
+
+static const struct soc_enum wm5102_hpout_osr[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L,
+ ARIZONA_OUT1_OSR_SHIFT, 0x7,
+ ARRAY_SIZE(wm5102_osr_text),
+ wm5102_osr_text, wm5102_osr_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2L,
+ ARIZONA_OUT2_OSR_SHIFT, 0x7,
+ ARRAY_SIZE(wm5102_osr_text),
+ wm5102_osr_text, wm5102_osr_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L,
+ ARIZONA_OUT3_OSR_SHIFT, 0x7,
+ ARRAY_SIZE(wm5102_osr_text),
+ wm5102_osr_text, wm5102_osr_val),
+};
+
+#define WM5102_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \
+ SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
+static const struct snd_kcontrol_new wm5102_snd_controls[] = {
+SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL,
+ ARIZONA_IN3_OSR_SHIFT, 1, 0),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+ ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+ ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+ ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+ ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+ ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R,
+ ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+
+SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
+
+ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
+SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0),
+SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
+SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0),
+SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
+SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0),
+SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
+SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0),
+SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5,
+ ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA),
+
+ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+
+ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),
+
+SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
+
+SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+
+ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv),
+
+ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT2L", ARIZONA_OUT2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT2R", ARIZONA_OUT2RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,
+ ARIZONA_OUT4_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
+ ARIZONA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+
+SOC_ENUM("HPOUT1 OSR", wm5102_hpout_osr[0]),
+SOC_ENUM("HPOUT2 OSR", wm5102_hpout_osr[1]),
+SOC_ENUM("EPOUT OSR", wm5102_hpout_osr[2]),
+
+SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0),
+SOC_DOUBLE("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0),
+SOC_SINGLE("EPOUT DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE3L_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("DRE Threshold", ARIZONA_DRE_CONTROL_2,
+ ARIZONA_DRE_T_LOW_SHIFT, 63, 0),
+
+SOC_SINGLE("DRE Low Level ABS", ARIZONA_DRE_CONTROL_3,
+ ARIZONA_DRE_LOW_LEVEL_ABS_SHIFT, 15, 0),
+
+SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
+
+SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
+ ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+SND_SOC_BYTES_EXT("Output Compensation Coefficient", 2,
+ wm5102_out_comp_coeff_get, wm5102_out_comp_coeff_put),
+
+SOC_SINGLE_EXT("Output Compensation Switch", 0, 0, 1, 0,
+ wm5102_out_comp_switch_get, wm5102_out_comp_switch_put),
+
+WM5102_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+WM5102_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+WM5102_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L),
+WM5102_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R),
+WM5102_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L),
+WM5102_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L),
+WM5102_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R),
+WM5102_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L),
+WM5102_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R),
+
+ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
+};
+
+ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE);
+
+ARIZONA_DSP_AUX_ENUMS(DSP1, ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE);
+
+static const char *wm5102_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "EPOUT",
+ "SPKOUTL", "SPKOUTR", "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int wm5102_aec_loopback_values[] = {
+ 0, 1, 2, 3, 4, 6, 7, 8, 9,
+};
+
+static const struct soc_enum wm5102_aec_loopback =
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(wm5102_aec_loopback_texts),
+ wm5102_aec_loopback_texts,
+ wm5102_aec_loopback_values);
+
+static const struct snd_kcontrol_new wm5102_aec_loopback_mux =
+ SOC_DAPM_ENUM("AEC Loopback", wm5102_aec_loopback);
+
+static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
+ 0, wm5102_sysclk_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
+ ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
+ ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+SND_SOC_DAPM_INPUT("IN3L"),
+SND_SOC_DAPM_INPUT("IN3R"),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
+ ARIZONA_MICB2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
+ ARIZONA_MICB3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1,
+ ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0,
+ 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, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0,
+ 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, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
+ 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, 0,
+ ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX8_ENA_SHIFT, 0),
+
+ARIZONA_DSP_WIDGETS(DSP1, "DSP1"),
+
+SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &wm5102_aec_loopback_mux),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"),
+ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"),
+ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+
+ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+ARIZONA_MIXER_WIDGETS(Mic, "Mic"),
+ARIZONA_MIXER_WIDGETS(Noise, "Noise"),
+
+ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"),
+ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+ARIZONA_MIXER_WIDGETS(OUT2L, "HPOUT2L"),
+ARIZONA_MIXER_WIDGETS(OUT2R, "HPOUT2R"),
+ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"),
+ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
+ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+
+ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
+ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
+
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+WM_ADSP2("DSP1", 0),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("EPOUTN"),
+SND_SOC_DAPM_OUTPUT("EPOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC", "AEC Loopback" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2L PGA" }, \
+ { name, "IN2R", "IN2R PGA" }, \
+ { name, "IN3L", "IN3L PGA" }, \
+ { name, "IN3R", "IN3R PGA" }, \
+ { name, "Mic Mute Mixer", "Mic Mute Mixer" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ASRC1L", "ASRC1L" }, \
+ { name, "ASRC1R", "ASRC1R" }, \
+ { name, "ASRC2L", "ASRC2L" }, \
+ { name, "ASRC2R", "ASRC2R" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }
+
+static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "AIF3 Capture", NULL, "DBVDD3" },
+ { "AIF3 Playback", NULL, "DBVDD3" },
+
+ { "OUT1L", NULL, "CPVDD" },
+ { "OUT1R", NULL, "CPVDD" },
+ { "OUT2L", NULL, "CPVDD" },
+ { "OUT2R", NULL, "CPVDD" },
+ { "OUT3L", NULL, "CPVDD" },
+
+ { "OUT4L", NULL, "SPKVDDL" },
+ { "OUT4R", NULL, "SPKVDDR" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT2L", NULL, "SYSCLK" },
+ { "OUT2R", NULL, "SYSCLK" },
+ { "OUT3L", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+ { "OUT4R", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+ { "MICBIAS3", NULL, "MICVDD" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+ { "AIF1 Capture", NULL, "AIF1TX7" },
+ { "AIF1 Capture", NULL, "AIF1TX8" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+ { "AIF1RX7", NULL, "AIF1 Playback" },
+ { "AIF1RX8", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
+
+ { "IN1L PGA", NULL, "IN1L" },
+ { "IN1R PGA", NULL, "IN1R" },
+
+ { "IN2L PGA", NULL, "IN2L" },
+ { "IN2R PGA", NULL, "IN2R" },
+
+ { "IN3L PGA", NULL, "IN3L" },
+ { "IN3R PGA", NULL, "IN3R" },
+
+ ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
+ ARIZONA_MIXER_ROUTES("OUT2R", "HPOUT2R"),
+ ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"),
+
+ ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
+ ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"),
+ ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+ ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+ ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+ ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+
+ ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
+ ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
+ ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
+ ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
+ ARIZONA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"),
+
+ ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"),
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
+
+ ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
+
+ ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ ARIZONA_DSP_ROUTES("DSP1"),
+
+ { "AEC Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+
+ { "AEC Loopback", "HPOUT2L", "OUT2L" },
+ { "AEC Loopback", "HPOUT2R", "OUT2R" },
+ { "HPOUT2L", NULL, "OUT2L" },
+ { "HPOUT2R", NULL, "OUT2R" },
+
+ { "AEC Loopback", "EPOUT", "OUT3L" },
+ { "EPOUTN", NULL, "OUT3L" },
+ { "EPOUTP", NULL, "OUT3L" },
+
+ { "AEC Loopback", "SPKOUTL", "OUT4L" },
+ { "SPKOUTLN", NULL, "OUT4L" },
+ { "SPKOUTLP", NULL, "OUT4L" },
+
+ { "AEC Loopback", "SPKOUTR", "OUT4R" },
+ { "SPKOUTRN", NULL, "OUT4R" },
+ { "SPKOUTRP", NULL, "OUT4R" },
+
+ { "AEC Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1L" },
+ { "DRC1 Signal Activity", NULL, "DRC1R" },
+};
+
+static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm5102_priv *wm5102 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fll_id) {
+ case WM5102_FLL1:
+ return arizona_set_fll(&wm5102->fll[0], source, Fref, Fout);
+ case WM5102_FLL2:
+ return arizona_set_fll(&wm5102->fll[1], source, Fref, Fout);
+ case WM5102_FLL1_REFCLK:
+ return arizona_set_fll_refclk(&wm5102->fll[0], source, Fref,
+ Fout);
+ case WM5102_FLL2_REFCLK:
+ return arizona_set_fll_refclk(&wm5102->fll[1], source, Fref,
+ Fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+#define WM5102_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM5102_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 wm5102_dai[] = {
+ {
+ .name = "wm5102-aif1",
+ .id = 1,
+ .base = ARIZONA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm5102-aif2",
+ .id = 2,
+ .base = ARIZONA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm5102-aif3",
+ .id = 3,
+ .base = ARIZONA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm5102-slim1",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm5102-slim2",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm5102-slim3",
+ .id = 6,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+};
+
+static int wm5102_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 2);
+ if (ret != 0)
+ return ret;
+
+ arizona_init_spk(codec);
+ arizona_init_gpio(codec);
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+
+ priv->core.arizona->dapm = &codec->dapm;
+
+ return 0;
+}
+
+static int wm5102_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->core.arizona->dapm = NULL;
+
+ return 0;
+}
+
+#define WM5102_DIG_VU 0x0200
+
+static unsigned int wm5102_digital_vu[] = {
+ ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R,
+ ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R,
+ ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R,
+ ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static struct regmap *wm5102_get_regmap(struct device *dev)
+{
+ struct wm5102_priv *priv = dev_get_drvdata(dev);
+
+ return priv->core.arizona->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm5102 = {
+ .probe = wm5102_codec_probe,
+ .remove = wm5102_codec_remove,
+ .get_regmap = wm5102_get_regmap,
+
+ .idle_bias_off = true,
+
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm5102_set_fll,
+
+ .controls = wm5102_snd_controls,
+ .num_controls = ARRAY_SIZE(wm5102_snd_controls),
+ .dapm_widgets = wm5102_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm5102_dapm_widgets),
+ .dapm_routes = wm5102_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm5102_dapm_routes),
+};
+
+static int wm5102_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct wm5102_priv *wm5102;
+ int i, ret;
+
+ wm5102 = devm_kzalloc(&pdev->dev, sizeof(struct wm5102_priv),
+ GFP_KERNEL);
+ if (wm5102 == NULL)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, wm5102);
+
+ mutex_init(&arizona->dac_comp_lock);
+
+ wm5102->core.arizona = arizona;
+ wm5102->core.num_inputs = 6;
+
+ wm5102->core.adsp[0].part = "wm5102";
+ wm5102->core.adsp[0].num = 1;
+ wm5102->core.adsp[0].type = WMFW_ADSP2;
+ wm5102->core.adsp[0].base = ARIZONA_DSP1_CONTROL_1;
+ wm5102->core.adsp[0].dev = arizona->dev;
+ wm5102->core.adsp[0].regmap = arizona->regmap;
+ wm5102->core.adsp[0].mem = wm5102_dsp1_regions;
+ wm5102->core.adsp[0].num_mems = ARRAY_SIZE(wm5102_dsp1_regions);
+
+ ret = wm_adsp2_init(&wm5102->core.adsp[0], true);
+ if (ret != 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(wm5102->fll); i++)
+ wm5102->fll[i].vco_mult = 1;
+
+ arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+ &wm5102->fll[0]);
+ arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+ &wm5102->fll[1]);
+
+ /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2,
+ ARIZONA_SAMPLE_RATE_2_MASK, 0x11);
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3,
+ ARIZONA_SAMPLE_RATE_3_MASK, 0x12);
+
+ for (i = 0; i < ARRAY_SIZE(wm5102_dai); i++)
+ arizona_init_dai(&wm5102->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(wm5102_digital_vu); i++)
+ regmap_update_bits(arizona->regmap, wm5102_digital_vu[i],
+ WM5102_DIG_VU, WM5102_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102,
+ wm5102_dai, ARRAY_SIZE(wm5102_dai));
+}
+
+static int wm5102_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver wm5102_codec_driver = {
+ .driver = {
+ .name = "wm5102-codec",
+ },
+ .probe = wm5102_probe,
+ .remove = wm5102_remove,
+};
+
+module_platform_driver(wm5102_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM5102 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm5102-codec");
diff --git a/kernel/sound/soc/codecs/wm5102.h b/kernel/sound/soc/codecs/wm5102.h
new file mode 100644
index 000000000..adb38040f
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm5102.h
@@ -0,0 +1,23 @@
+/*
+ * wm5102.h -- WM5102 ALSA SoC Audio driver
+ *
+ * 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 _WM5102_H
+#define _WM5102_H
+
+#include "arizona.h"
+
+#define WM5102_FLL1 1
+#define WM5102_FLL2 2
+#define WM5102_FLL1_REFCLK 3
+#define WM5102_FLL2_REFCLK 4
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm5110.c b/kernel/sound/soc/codecs/wm5110.c
new file mode 100644
index 000000000..fbaeddb3e
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm5110.c
@@ -0,0 +1,1757 @@
+/*
+ * wm5110.c -- WM5110 ALSA SoC Audio driver
+ *
+ * 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/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 "wm5110.h"
+
+#define WM5110_NUM_ADSP 4
+
+struct wm5110_priv {
+ struct arizona_priv core;
+ struct arizona_fll fll[2];
+};
+
+static const struct wm_adsp_region wm5110_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x100000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x180000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x190000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x1a8000 },
+};
+
+static const struct wm_adsp_region wm5110_dsp2_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x200000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x280000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x290000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x2a8000 },
+};
+
+static const struct wm_adsp_region wm5110_dsp3_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x300000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x380000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x390000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x3a8000 },
+};
+
+static const struct wm_adsp_region wm5110_dsp4_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x400000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x480000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x490000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x4a8000 },
+};
+
+static const struct wm_adsp_region *wm5110_dsp_regions[] = {
+ wm5110_dsp1_regions,
+ wm5110_dsp2_regions,
+ wm5110_dsp3_regions,
+ wm5110_dsp4_regions,
+};
+
+static const struct reg_default wm5110_sysclk_revd_patch[] = {
+ { 0x3093, 0x1001 },
+ { 0x30E3, 0x1301 },
+ { 0x3133, 0x1201 },
+ { 0x3183, 0x1501 },
+ { 0x31D3, 0x1401 },
+ { 0x0049, 0x01ea },
+ { 0x004a, 0x01f2 },
+ { 0x0057, 0x01e7 },
+ { 0x0058, 0x01fb },
+ { 0x33ce, 0xc4f5 },
+ { 0x33cf, 0x1361 },
+ { 0x33d0, 0x0402 },
+ { 0x33d1, 0x4700 },
+ { 0x33d2, 0x026d },
+ { 0x33d3, 0xff00 },
+ { 0x33d4, 0x026d },
+ { 0x33d5, 0x0101 },
+ { 0x33d6, 0xc4f5 },
+ { 0x33d7, 0x0361 },
+ { 0x33d8, 0x0402 },
+ { 0x33d9, 0x6701 },
+ { 0x33da, 0xc4f5 },
+ { 0x33db, 0x136f },
+ { 0x33dc, 0xc4f5 },
+ { 0x33dd, 0x134f },
+ { 0x33de, 0xc4f5 },
+ { 0x33df, 0x131f },
+ { 0x33e0, 0x026d },
+ { 0x33e1, 0x4f01 },
+ { 0x33e2, 0x026d },
+ { 0x33e3, 0xf100 },
+ { 0x33e4, 0x026d },
+ { 0x33e5, 0x0001 },
+ { 0x33e6, 0xc4f5 },
+ { 0x33e7, 0x0361 },
+ { 0x33e8, 0x0402 },
+ { 0x33e9, 0x6601 },
+ { 0x33ea, 0xc4f5 },
+ { 0x33eb, 0x136f },
+ { 0x33ec, 0xc4f5 },
+ { 0x33ed, 0x134f },
+ { 0x33ee, 0xc4f5 },
+ { 0x33ef, 0x131f },
+ { 0x33f0, 0x026d },
+ { 0x33f1, 0x4e01 },
+ { 0x33f2, 0x026d },
+ { 0x33f3, 0xf000 },
+ { 0x33f6, 0xc4f5 },
+ { 0x33f7, 0x1361 },
+ { 0x33f8, 0x0402 },
+ { 0x33f9, 0x4600 },
+ { 0x33fa, 0x026d },
+ { 0x33fb, 0xfe00 },
+};
+
+static int wm5110_sysclk_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 regmap *regmap = arizona->regmap;
+ const struct reg_default *patch = NULL;
+ int i, patch_size;
+
+ switch (arizona->rev) {
+ case 3:
+ patch = wm5110_sysclk_revd_patch;
+ patch_size = ARRAY_SIZE(wm5110_sysclk_revd_patch);
+ break;
+ default:
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (patch)
+ for (i = 0; i < patch_size; i++)
+ regmap_write_async(regmap, patch[i].reg,
+ patch[i].def);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
+static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+
+#define WM5110_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
+
+static const struct snd_kcontrol_new wm5110_snd_controls[] = {
+SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
+SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]),
+SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+ ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+ ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+ ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum),
+
+SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", ARIZONA_IN2R_CONTROL,
+ ARIZONA_IN2R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3L HPF Switch", ARIZONA_IN3L_CONTROL,
+ ARIZONA_IN3L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN3R HPF Switch", ARIZONA_IN3R_CONTROL,
+ ARIZONA_IN3R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4L HPF Switch", ARIZONA_IN4L_CONTROL,
+ ARIZONA_IN4L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN4R HPF Switch", ARIZONA_IN4R_CONTROL,
+ ARIZONA_IN4R_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+ ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+ ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R,
+ ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN4L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L,
+ ARIZONA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R,
+ ARIZONA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+
+SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
+
+ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
+SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0),
+SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
+SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0),
+SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
+SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0),
+SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
+SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0),
+SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+ARIZONA_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),
+
+SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+
+SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
+
+SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+SOC_ENUM("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),
+
+ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP2R", ARIZONA_DSP2RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP3L", ARIZONA_DSP3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP3R", ARIZONA_DSP3RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP4L", ARIZONA_DSP4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP4R", ARIZONA_DSP4RMIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv),
+
+ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT2L", ARIZONA_OUT2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT2R", ARIZONA_OUT2RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT3L", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT3R", ARIZONA_OUT3RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT2L", ARIZONA_OUT6LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT2R", ARIZONA_OUT6RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 SC Protect Switch", ARIZONA_HP1_SHORT_CIRCUIT_CTRL,
+ ARIZONA_HP1_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT2 SC Protect Switch", ARIZONA_HP2_SHORT_CIRCUIT_CTRL,
+ ARIZONA_HP2_SC_ENA_SHIFT, 1, 0),
+SOC_SINGLE("HPOUT3 SC Protect Switch", ARIZONA_HP3_SHORT_CIRCUIT_CTRL,
+ ARIZONA_HP3_SC_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
+ ARIZONA_OUT5_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_6L,
+ ARIZONA_OUT6_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("HPOUT3 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_DAC_DIGITAL_VOLUME_3R, ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_6L,
+ ARIZONA_DAC_DIGITAL_VOLUME_6R, ARIZONA_OUT6L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("HPOUT3 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_DAC_DIGITAL_VOLUME_3R, ARIZONA_OUT3L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_6L,
+ ARIZONA_DAC_DIGITAL_VOLUME_6R, ARIZONA_OUT6L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
+ ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT,
+ ARIZONA_SPK2R_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0),
+SOC_DOUBLE("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0),
+SOC_DOUBLE("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0),
+
+SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+WM5110_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+WM5110_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+WM5110_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L),
+WM5110_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R),
+WM5110_NG_SRC("HPOUT3L", ARIZONA_NOISE_GATE_SELECT_3L),
+WM5110_NG_SRC("HPOUT3R", ARIZONA_NOISE_GATE_SELECT_3R),
+WM5110_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L),
+WM5110_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R),
+WM5110_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L),
+WM5110_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R),
+WM5110_NG_SRC("SPKDAT2L", ARIZONA_NOISE_GATE_SELECT_6L),
+WM5110_NG_SRC("SPKDAT2R", ARIZONA_NOISE_GATE_SELECT_6R),
+
+ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
+};
+
+ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC2L, ARIZONA_DRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC2R, ARIZONA_DRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE);
+ARIZONA_DSP_AUX_ENUMS(DSP1, ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DSP2L, ARIZONA_DSP2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DSP2R, ARIZONA_DSP2RMIX_INPUT_1_SOURCE);
+ARIZONA_DSP_AUX_ENUMS(DSP2, ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DSP3L, ARIZONA_DSP3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DSP3R, ARIZONA_DSP3RMIX_INPUT_1_SOURCE);
+ARIZONA_DSP_AUX_ENUMS(DSP3, ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DSP4L, ARIZONA_DSP4LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DSP4R, ARIZONA_DSP4RMIX_INPUT_1_SOURCE);
+ARIZONA_DSP_AUX_ENUMS(DSP4, ARIZONA_DSP4AUX1MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3L, ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3R, ARIZONA_OUT3RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT2L, ARIZONA_OUT6LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT2R, ARIZONA_OUT6RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT3, ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT4, ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC3, ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC4, ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC3INT1, ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3INT2, ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3INT3, ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3INT4, ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC3DEC1, ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3DEC2, ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3DEC3, ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3DEC4, ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE);
+
+static const char *wm5110_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R",
+ "SPKOUTL", "SPKOUTR", "SPKDAT1L", "SPKDAT1R", "SPKDAT2L", "SPKDAT2R",
+};
+
+static const unsigned int wm5110_aec_loopback_values[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+};
+
+static const struct soc_enum wm5110_aec_loopback =
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(wm5110_aec_loopback_texts),
+ wm5110_aec_loopback_texts,
+ wm5110_aec_loopback_values);
+
+static const struct snd_kcontrol_new wm5110_aec_loopback_mux =
+ SOC_DAPM_ENUM("AEC Loopback", wm5110_aec_loopback);
+
+static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
+ 0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
+ ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
+ ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+SND_SOC_DAPM_INPUT("IN3L"),
+SND_SOC_DAPM_INPUT("IN3R"),
+SND_SOC_DAPM_INPUT("IN4L"),
+SND_SOC_DAPM_INPUT("IN4R"),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1,
+ ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+WM_ADSP2("DSP1", 0),
+WM_ADSP2("DSP2", 1),
+WM_ADSP2("DSP3", 2),
+WM_ADSP2("DSP4", 3),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT3", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT4", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC3", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC4", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3INT1", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT2", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT3", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT4", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_INT3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3DEC1", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC2", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC3", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC4", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_DEC3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &wm5110_aec_loopback_mux),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0,
+ 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, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0,
+ 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, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0,
+ 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, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
+ 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, 0,
+ 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),
+SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT3R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT6L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT6L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT6R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT6R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"),
+ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"),
+ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+ARIZONA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+ARIZONA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+ARIZONA_MIXER_WIDGETS(Mic, "Mic"),
+ARIZONA_MIXER_WIDGETS(Noise, "Noise"),
+
+ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"),
+ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+ARIZONA_MIXER_WIDGETS(OUT2L, "HPOUT2L"),
+ARIZONA_MIXER_WIDGETS(OUT2R, "HPOUT2R"),
+ARIZONA_MIXER_WIDGETS(OUT3L, "HPOUT3L"),
+ARIZONA_MIXER_WIDGETS(OUT3R, "HPOUT3R"),
+ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
+ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+ARIZONA_MIXER_WIDGETS(SPKDAT2L, "SPKDAT2L"),
+ARIZONA_MIXER_WIDGETS(SPKDAT2R, "SPKDAT2R"),
+
+ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"),
+ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"),
+
+ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
+ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
+
+ARIZONA_DSP_WIDGETS(DSP1, "DSP1"),
+ARIZONA_DSP_WIDGETS(DSP2, "DSP2"),
+ARIZONA_DSP_WIDGETS(DSP3, "DSP3"),
+ARIZONA_DSP_WIDGETS(DSP4, "DSP4"),
+
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+ARIZONA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"),
+ARIZONA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"),
+
+ARIZONA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"),
+ARIZONA_MUX_WIDGETS(ISRC3DEC3, "ISRC3DEC3"),
+ARIZONA_MUX_WIDGETS(ISRC3DEC4, "ISRC3DEC4"),
+
+ARIZONA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"),
+ARIZONA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"),
+ARIZONA_MUX_WIDGETS(ISRC3INT3, "ISRC3INT3"),
+ARIZONA_MUX_WIDGETS(ISRC3INT4, "ISRC3INT4"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("HPOUT3L"),
+SND_SOC_DAPM_OUTPUT("HPOUT3R"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+SND_SOC_DAPM_OUTPUT("SPKDAT2L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT2R"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC", "AEC Loopback" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2L PGA" }, \
+ { name, "IN2R", "IN2R PGA" }, \
+ { name, "IN3L", "IN3L PGA" }, \
+ { name, "IN3R", "IN3R PGA" }, \
+ { name, "IN4L", "IN4L PGA" }, \
+ { name, "IN4R", "IN4R PGA" }, \
+ { name, "Mic Mute Mixer", "Mic Mute Mixer" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF2RX3", "AIF2RX3" }, \
+ { name, "AIF2RX4", "AIF2RX4" }, \
+ { name, "AIF2RX5", "AIF2RX5" }, \
+ { name, "AIF2RX6", "AIF2RX6" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ASRC1L", "ASRC1L" }, \
+ { name, "ASRC1R", "ASRC1R" }, \
+ { name, "ASRC2L", "ASRC2L" }, \
+ { name, "ASRC2R", "ASRC2R" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2DEC3", "ISRC2DEC3" }, \
+ { name, "ISRC2DEC4", "ISRC2DEC4" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC2INT3", "ISRC2INT3" }, \
+ { name, "ISRC2INT4", "ISRC2INT4" }, \
+ { name, "ISRC3DEC1", "ISRC3DEC1" }, \
+ { name, "ISRC3DEC2", "ISRC3DEC2" }, \
+ { name, "ISRC3DEC3", "ISRC3DEC3" }, \
+ { name, "ISRC3DEC4", "ISRC3DEC4" }, \
+ { name, "ISRC3INT1", "ISRC3INT1" }, \
+ { name, "ISRC3INT2", "ISRC3INT2" }, \
+ { name, "ISRC3INT3", "ISRC3INT3" }, \
+ { name, "ISRC3INT4", "ISRC3INT4" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }, \
+ { name, "DSP2.1", "DSP2" }, \
+ { name, "DSP2.2", "DSP2" }, \
+ { name, "DSP2.3", "DSP2" }, \
+ { name, "DSP2.4", "DSP2" }, \
+ { name, "DSP2.5", "DSP2" }, \
+ { name, "DSP2.6", "DSP2" }, \
+ { name, "DSP3.1", "DSP3" }, \
+ { name, "DSP3.2", "DSP3" }, \
+ { name, "DSP3.3", "DSP3" }, \
+ { name, "DSP3.4", "DSP3" }, \
+ { name, "DSP3.5", "DSP3" }, \
+ { name, "DSP3.6", "DSP3" }, \
+ { name, "DSP4.1", "DSP4" }, \
+ { name, "DSP4.2", "DSP4" }, \
+ { name, "DSP4.3", "DSP4" }, \
+ { name, "DSP4.4", "DSP4" }, \
+ { name, "DSP4.5", "DSP4" }, \
+ { name, "DSP4.6", "DSP4" }
+
+static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "AIF3 Capture", NULL, "DBVDD3" },
+ { "AIF3 Playback", NULL, "DBVDD3" },
+
+ { "OUT1L", NULL, "CPVDD" },
+ { "OUT1R", NULL, "CPVDD" },
+ { "OUT2L", NULL, "CPVDD" },
+ { "OUT2R", NULL, "CPVDD" },
+ { "OUT3L", NULL, "CPVDD" },
+ { "OUT3R", NULL, "CPVDD" },
+
+ { "OUT4L", NULL, "SPKVDDL" },
+ { "OUT4R", NULL, "SPKVDDR" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT2L", NULL, "SYSCLK" },
+ { "OUT2R", NULL, "SYSCLK" },
+ { "OUT3L", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+ { "OUT4R", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+ { "OUT6L", NULL, "SYSCLK" },
+ { "OUT6R", NULL, "SYSCLK" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+ { "IN4L", NULL, "SYSCLK" },
+ { "IN4R", NULL, "SYSCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+ { "MICBIAS3", NULL, "MICVDD" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+ { "AIF1 Capture", NULL, "AIF1TX7" },
+ { "AIF1 Capture", NULL, "AIF1TX8" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+ { "AIF1RX7", NULL, "AIF1 Playback" },
+ { "AIF1RX8", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+ { "AIF2 Capture", NULL, "AIF2TX3" },
+ { "AIF2 Capture", NULL, "AIF2TX4" },
+ { "AIF2 Capture", NULL, "AIF2TX5" },
+ { "AIF2 Capture", NULL, "AIF2TX6" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+ { "AIF2RX3", NULL, "AIF2 Playback" },
+ { "AIF2RX4", NULL, "AIF2 Playback" },
+ { "AIF2RX5", NULL, "AIF2 Playback" },
+ { "AIF2RX6", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
+
+ { "IN1L PGA", NULL, "IN1L" },
+ { "IN1R PGA", NULL, "IN1R" },
+
+ { "IN2L PGA", NULL, "IN2L" },
+ { "IN2R PGA", NULL, "IN2R" },
+
+ { "IN3L PGA", NULL, "IN3L" },
+ { "IN3R PGA", NULL, "IN3R" },
+
+ { "IN4L PGA", NULL, "IN4L" },
+ { "IN4R PGA", NULL, "IN4R" },
+
+ ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
+ ARIZONA_MIXER_ROUTES("OUT2R", "HPOUT2R"),
+ ARIZONA_MIXER_ROUTES("OUT3L", "HPOUT3L"),
+ ARIZONA_MIXER_ROUTES("OUT3R", "HPOUT3R"),
+
+ ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
+ ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"),
+ ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+ ARIZONA_MIXER_ROUTES("OUT6L", "SPKDAT2L"),
+ ARIZONA_MIXER_ROUTES("OUT6R", "SPKDAT2R"),
+
+ ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+ ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+ ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+ ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+ ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+ ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"),
+ ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"),
+
+ ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
+ ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
+ ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
+ ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
+ ARIZONA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"),
+ ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"),
+ ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"),
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
+
+ ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
+
+ ARIZONA_DSP_ROUTES("DSP1"),
+ ARIZONA_DSP_ROUTES("DSP2"),
+ ARIZONA_DSP_ROUTES("DSP3"),
+ ARIZONA_DSP_ROUTES("DSP4"),
+
+ ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+ ARIZONA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"),
+ ARIZONA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"),
+
+ ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"),
+
+ ARIZONA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"),
+ ARIZONA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"),
+ ARIZONA_MUX_ROUTES("ISRC3INT3", "ISRC3INT3"),
+ ARIZONA_MUX_ROUTES("ISRC3INT4", "ISRC3INT4"),
+
+ ARIZONA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC3DEC3", "ISRC3DEC3"),
+ ARIZONA_MUX_ROUTES("ISRC3DEC4", "ISRC3DEC4"),
+
+ { "AEC Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+
+ { "AEC Loopback", "HPOUT2L", "OUT2L" },
+ { "AEC Loopback", "HPOUT2R", "OUT2R" },
+ { "HPOUT2L", NULL, "OUT2L" },
+ { "HPOUT2R", NULL, "OUT2R" },
+
+ { "AEC Loopback", "HPOUT3L", "OUT3L" },
+ { "AEC Loopback", "HPOUT3R", "OUT3R" },
+ { "HPOUT3L", NULL, "OUT3L" },
+ { "HPOUT3R", NULL, "OUT3R" },
+
+ { "AEC Loopback", "SPKOUTL", "OUT4L" },
+ { "SPKOUTLN", NULL, "OUT4L" },
+ { "SPKOUTLP", NULL, "OUT4L" },
+
+ { "AEC Loopback", "SPKOUTR", "OUT4R" },
+ { "SPKOUTRN", NULL, "OUT4R" },
+ { "SPKOUTRP", NULL, "OUT4R" },
+
+ { "AEC Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ { "AEC Loopback", "SPKDAT2L", "OUT6L" },
+ { "AEC Loopback", "SPKDAT2R", "OUT6R" },
+ { "SPKDAT2L", NULL, "OUT6L" },
+ { "SPKDAT2R", NULL, "OUT6R" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1L" },
+ { "DRC1 Signal Activity", NULL, "DRC1R" },
+ { "DRC2 Signal Activity", NULL, "DRC2L" },
+ { "DRC2 Signal Activity", NULL, "DRC2R" },
+};
+
+static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fll_id) {
+ case WM5110_FLL1:
+ return arizona_set_fll(&wm5110->fll[0], source, Fref, Fout);
+ case WM5110_FLL2:
+ return arizona_set_fll(&wm5110->fll[1], source, Fref, Fout);
+ case WM5110_FLL1_REFCLK:
+ return arizona_set_fll_refclk(&wm5110->fll[0], source, Fref,
+ Fout);
+ case WM5110_FLL2_REFCLK:
+ return arizona_set_fll_refclk(&wm5110->fll[1], source, Fref,
+ Fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+#define WM5110_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM5110_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 wm5110_dai[] = {
+ {
+ .name = "wm5110-aif1",
+ .id = 1,
+ .base = ARIZONA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm5110-aif2",
+ .id = 2,
+ .base = ARIZONA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm5110-aif3",
+ .id = 3,
+ .base = ARIZONA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm5110-slim1",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm5110-slim2",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm5110-slim3",
+ .id = 6,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+};
+
+static int wm5110_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ priv->core.arizona->dapm = &codec->dapm;
+
+ arizona_init_spk(codec);
+ arizona_init_gpio(codec);
+ arizona_init_mono(codec);
+
+ ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 8);
+ if (ret != 0)
+ return ret;
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+
+ priv->core.arizona->dapm = &codec->dapm;
+
+ return 0;
+}
+
+static int wm5110_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->core.arizona->dapm = NULL;
+
+ return 0;
+}
+
+#define WM5110_DIG_VU 0x0200
+
+static unsigned int wm5110_digital_vu[] = {
+ ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R,
+ ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R,
+ ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_DAC_DIGITAL_VOLUME_3R,
+ ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R,
+ ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R,
+ ARIZONA_DAC_DIGITAL_VOLUME_6L,
+ ARIZONA_DAC_DIGITAL_VOLUME_6R,
+};
+
+static struct regmap *wm5110_get_regmap(struct device *dev)
+{
+ struct wm5110_priv *priv = dev_get_drvdata(dev);
+
+ return priv->core.arizona->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
+ .probe = wm5110_codec_probe,
+ .remove = wm5110_codec_remove,
+ .get_regmap = wm5110_get_regmap,
+
+ .idle_bias_off = true,
+
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm5110_set_fll,
+
+ .controls = wm5110_snd_controls,
+ .num_controls = ARRAY_SIZE(wm5110_snd_controls),
+ .dapm_widgets = wm5110_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm5110_dapm_widgets),
+ .dapm_routes = wm5110_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm5110_dapm_routes),
+};
+
+static int wm5110_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct wm5110_priv *wm5110;
+ int i, ret;
+
+ wm5110 = devm_kzalloc(&pdev->dev, sizeof(struct wm5110_priv),
+ GFP_KERNEL);
+ if (wm5110 == NULL)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, wm5110);
+
+ wm5110->core.arizona = arizona;
+ wm5110->core.num_inputs = 8;
+
+ for (i = 0; i < WM5110_NUM_ADSP; i++) {
+ wm5110->core.adsp[i].part = "wm5110";
+ wm5110->core.adsp[i].num = i + 1;
+ wm5110->core.adsp[i].type = WMFW_ADSP2;
+ wm5110->core.adsp[i].dev = arizona->dev;
+ wm5110->core.adsp[i].regmap = arizona->regmap;
+
+ wm5110->core.adsp[i].base = ARIZONA_DSP1_CONTROL_1
+ + (0x100 * i);
+ wm5110->core.adsp[i].mem = wm5110_dsp_regions[i];
+ wm5110->core.adsp[i].num_mems
+ = ARRAY_SIZE(wm5110_dsp1_regions);
+
+ ret = wm_adsp2_init(&wm5110->core.adsp[i], false);
+ if (ret != 0)
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm5110->fll); i++)
+ wm5110->fll[i].vco_mult = 3;
+
+ arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+ &wm5110->fll[0]);
+ arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+ &wm5110->fll[1]);
+
+ /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2,
+ ARIZONA_SAMPLE_RATE_2_MASK, 0x11);
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3,
+ ARIZONA_SAMPLE_RATE_3_MASK, 0x12);
+
+ for (i = 0; i < ARRAY_SIZE(wm5110_dai); i++)
+ arizona_init_dai(&wm5110->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(wm5110_digital_vu); i++)
+ regmap_update_bits(arizona->regmap, wm5110_digital_vu[i],
+ WM5110_DIG_VU, WM5110_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
+ wm5110_dai, ARRAY_SIZE(wm5110_dai));
+}
+
+static int wm5110_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver wm5110_codec_driver = {
+ .driver = {
+ .name = "wm5110-codec",
+ },
+ .probe = wm5110_probe,
+ .remove = wm5110_remove,
+};
+
+module_platform_driver(wm5110_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM5110 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm5110-codec");
diff --git a/kernel/sound/soc/codecs/wm5110.h b/kernel/sound/soc/codecs/wm5110.h
new file mode 100644
index 000000000..e6c0cd423
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm5110.h
@@ -0,0 +1,23 @@
+/*
+ * wm5110.h -- WM5110 ALSA SoC Audio driver
+ *
+ * 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 _WM5110_H
+#define _WM5110_H
+
+#include "arizona.h"
+
+#define WM5110_FLL1 1
+#define WM5110_FLL2 2
+#define WM5110_FLL1_REFCLK 3
+#define WM5110_FLL2_REFCLK 4
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8350.c b/kernel/sound/soc/codecs/wm8350.c
new file mode 100644
index 000000000..c65e5a75f
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8350.c
@@ -0,0 +1,1632 @@
+/*
+ * wm8350.c -- WM8350 ALSA SoC audio driver
+ *
+ * Copyright (C) 2007-12 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wm8350/audio.h>
+#include <linux/mfd/wm8350/core.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/initval.h>
+#include <sound/tlv.h>
+#include <trace/events/asoc.h>
+
+#include "wm8350.h"
+
+#define WM8350_OUTn_0dB 0x39
+
+#define WM8350_RAMP_NONE 0
+#define WM8350_RAMP_UP 1
+#define WM8350_RAMP_DOWN 2
+
+/* We only include the analogue supplies here; the digital supplies
+ * need to be available well before this driver can be probed.
+ */
+static const char *supply_names[] = {
+ "AVDD",
+ "HPVDD",
+};
+
+struct wm8350_output {
+ u16 active;
+ u16 left_vol;
+ u16 right_vol;
+ u16 ramp;
+ u16 mute;
+};
+
+struct wm8350_jack_data {
+ struct snd_soc_jack *jack;
+ struct delayed_work work;
+ int report;
+ int short_report;
+};
+
+struct wm8350_data {
+ struct wm8350 *wm8350;
+ struct wm8350_output out1;
+ struct wm8350_output out2;
+ struct wm8350_jack_data hpl;
+ struct wm8350_jack_data hpr;
+ struct wm8350_jack_data mic;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+ int fll_freq_out;
+ int fll_freq_in;
+ struct delayed_work pga_work;
+};
+
+/*
+ * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown.
+ */
+static inline int wm8350_out1_ramp_step(struct wm8350_data *wm8350_data)
+{
+ struct wm8350_output *out1 = &wm8350_data->out1;
+ struct wm8350 *wm8350 = wm8350_data->wm8350;
+ int left_complete = 0, right_complete = 0;
+ u16 reg, val;
+
+ /* left channel */
+ reg = wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME);
+ val = (reg & WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+
+ if (out1->ramp == WM8350_RAMP_UP) {
+ /* ramp step up */
+ if (val < out1->left_vol) {
+ val++;
+ reg &= ~WM8350_OUT1L_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME,
+ reg | (val << WM8350_OUT1L_VOL_SHIFT));
+ } else
+ left_complete = 1;
+ } else if (out1->ramp == WM8350_RAMP_DOWN) {
+ /* ramp step down */
+ if (val > 0) {
+ val--;
+ reg &= ~WM8350_OUT1L_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME,
+ reg | (val << WM8350_OUT1L_VOL_SHIFT));
+ } else
+ left_complete = 1;
+ } else
+ return 1;
+
+ /* right channel */
+ reg = wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME);
+ val = (reg & WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+ if (out1->ramp == WM8350_RAMP_UP) {
+ /* ramp step up */
+ if (val < out1->right_vol) {
+ val++;
+ reg &= ~WM8350_OUT1R_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME,
+ reg | (val << WM8350_OUT1R_VOL_SHIFT));
+ } else
+ right_complete = 1;
+ } else if (out1->ramp == WM8350_RAMP_DOWN) {
+ /* ramp step down */
+ if (val > 0) {
+ val--;
+ reg &= ~WM8350_OUT1R_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME,
+ reg | (val << WM8350_OUT1R_VOL_SHIFT));
+ } else
+ right_complete = 1;
+ }
+
+ /* only hit the update bit if either volume has changed this step */
+ if (!left_complete || !right_complete)
+ wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU);
+
+ return left_complete & right_complete;
+}
+
+/*
+ * Ramp OUT2 PGA volume to minimise pops at stream startup and shutdown.
+ */
+static inline int wm8350_out2_ramp_step(struct wm8350_data *wm8350_data)
+{
+ struct wm8350_output *out2 = &wm8350_data->out2;
+ struct wm8350 *wm8350 = wm8350_data->wm8350;
+ int left_complete = 0, right_complete = 0;
+ u16 reg, val;
+
+ /* left channel */
+ reg = wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME);
+ val = (reg & WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+ if (out2->ramp == WM8350_RAMP_UP) {
+ /* ramp step up */
+ if (val < out2->left_vol) {
+ val++;
+ reg &= ~WM8350_OUT2L_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME,
+ reg | (val << WM8350_OUT1L_VOL_SHIFT));
+ } else
+ left_complete = 1;
+ } else if (out2->ramp == WM8350_RAMP_DOWN) {
+ /* ramp step down */
+ if (val > 0) {
+ val--;
+ reg &= ~WM8350_OUT2L_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME,
+ reg | (val << WM8350_OUT1L_VOL_SHIFT));
+ } else
+ left_complete = 1;
+ } else
+ return 1;
+
+ /* right channel */
+ reg = wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME);
+ val = (reg & WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+ if (out2->ramp == WM8350_RAMP_UP) {
+ /* ramp step up */
+ if (val < out2->right_vol) {
+ val++;
+ reg &= ~WM8350_OUT2R_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME,
+ reg | (val << WM8350_OUT1R_VOL_SHIFT));
+ } else
+ right_complete = 1;
+ } else if (out2->ramp == WM8350_RAMP_DOWN) {
+ /* ramp step down */
+ if (val > 0) {
+ val--;
+ reg &= ~WM8350_OUT2R_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME,
+ reg | (val << WM8350_OUT1R_VOL_SHIFT));
+ } else
+ right_complete = 1;
+ }
+
+ /* only hit the update bit if either volume has changed this step */
+ if (!left_complete || !right_complete)
+ wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2_VU);
+
+ return left_complete & right_complete;
+}
+
+/*
+ * This work ramps both output PGAs at stream start/stop time to
+ * minimise pop associated with DAPM power switching.
+ * It's best to enable Zero Cross when ramping occurs to minimise any
+ * zipper noises.
+ */
+static void wm8350_pga_work(struct work_struct *work)
+{
+ struct wm8350_data *wm8350_data =
+ container_of(work, struct wm8350_data, pga_work.work);
+ struct wm8350_output *out1 = &wm8350_data->out1,
+ *out2 = &wm8350_data->out2;
+ int i, out1_complete, out2_complete;
+
+ /* do we need to ramp at all ? */
+ if (out1->ramp == WM8350_RAMP_NONE && out2->ramp == WM8350_RAMP_NONE)
+ return;
+
+ /* PGA volumes have 6 bits of resolution to ramp */
+ for (i = 0; i <= 63; i++) {
+ out1_complete = 1, out2_complete = 1;
+ if (out1->ramp != WM8350_RAMP_NONE)
+ out1_complete = wm8350_out1_ramp_step(wm8350_data);
+ if (out2->ramp != WM8350_RAMP_NONE)
+ out2_complete = wm8350_out2_ramp_step(wm8350_data);
+
+ /* ramp finished ? */
+ if (out1_complete && out2_complete)
+ break;
+
+ /* we need to delay longer on the up ramp */
+ if (out1->ramp == WM8350_RAMP_UP ||
+ out2->ramp == WM8350_RAMP_UP) {
+ /* delay is longer over 0dB as increases are larger */
+ if (i >= WM8350_OUTn_0dB)
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (2));
+ else
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (1));
+ } else
+ udelay(50); /* doesn't matter if we delay longer */
+ }
+
+ out1->ramp = WM8350_RAMP_NONE;
+ out2->ramp = WM8350_RAMP_NONE;
+}
+
+/*
+ * WM8350 Controls
+ */
+
+static int pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
+ struct wm8350_output *out;
+
+ switch (w->shift) {
+ case 0:
+ case 1:
+ out = &wm8350_data->out1;
+ break;
+ case 2:
+ case 3:
+ out = &wm8350_data->out2;
+ break;
+
+ default:
+ WARN(1, "Invalid shift %d\n", w->shift);
+ return -1;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ out->ramp = WM8350_RAMP_UP;
+ out->active = 1;
+
+ schedule_delayed_work(&wm8350_data->pga_work,
+ msecs_to_jiffies(1));
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ out->ramp = WM8350_RAMP_DOWN;
+ out->active = 0;
+
+ schedule_delayed_work(&wm8350_data->pga_work,
+ msecs_to_jiffies(1));
+ break;
+ }
+
+ return 0;
+}
+
+static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8350_data *wm8350_priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8350_output *out = NULL;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int ret;
+ unsigned int reg = mc->reg;
+ u16 val;
+
+ /* For OUT1 and OUT2 we shadow the values and only actually write
+ * them out when active in order to ensure the amplifier comes on
+ * as quietly as possible. */
+ switch (reg) {
+ case WM8350_LOUT1_VOLUME:
+ out = &wm8350_priv->out1;
+ break;
+ case WM8350_LOUT2_VOLUME:
+ out = &wm8350_priv->out2;
+ break;
+ default:
+ break;
+ }
+
+ if (out) {
+ out->left_vol = ucontrol->value.integer.value[0];
+ out->right_vol = ucontrol->value.integer.value[1];
+ if (!out->active)
+ return 1;
+ }
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /* now hit the volume update bits (always bit 8) */
+ val = snd_soc_read(codec, reg);
+ snd_soc_write(codec, reg, val | WM8350_OUT1_VU);
+ return 1;
+}
+
+static int wm8350_get_volsw_2r(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8350_data *wm8350_priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8350_output *out1 = &wm8350_priv->out1;
+ struct wm8350_output *out2 = &wm8350_priv->out2;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+
+ /* If these are cached registers use the cache */
+ switch (reg) {
+ case WM8350_LOUT1_VOLUME:
+ ucontrol->value.integer.value[0] = out1->left_vol;
+ ucontrol->value.integer.value[1] = out1->right_vol;
+ return 0;
+
+ case WM8350_LOUT2_VOLUME:
+ ucontrol->value.integer.value[0] = out2->left_vol;
+ ucontrol->value.integer.value[1] = out2->right_vol;
+ return 0;
+
+ default:
+ break;
+ }
+
+ return snd_soc_get_volsw(kcontrol, ucontrol);
+}
+
+static const char *wm8350_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" };
+static const char *wm8350_dacmutem[] = { "Normal", "Soft" };
+static const char *wm8350_dacmutes[] = { "Fast", "Slow" };
+static const char *wm8350_adcfilter[] = { "None", "High Pass" };
+static const char *wm8350_adchp[] = { "44.1kHz", "8kHz", "16kHz", "32kHz" };
+static const char *wm8350_lr[] = { "Left", "Right" };
+
+static const struct soc_enum wm8350_enum[] = {
+ SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 4, 4, wm8350_deemp),
+ SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 0, 4, wm8350_pol),
+ SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 14, 2, wm8350_dacmutem),
+ SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 13, 2, wm8350_dacmutes),
+ SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 15, 2, wm8350_adcfilter),
+ SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 8, 4, wm8350_adchp),
+ SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 0, 4, wm8350_pol),
+ SOC_ENUM_SINGLE(WM8350_INPUT_MIXER_VOLUME, 15, 2, wm8350_lr),
+};
+
+static DECLARE_TLV_DB_SCALE(pre_amp_tlv, -1200, 3525, 0);
+static DECLARE_TLV_DB_SCALE(out_pga_tlv, -5700, 600, 0);
+static DECLARE_TLV_DB_SCALE(dac_pcm_tlv, -7163, 36, 1);
+static DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -12700, 50, 1);
+static DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 1);
+
+static const unsigned int capture_sd_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 12, TLV_DB_SCALE_ITEM(-3600, 300, 1),
+ 13, 15, TLV_DB_SCALE_ITEM(0, 0, 0),
+};
+
+static const struct snd_kcontrol_new wm8350_snd_controls[] = {
+ SOC_ENUM("Playback Deemphasis", wm8350_enum[0]),
+ SOC_ENUM("Playback DAC Inversion", wm8350_enum[1]),
+ SOC_DOUBLE_R_EXT_TLV("Playback PCM Volume",
+ WM8350_DAC_DIGITAL_VOLUME_L,
+ WM8350_DAC_DIGITAL_VOLUME_R,
+ 0, 255, 0, wm8350_get_volsw_2r,
+ wm8350_put_volsw_2r_vu, dac_pcm_tlv),
+ SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]),
+ SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]),
+ SOC_ENUM("Capture PCM Filter", wm8350_enum[4]),
+ SOC_ENUM("Capture PCM HP Filter", wm8350_enum[5]),
+ SOC_ENUM("Capture ADC Inversion", wm8350_enum[6]),
+ SOC_DOUBLE_R_EXT_TLV("Capture PCM Volume",
+ WM8350_ADC_DIGITAL_VOLUME_L,
+ WM8350_ADC_DIGITAL_VOLUME_R,
+ 0, 255, 0, wm8350_get_volsw_2r,
+ wm8350_put_volsw_2r_vu, adc_pcm_tlv),
+ SOC_DOUBLE_TLV("Capture Sidetone Volume",
+ WM8350_ADC_DIVIDER,
+ 8, 4, 15, 1, capture_sd_tlv),
+ SOC_DOUBLE_R_EXT_TLV("Capture Volume",
+ WM8350_LEFT_INPUT_VOLUME,
+ WM8350_RIGHT_INPUT_VOLUME,
+ 2, 63, 0, wm8350_get_volsw_2r,
+ wm8350_put_volsw_2r_vu, pre_amp_tlv),
+ SOC_DOUBLE_R("Capture ZC Switch",
+ WM8350_LEFT_INPUT_VOLUME,
+ WM8350_RIGHT_INPUT_VOLUME, 13, 1, 0),
+ SOC_SINGLE_TLV("Left Input Left Sidetone Volume",
+ WM8350_OUTPUT_LEFT_MIXER_VOLUME, 1, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Left Input Right Sidetone Volume",
+ WM8350_OUTPUT_LEFT_MIXER_VOLUME,
+ 5, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Left Input Bypass Volume",
+ WM8350_OUTPUT_LEFT_MIXER_VOLUME,
+ 9, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Right Input Left Sidetone Volume",
+ WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+ 1, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Right Input Right Sidetone Volume",
+ WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+ 5, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Right Input Bypass Volume",
+ WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+ 13, 7, 0, out_mix_tlv),
+ SOC_SINGLE("Left Input Mixer +20dB Switch",
+ WM8350_INPUT_MIXER_VOLUME_L, 0, 1, 0),
+ SOC_SINGLE("Right Input Mixer +20dB Switch",
+ WM8350_INPUT_MIXER_VOLUME_R, 0, 1, 0),
+ SOC_SINGLE_TLV("Out4 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME,
+ 1, 7, 0, out_mix_tlv),
+ SOC_DOUBLE_R_EXT_TLV("Out1 Playback Volume",
+ WM8350_LOUT1_VOLUME,
+ WM8350_ROUT1_VOLUME,
+ 2, 63, 0, wm8350_get_volsw_2r,
+ wm8350_put_volsw_2r_vu, out_pga_tlv),
+ SOC_DOUBLE_R("Out1 Playback ZC Switch",
+ WM8350_LOUT1_VOLUME,
+ WM8350_ROUT1_VOLUME, 13, 1, 0),
+ SOC_DOUBLE_R_EXT_TLV("Out2 Playback Volume",
+ WM8350_LOUT2_VOLUME,
+ WM8350_ROUT2_VOLUME,
+ 2, 63, 0, wm8350_get_volsw_2r,
+ wm8350_put_volsw_2r_vu, out_pga_tlv),
+ SOC_DOUBLE_R("Out2 Playback ZC Switch", WM8350_LOUT2_VOLUME,
+ WM8350_ROUT2_VOLUME, 13, 1, 0),
+ SOC_SINGLE("Out2 Right Invert Switch", WM8350_ROUT2_VOLUME, 10, 1, 0),
+ SOC_SINGLE_TLV("Out2 Beep Volume", WM8350_BEEP_VOLUME,
+ 5, 7, 0, out_mix_tlv),
+
+ SOC_DOUBLE_R("Out1 Playback Switch",
+ WM8350_LOUT1_VOLUME,
+ WM8350_ROUT1_VOLUME,
+ 14, 1, 1),
+ SOC_DOUBLE_R("Out2 Playback Switch",
+ WM8350_LOUT2_VOLUME,
+ WM8350_ROUT2_VOLUME,
+ 14, 1, 1),
+};
+
+/*
+ * DAPM Controls
+ */
+
+/* Left Playback Mixer */
+static const struct snd_kcontrol_new wm8350_left_play_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Playback Switch",
+ WM8350_LEFT_MIXER_CONTROL, 11, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch",
+ WM8350_LEFT_MIXER_CONTROL, 2, 1, 0),
+ SOC_DAPM_SINGLE("Right Playback Switch",
+ WM8350_LEFT_MIXER_CONTROL, 12, 1, 0),
+ SOC_DAPM_SINGLE("Left Sidetone Switch",
+ WM8350_LEFT_MIXER_CONTROL, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right Sidetone Switch",
+ WM8350_LEFT_MIXER_CONTROL, 1, 1, 0),
+};
+
+/* Right Playback Mixer */
+static const struct snd_kcontrol_new wm8350_right_play_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Playback Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 12, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 3, 1, 0),
+ SOC_DAPM_SINGLE("Left Playback Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 11, 1, 0),
+ SOC_DAPM_SINGLE("Left Sidetone Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right Sidetone Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 1, 1, 0),
+};
+
+/* Out4 Mixer */
+static const struct snd_kcontrol_new wm8350_out4_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Right Playback Switch",
+ WM8350_OUT4_MIXER_CONTROL, 12, 1, 0),
+ SOC_DAPM_SINGLE("Left Playback Switch",
+ WM8350_OUT4_MIXER_CONTROL, 11, 1, 0),
+ SOC_DAPM_SINGLE("Right Capture Switch",
+ WM8350_OUT4_MIXER_CONTROL, 9, 1, 0),
+ SOC_DAPM_SINGLE("Out3 Playback Switch",
+ WM8350_OUT4_MIXER_CONTROL, 2, 1, 0),
+ SOC_DAPM_SINGLE("Right Mixer Switch",
+ WM8350_OUT4_MIXER_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("Left Mixer Switch",
+ WM8350_OUT4_MIXER_CONTROL, 0, 1, 0),
+};
+
+/* Out3 Mixer */
+static const struct snd_kcontrol_new wm8350_out3_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left Playback Switch",
+ WM8350_OUT3_MIXER_CONTROL, 11, 1, 0),
+ SOC_DAPM_SINGLE("Left Capture Switch",
+ WM8350_OUT3_MIXER_CONTROL, 8, 1, 0),
+ SOC_DAPM_SINGLE("Out4 Playback Switch",
+ WM8350_OUT3_MIXER_CONTROL, 3, 1, 0),
+ SOC_DAPM_SINGLE("Left Mixer Switch",
+ WM8350_OUT3_MIXER_CONTROL, 0, 1, 0),
+};
+
+/* Left Input Mixer */
+static const struct snd_kcontrol_new wm8350_left_capt_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("L2 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME_L, 1, 7, 0, out_mix_tlv),
+ SOC_DAPM_SINGLE_TLV("L3 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME_L, 9, 7, 0, out_mix_tlv),
+ SOC_DAPM_SINGLE("PGA Capture Switch",
+ WM8350_LEFT_INPUT_VOLUME, 14, 1, 1),
+};
+
+/* Right Input Mixer */
+static const struct snd_kcontrol_new wm8350_right_capt_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("L2 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME_R, 5, 7, 0, out_mix_tlv),
+ SOC_DAPM_SINGLE_TLV("L3 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME_R, 13, 7, 0, out_mix_tlv),
+ SOC_DAPM_SINGLE("PGA Capture Switch",
+ WM8350_RIGHT_INPUT_VOLUME, 14, 1, 1),
+};
+
+/* Left Mic Mixer */
+static const struct snd_kcontrol_new wm8350_left_mic_mixer_controls[] = {
+ SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 0, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 2, 1, 0),
+};
+
+/* Right Mic Mixer */
+static const struct snd_kcontrol_new wm8350_right_mic_mixer_controls[] = {
+ SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 9, 1, 0),
+ SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 8, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 10, 1, 0),
+};
+
+/* Beep Switch */
+static const struct snd_kcontrol_new wm8350_beep_switch_controls =
+SOC_DAPM_SINGLE("Switch", WM8350_BEEP_VOLUME, 15, 1, 1);
+
+/* Out4 Capture Mux */
+static const struct snd_kcontrol_new wm8350_out4_capture_controls =
+SOC_DAPM_ENUM("Route", wm8350_enum[7]);
+
+static const struct snd_soc_dapm_widget wm8350_dapm_widgets[] = {
+
+ SND_SOC_DAPM_PGA("IN3R PGA", WM8350_POWER_MGMT_2, 11, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IN3L PGA", WM8350_POWER_MGMT_2, 10, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("Right Out2 PGA", WM8350_POWER_MGMT_3, 3, 0, NULL,
+ 0, pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Left Out2 PGA", WM8350_POWER_MGMT_3, 2, 0, NULL, 0,
+ pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Right Out1 PGA", WM8350_POWER_MGMT_3, 1, 0, NULL,
+ 0, pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Left Out1 PGA", WM8350_POWER_MGMT_3, 0, 0, NULL, 0,
+ pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MIXER("Right Capture Mixer", WM8350_POWER_MGMT_2,
+ 7, 0, &wm8350_right_capt_mixer_controls[0],
+ ARRAY_SIZE(wm8350_right_capt_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Capture Mixer", WM8350_POWER_MGMT_2,
+ 6, 0, &wm8350_left_capt_mixer_controls[0],
+ ARRAY_SIZE(wm8350_left_capt_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Out4 Mixer", WM8350_POWER_MGMT_2, 5, 0,
+ &wm8350_out4_mixer_controls[0],
+ ARRAY_SIZE(wm8350_out4_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Out3 Mixer", WM8350_POWER_MGMT_2, 4, 0,
+ &wm8350_out3_mixer_controls[0],
+ ARRAY_SIZE(wm8350_out3_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Playback Mixer", WM8350_POWER_MGMT_2, 1, 0,
+ &wm8350_right_play_mixer_controls[0],
+ ARRAY_SIZE(wm8350_right_play_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Playback Mixer", WM8350_POWER_MGMT_2, 0, 0,
+ &wm8350_left_play_mixer_controls[0],
+ ARRAY_SIZE(wm8350_left_play_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Mic Mixer", WM8350_POWER_MGMT_2, 8, 0,
+ &wm8350_left_mic_mixer_controls[0],
+ ARRAY_SIZE(wm8350_left_mic_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Mic Mixer", WM8350_POWER_MGMT_2, 9, 0,
+ &wm8350_right_mic_mixer_controls[0],
+ ARRAY_SIZE(wm8350_right_mic_mixer_controls)),
+
+ /* virtual mixer for Beep and Out2R */
+ SND_SOC_DAPM_MIXER("Out2 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("Beep", WM8350_POWER_MGMT_3, 7, 0,
+ &wm8350_beep_switch_controls),
+
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
+ WM8350_POWER_MGMT_4, 3, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture",
+ WM8350_POWER_MGMT_4, 2, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback",
+ WM8350_POWER_MGMT_4, 5, 0),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback",
+ WM8350_POWER_MGMT_4, 4, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8350_POWER_MGMT_1, 4, 0),
+
+ SND_SOC_DAPM_MUX("Out4 Capture Channel", SND_SOC_NOPM, 0, 0,
+ &wm8350_out4_capture_controls),
+
+ SND_SOC_DAPM_OUTPUT("OUT1R"),
+ SND_SOC_DAPM_OUTPUT("OUT1L"),
+ SND_SOC_DAPM_OUTPUT("OUT2R"),
+ SND_SOC_DAPM_OUTPUT("OUT2L"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("OUT4"),
+
+ SND_SOC_DAPM_INPUT("IN1RN"),
+ SND_SOC_DAPM_INPUT("IN1RP"),
+ SND_SOC_DAPM_INPUT("IN2R"),
+ SND_SOC_DAPM_INPUT("IN1LP"),
+ SND_SOC_DAPM_INPUT("IN1LN"),
+ SND_SOC_DAPM_INPUT("IN2L"),
+ SND_SOC_DAPM_INPUT("IN3R"),
+ SND_SOC_DAPM_INPUT("IN3L"),
+};
+
+static const struct snd_soc_dapm_route wm8350_dapm_routes[] = {
+
+ /* left playback mixer */
+ {"Left Playback Mixer", "Playback Switch", "Left DAC"},
+ {"Left Playback Mixer", "Left Bypass Switch", "IN3L PGA"},
+ {"Left Playback Mixer", "Right Playback Switch", "Right DAC"},
+ {"Left Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"},
+ {"Left Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"},
+
+ /* right playback mixer */
+ {"Right Playback Mixer", "Playback Switch", "Right DAC"},
+ {"Right Playback Mixer", "Right Bypass Switch", "IN3R PGA"},
+ {"Right Playback Mixer", "Left Playback Switch", "Left DAC"},
+ {"Right Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"},
+ {"Right Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"},
+
+ /* out4 playback mixer */
+ {"Out4 Mixer", "Right Playback Switch", "Right DAC"},
+ {"Out4 Mixer", "Left Playback Switch", "Left DAC"},
+ {"Out4 Mixer", "Right Capture Switch", "Right Capture Mixer"},
+ {"Out4 Mixer", "Out3 Playback Switch", "Out3 Mixer"},
+ {"Out4 Mixer", "Right Mixer Switch", "Right Playback Mixer"},
+ {"Out4 Mixer", "Left Mixer Switch", "Left Playback Mixer"},
+ {"OUT4", NULL, "Out4 Mixer"},
+
+ /* out3 playback mixer */
+ {"Out3 Mixer", "Left Playback Switch", "Left DAC"},
+ {"Out3 Mixer", "Left Capture Switch", "Left Capture Mixer"},
+ {"Out3 Mixer", "Left Mixer Switch", "Left Playback Mixer"},
+ {"Out3 Mixer", "Out4 Playback Switch", "Out4 Mixer"},
+ {"OUT3", NULL, "Out3 Mixer"},
+
+ /* out2 */
+ {"Right Out2 PGA", NULL, "Right Playback Mixer"},
+ {"Left Out2 PGA", NULL, "Left Playback Mixer"},
+ {"OUT2L", NULL, "Left Out2 PGA"},
+ {"OUT2R", NULL, "Right Out2 PGA"},
+
+ /* out1 */
+ {"Right Out1 PGA", NULL, "Right Playback Mixer"},
+ {"Left Out1 PGA", NULL, "Left Playback Mixer"},
+ {"OUT1L", NULL, "Left Out1 PGA"},
+ {"OUT1R", NULL, "Right Out1 PGA"},
+
+ /* ADCs */
+ {"Left ADC", NULL, "Left Capture Mixer"},
+ {"Right ADC", NULL, "Right Capture Mixer"},
+
+ /* Left capture mixer */
+ {"Left Capture Mixer", "L2 Capture Volume", "IN2L"},
+ {"Left Capture Mixer", "L3 Capture Volume", "IN3L PGA"},
+ {"Left Capture Mixer", "PGA Capture Switch", "Left Mic Mixer"},
+ {"Left Capture Mixer", NULL, "Out4 Capture Channel"},
+
+ /* Right capture mixer */
+ {"Right Capture Mixer", "L2 Capture Volume", "IN2R"},
+ {"Right Capture Mixer", "L3 Capture Volume", "IN3R PGA"},
+ {"Right Capture Mixer", "PGA Capture Switch", "Right Mic Mixer"},
+ {"Right Capture Mixer", NULL, "Out4 Capture Channel"},
+
+ /* L3 Inputs */
+ {"IN3L PGA", NULL, "IN3L"},
+ {"IN3R PGA", NULL, "IN3R"},
+
+ /* Left Mic mixer */
+ {"Left Mic Mixer", "INN Capture Switch", "IN1LN"},
+ {"Left Mic Mixer", "INP Capture Switch", "IN1LP"},
+ {"Left Mic Mixer", "IN2 Capture Switch", "IN2L"},
+
+ /* Right Mic mixer */
+ {"Right Mic Mixer", "INN Capture Switch", "IN1RN"},
+ {"Right Mic Mixer", "INP Capture Switch", "IN1RP"},
+ {"Right Mic Mixer", "IN2 Capture Switch", "IN2R"},
+
+ /* out 4 capture */
+ {"Out4 Capture Channel", NULL, "Out4 Mixer"},
+
+ /* Beep */
+ {"Beep", NULL, "IN3R PGA"},
+};
+
+static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
+ struct wm8350 *wm8350 = wm8350_data->wm8350;
+ u16 fll_4;
+
+ switch (clk_id) {
+ case WM8350_MCLK_SEL_MCLK:
+ wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_1,
+ WM8350_MCLK_SEL);
+ break;
+ case WM8350_MCLK_SEL_PLL_MCLK:
+ case WM8350_MCLK_SEL_PLL_DAC:
+ case WM8350_MCLK_SEL_PLL_ADC:
+ case WM8350_MCLK_SEL_PLL_32K:
+ wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_1,
+ WM8350_MCLK_SEL);
+ fll_4 = snd_soc_read(codec, WM8350_FLL_CONTROL_4) &
+ ~WM8350_FLL_CLK_SRC_MASK;
+ snd_soc_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id);
+ break;
+ }
+
+ /* MCLK direction */
+ if (dir == SND_SOC_CLOCK_OUT)
+ wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_MCLK_DIR);
+ else
+ wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_MCLK_DIR);
+
+ return 0;
+}
+
+static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 val;
+
+ switch (div_id) {
+ case WM8350_ADC_CLKDIV:
+ val = snd_soc_read(codec, WM8350_ADC_DIVIDER) &
+ ~WM8350_ADC_CLKDIV_MASK;
+ snd_soc_write(codec, WM8350_ADC_DIVIDER, val | div);
+ break;
+ case WM8350_DAC_CLKDIV:
+ val = snd_soc_read(codec, WM8350_DAC_CLOCK_CONTROL) &
+ ~WM8350_DAC_CLKDIV_MASK;
+ snd_soc_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div);
+ break;
+ case WM8350_BCLK_CLKDIV:
+ val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) &
+ ~WM8350_BCLK_DIV_MASK;
+ snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ break;
+ case WM8350_OPCLK_CLKDIV:
+ val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) &
+ ~WM8350_OPCLK_DIV_MASK;
+ snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ break;
+ case WM8350_SYS_CLKDIV:
+ val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) &
+ ~WM8350_MCLK_DIV_MASK;
+ snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ break;
+ case WM8350_DACLR_CLKDIV:
+ val = snd_soc_read(codec, WM8350_DAC_LR_RATE) &
+ ~WM8350_DACLRC_RATE_MASK;
+ snd_soc_write(codec, WM8350_DAC_LR_RATE, val | div);
+ break;
+ case WM8350_ADCLR_CLKDIV:
+ val = snd_soc_read(codec, WM8350_ADC_LR_RATE) &
+ ~WM8350_ADCLRC_RATE_MASK;
+ snd_soc_write(codec, WM8350_ADC_LR_RATE, val | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = snd_soc_read(codec, WM8350_AI_FORMATING) &
+ ~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | WM8350_AIF_FMT_MASK);
+ u16 master = snd_soc_read(codec, WM8350_AI_DAC_CONTROL) &
+ ~WM8350_BCLK_MSTR;
+ u16 dac_lrc = snd_soc_read(codec, WM8350_DAC_LR_RATE) &
+ ~WM8350_DACLRC_ENA;
+ u16 adc_lrc = snd_soc_read(codec, WM8350_ADC_LR_RATE) &
+ ~WM8350_ADCLRC_ENA;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ master |= WM8350_BCLK_MSTR;
+ dac_lrc |= WM8350_DACLRC_ENA;
+ adc_lrc |= WM8350_ADCLRC_ENA;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x2 << 8;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x1 << 8;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x3 << 8;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x3 << 8 | WM8350_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= WM8350_AIF_LRCLK_INV | WM8350_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= WM8350_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= WM8350_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8350_AI_FORMATING, iface);
+ snd_soc_write(codec, WM8350_AI_DAC_CONTROL, master);
+ snd_soc_write(codec, WM8350_DAC_LR_RATE, dac_lrc);
+ snd_soc_write(codec, WM8350_ADC_LR_RATE, adc_lrc);
+ return 0;
+}
+
+static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
+ struct wm8350 *wm8350 = wm8350_data->wm8350;
+ u16 iface = snd_soc_read(codec, WM8350_AI_FORMATING) &
+ ~WM8350_AIF_WL_MASK;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface |= 0x1 << 10;
+ break;
+ case 24:
+ iface |= 0x2 << 10;
+ break;
+ case 32:
+ iface |= 0x3 << 10;
+ break;
+ }
+
+ snd_soc_write(codec, WM8350_AI_FORMATING, iface);
+
+ /* The sloping stopband filter is recommended for use with
+ * lower sample rates to improve performance.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (params_rate(params) < 24000)
+ wm8350_set_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
+ WM8350_DAC_SB_FILT);
+ else
+ wm8350_clear_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
+ WM8350_DAC_SB_FILT);
+ }
+
+ return 0;
+}
+
+static int wm8350_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int val;
+
+ if (mute)
+ val = WM8350_DAC_MUTE_ENA;
+ else
+ val = 0;
+
+ snd_soc_update_bits(codec, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA, val);
+
+ return 0;
+}
+
+/* FLL divisors */
+struct _fll_div {
+ int div; /* FLL_OUTDIV */
+ int n;
+ int k;
+ int ratio; /* FLL_FRATIO */
+};
+
+/* The size in bits of the fll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static inline int fll_factors(struct _fll_div *fll_div, unsigned int input,
+ unsigned int output)
+{
+ u64 Kpart;
+ unsigned int t1, t2, K, Nmod;
+
+ if (output >= 2815250 && output <= 3125000)
+ fll_div->div = 0x4;
+ else if (output >= 5625000 && output <= 6250000)
+ fll_div->div = 0x3;
+ else if (output >= 11250000 && output <= 12500000)
+ fll_div->div = 0x2;
+ else if (output >= 22500000 && output <= 25000000)
+ fll_div->div = 0x1;
+ else {
+ printk(KERN_ERR "wm8350: fll freq %d out of range\n", output);
+ return -EINVAL;
+ }
+
+ if (input > 48000)
+ fll_div->ratio = 1;
+ else
+ fll_div->ratio = 8;
+
+ t1 = output * (1 << (fll_div->div + 1));
+ t2 = input * fll_div->ratio;
+
+ fll_div->n = t1 / t2;
+ Nmod = t1 % t2;
+
+ if (Nmod) {
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+ do_div(Kpart, t2);
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+ fll_div->k = K;
+ } else
+ fll_div->k = 0;
+
+ return 0;
+}
+
+static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8350 *wm8350 = priv->wm8350;
+ struct _fll_div fll_div;
+ int ret = 0;
+ u16 fll_1, fll_4;
+
+ if (freq_in == priv->fll_freq_in && freq_out == priv->fll_freq_out)
+ return 0;
+
+ /* power down FLL - we need to do this for reconfiguration */
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
+ WM8350_FLL_ENA | WM8350_FLL_OSC_ENA);
+
+ if (freq_out == 0 || freq_in == 0)
+ return ret;
+
+ ret = fll_factors(&fll_div, freq_in, freq_out);
+ if (ret < 0)
+ return ret;
+ dev_dbg(wm8350->dev,
+ "FLL in %u FLL out %u N 0x%x K 0x%x div %d ratio %d",
+ freq_in, freq_out, fll_div.n, fll_div.k, fll_div.div,
+ fll_div.ratio);
+
+ /* set up N.K & dividers */
+ fll_1 = snd_soc_read(codec, WM8350_FLL_CONTROL_1) &
+ ~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000);
+ snd_soc_write(codec, WM8350_FLL_CONTROL_1,
+ fll_1 | (fll_div.div << 8) | 0x50);
+ snd_soc_write(codec, WM8350_FLL_CONTROL_2,
+ (fll_div.ratio << 11) | (fll_div.
+ n & WM8350_FLL_N_MASK));
+ snd_soc_write(codec, WM8350_FLL_CONTROL_3, fll_div.k);
+ fll_4 = snd_soc_read(codec, WM8350_FLL_CONTROL_4) &
+ ~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF);
+ snd_soc_write(codec, WM8350_FLL_CONTROL_4,
+ fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) |
+ (fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0));
+
+ /* power FLL on */
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_OSC_ENA);
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA);
+
+ priv->fll_freq_out = freq_out;
+ priv->fll_freq_in = freq_in;
+
+ return 0;
+}
+
+static int wm8350_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8350 *wm8350 = priv->wm8350;
+ struct wm8350_audio_platform_data *platform =
+ wm8350->codec.platform_data;
+ u16 pm1;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1 | WM8350_VMID_50K |
+ platform->codec_current_on << 14);
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1);
+ pm1 &= ~WM8350_VMID_MASK;
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1 | WM8350_VMID_50K);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret != 0)
+ return ret;
+
+ /* Enable the system clock */
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4,
+ WM8350_SYSCLK_ENA);
+
+ /* mute DAC & outputs */
+ wm8350_set_bits(wm8350, WM8350_DAC_MUTE,
+ WM8350_DAC_MUTE_ENA);
+
+ /* discharge cap memory */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+ platform->dis_out1 |
+ (platform->dis_out2 << 2) |
+ (platform->dis_out3 << 4) |
+ (platform->dis_out4 << 6));
+
+ /* wait for discharge */
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (platform->
+ cap_discharge_msecs));
+
+ /* enable antipop */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+ (platform->vmid_s_curve << 8));
+
+ /* ramp up vmid */
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ (platform->
+ codec_current_charge << 14) |
+ WM8350_VMID_5K | WM8350_VMIDEN |
+ WM8350_VBUFEN);
+
+ /* wait for vmid */
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (platform->
+ vmid_charge_msecs));
+
+ /* turn on vmid 300k */
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+ pm1 |= WM8350_VMID_300K |
+ (platform->codec_current_standby << 14);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1);
+
+
+ /* enable analogue bias */
+ pm1 |= WM8350_BIASEN;
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+ /* disable antipop */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0);
+
+ } else {
+ /* turn on vmid 300k and reduce current */
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1 | WM8350_VMID_300K |
+ (platform->
+ codec_current_standby << 14));
+
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+
+ /* mute DAC & enable outputs */
+ wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_3,
+ WM8350_OUT1L_ENA | WM8350_OUT1R_ENA |
+ WM8350_OUT2L_ENA | WM8350_OUT2R_ENA);
+
+ /* enable anti pop S curve */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+ (platform->vmid_s_curve << 8));
+
+ /* turn off vmid */
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~WM8350_VMIDEN;
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+ /* wait */
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (platform->
+ vmid_discharge_msecs));
+
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+ (platform->vmid_s_curve << 8) |
+ platform->dis_out1 |
+ (platform->dis_out2 << 2) |
+ (platform->dis_out3 << 4) |
+ (platform->dis_out4 << 6));
+
+ /* turn off VBuf and drain */
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~(WM8350_VBUFEN | WM8350_VMID_MASK);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1 | WM8350_OUTPUT_DRAIN_EN);
+
+ /* wait */
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (platform->drain_msecs));
+
+ pm1 &= ~WM8350_BIASEN;
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+ /* disable anti-pop */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0);
+
+ wm8350_clear_bits(wm8350, WM8350_LOUT1_VOLUME,
+ WM8350_OUT1L_ENA);
+ wm8350_clear_bits(wm8350, WM8350_ROUT1_VOLUME,
+ WM8350_OUT1R_ENA);
+ wm8350_clear_bits(wm8350, WM8350_LOUT2_VOLUME,
+ WM8350_OUT2L_ENA);
+ wm8350_clear_bits(wm8350, WM8350_ROUT2_VOLUME,
+ WM8350_OUT2R_ENA);
+
+ /* disable clock gen */
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
+ WM8350_SYSCLK_ENA);
+
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static void wm8350_hp_work(struct wm8350_data *priv,
+ struct wm8350_jack_data *jack,
+ u16 mask)
+{
+ struct wm8350 *wm8350 = priv->wm8350;
+ u16 reg;
+ int report;
+
+ reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS);
+ if (reg & mask)
+ report = jack->report;
+ else
+ report = 0;
+
+ snd_soc_jack_report(jack->jack, report, jack->report);
+
+}
+
+static void wm8350_hpl_work(struct work_struct *work)
+{
+ struct wm8350_data *priv =
+ container_of(work, struct wm8350_data, hpl.work.work);
+
+ wm8350_hp_work(priv, &priv->hpl, WM8350_JACK_L_LVL);
+}
+
+static void wm8350_hpr_work(struct work_struct *work)
+{
+ struct wm8350_data *priv =
+ container_of(work, struct wm8350_data, hpr.work.work);
+
+ wm8350_hp_work(priv, &priv->hpr, WM8350_JACK_R_LVL);
+}
+
+static irqreturn_t wm8350_hpl_jack_handler(int irq, void *data)
+{
+ struct wm8350_data *priv = data;
+ struct wm8350 *wm8350 = priv->wm8350;
+
+#ifndef CONFIG_SND_SOC_WM8350_MODULE
+ trace_snd_soc_jack_irq("WM8350 HPL");
+#endif
+
+ if (device_may_wakeup(wm8350->dev))
+ pm_wakeup_event(wm8350->dev, 250);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &priv->hpl.work, msecs_to_jiffies(200));
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data)
+{
+ struct wm8350_data *priv = data;
+ struct wm8350 *wm8350 = priv->wm8350;
+
+#ifndef CONFIG_SND_SOC_WM8350_MODULE
+ trace_snd_soc_jack_irq("WM8350 HPR");
+#endif
+
+ if (device_may_wakeup(wm8350->dev))
+ pm_wakeup_event(wm8350->dev, 250);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &priv->hpr.work, msecs_to_jiffies(200));
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * wm8350_hp_jack_detect - Enable headphone jack detection.
+ *
+ * @codec: WM8350 codec
+ * @which: left or right jack detect signal
+ * @jack: jack to report detection events on
+ * @report: value to report
+ *
+ * Enables the headphone jack detection of the WM8350. If no report
+ * is specified then detection is disabled.
+ */
+int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
+ struct snd_soc_jack *jack, int report)
+{
+ struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8350 *wm8350 = priv->wm8350;
+ int ena;
+
+ switch (which) {
+ case WM8350_JDL:
+ priv->hpl.jack = jack;
+ priv->hpl.report = report;
+ ena = WM8350_JDL_ENA;
+ break;
+
+ case WM8350_JDR:
+ priv->hpr.jack = jack;
+ priv->hpr.report = report;
+ ena = WM8350_JDR_ENA;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (report) {
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
+ wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena);
+ } else {
+ wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, ena);
+ }
+
+ /* Sync status */
+ switch (which) {
+ case WM8350_JDL:
+ wm8350_hpl_jack_handler(0, priv);
+ break;
+ case WM8350_JDR:
+ wm8350_hpr_jack_handler(0, priv);
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_hp_jack_detect);
+
+static irqreturn_t wm8350_mic_handler(int irq, void *data)
+{
+ struct wm8350_data *priv = data;
+ struct wm8350 *wm8350 = priv->wm8350;
+ u16 reg;
+ int report = 0;
+
+#ifndef CONFIG_SND_SOC_WM8350_MODULE
+ trace_snd_soc_jack_irq("WM8350 mic");
+#endif
+
+ reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS);
+ if (reg & WM8350_JACK_MICSCD_LVL)
+ report |= priv->mic.short_report;
+ if (reg & WM8350_JACK_MICSD_LVL)
+ report |= priv->mic.report;
+
+ snd_soc_jack_report(priv->mic.jack, report,
+ priv->mic.report | priv->mic.short_report);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * wm8350_mic_jack_detect - Enable microphone jack detection.
+ *
+ * @codec: WM8350 codec
+ * @jack: jack to report detection events on
+ * @detect_report: value to report when presence detected
+ * @short_report: value to report when microphone short detected
+ *
+ * Enables the microphone jack detection of the WM8350. If both reports
+ * are specified as zero then detection is disabled.
+ */
+int wm8350_mic_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack,
+ int detect_report, int short_report)
+{
+ struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8350 *wm8350 = priv->wm8350;
+
+ priv->mic.jack = jack;
+ priv->mic.report = detect_report;
+ priv->mic.short_report = short_report;
+
+ if (detect_report || short_report) {
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_1,
+ WM8350_MIC_DET_ENA);
+ } else {
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_1,
+ WM8350_MIC_DET_ENA);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_mic_jack_detect);
+
+#define WM8350_RATES (SNDRV_PCM_RATE_8000_96000)
+
+#define WM8350_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8350_dai_ops = {
+ .hw_params = wm8350_pcm_hw_params,
+ .digital_mute = wm8350_mute,
+ .set_fmt = wm8350_set_dai_fmt,
+ .set_sysclk = wm8350_set_dai_sysclk,
+ .set_pll = wm8350_set_fll,
+ .set_clkdiv = wm8350_set_clkdiv,
+};
+
+static struct snd_soc_dai_driver wm8350_dai = {
+ .name = "wm8350-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8350_RATES,
+ .formats = WM8350_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8350_RATES,
+ .formats = WM8350_FORMATS,
+ },
+ .ops = &wm8350_dai_ops,
+};
+
+static int wm8350_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wm8350 *wm8350 = dev_get_platdata(codec->dev);
+ struct wm8350_data *priv;
+ struct wm8350_output *out1;
+ struct wm8350_output *out2;
+ int ret, i;
+
+ if (wm8350->codec.platform_data == NULL) {
+ dev_err(codec->dev, "No audio platform data supplied\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(codec->dev, sizeof(struct wm8350_data),
+ GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+ snd_soc_codec_set_drvdata(codec, priv);
+
+ priv->wm8350 = wm8350;
+
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ priv->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(wm8350->dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret != 0)
+ return ret;
+
+ /* Put the codec into reset if it wasn't already */
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+ INIT_DELAYED_WORK(&priv->pga_work, wm8350_pga_work);
+ INIT_DELAYED_WORK(&priv->hpl.work, wm8350_hpl_work);
+ INIT_DELAYED_WORK(&priv->hpr.work, wm8350_hpr_work);
+
+ /* Enable the codec */
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+ /* Enable robust clocking mode in ADC */
+ snd_soc_write(codec, WM8350_SECURITY, 0xa7);
+ snd_soc_write(codec, 0xde, 0x13);
+ snd_soc_write(codec, WM8350_SECURITY, 0);
+
+ /* read OUT1 & OUT2 volumes */
+ out1 = &priv->out1;
+ out2 = &priv->out2;
+ out1->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME) &
+ WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+ out1->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME) &
+ WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+ out2->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME) &
+ WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+ out2->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME) &
+ WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+ wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, 0);
+ wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, 0);
+ wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, 0);
+ wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, 0);
+
+ /* Latch VU bits & mute */
+ wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME,
+ WM8350_OUT1_VU | WM8350_OUT1L_MUTE);
+ wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME,
+ WM8350_OUT2_VU | WM8350_OUT2L_MUTE);
+ wm8350_set_bits(wm8350, WM8350_ROUT1_VOLUME,
+ WM8350_OUT1_VU | WM8350_OUT1R_MUTE);
+ wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME,
+ WM8350_OUT2_VU | WM8350_OUT2R_MUTE);
+
+ /* Make sure AIF tristating is disabled by default */
+ wm8350_clear_bits(wm8350, WM8350_AI_FORMATING, WM8350_AIF_TRI);
+
+ /* Make sure we've got a sane companding setup too */
+ wm8350_clear_bits(wm8350, WM8350_ADC_DAC_COMP,
+ WM8350_DAC_COMP | WM8350_LOOPBACK);
+
+ /* Make sure jack detect is disabled to start off with */
+ wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
+ WM8350_JDL_ENA | WM8350_JDR_ENA);
+
+ wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L,
+ wm8350_hpl_jack_handler, 0, "Left jack detect",
+ priv);
+ wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R,
+ wm8350_hpr_jack_handler, 0, "Right jack detect",
+ priv);
+ wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD,
+ wm8350_mic_handler, 0, "Microphone short", priv);
+ wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICD,
+ wm8350_mic_handler, 0, "Microphone detect", priv);
+
+ return 0;
+}
+
+static int wm8350_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8350 *wm8350 = dev_get_platdata(codec->dev);
+
+ wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
+ WM8350_JDL_ENA | WM8350_JDR_ENA);
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
+
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_MICD, priv);
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, priv);
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, priv);
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, priv);
+
+ priv->hpl.jack = NULL;
+ priv->hpr.jack = NULL;
+ priv->mic.jack = NULL;
+
+ cancel_delayed_work_sync(&priv->hpl.work);
+ cancel_delayed_work_sync(&priv->hpr.work);
+
+ /* if there was any work waiting then we run it now and
+ * wait for its completion */
+ flush_delayed_work(&priv->pga_work);
+
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+ return 0;
+}
+
+static struct regmap *wm8350_get_regmap(struct device *dev)
+{
+ struct wm8350 *wm8350 = dev_get_platdata(dev);
+
+ return wm8350->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8350 = {
+ .probe = wm8350_codec_probe,
+ .remove = wm8350_codec_remove,
+ .get_regmap = wm8350_get_regmap,
+ .set_bias_level = wm8350_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8350_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8350_snd_controls),
+ .dapm_widgets = wm8350_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8350_dapm_widgets),
+ .dapm_routes = wm8350_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8350_dapm_routes),
+};
+
+static int wm8350_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8350,
+ &wm8350_dai, 1);
+}
+
+static int wm8350_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver wm8350_codec_driver = {
+ .driver = {
+ .name = "wm8350-codec",
+ },
+ .probe = wm8350_probe,
+ .remove = wm8350_remove,
+};
+
+module_platform_driver(wm8350_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM8350 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8350-codec");
diff --git a/kernel/sound/soc/codecs/wm8350.h b/kernel/sound/soc/codecs/wm8350.h
new file mode 100644
index 000000000..74108eb82
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8350.h
@@ -0,0 +1,29 @@
+/*
+ * wm8350.h - WM8903 audio codec interface
+ *
+ * Copyright 2008 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.
+ */
+
+#ifndef _WM8350_H
+#define _WM8350_H
+
+#include <sound/soc.h>
+#include <linux/mfd/wm8350/audio.h>
+
+enum wm8350_jack {
+ WM8350_JDL = 1,
+ WM8350_JDR = 2,
+};
+
+int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
+ struct snd_soc_jack *jack, int report);
+int wm8350_mic_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack,
+ int detect_report, int short_report);
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8400.c b/kernel/sound/soc/codecs/wm8400.c
new file mode 100644
index 000000000..b0d84e552
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8400.c
@@ -0,0 +1,1379 @@
+/*
+ * wm8400.c -- WM8400 ALSA Soc Audio driver
+ *
+ * Copyright 2008-11 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/wm8400-audio.h>
+#include <linux/mfd/wm8400-private.h>
+#include <linux/mfd/core.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 "wm8400.h"
+
+static struct regulator_bulk_data power[] = {
+ {
+ .supply = "I2S1VDD",
+ },
+ {
+ .supply = "I2S2VDD",
+ },
+ {
+ .supply = "DCVDD",
+ },
+ {
+ .supply = "AVDD",
+ },
+ {
+ .supply = "FLLVDD",
+ },
+ {
+ .supply = "HPVDD",
+ },
+ {
+ .supply = "SPKVDD",
+ },
+};
+
+/* codec private data */
+struct wm8400_priv {
+ struct wm8400 *wm8400;
+ u16 fake_register;
+ unsigned int sysclk;
+ unsigned int pcmclk;
+ int fll_in, fll_out;
+};
+
+static void wm8400_codec_reset(struct snd_soc_codec *codec)
+{
+ struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec);
+
+ wm8400_reset_codec_reg_cache(wm8400->wm8400);
+}
+
+static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 600, 0);
+
+static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1650, 3000, 0);
+
+static const DECLARE_TLV_DB_SCALE(out_mix_tlv, -2100, 0, 0);
+
+static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -7300, 600, 0);
+
+static const DECLARE_TLV_DB_SCALE(out_omix_tlv, -600, 0, 0);
+
+static const DECLARE_TLV_DB_SCALE(out_dac_tlv, -7163, 0, 0);
+
+static const DECLARE_TLV_DB_SCALE(in_adc_tlv, -7163, 1763, 0);
+
+static const DECLARE_TLV_DB_SCALE(out_sidetone_tlv, -3600, 0, 0);
+
+static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int reg = mc->reg;
+ int ret;
+ u16 val;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /* now hit the volume update bits (always bit 8) */
+ val = snd_soc_read(codec, reg);
+ return snd_soc_write(codec, reg, val | 0x0100);
+}
+
+#define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm8400_outpga_put_volsw_vu, tlv_array)
+
+
+static const char *wm8400_digital_sidetone[] =
+ {"None", "Left ADC", "Right ADC", "Reserved"};
+
+static SOC_ENUM_SINGLE_DECL(wm8400_left_digital_sidetone_enum,
+ WM8400_DIGITAL_SIDE_TONE,
+ WM8400_ADC_TO_DACL_SHIFT,
+ wm8400_digital_sidetone);
+
+static SOC_ENUM_SINGLE_DECL(wm8400_right_digital_sidetone_enum,
+ WM8400_DIGITAL_SIDE_TONE,
+ WM8400_ADC_TO_DACR_SHIFT,
+ wm8400_digital_sidetone);
+
+static const char *wm8400_adcmode[] =
+ {"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
+
+static SOC_ENUM_SINGLE_DECL(wm8400_right_adcmode_enum,
+ WM8400_ADC_CTRL,
+ WM8400_ADC_HPF_CUT_SHIFT,
+ wm8400_adcmode);
+
+static const struct snd_kcontrol_new wm8400_snd_controls[] = {
+/* INMIXL */
+SOC_SINGLE("LIN12 PGA Boost", WM8400_INPUT_MIXER3, WM8400_L12MNBST_SHIFT,
+ 1, 0),
+SOC_SINGLE("LIN34 PGA Boost", WM8400_INPUT_MIXER3, WM8400_L34MNBST_SHIFT,
+ 1, 0),
+/* INMIXR */
+SOC_SINGLE("RIN12 PGA Boost", WM8400_INPUT_MIXER3, WM8400_R12MNBST_SHIFT,
+ 1, 0),
+SOC_SINGLE("RIN34 PGA Boost", WM8400_INPUT_MIXER3, WM8400_R34MNBST_SHIFT,
+ 1, 0),
+
+/* LOMIX */
+SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8400_OUTPUT_MIXER3,
+ WM8400_LLI3LOVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER3,
+ WM8400_LR12LOVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER3,
+ WM8400_LL12LOVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8400_OUTPUT_MIXER5,
+ WM8400_LRI3LOVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8400_OUTPUT_MIXER5,
+ WM8400_LRBLOVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8400_OUTPUT_MIXER5,
+ WM8400_LRBLOVOL_SHIFT, 7, 0, out_mix_tlv),
+
+/* ROMIX */
+SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8400_OUTPUT_MIXER4,
+ WM8400_RRI3ROVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER4,
+ WM8400_RL12ROVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER4,
+ WM8400_RR12ROVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8400_OUTPUT_MIXER6,
+ WM8400_RLI3ROVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8400_OUTPUT_MIXER6,
+ WM8400_RLBROVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8400_OUTPUT_MIXER6,
+ WM8400_RRBROVOL_SHIFT, 7, 0, out_mix_tlv),
+
+/* LOUT */
+WM8400_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8400_LEFT_OUTPUT_VOLUME,
+ WM8400_LOUTVOL_SHIFT, WM8400_LOUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOUT ZC", WM8400_LEFT_OUTPUT_VOLUME, WM8400_LOZC_SHIFT, 1, 0),
+
+/* ROUT */
+WM8400_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8400_RIGHT_OUTPUT_VOLUME,
+ WM8400_ROUTVOL_SHIFT, WM8400_ROUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROUT ZC", WM8400_RIGHT_OUTPUT_VOLUME, WM8400_ROZC_SHIFT, 1, 0),
+
+/* LOPGA */
+WM8400_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8400_LEFT_OPGA_VOLUME,
+ WM8400_LOPGAVOL_SHIFT, WM8400_LOPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOPGA ZC Switch", WM8400_LEFT_OPGA_VOLUME,
+ WM8400_LOPGAZC_SHIFT, 1, 0),
+
+/* ROPGA */
+WM8400_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8400_RIGHT_OPGA_VOLUME,
+ WM8400_ROPGAVOL_SHIFT, WM8400_ROPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROPGA ZC Switch", WM8400_RIGHT_OPGA_VOLUME,
+ WM8400_ROPGAZC_SHIFT, 1, 0),
+
+SOC_SINGLE("LON Mute Switch", WM8400_LINE_OUTPUTS_VOLUME,
+ WM8400_LONMUTE_SHIFT, 1, 0),
+SOC_SINGLE("LOP Mute Switch", WM8400_LINE_OUTPUTS_VOLUME,
+ WM8400_LOPMUTE_SHIFT, 1, 0),
+SOC_SINGLE("LOP Attenuation Switch", WM8400_LINE_OUTPUTS_VOLUME,
+ WM8400_LOATTN_SHIFT, 1, 0),
+SOC_SINGLE("RON Mute Switch", WM8400_LINE_OUTPUTS_VOLUME,
+ WM8400_RONMUTE_SHIFT, 1, 0),
+SOC_SINGLE("ROP Mute Switch", WM8400_LINE_OUTPUTS_VOLUME,
+ WM8400_ROPMUTE_SHIFT, 1, 0),
+SOC_SINGLE("ROP Attenuation Switch", WM8400_LINE_OUTPUTS_VOLUME,
+ WM8400_ROATTN_SHIFT, 1, 0),
+
+SOC_SINGLE("OUT3 Mute Switch", WM8400_OUT3_4_VOLUME,
+ WM8400_OUT3MUTE_SHIFT, 1, 0),
+SOC_SINGLE("OUT3 Attenuation Switch", WM8400_OUT3_4_VOLUME,
+ WM8400_OUT3ATTN_SHIFT, 1, 0),
+
+SOC_SINGLE("OUT4 Mute Switch", WM8400_OUT3_4_VOLUME,
+ WM8400_OUT4MUTE_SHIFT, 1, 0),
+SOC_SINGLE("OUT4 Attenuation Switch", WM8400_OUT3_4_VOLUME,
+ WM8400_OUT4ATTN_SHIFT, 1, 0),
+
+SOC_SINGLE("Speaker Mode Switch", WM8400_CLASSD1,
+ WM8400_CDMODE_SHIFT, 1, 0),
+
+SOC_SINGLE("Speaker Output Attenuation Volume", WM8400_SPEAKER_VOLUME,
+ WM8400_SPKATTN_SHIFT, WM8400_SPKATTN_MASK, 0),
+SOC_SINGLE("Speaker DC Boost Volume", WM8400_CLASSD3,
+ WM8400_DCGAIN_SHIFT, 6, 0),
+SOC_SINGLE("Speaker AC Boost Volume", WM8400_CLASSD3,
+ WM8400_ACGAIN_SHIFT, 6, 0),
+
+WM8400_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
+ WM8400_LEFT_DAC_DIGITAL_VOLUME, WM8400_DACL_VOL_SHIFT,
+ 127, 0, out_dac_tlv),
+
+WM8400_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
+ WM8400_RIGHT_DAC_DIGITAL_VOLUME, WM8400_DACR_VOL_SHIFT,
+ 127, 0, out_dac_tlv),
+
+SOC_ENUM("Left Digital Sidetone", wm8400_left_digital_sidetone_enum),
+SOC_ENUM("Right Digital Sidetone", wm8400_right_digital_sidetone_enum),
+
+SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8400_DIGITAL_SIDE_TONE,
+ WM8400_ADCL_DAC_SVOL_SHIFT, 15, 0, out_sidetone_tlv),
+SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8400_DIGITAL_SIDE_TONE,
+ WM8400_ADCR_DAC_SVOL_SHIFT, 15, 0, out_sidetone_tlv),
+
+SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8400_ADC_CTRL,
+ WM8400_ADC_HPF_ENA_SHIFT, 1, 0),
+
+SOC_ENUM("ADC HPF Mode", wm8400_right_adcmode_enum),
+
+WM8400_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
+ WM8400_LEFT_ADC_DIGITAL_VOLUME,
+ WM8400_ADCL_VOL_SHIFT,
+ WM8400_ADCL_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+WM8400_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
+ WM8400_RIGHT_ADC_DIGITAL_VOLUME,
+ WM8400_ADCR_VOL_SHIFT,
+ WM8400_ADCR_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+WM8400_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
+ WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8400_LIN12VOL_SHIFT,
+ WM8400_LIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("LIN12 ZC Switch", WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8400_LI12ZC_SHIFT, 1, 0),
+
+SOC_SINGLE("LIN12 Mute Switch", WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8400_LI12MUTE_SHIFT, 1, 0),
+
+WM8400_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
+ WM8400_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8400_LIN34VOL_SHIFT,
+ WM8400_LIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("LIN34 ZC Switch", WM8400_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8400_LI34ZC_SHIFT, 1, 0),
+
+SOC_SINGLE("LIN34 Mute Switch", WM8400_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8400_LI34MUTE_SHIFT, 1, 0),
+
+WM8400_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
+ WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8400_RIN12VOL_SHIFT,
+ WM8400_RIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("RIN12 ZC Switch", WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8400_RI12ZC_SHIFT, 1, 0),
+
+SOC_SINGLE("RIN12 Mute Switch", WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8400_RI12MUTE_SHIFT, 1, 0),
+
+WM8400_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
+ WM8400_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8400_RIN34VOL_SHIFT,
+ WM8400_RIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("RIN34 ZC Switch", WM8400_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8400_RI34ZC_SHIFT, 1, 0),
+
+SOC_SINGLE("RIN34 Mute Switch", WM8400_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8400_RI34MUTE_SHIFT, 1, 0),
+
+};
+
+/*
+ * _DAPM_ Controls
+ */
+
+static int outmixer_event (struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol * kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ u32 reg_shift = mc->shift;
+ int ret = 0;
+ u16 reg;
+
+ switch (reg_shift) {
+ case WM8400_SPEAKER_MIXER | (WM8400_LDSPK << 8) :
+ reg = snd_soc_read(codec, WM8400_OUTPUT_MIXER1);
+ if (reg & WM8400_LDLO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 1 LDLO Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8400_SPEAKER_MIXER | (WM8400_RDSPK << 8):
+ reg = snd_soc_read(codec, WM8400_OUTPUT_MIXER2);
+ if (reg & WM8400_RDRO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 2 RDRO Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8400_OUTPUT_MIXER1 | (WM8400_LDLO << 8):
+ reg = snd_soc_read(codec, WM8400_SPEAKER_MIXER);
+ if (reg & WM8400_LDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer LDSPK Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8400_OUTPUT_MIXER2 | (WM8400_RDRO << 8):
+ reg = snd_soc_read(codec, WM8400_SPEAKER_MIXER);
+ if (reg & WM8400_RDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer RDSPK Set\n");
+ ret = -1;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+/* INMIX dB values */
+static const unsigned int in_mix_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0,7, TLV_DB_SCALE_ITEM(-1200, 600, 0),
+};
+
+/* Left In PGA Connections */
+static const struct snd_kcontrol_new wm8400_dapm_lin12_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN1 Switch", WM8400_INPUT_MIXER2, WM8400_LMN1_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LIN2 Switch", WM8400_INPUT_MIXER2, WM8400_LMP2_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8400_dapm_lin34_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN3 Switch", WM8400_INPUT_MIXER2, WM8400_LMN3_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LIN4 Switch", WM8400_INPUT_MIXER2, WM8400_LMP4_SHIFT, 1, 0),
+};
+
+/* Right In PGA Connections */
+static const struct snd_kcontrol_new wm8400_dapm_rin12_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN1 Switch", WM8400_INPUT_MIXER2, WM8400_RMN1_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("RIN2 Switch", WM8400_INPUT_MIXER2, WM8400_RMP2_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8400_dapm_rin34_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN3 Switch", WM8400_INPUT_MIXER2, WM8400_RMN3_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("RIN4 Switch", WM8400_INPUT_MIXER2, WM8400_RMP4_SHIFT, 1, 0),
+};
+
+/* INMIXL */
+static const struct snd_kcontrol_new wm8400_dapm_inmixl_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8400_INPUT_MIXER3,
+ WM8400_LDBVOL_SHIFT, WM8400_LDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8400_INPUT_MIXER5, WM8400_LI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("LINPGA12 Switch", WM8400_INPUT_MIXER3, WM8400_L12MNB_SHIFT,
+ 1, 0),
+SOC_DAPM_SINGLE("LINPGA34 Switch", WM8400_INPUT_MIXER3, WM8400_L34MNB_SHIFT,
+ 1, 0),
+};
+
+/* INMIXR */
+static const struct snd_kcontrol_new wm8400_dapm_inmixr_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8400_INPUT_MIXER4,
+ WM8400_RDBVOL_SHIFT, WM8400_RDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8400_INPUT_MIXER6, WM8400_RI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("RINPGA12 Switch", WM8400_INPUT_MIXER3, WM8400_L12MNB_SHIFT,
+ 1, 0),
+SOC_DAPM_SINGLE("RINPGA34 Switch", WM8400_INPUT_MIXER3, WM8400_L34MNB_SHIFT,
+ 1, 0),
+};
+
+/* AINLMUX */
+static const char *wm8400_ainlmux[] =
+ {"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
+
+static SOC_ENUM_SINGLE_DECL(wm8400_ainlmux_enum,
+ WM8400_INPUT_MIXER1,
+ WM8400_AINLMODE_SHIFT,
+ wm8400_ainlmux);
+
+static const struct snd_kcontrol_new wm8400_dapm_ainlmux_controls =
+SOC_DAPM_ENUM("Route", wm8400_ainlmux_enum);
+
+/* DIFFINL */
+
+/* AINRMUX */
+static const char *wm8400_ainrmux[] =
+ {"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
+
+static SOC_ENUM_SINGLE_DECL(wm8400_ainrmux_enum,
+ WM8400_INPUT_MIXER1,
+ WM8400_AINRMODE_SHIFT,
+ wm8400_ainrmux);
+
+static const struct snd_kcontrol_new wm8400_dapm_ainrmux_controls =
+SOC_DAPM_ENUM("Route", wm8400_ainrmux_enum);
+
+/* RXVOICE */
+static const struct snd_kcontrol_new wm8400_dapm_rxvoice_controls[] = {
+SOC_DAPM_SINGLE_TLV("LIN4/RXN", WM8400_INPUT_MIXER5, WM8400_LR4BVOL_SHIFT,
+ WM8400_LR4BVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN4/RXP", WM8400_INPUT_MIXER6, WM8400_RL4BVOL_SHIFT,
+ WM8400_RL4BVOL_MASK, 0, in_mix_tlv),
+};
+
+/* LOMIX */
+static const struct snd_kcontrol_new wm8400_dapm_lomix_controls[] = {
+SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LRBLO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LLBLO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LRI3LO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LLI3LO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LR12LO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LL12LO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LDLO_SHIFT, 1, 0),
+};
+
+/* ROMIX */
+static const struct snd_kcontrol_new wm8400_dapm_romix_controls[] = {
+SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RLBRO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RRBRO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RLI3RO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RRI3RO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RL12RO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RR12RO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RDRO_SHIFT, 1, 0),
+};
+
+/* LONMIX */
+static const struct snd_kcontrol_new wm8400_dapm_lonmix_controls[] = {
+SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8400_LINE_MIXER1,
+ WM8400_LLOPGALON_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8400_LINE_MIXER1,
+ WM8400_LROPGALON_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8400_LINE_MIXER1,
+ WM8400_LOPLON_SHIFT, 1, 0),
+};
+
+/* LOPMIX */
+static const struct snd_kcontrol_new wm8400_dapm_lopmix_controls[] = {
+SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8400_LINE_MIXER1,
+ WM8400_LR12LOP_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8400_LINE_MIXER1,
+ WM8400_LL12LOP_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8400_LINE_MIXER1,
+ WM8400_LLOPGALOP_SHIFT, 1, 0),
+};
+
+/* RONMIX */
+static const struct snd_kcontrol_new wm8400_dapm_ronmix_controls[] = {
+SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8400_LINE_MIXER2,
+ WM8400_RROPGARON_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8400_LINE_MIXER2,
+ WM8400_RLOPGARON_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8400_LINE_MIXER2,
+ WM8400_ROPRON_SHIFT, 1, 0),
+};
+
+/* ROPMIX */
+static const struct snd_kcontrol_new wm8400_dapm_ropmix_controls[] = {
+SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8400_LINE_MIXER2,
+ WM8400_RL12ROP_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8400_LINE_MIXER2,
+ WM8400_RR12ROP_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8400_LINE_MIXER2,
+ WM8400_RROPGAROP_SHIFT, 1, 0),
+};
+
+/* OUT3MIX */
+static const struct snd_kcontrol_new wm8400_dapm_out3mix_controls[] = {
+SOC_DAPM_SINGLE("OUT3MIX LIN4/RXP Bypass Switch", WM8400_OUT3_4_MIXER,
+ WM8400_LI4O3_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8400_OUT3_4_MIXER,
+ WM8400_LPGAO3_SHIFT, 1, 0),
+};
+
+/* OUT4MIX */
+static const struct snd_kcontrol_new wm8400_dapm_out4mix_controls[] = {
+SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8400_OUT3_4_MIXER,
+ WM8400_RPGAO4_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("OUT4MIX RIN4/RXP Bypass Switch", WM8400_OUT3_4_MIXER,
+ WM8400_RI4O4_SHIFT, 1, 0),
+};
+
+/* SPKMIX */
+static const struct snd_kcontrol_new wm8400_dapm_spkmix_controls[] = {
+SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8400_SPEAKER_MIXER,
+ WM8400_LI2SPK_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8400_SPEAKER_MIXER,
+ WM8400_LB2SPK_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8400_SPEAKER_MIXER,
+ WM8400_LOPGASPK_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8400_SPEAKER_MIXER,
+ WM8400_LDSPK_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8400_SPEAKER_MIXER,
+ WM8400_RDSPK_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8400_SPEAKER_MIXER,
+ WM8400_ROPGASPK_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8400_SPEAKER_MIXER,
+ WM8400_RL12ROP_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8400_SPEAKER_MIXER,
+ WM8400_RI2SPK_SHIFT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8400_dapm_widgets[] = {
+/* Input Side */
+/* Input Lines */
+SND_SOC_DAPM_INPUT("LIN1"),
+SND_SOC_DAPM_INPUT("LIN2"),
+SND_SOC_DAPM_INPUT("LIN3"),
+SND_SOC_DAPM_INPUT("LIN4/RXN"),
+SND_SOC_DAPM_INPUT("RIN3"),
+SND_SOC_DAPM_INPUT("RIN4/RXP"),
+SND_SOC_DAPM_INPUT("RIN1"),
+SND_SOC_DAPM_INPUT("RIN2"),
+SND_SOC_DAPM_INPUT("Internal ADC Source"),
+
+/* DACs */
+SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8400_POWER_MANAGEMENT_2,
+ WM8400_ADCL_ENA_SHIFT, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8400_POWER_MANAGEMENT_2,
+ WM8400_ADCR_ENA_SHIFT, 0),
+
+/* Input PGAs */
+SND_SOC_DAPM_MIXER("LIN12 PGA", WM8400_POWER_MANAGEMENT_2,
+ WM8400_LIN12_ENA_SHIFT,
+ 0, &wm8400_dapm_lin12_pga_controls[0],
+ ARRAY_SIZE(wm8400_dapm_lin12_pga_controls)),
+SND_SOC_DAPM_MIXER("LIN34 PGA", WM8400_POWER_MANAGEMENT_2,
+ WM8400_LIN34_ENA_SHIFT,
+ 0, &wm8400_dapm_lin34_pga_controls[0],
+ ARRAY_SIZE(wm8400_dapm_lin34_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN12 PGA", WM8400_POWER_MANAGEMENT_2,
+ WM8400_RIN12_ENA_SHIFT,
+ 0, &wm8400_dapm_rin12_pga_controls[0],
+ ARRAY_SIZE(wm8400_dapm_rin12_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN34 PGA", WM8400_POWER_MANAGEMENT_2,
+ WM8400_RIN34_ENA_SHIFT,
+ 0, &wm8400_dapm_rin34_pga_controls[0],
+ ARRAY_SIZE(wm8400_dapm_rin34_pga_controls)),
+
+SND_SOC_DAPM_SUPPLY("INL", WM8400_POWER_MANAGEMENT_2, WM8400_AINL_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("INR", WM8400_POWER_MANAGEMENT_2, WM8400_AINR_ENA_SHIFT,
+ 0, NULL, 0),
+
+/* INMIXL */
+SND_SOC_DAPM_MIXER("INMIXL", SND_SOC_NOPM, 0, 0,
+ &wm8400_dapm_inmixl_controls[0],
+ ARRAY_SIZE(wm8400_dapm_inmixl_controls)),
+
+/* AINLMUX */
+SND_SOC_DAPM_MUX("AILNMUX", SND_SOC_NOPM, 0, 0, &wm8400_dapm_ainlmux_controls),
+
+/* INMIXR */
+SND_SOC_DAPM_MIXER("INMIXR", SND_SOC_NOPM, 0, 0,
+ &wm8400_dapm_inmixr_controls[0],
+ ARRAY_SIZE(wm8400_dapm_inmixr_controls)),
+
+/* AINRMUX */
+SND_SOC_DAPM_MUX("AIRNMUX", SND_SOC_NOPM, 0, 0, &wm8400_dapm_ainrmux_controls),
+
+/* Output Side */
+/* DACs */
+SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8400_POWER_MANAGEMENT_3,
+ WM8400_DACL_ENA_SHIFT, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8400_POWER_MANAGEMENT_3,
+ WM8400_DACR_ENA_SHIFT, 0),
+
+/* LOMIX */
+SND_SOC_DAPM_MIXER_E("LOMIX", WM8400_POWER_MANAGEMENT_3,
+ WM8400_LOMIX_ENA_SHIFT,
+ 0, &wm8400_dapm_lomix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_lomix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LONMIX */
+SND_SOC_DAPM_MIXER("LONMIX", WM8400_POWER_MANAGEMENT_3, WM8400_LON_ENA_SHIFT,
+ 0, &wm8400_dapm_lonmix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_lonmix_controls)),
+
+/* LOPMIX */
+SND_SOC_DAPM_MIXER("LOPMIX", WM8400_POWER_MANAGEMENT_3, WM8400_LOP_ENA_SHIFT,
+ 0, &wm8400_dapm_lopmix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_lopmix_controls)),
+
+/* OUT3MIX */
+SND_SOC_DAPM_MIXER("OUT3MIX", WM8400_POWER_MANAGEMENT_1, WM8400_OUT3_ENA_SHIFT,
+ 0, &wm8400_dapm_out3mix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_out3mix_controls)),
+
+/* SPKMIX */
+SND_SOC_DAPM_MIXER_E("SPKMIX", WM8400_POWER_MANAGEMENT_1, WM8400_SPK_ENA_SHIFT,
+ 0, &wm8400_dapm_spkmix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_spkmix_controls), outmixer_event,
+ SND_SOC_DAPM_PRE_REG),
+
+/* OUT4MIX */
+SND_SOC_DAPM_MIXER("OUT4MIX", WM8400_POWER_MANAGEMENT_1, WM8400_OUT4_ENA_SHIFT,
+ 0, &wm8400_dapm_out4mix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_out4mix_controls)),
+
+/* ROPMIX */
+SND_SOC_DAPM_MIXER("ROPMIX", WM8400_POWER_MANAGEMENT_3, WM8400_ROP_ENA_SHIFT,
+ 0, &wm8400_dapm_ropmix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_ropmix_controls)),
+
+/* RONMIX */
+SND_SOC_DAPM_MIXER("RONMIX", WM8400_POWER_MANAGEMENT_3, WM8400_RON_ENA_SHIFT,
+ 0, &wm8400_dapm_ronmix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_ronmix_controls)),
+
+/* ROMIX */
+SND_SOC_DAPM_MIXER_E("ROMIX", WM8400_POWER_MANAGEMENT_3,
+ WM8400_ROMIX_ENA_SHIFT,
+ 0, &wm8400_dapm_romix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_romix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LOUT PGA */
+SND_SOC_DAPM_PGA("LOUT PGA", WM8400_POWER_MANAGEMENT_1, WM8400_LOUT_ENA_SHIFT,
+ 0, NULL, 0),
+
+/* ROUT PGA */
+SND_SOC_DAPM_PGA("ROUT PGA", WM8400_POWER_MANAGEMENT_1, WM8400_ROUT_ENA_SHIFT,
+ 0, NULL, 0),
+
+/* LOPGA */
+SND_SOC_DAPM_PGA("LOPGA", WM8400_POWER_MANAGEMENT_3, WM8400_LOPGA_ENA_SHIFT, 0,
+ NULL, 0),
+
+/* ROPGA */
+SND_SOC_DAPM_PGA("ROPGA", WM8400_POWER_MANAGEMENT_3, WM8400_ROPGA_ENA_SHIFT, 0,
+ NULL, 0),
+
+/* MICBIAS */
+SND_SOC_DAPM_SUPPLY("MICBIAS", WM8400_POWER_MANAGEMENT_1,
+ WM8400_MIC1BIAS_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("LON"),
+SND_SOC_DAPM_OUTPUT("LOP"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("SPKN"),
+SND_SOC_DAPM_OUTPUT("SPKP"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("OUT4"),
+SND_SOC_DAPM_OUTPUT("ROP"),
+SND_SOC_DAPM_OUTPUT("RON"),
+
+SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
+};
+
+static const struct snd_soc_dapm_route wm8400_dapm_routes[] = {
+ /* Make DACs turn on when playing even if not mixed into any outputs */
+ {"Internal DAC Sink", NULL, "Left DAC"},
+ {"Internal DAC Sink", NULL, "Right DAC"},
+
+ /* Make ADCs turn on when recording
+ * even if not mixed from any inputs */
+ {"Left ADC", NULL, "Internal ADC Source"},
+ {"Right ADC", NULL, "Internal ADC Source"},
+
+ /* Input Side */
+ /* LIN12 PGA */
+ {"LIN12 PGA", "LIN1 Switch", "LIN1"},
+ {"LIN12 PGA", "LIN2 Switch", "LIN2"},
+ /* LIN34 PGA */
+ {"LIN34 PGA", "LIN3 Switch", "LIN3"},
+ {"LIN34 PGA", "LIN4 Switch", "LIN4/RXN"},
+ /* INMIXL */
+ {"INMIXL", NULL, "INL"},
+ {"INMIXL", "Record Left Volume", "LOMIX"},
+ {"INMIXL", "LIN2 Volume", "LIN2"},
+ {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"},
+ {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"},
+ /* AILNMUX */
+ {"AILNMUX", NULL, "INL"},
+ {"AILNMUX", "INMIXL Mix", "INMIXL"},
+ {"AILNMUX", "DIFFINL Mix", "LIN12 PGA"},
+ {"AILNMUX", "DIFFINL Mix", "LIN34 PGA"},
+ {"AILNMUX", "RXVOICE Mix", "LIN4/RXN"},
+ {"AILNMUX", "RXVOICE Mix", "RIN4/RXP"},
+ /* ADC */
+ {"Left ADC", NULL, "AILNMUX"},
+
+ /* RIN12 PGA */
+ {"RIN12 PGA", "RIN1 Switch", "RIN1"},
+ {"RIN12 PGA", "RIN2 Switch", "RIN2"},
+ /* RIN34 PGA */
+ {"RIN34 PGA", "RIN3 Switch", "RIN3"},
+ {"RIN34 PGA", "RIN4 Switch", "RIN4/RXP"},
+ /* INMIXR */
+ {"INMIXR", NULL, "INR"},
+ {"INMIXR", "Record Right Volume", "ROMIX"},
+ {"INMIXR", "RIN2 Volume", "RIN2"},
+ {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"},
+ {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"},
+ /* AIRNMUX */
+ {"AIRNMUX", NULL, "INR"},
+ {"AIRNMUX", "INMIXR Mix", "INMIXR"},
+ {"AIRNMUX", "DIFFINR Mix", "RIN12 PGA"},
+ {"AIRNMUX", "DIFFINR Mix", "RIN34 PGA"},
+ {"AIRNMUX", "RXVOICE Mix", "LIN4/RXN"},
+ {"AIRNMUX", "RXVOICE Mix", "RIN4/RXP"},
+ /* ADC */
+ {"Right ADC", NULL, "AIRNMUX"},
+
+ /* LOMIX */
+ {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"},
+ {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"},
+ {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"LOMIX", "LOMIX Right ADC Bypass Switch", "AIRNMUX"},
+ {"LOMIX", "LOMIX Left ADC Bypass Switch", "AILNMUX"},
+ {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
+
+ /* ROMIX */
+ {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"},
+ {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"},
+ {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"ROMIX", "ROMIX Right ADC Bypass Switch", "AIRNMUX"},
+ {"ROMIX", "ROMIX Left ADC Bypass Switch", "AILNMUX"},
+ {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"},
+
+ /* SPKMIX */
+ {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"},
+ {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"},
+ {"SPKMIX", "SPKMIX LADC Bypass Switch", "AILNMUX"},
+ {"SPKMIX", "SPKMIX RADC Bypass Switch", "AIRNMUX"},
+ {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"},
+ {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"},
+ {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"},
+ {"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"},
+
+ /* LONMIX */
+ {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"},
+
+ /* LOPMIX */
+ {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"},
+
+ /* OUT3MIX */
+ {"OUT3MIX", "OUT3MIX LIN4/RXP Bypass Switch", "LIN4/RXN"},
+ {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"},
+
+ /* OUT4MIX */
+ {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"},
+ {"OUT4MIX", "OUT4MIX RIN4/RXP Bypass Switch", "RIN4/RXP"},
+
+ /* RONMIX */
+ {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"},
+
+ /* ROPMIX */
+ {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"},
+
+ /* Out Mixer PGAs */
+ {"LOPGA", NULL, "LOMIX"},
+ {"ROPGA", NULL, "ROMIX"},
+
+ {"LOUT PGA", NULL, "LOMIX"},
+ {"ROUT PGA", NULL, "ROMIX"},
+
+ /* Output Pins */
+ {"LON", NULL, "LONMIX"},
+ {"LOP", NULL, "LOPMIX"},
+ {"OUT3", NULL, "OUT3MIX"},
+ {"LOUT", NULL, "LOUT PGA"},
+ {"SPKN", NULL, "SPKMIX"},
+ {"ROUT", NULL, "ROUT PGA"},
+ {"OUT4", NULL, "OUT4MIX"},
+ {"ROP", NULL, "ROPMIX"},
+ {"RON", NULL, "RONMIX"},
+};
+
+/*
+ * Clock after FLL and dividers
+ */
+static int wm8400_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec);
+
+ wm8400->sysclk = freq;
+ return 0;
+}
+
+struct fll_factors {
+ u16 n;
+ u16 k;
+ u16 outdiv;
+ u16 fratio;
+ u16 freq_ref;
+};
+
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors,
+ unsigned int Fref, unsigned int Fout)
+{
+ u64 Kpart;
+ unsigned int K, Nmod, target;
+
+ factors->outdiv = 2;
+ while (Fout * factors->outdiv < 90000000 ||
+ Fout * factors->outdiv > 100000000) {
+ factors->outdiv *= 2;
+ if (factors->outdiv > 32) {
+ dev_err(wm8400->wm8400->dev,
+ "Unsupported FLL output frequency %uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ target = Fout * factors->outdiv;
+ factors->outdiv = factors->outdiv >> 2;
+
+ if (Fref < 48000)
+ factors->freq_ref = 1;
+ else
+ factors->freq_ref = 0;
+
+ if (Fref < 1000000)
+ factors->fratio = 9;
+ else
+ factors->fratio = 0;
+
+ /* Ensure we have a fractional part */
+ do {
+ if (Fref < 1000000)
+ factors->fratio--;
+ else
+ factors->fratio++;
+
+ if (factors->fratio < 1 || factors->fratio > 8) {
+ dev_err(wm8400->wm8400->dev,
+ "Unable to calculate FRATIO\n");
+ return -EINVAL;
+ }
+
+ factors->n = target / (Fref * factors->fratio);
+ Nmod = target % (Fref * factors->fratio);
+ } while (Nmod == 0);
+
+ /* Calculate fractional part - scale up so we can round. */
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, (Fref * factors->fratio));
+
+ K = Kpart & 0xFFFFFFFF;
+
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ factors->k = K / 10;
+
+ dev_dbg(wm8400->wm8400->dev,
+ "FLL: Fref=%u Fout=%u N=%x K=%x, FRATIO=%x OUTDIV=%x\n",
+ Fref, Fout,
+ factors->n, factors->k, factors->fratio, factors->outdiv);
+
+ return 0;
+}
+
+static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec);
+ struct fll_factors factors;
+ int ret;
+ u16 reg;
+
+ if (freq_in == wm8400->fll_in && freq_out == wm8400->fll_out)
+ return 0;
+
+ if (freq_out) {
+ ret = fll_factors(wm8400, &factors, freq_in, freq_out);
+ if (ret != 0)
+ return ret;
+ } else {
+ /* Bodge GCC 4.4.0 uninitialised variable warning - it
+ * doesn't seem capable of working out that we exit if
+ * freq_out is 0 before any of the uses. */
+ memset(&factors, 0, sizeof(factors));
+ }
+
+ wm8400->fll_out = freq_out;
+ wm8400->fll_in = freq_in;
+
+ /* We *must* disable the FLL before any changes */
+ reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_2);
+ reg &= ~WM8400_FLL_ENA;
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_2, reg);
+
+ reg = snd_soc_read(codec, WM8400_FLL_CONTROL_1);
+ reg &= ~WM8400_FLL_OSC_ENA;
+ snd_soc_write(codec, WM8400_FLL_CONTROL_1, reg);
+
+ if (!freq_out)
+ return 0;
+
+ reg &= ~(WM8400_FLL_REF_FREQ | WM8400_FLL_FRATIO_MASK);
+ reg |= WM8400_FLL_FRAC | factors.fratio;
+ reg |= factors.freq_ref << WM8400_FLL_REF_FREQ_SHIFT;
+ snd_soc_write(codec, WM8400_FLL_CONTROL_1, reg);
+
+ snd_soc_write(codec, WM8400_FLL_CONTROL_2, factors.k);
+ snd_soc_write(codec, WM8400_FLL_CONTROL_3, factors.n);
+
+ reg = snd_soc_read(codec, WM8400_FLL_CONTROL_4);
+ reg &= ~WM8400_FLL_OUTDIV_MASK;
+ reg |= factors.outdiv;
+ snd_soc_write(codec, WM8400_FLL_CONTROL_4, reg);
+
+ return 0;
+}
+
+/*
+ * Sets ADC and Voice DAC format.
+ */
+static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 audio1, audio3;
+
+ audio1 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_1);
+ audio3 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_3);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ audio3 &= ~WM8400_AIF_MSTR1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ audio3 |= WM8400_AIF_MSTR1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ audio1 &= ~WM8400_AIF_FMT_MASK;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ audio1 |= WM8400_AIF_FMT_I2S;
+ audio1 &= ~WM8400_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audio1 |= WM8400_AIF_FMT_RIGHTJ;
+ audio1 &= ~WM8400_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audio1 |= WM8400_AIF_FMT_LEFTJ;
+ audio1 &= ~WM8400_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audio1 |= WM8400_AIF_FMT_DSP;
+ audio1 &= ~WM8400_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audio1 |= WM8400_AIF_FMT_DSP | WM8400_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
+ snd_soc_write(codec, WM8400_AUDIO_INTERFACE_3, audio3);
+ return 0;
+}
+
+static int wm8400_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8400_MCLK_DIV:
+ reg = snd_soc_read(codec, WM8400_CLOCKING_2) &
+ ~WM8400_MCLK_DIV_MASK;
+ snd_soc_write(codec, WM8400_CLOCKING_2, reg | div);
+ break;
+ case WM8400_DACCLK_DIV:
+ reg = snd_soc_read(codec, WM8400_CLOCKING_2) &
+ ~WM8400_DAC_CLKDIV_MASK;
+ snd_soc_write(codec, WM8400_CLOCKING_2, reg | div);
+ break;
+ case WM8400_ADCCLK_DIV:
+ reg = snd_soc_read(codec, WM8400_CLOCKING_2) &
+ ~WM8400_ADC_CLKDIV_MASK;
+ snd_soc_write(codec, WM8400_CLOCKING_2, reg | div);
+ break;
+ case WM8400_BCLK_DIV:
+ reg = snd_soc_read(codec, WM8400_CLOCKING_1) &
+ ~WM8400_BCLK_DIV_MASK;
+ snd_soc_write(codec, WM8400_CLOCKING_1, reg | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8400_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 audio1 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_1);
+
+ audio1 &= ~WM8400_AIF_WL_MASK;
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ audio1 |= WM8400_AIF_WL_20BITS;
+ break;
+ case 24:
+ audio1 |= WM8400_AIF_WL_24BITS;
+ break;
+ case 32:
+ audio1 |= WM8400_AIF_WL_32BITS;
+ break;
+ }
+
+ snd_soc_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
+ return 0;
+}
+
+static int wm8400_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 val = snd_soc_read(codec, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE;
+
+ if (mute)
+ snd_soc_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
+ else
+ snd_soc_write(codec, WM8400_DAC_CTRL, val);
+
+ return 0;
+}
+
+/* TODO: set bias for best performance at standby */
+static int wm8400_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec);
+ u16 val;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID=2*50k */
+ val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1) &
+ ~WM8400_VMID_MODE_MASK;
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x2);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(power),
+ &power[0]);
+ if (ret != 0) {
+ dev_err(wm8400->wm8400->dev,
+ "Failed to enable regulators: %d\n",
+ ret);
+ return ret;
+ }
+
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1,
+ WM8400_CODEC_ENA | WM8400_SYSCLK_ENA);
+
+ /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
+ snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ WM8400_BUFDCOPEN | WM8400_POBCTRL);
+
+ msleep(50);
+
+ /* Enable VREF & VMID at 2x50k */
+ val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
+ val |= 0x2 | WM8400_VREF_ENA;
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+
+ /* Enable BUFIOEN */
+ snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ WM8400_BUFDCOPEN | WM8400_POBCTRL |
+ WM8400_BUFIOEN);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_BUFIOEN);
+ }
+
+ /* VMID=2*300k */
+ val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1) &
+ ~WM8400_VMID_MODE_MASK;
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x4);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Enable POBCTRL and SOFT_ST */
+ snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ WM8400_POBCTRL | WM8400_BUFIOEN);
+
+ /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
+ snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ WM8400_BUFDCOPEN | WM8400_POBCTRL |
+ WM8400_BUFIOEN);
+
+ /* mute DAC */
+ val = snd_soc_read(codec, WM8400_DAC_CTRL);
+ snd_soc_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
+
+ /* Enable any disabled outputs */
+ val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
+ val |= WM8400_SPK_ENA | WM8400_OUT3_ENA |
+ WM8400_OUT4_ENA | WM8400_LOUT_ENA |
+ WM8400_ROUT_ENA;
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+
+ /* Disable VMID */
+ val &= ~WM8400_VMID_MODE_MASK;
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+
+ msleep(300);
+
+ /* Enable all output discharge bits */
+ snd_soc_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE |
+ WM8400_DIS_RLINE | WM8400_DIS_OUT3 |
+ WM8400_DIS_OUT4 | WM8400_DIS_LOUT |
+ WM8400_DIS_ROUT);
+
+ /* Disable VREF */
+ val &= ~WM8400_VREF_ENA;
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ snd_soc_write(codec, WM8400_ANTIPOP2, 0x0);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(power),
+ &power[0]);
+ if (ret != 0)
+ return ret;
+
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8400_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8400_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8400_dai_ops = {
+ .hw_params = wm8400_hw_params,
+ .digital_mute = wm8400_mute,
+ .set_fmt = wm8400_set_dai_fmt,
+ .set_clkdiv = wm8400_set_dai_clkdiv,
+ .set_sysclk = wm8400_set_dai_sysclk,
+ .set_pll = wm8400_set_dai_pll,
+};
+
+/*
+ * The WM8400 supports 2 different and mutually exclusive DAI
+ * configurations.
+ *
+ * 1. ADC/DAC on Primary Interface
+ * 2. ADC on Primary Interface/DAC on secondary
+ */
+static struct snd_soc_dai_driver wm8400_dai = {
+/* ADC/DAC on primary */
+ .name = "wm8400-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8400_RATES,
+ .formats = WM8400_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8400_RATES,
+ .formats = WM8400_FORMATS,
+ },
+ .ops = &wm8400_dai_ops,
+};
+
+static int wm8400_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wm8400 *wm8400 = dev_get_platdata(codec->dev);
+ struct wm8400_priv *priv;
+ int ret;
+ u16 reg;
+
+ priv = devm_kzalloc(codec->dev, sizeof(struct wm8400_priv),
+ GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ snd_soc_codec_set_drvdata(codec, priv);
+ priv->wm8400 = wm8400;
+
+ ret = devm_regulator_bulk_get(wm8400->dev,
+ ARRAY_SIZE(power), &power[0]);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to get regulators: %d\n", ret);
+ return ret;
+ }
+
+ wm8400_codec_reset(codec);
+
+ reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA);
+
+ /* Latch volume update bits */
+ reg = snd_soc_read(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME);
+ snd_soc_write(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
+ reg & WM8400_IPVU);
+ reg = snd_soc_read(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME);
+ snd_soc_write(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
+ reg & WM8400_IPVU);
+
+ snd_soc_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+ snd_soc_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+
+ return 0;
+}
+
+static int wm8400_codec_remove(struct snd_soc_codec *codec)
+{
+ u16 reg;
+
+ reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
+ snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1,
+ reg & (~WM8400_CODEC_ENA));
+
+ return 0;
+}
+
+static struct regmap *wm8400_get_regmap(struct device *dev)
+{
+ struct wm8400 *wm8400 = dev_get_platdata(dev);
+
+ return wm8400->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8400 = {
+ .probe = wm8400_codec_probe,
+ .remove = wm8400_codec_remove,
+ .get_regmap = wm8400_get_regmap,
+ .set_bias_level = wm8400_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8400_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8400_snd_controls),
+ .dapm_widgets = wm8400_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8400_dapm_widgets),
+ .dapm_routes = wm8400_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8400_dapm_routes),
+};
+
+static int wm8400_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8400,
+ &wm8400_dai, 1);
+}
+
+static int wm8400_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver wm8400_codec_driver = {
+ .driver = {
+ .name = "wm8400-codec",
+ },
+ .probe = wm8400_probe,
+ .remove = wm8400_remove,
+};
+
+module_platform_driver(wm8400_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM8400 driver");
+MODULE_AUTHOR("Mark Brown");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8400-codec");
diff --git a/kernel/sound/soc/codecs/wm8400.h b/kernel/sound/soc/codecs/wm8400.h
new file mode 100644
index 000000000..521adb193
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8400.h
@@ -0,0 +1,59 @@
+/*
+ * wm8400.h -- audio driver for WM8400
+ *
+ * Copyright 2008 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.
+ *
+ */
+
+#ifndef _WM8400_CODEC_H
+#define _WM8400_CODEC_H
+
+#define WM8400_MCLK_DIV 0
+#define WM8400_DACCLK_DIV 1
+#define WM8400_ADCCLK_DIV 2
+#define WM8400_BCLK_DIV 3
+
+#define WM8400_MCLK_DIV_1 0x400
+#define WM8400_MCLK_DIV_2 0x800
+
+#define WM8400_DAC_CLKDIV_1 0x00
+#define WM8400_DAC_CLKDIV_1_5 0x04
+#define WM8400_DAC_CLKDIV_2 0x08
+#define WM8400_DAC_CLKDIV_3 0x0c
+#define WM8400_DAC_CLKDIV_4 0x10
+#define WM8400_DAC_CLKDIV_5_5 0x14
+#define WM8400_DAC_CLKDIV_6 0x18
+
+#define WM8400_ADC_CLKDIV_1 0x00
+#define WM8400_ADC_CLKDIV_1_5 0x20
+#define WM8400_ADC_CLKDIV_2 0x40
+#define WM8400_ADC_CLKDIV_3 0x60
+#define WM8400_ADC_CLKDIV_4 0x80
+#define WM8400_ADC_CLKDIV_5_5 0xa0
+#define WM8400_ADC_CLKDIV_6 0xc0
+
+
+#define WM8400_BCLK_DIV_1 (0x0 << 1)
+#define WM8400_BCLK_DIV_1_5 (0x1 << 1)
+#define WM8400_BCLK_DIV_2 (0x2 << 1)
+#define WM8400_BCLK_DIV_3 (0x3 << 1)
+#define WM8400_BCLK_DIV_4 (0x4 << 1)
+#define WM8400_BCLK_DIV_5_5 (0x5 << 1)
+#define WM8400_BCLK_DIV_6 (0x6 << 1)
+#define WM8400_BCLK_DIV_8 (0x7 << 1)
+#define WM8400_BCLK_DIV_11 (0x8 << 1)
+#define WM8400_BCLK_DIV_12 (0x9 << 1)
+#define WM8400_BCLK_DIV_16 (0xA << 1)
+#define WM8400_BCLK_DIV_22 (0xB << 1)
+#define WM8400_BCLK_DIV_24 (0xC << 1)
+#define WM8400_BCLK_DIV_32 (0xD << 1)
+#define WM8400_BCLK_DIV_44 (0xE << 1)
+#define WM8400_BCLK_DIV_48 (0xF << 1)
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8510.c b/kernel/sound/soc/codecs/wm8510.c
new file mode 100644
index 000000000..8736ad094
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8510.c
@@ -0,0 +1,737 @@
+/*
+ * wm8510.c -- WM8510 ALSA Soc Audio driver
+ *
+ * Copyright 2006 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 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/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/of_device.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 "wm8510.h"
+
+/*
+ * wm8510 register cache
+ * We can't read the WM8510 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ */
+static const struct reg_default wm8510_reg_defaults[] = {
+ { 1, 0x0000 },
+ { 2, 0x0000 },
+ { 3, 0x0000 },
+ { 4, 0x0050 },
+ { 5, 0x0000 },
+ { 6, 0x0140 },
+ { 7, 0x0000 },
+ { 8, 0x0000 },
+ { 9, 0x0000 },
+ { 10, 0x0000 },
+ { 11, 0x00ff },
+ { 12, 0x0000 },
+ { 13, 0x0000 },
+ { 14, 0x0100 },
+ { 15, 0x00ff },
+ { 16, 0x0000 },
+ { 17, 0x0000 },
+ { 18, 0x012c },
+ { 19, 0x002c },
+ { 20, 0x002c },
+ { 21, 0x002c },
+ { 22, 0x002c },
+ { 23, 0x0000 },
+ { 24, 0x0032 },
+ { 25, 0x0000 },
+ { 26, 0x0000 },
+ { 27, 0x0000 },
+ { 28, 0x0000 },
+ { 29, 0x0000 },
+ { 30, 0x0000 },
+ { 31, 0x0000 },
+ { 32, 0x0038 },
+ { 33, 0x000b },
+ { 34, 0x0032 },
+ { 35, 0x0000 },
+ { 36, 0x0008 },
+ { 37, 0x000c },
+ { 38, 0x0093 },
+ { 39, 0x00e9 },
+ { 40, 0x0000 },
+ { 41, 0x0000 },
+ { 42, 0x0000 },
+ { 43, 0x0000 },
+ { 44, 0x0003 },
+ { 45, 0x0010 },
+ { 46, 0x0000 },
+ { 47, 0x0000 },
+ { 48, 0x0000 },
+ { 49, 0x0002 },
+ { 50, 0x0001 },
+ { 51, 0x0000 },
+ { 52, 0x0000 },
+ { 53, 0x0000 },
+ { 54, 0x0039 },
+ { 55, 0x0000 },
+ { 56, 0x0001 },
+};
+
+static bool wm8510_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8510_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#define WM8510_POWER1_BIASEN 0x08
+#define WM8510_POWER1_BUFIOEN 0x10
+
+#define wm8510_reset(c) snd_soc_write(c, WM8510_RESET, 0)
+
+/* codec private data */
+struct wm8510_priv {
+ struct regmap *regmap;
+};
+
+static const char *wm8510_companding[] = { "Off", "NC", "u-law", "A-law" };
+static const char *wm8510_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8510_alc[] = { "ALC", "Limiter" };
+
+static const struct soc_enum wm8510_enum[] = {
+ SOC_ENUM_SINGLE(WM8510_COMP, 1, 4, wm8510_companding), /* adc */
+ SOC_ENUM_SINGLE(WM8510_COMP, 3, 4, wm8510_companding), /* dac */
+ SOC_ENUM_SINGLE(WM8510_DAC, 4, 4, wm8510_deemp),
+ SOC_ENUM_SINGLE(WM8510_ALC3, 8, 2, wm8510_alc),
+};
+
+static const struct snd_kcontrol_new wm8510_snd_controls[] = {
+
+SOC_SINGLE("Digital Loopback Switch", WM8510_COMP, 0, 1, 0),
+
+SOC_ENUM("DAC Companding", wm8510_enum[1]),
+SOC_ENUM("ADC Companding", wm8510_enum[0]),
+
+SOC_ENUM("Playback De-emphasis", wm8510_enum[2]),
+SOC_SINGLE("DAC Inversion Switch", WM8510_DAC, 0, 1, 0),
+
+SOC_SINGLE("Master Playback Volume", WM8510_DACVOL, 0, 127, 0),
+
+SOC_SINGLE("High Pass Filter Switch", WM8510_ADC, 8, 1, 0),
+SOC_SINGLE("High Pass Cut Off", WM8510_ADC, 4, 7, 0),
+SOC_SINGLE("ADC Inversion Switch", WM8510_COMP, 0, 1, 0),
+
+SOC_SINGLE("Capture Volume", WM8510_ADCVOL, 0, 127, 0),
+
+SOC_SINGLE("DAC Playback Limiter Switch", WM8510_DACLIM1, 8, 1, 0),
+SOC_SINGLE("DAC Playback Limiter Decay", WM8510_DACLIM1, 4, 15, 0),
+SOC_SINGLE("DAC Playback Limiter Attack", WM8510_DACLIM1, 0, 15, 0),
+
+SOC_SINGLE("DAC Playback Limiter Threshold", WM8510_DACLIM2, 4, 7, 0),
+SOC_SINGLE("DAC Playback Limiter Boost", WM8510_DACLIM2, 0, 15, 0),
+
+SOC_SINGLE("ALC Enable Switch", WM8510_ALC1, 8, 1, 0),
+SOC_SINGLE("ALC Capture Max Gain", WM8510_ALC1, 3, 7, 0),
+SOC_SINGLE("ALC Capture Min Gain", WM8510_ALC1, 0, 7, 0),
+
+SOC_SINGLE("ALC Capture ZC Switch", WM8510_ALC2, 8, 1, 0),
+SOC_SINGLE("ALC Capture Hold", WM8510_ALC2, 4, 7, 0),
+SOC_SINGLE("ALC Capture Target", WM8510_ALC2, 0, 15, 0),
+
+SOC_ENUM("ALC Capture Mode", wm8510_enum[3]),
+SOC_SINGLE("ALC Capture Decay", WM8510_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Capture Attack", WM8510_ALC3, 0, 15, 0),
+
+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8510_NGATE, 3, 1, 0),
+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8510_NGATE, 0, 7, 0),
+
+SOC_SINGLE("Capture PGA ZC Switch", WM8510_INPPGA, 7, 1, 0),
+SOC_SINGLE("Capture PGA Volume", WM8510_INPPGA, 0, 63, 0),
+
+SOC_SINGLE("Speaker Playback ZC Switch", WM8510_SPKVOL, 7, 1, 0),
+SOC_SINGLE("Speaker Playback Switch", WM8510_SPKVOL, 6, 1, 1),
+SOC_SINGLE("Speaker Playback Volume", WM8510_SPKVOL, 0, 63, 0),
+SOC_SINGLE("Speaker Boost", WM8510_OUTPUT, 2, 1, 0),
+
+SOC_SINGLE("Capture Boost(+20dB)", WM8510_ADCBOOST, 8, 1, 0),
+SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 1),
+};
+
+/* Speaker Output Mixer */
+static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_SPKMIX, 5, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_SPKMIX, 0, 1, 0),
+};
+
+/* Mono Output Mixer */
+static const struct snd_kcontrol_new wm8510_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_MONOMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_MONOMIX, 2, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8510_boost_controls[] = {
+SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA, 6, 1, 1),
+SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0),
+SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0),
+};
+
+static const struct snd_kcontrol_new wm8510_micpga_controls[] = {
+SOC_DAPM_SINGLE("MICP Switch", WM8510_INPUT, 0, 1, 0),
+SOC_DAPM_SINGLE("MICN Switch", WM8510_INPUT, 1, 1, 0),
+SOC_DAPM_SINGLE("AUX Switch", WM8510_INPUT, 2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8510_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8510_POWER3, 2, 0,
+ &wm8510_speaker_mixer_controls[0],
+ ARRAY_SIZE(wm8510_speaker_mixer_controls)),
+SND_SOC_DAPM_MIXER("Mono Mixer", WM8510_POWER3, 3, 0,
+ &wm8510_mono_mixer_controls[0],
+ ARRAY_SIZE(wm8510_mono_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8510_POWER3, 0, 0),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8510_POWER2, 0, 0),
+SND_SOC_DAPM_PGA("Aux Input", WM8510_POWER1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("Mic PGA", WM8510_POWER2, 2, 0,
+ &wm8510_micpga_controls[0],
+ ARRAY_SIZE(wm8510_micpga_controls)),
+SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0,
+ &wm8510_boost_controls[0],
+ ARRAY_SIZE(wm8510_boost_controls)),
+
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8510_POWER1, 4, 0),
+
+SND_SOC_DAPM_INPUT("MICN"),
+SND_SOC_DAPM_INPUT("MICP"),
+SND_SOC_DAPM_INPUT("AUX"),
+SND_SOC_DAPM_OUTPUT("MONOOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+};
+
+static const struct snd_soc_dapm_route wm8510_dapm_routes[] = {
+ /* Mono output mixer */
+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+ /* Speaker output mixer */
+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+ /* Outputs */
+ {"Mono Out", NULL, "Mono Mixer"},
+ {"MONOOUT", NULL, "Mono Out"},
+ {"SpkN Out", NULL, "Speaker Mixer"},
+ {"SpkP Out", NULL, "Speaker Mixer"},
+ {"SPKOUTN", NULL, "SpkN Out"},
+ {"SPKOUTP", NULL, "SpkP Out"},
+
+ /* Microphone PGA */
+ {"Mic PGA", "MICN Switch", "MICN"},
+ {"Mic PGA", "MICP Switch", "MICP"},
+ { "Mic PGA", "AUX Switch", "Aux Input" },
+
+ /* Boost Mixer */
+ {"Boost Mixer", "Mic PGA Switch", "Mic PGA"},
+ {"Boost Mixer", "Mic Volume", "MICP"},
+ {"Boost Mixer", "Aux Volume", "Aux Input"},
+
+ {"ADC", NULL, "Boost Mixer"},
+};
+
+struct pll_ {
+ unsigned int pre_div:4; /* prescale - 1 */
+ unsigned int n:4;
+ unsigned int k;
+};
+
+static struct pll_ pll_div;
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+
+static void pll_factors(unsigned int target, unsigned int source)
+{
+ unsigned long long Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div.pre_div = 1;
+ Ndiv = target / source;
+ } else
+ pll_div.pre_div = 0;
+
+ if ((Ndiv < 6) || (Ndiv > 12))
+ printk(KERN_WARNING
+ "WM8510 N value %u outwith recommended range!d\n",
+ Ndiv);
+
+ pll_div.n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div.k = K;
+}
+
+static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ if (freq_in == 0 || freq_out == 0) {
+ /* Clock CODEC directly from MCLK */
+ reg = snd_soc_read(codec, WM8510_CLOCK);
+ snd_soc_write(codec, WM8510_CLOCK, reg & 0x0ff);
+
+ /* Turn off PLL */
+ reg = snd_soc_read(codec, WM8510_POWER1);
+ snd_soc_write(codec, WM8510_POWER1, reg & 0x1df);
+ return 0;
+ }
+
+ pll_factors(freq_out*4, freq_in);
+
+ snd_soc_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
+ snd_soc_write(codec, WM8510_PLLK1, pll_div.k >> 18);
+ snd_soc_write(codec, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8510_PLLK3, pll_div.k & 0x1ff);
+ reg = snd_soc_read(codec, WM8510_POWER1);
+ snd_soc_write(codec, WM8510_POWER1, reg | 0x020);
+
+ /* Run CODEC from PLL instead of MCLK */
+ reg = snd_soc_read(codec, WM8510_CLOCK);
+ snd_soc_write(codec, WM8510_CLOCK, reg | 0x100);
+
+ return 0;
+}
+
+/*
+ * Configure WM8510 clock dividers.
+ */
+static int wm8510_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8510_OPCLKDIV:
+ reg = snd_soc_read(codec, WM8510_GPIO) & 0x1cf;
+ snd_soc_write(codec, WM8510_GPIO, reg | div);
+ break;
+ case WM8510_MCLKDIV:
+ reg = snd_soc_read(codec, WM8510_CLOCK) & 0x11f;
+ snd_soc_write(codec, WM8510_CLOCK, reg | div);
+ break;
+ case WM8510_ADCCLK:
+ reg = snd_soc_read(codec, WM8510_ADC) & 0x1f7;
+ snd_soc_write(codec, WM8510_ADC, reg | div);
+ break;
+ case WM8510_DACCLK:
+ reg = snd_soc_read(codec, WM8510_DAC) & 0x1f7;
+ snd_soc_write(codec, WM8510_DAC, reg | div);
+ break;
+ case WM8510_BCLKDIV:
+ reg = snd_soc_read(codec, WM8510_CLOCK) & 0x1e3;
+ snd_soc_write(codec, WM8510_CLOCK, reg | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+ u16 clk = snd_soc_read(codec, WM8510_CLOCK) & 0x1fe;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ clk |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0010;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0008;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x00018;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0180;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0100;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0080;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8510_IFACE, iface);
+ snd_soc_write(codec, WM8510_CLOCK, clk);
+ return 0;
+}
+
+static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 iface = snd_soc_read(codec, WM8510_IFACE) & 0x19f;
+ u16 adn = snd_soc_read(codec, WM8510_ADD) & 0x1f1;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface |= 0x0020;
+ break;
+ case 24:
+ iface |= 0x0040;
+ break;
+ case 32:
+ iface |= 0x0060;
+ break;
+ }
+
+ /* filter coefficient */
+ switch (params_rate(params)) {
+ case 8000:
+ adn |= 0x5 << 1;
+ break;
+ case 11025:
+ adn |= 0x4 << 1;
+ break;
+ case 16000:
+ adn |= 0x3 << 1;
+ break;
+ case 22050:
+ adn |= 0x2 << 1;
+ break;
+ case 32000:
+ adn |= 0x1 << 1;
+ break;
+ case 44100:
+ case 48000:
+ break;
+ }
+
+ snd_soc_write(codec, WM8510_IFACE, iface);
+ snd_soc_write(codec, WM8510_ADD, adn);
+ return 0;
+}
+
+static int wm8510_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8510_DAC) & 0xffbf;
+
+ if (mute)
+ snd_soc_write(codec, WM8510_DAC, mute_reg | 0x40);
+ else
+ snd_soc_write(codec, WM8510_DAC, mute_reg);
+ return 0;
+}
+
+/* liam need to make this lower power with dapm */
+static int wm8510_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8510_priv *wm8510 = snd_soc_codec_get_drvdata(codec);
+ u16 power1 = snd_soc_read(codec, WM8510_POWER1) & ~0x3;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ power1 |= 0x1; /* VMID 50k */
+ snd_soc_write(codec, WM8510_POWER1, power1);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN;
+
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ regcache_sync(wm8510->regmap);
+
+ /* Initial cap charge at VMID 5k */
+ snd_soc_write(codec, WM8510_POWER1, power1 | 0x3);
+ mdelay(100);
+ }
+
+ power1 |= 0x2; /* VMID 500k */
+ snd_soc_write(codec, WM8510_POWER1, power1);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, WM8510_POWER1, 0);
+ snd_soc_write(codec, WM8510_POWER2, 0);
+ snd_soc_write(codec, WM8510_POWER3, 0);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8510_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define WM8510_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8510_dai_ops = {
+ .hw_params = wm8510_pcm_hw_params,
+ .digital_mute = wm8510_mute,
+ .set_fmt = wm8510_set_dai_fmt,
+ .set_clkdiv = wm8510_set_dai_clkdiv,
+ .set_pll = wm8510_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver wm8510_dai = {
+ .name = "wm8510-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8510_RATES,
+ .formats = WM8510_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8510_RATES,
+ .formats = WM8510_FORMATS,},
+ .ops = &wm8510_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int wm8510_probe(struct snd_soc_codec *codec)
+{
+ wm8510_reset(codec);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8510 = {
+ .probe = wm8510_probe,
+ .set_bias_level = wm8510_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8510_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8510_snd_controls),
+ .dapm_widgets = wm8510_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8510_dapm_widgets),
+ .dapm_routes = wm8510_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8510_dapm_routes),
+};
+
+static const struct of_device_id wm8510_of_match[] = {
+ { .compatible = "wlf,wm8510" },
+ { },
+};
+
+static const struct regmap_config wm8510_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8510_MONOMIX,
+
+ .reg_defaults = wm8510_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8510_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8510_volatile,
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8510_spi_probe(struct spi_device *spi)
+{
+ struct wm8510_priv *wm8510;
+ int ret;
+
+ wm8510 = devm_kzalloc(&spi->dev, sizeof(struct wm8510_priv),
+ GFP_KERNEL);
+ if (wm8510 == NULL)
+ return -ENOMEM;
+
+ wm8510->regmap = devm_regmap_init_spi(spi, &wm8510_regmap);
+ if (IS_ERR(wm8510->regmap))
+ return PTR_ERR(wm8510->regmap);
+
+ spi_set_drvdata(spi, wm8510);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8510, &wm8510_dai, 1);
+
+ return ret;
+}
+
+static int wm8510_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver wm8510_spi_driver = {
+ .driver = {
+ .name = "wm8510",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8510_of_match,
+ },
+ .probe = wm8510_spi_probe,
+ .remove = wm8510_spi_remove,
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8510_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8510_priv *wm8510;
+ int ret;
+
+ wm8510 = devm_kzalloc(&i2c->dev, sizeof(struct wm8510_priv),
+ GFP_KERNEL);
+ if (wm8510 == NULL)
+ return -ENOMEM;
+
+ wm8510->regmap = devm_regmap_init_i2c(i2c, &wm8510_regmap);
+ if (IS_ERR(wm8510->regmap))
+ return PTR_ERR(wm8510->regmap);
+
+ i2c_set_clientdata(i2c, wm8510);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8510, &wm8510_dai, 1);
+
+ return ret;
+}
+
+static int wm8510_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8510_i2c_id[] = {
+ { "wm8510", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id);
+
+static struct i2c_driver wm8510_i2c_driver = {
+ .driver = {
+ .name = "wm8510",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8510_of_match,
+ },
+ .probe = wm8510_i2c_probe,
+ .remove = wm8510_i2c_remove,
+ .id_table = wm8510_i2c_id,
+};
+#endif
+
+static int __init wm8510_modinit(void)
+{
+ int ret = 0;
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8510_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8510 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8510_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8510 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(wm8510_modinit);
+
+static void __exit wm8510_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8510_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8510_spi_driver);
+#endif
+}
+module_exit(wm8510_exit);
+
+MODULE_DESCRIPTION("ASoC WM8510 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8510.h b/kernel/sound/soc/codecs/wm8510.h
new file mode 100644
index 000000000..b3e26ed9f
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8510.h
@@ -0,0 +1,102 @@
+/*
+ * wm8510.h -- WM8510 Soc Audio driver
+ *
+ * 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 _WM8510_H
+#define _WM8510_H
+
+/* WM8510 register space */
+
+#define WM8510_RESET 0x0
+#define WM8510_POWER1 0x1
+#define WM8510_POWER2 0x2
+#define WM8510_POWER3 0x3
+#define WM8510_IFACE 0x4
+#define WM8510_COMP 0x5
+#define WM8510_CLOCK 0x6
+#define WM8510_ADD 0x7
+#define WM8510_GPIO 0x8
+#define WM8510_DAC 0xa
+#define WM8510_DACVOL 0xb
+#define WM8510_ADC 0xe
+#define WM8510_ADCVOL 0xf
+#define WM8510_EQ1 0x12
+#define WM8510_EQ2 0x13
+#define WM8510_EQ3 0x14
+#define WM8510_EQ4 0x15
+#define WM8510_EQ5 0x16
+#define WM8510_DACLIM1 0x18
+#define WM8510_DACLIM2 0x19
+#define WM8510_NOTCH1 0x1b
+#define WM8510_NOTCH2 0x1c
+#define WM8510_NOTCH3 0x1d
+#define WM8510_NOTCH4 0x1e
+#define WM8510_ALC1 0x20
+#define WM8510_ALC2 0x21
+#define WM8510_ALC3 0x22
+#define WM8510_NGATE 0x23
+#define WM8510_PLLN 0x24
+#define WM8510_PLLK1 0x25
+#define WM8510_PLLK2 0x26
+#define WM8510_PLLK3 0x27
+#define WM8510_ATTEN 0x28
+#define WM8510_INPUT 0x2c
+#define WM8510_INPPGA 0x2d
+#define WM8510_ADCBOOST 0x2f
+#define WM8510_OUTPUT 0x31
+#define WM8510_SPKMIX 0x32
+#define WM8510_SPKVOL 0x36
+#define WM8510_MONOMIX 0x38
+
+#define WM8510_CACHEREGNUM 57
+
+/* Clock divider Id's */
+#define WM8510_OPCLKDIV 0
+#define WM8510_MCLKDIV 1
+#define WM8510_ADCCLK 2
+#define WM8510_DACCLK 3
+#define WM8510_BCLKDIV 4
+
+/* DAC clock dividers */
+#define WM8510_DACCLK_F2 (1 << 3)
+#define WM8510_DACCLK_F4 (0 << 3)
+
+/* ADC clock dividers */
+#define WM8510_ADCCLK_F2 (1 << 3)
+#define WM8510_ADCCLK_F4 (0 << 3)
+
+/* PLL Out dividers */
+#define WM8510_OPCLKDIV_1 (0 << 4)
+#define WM8510_OPCLKDIV_2 (1 << 4)
+#define WM8510_OPCLKDIV_3 (2 << 4)
+#define WM8510_OPCLKDIV_4 (3 << 4)
+
+/* BCLK clock dividers */
+#define WM8510_BCLKDIV_1 (0 << 2)
+#define WM8510_BCLKDIV_2 (1 << 2)
+#define WM8510_BCLKDIV_4 (2 << 2)
+#define WM8510_BCLKDIV_8 (3 << 2)
+#define WM8510_BCLKDIV_16 (4 << 2)
+#define WM8510_BCLKDIV_32 (5 << 2)
+
+/* MCLK clock dividers */
+#define WM8510_MCLKDIV_1 (0 << 5)
+#define WM8510_MCLKDIV_1_5 (1 << 5)
+#define WM8510_MCLKDIV_2 (2 << 5)
+#define WM8510_MCLKDIV_3 (3 << 5)
+#define WM8510_MCLKDIV_4 (4 << 5)
+#define WM8510_MCLKDIV_6 (5 << 5)
+#define WM8510_MCLKDIV_8 (6 << 5)
+#define WM8510_MCLKDIV_12 (7 << 5)
+
+struct wm8510_setup_data {
+ int spi;
+ int i2c_bus;
+ unsigned short i2c_address;
+};
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8523.c b/kernel/sound/soc/codecs/wm8523.c
new file mode 100644
index 000000000..b1cc94f5f
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8523.c
@@ -0,0 +1,545 @@
+/*
+ * wm8523.c -- WM8523 ALSA SoC Audio driver
+ *
+ * Copyright 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 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/i2c.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/of_device.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 "wm8523.h"
+
+#define WM8523_NUM_SUPPLIES 2
+static const char *wm8523_supply_names[WM8523_NUM_SUPPLIES] = {
+ "AVDD",
+ "LINEVDD",
+};
+
+#define WM8523_NUM_RATES 7
+
+/* codec private data */
+struct wm8523_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[WM8523_NUM_SUPPLIES];
+ unsigned int sysclk;
+ unsigned int rate_constraint_list[WM8523_NUM_RATES];
+ struct snd_pcm_hw_constraint_list rate_constraint;
+};
+
+static const struct reg_default wm8523_reg_defaults[] = {
+ { 2, 0x0000 }, /* R2 - PSCTRL1 */
+ { 3, 0x1812 }, /* R3 - AIF_CTRL1 */
+ { 4, 0x0000 }, /* R4 - AIF_CTRL2 */
+ { 5, 0x0001 }, /* R5 - DAC_CTRL3 */
+ { 6, 0x0190 }, /* R6 - DAC_GAINL */
+ { 7, 0x0190 }, /* R7 - DAC_GAINR */
+ { 8, 0x0000 }, /* R8 - ZERO_DETECT */
+};
+
+static bool wm8523_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8523_DEVICE_ID:
+ case WM8523_REVISION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -10000, 25, 0);
+
+static const char *wm8523_zd_count_text[] = {
+ "1024",
+ "2048",
+};
+
+static SOC_ENUM_SINGLE_DECL(wm8523_zc_count, WM8523_ZERO_DETECT, 0,
+ wm8523_zd_count_text);
+
+static const struct snd_kcontrol_new wm8523_controls[] = {
+SOC_DOUBLE_R_TLV("Playback Volume", WM8523_DAC_GAINL, WM8523_DAC_GAINR,
+ 0, 448, 0, dac_tlv),
+SOC_SINGLE("ZC Switch", WM8523_DAC_CTRL3, 4, 1, 0),
+SOC_SINGLE("Playback Deemphasis Switch", WM8523_AIF_CTRL1, 8, 1, 0),
+SOC_DOUBLE("Playback Switch", WM8523_DAC_CTRL3, 2, 3, 1, 1),
+SOC_SINGLE("Volume Ramp Up Switch", WM8523_DAC_CTRL3, 1, 1, 0),
+SOC_SINGLE("Volume Ramp Down Switch", WM8523_DAC_CTRL3, 0, 1, 0),
+SOC_ENUM("Zero Detect Count", wm8523_zc_count),
+};
+
+static const struct snd_soc_dapm_widget wm8523_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("LINEVOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEVOUTR"),
+};
+
+static const struct snd_soc_dapm_route wm8523_dapm_routes[] = {
+ { "LINEVOUTL", NULL, "DAC" },
+ { "LINEVOUTR", NULL, "DAC" },
+};
+
+static struct {
+ int value;
+ int ratio;
+} lrclk_ratios[WM8523_NUM_RATES] = {
+ { 1, 128 },
+ { 2, 192 },
+ { 3, 256 },
+ { 4, 384 },
+ { 5, 512 },
+ { 6, 768 },
+ { 7, 1152 },
+};
+
+static int wm8523_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
+
+ /* The set of sample rates that can be supported depends on the
+ * MCLK supplied to the CODEC - enforce this.
+ */
+ if (!wm8523->sysclk) {
+ dev_err(codec->dev,
+ "No MCLK configured, call set_sysclk() on init\n");
+ return -EINVAL;
+ }
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &wm8523->rate_constraint);
+
+ return 0;
+}
+
+static int wm8523_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
+ int i;
+ u16 aifctrl1 = snd_soc_read(codec, WM8523_AIF_CTRL1);
+ u16 aifctrl2 = snd_soc_read(codec, WM8523_AIF_CTRL2);
+
+ /* Find a supported LRCLK ratio */
+ for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
+ if (wm8523->sysclk / params_rate(params) ==
+ lrclk_ratios[i].ratio)
+ break;
+ }
+
+ /* Should never happen, should be handled by constraints */
+ if (i == ARRAY_SIZE(lrclk_ratios)) {
+ dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n",
+ wm8523->sysclk / params_rate(params));
+ return -EINVAL;
+ }
+
+ aifctrl2 &= ~WM8523_SR_MASK;
+ aifctrl2 |= lrclk_ratios[i].value;
+
+ aifctrl1 &= ~WM8523_WL_MASK;
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ aifctrl1 |= 0x8;
+ break;
+ case 24:
+ aifctrl1 |= 0x10;
+ break;
+ case 32:
+ aifctrl1 |= 0x18;
+ break;
+ }
+
+ snd_soc_write(codec, WM8523_AIF_CTRL1, aifctrl1);
+ snd_soc_write(codec, WM8523_AIF_CTRL2, aifctrl2);
+
+ return 0;
+}
+
+static int wm8523_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+ int i;
+
+ wm8523->sysclk = freq;
+
+ wm8523->rate_constraint.count = 0;
+ for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
+ val = freq / lrclk_ratios[i].ratio;
+ /* Check that it's a standard rate since core can't
+ * cope with others and having the odd rates confuses
+ * constraint matching.
+ */
+ switch (val) {
+ case 8000:
+ case 11025:
+ case 16000:
+ case 22050:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 64000:
+ case 88200:
+ case 96000:
+ case 176400:
+ case 192000:
+ dev_dbg(codec->dev, "Supported sample rate: %dHz\n",
+ val);
+ wm8523->rate_constraint_list[i] = val;
+ wm8523->rate_constraint.count++;
+ break;
+ default:
+ dev_dbg(codec->dev, "Skipping sample rate: %dHz\n",
+ val);
+ }
+ }
+
+ /* Need at least one supported rate... */
+ if (wm8523->rate_constraint.count == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static int wm8523_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 aifctrl1 = snd_soc_read(codec, WM8523_AIF_CTRL1);
+
+ aifctrl1 &= ~(WM8523_BCLK_INV_MASK | WM8523_LRCLK_INV_MASK |
+ WM8523_FMT_MASK | WM8523_AIF_MSTR_MASK);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aifctrl1 |= WM8523_AIF_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ aifctrl1 |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aifctrl1 |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ aifctrl1 |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aifctrl1 |= 0x0023;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aifctrl1 |= WM8523_BCLK_INV | WM8523_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aifctrl1 |= WM8523_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aifctrl1 |= WM8523_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8523_AIF_CTRL1, aifctrl1);
+
+ return 0;
+}
+
+static int wm8523_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* Full power on */
+ snd_soc_update_bits(codec, WM8523_PSCTRL1,
+ WM8523_SYS_ENA_MASK, 3);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
+ wm8523->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* Sync back default/cached values */
+ regcache_sync(wm8523->regmap);
+
+ /* Initial power up */
+ snd_soc_update_bits(codec, WM8523_PSCTRL1,
+ WM8523_SYS_ENA_MASK, 1);
+
+ msleep(100);
+ }
+
+ /* Power up to mute */
+ snd_soc_update_bits(codec, WM8523_PSCTRL1,
+ WM8523_SYS_ENA_MASK, 2);
+
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* The chip runs through the power down sequence for us. */
+ snd_soc_update_bits(codec, WM8523_PSCTRL1,
+ WM8523_SYS_ENA_MASK, 0);
+ msleep(100);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies),
+ wm8523->supplies);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8523_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM8523_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8523_dai_ops = {
+ .startup = wm8523_startup,
+ .hw_params = wm8523_hw_params,
+ .set_sysclk = wm8523_set_dai_sysclk,
+ .set_fmt = wm8523_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver wm8523_dai = {
+ .name = "wm8523-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2, /* Mono modes not yet supported */
+ .channels_max = 2,
+ .rates = WM8523_RATES,
+ .formats = WM8523_FORMATS,
+ },
+ .ops = &wm8523_dai_ops,
+};
+
+static int wm8523_probe(struct snd_soc_codec *codec)
+{
+ struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
+
+ wm8523->rate_constraint.list = &wm8523->rate_constraint_list[0];
+ wm8523->rate_constraint.count =
+ ARRAY_SIZE(wm8523->rate_constraint_list);
+
+ /* Change some default settings - latch VU and enable ZC */
+ snd_soc_update_bits(codec, WM8523_DAC_GAINR,
+ WM8523_DACR_VU, WM8523_DACR_VU);
+ snd_soc_update_bits(codec, WM8523_DAC_CTRL3, WM8523_ZC, WM8523_ZC);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8523 = {
+ .probe = wm8523_probe,
+ .set_bias_level = wm8523_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8523_controls,
+ .num_controls = ARRAY_SIZE(wm8523_controls),
+ .dapm_widgets = wm8523_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8523_dapm_widgets),
+ .dapm_routes = wm8523_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8523_dapm_routes),
+};
+
+static const struct of_device_id wm8523_of_match[] = {
+ { .compatible = "wlf,wm8523" },
+ { },
+};
+
+static const struct regmap_config wm8523_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = WM8523_ZERO_DETECT,
+
+ .reg_defaults = wm8523_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8523_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8523_volatile_register,
+};
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8523_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8523_priv *wm8523;
+ unsigned int val;
+ int ret, i;
+
+ wm8523 = devm_kzalloc(&i2c->dev, sizeof(struct wm8523_priv),
+ GFP_KERNEL);
+ if (wm8523 == NULL)
+ return -ENOMEM;
+
+ wm8523->regmap = devm_regmap_init_i2c(i2c, &wm8523_regmap);
+ if (IS_ERR(wm8523->regmap)) {
+ ret = PTR_ERR(wm8523->regmap);
+ dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8523->supplies); i++)
+ wm8523->supplies[i].supply = wm8523_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8523->supplies),
+ wm8523->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
+ wm8523->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(wm8523->regmap, WM8523_DEVICE_ID, &val);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read ID register\n");
+ goto err_enable;
+ }
+ if (val != 0x8523) {
+ dev_err(&i2c->dev, "Device is not a WM8523, ID is %x\n", ret);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ ret = regmap_read(wm8523->regmap, WM8523_REVISION, &val);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read revision register\n");
+ goto err_enable;
+ }
+ dev_info(&i2c->dev, "revision %c\n",
+ (val & WM8523_CHIP_REV_MASK) + 'A');
+
+ ret = regmap_write(wm8523->regmap, WM8523_DEVICE_ID, 0x8523);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to reset device: %d\n", ret);
+ goto err_enable;
+ }
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+
+ i2c_set_clientdata(i2c, wm8523);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8523, &wm8523_dai, 1);
+
+ return ret;
+
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+ return ret;
+}
+
+static int wm8523_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8523_i2c_id[] = {
+ { "wm8523", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id);
+
+static struct i2c_driver wm8523_i2c_driver = {
+ .driver = {
+ .name = "wm8523",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8523_of_match,
+ },
+ .probe = wm8523_i2c_probe,
+ .remove = wm8523_i2c_remove,
+ .id_table = wm8523_i2c_id,
+};
+#endif
+
+static int __init wm8523_modinit(void)
+{
+ int ret;
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8523_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8523 I2C driver: %d\n",
+ ret);
+ }
+#endif
+ return 0;
+}
+module_init(wm8523_modinit);
+
+static void __exit wm8523_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8523_i2c_driver);
+#endif
+}
+module_exit(wm8523_exit);
+
+MODULE_DESCRIPTION("ASoC WM8523 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8523.h b/kernel/sound/soc/codecs/wm8523.h
new file mode 100644
index 000000000..4d5b1eb8f
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8523.h
@@ -0,0 +1,157 @@
+/*
+ * wm8523.h -- WM8423 ASoC driver
+ *
+ * Copyright 2009 Wolfson Microelectronics, plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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 _WM8523_H
+#define _WM8523_H
+
+/*
+ * Register values.
+ */
+#define WM8523_DEVICE_ID 0x00
+#define WM8523_REVISION 0x01
+#define WM8523_PSCTRL1 0x02
+#define WM8523_AIF_CTRL1 0x03
+#define WM8523_AIF_CTRL2 0x04
+#define WM8523_DAC_CTRL3 0x05
+#define WM8523_DAC_GAINL 0x06
+#define WM8523_DAC_GAINR 0x07
+#define WM8523_ZERO_DETECT 0x08
+
+#define WM8523_REGISTER_COUNT 9
+#define WM8523_MAX_REGISTER 0x08
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - DEVICE_ID
+ */
+#define WM8523_CHIP_ID_MASK 0xFFFF /* CHIP_ID - [15:0] */
+#define WM8523_CHIP_ID_SHIFT 0 /* CHIP_ID - [15:0] */
+#define WM8523_CHIP_ID_WIDTH 16 /* CHIP_ID - [15:0] */
+
+/*
+ * R1 (0x01) - REVISION
+ */
+#define WM8523_CHIP_REV_MASK 0x0007 /* CHIP_REV - [2:0] */
+#define WM8523_CHIP_REV_SHIFT 0 /* CHIP_REV - [2:0] */
+#define WM8523_CHIP_REV_WIDTH 3 /* CHIP_REV - [2:0] */
+
+/*
+ * R2 (0x02) - PSCTRL1
+ */
+#define WM8523_SYS_ENA_MASK 0x0003 /* SYS_ENA - [1:0] */
+#define WM8523_SYS_ENA_SHIFT 0 /* SYS_ENA - [1:0] */
+#define WM8523_SYS_ENA_WIDTH 2 /* SYS_ENA - [1:0] */
+
+/*
+ * R3 (0x03) - AIF_CTRL1
+ */
+#define WM8523_TDM_MODE_MASK 0x1800 /* TDM_MODE - [12:11] */
+#define WM8523_TDM_MODE_SHIFT 11 /* TDM_MODE - [12:11] */
+#define WM8523_TDM_MODE_WIDTH 2 /* TDM_MODE - [12:11] */
+#define WM8523_TDM_SLOT_MASK 0x0600 /* TDM_SLOT - [10:9] */
+#define WM8523_TDM_SLOT_SHIFT 9 /* TDM_SLOT - [10:9] */
+#define WM8523_TDM_SLOT_WIDTH 2 /* TDM_SLOT - [10:9] */
+#define WM8523_DEEMPH 0x0100 /* DEEMPH */
+#define WM8523_DEEMPH_MASK 0x0100 /* DEEMPH */
+#define WM8523_DEEMPH_SHIFT 8 /* DEEMPH */
+#define WM8523_DEEMPH_WIDTH 1 /* DEEMPH */
+#define WM8523_AIF_MSTR 0x0080 /* AIF_MSTR */
+#define WM8523_AIF_MSTR_MASK 0x0080 /* AIF_MSTR */
+#define WM8523_AIF_MSTR_SHIFT 7 /* AIF_MSTR */
+#define WM8523_AIF_MSTR_WIDTH 1 /* AIF_MSTR */
+#define WM8523_LRCLK_INV 0x0040 /* LRCLK_INV */
+#define WM8523_LRCLK_INV_MASK 0x0040 /* LRCLK_INV */
+#define WM8523_LRCLK_INV_SHIFT 6 /* LRCLK_INV */
+#define WM8523_LRCLK_INV_WIDTH 1 /* LRCLK_INV */
+#define WM8523_BCLK_INV 0x0020 /* BCLK_INV */
+#define WM8523_BCLK_INV_MASK 0x0020 /* BCLK_INV */
+#define WM8523_BCLK_INV_SHIFT 5 /* BCLK_INV */
+#define WM8523_BCLK_INV_WIDTH 1 /* BCLK_INV */
+#define WM8523_WL_MASK 0x0018 /* WL - [4:3] */
+#define WM8523_WL_SHIFT 3 /* WL - [4:3] */
+#define WM8523_WL_WIDTH 2 /* WL - [4:3] */
+#define WM8523_FMT_MASK 0x0007 /* FMT - [2:0] */
+#define WM8523_FMT_SHIFT 0 /* FMT - [2:0] */
+#define WM8523_FMT_WIDTH 3 /* FMT - [2:0] */
+
+/*
+ * R4 (0x04) - AIF_CTRL2
+ */
+#define WM8523_DAC_OP_MUX_MASK 0x00C0 /* DAC_OP_MUX - [7:6] */
+#define WM8523_DAC_OP_MUX_SHIFT 6 /* DAC_OP_MUX - [7:6] */
+#define WM8523_DAC_OP_MUX_WIDTH 2 /* DAC_OP_MUX - [7:6] */
+#define WM8523_BCLKDIV_MASK 0x0038 /* BCLKDIV - [5:3] */
+#define WM8523_BCLKDIV_SHIFT 3 /* BCLKDIV - [5:3] */
+#define WM8523_BCLKDIV_WIDTH 3 /* BCLKDIV - [5:3] */
+#define WM8523_SR_MASK 0x0007 /* SR - [2:0] */
+#define WM8523_SR_SHIFT 0 /* SR - [2:0] */
+#define WM8523_SR_WIDTH 3 /* SR - [2:0] */
+
+/*
+ * R5 (0x05) - DAC_CTRL3
+ */
+#define WM8523_ZC 0x0010 /* ZC */
+#define WM8523_ZC_MASK 0x0010 /* ZC */
+#define WM8523_ZC_SHIFT 4 /* ZC */
+#define WM8523_ZC_WIDTH 1 /* ZC */
+#define WM8523_DACR 0x0008 /* DACR */
+#define WM8523_DACR_MASK 0x0008 /* DACR */
+#define WM8523_DACR_SHIFT 3 /* DACR */
+#define WM8523_DACR_WIDTH 1 /* DACR */
+#define WM8523_DACL 0x0004 /* DACL */
+#define WM8523_DACL_MASK 0x0004 /* DACL */
+#define WM8523_DACL_SHIFT 2 /* DACL */
+#define WM8523_DACL_WIDTH 1 /* DACL */
+#define WM8523_VOL_UP_RAMP 0x0002 /* VOL_UP_RAMP */
+#define WM8523_VOL_UP_RAMP_MASK 0x0002 /* VOL_UP_RAMP */
+#define WM8523_VOL_UP_RAMP_SHIFT 1 /* VOL_UP_RAMP */
+#define WM8523_VOL_UP_RAMP_WIDTH 1 /* VOL_UP_RAMP */
+#define WM8523_VOL_DOWN_RAMP 0x0001 /* VOL_DOWN_RAMP */
+#define WM8523_VOL_DOWN_RAMP_MASK 0x0001 /* VOL_DOWN_RAMP */
+#define WM8523_VOL_DOWN_RAMP_SHIFT 0 /* VOL_DOWN_RAMP */
+#define WM8523_VOL_DOWN_RAMP_WIDTH 1 /* VOL_DOWN_RAMP */
+
+/*
+ * R6 (0x06) - DAC_GAINL
+ */
+#define WM8523_DACL_VU 0x0200 /* DACL_VU */
+#define WM8523_DACL_VU_MASK 0x0200 /* DACL_VU */
+#define WM8523_DACL_VU_SHIFT 9 /* DACL_VU */
+#define WM8523_DACL_VU_WIDTH 1 /* DACL_VU */
+#define WM8523_DACL_VOL_MASK 0x01FF /* DACL_VOL - [8:0] */
+#define WM8523_DACL_VOL_SHIFT 0 /* DACL_VOL - [8:0] */
+#define WM8523_DACL_VOL_WIDTH 9 /* DACL_VOL - [8:0] */
+
+/*
+ * R7 (0x07) - DAC_GAINR
+ */
+#define WM8523_DACR_VU 0x0200 /* DACR_VU */
+#define WM8523_DACR_VU_MASK 0x0200 /* DACR_VU */
+#define WM8523_DACR_VU_SHIFT 9 /* DACR_VU */
+#define WM8523_DACR_VU_WIDTH 1 /* DACR_VU */
+#define WM8523_DACR_VOL_MASK 0x01FF /* DACR_VOL - [8:0] */
+#define WM8523_DACR_VOL_SHIFT 0 /* DACR_VOL - [8:0] */
+#define WM8523_DACR_VOL_WIDTH 9 /* DACR_VOL - [8:0] */
+
+/*
+ * R8 (0x08) - ZERO_DETECT
+ */
+#define WM8523_ZD_COUNT_MASK 0x0003 /* ZD_COUNT - [1:0] */
+#define WM8523_ZD_COUNT_SHIFT 0 /* ZD_COUNT - [1:0] */
+#define WM8523_ZD_COUNT_WIDTH 2 /* ZD_COUNT - [1:0] */
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8580.c b/kernel/sound/soc/codecs/wm8580.c
new file mode 100644
index 000000000..0a887c5ec
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8580.c
@@ -0,0 +1,1016 @@
+/*
+ * wm8580.c -- WM8580 ALSA Soc Audio driver
+ *
+ * Copyright 2008-12 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.
+ *
+ * Notes:
+ * The WM8580 is a multichannel codec with S/PDIF support, featuring six
+ * DAC channels and two ADC channels.
+ *
+ * Currently only the primary audio interface is supported - S/PDIF and
+ * the secondary audio interfaces are not.
+ */
+
+#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/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <asm/div64.h>
+
+#include "wm8580.h"
+
+/* WM8580 register space */
+#define WM8580_PLLA1 0x00
+#define WM8580_PLLA2 0x01
+#define WM8580_PLLA3 0x02
+#define WM8580_PLLA4 0x03
+#define WM8580_PLLB1 0x04
+#define WM8580_PLLB2 0x05
+#define WM8580_PLLB3 0x06
+#define WM8580_PLLB4 0x07
+#define WM8580_CLKSEL 0x08
+#define WM8580_PAIF1 0x09
+#define WM8580_PAIF2 0x0A
+#define WM8580_SAIF1 0x0B
+#define WM8580_PAIF3 0x0C
+#define WM8580_PAIF4 0x0D
+#define WM8580_SAIF2 0x0E
+#define WM8580_DAC_CONTROL1 0x0F
+#define WM8580_DAC_CONTROL2 0x10
+#define WM8580_DAC_CONTROL3 0x11
+#define WM8580_DAC_CONTROL4 0x12
+#define WM8580_DAC_CONTROL5 0x13
+#define WM8580_DIGITAL_ATTENUATION_DACL1 0x14
+#define WM8580_DIGITAL_ATTENUATION_DACR1 0x15
+#define WM8580_DIGITAL_ATTENUATION_DACL2 0x16
+#define WM8580_DIGITAL_ATTENUATION_DACR2 0x17
+#define WM8580_DIGITAL_ATTENUATION_DACL3 0x18
+#define WM8580_DIGITAL_ATTENUATION_DACR3 0x19
+#define WM8580_MASTER_DIGITAL_ATTENUATION 0x1C
+#define WM8580_ADC_CONTROL1 0x1D
+#define WM8580_SPDTXCHAN0 0x1E
+#define WM8580_SPDTXCHAN1 0x1F
+#define WM8580_SPDTXCHAN2 0x20
+#define WM8580_SPDTXCHAN3 0x21
+#define WM8580_SPDTXCHAN4 0x22
+#define WM8580_SPDTXCHAN5 0x23
+#define WM8580_SPDMODE 0x24
+#define WM8580_INTMASK 0x25
+#define WM8580_GPO1 0x26
+#define WM8580_GPO2 0x27
+#define WM8580_GPO3 0x28
+#define WM8580_GPO4 0x29
+#define WM8580_GPO5 0x2A
+#define WM8580_INTSTAT 0x2B
+#define WM8580_SPDRXCHAN1 0x2C
+#define WM8580_SPDRXCHAN2 0x2D
+#define WM8580_SPDRXCHAN3 0x2E
+#define WM8580_SPDRXCHAN4 0x2F
+#define WM8580_SPDRXCHAN5 0x30
+#define WM8580_SPDSTAT 0x31
+#define WM8580_PWRDN1 0x32
+#define WM8580_PWRDN2 0x33
+#define WM8580_READBACK 0x34
+#define WM8580_RESET 0x35
+
+#define WM8580_MAX_REGISTER 0x35
+
+#define WM8580_DACOSR 0x40
+
+/* PLLB4 (register 7h) */
+#define WM8580_PLLB4_MCLKOUTSRC_MASK 0x60
+#define WM8580_PLLB4_MCLKOUTSRC_PLLA 0x20
+#define WM8580_PLLB4_MCLKOUTSRC_PLLB 0x40
+#define WM8580_PLLB4_MCLKOUTSRC_OSC 0x60
+
+#define WM8580_PLLB4_CLKOUTSRC_MASK 0x180
+#define WM8580_PLLB4_CLKOUTSRC_PLLACLK 0x080
+#define WM8580_PLLB4_CLKOUTSRC_PLLBCLK 0x100
+#define WM8580_PLLB4_CLKOUTSRC_OSCCLK 0x180
+
+/* CLKSEL (register 8h) */
+#define WM8580_CLKSEL_DAC_CLKSEL_MASK 0x03
+#define WM8580_CLKSEL_DAC_CLKSEL_PLLA 0x01
+#define WM8580_CLKSEL_DAC_CLKSEL_PLLB 0x02
+
+/* AIF control 1 (registers 9h-bh) */
+#define WM8580_AIF_RATE_MASK 0x7
+#define WM8580_AIF_BCLKSEL_MASK 0x18
+
+#define WM8580_AIF_MS 0x20
+
+#define WM8580_AIF_CLKSRC_MASK 0xc0
+#define WM8580_AIF_CLKSRC_PLLA 0x40
+#define WM8580_AIF_CLKSRC_PLLB 0x40
+#define WM8580_AIF_CLKSRC_MCLK 0xc0
+
+/* AIF control 2 (registers ch-eh) */
+#define WM8580_AIF_FMT_MASK 0x03
+#define WM8580_AIF_FMT_RIGHTJ 0x00
+#define WM8580_AIF_FMT_LEFTJ 0x01
+#define WM8580_AIF_FMT_I2S 0x02
+#define WM8580_AIF_FMT_DSP 0x03
+
+#define WM8580_AIF_LENGTH_MASK 0x0c
+#define WM8580_AIF_LENGTH_16 0x00
+#define WM8580_AIF_LENGTH_20 0x04
+#define WM8580_AIF_LENGTH_24 0x08
+#define WM8580_AIF_LENGTH_32 0x0c
+
+#define WM8580_AIF_LRP 0x10
+#define WM8580_AIF_BCP 0x20
+
+/* Powerdown Register 1 (register 32h) */
+#define WM8580_PWRDN1_PWDN 0x001
+#define WM8580_PWRDN1_ALLDACPD 0x040
+
+/* Powerdown Register 2 (register 33h) */
+#define WM8580_PWRDN2_OSSCPD 0x001
+#define WM8580_PWRDN2_PLLAPD 0x002
+#define WM8580_PWRDN2_PLLBPD 0x004
+#define WM8580_PWRDN2_SPDIFPD 0x008
+#define WM8580_PWRDN2_SPDIFTXD 0x010
+#define WM8580_PWRDN2_SPDIFRXD 0x020
+
+#define WM8580_DAC_CONTROL5_MUTEALL 0x10
+
+/*
+ * wm8580 register cache
+ * We can't read the WM8580 register space when we
+ * are using 2 wire for device control, so we cache them instead.
+ */
+static const struct reg_default wm8580_reg_defaults[] = {
+ { 0, 0x0121 },
+ { 1, 0x017e },
+ { 2, 0x007d },
+ { 3, 0x0014 },
+ { 4, 0x0121 },
+ { 5, 0x017e },
+ { 6, 0x007d },
+ { 7, 0x0194 },
+ { 8, 0x0010 },
+ { 9, 0x0002 },
+ { 10, 0x0002 },
+ { 11, 0x00c2 },
+ { 12, 0x0182 },
+ { 13, 0x0082 },
+ { 14, 0x000a },
+ { 15, 0x0024 },
+ { 16, 0x0009 },
+ { 17, 0x0000 },
+ { 18, 0x00ff },
+ { 19, 0x0000 },
+ { 20, 0x00ff },
+ { 21, 0x00ff },
+ { 22, 0x00ff },
+ { 23, 0x00ff },
+ { 24, 0x00ff },
+ { 25, 0x00ff },
+ { 26, 0x00ff },
+ { 27, 0x00ff },
+ { 28, 0x01f0 },
+ { 29, 0x0040 },
+ { 30, 0x0000 },
+ { 31, 0x0000 },
+ { 32, 0x0000 },
+ { 33, 0x0000 },
+ { 34, 0x0031 },
+ { 35, 0x000b },
+ { 36, 0x0039 },
+ { 37, 0x0000 },
+ { 38, 0x0010 },
+ { 39, 0x0032 },
+ { 40, 0x0054 },
+ { 41, 0x0076 },
+ { 42, 0x0098 },
+ { 43, 0x0000 },
+ { 44, 0x0000 },
+ { 45, 0x0000 },
+ { 46, 0x0000 },
+ { 47, 0x0000 },
+ { 48, 0x0000 },
+ { 49, 0x0000 },
+ { 50, 0x005e },
+ { 51, 0x003e },
+ { 52, 0x0000 },
+};
+
+static bool wm8580_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8580_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+struct pll_state {
+ unsigned int in;
+ unsigned int out;
+};
+
+#define WM8580_NUM_SUPPLIES 3
+static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = {
+ "AVDD",
+ "DVDD",
+ "PVDD",
+};
+
+/* codec private data */
+struct wm8580_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES];
+ struct pll_state a;
+ struct pll_state b;
+ int sysclk[2];
+};
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+
+static int wm8580_out_vu(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ int ret;
+
+ /* Clear the register cache VU so we write without VU set */
+ regcache_cache_only(wm8580->regmap, true);
+ regmap_update_bits(wm8580->regmap, reg, 0x100, 0x000);
+ regmap_update_bits(wm8580->regmap, reg2, 0x100, 0x000);
+ regcache_cache_only(wm8580->regmap, false);
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /* Now write again with the volume update bit set */
+ snd_soc_update_bits(codec, reg, 0x100, 0x100);
+ snd_soc_update_bits(codec, reg2, 0x100, 0x100);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wm8580_snd_controls[] = {
+SOC_DOUBLE_R_EXT_TLV("DAC1 Playback Volume",
+ WM8580_DIGITAL_ATTENUATION_DACL1,
+ WM8580_DIGITAL_ATTENUATION_DACR1,
+ 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv),
+SOC_DOUBLE_R_EXT_TLV("DAC2 Playback Volume",
+ WM8580_DIGITAL_ATTENUATION_DACL2,
+ WM8580_DIGITAL_ATTENUATION_DACR2,
+ 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv),
+SOC_DOUBLE_R_EXT_TLV("DAC3 Playback Volume",
+ WM8580_DIGITAL_ATTENUATION_DACL3,
+ WM8580_DIGITAL_ATTENUATION_DACR3,
+ 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv),
+
+SOC_SINGLE("DAC1 Deemphasis Switch", WM8580_DAC_CONTROL3, 0, 1, 0),
+SOC_SINGLE("DAC2 Deemphasis Switch", WM8580_DAC_CONTROL3, 1, 1, 0),
+SOC_SINGLE("DAC3 Deemphasis Switch", WM8580_DAC_CONTROL3, 2, 1, 0),
+
+SOC_DOUBLE("DAC1 Invert Switch", WM8580_DAC_CONTROL4, 0, 1, 1, 0),
+SOC_DOUBLE("DAC2 Invert Switch", WM8580_DAC_CONTROL4, 2, 3, 1, 0),
+SOC_DOUBLE("DAC3 Invert Switch", WM8580_DAC_CONTROL4, 4, 5, 1, 0),
+
+SOC_SINGLE("DAC ZC Switch", WM8580_DAC_CONTROL5, 5, 1, 0),
+SOC_SINGLE("DAC1 Switch", WM8580_DAC_CONTROL5, 0, 1, 1),
+SOC_SINGLE("DAC2 Switch", WM8580_DAC_CONTROL5, 1, 1, 1),
+SOC_SINGLE("DAC3 Switch", WM8580_DAC_CONTROL5, 2, 1, 1),
+
+SOC_DOUBLE("Capture Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 1),
+SOC_SINGLE("Capture High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1),
+SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1),
+SND_SOC_DAPM_DAC("DAC3", "Playback", WM8580_PWRDN1, 4, 1),
+
+SND_SOC_DAPM_OUTPUT("VOUT1L"),
+SND_SOC_DAPM_OUTPUT("VOUT1R"),
+SND_SOC_DAPM_OUTPUT("VOUT2L"),
+SND_SOC_DAPM_OUTPUT("VOUT2R"),
+SND_SOC_DAPM_OUTPUT("VOUT3L"),
+SND_SOC_DAPM_OUTPUT("VOUT3R"),
+
+SND_SOC_DAPM_ADC("ADC", "Capture", WM8580_PWRDN1, 1, 1),
+
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+};
+
+static const struct snd_soc_dapm_route wm8580_dapm_routes[] = {
+ { "VOUT1L", NULL, "DAC1" },
+ { "VOUT1R", NULL, "DAC1" },
+
+ { "VOUT2L", NULL, "DAC2" },
+ { "VOUT2R", NULL, "DAC2" },
+
+ { "VOUT3L", NULL, "DAC3" },
+ { "VOUT3R", NULL, "DAC3" },
+
+ { "ADC", NULL, "AINL" },
+ { "ADC", NULL, "AINR" },
+};
+
+/* PLL divisors */
+struct _pll_div {
+ u32 prescale:1;
+ u32 postscale:1;
+ u32 freqmode:2;
+ u32 n:4;
+ u32 k:24;
+};
+
+/* The size in bits of the pll divide */
+#define FIXED_PLL_SIZE (1 << 22)
+
+/* PLL rate to output rate divisions */
+static struct {
+ unsigned int div;
+ unsigned int freqmode;
+ unsigned int postscale;
+} post_table[] = {
+ { 2, 0, 0 },
+ { 4, 0, 1 },
+ { 4, 1, 0 },
+ { 8, 1, 1 },
+ { 8, 2, 0 },
+ { 16, 2, 1 },
+ { 12, 3, 0 },
+ { 24, 3, 1 }
+};
+
+static int pll_factors(struct _pll_div *pll_div, unsigned int target,
+ unsigned int source)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod;
+ int i;
+
+ pr_debug("wm8580: PLL %uHz->%uHz\n", source, target);
+
+ /* Scale the output frequency up; the PLL should run in the
+ * region of 90-100MHz.
+ */
+ for (i = 0; i < ARRAY_SIZE(post_table); i++) {
+ if (target * post_table[i].div >= 90000000 &&
+ target * post_table[i].div <= 100000000) {
+ pll_div->freqmode = post_table[i].freqmode;
+ pll_div->postscale = post_table[i].postscale;
+ target *= post_table[i].div;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(post_table)) {
+ printk(KERN_ERR "wm8580: Unable to scale output frequency "
+ "%u\n", target);
+ return -EINVAL;
+ }
+
+ Ndiv = target / source;
+
+ if (Ndiv < 5) {
+ source /= 2;
+ pll_div->prescale = 1;
+ Ndiv = target / source;
+ } else
+ pll_div->prescale = 0;
+
+ if ((Ndiv < 5) || (Ndiv > 13)) {
+ printk(KERN_ERR
+ "WM8580 N=%u outside supported range\n", Ndiv);
+ return -EINVAL;
+ }
+
+ pll_div->n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ pll_div->k = K;
+
+ pr_debug("PLL %x.%x prescale %d freqmode %d postscale %d\n",
+ pll_div->n, pll_div->k, pll_div->prescale, pll_div->freqmode,
+ pll_div->postscale);
+
+ return 0;
+}
+
+static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ int offset;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
+ struct pll_state *state;
+ struct _pll_div pll_div;
+ unsigned int reg;
+ unsigned int pwr_mask;
+ int ret;
+
+ /* GCC isn't able to work out the ifs below for initialising/using
+ * pll_div so suppress warnings.
+ */
+ memset(&pll_div, 0, sizeof(pll_div));
+
+ switch (pll_id) {
+ case WM8580_PLLA:
+ state = &wm8580->a;
+ offset = 0;
+ pwr_mask = WM8580_PWRDN2_PLLAPD;
+ break;
+ case WM8580_PLLB:
+ state = &wm8580->b;
+ offset = 4;
+ pwr_mask = WM8580_PWRDN2_PLLBPD;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ if (freq_in && freq_out) {
+ ret = pll_factors(&pll_div, freq_out, freq_in);
+ if (ret != 0)
+ return ret;
+ }
+
+ state->in = freq_in;
+ state->out = freq_out;
+
+ /* Always disable the PLL - it is not safe to leave it running
+ * while reprogramming it.
+ */
+ snd_soc_update_bits(codec, WM8580_PWRDN2, pwr_mask, pwr_mask);
+
+ if (!freq_in || !freq_out)
+ return 0;
+
+ snd_soc_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff);
+ snd_soc_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8580_PLLA3 + offset,
+ (pll_div.k >> 18 & 0xf) | (pll_div.n << 4));
+
+ reg = snd_soc_read(codec, WM8580_PLLA4 + offset);
+ reg &= ~0x1b;
+ reg |= pll_div.prescale | pll_div.postscale << 1 |
+ pll_div.freqmode << 3;
+
+ snd_soc_write(codec, WM8580_PLLA4 + offset, reg);
+
+ /* All done, turn it on */
+ snd_soc_update_bits(codec, WM8580_PWRDN2, pwr_mask, 0);
+
+ return 0;
+}
+
+static const int wm8580_sysclk_ratios[] = {
+ 128, 192, 256, 384, 512, 768, 1152,
+};
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
+ u16 paifa = 0;
+ u16 paifb = 0;
+ int i, ratio, osr;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ paifa |= 0x8;
+ break;
+ case 20:
+ paifa |= 0x0;
+ paifb |= WM8580_AIF_LENGTH_20;
+ break;
+ case 24:
+ paifa |= 0x0;
+ paifb |= WM8580_AIF_LENGTH_24;
+ break;
+ case 32:
+ paifa |= 0x0;
+ paifb |= WM8580_AIF_LENGTH_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Look up the SYSCLK ratio; accept only exact matches */
+ ratio = wm8580->sysclk[dai->driver->id] / params_rate(params);
+ for (i = 0; i < ARRAY_SIZE(wm8580_sysclk_ratios); i++)
+ if (ratio == wm8580_sysclk_ratios[i])
+ break;
+ if (i == ARRAY_SIZE(wm8580_sysclk_ratios)) {
+ dev_err(codec->dev, "Invalid clock ratio %d/%d\n",
+ wm8580->sysclk[dai->driver->id], params_rate(params));
+ return -EINVAL;
+ }
+ paifa |= i;
+ dev_dbg(codec->dev, "Running at %dfs with %dHz clock\n",
+ wm8580_sysclk_ratios[i], wm8580->sysclk[dai->driver->id]);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (ratio) {
+ case 128:
+ case 192:
+ osr = WM8580_DACOSR;
+ dev_dbg(codec->dev, "Selecting 64x OSR\n");
+ break;
+ default:
+ osr = 0;
+ dev_dbg(codec->dev, "Selecting 128x OSR\n");
+ break;
+ }
+
+ snd_soc_update_bits(codec, WM8580_PAIF3, WM8580_DACOSR, osr);
+ }
+
+ snd_soc_update_bits(codec, WM8580_PAIF1 + dai->driver->id,
+ WM8580_AIF_RATE_MASK | WM8580_AIF_BCLKSEL_MASK,
+ paifa);
+ snd_soc_update_bits(codec, WM8580_PAIF3 + dai->driver->id,
+ WM8580_AIF_LENGTH_MASK, paifb);
+ return 0;
+}
+
+static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int aifa;
+ unsigned int aifb;
+ int can_invert_lrclk;
+
+ aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->driver->id);
+ aifb = snd_soc_read(codec, WM8580_PAIF3 + codec_dai->driver->id);
+
+ aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ aifa &= ~WM8580_AIF_MS;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aifa |= WM8580_AIF_MS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ can_invert_lrclk = 1;
+ aifb |= WM8580_AIF_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ can_invert_lrclk = 1;
+ aifb |= WM8580_AIF_FMT_RIGHTJ;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ can_invert_lrclk = 1;
+ aifb |= WM8580_AIF_FMT_LEFTJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ can_invert_lrclk = 0;
+ aifb |= WM8580_AIF_FMT_DSP;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ can_invert_lrclk = 0;
+ aifb |= WM8580_AIF_FMT_DSP;
+ aifb |= WM8580_AIF_LRP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+
+ case SND_SOC_DAIFMT_IB_IF:
+ if (!can_invert_lrclk)
+ return -EINVAL;
+ aifb |= WM8580_AIF_BCP;
+ aifb |= WM8580_AIF_LRP;
+ break;
+
+ case SND_SOC_DAIFMT_IB_NF:
+ aifb |= WM8580_AIF_BCP;
+ break;
+
+ case SND_SOC_DAIFMT_NB_IF:
+ if (!can_invert_lrclk)
+ return -EINVAL;
+ aifb |= WM8580_AIF_LRP;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8580_PAIF1 + codec_dai->driver->id, aifa);
+ snd_soc_write(codec, WM8580_PAIF3 + codec_dai->driver->id, aifb);
+
+ return 0;
+}
+
+static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int reg;
+
+ switch (div_id) {
+ case WM8580_MCLK:
+ reg = snd_soc_read(codec, WM8580_PLLB4);
+ reg &= ~WM8580_PLLB4_MCLKOUTSRC_MASK;
+
+ switch (div) {
+ case WM8580_CLKSRC_MCLK:
+ /* Input */
+ break;
+
+ case WM8580_CLKSRC_PLLA:
+ reg |= WM8580_PLLB4_MCLKOUTSRC_PLLA;
+ break;
+ case WM8580_CLKSRC_PLLB:
+ reg |= WM8580_PLLB4_MCLKOUTSRC_PLLB;
+ break;
+
+ case WM8580_CLKSRC_OSC:
+ reg |= WM8580_PLLB4_MCLKOUTSRC_OSC;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ snd_soc_write(codec, WM8580_PLLB4, reg);
+ break;
+
+ case WM8580_CLKOUTSRC:
+ reg = snd_soc_read(codec, WM8580_PLLB4);
+ reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK;
+
+ switch (div) {
+ case WM8580_CLKSRC_NONE:
+ break;
+
+ case WM8580_CLKSRC_PLLA:
+ reg |= WM8580_PLLB4_CLKOUTSRC_PLLACLK;
+ break;
+
+ case WM8580_CLKSRC_PLLB:
+ reg |= WM8580_PLLB4_CLKOUTSRC_PLLBCLK;
+ break;
+
+ case WM8580_CLKSRC_OSC:
+ reg |= WM8580_PLLB4_CLKOUTSRC_OSCCLK;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ snd_soc_write(codec, WM8580_PLLB4, reg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
+ int ret, sel, sel_mask, sel_shift;
+
+ switch (dai->driver->id) {
+ case WM8580_DAI_PAIFRX:
+ sel_mask = 0x3;
+ sel_shift = 0;
+ break;
+
+ case WM8580_DAI_PAIFTX:
+ sel_mask = 0xc;
+ sel_shift = 2;
+ break;
+
+ default:
+ WARN(1, "Unknown DAI driver ID\n");
+ return -EINVAL;
+ }
+
+ switch (clk_id) {
+ case WM8580_CLKSRC_ADCMCLK:
+ if (dai->driver->id != WM8580_DAI_PAIFTX)
+ return -EINVAL;
+ sel = 0 << sel_shift;
+ break;
+ case WM8580_CLKSRC_PLLA:
+ sel = 1 << sel_shift;
+ break;
+ case WM8580_CLKSRC_PLLB:
+ sel = 2 << sel_shift;
+ break;
+ case WM8580_CLKSRC_MCLK:
+ sel = 3 << sel_shift;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown clock %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ /* We really should validate PLL settings but not yet */
+ wm8580->sysclk[dai->driver->id] = freq;
+
+ ret = snd_soc_update_bits(codec, WM8580_CLKSEL, sel_mask, sel);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, WM8580_DAC_CONTROL5);
+
+ if (mute)
+ reg |= WM8580_DAC_CONTROL5_MUTEALL;
+ else
+ reg &= ~WM8580_DAC_CONTROL5_MUTEALL;
+
+ snd_soc_write(codec, WM8580_DAC_CONTROL5, reg);
+
+ return 0;
+}
+
+static int wm8580_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ /* Power up and get individual control of the DACs */
+ snd_soc_update_bits(codec, WM8580_PWRDN1,
+ WM8580_PWRDN1_PWDN |
+ WM8580_PWRDN1_ALLDACPD, 0);
+
+ /* Make VMID high impedance */
+ snd_soc_update_bits(codec, WM8580_ADC_CONTROL1,
+ 0x100, 0);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, WM8580_PWRDN1,
+ WM8580_PWRDN1_PWDN, WM8580_PWRDN1_PWDN);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8580_dai_ops_playback = {
+ .set_sysclk = wm8580_set_sysclk,
+ .hw_params = wm8580_paif_hw_params,
+ .set_fmt = wm8580_set_paif_dai_fmt,
+ .set_clkdiv = wm8580_set_dai_clkdiv,
+ .set_pll = wm8580_set_dai_pll,
+ .digital_mute = wm8580_digital_mute,
+};
+
+static const struct snd_soc_dai_ops wm8580_dai_ops_capture = {
+ .set_sysclk = wm8580_set_sysclk,
+ .hw_params = wm8580_paif_hw_params,
+ .set_fmt = wm8580_set_paif_dai_fmt,
+ .set_clkdiv = wm8580_set_dai_clkdiv,
+ .set_pll = wm8580_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver wm8580_dai[] = {
+ {
+ .name = "wm8580-hifi-playback",
+ .id = WM8580_DAI_PAIFRX,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = WM8580_FORMATS,
+ },
+ .ops = &wm8580_dai_ops_playback,
+ },
+ {
+ .name = "wm8580-hifi-capture",
+ .id = WM8580_DAI_PAIFTX,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = WM8580_FORMATS,
+ },
+ .ops = &wm8580_dai_ops_capture,
+ },
+};
+
+static int wm8580_probe(struct snd_soc_codec *codec)
+{
+ struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies),
+ wm8580->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_regulator_get;
+ }
+
+ /* Get the codec into a known state */
+ ret = snd_soc_write(codec, WM8580_RESET, 0);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
+ goto err_regulator_enable;
+ }
+
+ return 0;
+
+err_regulator_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
+err_regulator_get:
+ return ret;
+}
+
+/* power down chip */
+static int wm8580_remove(struct snd_soc_codec *codec)
+{
+ struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8580 = {
+ .probe = wm8580_probe,
+ .remove = wm8580_remove,
+ .set_bias_level = wm8580_set_bias_level,
+
+ .controls = wm8580_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8580_snd_controls),
+ .dapm_widgets = wm8580_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8580_dapm_widgets),
+ .dapm_routes = wm8580_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8580_dapm_routes),
+};
+
+static const struct of_device_id wm8580_of_match[] = {
+ { .compatible = "wlf,wm8580" },
+ { },
+};
+
+static const struct regmap_config wm8580_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8580_MAX_REGISTER,
+
+ .reg_defaults = wm8580_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8580_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8580_volatile,
+};
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8580_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8580_priv *wm8580;
+ int ret, i;
+
+ wm8580 = devm_kzalloc(&i2c->dev, sizeof(struct wm8580_priv),
+ GFP_KERNEL);
+ if (wm8580 == NULL)
+ return -ENOMEM;
+
+ wm8580->regmap = devm_regmap_init_i2c(i2c, &wm8580_regmap);
+ if (IS_ERR(wm8580->regmap))
+ return PTR_ERR(wm8580->regmap);
+
+ for (i = 0; i < ARRAY_SIZE(wm8580->supplies); i++)
+ wm8580->supplies[i].supply = wm8580_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8580->supplies),
+ wm8580->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, wm8580);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8580, wm8580_dai, ARRAY_SIZE(wm8580_dai));
+
+ return ret;
+}
+
+static int wm8580_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8580_i2c_id[] = {
+ { "wm8580", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
+
+static struct i2c_driver wm8580_i2c_driver = {
+ .driver = {
+ .name = "wm8580",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8580_of_match,
+ },
+ .probe = wm8580_i2c_probe,
+ .remove = wm8580_i2c_remove,
+ .id_table = wm8580_i2c_id,
+};
+#endif
+
+static int __init wm8580_modinit(void)
+{
+ int ret = 0;
+
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8580_i2c_driver);
+ if (ret != 0) {
+ pr_err("Failed to register WM8580 I2C driver: %d\n", ret);
+ }
+#endif
+
+ return ret;
+}
+module_init(wm8580_modinit);
+
+static void __exit wm8580_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8580_i2c_driver);
+#endif
+}
+module_exit(wm8580_exit);
+
+MODULE_DESCRIPTION("ASoC WM8580 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8580.h b/kernel/sound/soc/codecs/wm8580.h
new file mode 100644
index 000000000..1d34656d0
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8580.h
@@ -0,0 +1,35 @@
+/*
+ * wm8580.h -- audio driver for WM8580
+ *
+ * Copyright 2008 Samsung Electronics.
+ * Author: Ryu Euiyoul
+ * ryu.real@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.
+ *
+ */
+
+#ifndef _WM8580_H
+#define _WM8580_H
+
+#define WM8580_PLLA 1
+#define WM8580_PLLB 2
+
+#define WM8580_MCLK 1
+#define WM8580_CLKOUTSRC 2
+
+#define WM8580_CLKSRC_MCLK 1
+#define WM8580_CLKSRC_PLLA 2
+#define WM8580_CLKSRC_PLLB 3
+#define WM8580_CLKSRC_OSC 4
+#define WM8580_CLKSRC_NONE 5
+#define WM8580_CLKSRC_ADCMCLK 6
+
+#define WM8580_DAI_PAIFRX 0
+#define WM8580_DAI_PAIFTX 1
+
+#endif
+
diff --git a/kernel/sound/soc/codecs/wm8711.c b/kernel/sound/soc/codecs/wm8711.c
new file mode 100644
index 000000000..121e46d53
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8711.c
@@ -0,0 +1,525 @@
+/*
+ * wm8711.c -- WM8711 ALSA SoC Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics
+ *
+ * Author: Mike Arthur <Mike.Arthur@wolfsonmicro.com>
+ *
+ * Based on wm8731.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>
+#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/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+
+#include "wm8711.h"
+
+/* codec private data */
+struct wm8711_priv {
+ struct regmap *regmap;
+ unsigned int sysclk;
+};
+
+/*
+ * wm8711 register cache
+ * We can't read the WM8711 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ * There is no point in caching the reset register
+ */
+static const struct reg_default wm8711_reg_defaults[] = {
+ { 0, 0x0079 }, { 1, 0x0079 }, { 2, 0x000a }, { 3, 0x0008 },
+ { 4, 0x009f }, { 5, 0x000a }, { 6, 0x0000 }, { 7, 0x0000 },
+};
+
+static bool wm8711_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8711_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#define wm8711_reset(c) snd_soc_write(c, WM8711_RESET, 0)
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+
+static const struct snd_kcontrol_new wm8711_snd_controls[] = {
+
+SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V,
+ 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V,
+ 7, 1, 0),
+
+};
+
+/* Output Mixer */
+static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0),
+SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1,
+ &wm8711_output_mixer_controls[0],
+ ARRAY_SIZE(wm8711_output_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
+};
+
+static const struct snd_soc_dapm_route wm8711_intercon[] = {
+ /* output mixer */
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
+
+ /* outputs */
+ {"RHPOUT", NULL, "Output Mixer"},
+ {"ROUT", NULL, "Output Mixer"},
+ {"LHPOUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+};
+
+struct _coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr:4;
+ u8 bosr:1;
+ u8 usb:1;
+};
+
+/* codec mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ /* 48k */
+ {12288000, 48000, 256, 0x0, 0x0, 0x0},
+ {18432000, 48000, 384, 0x0, 0x1, 0x0},
+ {12000000, 48000, 250, 0x0, 0x0, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 384, 0x6, 0x0, 0x0},
+ {18432000, 32000, 576, 0x6, 0x1, 0x0},
+ {12000000, 32000, 375, 0x6, 0x0, 0x1},
+
+ /* 8k */
+ {12288000, 8000, 1536, 0x3, 0x0, 0x0},
+ {18432000, 8000, 2304, 0x3, 0x1, 0x0},
+ {11289600, 8000, 1408, 0xb, 0x0, 0x0},
+ {16934400, 8000, 2112, 0xb, 0x1, 0x0},
+ {12000000, 8000, 1500, 0x3, 0x0, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 128, 0x7, 0x0, 0x0},
+ {18432000, 96000, 192, 0x7, 0x1, 0x0},
+ {12000000, 96000, 125, 0x7, 0x0, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x8, 0x0, 0x0},
+ {16934400, 44100, 384, 0x8, 0x1, 0x0},
+ {12000000, 44100, 272, 0x8, 0x1, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0xf, 0x0, 0x0},
+ {16934400, 88200, 192, 0xf, 0x1, 0x0},
+ {12000000, 88200, 136, 0xf, 0x1, 0x1},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+ return 0;
+}
+
+static int wm8711_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8711_priv *wm8711 = snd_soc_codec_get_drvdata(codec);
+ u16 iface = snd_soc_read(codec, WM8711_IFACE) & 0xfff3;
+ int i = get_coeff(wm8711->sysclk, params_rate(params));
+ u16 srate = (coeff_div[i].sr << 2) |
+ (coeff_div[i].bosr << 1) | coeff_div[i].usb;
+
+ snd_soc_write(codec, WM8711_SRATE, srate);
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface |= 0x0004;
+ break;
+ case 24:
+ iface |= 0x0008;
+ break;
+ }
+
+ snd_soc_write(codec, WM8711_IFACE, iface);
+ return 0;
+}
+
+static int wm8711_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ /* set active */
+ snd_soc_write(codec, WM8711_ACTIVE, 0x0001);
+
+ return 0;
+}
+
+static void wm8711_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ /* deactivate */
+ if (!snd_soc_codec_is_active(codec)) {
+ udelay(50);
+ snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+ }
+}
+
+static int wm8711_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8711_APDIGI) & 0xfff7;
+
+ if (mute)
+ snd_soc_write(codec, WM8711_APDIGI, mute_reg | 0x8);
+ else
+ snd_soc_write(codec, WM8711_APDIGI, mute_reg);
+
+ return 0;
+}
+
+static int wm8711_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8711_priv *wm8711 = snd_soc_codec_get_drvdata(codec);
+
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ wm8711->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = snd_soc_read(codec, WM8711_IFACE) & 0x000c;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface |= 0x0040;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface */
+ snd_soc_write(codec, WM8711_IFACE, iface);
+ return 0;
+}
+
+static int wm8711_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8711_priv *wm8711 = snd_soc_codec_get_drvdata(codec);
+ u16 reg = snd_soc_read(codec, WM8711_PWR) & 0xff7f;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_write(codec, WM8711_PWR, reg);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ regcache_sync(wm8711->regmap);
+
+ snd_soc_write(codec, WM8711_PWR, reg | 0x0040);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+ snd_soc_write(codec, WM8711_PWR, 0xffff);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8711_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8711_ops = {
+ .prepare = wm8711_pcm_prepare,
+ .hw_params = wm8711_hw_params,
+ .shutdown = wm8711_shutdown,
+ .digital_mute = wm8711_mute,
+ .set_sysclk = wm8711_set_dai_sysclk,
+ .set_fmt = wm8711_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver wm8711_dai = {
+ .name = "wm8711-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8711_RATES,
+ .formats = WM8711_FORMATS,
+ },
+ .ops = &wm8711_ops,
+};
+
+static int wm8711_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ ret = wm8711_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ return ret;
+ }
+
+ /* Latch the update bits */
+ snd_soc_update_bits(codec, WM8711_LOUT1V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8711_ROUT1V, 0x0100, 0x0100);
+
+ return ret;
+
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8711 = {
+ .probe = wm8711_probe,
+ .set_bias_level = wm8711_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8711_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8711_snd_controls),
+ .dapm_widgets = wm8711_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets),
+ .dapm_routes = wm8711_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8711_intercon),
+};
+
+static const struct of_device_id wm8711_of_match[] = {
+ { .compatible = "wlf,wm8711", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8711_of_match);
+
+static const struct regmap_config wm8711_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8711_RESET,
+
+ .reg_defaults = wm8711_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8711_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8711_volatile,
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8711_spi_probe(struct spi_device *spi)
+{
+ struct wm8711_priv *wm8711;
+ int ret;
+
+ wm8711 = devm_kzalloc(&spi->dev, sizeof(struct wm8711_priv),
+ GFP_KERNEL);
+ if (wm8711 == NULL)
+ return -ENOMEM;
+
+ wm8711->regmap = devm_regmap_init_spi(spi, &wm8711_regmap);
+ if (IS_ERR(wm8711->regmap))
+ return PTR_ERR(wm8711->regmap);
+
+ spi_set_drvdata(spi, wm8711);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8711, &wm8711_dai, 1);
+
+ return ret;
+}
+
+static int wm8711_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+
+ return 0;
+}
+
+static struct spi_driver wm8711_spi_driver = {
+ .driver = {
+ .name = "wm8711",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8711_of_match,
+ },
+ .probe = wm8711_spi_probe,
+ .remove = wm8711_spi_remove,
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8711_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct wm8711_priv *wm8711;
+ int ret;
+
+ wm8711 = devm_kzalloc(&client->dev, sizeof(struct wm8711_priv),
+ GFP_KERNEL);
+ if (wm8711 == NULL)
+ return -ENOMEM;
+
+ wm8711->regmap = devm_regmap_init_i2c(client, &wm8711_regmap);
+ if (IS_ERR(wm8711->regmap))
+ return PTR_ERR(wm8711->regmap);
+
+ i2c_set_clientdata(client, wm8711);
+
+ ret = snd_soc_register_codec(&client->dev,
+ &soc_codec_dev_wm8711, &wm8711_dai, 1);
+
+ return ret;
+}
+
+static int wm8711_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8711_i2c_id[] = {
+ { "wm8711", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
+
+static struct i2c_driver wm8711_i2c_driver = {
+ .driver = {
+ .name = "wm8711",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8711_of_match,
+ },
+ .probe = wm8711_i2c_probe,
+ .remove = wm8711_i2c_remove,
+ .id_table = wm8711_i2c_id,
+};
+#endif
+
+static int __init wm8711_modinit(void)
+{
+ int ret;
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8711_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8711_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return 0;
+}
+module_init(wm8711_modinit);
+
+static void __exit wm8711_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8711_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8711_spi_driver);
+#endif
+}
+module_exit(wm8711_exit);
+
+MODULE_DESCRIPTION("ASoC WM8711 driver");
+MODULE_AUTHOR("Mike Arthur");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8711.h b/kernel/sound/soc/codecs/wm8711.h
new file mode 100644
index 000000000..a61db9854
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8711.h
@@ -0,0 +1,39 @@
+/*
+ * wm8711.h -- WM8711 Soc Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics
+ *
+ * Author: Mike Arthur <linux@wolfsonmicro.com>
+ *
+ * Based on wm8731.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 _WM8711_H
+#define _WM8711_H
+
+/* WM8711 register space */
+
+#define WM8711_LOUT1V 0x02
+#define WM8711_ROUT1V 0x03
+#define WM8711_APANA 0x04
+#define WM8711_APDIGI 0x05
+#define WM8711_PWR 0x06
+#define WM8711_IFACE 0x07
+#define WM8711_SRATE 0x08
+#define WM8711_ACTIVE 0x09
+#define WM8711_RESET 0x0f
+
+#define WM8711_CACHEREGNUM 8
+
+#define WM8711_SYSCLK 0
+#define WM8711_DAI 0
+
+struct wm8711_setup_data {
+ unsigned short i2c_address;
+};
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8727.c b/kernel/sound/soc/codecs/wm8727.c
new file mode 100644
index 000000000..bb25a75f9
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8727.c
@@ -0,0 +1,88 @@
+/*
+ * wm8727.c
+ *
+ * Created on: 15-Oct-2009
+ * Author: neil.jones@imgtec.com
+ *
+ * Copyright (C) 2009 Imagination Technologies 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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget wm8727_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route wm8727_dapm_routes[] = {
+ { "VOUTL", NULL, "Playback" },
+ { "VOUTR", NULL, "Playback" },
+};
+
+/*
+ * Note this is a simple chip with no configuration interface, sample rate is
+ * determined automatically by examining the Master clock and Bit clock ratios
+ */
+#define WM8727_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+
+static struct snd_soc_dai_driver wm8727_dai = {
+ .name = "wm8727-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8727_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8727 = {
+ .dapm_widgets = wm8727_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8727_dapm_widgets),
+ .dapm_routes = wm8727_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8727_dapm_routes),
+};
+
+static int wm8727_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_wm8727, &wm8727_dai, 1);
+}
+
+static int wm8727_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver wm8727_codec_driver = {
+ .driver = {
+ .name = "wm8727",
+ },
+
+ .probe = wm8727_probe,
+ .remove = wm8727_remove,
+};
+
+module_platform_driver(wm8727_codec_driver);
+
+MODULE_DESCRIPTION("ASoC wm8727 driver");
+MODULE_AUTHOR("Neil Jones");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8728.c b/kernel/sound/soc/codecs/wm8728.c
new file mode 100644
index 000000000..55c7fb4fc
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8728.c
@@ -0,0 +1,366 @@
+/*
+ * wm8728.c -- WM8728 ALSA SoC Audio driver
+ *
+ * Copyright 2008 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/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/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/of_device.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 "wm8728.h"
+
+/*
+ * We can't read the WM8728 register space so we cache them instead.
+ * Note that the defaults here aren't the physical defaults, we latch
+ * the volume update bits, mute the output and enable infinite zero
+ * detect.
+ */
+static const struct reg_default wm8728_reg_defaults[] = {
+ { 0, 0x1ff },
+ { 1, 0x1ff },
+ { 2, 0x001 },
+ { 3, 0x100 },
+};
+
+/* codec private data */
+struct wm8728_priv {
+ struct regmap *regmap;
+};
+
+static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new wm8728_snd_controls[] = {
+
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8728_DACLVOL, WM8728_DACRVOL,
+ 0, 255, 0, wm8728_tlv),
+
+SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0),
+};
+
+/*
+ * DAPM controls.
+ */
+static const struct snd_soc_dapm_widget wm8728_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route wm8728_intercon[] = {
+ {"VOUTL", NULL, "DAC"},
+ {"VOUTR", NULL, "DAC"},
+};
+
+static int wm8728_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8728_DACCTL);
+
+ if (mute)
+ snd_soc_write(codec, WM8728_DACCTL, mute_reg | 1);
+ else
+ snd_soc_write(codec, WM8728_DACCTL, mute_reg & ~1);
+
+ return 0;
+}
+
+static int wm8728_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 dac = snd_soc_read(codec, WM8728_DACCTL);
+
+ dac &= ~0x18;
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ dac |= 0x10;
+ break;
+ case 24:
+ dac |= 0x08;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8728_DACCTL, dac);
+
+ return 0;
+}
+
+static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = snd_soc_read(codec, WM8728_IFCTL);
+
+ /* Currently only I2S is supported by the driver, though the
+ * hardware is more flexible.
+ */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* The hardware only support full slave mode */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ iface &= ~0x22;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x20;
+ iface &= ~0x02;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x02;
+ iface &= ~0x20;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x22;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8728_IFCTL, iface);
+ return 0;
+}
+
+static int wm8728_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8728_priv *wm8728 = snd_soc_codec_get_drvdata(codec);
+ u16 reg;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ /* Power everything up... */
+ reg = snd_soc_read(codec, WM8728_DACCTL);
+ snd_soc_write(codec, WM8728_DACCTL, reg & ~0x4);
+
+ /* ..then sync in the register cache. */
+ regcache_sync(wm8728->regmap);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ reg = snd_soc_read(codec, WM8728_DACCTL);
+ snd_soc_write(codec, WM8728_DACCTL, reg | 0x4);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8728_RATES (SNDRV_PCM_RATE_8000_192000)
+
+#define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8728_dai_ops = {
+ .hw_params = wm8728_hw_params,
+ .digital_mute = wm8728_mute,
+ .set_fmt = wm8728_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver wm8728_dai = {
+ .name = "wm8728-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8728_RATES,
+ .formats = WM8728_FORMATS,
+ },
+ .ops = &wm8728_dai_ops,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8728 = {
+ .set_bias_level = wm8728_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8728_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8728_snd_controls),
+ .dapm_widgets = wm8728_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8728_dapm_widgets),
+ .dapm_routes = wm8728_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8728_intercon),
+};
+
+static const struct of_device_id wm8728_of_match[] = {
+ { .compatible = "wlf,wm8728", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8728_of_match);
+
+static const struct regmap_config wm8728_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8728_IFCTL,
+
+ .reg_defaults = wm8728_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8728_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8728_spi_probe(struct spi_device *spi)
+{
+ struct wm8728_priv *wm8728;
+ int ret;
+
+ wm8728 = devm_kzalloc(&spi->dev, sizeof(struct wm8728_priv),
+ GFP_KERNEL);
+ if (wm8728 == NULL)
+ return -ENOMEM;
+
+ wm8728->regmap = devm_regmap_init_spi(spi, &wm8728_regmap);
+ if (IS_ERR(wm8728->regmap))
+ return PTR_ERR(wm8728->regmap);
+
+ spi_set_drvdata(spi, wm8728);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8728, &wm8728_dai, 1);
+
+ return ret;
+}
+
+static int wm8728_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+
+ return 0;
+}
+
+static struct spi_driver wm8728_spi_driver = {
+ .driver = {
+ .name = "wm8728",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8728_of_match,
+ },
+ .probe = wm8728_spi_probe,
+ .remove = wm8728_spi_remove,
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8728_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8728_priv *wm8728;
+ int ret;
+
+ wm8728 = devm_kzalloc(&i2c->dev, sizeof(struct wm8728_priv),
+ GFP_KERNEL);
+ if (wm8728 == NULL)
+ return -ENOMEM;
+
+ wm8728->regmap = devm_regmap_init_i2c(i2c, &wm8728_regmap);
+ if (IS_ERR(wm8728->regmap))
+ return PTR_ERR(wm8728->regmap);
+
+ i2c_set_clientdata(i2c, wm8728);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8728, &wm8728_dai, 1);
+
+ return ret;
+}
+
+static int wm8728_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8728_i2c_id[] = {
+ { "wm8728", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
+
+static struct i2c_driver wm8728_i2c_driver = {
+ .driver = {
+ .name = "wm8728",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8728_of_match,
+ },
+ .probe = wm8728_i2c_probe,
+ .remove = wm8728_i2c_remove,
+ .id_table = wm8728_i2c_id,
+};
+#endif
+
+static int __init wm8728_modinit(void)
+{
+ int ret = 0;
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8728_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8728 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8728_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8728 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(wm8728_modinit);
+
+static void __exit wm8728_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8728_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8728_spi_driver);
+#endif
+}
+module_exit(wm8728_exit);
+
+MODULE_DESCRIPTION("ASoC WM8728 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8728.h b/kernel/sound/soc/codecs/wm8728.h
new file mode 100644
index 000000000..8aea362ff
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8728.h
@@ -0,0 +1,21 @@
+/*
+ * wm8728.h -- WM8728 ASoC codec driver
+ *
+ * Copyright 2008 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 _WM8728_H
+#define _WM8728_H
+
+#define WM8728_DACLVOL 0x00
+#define WM8728_DACRVOL 0x01
+#define WM8728_DACCTL 0x02
+#define WM8728_IFCTL 0x03
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8731.c b/kernel/sound/soc/codecs/wm8731.c
new file mode 100644
index 000000000..2245b6a32
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8731.c
@@ -0,0 +1,835 @@
+/*
+ * wm8731.c -- WM8731 ALSA SoC Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright 2006-12 Wolfson Microelectronics, plc
+ *
+ * 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>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+#include <linux/mutex.h>
+#include <linux/clk.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 "wm8731.h"
+
+#define WM8731_NUM_SUPPLIES 4
+static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
+ "AVDD",
+ "HPVDD",
+ "DCVDD",
+ "DBVDD",
+};
+
+/* codec private data */
+struct wm8731_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
+ const struct snd_pcm_hw_constraint_list *constraints;
+ unsigned int sysclk;
+ int sysclk_type;
+ int playback_fs;
+ bool deemph;
+
+ struct mutex lock;
+};
+
+
+/*
+ * wm8731 register cache
+ */
+static const struct reg_default wm8731_reg_defaults[] = {
+ { 0, 0x0097 },
+ { 1, 0x0097 },
+ { 2, 0x0079 },
+ { 3, 0x0079 },
+ { 4, 0x000a },
+ { 5, 0x0008 },
+ { 6, 0x009f },
+ { 7, 0x000a },
+ { 8, 0x0000 },
+ { 9, 0x0000 },
+};
+
+static bool wm8731_volatile(struct device *dev, unsigned int reg)
+{
+ return reg == WM8731_RESET;
+}
+
+static bool wm8731_writeable(struct device *dev, unsigned int reg)
+{
+ return reg <= WM8731_RESET;
+}
+
+#define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0)
+
+static const char *wm8731_input_select[] = {"Line In", "Mic"};
+
+static SOC_ENUM_SINGLE_DECL(wm8731_insel_enum,
+ WM8731_APANA, 2, wm8731_input_select);
+
+static int wm8731_deemph[] = { 0, 32000, 44100, 48000 };
+
+static int wm8731_set_deemph(struct snd_soc_codec *codec)
+{
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+ int val, i, best;
+
+ /* If we're using deemphasis select the nearest available sample
+ * rate.
+ */
+ if (wm8731->deemph) {
+ best = 1;
+ for (i = 2; i < ARRAY_SIZE(wm8731_deemph); i++) {
+ if (abs(wm8731_deemph[i] - wm8731->playback_fs) <
+ abs(wm8731_deemph[best] - wm8731->playback_fs))
+ best = i;
+ }
+
+ val = best << 1;
+ } else {
+ best = 0;
+ val = 0;
+ }
+
+ dev_dbg(codec->dev, "Set deemphasis %d (%dHz)\n",
+ best, wm8731_deemph[best]);
+
+ return snd_soc_update_bits(codec, WM8731_APDIGI, 0x6, val);
+}
+
+static int wm8731_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8731->deemph;
+
+ return 0;
+}
+
+static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+ int deemph = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ mutex_lock(&wm8731->lock);
+ if (wm8731->deemph != deemph) {
+ wm8731->deemph = deemph;
+
+ wm8731_set_deemph(codec);
+
+ ret = 1;
+ }
+ mutex_unlock(&wm8731->lock);
+
+ return ret;
+}
+
+static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 2000, 0);
+
+static const struct snd_kcontrol_new wm8731_snd_controls[] = {
+
+SOC_DOUBLE_R_TLV("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
+ 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V,
+ 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0,
+ in_tlv),
+SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),
+
+SOC_SINGLE_TLV("Mic Boost Volume", WM8731_APANA, 0, 1, 0, mic_tlv),
+SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1),
+
+SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1,
+ sidetone_tlv),
+
+SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1),
+SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
+
+SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
+ wm8731_get_deemph, wm8731_put_deemph),
+};
+
+/* Output Mixer */
+static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
+SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
+SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
+};
+
+/* Input mux */
+static const struct snd_kcontrol_new wm8731_input_mux_controls =
+SOC_DAPM_ENUM("Input Select", wm8731_insel_enum);
+
+static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("ACTIVE",WM8731_ACTIVE, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("OSC", WM8731_PWR, 5, 1, NULL, 0),
+SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1,
+ &wm8731_output_mixer_controls[0],
+ ARRAY_SIZE(wm8731_output_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1),
+SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls),
+SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0),
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1),
+SND_SOC_DAPM_INPUT("MICIN"),
+SND_SOC_DAPM_INPUT("RLINEIN"),
+SND_SOC_DAPM_INPUT("LLINEIN"),
+};
+
+static int wm8731_check_osc(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+
+ return wm8731->sysclk_type == WM8731_SYSCLK_XTAL;
+}
+
+static const struct snd_soc_dapm_route wm8731_intercon[] = {
+ {"DAC", NULL, "OSC", wm8731_check_osc},
+ {"ADC", NULL, "OSC", wm8731_check_osc},
+ {"DAC", NULL, "ACTIVE"},
+ {"ADC", NULL, "ACTIVE"},
+
+ /* output mixer */
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
+ {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
+
+ /* outputs */
+ {"RHPOUT", NULL, "Output Mixer"},
+ {"ROUT", NULL, "Output Mixer"},
+ {"LHPOUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+
+ /* input mux */
+ {"Input Mux", "Line In", "Line Input"},
+ {"Input Mux", "Mic", "Mic Bias"},
+ {"ADC", NULL, "Input Mux"},
+
+ /* inputs */
+ {"Line Input", NULL, "LLINEIN"},
+ {"Line Input", NULL, "RLINEIN"},
+ {"Mic Bias", NULL, "MICIN"},
+};
+
+struct _coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr:4;
+ u8 bosr:1;
+ u8 usb:1;
+};
+
+/* codec mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ /* 48k */
+ {12288000, 48000, 256, 0x0, 0x0, 0x0},
+ {18432000, 48000, 384, 0x0, 0x1, 0x0},
+ {12000000, 48000, 250, 0x0, 0x0, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 384, 0x6, 0x0, 0x0},
+ {18432000, 32000, 576, 0x6, 0x1, 0x0},
+ {12000000, 32000, 375, 0x6, 0x0, 0x1},
+
+ /* 8k */
+ {12288000, 8000, 1536, 0x3, 0x0, 0x0},
+ {18432000, 8000, 2304, 0x3, 0x1, 0x0},
+ {11289600, 8000, 1408, 0xb, 0x0, 0x0},
+ {16934400, 8000, 2112, 0xb, 0x1, 0x0},
+ {12000000, 8000, 1500, 0x3, 0x0, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 128, 0x7, 0x0, 0x0},
+ {18432000, 96000, 192, 0x7, 0x1, 0x0},
+ {12000000, 96000, 125, 0x7, 0x0, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x8, 0x0, 0x0},
+ {16934400, 44100, 384, 0x8, 0x1, 0x0},
+ {12000000, 44100, 272, 0x8, 0x1, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0xf, 0x0, 0x0},
+ {16934400, 88200, 192, 0xf, 0x1, 0x0},
+ {12000000, 88200, 136, 0xf, 0x1, 0x1},
+};
+
+/* rates constraints */
+static const unsigned int wm8731_rates_12000000[] = {
+ 8000, 32000, 44100, 48000, 96000, 88200,
+};
+
+static const unsigned int wm8731_rates_12288000_18432000[] = {
+ 8000, 32000, 48000, 96000,
+};
+
+static const unsigned int wm8731_rates_11289600_16934400[] = {
+ 8000, 44100, 88200,
+};
+
+static const struct snd_pcm_hw_constraint_list wm8731_constraints_12000000 = {
+ .list = wm8731_rates_12000000,
+ .count = ARRAY_SIZE(wm8731_rates_12000000),
+};
+
+static const
+struct snd_pcm_hw_constraint_list wm8731_constraints_12288000_18432000 = {
+ .list = wm8731_rates_12288000_18432000,
+ .count = ARRAY_SIZE(wm8731_rates_12288000_18432000),
+};
+
+static const
+struct snd_pcm_hw_constraint_list wm8731_constraints_11289600_16934400 = {
+ .list = wm8731_rates_11289600_16934400,
+ .count = ARRAY_SIZE(wm8731_rates_11289600_16934400),
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+ return 0;
+}
+
+static int wm8731_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+ u16 iface = snd_soc_read(codec, WM8731_IFACE) & 0xfff3;
+ int i = get_coeff(wm8731->sysclk, params_rate(params));
+ u16 srate = (coeff_div[i].sr << 2) |
+ (coeff_div[i].bosr << 1) | coeff_div[i].usb;
+
+ wm8731->playback_fs = params_rate(params);
+
+ snd_soc_write(codec, WM8731_SRATE, srate);
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface |= 0x0004;
+ break;
+ case 24:
+ iface |= 0x0008;
+ break;
+ }
+
+ wm8731_set_deemph(codec);
+
+ snd_soc_write(codec, WM8731_IFACE, iface);
+ return 0;
+}
+
+static int wm8731_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8731_APDIGI) & 0xfff7;
+
+ if (mute)
+ snd_soc_write(codec, WM8731_APDIGI, mute_reg | 0x8);
+ else
+ snd_soc_write(codec, WM8731_APDIGI, mute_reg);
+ return 0;
+}
+
+static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case WM8731_SYSCLK_XTAL:
+ case WM8731_SYSCLK_MCLK:
+ if (wm8731->mclk && clk_set_rate(wm8731->mclk, freq))
+ return -EINVAL;
+ wm8731->sysclk_type = clk_id;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (freq) {
+ case 0:
+ wm8731->constraints = NULL;
+ break;
+ case 12000000:
+ wm8731->constraints = &wm8731_constraints_12000000;
+ break;
+ case 12288000:
+ case 18432000:
+ wm8731->constraints = &wm8731_constraints_12288000_18432000;
+ break;
+ case 16934400:
+ case 11289600:
+ wm8731->constraints = &wm8731_constraints_11289600_16934400;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8731->sysclk = freq;
+
+ snd_soc_dapm_sync(&codec->dapm);
+
+ return 0;
+}
+
+
+static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface |= 0x0040;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0013;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0003;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface */
+ snd_soc_write(codec, WM8731_IFACE, iface);
+ return 0;
+}
+
+static int wm8731_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+ u16 reg;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ if (wm8731->mclk)
+ clk_prepare_enable(wm8731->mclk);
+ break;
+ 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(wm8731->supplies),
+ wm8731->supplies);
+ if (ret != 0)
+ return ret;
+
+ regcache_sync(wm8731->regmap);
+ }
+
+ /* Clear PWROFF, gate CLKOUT, everything else as-is */
+ reg = snd_soc_read(codec, WM8731_PWR) & 0xff7f;
+ snd_soc_write(codec, WM8731_PWR, reg | 0x0040);
+ break;
+ case SND_SOC_BIAS_OFF:
+ if (wm8731->mclk)
+ clk_disable_unprepare(wm8731->mclk);
+ snd_soc_write(codec, WM8731_PWR, 0xffff);
+ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
+ regcache_mark_dirty(wm8731->regmap);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int wm8731_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(dai->codec);
+
+ if (wm8731->constraints)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ wm8731->constraints);
+
+ return 0;
+}
+
+#define WM8731_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8731_dai_ops = {
+ .startup = wm8731_startup,
+ .hw_params = wm8731_hw_params,
+ .digital_mute = wm8731_mute,
+ .set_sysclk = wm8731_set_dai_sysclk,
+ .set_fmt = wm8731_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver wm8731_dai = {
+ .name = "wm8731-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8731_RATES,
+ .formats = WM8731_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8731_RATES,
+ .formats = WM8731_FORMATS,},
+ .ops = &wm8731_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int wm8731_probe(struct snd_soc_codec *codec)
+{
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
+ wm8731->supplies[i].supply = wm8731_supply_names[i];
+
+ ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm8731_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ goto err_regulator_enable;
+ }
+
+ wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Latch the update bits */
+ snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0);
+ snd_soc_update_bits(codec, WM8731_ROUT1V, 0x100, 0);
+ snd_soc_update_bits(codec, WM8731_LINVOL, 0x100, 0);
+ snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0);
+
+ /* Disable bypass path by default */
+ snd_soc_update_bits(codec, WM8731_APANA, 0x8, 0);
+
+ /* Regulators will have been enabled by bias management */
+ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
+
+ return 0;
+
+err_regulator_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
+
+ return ret;
+}
+
+/* power down chip */
+static int wm8731_remove(struct snd_soc_codec *codec)
+{
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8731 = {
+ .probe = wm8731_probe,
+ .remove = wm8731_remove,
+ .set_bias_level = wm8731_set_bias_level,
+ .suspend_bias_off = true,
+
+ .dapm_widgets = wm8731_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
+ .dapm_routes = wm8731_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8731_intercon),
+ .controls = wm8731_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8731_snd_controls),
+};
+
+static const struct of_device_id wm8731_of_match[] = {
+ { .compatible = "wlf,wm8731", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, wm8731_of_match);
+
+static const struct regmap_config wm8731_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = WM8731_RESET,
+ .volatile_reg = wm8731_volatile,
+ .writeable_reg = wm8731_writeable,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm8731_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults),
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8731_spi_probe(struct spi_device *spi)
+{
+ struct wm8731_priv *wm8731;
+ int ret;
+
+ wm8731 = devm_kzalloc(&spi->dev, sizeof(*wm8731), GFP_KERNEL);
+ if (wm8731 == NULL)
+ return -ENOMEM;
+
+ wm8731->mclk = devm_clk_get(&spi->dev, "mclk");
+ if (IS_ERR(wm8731->mclk)) {
+ ret = PTR_ERR(wm8731->mclk);
+ if (ret == -ENOENT) {
+ wm8731->mclk = NULL;
+ dev_warn(&spi->dev, "Assuming static MCLK\n");
+ } else {
+ dev_err(&spi->dev, "Failed to get MCLK: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ mutex_init(&wm8731->lock);
+
+ wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
+ if (IS_ERR(wm8731->regmap)) {
+ ret = PTR_ERR(wm8731->regmap);
+ dev_err(&spi->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ spi_set_drvdata(spi, wm8731);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8731, &wm8731_dai, 1);
+ if (ret != 0) {
+ dev_err(&spi->dev, "Failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wm8731_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver wm8731_spi_driver = {
+ .driver = {
+ .name = "wm8731",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8731_of_match,
+ },
+ .probe = wm8731_spi_probe,
+ .remove = wm8731_spi_remove,
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8731_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8731_priv *wm8731;
+ int ret;
+
+ wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv),
+ GFP_KERNEL);
+ if (wm8731 == NULL)
+ return -ENOMEM;
+
+ wm8731->mclk = devm_clk_get(&i2c->dev, "mclk");
+ if (IS_ERR(wm8731->mclk)) {
+ ret = PTR_ERR(wm8731->mclk);
+ if (ret == -ENOENT) {
+ wm8731->mclk = NULL;
+ dev_warn(&i2c->dev, "Assuming static MCLK\n");
+ } else {
+ dev_err(&i2c->dev, "Failed to get MCLK: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ mutex_init(&wm8731->lock);
+
+ wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap);
+ if (IS_ERR(wm8731->regmap)) {
+ ret = PTR_ERR(wm8731->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, wm8731);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8731, &wm8731_dai, 1);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wm8731_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8731_i2c_id[] = {
+ { "wm8731", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
+
+static struct i2c_driver wm8731_i2c_driver = {
+ .driver = {
+ .name = "wm8731",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8731_of_match,
+ },
+ .probe = wm8731_i2c_probe,
+ .remove = wm8731_i2c_remove,
+ .id_table = wm8731_i2c_id,
+};
+#endif
+
+static int __init wm8731_modinit(void)
+{
+ int ret = 0;
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8731_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8731_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(wm8731_modinit);
+
+static void __exit wm8731_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8731_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8731_spi_driver);
+#endif
+}
+module_exit(wm8731_exit);
+
+MODULE_DESCRIPTION("ASoC WM8731 driver");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8731.h b/kernel/sound/soc/codecs/wm8731.h
new file mode 100644
index 000000000..e9c0c76ab
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8731.h
@@ -0,0 +1,39 @@
+/*
+ * wm8731.h -- WM8731 Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * 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 _WM8731_H
+#define _WM8731_H
+
+/* WM8731 register space */
+
+#define WM8731_LINVOL 0x00
+#define WM8731_RINVOL 0x01
+#define WM8731_LOUT1V 0x02
+#define WM8731_ROUT1V 0x03
+#define WM8731_APANA 0x04
+#define WM8731_APDIGI 0x05
+#define WM8731_PWR 0x06
+#define WM8731_IFACE 0x07
+#define WM8731_SRATE 0x08
+#define WM8731_ACTIVE 0x09
+#define WM8731_RESET 0x0f
+
+#define WM8731_CACHEREGNUM 10
+
+#define WM8731_SYSCLK_XTAL 1
+#define WM8731_SYSCLK_MCLK 2
+
+#define WM8731_DAI 0
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8737.c b/kernel/sound/soc/codecs/wm8737.c
new file mode 100644
index 000000000..ada9ac1ba
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8737.c
@@ -0,0 +1,753 @@
+/*
+ * wm8737.c -- WM8737 ALSA SoC Audio driver
+ *
+ * Copyright 2010 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/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_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 "wm8737.h"
+
+#define WM8737_NUM_SUPPLIES 4
+static const char *wm8737_supply_names[WM8737_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD",
+ "AVDD",
+ "MVDD",
+};
+
+/* codec private data */
+struct wm8737_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[WM8737_NUM_SUPPLIES];
+ unsigned int mclk;
+};
+
+static const struct reg_default wm8737_reg_defaults[] = {
+ { 0, 0x00C3 }, /* R0 - Left PGA volume */
+ { 1, 0x00C3 }, /* R1 - Right PGA volume */
+ { 2, 0x0007 }, /* R2 - AUDIO path L */
+ { 3, 0x0007 }, /* R3 - AUDIO path R */
+ { 4, 0x0000 }, /* R4 - 3D Enhance */
+ { 5, 0x0000 }, /* R5 - ADC Control */
+ { 6, 0x0000 }, /* R6 - Power Management */
+ { 7, 0x000A }, /* R7 - Audio Format */
+ { 8, 0x0000 }, /* R8 - Clocking */
+ { 9, 0x000F }, /* R9 - MIC Preamp Control */
+ { 10, 0x0003 }, /* R10 - Misc Bias Control */
+ { 11, 0x0000 }, /* R11 - Noise Gate */
+ { 12, 0x007C }, /* R12 - ALC1 */
+ { 13, 0x0000 }, /* R13 - ALC2 */
+ { 14, 0x0032 }, /* R14 - ALC3 */
+};
+
+static bool wm8737_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8737_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int wm8737_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, WM8737_RESET, 0);
+}
+
+static const unsigned int micboost_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 0, TLV_DB_SCALE_ITEM(1300, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(1800, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2800, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(3300, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -9750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(ng_tlv, -7800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(alc_max_tlv, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -1800, 100, 0);
+
+static const char *micbias_enum_text[] = {
+ "25%",
+ "50%",
+ "75%",
+ "100%",
+};
+
+static SOC_ENUM_SINGLE_DECL(micbias_enum,
+ WM8737_MIC_PREAMP_CONTROL, 0, micbias_enum_text);
+
+static const char *low_cutoff_text[] = {
+ "Low", "High"
+};
+
+static SOC_ENUM_SINGLE_DECL(low_3d,
+ WM8737_3D_ENHANCE, 6, low_cutoff_text);
+
+static const char *high_cutoff_text[] = {
+ "High", "Low"
+};
+
+static SOC_ENUM_SINGLE_DECL(high_3d,
+ WM8737_3D_ENHANCE, 5, high_cutoff_text);
+
+static const char *alc_fn_text[] = {
+ "Disabled", "Right", "Left", "Stereo"
+};
+
+static SOC_ENUM_SINGLE_DECL(alc_fn,
+ WM8737_ALC1, 7, alc_fn_text);
+
+static const char *alc_hold_text[] = {
+ "0", "2.67ms", "5.33ms", "10.66ms", "21.32ms", "42.64ms", "85.28ms",
+ "170.56ms", "341.12ms", "682.24ms", "1.364s", "2.728s", "5.458s",
+ "10.916s", "21.832s", "43.691s"
+};
+
+static SOC_ENUM_SINGLE_DECL(alc_hold,
+ WM8737_ALC2, 0, alc_hold_text);
+
+static const char *alc_atk_text[] = {
+ "8.4ms", "16.8ms", "33.6ms", "67.2ms", "134.4ms", "268.8ms", "537.6ms",
+ "1.075s", "2.15s", "4.3s", "8.6s"
+};
+
+static SOC_ENUM_SINGLE_DECL(alc_atk,
+ WM8737_ALC3, 0, alc_atk_text);
+
+static const char *alc_dcy_text[] = {
+ "33.6ms", "67.2ms", "134.4ms", "268.8ms", "537.6ms", "1.075s", "2.15s",
+ "4.3s", "8.6s", "17.2s", "34.41s"
+};
+
+static SOC_ENUM_SINGLE_DECL(alc_dcy,
+ WM8737_ALC3, 4, alc_dcy_text);
+
+static const struct snd_kcontrol_new wm8737_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Mic Boost Volume", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R,
+ 6, 3, 0, micboost_tlv),
+SOC_DOUBLE_R("Mic Boost Switch", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R,
+ 4, 1, 0),
+SOC_DOUBLE("Mic ZC Switch", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R,
+ 3, 1, 0),
+
+SOC_DOUBLE_R_TLV("Capture Volume", WM8737_LEFT_PGA_VOLUME,
+ WM8737_RIGHT_PGA_VOLUME, 0, 255, 0, pga_tlv),
+SOC_DOUBLE("Capture ZC Switch", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R,
+ 2, 1, 0),
+
+SOC_DOUBLE("INPUT1 DC Bias Switch", WM8737_MISC_BIAS_CONTROL, 0, 1, 1, 0),
+
+SOC_ENUM("Mic PGA Bias", micbias_enum),
+SOC_SINGLE("ADC Low Power Switch", WM8737_ADC_CONTROL, 2, 1, 0),
+SOC_SINGLE("High Pass Filter Switch", WM8737_ADC_CONTROL, 0, 1, 1),
+SOC_DOUBLE("Polarity Invert Switch", WM8737_ADC_CONTROL, 5, 6, 1, 0),
+
+SOC_SINGLE("3D Switch", WM8737_3D_ENHANCE, 0, 1, 0),
+SOC_SINGLE("3D Depth", WM8737_3D_ENHANCE, 1, 15, 0),
+SOC_ENUM("3D Low Cut-off", low_3d),
+SOC_ENUM("3D High Cut-off", low_3d),
+SOC_SINGLE_TLV("3D ADC Volume", WM8737_3D_ENHANCE, 7, 1, 1, adc_tlv),
+
+SOC_SINGLE("Noise Gate Switch", WM8737_NOISE_GATE, 0, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", WM8737_NOISE_GATE, 2, 7, 0,
+ ng_tlv),
+
+SOC_ENUM("ALC", alc_fn),
+SOC_SINGLE_TLV("ALC Max Gain Volume", WM8737_ALC1, 4, 7, 0, alc_max_tlv),
+SOC_SINGLE_TLV("ALC Target Volume", WM8737_ALC1, 0, 15, 0, alc_target_tlv),
+SOC_ENUM("ALC Hold Time", alc_hold),
+SOC_SINGLE("ALC ZC Switch", WM8737_ALC2, 4, 1, 0),
+SOC_ENUM("ALC Attack Time", alc_atk),
+SOC_ENUM("ALC Decay Time", alc_dcy),
+};
+
+static const char *linsel_text[] = {
+ "LINPUT1", "LINPUT2", "LINPUT3", "LINPUT1 DC",
+};
+
+static SOC_ENUM_SINGLE_DECL(linsel_enum,
+ WM8737_AUDIO_PATH_L, 7, linsel_text);
+
+static const struct snd_kcontrol_new linsel_mux =
+ SOC_DAPM_ENUM("LINSEL", linsel_enum);
+
+
+static const char *rinsel_text[] = {
+ "RINPUT1", "RINPUT2", "RINPUT3", "RINPUT1 DC",
+};
+
+static SOC_ENUM_SINGLE_DECL(rinsel_enum,
+ WM8737_AUDIO_PATH_R, 7, rinsel_text);
+
+static const struct snd_kcontrol_new rinsel_mux =
+ SOC_DAPM_ENUM("RINSEL", rinsel_enum);
+
+static const char *bypass_text[] = {
+ "Direct", "Preamp"
+};
+
+static SOC_ENUM_SINGLE_DECL(lbypass_enum,
+ WM8737_MIC_PREAMP_CONTROL, 2, bypass_text);
+
+static const struct snd_kcontrol_new lbypass_mux =
+ SOC_DAPM_ENUM("Left Bypass", lbypass_enum);
+
+
+static SOC_ENUM_SINGLE_DECL(rbypass_enum,
+ WM8737_MIC_PREAMP_CONTROL, 3, bypass_text);
+
+static const struct snd_kcontrol_new rbypass_mux =
+ SOC_DAPM_ENUM("Left Bypass", rbypass_enum);
+
+static const struct snd_soc_dapm_widget wm8737_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("LINPUT1"),
+SND_SOC_DAPM_INPUT("LINPUT2"),
+SND_SOC_DAPM_INPUT("LINPUT3"),
+SND_SOC_DAPM_INPUT("RINPUT1"),
+SND_SOC_DAPM_INPUT("RINPUT2"),
+SND_SOC_DAPM_INPUT("RINPUT3"),
+SND_SOC_DAPM_INPUT("LACIN"),
+SND_SOC_DAPM_INPUT("RACIN"),
+
+SND_SOC_DAPM_MUX("LINSEL", SND_SOC_NOPM, 0, 0, &linsel_mux),
+SND_SOC_DAPM_MUX("RINSEL", SND_SOC_NOPM, 0, 0, &rinsel_mux),
+
+SND_SOC_DAPM_MUX("Left Preamp Mux", SND_SOC_NOPM, 0, 0, &lbypass_mux),
+SND_SOC_DAPM_MUX("Right Preamp Mux", SND_SOC_NOPM, 0, 0, &rbypass_mux),
+
+SND_SOC_DAPM_PGA("PGAL", WM8737_POWER_MANAGEMENT, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("PGAR", WM8737_POWER_MANAGEMENT, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_DAC("ADCL", NULL, WM8737_POWER_MANAGEMENT, 3, 0),
+SND_SOC_DAPM_DAC("ADCR", NULL, WM8737_POWER_MANAGEMENT, 2, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF", "Capture", 0, WM8737_POWER_MANAGEMENT, 6, 0),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ { "LINSEL", "LINPUT1", "LINPUT1" },
+ { "LINSEL", "LINPUT2", "LINPUT2" },
+ { "LINSEL", "LINPUT3", "LINPUT3" },
+ { "LINSEL", "LINPUT1 DC", "LINPUT1" },
+
+ { "RINSEL", "RINPUT1", "RINPUT1" },
+ { "RINSEL", "RINPUT2", "RINPUT2" },
+ { "RINSEL", "RINPUT3", "RINPUT3" },
+ { "RINSEL", "RINPUT1 DC", "RINPUT1" },
+
+ { "Left Preamp Mux", "Preamp", "LINSEL" },
+ { "Left Preamp Mux", "Direct", "LACIN" },
+
+ { "Right Preamp Mux", "Preamp", "RINSEL" },
+ { "Right Preamp Mux", "Direct", "RACIN" },
+
+ { "PGAL", NULL, "Left Preamp Mux" },
+ { "PGAR", NULL, "Right Preamp Mux" },
+
+ { "ADCL", NULL, "PGAL" },
+ { "ADCR", NULL, "PGAR" },
+
+ { "AIF", NULL, "ADCL" },
+ { "AIF", NULL, "ADCR" },
+};
+
+/* codec mclk clock divider coefficients */
+static const struct {
+ u32 mclk;
+ u32 rate;
+ u8 usb;
+ u8 sr;
+} coeff_div[] = {
+ { 12288000, 8000, 0, 0x4 },
+ { 12288000, 12000, 0, 0x8 },
+ { 12288000, 16000, 0, 0xa },
+ { 12288000, 24000, 0, 0x1c },
+ { 12288000, 32000, 0, 0xc },
+ { 12288000, 48000, 0, 0 },
+ { 12288000, 96000, 0, 0xe },
+
+ { 11289600, 8000, 0, 0x14 },
+ { 11289600, 11025, 0, 0x18 },
+ { 11289600, 22050, 0, 0x1a },
+ { 11289600, 44100, 0, 0x10 },
+ { 11289600, 88200, 0, 0x1e },
+
+ { 18432000, 8000, 0, 0x5 },
+ { 18432000, 12000, 0, 0x9 },
+ { 18432000, 16000, 0, 0xb },
+ { 18432000, 24000, 0, 0x1b },
+ { 18432000, 32000, 0, 0xd },
+ { 18432000, 48000, 0, 0x1 },
+ { 18432000, 96000, 0, 0x1f },
+
+ { 16934400, 8000, 0, 0x15 },
+ { 16934400, 11025, 0, 0x19 },
+ { 16934400, 22050, 0, 0x1b },
+ { 16934400, 44100, 0, 0x11 },
+ { 16934400, 88200, 0, 0x1f },
+
+ { 12000000, 8000, 1, 0x4 },
+ { 12000000, 11025, 1, 0x19 },
+ { 12000000, 12000, 1, 0x8 },
+ { 12000000, 16000, 1, 0xa },
+ { 12000000, 22050, 1, 0x1b },
+ { 12000000, 24000, 1, 0x1c },
+ { 12000000, 32000, 1, 0xc },
+ { 12000000, 44100, 1, 0x11 },
+ { 12000000, 48000, 1, 0x0 },
+ { 12000000, 88200, 1, 0x1f },
+ { 12000000, 96000, 1, 0xe },
+};
+
+static int wm8737_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec);
+ int i;
+ u16 clocking = 0;
+ u16 af = 0;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate != params_rate(params))
+ continue;
+
+ if (coeff_div[i].mclk == wm8737->mclk)
+ break;
+
+ if (coeff_div[i].mclk == wm8737->mclk * 2) {
+ clocking |= WM8737_CLKDIV2;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(coeff_div)) {
+ dev_err(codec->dev, "%dHz MCLK can't support %dHz\n",
+ wm8737->mclk, params_rate(params));
+ return -EINVAL;
+ }
+
+ clocking |= coeff_div[i].usb | (coeff_div[i].sr << WM8737_SR_SHIFT);
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ af |= 0x8;
+ break;
+ case 24:
+ af |= 0x10;
+ break;
+ case 32:
+ af |= 0x18;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8737_AUDIO_FORMAT, WM8737_WL_MASK, af);
+ snd_soc_update_bits(codec, WM8737_CLOCKING,
+ WM8737_USB_MODE | WM8737_CLKDIV2 | WM8737_SR_MASK,
+ clocking);
+
+ return 0;
+}
+
+static int wm8737_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (freq == coeff_div[i].mclk ||
+ freq == coeff_div[i].mclk * 2) {
+ wm8737->mclk = freq;
+ return 0;
+ }
+ }
+
+ dev_err(codec->dev, "MCLK rate %dHz not supported\n", freq);
+
+ return -EINVAL;
+}
+
+
+static int wm8737_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 af = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ af |= WM8737_MS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ af |= 0x2;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ af |= 0x1;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ af |= 0x3;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ af |= 0x13;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ af |= WM8737_LRP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8737_AUDIO_FORMAT,
+ WM8737_FORMAT_MASK | WM8737_LRP | WM8737_MS, af);
+
+ return 0;
+}
+
+static int wm8737_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID at 2*75k */
+ snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL,
+ WM8737_VMIDSEL_MASK, 0);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies),
+ wm8737->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ regcache_sync(wm8737->regmap);
+
+ /* Fast VMID ramp at 2*2.5k */
+ snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL,
+ WM8737_VMIDSEL_MASK, 0x4);
+
+ /* Bring VMID up */
+ snd_soc_update_bits(codec, WM8737_POWER_MANAGEMENT,
+ WM8737_VMID_MASK |
+ WM8737_VREF_MASK,
+ WM8737_VMID_MASK |
+ WM8737_VREF_MASK);
+
+ msleep(500);
+ }
+
+ /* VMID at 2*300k */
+ snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL,
+ WM8737_VMIDSEL_MASK, 2);
+
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, WM8737_POWER_MANAGEMENT,
+ WM8737_VMID_MASK | WM8737_VREF_MASK, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies),
+ wm8737->supplies);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8737_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8737_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8737_dai_ops = {
+ .hw_params = wm8737_hw_params,
+ .set_sysclk = wm8737_set_dai_sysclk,
+ .set_fmt = wm8737_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver wm8737_dai = {
+ .name = "wm8737",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2, /* Mono modes not yet supported */
+ .channels_max = 2,
+ .rates = WM8737_RATES,
+ .formats = WM8737_FORMATS,
+ },
+ .ops = &wm8737_dai_ops,
+};
+
+static int wm8737_probe(struct snd_soc_codec *codec)
+{
+ struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies),
+ wm8737->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_get;
+ }
+
+ ret = wm8737_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err_enable;
+ }
+
+ snd_soc_update_bits(codec, WM8737_LEFT_PGA_VOLUME, WM8737_LVU,
+ WM8737_LVU);
+ snd_soc_update_bits(codec, WM8737_RIGHT_PGA_VOLUME, WM8737_RVU,
+ WM8737_RVU);
+
+ wm8737_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Bias level configuration will have done an extra enable */
+ regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies);
+
+ return 0;
+
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies);
+err_get:
+ return ret;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8737 = {
+ .probe = wm8737_probe,
+ .set_bias_level = wm8737_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8737_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8737_snd_controls),
+ .dapm_widgets = wm8737_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8737_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+};
+
+static const struct of_device_id wm8737_of_match[] = {
+ { .compatible = "wlf,wm8737", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, wm8737_of_match);
+
+static const struct regmap_config wm8737_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8737_MAX_REGISTER,
+
+ .reg_defaults = wm8737_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8737_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8737_volatile,
+};
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8737_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8737_priv *wm8737;
+ int ret, i;
+
+ wm8737 = devm_kzalloc(&i2c->dev, sizeof(struct wm8737_priv),
+ GFP_KERNEL);
+ if (wm8737 == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(wm8737->supplies); i++)
+ wm8737->supplies[i].supply = wm8737_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8737->supplies),
+ wm8737->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ wm8737->regmap = devm_regmap_init_i2c(i2c, &wm8737_regmap);
+ if (IS_ERR(wm8737->regmap))
+ return PTR_ERR(wm8737->regmap);
+
+ i2c_set_clientdata(i2c, wm8737);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8737, &wm8737_dai, 1);
+
+ return ret;
+
+}
+
+static int wm8737_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm8737_i2c_id[] = {
+ { "wm8737", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8737_i2c_id);
+
+static struct i2c_driver wm8737_i2c_driver = {
+ .driver = {
+ .name = "wm8737",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8737_of_match,
+ },
+ .probe = wm8737_i2c_probe,
+ .remove = wm8737_i2c_remove,
+ .id_table = wm8737_i2c_id,
+};
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8737_spi_probe(struct spi_device *spi)
+{
+ struct wm8737_priv *wm8737;
+ int ret, i;
+
+ wm8737 = devm_kzalloc(&spi->dev, sizeof(struct wm8737_priv),
+ GFP_KERNEL);
+ if (wm8737 == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(wm8737->supplies); i++)
+ wm8737->supplies[i].supply = wm8737_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8737->supplies),
+ wm8737->supplies);
+ if (ret != 0) {
+ dev_err(&spi->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ wm8737->regmap = devm_regmap_init_spi(spi, &wm8737_regmap);
+ if (IS_ERR(wm8737->regmap))
+ return PTR_ERR(wm8737->regmap);
+
+ spi_set_drvdata(spi, wm8737);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8737, &wm8737_dai, 1);
+
+ return ret;
+}
+
+static int wm8737_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+
+ return 0;
+}
+
+static struct spi_driver wm8737_spi_driver = {
+ .driver = {
+ .name = "wm8737",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8737_of_match,
+ },
+ .probe = wm8737_spi_probe,
+ .remove = wm8737_spi_remove,
+};
+#endif /* CONFIG_SPI_MASTER */
+
+static int __init wm8737_modinit(void)
+{
+ int ret;
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8737_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8737 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8737_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8737 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return 0;
+}
+module_init(wm8737_modinit);
+
+static void __exit wm8737_exit(void)
+{
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8737_spi_driver);
+#endif
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8737_i2c_driver);
+#endif
+}
+module_exit(wm8737_exit);
+
+MODULE_DESCRIPTION("ASoC WM8737 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8737.h b/kernel/sound/soc/codecs/wm8737.h
new file mode 100644
index 000000000..23d14c8ff
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8737.h
@@ -0,0 +1,322 @@
+#ifndef _WM8737_H
+#define _WM8737_H
+
+/*
+ * wm8737.c -- WM8523 ALSA SoC Audio driver
+ *
+ * Copyright 2010 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.
+ */
+
+/*
+ * Register values.
+ */
+#define WM8737_LEFT_PGA_VOLUME 0x00
+#define WM8737_RIGHT_PGA_VOLUME 0x01
+#define WM8737_AUDIO_PATH_L 0x02
+#define WM8737_AUDIO_PATH_R 0x03
+#define WM8737_3D_ENHANCE 0x04
+#define WM8737_ADC_CONTROL 0x05
+#define WM8737_POWER_MANAGEMENT 0x06
+#define WM8737_AUDIO_FORMAT 0x07
+#define WM8737_CLOCKING 0x08
+#define WM8737_MIC_PREAMP_CONTROL 0x09
+#define WM8737_MISC_BIAS_CONTROL 0x0A
+#define WM8737_NOISE_GATE 0x0B
+#define WM8737_ALC1 0x0C
+#define WM8737_ALC2 0x0D
+#define WM8737_ALC3 0x0E
+#define WM8737_RESET 0x0F
+
+#define WM8737_REGISTER_COUNT 16
+#define WM8737_MAX_REGISTER 0x0F
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Left PGA volume
+ */
+#define WM8737_LVU 0x0100 /* LVU */
+#define WM8737_LVU_MASK 0x0100 /* LVU */
+#define WM8737_LVU_SHIFT 8 /* LVU */
+#define WM8737_LVU_WIDTH 1 /* LVU */
+#define WM8737_LINVOL_MASK 0x00FF /* LINVOL - [7:0] */
+#define WM8737_LINVOL_SHIFT 0 /* LINVOL - [7:0] */
+#define WM8737_LINVOL_WIDTH 8 /* LINVOL - [7:0] */
+
+/*
+ * R1 (0x01) - Right PGA volume
+ */
+#define WM8737_RVU 0x0100 /* RVU */
+#define WM8737_RVU_MASK 0x0100 /* RVU */
+#define WM8737_RVU_SHIFT 8 /* RVU */
+#define WM8737_RVU_WIDTH 1 /* RVU */
+#define WM8737_RINVOL_MASK 0x00FF /* RINVOL - [7:0] */
+#define WM8737_RINVOL_SHIFT 0 /* RINVOL - [7:0] */
+#define WM8737_RINVOL_WIDTH 8 /* RINVOL - [7:0] */
+
+/*
+ * R2 (0x02) - AUDIO path L
+ */
+#define WM8737_LINSEL_MASK 0x0180 /* LINSEL - [8:7] */
+#define WM8737_LINSEL_SHIFT 7 /* LINSEL - [8:7] */
+#define WM8737_LINSEL_WIDTH 2 /* LINSEL - [8:7] */
+#define WM8737_LMICBOOST_MASK 0x0060 /* LMICBOOST - [6:5] */
+#define WM8737_LMICBOOST_SHIFT 5 /* LMICBOOST - [6:5] */
+#define WM8737_LMICBOOST_WIDTH 2 /* LMICBOOST - [6:5] */
+#define WM8737_LMBE 0x0010 /* LMBE */
+#define WM8737_LMBE_MASK 0x0010 /* LMBE */
+#define WM8737_LMBE_SHIFT 4 /* LMBE */
+#define WM8737_LMBE_WIDTH 1 /* LMBE */
+#define WM8737_LMZC 0x0008 /* LMZC */
+#define WM8737_LMZC_MASK 0x0008 /* LMZC */
+#define WM8737_LMZC_SHIFT 3 /* LMZC */
+#define WM8737_LMZC_WIDTH 1 /* LMZC */
+#define WM8737_LPZC 0x0004 /* LPZC */
+#define WM8737_LPZC_MASK 0x0004 /* LPZC */
+#define WM8737_LPZC_SHIFT 2 /* LPZC */
+#define WM8737_LPZC_WIDTH 1 /* LPZC */
+#define WM8737_LZCTO_MASK 0x0003 /* LZCTO - [1:0] */
+#define WM8737_LZCTO_SHIFT 0 /* LZCTO - [1:0] */
+#define WM8737_LZCTO_WIDTH 2 /* LZCTO - [1:0] */
+
+/*
+ * R3 (0x03) - AUDIO path R
+ */
+#define WM8737_RINSEL_MASK 0x0180 /* RINSEL - [8:7] */
+#define WM8737_RINSEL_SHIFT 7 /* RINSEL - [8:7] */
+#define WM8737_RINSEL_WIDTH 2 /* RINSEL - [8:7] */
+#define WM8737_RMICBOOST_MASK 0x0060 /* RMICBOOST - [6:5] */
+#define WM8737_RMICBOOST_SHIFT 5 /* RMICBOOST - [6:5] */
+#define WM8737_RMICBOOST_WIDTH 2 /* RMICBOOST - [6:5] */
+#define WM8737_RMBE 0x0010 /* RMBE */
+#define WM8737_RMBE_MASK 0x0010 /* RMBE */
+#define WM8737_RMBE_SHIFT 4 /* RMBE */
+#define WM8737_RMBE_WIDTH 1 /* RMBE */
+#define WM8737_RMZC 0x0008 /* RMZC */
+#define WM8737_RMZC_MASK 0x0008 /* RMZC */
+#define WM8737_RMZC_SHIFT 3 /* RMZC */
+#define WM8737_RMZC_WIDTH 1 /* RMZC */
+#define WM8737_RPZC 0x0004 /* RPZC */
+#define WM8737_RPZC_MASK 0x0004 /* RPZC */
+#define WM8737_RPZC_SHIFT 2 /* RPZC */
+#define WM8737_RPZC_WIDTH 1 /* RPZC */
+#define WM8737_RZCTO_MASK 0x0003 /* RZCTO - [1:0] */
+#define WM8737_RZCTO_SHIFT 0 /* RZCTO - [1:0] */
+#define WM8737_RZCTO_WIDTH 2 /* RZCTO - [1:0] */
+
+/*
+ * R4 (0x04) - 3D Enhance
+ */
+#define WM8737_DIV2 0x0080 /* DIV2 */
+#define WM8737_DIV2_MASK 0x0080 /* DIV2 */
+#define WM8737_DIV2_SHIFT 7 /* DIV2 */
+#define WM8737_DIV2_WIDTH 1 /* DIV2 */
+#define WM8737_3DLC 0x0040 /* 3DLC */
+#define WM8737_3DLC_MASK 0x0040 /* 3DLC */
+#define WM8737_3DLC_SHIFT 6 /* 3DLC */
+#define WM8737_3DLC_WIDTH 1 /* 3DLC */
+#define WM8737_3DUC 0x0020 /* 3DUC */
+#define WM8737_3DUC_MASK 0x0020 /* 3DUC */
+#define WM8737_3DUC_SHIFT 5 /* 3DUC */
+#define WM8737_3DUC_WIDTH 1 /* 3DUC */
+#define WM8737_3DDEPTH_MASK 0x001E /* 3DDEPTH - [4:1] */
+#define WM8737_3DDEPTH_SHIFT 1 /* 3DDEPTH - [4:1] */
+#define WM8737_3DDEPTH_WIDTH 4 /* 3DDEPTH - [4:1] */
+#define WM8737_3DE 0x0001 /* 3DE */
+#define WM8737_3DE_MASK 0x0001 /* 3DE */
+#define WM8737_3DE_SHIFT 0 /* 3DE */
+#define WM8737_3DE_WIDTH 1 /* 3DE */
+
+/*
+ * R5 (0x05) - ADC Control
+ */
+#define WM8737_MONOMIX_MASK 0x0180 /* MONOMIX - [8:7] */
+#define WM8737_MONOMIX_SHIFT 7 /* MONOMIX - [8:7] */
+#define WM8737_MONOMIX_WIDTH 2 /* MONOMIX - [8:7] */
+#define WM8737_POLARITY_MASK 0x0060 /* POLARITY - [6:5] */
+#define WM8737_POLARITY_SHIFT 5 /* POLARITY - [6:5] */
+#define WM8737_POLARITY_WIDTH 2 /* POLARITY - [6:5] */
+#define WM8737_HPOR 0x0010 /* HPOR */
+#define WM8737_HPOR_MASK 0x0010 /* HPOR */
+#define WM8737_HPOR_SHIFT 4 /* HPOR */
+#define WM8737_HPOR_WIDTH 1 /* HPOR */
+#define WM8737_LP 0x0004 /* LP */
+#define WM8737_LP_MASK 0x0004 /* LP */
+#define WM8737_LP_SHIFT 2 /* LP */
+#define WM8737_LP_WIDTH 1 /* LP */
+#define WM8737_MONOUT 0x0002 /* MONOUT */
+#define WM8737_MONOUT_MASK 0x0002 /* MONOUT */
+#define WM8737_MONOUT_SHIFT 1 /* MONOUT */
+#define WM8737_MONOUT_WIDTH 1 /* MONOUT */
+#define WM8737_ADCHPD 0x0001 /* ADCHPD */
+#define WM8737_ADCHPD_MASK 0x0001 /* ADCHPD */
+#define WM8737_ADCHPD_SHIFT 0 /* ADCHPD */
+#define WM8737_ADCHPD_WIDTH 1 /* ADCHPD */
+
+/*
+ * R6 (0x06) - Power Management
+ */
+#define WM8737_VMID 0x0100 /* VMID */
+#define WM8737_VMID_MASK 0x0100 /* VMID */
+#define WM8737_VMID_SHIFT 8 /* VMID */
+#define WM8737_VMID_WIDTH 1 /* VMID */
+#define WM8737_VREF 0x0080 /* VREF */
+#define WM8737_VREF_MASK 0x0080 /* VREF */
+#define WM8737_VREF_SHIFT 7 /* VREF */
+#define WM8737_VREF_WIDTH 1 /* VREF */
+#define WM8737_AI 0x0040 /* AI */
+#define WM8737_AI_MASK 0x0040 /* AI */
+#define WM8737_AI_SHIFT 6 /* AI */
+#define WM8737_AI_WIDTH 1 /* AI */
+#define WM8737_PGL 0x0020 /* PGL */
+#define WM8737_PGL_MASK 0x0020 /* PGL */
+#define WM8737_PGL_SHIFT 5 /* PGL */
+#define WM8737_PGL_WIDTH 1 /* PGL */
+#define WM8737_PGR 0x0010 /* PGR */
+#define WM8737_PGR_MASK 0x0010 /* PGR */
+#define WM8737_PGR_SHIFT 4 /* PGR */
+#define WM8737_PGR_WIDTH 1 /* PGR */
+#define WM8737_ADL 0x0008 /* ADL */
+#define WM8737_ADL_MASK 0x0008 /* ADL */
+#define WM8737_ADL_SHIFT 3 /* ADL */
+#define WM8737_ADL_WIDTH 1 /* ADL */
+#define WM8737_ADR 0x0004 /* ADR */
+#define WM8737_ADR_MASK 0x0004 /* ADR */
+#define WM8737_ADR_SHIFT 2 /* ADR */
+#define WM8737_ADR_WIDTH 1 /* ADR */
+#define WM8737_MICBIAS_MASK 0x0003 /* MICBIAS - [1:0] */
+#define WM8737_MICBIAS_SHIFT 0 /* MICBIAS - [1:0] */
+#define WM8737_MICBIAS_WIDTH 2 /* MICBIAS - [1:0] */
+
+/*
+ * R7 (0x07) - Audio Format
+ */
+#define WM8737_SDODIS 0x0080 /* SDODIS */
+#define WM8737_SDODIS_MASK 0x0080 /* SDODIS */
+#define WM8737_SDODIS_SHIFT 7 /* SDODIS */
+#define WM8737_SDODIS_WIDTH 1 /* SDODIS */
+#define WM8737_MS 0x0040 /* MS */
+#define WM8737_MS_MASK 0x0040 /* MS */
+#define WM8737_MS_SHIFT 6 /* MS */
+#define WM8737_MS_WIDTH 1 /* MS */
+#define WM8737_LRP 0x0010 /* LRP */
+#define WM8737_LRP_MASK 0x0010 /* LRP */
+#define WM8737_LRP_SHIFT 4 /* LRP */
+#define WM8737_LRP_WIDTH 1 /* LRP */
+#define WM8737_WL_MASK 0x000C /* WL - [3:2] */
+#define WM8737_WL_SHIFT 2 /* WL - [3:2] */
+#define WM8737_WL_WIDTH 2 /* WL - [3:2] */
+#define WM8737_FORMAT_MASK 0x0003 /* FORMAT - [1:0] */
+#define WM8737_FORMAT_SHIFT 0 /* FORMAT - [1:0] */
+#define WM8737_FORMAT_WIDTH 2 /* FORMAT - [1:0] */
+
+/*
+ * R8 (0x08) - Clocking
+ */
+#define WM8737_AUTODETECT 0x0080 /* AUTODETECT */
+#define WM8737_AUTODETECT_MASK 0x0080 /* AUTODETECT */
+#define WM8737_AUTODETECT_SHIFT 7 /* AUTODETECT */
+#define WM8737_AUTODETECT_WIDTH 1 /* AUTODETECT */
+#define WM8737_CLKDIV2 0x0040 /* CLKDIV2 */
+#define WM8737_CLKDIV2_MASK 0x0040 /* CLKDIV2 */
+#define WM8737_CLKDIV2_SHIFT 6 /* CLKDIV2 */
+#define WM8737_CLKDIV2_WIDTH 1 /* CLKDIV2 */
+#define WM8737_SR_MASK 0x003E /* SR - [5:1] */
+#define WM8737_SR_SHIFT 1 /* SR - [5:1] */
+#define WM8737_SR_WIDTH 5 /* SR - [5:1] */
+#define WM8737_USB_MODE 0x0001 /* USB MODE */
+#define WM8737_USB_MODE_MASK 0x0001 /* USB MODE */
+#define WM8737_USB_MODE_SHIFT 0 /* USB MODE */
+#define WM8737_USB_MODE_WIDTH 1 /* USB MODE */
+
+/*
+ * R9 (0x09) - MIC Preamp Control
+ */
+#define WM8737_RBYPEN 0x0008 /* RBYPEN */
+#define WM8737_RBYPEN_MASK 0x0008 /* RBYPEN */
+#define WM8737_RBYPEN_SHIFT 3 /* RBYPEN */
+#define WM8737_RBYPEN_WIDTH 1 /* RBYPEN */
+#define WM8737_LBYPEN 0x0004 /* LBYPEN */
+#define WM8737_LBYPEN_MASK 0x0004 /* LBYPEN */
+#define WM8737_LBYPEN_SHIFT 2 /* LBYPEN */
+#define WM8737_LBYPEN_WIDTH 1 /* LBYPEN */
+#define WM8737_MBCTRL_MASK 0x0003 /* MBCTRL - [1:0] */
+#define WM8737_MBCTRL_SHIFT 0 /* MBCTRL - [1:0] */
+#define WM8737_MBCTRL_WIDTH 2 /* MBCTRL - [1:0] */
+
+/*
+ * R10 (0x0A) - Misc Bias Control
+ */
+#define WM8737_VMIDSEL_MASK 0x000C /* VMIDSEL - [3:2] */
+#define WM8737_VMIDSEL_SHIFT 2 /* VMIDSEL - [3:2] */
+#define WM8737_VMIDSEL_WIDTH 2 /* VMIDSEL - [3:2] */
+#define WM8737_LINPUT1_DC_BIAS_ENABLE 0x0002 /* LINPUT1 DC BIAS ENABLE */
+#define WM8737_LINPUT1_DC_BIAS_ENABLE_MASK 0x0002 /* LINPUT1 DC BIAS ENABLE */
+#define WM8737_LINPUT1_DC_BIAS_ENABLE_SHIFT 1 /* LINPUT1 DC BIAS ENABLE */
+#define WM8737_LINPUT1_DC_BIAS_ENABLE_WIDTH 1 /* LINPUT1 DC BIAS ENABLE */
+#define WM8737_RINPUT1_DC_BIAS_ENABLE 0x0001 /* RINPUT1 DC BIAS ENABLE */
+#define WM8737_RINPUT1_DC_BIAS_ENABLE_MASK 0x0001 /* RINPUT1 DC BIAS ENABLE */
+#define WM8737_RINPUT1_DC_BIAS_ENABLE_SHIFT 0 /* RINPUT1 DC BIAS ENABLE */
+#define WM8737_RINPUT1_DC_BIAS_ENABLE_WIDTH 1 /* RINPUT1 DC BIAS ENABLE */
+
+/*
+ * R11 (0x0B) - Noise Gate
+ */
+#define WM8737_NGTH_MASK 0x001C /* NGTH - [4:2] */
+#define WM8737_NGTH_SHIFT 2 /* NGTH - [4:2] */
+#define WM8737_NGTH_WIDTH 3 /* NGTH - [4:2] */
+#define WM8737_NGAT 0x0001 /* NGAT */
+#define WM8737_NGAT_MASK 0x0001 /* NGAT */
+#define WM8737_NGAT_SHIFT 0 /* NGAT */
+#define WM8737_NGAT_WIDTH 1 /* NGAT */
+
+/*
+ * R12 (0x0C) - ALC1
+ */
+#define WM8737_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */
+#define WM8737_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */
+#define WM8737_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */
+#define WM8737_MAX_GAIN_MASK 0x0070 /* MAX GAIN - [6:4] */
+#define WM8737_MAX_GAIN_SHIFT 4 /* MAX GAIN - [6:4] */
+#define WM8737_MAX_GAIN_WIDTH 3 /* MAX GAIN - [6:4] */
+#define WM8737_ALCL_MASK 0x000F /* ALCL - [3:0] */
+#define WM8737_ALCL_SHIFT 0 /* ALCL - [3:0] */
+#define WM8737_ALCL_WIDTH 4 /* ALCL - [3:0] */
+
+/*
+ * R13 (0x0D) - ALC2
+ */
+#define WM8737_ALCZCE 0x0010 /* ALCZCE */
+#define WM8737_ALCZCE_MASK 0x0010 /* ALCZCE */
+#define WM8737_ALCZCE_SHIFT 4 /* ALCZCE */
+#define WM8737_ALCZCE_WIDTH 1 /* ALCZCE */
+#define WM8737_HLD_MASK 0x000F /* HLD - [3:0] */
+#define WM8737_HLD_SHIFT 0 /* HLD - [3:0] */
+#define WM8737_HLD_WIDTH 4 /* HLD - [3:0] */
+
+/*
+ * R14 (0x0E) - ALC3
+ */
+#define WM8737_DCY_MASK 0x00F0 /* DCY - [7:4] */
+#define WM8737_DCY_SHIFT 4 /* DCY - [7:4] */
+#define WM8737_DCY_WIDTH 4 /* DCY - [7:4] */
+#define WM8737_ATK_MASK 0x000F /* ATK - [3:0] */
+#define WM8737_ATK_SHIFT 0 /* ATK - [3:0] */
+#define WM8737_ATK_WIDTH 4 /* ATK - [3:0] */
+
+/*
+ * R15 (0x0F) - Reset
+ */
+#define WM8737_RESET_MASK 0x01FF /* RESET - [8:0] */
+#define WM8737_RESET_SHIFT 0 /* RESET - [8:0] */
+#define WM8737_RESET_WIDTH 9 /* RESET - [8:0] */
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8741.c b/kernel/sound/soc/codecs/wm8741.c
new file mode 100644
index 000000000..9e71c7689
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8741.c
@@ -0,0 +1,643 @@
+/*
+ * wm8741.c -- WM8741 ALSA SoC Audio driver
+ *
+ * Copyright 2010-1 Wolfson Microelectronics plc
+ *
+ * Author: Ian Lartey <ian@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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/of_device.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 "wm8741.h"
+
+#define WM8741_NUM_SUPPLIES 2
+static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = {
+ "AVDD",
+ "DVDD",
+};
+
+#define WM8741_NUM_RATES 6
+
+/* codec private data */
+struct wm8741_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
+ unsigned int sysclk;
+ const struct snd_pcm_hw_constraint_list *sysclk_constraints;
+};
+
+static const struct reg_default wm8741_reg_defaults[] = {
+ { 0, 0x0000 }, /* R0 - DACLLSB Attenuation */
+ { 1, 0x0000 }, /* R1 - DACLMSB Attenuation */
+ { 2, 0x0000 }, /* R2 - DACRLSB Attenuation */
+ { 3, 0x0000 }, /* R3 - DACRMSB Attenuation */
+ { 4, 0x0000 }, /* R4 - Volume Control */
+ { 5, 0x000A }, /* R5 - Format Control */
+ { 6, 0x0000 }, /* R6 - Filter Control */
+ { 7, 0x0000 }, /* R7 - Mode Control 1 */
+ { 8, 0x0002 }, /* R8 - Mode Control 2 */
+ { 32, 0x0002 }, /* R32 - ADDITONAL_CONTROL_1 */
+};
+
+static bool wm8741_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8741_DACLLSB_ATTENUATION:
+ case WM8741_DACLMSB_ATTENUATION:
+ case WM8741_DACRLSB_ATTENUATION:
+ case WM8741_DACRMSB_ATTENUATION:
+ case WM8741_VOLUME_CONTROL:
+ case WM8741_FORMAT_CONTROL:
+ case WM8741_FILTER_CONTROL:
+ case WM8741_MODE_CONTROL_1:
+ case WM8741_MODE_CONTROL_2:
+ case WM8741_ADDITIONAL_CONTROL_1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int wm8741_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, WM8741_RESET, 0);
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0);
+
+static const struct snd_kcontrol_new wm8741_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
+ WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine),
+SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
+ WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv),
+};
+
+static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("VOUTLP"),
+SND_SOC_DAPM_OUTPUT("VOUTLN"),
+SND_SOC_DAPM_OUTPUT("VOUTRP"),
+SND_SOC_DAPM_OUTPUT("VOUTRN"),
+};
+
+static const struct snd_soc_dapm_route wm8741_dapm_routes[] = {
+ { "VOUTLP", NULL, "DACL" },
+ { "VOUTLN", NULL, "DACL" },
+ { "VOUTRP", NULL, "DACR" },
+ { "VOUTRN", NULL, "DACR" },
+};
+
+static struct {
+ int value;
+ int ratio;
+} lrclk_ratios[WM8741_NUM_RATES] = {
+ { 1, 128 },
+ { 2, 192 },
+ { 3, 256 },
+ { 4, 384 },
+ { 5, 512 },
+ { 6, 768 },
+};
+
+static const unsigned int rates_11289[] = {
+ 44100, 88200,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_11289 = {
+ .count = ARRAY_SIZE(rates_11289),
+ .list = rates_11289,
+};
+
+static const unsigned int rates_12288[] = {
+ 32000, 48000, 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_12288 = {
+ .count = ARRAY_SIZE(rates_12288),
+ .list = rates_12288,
+};
+
+static const unsigned int rates_16384[] = {
+ 32000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_16384 = {
+ .count = ARRAY_SIZE(rates_16384),
+ .list = rates_16384,
+};
+
+static const unsigned int rates_16934[] = {
+ 44100, 88200,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_16934 = {
+ .count = ARRAY_SIZE(rates_16934),
+ .list = rates_16934,
+};
+
+static const unsigned int rates_18432[] = {
+ 48000, 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_18432 = {
+ .count = ARRAY_SIZE(rates_18432),
+ .list = rates_18432,
+};
+
+static const unsigned int rates_22579[] = {
+ 44100, 88200, 176400
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_22579 = {
+ .count = ARRAY_SIZE(rates_22579),
+ .list = rates_22579,
+};
+
+static const unsigned int rates_24576[] = {
+ 32000, 48000, 96000, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_24576 = {
+ .count = ARRAY_SIZE(rates_24576),
+ .list = rates_24576,
+};
+
+static const unsigned int rates_36864[] = {
+ 48000, 96000, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_36864 = {
+ .count = ARRAY_SIZE(rates_36864),
+ .list = rates_36864,
+};
+
+
+static int wm8741_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+
+ /* The set of sample rates that can be supported depends on the
+ * MCLK supplied to the CODEC - enforce this.
+ */
+ if (!wm8741->sysclk) {
+ dev_err(codec->dev,
+ "No MCLK configured, call set_sysclk() on init\n");
+ return -EINVAL;
+ }
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ wm8741->sysclk_constraints);
+
+ return 0;
+}
+
+static int wm8741_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+ u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC;
+ int i;
+
+ /* Find a supported LRCLK ratio */
+ for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
+ if (wm8741->sysclk / params_rate(params) ==
+ lrclk_ratios[i].ratio)
+ break;
+ }
+
+ /* Should never happen, should be handled by constraints */
+ if (i == ARRAY_SIZE(lrclk_ratios)) {
+ dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n",
+ wm8741->sysclk / params_rate(params));
+ return -EINVAL;
+ }
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface |= 0x0001;
+ break;
+ case 24:
+ iface |= 0x0002;
+ break;
+ case 32:
+ iface |= 0x0003;
+ break;
+ default:
+ dev_dbg(codec->dev, "wm8741_hw_params: Unsupported bit size param = %d",
+ params_width(params));
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d",
+ params_width(params));
+
+ snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface);
+ return 0;
+}
+
+static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq);
+
+ switch (freq) {
+ case 11289600:
+ wm8741->sysclk_constraints = &constraints_11289;
+ wm8741->sysclk = freq;
+ return 0;
+
+ case 12288000:
+ wm8741->sysclk_constraints = &constraints_12288;
+ wm8741->sysclk = freq;
+ return 0;
+
+ case 16384000:
+ wm8741->sysclk_constraints = &constraints_16384;
+ wm8741->sysclk = freq;
+ return 0;
+
+ case 16934400:
+ wm8741->sysclk_constraints = &constraints_16934;
+ wm8741->sysclk = freq;
+ return 0;
+
+ case 18432000:
+ wm8741->sysclk_constraints = &constraints_18432;
+ wm8741->sysclk = freq;
+ return 0;
+
+ case 22579200:
+ case 33868800:
+ wm8741->sysclk_constraints = &constraints_22579;
+ wm8741->sysclk = freq;
+ return 0;
+
+ case 24576000:
+ wm8741->sysclk_constraints = &constraints_24576;
+ wm8741->sysclk = freq;
+ return 0;
+
+ case 36864000:
+ wm8741->sysclk_constraints = &constraints_36864;
+ wm8741->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1C3;
+
+ /* check master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0008;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0004;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x000C;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x001C;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0010;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0020;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0030;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ dev_dbg(codec->dev, "wm8741_set_dai_fmt: Format=%x, Clock Inv=%x\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK,
+ ((fmt & SND_SOC_DAIFMT_INV_MASK)));
+
+ snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface);
+ return 0;
+}
+
+#define WM8741_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000)
+
+#define WM8741_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8741_dai_ops = {
+ .startup = wm8741_startup,
+ .hw_params = wm8741_hw_params,
+ .set_sysclk = wm8741_set_dai_sysclk,
+ .set_fmt = wm8741_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver wm8741_dai = {
+ .name = "wm8741",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2, /* Mono modes not yet supported */
+ .channels_max = 2,
+ .rates = WM8741_RATES,
+ .formats = WM8741_FORMATS,
+ },
+ .ops = &wm8741_dai_ops,
+};
+
+#ifdef CONFIG_PM
+static int wm8741_resume(struct snd_soc_codec *codec)
+{
+ snd_soc_cache_sync(codec);
+ return 0;
+}
+#else
+#define wm8741_resume NULL
+#endif
+
+static int wm8741_probe(struct snd_soc_codec *codec)
+{
+ struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8741->supplies),
+ wm8741->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_get;
+ }
+
+ ret = wm8741_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err_enable;
+ }
+
+ /* Change some default settings - latch VU */
+ snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
+ WM8741_UPDATELL, WM8741_UPDATELL);
+ snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
+ WM8741_UPDATELM, WM8741_UPDATELM);
+ snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
+ WM8741_UPDATERL, WM8741_UPDATERL);
+ snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION,
+ WM8741_UPDATERM, WM8741_UPDATERM);
+
+ dev_dbg(codec->dev, "Successful registration\n");
+ return ret;
+
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
+err_get:
+ return ret;
+}
+
+static int wm8741_remove(struct snd_soc_codec *codec)
+{
+ struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8741 = {
+ .probe = wm8741_probe,
+ .remove = wm8741_remove,
+ .resume = wm8741_resume,
+
+ .controls = wm8741_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8741_snd_controls),
+ .dapm_widgets = wm8741_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets),
+ .dapm_routes = wm8741_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8741_dapm_routes),
+};
+
+static const struct of_device_id wm8741_of_match[] = {
+ { .compatible = "wlf,wm8741", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8741_of_match);
+
+static const struct regmap_config wm8741_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8741_MAX_REGISTER,
+
+ .reg_defaults = wm8741_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8741_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .readable_reg = wm8741_readable,
+};
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8741_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8741_priv *wm8741;
+ int ret, i;
+
+ wm8741 = devm_kzalloc(&i2c->dev, sizeof(struct wm8741_priv),
+ GFP_KERNEL);
+ if (wm8741 == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++)
+ wm8741->supplies[i].supply = wm8741_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8741->supplies),
+ wm8741->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ wm8741->regmap = devm_regmap_init_i2c(i2c, &wm8741_regmap);
+ if (IS_ERR(wm8741->regmap)) {
+ ret = PTR_ERR(wm8741->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, wm8741);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8741, &wm8741_dai, 1);
+
+ return ret;
+}
+
+static int wm8741_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8741_i2c_id[] = {
+ { "wm8741", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id);
+
+static struct i2c_driver wm8741_i2c_driver = {
+ .driver = {
+ .name = "wm8741",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8741_of_match,
+ },
+ .probe = wm8741_i2c_probe,
+ .remove = wm8741_i2c_remove,
+ .id_table = wm8741_i2c_id,
+};
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8741_spi_probe(struct spi_device *spi)
+{
+ struct wm8741_priv *wm8741;
+ int ret, i;
+
+ wm8741 = devm_kzalloc(&spi->dev, sizeof(struct wm8741_priv),
+ GFP_KERNEL);
+ if (wm8741 == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++)
+ wm8741->supplies[i].supply = wm8741_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8741->supplies),
+ wm8741->supplies);
+ if (ret != 0) {
+ dev_err(&spi->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ wm8741->regmap = devm_regmap_init_spi(spi, &wm8741_regmap);
+ if (IS_ERR(wm8741->regmap)) {
+ ret = PTR_ERR(wm8741->regmap);
+ dev_err(&spi->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ spi_set_drvdata(spi, wm8741);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8741, &wm8741_dai, 1);
+ return ret;
+}
+
+static int wm8741_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver wm8741_spi_driver = {
+ .driver = {
+ .name = "wm8741",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8741_of_match,
+ },
+ .probe = wm8741_spi_probe,
+ .remove = wm8741_spi_remove,
+};
+#endif /* CONFIG_SPI_MASTER */
+
+static int __init wm8741_modinit(void)
+{
+ int ret = 0;
+
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8741_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM8741 I2C driver: %d\n", ret);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8741_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8741 SPI driver: %d\n",
+ ret);
+ }
+#endif
+
+ return ret;
+}
+module_init(wm8741_modinit);
+
+static void __exit wm8741_exit(void)
+{
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8741_spi_driver);
+#endif
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8741_i2c_driver);
+#endif
+}
+module_exit(wm8741_exit);
+
+MODULE_DESCRIPTION("ASoC WM8741 driver");
+MODULE_AUTHOR("Ian Lartey <ian@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8741.h b/kernel/sound/soc/codecs/wm8741.h
new file mode 100644
index 000000000..56c1b1d4a
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8741.h
@@ -0,0 +1,211 @@
+/*
+ * wm8741.h -- WM8423 ASoC driver
+ *
+ * Copyright 2010 Wolfson Microelectronics, plc
+ *
+ * Author: Ian Lartey <ian@opensource.wolfsonmicro.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 _WM8741_H
+#define _WM8741_H
+
+/*
+ * Register values.
+ */
+#define WM8741_DACLLSB_ATTENUATION 0x00
+#define WM8741_DACLMSB_ATTENUATION 0x01
+#define WM8741_DACRLSB_ATTENUATION 0x02
+#define WM8741_DACRMSB_ATTENUATION 0x03
+#define WM8741_VOLUME_CONTROL 0x04
+#define WM8741_FORMAT_CONTROL 0x05
+#define WM8741_FILTER_CONTROL 0x06
+#define WM8741_MODE_CONTROL_1 0x07
+#define WM8741_MODE_CONTROL_2 0x08
+#define WM8741_RESET 0x09
+#define WM8741_ADDITIONAL_CONTROL_1 0x20
+
+#define WM8741_REGISTER_COUNT 11
+#define WM8741_MAX_REGISTER 0x20
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - DACLLSB_ATTENUATION
+ */
+#define WM8741_UPDATELL 0x0020 /* UPDATELL */
+#define WM8741_UPDATELL_MASK 0x0020 /* UPDATELL */
+#define WM8741_UPDATELL_SHIFT 5 /* UPDATELL */
+#define WM8741_UPDATELL_WIDTH 1 /* UPDATELL */
+#define WM8741_LAT_4_0_MASK 0x001F /* LAT[4:0] - [4:0] */
+#define WM8741_LAT_4_0_SHIFT 0 /* LAT[4:0] - [4:0] */
+#define WM8741_LAT_4_0_WIDTH 5 /* LAT[4:0] - [4:0] */
+
+/*
+ * R1 (0x01) - DACLMSB_ATTENUATION
+ */
+#define WM8741_UPDATELM 0x0020 /* UPDATELM */
+#define WM8741_UPDATELM_MASK 0x0020 /* UPDATELM */
+#define WM8741_UPDATELM_SHIFT 5 /* UPDATELM */
+#define WM8741_UPDATELM_WIDTH 1 /* UPDATELM */
+#define WM8741_LAT_9_5_0_MASK 0x001F /* LAT[9:5] - [4:0] */
+#define WM8741_LAT_9_5_0_SHIFT 0 /* LAT[9:5] - [4:0] */
+#define WM8741_LAT_9_5_0_WIDTH 5 /* LAT[9:5] - [4:0] */
+
+/*
+ * R2 (0x02) - DACRLSB_ATTENUATION
+ */
+#define WM8741_UPDATERL 0x0020 /* UPDATERL */
+#define WM8741_UPDATERL_MASK 0x0020 /* UPDATERL */
+#define WM8741_UPDATERL_SHIFT 5 /* UPDATERL */
+#define WM8741_UPDATERL_WIDTH 1 /* UPDATERL */
+#define WM8741_RAT_4_0_MASK 0x001F /* RAT[4:0] - [4:0] */
+#define WM8741_RAT_4_0_SHIFT 0 /* RAT[4:0] - [4:0] */
+#define WM8741_RAT_4_0_WIDTH 5 /* RAT[4:0] - [4:0] */
+
+/*
+ * R3 (0x03) - DACRMSB_ATTENUATION
+ */
+#define WM8741_UPDATERM 0x0020 /* UPDATERM */
+#define WM8741_UPDATERM_MASK 0x0020 /* UPDATERM */
+#define WM8741_UPDATERM_SHIFT 5 /* UPDATERM */
+#define WM8741_UPDATERM_WIDTH 1 /* UPDATERM */
+#define WM8741_RAT_9_5_0_MASK 0x001F /* RAT[9:5] - [4:0] */
+#define WM8741_RAT_9_5_0_SHIFT 0 /* RAT[9:5] - [4:0] */
+#define WM8741_RAT_9_5_0_WIDTH 5 /* RAT[9:5] - [4:0] */
+
+/*
+ * R4 (0x04) - VOLUME_CONTROL
+ */
+#define WM8741_AMUTE 0x0080 /* AMUTE */
+#define WM8741_AMUTE_MASK 0x0080 /* AMUTE */
+#define WM8741_AMUTE_SHIFT 7 /* AMUTE */
+#define WM8741_AMUTE_WIDTH 1 /* AMUTE */
+#define WM8741_ZFLAG_MASK 0x0060 /* ZFLAG - [6:5] */
+#define WM8741_ZFLAG_SHIFT 5 /* ZFLAG - [6:5] */
+#define WM8741_ZFLAG_WIDTH 2 /* ZFLAG - [6:5] */
+#define WM8741_IZD 0x0010 /* IZD */
+#define WM8741_IZD_MASK 0x0010 /* IZD */
+#define WM8741_IZD_SHIFT 4 /* IZD */
+#define WM8741_IZD_WIDTH 1 /* IZD */
+#define WM8741_SOFT 0x0008 /* SOFT MUTE */
+#define WM8741_SOFT_MASK 0x0008 /* SOFT MUTE */
+#define WM8741_SOFT_SHIFT 3 /* SOFT MUTE */
+#define WM8741_SOFT_WIDTH 1 /* SOFT MUTE */
+#define WM8741_ATC 0x0004 /* ATC */
+#define WM8741_ATC_MASK 0x0004 /* ATC */
+#define WM8741_ATC_SHIFT 2 /* ATC */
+#define WM8741_ATC_WIDTH 1 /* ATC */
+#define WM8741_ATT2DB 0x0002 /* ATT2DB */
+#define WM8741_ATT2DB_MASK 0x0002 /* ATT2DB */
+#define WM8741_ATT2DB_SHIFT 1 /* ATT2DB */
+#define WM8741_ATT2DB_WIDTH 1 /* ATT2DB */
+#define WM8741_VOL_RAMP 0x0001 /* VOL_RAMP */
+#define WM8741_VOL_RAMP_MASK 0x0001 /* VOL_RAMP */
+#define WM8741_VOL_RAMP_SHIFT 0 /* VOL_RAMP */
+#define WM8741_VOL_RAMP_WIDTH 1 /* VOL_RAMP */
+
+/*
+ * R5 (0x05) - FORMAT_CONTROL
+ */
+#define WM8741_PWDN 0x0080 /* PWDN */
+#define WM8741_PWDN_MASK 0x0080 /* PWDN */
+#define WM8741_PWDN_SHIFT 7 /* PWDN */
+#define WM8741_PWDN_WIDTH 1 /* PWDN */
+#define WM8741_REV 0x0040 /* REV */
+#define WM8741_REV_MASK 0x0040 /* REV */
+#define WM8741_REV_SHIFT 6 /* REV */
+#define WM8741_REV_WIDTH 1 /* REV */
+#define WM8741_BCP 0x0020 /* BCP */
+#define WM8741_BCP_MASK 0x0020 /* BCP */
+#define WM8741_BCP_SHIFT 5 /* BCP */
+#define WM8741_BCP_WIDTH 1 /* BCP */
+#define WM8741_LRP 0x0010 /* LRP */
+#define WM8741_LRP_MASK 0x0010 /* LRP */
+#define WM8741_LRP_SHIFT 4 /* LRP */
+#define WM8741_LRP_WIDTH 1 /* LRP */
+#define WM8741_FMT_MASK 0x000C /* FMT - [3:2] */
+#define WM8741_FMT_SHIFT 2 /* FMT - [3:2] */
+#define WM8741_FMT_WIDTH 2 /* FMT - [3:2] */
+#define WM8741_IWL_MASK 0x0003 /* IWL - [1:0] */
+#define WM8741_IWL_SHIFT 0 /* IWL - [1:0] */
+#define WM8741_IWL_WIDTH 2 /* IWL - [1:0] */
+
+/*
+ * R6 (0x06) - FILTER_CONTROL
+ */
+#define WM8741_ZFLAG_HI 0x0080 /* ZFLAG_HI */
+#define WM8741_ZFLAG_HI_MASK 0x0080 /* ZFLAG_HI */
+#define WM8741_ZFLAG_HI_SHIFT 7 /* ZFLAG_HI */
+#define WM8741_ZFLAG_HI_WIDTH 1 /* ZFLAG_HI */
+#define WM8741_DEEMPH_MASK 0x0060 /* DEEMPH - [6:5] */
+#define WM8741_DEEMPH_SHIFT 5 /* DEEMPH - [6:5] */
+#define WM8741_DEEMPH_WIDTH 2 /* DEEMPH - [6:5] */
+#define WM8741_DSDFILT_MASK 0x0018 /* DSDFILT - [4:3] */
+#define WM8741_DSDFILT_SHIFT 3 /* DSDFILT - [4:3] */
+#define WM8741_DSDFILT_WIDTH 2 /* DSDFILT - [4:3] */
+#define WM8741_FIRSEL_MASK 0x0007 /* FIRSEL - [2:0] */
+#define WM8741_FIRSEL_SHIFT 0 /* FIRSEL - [2:0] */
+#define WM8741_FIRSEL_WIDTH 3 /* FIRSEL - [2:0] */
+
+/*
+ * R7 (0x07) - MODE_CONTROL_1
+ */
+#define WM8741_MODE8X 0x0080 /* MODE8X */
+#define WM8741_MODE8X_MASK 0x0080 /* MODE8X */
+#define WM8741_MODE8X_SHIFT 7 /* MODE8X */
+#define WM8741_MODE8X_WIDTH 1 /* MODE8X */
+#define WM8741_OSR_MASK 0x0060 /* OSR - [6:5] */
+#define WM8741_OSR_SHIFT 5 /* OSR - [6:5] */
+#define WM8741_OSR_WIDTH 2 /* OSR - [6:5] */
+#define WM8741_SR_MASK 0x001C /* SR - [4:2] */
+#define WM8741_SR_SHIFT 2 /* SR - [4:2] */
+#define WM8741_SR_WIDTH 3 /* SR - [4:2] */
+#define WM8741_MODESEL_MASK 0x0003 /* MODESEL - [1:0] */
+#define WM8741_MODESEL_SHIFT 0 /* MODESEL - [1:0] */
+#define WM8741_MODESEL_WIDTH 2 /* MODESEL - [1:0] */
+
+/*
+ * R8 (0x08) - MODE_CONTROL_2
+ */
+#define WM8741_DSD_GAIN 0x0040 /* DSD_GAIN */
+#define WM8741_DSD_GAIN_MASK 0x0040 /* DSD_GAIN */
+#define WM8741_DSD_GAIN_SHIFT 6 /* DSD_GAIN */
+#define WM8741_DSD_GAIN_WIDTH 1 /* DSD_GAIN */
+#define WM8741_SDOUT 0x0020 /* SDOUT */
+#define WM8741_SDOUT_MASK 0x0020 /* SDOUT */
+#define WM8741_SDOUT_SHIFT 5 /* SDOUT */
+#define WM8741_SDOUT_WIDTH 1 /* SDOUT */
+#define WM8741_DOUT 0x0010 /* DOUT */
+#define WM8741_DOUT_MASK 0x0010 /* DOUT */
+#define WM8741_DOUT_SHIFT 4 /* DOUT */
+#define WM8741_DOUT_WIDTH 1 /* DOUT */
+#define WM8741_DIFF_MASK 0x000C /* DIFF - [3:2] */
+#define WM8741_DIFF_SHIFT 2 /* DIFF - [3:2] */
+#define WM8741_DIFF_WIDTH 2 /* DIFF - [3:2] */
+#define WM8741_DITHER_MASK 0x0003 /* DITHER - [1:0] */
+#define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */
+#define WM8741_DITHER_WIDTH 2 /* DITHER - [1:0] */
+
+/*
+ * R32 (0x20) - ADDITONAL_CONTROL_1
+ */
+#define WM8741_DSD_LEVEL 0x0002 /* DSD_LEVEL */
+#define WM8741_DSD_LEVEL_MASK 0x0002 /* DSD_LEVEL */
+#define WM8741_DSD_LEVEL_SHIFT 1 /* DSD_LEVEL */
+#define WM8741_DSD_LEVEL_WIDTH 1 /* DSD_LEVEL */
+#define WM8741_DSD_NO_NOTCH 0x0001 /* DSD_NO_NOTCH */
+#define WM8741_DSD_NO_NOTCH_MASK 0x0001 /* DSD_NO_NOTCH */
+#define WM8741_DSD_NO_NOTCH_SHIFT 0 /* DSD_NO_NOTCH */
+#define WM8741_DSD_NO_NOTCH_WIDTH 1 /* DSD_NO_NOTCH */
+
+#define WM8741_SYSCLK 0
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8750.c b/kernel/sound/soc/codecs/wm8750.c
new file mode 100644
index 000000000..eb0a1644b
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8750.c
@@ -0,0 +1,873 @@
+/*
+ * wm8750.c -- WM8750 ALSA SoC audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.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>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#include "wm8750.h"
+
+/*
+ * wm8750 register cache
+ * We can't read the WM8750 register space when we
+ * are using 2 wire for device control, so we cache them instead.
+ */
+static const struct reg_default wm8750_reg_defaults[] = {
+ { 0, 0x0097 },
+ { 1, 0x0097 },
+ { 2, 0x0079 },
+ { 3, 0x0079 },
+ { 4, 0x0000 },
+ { 5, 0x0008 },
+ { 6, 0x0000 },
+ { 7, 0x000a },
+ { 8, 0x0000 },
+ { 9, 0x0000 },
+ { 10, 0x00ff },
+ { 11, 0x00ff },
+ { 12, 0x000f },
+ { 13, 0x000f },
+ { 14, 0x0000 },
+ { 15, 0x0000 },
+ { 16, 0x0000 },
+ { 17, 0x007b },
+ { 18, 0x0000 },
+ { 19, 0x0032 },
+ { 20, 0x0000 },
+ { 21, 0x00c3 },
+ { 22, 0x00c3 },
+ { 23, 0x00c0 },
+ { 24, 0x0000 },
+ { 25, 0x0000 },
+ { 26, 0x0000 },
+ { 27, 0x0000 },
+ { 28, 0x0000 },
+ { 29, 0x0000 },
+ { 30, 0x0000 },
+ { 31, 0x0000 },
+ { 32, 0x0000 },
+ { 33, 0x0000 },
+ { 34, 0x0050 },
+ { 35, 0x0050 },
+ { 36, 0x0050 },
+ { 37, 0x0050 },
+ { 38, 0x0050 },
+ { 39, 0x0050 },
+ { 40, 0x0079 },
+ { 41, 0x0079 },
+ { 42, 0x0079 },
+};
+
+/* codec private data */
+struct wm8750_priv {
+ unsigned int sysclk;
+};
+
+#define wm8750_reset(c) snd_soc_write(c, WM8750_RESET, 0)
+
+/*
+ * WM8750 Controls
+ */
+static const char *wm8750_bass[] = {"Linear Control", "Adaptive Boost"};
+static const char *wm8750_bass_filter[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" };
+static const char *wm8750_treble[] = {"8kHz", "4kHz"};
+static const char *wm8750_3d_lc[] = {"200Hz", "500Hz"};
+static const char *wm8750_3d_uc[] = {"2.2kHz", "1.5kHz"};
+static const char *wm8750_3d_func[] = {"Capture", "Playback"};
+static const char *wm8750_alc_func[] = {"Off", "Right", "Left", "Stereo"};
+static const char *wm8750_ng_type[] = {"Constant PGA Gain",
+ "Mute ADC Output"};
+static const char *wm8750_line_mux[] = {"Line 1", "Line 2", "Line 3", "PGA",
+ "Differential"};
+static const char *wm8750_pga_sel[] = {"Line 1", "Line 2", "Line 3",
+ "Differential"};
+static const char *wm8750_out3[] = {"VREF", "ROUT1 + Vol", "MonoOut",
+ "ROUT1"};
+static const char *wm8750_diff_sel[] = {"Line 1", "Line 2"};
+static const char *wm8750_adcpol[] = {"Normal", "L Invert", "R Invert",
+ "L + R Invert"};
+static const char *wm8750_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static const char *wm8750_mono_mux[] = {"Stereo", "Mono (Left)",
+ "Mono (Right)", "Digital Mono"};
+
+static const struct soc_enum wm8750_enum[] = {
+SOC_ENUM_SINGLE(WM8750_BASS, 7, 2, wm8750_bass),
+SOC_ENUM_SINGLE(WM8750_BASS, 6, 2, wm8750_bass_filter),
+SOC_ENUM_SINGLE(WM8750_TREBLE, 6, 2, wm8750_treble),
+SOC_ENUM_SINGLE(WM8750_3D, 5, 2, wm8750_3d_lc),
+SOC_ENUM_SINGLE(WM8750_3D, 6, 2, wm8750_3d_uc),
+SOC_ENUM_SINGLE(WM8750_3D, 7, 2, wm8750_3d_func),
+SOC_ENUM_SINGLE(WM8750_ALC1, 7, 4, wm8750_alc_func),
+SOC_ENUM_SINGLE(WM8750_NGATE, 1, 2, wm8750_ng_type),
+SOC_ENUM_SINGLE(WM8750_LOUTM1, 0, 5, wm8750_line_mux),
+SOC_ENUM_SINGLE(WM8750_ROUTM1, 0, 5, wm8750_line_mux),
+SOC_ENUM_SINGLE(WM8750_LADCIN, 6, 4, wm8750_pga_sel), /* 10 */
+SOC_ENUM_SINGLE(WM8750_RADCIN, 6, 4, wm8750_pga_sel),
+SOC_ENUM_SINGLE(WM8750_ADCTL2, 7, 4, wm8750_out3),
+SOC_ENUM_SINGLE(WM8750_ADCIN, 8, 2, wm8750_diff_sel),
+SOC_ENUM_SINGLE(WM8750_ADCDAC, 5, 4, wm8750_adcpol),
+SOC_ENUM_SINGLE(WM8750_ADCDAC, 1, 4, wm8750_deemph),
+SOC_ENUM_SINGLE(WM8750_ADCIN, 6, 4, wm8750_mono_mux), /* 16 */
+
+};
+
+static const struct snd_kcontrol_new wm8750_snd_controls[] = {
+
+SOC_DOUBLE_R("Capture Volume", WM8750_LINVOL, WM8750_RINVOL, 0, 63, 0),
+SOC_DOUBLE_R("Capture ZC Switch", WM8750_LINVOL, WM8750_RINVOL, 6, 1, 0),
+SOC_DOUBLE_R("Capture Switch", WM8750_LINVOL, WM8750_RINVOL, 7, 1, 1),
+
+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8750_LOUT1V,
+ WM8750_ROUT1V, 7, 1, 0),
+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8750_LOUT2V,
+ WM8750_ROUT2V, 7, 1, 0),
+
+SOC_ENUM("Playback De-emphasis", wm8750_enum[15]),
+
+SOC_ENUM("Capture Polarity", wm8750_enum[14]),
+SOC_SINGLE("Playback 6dB Attenuate", WM8750_ADCDAC, 7, 1, 0),
+SOC_SINGLE("Capture 6dB Attenuate", WM8750_ADCDAC, 8, 1, 0),
+
+SOC_DOUBLE_R("PCM Volume", WM8750_LDAC, WM8750_RDAC, 0, 255, 0),
+
+SOC_ENUM("Bass Boost", wm8750_enum[0]),
+SOC_ENUM("Bass Filter", wm8750_enum[1]),
+SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1),
+
+SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 1),
+SOC_ENUM("Treble Cut-off", wm8750_enum[2]),
+
+SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0),
+SOC_SINGLE("3D Volume", WM8750_3D, 1, 15, 0),
+SOC_ENUM("3D Lower Cut-off", wm8750_enum[3]),
+SOC_ENUM("3D Upper Cut-off", wm8750_enum[4]),
+SOC_ENUM("3D Mode", wm8750_enum[5]),
+
+SOC_SINGLE("ALC Capture Target Volume", WM8750_ALC1, 0, 7, 0),
+SOC_SINGLE("ALC Capture Max Volume", WM8750_ALC1, 4, 7, 0),
+SOC_ENUM("ALC Capture Function", wm8750_enum[6]),
+SOC_SINGLE("ALC Capture ZC Switch", WM8750_ALC2, 7, 1, 0),
+SOC_SINGLE("ALC Capture Hold Time", WM8750_ALC2, 0, 15, 0),
+SOC_SINGLE("ALC Capture Decay Time", WM8750_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Capture Attack Time", WM8750_ALC3, 0, 15, 0),
+SOC_SINGLE("ALC Capture NG Threshold", WM8750_NGATE, 3, 31, 0),
+SOC_ENUM("ALC Capture NG Type", wm8750_enum[4]),
+SOC_SINGLE("ALC Capture NG Switch", WM8750_NGATE, 0, 1, 0),
+
+SOC_SINGLE("Left ADC Capture Volume", WM8750_LADC, 0, 255, 0),
+SOC_SINGLE("Right ADC Capture Volume", WM8750_RADC, 0, 255, 0),
+
+SOC_SINGLE("ZC Timeout Switch", WM8750_ADCTL1, 0, 1, 0),
+SOC_SINGLE("Playback Invert Switch", WM8750_ADCTL1, 1, 1, 0),
+
+SOC_SINGLE("Right Speaker Playback Invert Switch", WM8750_ADCTL2, 4, 1, 0),
+
+/* Unimplemented */
+/* ADCDAC Bit 0 - ADCHPD */
+/* ADCDAC Bit 4 - HPOR */
+/* ADCTL1 Bit 2,3 - DATSEL */
+/* ADCTL1 Bit 4,5 - DMONOMIX */
+/* ADCTL1 Bit 6,7 - VSEL */
+/* ADCTL2 Bit 2 - LRCM */
+/* ADCTL2 Bit 3 - TRI */
+/* ADCTL3 Bit 5 - HPFLREN */
+/* ADCTL3 Bit 6 - VROI */
+/* ADCTL3 Bit 7,8 - ADCLRM */
+/* ADCIN Bit 4 - LDCM */
+/* ADCIN Bit 5 - RDCM */
+
+SOC_DOUBLE_R("Mic Boost", WM8750_LADCIN, WM8750_RADCIN, 4, 3, 0),
+
+SOC_DOUBLE_R("Bypass Left Playback Volume", WM8750_LOUTM1,
+ WM8750_LOUTM2, 4, 7, 1),
+SOC_DOUBLE_R("Bypass Right Playback Volume", WM8750_ROUTM1,
+ WM8750_ROUTM2, 4, 7, 1),
+SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8750_MOUTM1,
+ WM8750_MOUTM2, 4, 7, 1),
+
+SOC_SINGLE("Mono Playback ZC Switch", WM8750_MOUTV, 7, 1, 0),
+
+SOC_DOUBLE_R("Headphone Playback Volume", WM8750_LOUT1V, WM8750_ROUT1V,
+ 0, 127, 0),
+SOC_DOUBLE_R("Speaker Playback Volume", WM8750_LOUT2V, WM8750_ROUT2V,
+ 0, 127, 0),
+
+SOC_SINGLE("Mono Playback Volume", WM8750_MOUTV, 0, 127, 0),
+
+};
+
+/*
+ * DAPM Controls
+ */
+
+/* Left Mixer */
+static const struct snd_kcontrol_new wm8750_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("Playback Switch", WM8750_LOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_LOUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8750_LOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_LOUTM2, 7, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new wm8750_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8750_ROUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_ROUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Playback Switch", WM8750_ROUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_ROUTM2, 7, 1, 0),
+};
+
+/* Mono Mixer */
+static const struct snd_kcontrol_new wm8750_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8750_MOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_MOUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8750_MOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_MOUTM2, 7, 1, 0),
+};
+
+/* Left Line Mux */
+static const struct snd_kcontrol_new wm8750_left_line_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[8]);
+
+/* Right Line Mux */
+static const struct snd_kcontrol_new wm8750_right_line_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[9]);
+
+/* Left PGA Mux */
+static const struct snd_kcontrol_new wm8750_left_pga_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[10]);
+
+/* Right PGA Mux */
+static const struct snd_kcontrol_new wm8750_right_pga_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[11]);
+
+/* Out 3 Mux */
+static const struct snd_kcontrol_new wm8750_out3_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[12]);
+
+/* Differential Mux */
+static const struct snd_kcontrol_new wm8750_diffmux_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[13]);
+
+/* Mono ADC Mux */
+static const struct snd_kcontrol_new wm8750_monomux_controls =
+SOC_DAPM_ENUM("Route", wm8750_enum[16]);
+
+static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ &wm8750_left_mixer_controls[0],
+ ARRAY_SIZE(wm8750_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ &wm8750_right_mixer_controls[0],
+ ARRAY_SIZE(wm8750_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono Mixer", WM8750_PWR2, 2, 0,
+ &wm8750_mono_mixer_controls[0],
+ ARRAY_SIZE(wm8750_mono_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("Right Out 2", WM8750_PWR2, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", WM8750_PWR2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", WM8750_PWR2, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", WM8750_PWR2, 6, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8750_PWR2, 7, 0),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8750_PWR2, 8, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8750_PWR1, 1, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8750_PWR1, 2, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8750_PWR1, 3, 0),
+
+ SND_SOC_DAPM_MUX("Left PGA Mux", WM8750_PWR1, 5, 0,
+ &wm8750_left_pga_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", WM8750_PWR1, 4, 0,
+ &wm8750_right_pga_controls),
+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
+ &wm8750_left_line_controls),
+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
+ &wm8750_right_line_controls),
+
+ SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8750_out3_controls),
+ SND_SOC_DAPM_PGA("Out 3", WM8750_PWR2, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out 1", WM8750_PWR2, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+ &wm8750_diffmux_controls),
+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+ &wm8750_monomux_controls),
+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+ &wm8750_monomux_controls),
+
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("MONO1"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_VMID("VREF"),
+
+ SND_SOC_DAPM_INPUT("LINPUT1"),
+ SND_SOC_DAPM_INPUT("LINPUT2"),
+ SND_SOC_DAPM_INPUT("LINPUT3"),
+ SND_SOC_DAPM_INPUT("RINPUT1"),
+ SND_SOC_DAPM_INPUT("RINPUT2"),
+ SND_SOC_DAPM_INPUT("RINPUT3"),
+};
+
+static const struct snd_soc_dapm_route wm8750_dapm_routes[] = {
+ /* left mixer */
+ {"Left Mixer", "Playback Switch", "Left DAC"},
+ {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Left Mixer", "Right Playback Switch", "Right DAC"},
+ {"Left Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* right mixer */
+ {"Right Mixer", "Left Playback Switch", "Left DAC"},
+ {"Right Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Right Mixer", "Playback Switch", "Right DAC"},
+ {"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* left out 1 */
+ {"Left Out 1", NULL, "Left Mixer"},
+ {"LOUT1", NULL, "Left Out 1"},
+
+ /* left out 2 */
+ {"Left Out 2", NULL, "Left Mixer"},
+ {"LOUT2", NULL, "Left Out 2"},
+
+ /* right out 1 */
+ {"Right Out 1", NULL, "Right Mixer"},
+ {"ROUT1", NULL, "Right Out 1"},
+
+ /* right out 2 */
+ {"Right Out 2", NULL, "Right Mixer"},
+ {"ROUT2", NULL, "Right Out 2"},
+
+ /* mono mixer */
+ {"Mono Mixer", "Left Playback Switch", "Left DAC"},
+ {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Mono Mixer", "Right Playback Switch", "Right DAC"},
+ {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* mono out */
+ {"Mono Out 1", NULL, "Mono Mixer"},
+ {"MONO1", NULL, "Mono Out 1"},
+
+ /* out 3 */
+ {"Out3 Mux", "VREF", "VREF"},
+ {"Out3 Mux", "ROUT1 + Vol", "ROUT1"},
+ {"Out3 Mux", "ROUT1", "Right Mixer"},
+ {"Out3 Mux", "MonoOut", "MONO1"},
+ {"Out 3", NULL, "Out3 Mux"},
+ {"OUT3", NULL, "Out 3"},
+
+ /* Left Line Mux */
+ {"Left Line Mux", "Line 1", "LINPUT1"},
+ {"Left Line Mux", "Line 2", "LINPUT2"},
+ {"Left Line Mux", "Line 3", "LINPUT3"},
+ {"Left Line Mux", "PGA", "Left PGA Mux"},
+ {"Left Line Mux", "Differential", "Differential Mux"},
+
+ /* Right Line Mux */
+ {"Right Line Mux", "Line 1", "RINPUT1"},
+ {"Right Line Mux", "Line 2", "RINPUT2"},
+ {"Right Line Mux", "Line 3", "RINPUT3"},
+ {"Right Line Mux", "PGA", "Right PGA Mux"},
+ {"Right Line Mux", "Differential", "Differential Mux"},
+
+ /* Left PGA Mux */
+ {"Left PGA Mux", "Line 1", "LINPUT1"},
+ {"Left PGA Mux", "Line 2", "LINPUT2"},
+ {"Left PGA Mux", "Line 3", "LINPUT3"},
+ {"Left PGA Mux", "Differential", "Differential Mux"},
+
+ /* Right PGA Mux */
+ {"Right PGA Mux", "Line 1", "RINPUT1"},
+ {"Right PGA Mux", "Line 2", "RINPUT2"},
+ {"Right PGA Mux", "Line 3", "RINPUT3"},
+ {"Right PGA Mux", "Differential", "Differential Mux"},
+
+ /* Differential Mux */
+ {"Differential Mux", "Line 1", "LINPUT1"},
+ {"Differential Mux", "Line 1", "RINPUT1"},
+ {"Differential Mux", "Line 2", "LINPUT2"},
+ {"Differential Mux", "Line 2", "RINPUT2"},
+
+ /* Left ADC Mux */
+ {"Left ADC Mux", "Stereo", "Left PGA Mux"},
+ {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
+ {"Left ADC Mux", "Digital Mono", "Left PGA Mux"},
+
+ /* Right ADC Mux */
+ {"Right ADC Mux", "Stereo", "Right PGA Mux"},
+ {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
+ {"Right ADC Mux", "Digital Mono", "Right PGA Mux"},
+
+ /* ADC */
+ {"Left ADC", NULL, "Left ADC Mux"},
+ {"Right ADC", NULL, "Right ADC Mux"},
+};
+
+struct _coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr:5;
+ u8 usb:1;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ /* 8k */
+ {12288000, 8000, 1536, 0x6, 0x0},
+ {11289600, 8000, 1408, 0x16, 0x0},
+ {18432000, 8000, 2304, 0x7, 0x0},
+ {16934400, 8000, 2112, 0x17, 0x0},
+ {12000000, 8000, 1500, 0x6, 0x1},
+
+ /* 11.025k */
+ {11289600, 11025, 1024, 0x18, 0x0},
+ {16934400, 11025, 1536, 0x19, 0x0},
+ {12000000, 11025, 1088, 0x19, 0x1},
+
+ /* 16k */
+ {12288000, 16000, 768, 0xa, 0x0},
+ {18432000, 16000, 1152, 0xb, 0x0},
+ {12000000, 16000, 750, 0xa, 0x1},
+
+ /* 22.05k */
+ {11289600, 22050, 512, 0x1a, 0x0},
+ {16934400, 22050, 768, 0x1b, 0x0},
+ {12000000, 22050, 544, 0x1b, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 384, 0xc, 0x0},
+ {18432000, 32000, 576, 0xd, 0x0},
+ {12000000, 32000, 375, 0xa, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x10, 0x0},
+ {16934400, 44100, 384, 0x11, 0x0},
+ {12000000, 44100, 272, 0x11, 0x1},
+
+ /* 48k */
+ {12288000, 48000, 256, 0x0, 0x0},
+ {18432000, 48000, 384, 0x1, 0x0},
+ {12000000, 48000, 250, 0x0, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0x1e, 0x0},
+ {16934400, 88200, 192, 0x1f, 0x0},
+ {12000000, 88200, 136, 0x1f, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 128, 0xe, 0x0},
+ {18432000, 96000, 192, 0xf, 0x0},
+ {12000000, 96000, 125, 0xe, 0x1},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+
+ printk(KERN_ERR "wm8750: could not get coeff for mclk %d @ rate %d\n",
+ mclk, rate);
+ return -EINVAL;
+}
+
+static int wm8750_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8750_priv *wm8750 = snd_soc_codec_get_drvdata(codec);
+
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ wm8750->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface = 0x0040;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8750_IFACE, iface);
+ return 0;
+}
+
+static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8750_priv *wm8750 = snd_soc_codec_get_drvdata(codec);
+ u16 iface = snd_soc_read(codec, WM8750_IFACE) & 0x1f3;
+ u16 srate = snd_soc_read(codec, WM8750_SRATE) & 0x1c0;
+ int coeff = get_coeff(wm8750->sysclk, params_rate(params));
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface |= 0x0004;
+ break;
+ case 24:
+ iface |= 0x0008;
+ break;
+ case 32:
+ iface |= 0x000c;
+ break;
+ }
+
+ /* set iface & srate */
+ snd_soc_write(codec, WM8750_IFACE, iface);
+ if (coeff >= 0)
+ snd_soc_write(codec, WM8750_SRATE, srate |
+ (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
+
+ return 0;
+}
+
+static int wm8750_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8750_ADCDAC) & 0xfff7;
+
+ if (mute)
+ snd_soc_write(codec, WM8750_ADCDAC, mute_reg | 0x8);
+ else
+ snd_soc_write(codec, WM8750_ADCDAC, mute_reg);
+ return 0;
+}
+
+static int wm8750_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 pwr_reg = snd_soc_read(codec, WM8750_PWR1) & 0xfe3e;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* set vmid to 50k and unmute dac */
+ snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ snd_soc_cache_sync(codec);
+
+ /* Set VMID to 5k */
+ snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
+
+ /* ...and ramp */
+ msleep(1000);
+ }
+
+ /* mute dac and set vmid to 500k, enable VREF */
+ snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, WM8750_PWR1, 0x0001);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8750_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8750_dai_ops = {
+ .hw_params = wm8750_pcm_hw_params,
+ .digital_mute = wm8750_mute,
+ .set_fmt = wm8750_set_dai_fmt,
+ .set_sysclk = wm8750_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver wm8750_dai = {
+ .name = "wm8750-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8750_RATES,
+ .formats = WM8750_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8750_RATES,
+ .formats = WM8750_FORMATS,},
+ .ops = &wm8750_dai_ops,
+};
+
+static int wm8750_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ ret = wm8750_reset(codec);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8750: failed to reset: %d\n", ret);
+ return ret;
+ }
+
+ /* set the update bits */
+ snd_soc_update_bits(codec, WM8750_LDAC, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8750_RDAC, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8750_LOUT1V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8750_ROUT1V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8750_LOUT2V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8750_ROUT2V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8750_LINVOL, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8750_RINVOL, 0x0100, 0x0100);
+
+ return ret;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8750 = {
+ .probe = wm8750_probe,
+ .set_bias_level = wm8750_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8750_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8750_snd_controls),
+ .dapm_widgets = wm8750_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
+ .dapm_routes = wm8750_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8750_dapm_routes),
+};
+
+static const struct of_device_id wm8750_of_match[] = {
+ { .compatible = "wlf,wm8750", },
+ { .compatible = "wlf,wm8987", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8750_of_match);
+
+static const struct regmap_config wm8750_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8750_MOUTV,
+
+ .reg_defaults = wm8750_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8750_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8750_spi_probe(struct spi_device *spi)
+{
+ struct wm8750_priv *wm8750;
+ struct regmap *regmap;
+ int ret;
+
+ wm8750 = devm_kzalloc(&spi->dev, sizeof(struct wm8750_priv),
+ GFP_KERNEL);
+ if (wm8750 == NULL)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_spi(spi, &wm8750_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ spi_set_drvdata(spi, wm8750);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8750, &wm8750_dai, 1);
+ return ret;
+}
+
+static int wm8750_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static const struct spi_device_id wm8750_spi_ids[] = {
+ { "wm8750", 0 },
+ { "wm8987", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, wm8750_spi_ids);
+
+static struct spi_driver wm8750_spi_driver = {
+ .driver = {
+ .name = "wm8750",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8750_of_match,
+ },
+ .id_table = wm8750_spi_ids,
+ .probe = wm8750_spi_probe,
+ .remove = wm8750_spi_remove,
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8750_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8750_priv *wm8750;
+ struct regmap *regmap;
+ int ret;
+
+ wm8750 = devm_kzalloc(&i2c->dev, sizeof(struct wm8750_priv),
+ GFP_KERNEL);
+ if (wm8750 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm8750);
+
+ regmap = devm_regmap_init_i2c(i2c, &wm8750_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8750, &wm8750_dai, 1);
+ return ret;
+}
+
+static int wm8750_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8750_i2c_id[] = {
+ { "wm8750", 0 },
+ { "wm8987", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id);
+
+static struct i2c_driver wm8750_i2c_driver = {
+ .driver = {
+ .name = "wm8750",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8750_of_match,
+ },
+ .probe = wm8750_i2c_probe,
+ .remove = wm8750_i2c_remove,
+ .id_table = wm8750_i2c_id,
+};
+#endif
+
+static int __init wm8750_modinit(void)
+{
+ int ret = 0;
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8750_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8750 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8750_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8750 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(wm8750_modinit);
+
+static void __exit wm8750_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8750_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8750_spi_driver);
+#endif
+}
+module_exit(wm8750_exit);
+
+MODULE_DESCRIPTION("ASoC WM8750 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8750.h b/kernel/sound/soc/codecs/wm8750.h
new file mode 100644
index 000000000..121427c04
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8750.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * 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 _WM8750_H
+#define _WM8750_H
+
+/* WM8750 register space */
+
+#define WM8750_LINVOL 0x00
+#define WM8750_RINVOL 0x01
+#define WM8750_LOUT1V 0x02
+#define WM8750_ROUT1V 0x03
+#define WM8750_ADCDAC 0x05
+#define WM8750_IFACE 0x07
+#define WM8750_SRATE 0x08
+#define WM8750_LDAC 0x0a
+#define WM8750_RDAC 0x0b
+#define WM8750_BASS 0x0c
+#define WM8750_TREBLE 0x0d
+#define WM8750_RESET 0x0f
+#define WM8750_3D 0x10
+#define WM8750_ALC1 0x11
+#define WM8750_ALC2 0x12
+#define WM8750_ALC3 0x13
+#define WM8750_NGATE 0x14
+#define WM8750_LADC 0x15
+#define WM8750_RADC 0x16
+#define WM8750_ADCTL1 0x17
+#define WM8750_ADCTL2 0x18
+#define WM8750_PWR1 0x19
+#define WM8750_PWR2 0x1a
+#define WM8750_ADCTL3 0x1b
+#define WM8750_ADCIN 0x1f
+#define WM8750_LADCIN 0x20
+#define WM8750_RADCIN 0x21
+#define WM8750_LOUTM1 0x22
+#define WM8750_LOUTM2 0x23
+#define WM8750_ROUTM1 0x24
+#define WM8750_ROUTM2 0x25
+#define WM8750_MOUTM1 0x26
+#define WM8750_MOUTM2 0x27
+#define WM8750_LOUT2V 0x28
+#define WM8750_ROUT2V 0x29
+#define WM8750_MOUTV 0x2a
+
+#define WM8750_CACHE_REGNUM 0x2a
+
+#define WM8750_SYSCLK 0
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8753.c b/kernel/sound/soc/codecs/wm8753.c
new file mode 100644
index 000000000..c50a59593
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8753.c
@@ -0,0 +1,1656 @@
+/*
+ * wm8753.c -- WM8753 ALSA Soc Audio driver
+ *
+ * Copyright 2003-11 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.
+ *
+ * Notes:
+ * The WM8753 is a low power, high quality stereo codec with integrated PCM
+ * codec designed for portable digital telephony applications.
+ *
+ * Dual DAI:-
+ *
+ * This driver support 2 DAI PCM's. This makes the default PCM available for
+ * HiFi audio (e.g. MP3, ogg) playback/capture and the other PCM available for
+ * voice.
+ *
+ * Please note that the voice PCM can be connected directly to a Bluetooth
+ * codec or GSM modem and thus cannot be read or written to, although it is
+ * available to be configured with snd_hw_params(), etc and kcontrols in the
+ * normal alsa manner.
+ *
+ * Fast DAI switching:-
+ *
+ * The driver can now fast switch between the DAI configurations via a
+ * an alsa kcontrol. This allows the PCM to remain open.
+ *
+ */
+
+#include <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/of_device.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.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 "wm8753.h"
+
+static int caps_charge = 2000;
+module_param(caps_charge, int, 0);
+MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
+
+static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec,
+ unsigned int fmt);
+static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec,
+ unsigned int fmt);
+
+/*
+ * wm8753 register cache
+ * We can't read the WM8753 register space when we
+ * are using 2 wire for device control, so we cache them instead.
+ */
+static const struct reg_default wm8753_reg_defaults[] = {
+ { 0x00, 0x0000 },
+ { 0x01, 0x0008 },
+ { 0x02, 0x0000 },
+ { 0x03, 0x000a },
+ { 0x04, 0x000a },
+ { 0x05, 0x0033 },
+ { 0x06, 0x0000 },
+ { 0x07, 0x0007 },
+ { 0x08, 0x00ff },
+ { 0x09, 0x00ff },
+ { 0x0a, 0x000f },
+ { 0x0b, 0x000f },
+ { 0x0c, 0x007b },
+ { 0x0d, 0x0000 },
+ { 0x0e, 0x0032 },
+ { 0x0f, 0x0000 },
+ { 0x10, 0x00c3 },
+ { 0x11, 0x00c3 },
+ { 0x12, 0x00c0 },
+ { 0x13, 0x0000 },
+ { 0x14, 0x0000 },
+ { 0x15, 0x0000 },
+ { 0x16, 0x0000 },
+ { 0x17, 0x0000 },
+ { 0x18, 0x0000 },
+ { 0x19, 0x0000 },
+ { 0x1a, 0x0000 },
+ { 0x1b, 0x0000 },
+ { 0x1c, 0x0000 },
+ { 0x1d, 0x0000 },
+ { 0x1e, 0x0000 },
+ { 0x1f, 0x0000 },
+ { 0x20, 0x0055 },
+ { 0x21, 0x0005 },
+ { 0x22, 0x0050 },
+ { 0x23, 0x0055 },
+ { 0x24, 0x0050 },
+ { 0x25, 0x0055 },
+ { 0x26, 0x0050 },
+ { 0x27, 0x0055 },
+ { 0x28, 0x0079 },
+ { 0x29, 0x0079 },
+ { 0x2a, 0x0079 },
+ { 0x2b, 0x0079 },
+ { 0x2c, 0x0079 },
+ { 0x2d, 0x0000 },
+ { 0x2e, 0x0000 },
+ { 0x2f, 0x0000 },
+ { 0x30, 0x0000 },
+ { 0x31, 0x0097 },
+ { 0x32, 0x0097 },
+ { 0x33, 0x0000 },
+ { 0x34, 0x0004 },
+ { 0x35, 0x0000 },
+ { 0x36, 0x0083 },
+ { 0x37, 0x0024 },
+ { 0x38, 0x01ba },
+ { 0x39, 0x0000 },
+ { 0x3a, 0x0083 },
+ { 0x3b, 0x0024 },
+ { 0x3c, 0x01ba },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x0000 },
+ { 0x3f, 0x0000 },
+};
+
+static bool wm8753_volatile(struct device *dev, unsigned int reg)
+{
+ return reg == WM8753_RESET;
+}
+
+static bool wm8753_writeable(struct device *dev, unsigned int reg)
+{
+ return reg <= WM8753_ADCTL2;
+}
+
+/* codec private data */
+struct wm8753_priv {
+ struct regmap *regmap;
+ unsigned int sysclk;
+ unsigned int pcmclk;
+
+ unsigned int voice_fmt;
+ unsigned int hifi_fmt;
+
+ int dai_func;
+ struct delayed_work charge_work;
+};
+
+#define wm8753_reset(c) snd_soc_write(c, WM8753_RESET, 0)
+
+/*
+ * WM8753 Controls
+ */
+static const char *wm8753_base[] = {"Linear Control", "Adaptive Boost"};
+static const char *wm8753_base_filter[] =
+ {"130Hz @ 48kHz", "200Hz @ 48kHz", "100Hz @ 16kHz", "400Hz @ 48kHz",
+ "100Hz @ 8kHz", "200Hz @ 8kHz"};
+static const char *wm8753_treble[] = {"8kHz", "4kHz"};
+static const char *wm8753_alc_func[] = {"Off", "Right", "Left", "Stereo"};
+static const char *wm8753_ng_type[] = {"Constant PGA Gain", "Mute ADC Output"};
+static const char *wm8753_3d_func[] = {"Capture", "Playback"};
+static const char *wm8753_3d_uc[] = {"2.2kHz", "1.5kHz"};
+static const char *wm8753_3d_lc[] = {"200Hz", "500Hz"};
+static const char *wm8753_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz"};
+static const char *wm8753_mono_mix[] = {"Stereo", "Left", "Right", "Mono"};
+static const char *wm8753_dac_phase[] = {"Non Inverted", "Inverted"};
+static const char *wm8753_line_mix[] = {"Line 1 + 2", "Line 1 - 2",
+ "Line 1", "Line 2"};
+static const char *wm8753_mono_mux[] = {"Line Mix", "Rx Mix"};
+static const char *wm8753_right_mux[] = {"Line 2", "Rx Mix"};
+static const char *wm8753_left_mux[] = {"Line 1", "Rx Mix"};
+static const char *wm8753_rxmsel[] = {"RXP - RXN", "RXP + RXN", "RXP", "RXN"};
+static const char *wm8753_sidetone_mux[] = {"Left PGA", "Mic 1", "Mic 2",
+ "Right PGA"};
+static const char *wm8753_mono2_src[] = {"Inverted Mono 1", "Left", "Right",
+ "Left + Right"};
+static const char *wm8753_out3[] = {"VREF", "ROUT2", "Left + Right"};
+static const char *wm8753_out4[] = {"VREF", "Capture ST", "LOUT2"};
+static const char *wm8753_radcsel[] = {"PGA", "Line or RXP-RXN", "Sidetone"};
+static const char *wm8753_ladcsel[] = {"PGA", "Line or RXP-RXN", "Line"};
+static const char *wm8753_mono_adc[] = {"Stereo", "Analogue Mix Left",
+ "Analogue Mix Right", "Digital Mono Mix"};
+static const char *wm8753_adc_hp[] = {"3.4Hz @ 48kHz", "82Hz @ 16k",
+ "82Hz @ 8kHz", "170Hz @ 8kHz"};
+static const char *wm8753_adc_filter[] = {"HiFi", "Voice"};
+static const char *wm8753_mic_sel[] = {"Mic 1", "Mic 2", "Mic 3"};
+static const char *wm8753_dai_mode[] = {"DAI 0", "DAI 1", "DAI 2", "DAI 3"};
+static const char *wm8753_dat_sel[] = {"Stereo", "Left ADC", "Right ADC",
+ "Channel Swap"};
+static const char *wm8753_rout2_phase[] = {"Non Inverted", "Inverted"};
+
+static const struct soc_enum wm8753_enum[] = {
+SOC_ENUM_SINGLE(WM8753_BASS, 7, 2, wm8753_base),
+SOC_ENUM_SINGLE(WM8753_BASS, 4, 6, wm8753_base_filter),
+SOC_ENUM_SINGLE(WM8753_TREBLE, 6, 2, wm8753_treble),
+SOC_ENUM_SINGLE(WM8753_ALC1, 7, 4, wm8753_alc_func),
+SOC_ENUM_SINGLE(WM8753_NGATE, 1, 2, wm8753_ng_type),
+SOC_ENUM_SINGLE(WM8753_3D, 7, 2, wm8753_3d_func),
+SOC_ENUM_SINGLE(WM8753_3D, 6, 2, wm8753_3d_uc),
+SOC_ENUM_SINGLE(WM8753_3D, 5, 2, wm8753_3d_lc),
+SOC_ENUM_SINGLE(WM8753_DAC, 1, 4, wm8753_deemp),
+SOC_ENUM_SINGLE(WM8753_DAC, 4, 4, wm8753_mono_mix),
+SOC_ENUM_SINGLE(WM8753_DAC, 6, 2, wm8753_dac_phase),
+SOC_ENUM_SINGLE(WM8753_INCTL1, 3, 4, wm8753_line_mix),
+SOC_ENUM_SINGLE(WM8753_INCTL1, 2, 2, wm8753_mono_mux),
+SOC_ENUM_SINGLE(WM8753_INCTL1, 1, 2, wm8753_right_mux),
+SOC_ENUM_SINGLE(WM8753_INCTL1, 0, 2, wm8753_left_mux),
+SOC_ENUM_SINGLE(WM8753_INCTL2, 6, 4, wm8753_rxmsel),
+SOC_ENUM_SINGLE(WM8753_INCTL2, 4, 4, wm8753_sidetone_mux),
+SOC_ENUM_SINGLE(WM8753_OUTCTL, 7, 4, wm8753_mono2_src),
+SOC_ENUM_SINGLE(WM8753_OUTCTL, 0, 3, wm8753_out3),
+SOC_ENUM_SINGLE(WM8753_ADCTL2, 7, 3, wm8753_out4),
+SOC_ENUM_SINGLE(WM8753_ADCIN, 2, 3, wm8753_radcsel),
+SOC_ENUM_SINGLE(WM8753_ADCIN, 0, 3, wm8753_ladcsel),
+SOC_ENUM_SINGLE(WM8753_ADCIN, 4, 4, wm8753_mono_adc),
+SOC_ENUM_SINGLE(WM8753_ADC, 2, 4, wm8753_adc_hp),
+SOC_ENUM_SINGLE(WM8753_ADC, 4, 2, wm8753_adc_filter),
+SOC_ENUM_SINGLE(WM8753_MICBIAS, 6, 3, wm8753_mic_sel),
+SOC_ENUM_SINGLE(WM8753_IOCTL, 2, 4, wm8753_dai_mode),
+SOC_ENUM_SINGLE(WM8753_ADC, 7, 4, wm8753_dat_sel),
+SOC_ENUM_SINGLE(WM8753_OUTCTL, 2, 2, wm8753_rout2_phase),
+};
+
+
+static int wm8753_get_dai(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8753->dai_func;
+ return 0;
+}
+
+static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+ u16 ioctl;
+
+ if (wm8753->dai_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (snd_soc_codec_is_active(codec))
+ return -EBUSY;
+
+ ioctl = snd_soc_read(codec, WM8753_IOCTL);
+
+ wm8753->dai_func = ucontrol->value.integer.value[0];
+
+ if (((ioctl >> 2) & 0x3) == wm8753->dai_func)
+ return 1;
+
+ ioctl = (ioctl & 0x1f3) | (wm8753->dai_func << 2);
+ snd_soc_write(codec, WM8753_IOCTL, ioctl);
+
+
+ wm8753_hifi_write_dai_fmt(codec, wm8753->hifi_fmt);
+ wm8753_voice_write_dai_fmt(codec, wm8753->voice_fmt);
+
+ return 1;
+}
+
+static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_preamp_tlv, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+static const unsigned int out_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ /* 0000000 - 0101111 = "Analogue mute" */
+ 0, 48, TLV_DB_SCALE_ITEM(-25500, 0, 0),
+ 48, 127, TLV_DB_SCALE_ITEM(-7300, 100, 0),
+};
+static const DECLARE_TLV_DB_SCALE(mix_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(voice_mix_tlv, -1200, 300, 0);
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);
+
+static const struct snd_kcontrol_new wm8753_snd_controls[] = {
+SOC_DOUBLE_R_TLV("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0, dac_tlv),
+
+SOC_DOUBLE_R_TLV("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0,
+ adc_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V,
+ 0, 127, 0, out_tlv),
+SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0,
+ 127, 0, out_tlv),
+
+SOC_SINGLE_TLV("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0, out_tlv),
+
+SOC_DOUBLE_R_TLV("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7,
+ 1, mix_tlv),
+SOC_DOUBLE_R_TLV("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4,
+ 7, 1, mix_tlv),
+SOC_DOUBLE_R_TLV("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7,
+ 1, voice_mix_tlv),
+
+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7,
+ 1, 0),
+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7,
+ 1, 0),
+
+SOC_SINGLE_TLV("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1, mix_tlv),
+SOC_SINGLE_TLV("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("Mono Voice Playback Volume", WM8753_MOUTM2, 0, 7, 1,
+ voice_mix_tlv),
+SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0),
+
+SOC_ENUM("Bass Boost", wm8753_enum[0]),
+SOC_ENUM("Bass Filter", wm8753_enum[1]),
+SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 15, 1),
+
+SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1),
+SOC_ENUM("Treble Cut-off", wm8753_enum[2]),
+
+SOC_DOUBLE_TLV("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1,
+ rec_mix_tlv),
+SOC_SINGLE_TLV("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1,
+ rec_mix_tlv),
+
+SOC_DOUBLE_R_TLV("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0,
+ pga_tlv),
+SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0),
+SOC_DOUBLE_R("Capture Switch", WM8753_LINVOL, WM8753_RINVOL, 7, 1, 1),
+
+SOC_ENUM("Capture Filter Select", wm8753_enum[23]),
+SOC_ENUM("Capture Filter Cut-off", wm8753_enum[24]),
+SOC_SINGLE("Capture Filter Switch", WM8753_ADC, 0, 1, 1),
+
+SOC_SINGLE("ALC Capture Target Volume", WM8753_ALC1, 0, 7, 0),
+SOC_SINGLE("ALC Capture Max Volume", WM8753_ALC1, 4, 7, 0),
+SOC_ENUM("ALC Capture Function", wm8753_enum[3]),
+SOC_SINGLE("ALC Capture ZC Switch", WM8753_ALC2, 8, 1, 0),
+SOC_SINGLE("ALC Capture Hold Time", WM8753_ALC2, 0, 15, 1),
+SOC_SINGLE("ALC Capture Decay Time", WM8753_ALC3, 4, 15, 1),
+SOC_SINGLE("ALC Capture Attack Time", WM8753_ALC3, 0, 15, 0),
+SOC_SINGLE("ALC Capture NG Threshold", WM8753_NGATE, 3, 31, 0),
+SOC_ENUM("ALC Capture NG Type", wm8753_enum[4]),
+SOC_SINGLE("ALC Capture NG Switch", WM8753_NGATE, 0, 1, 0),
+
+SOC_ENUM("3D Function", wm8753_enum[5]),
+SOC_ENUM("3D Upper Cut-off", wm8753_enum[6]),
+SOC_ENUM("3D Lower Cut-off", wm8753_enum[7]),
+SOC_SINGLE("3D Volume", WM8753_3D, 1, 15, 0),
+SOC_SINGLE("3D Switch", WM8753_3D, 0, 1, 0),
+
+SOC_SINGLE("Capture 6dB Attenuate", WM8753_ADCTL1, 2, 1, 0),
+SOC_SINGLE("Playback 6dB Attenuate", WM8753_ADCTL1, 1, 1, 0),
+
+SOC_ENUM("De-emphasis", wm8753_enum[8]),
+SOC_ENUM("Playback Mono Mix", wm8753_enum[9]),
+SOC_ENUM("Playback Phase", wm8753_enum[10]),
+
+SOC_SINGLE_TLV("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0, mic_preamp_tlv),
+SOC_SINGLE_TLV("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0, mic_preamp_tlv),
+
+SOC_ENUM_EXT("DAI Mode", wm8753_enum[26], wm8753_get_dai, wm8753_set_dai),
+
+SOC_ENUM("ADC Data Select", wm8753_enum[27]),
+SOC_ENUM("ROUT2 Phase", wm8753_enum[28]),
+};
+
+/*
+ * _DAPM_ Controls
+ */
+
+/* Left Mixer */
+static const struct snd_kcontrol_new wm8753_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_LOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_LOUTM2, 7, 1, 0),
+SOC_DAPM_SINGLE("Left Playback Switch", WM8753_LOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_LOUTM1, 7, 1, 0),
+};
+
+/* Right mixer */
+static const struct snd_kcontrol_new wm8753_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_ROUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_ROUTM2, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8753_ROUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_ROUTM1, 7, 1, 0),
+};
+
+/* Mono mixer */
+static const struct snd_kcontrol_new wm8753_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8753_MOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8753_MOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_MOUTM2, 3, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_MOUTM2, 7, 1, 0),
+SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_MOUTM1, 7, 1, 0),
+};
+
+/* Mono 2 Mux */
+static const struct snd_kcontrol_new wm8753_mono2_controls =
+SOC_DAPM_ENUM("Route", wm8753_enum[17]);
+
+/* Out 3 Mux */
+static const struct snd_kcontrol_new wm8753_out3_controls =
+SOC_DAPM_ENUM("Route", wm8753_enum[18]);
+
+/* Out 4 Mux */
+static const struct snd_kcontrol_new wm8753_out4_controls =
+SOC_DAPM_ENUM("Route", wm8753_enum[19]);
+
+/* ADC Mono Mix */
+static const struct snd_kcontrol_new wm8753_adc_mono_controls =
+SOC_DAPM_ENUM("Route", wm8753_enum[22]);
+
+/* Record mixer */
+static const struct snd_kcontrol_new wm8753_record_mixer_controls[] = {
+SOC_DAPM_SINGLE("Voice Capture Switch", WM8753_RECMIX2, 3, 1, 0),
+SOC_DAPM_SINGLE("Left Capture Switch", WM8753_RECMIX1, 3, 1, 0),
+SOC_DAPM_SINGLE("Right Capture Switch", WM8753_RECMIX1, 7, 1, 0),
+};
+
+/* Left ADC mux */
+static const struct snd_kcontrol_new wm8753_adc_left_controls =
+SOC_DAPM_ENUM("Route", wm8753_enum[21]);
+
+/* Right ADC mux */
+static const struct snd_kcontrol_new wm8753_adc_right_controls =
+SOC_DAPM_ENUM("Route", wm8753_enum[20]);
+
+/* MIC mux */
+static const struct snd_kcontrol_new wm8753_mic_mux_controls =
+SOC_DAPM_ENUM("Route", wm8753_enum[16]);
+
+/* ALC mixer */
+static const struct snd_kcontrol_new wm8753_alc_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Capture Switch", WM8753_INCTL2, 3, 1, 0),
+SOC_DAPM_SINGLE("Mic2 Capture Switch", WM8753_INCTL2, 2, 1, 0),
+SOC_DAPM_SINGLE("Mic1 Capture Switch", WM8753_INCTL2, 1, 1, 0),
+SOC_DAPM_SINGLE("Rx Capture Switch", WM8753_INCTL2, 0, 1, 0),
+};
+
+/* Left Line mux */
+static const struct snd_kcontrol_new wm8753_line_left_controls =
+SOC_DAPM_ENUM("Route", wm8753_enum[14]);
+
+/* Right Line mux */
+static const struct snd_kcontrol_new wm8753_line_right_controls =
+SOC_DAPM_ENUM("Route", wm8753_enum[13]);
+
+/* Mono Line mux */
+static const struct snd_kcontrol_new wm8753_line_mono_controls =
+SOC_DAPM_ENUM("Route", wm8753_enum[12]);
+
+/* Line mux and mixer */
+static const struct snd_kcontrol_new wm8753_line_mux_mix_controls =
+SOC_DAPM_ENUM("Route", wm8753_enum[11]);
+
+/* Rx mux and mixer */
+static const struct snd_kcontrol_new wm8753_rx_mux_mix_controls =
+SOC_DAPM_ENUM("Route", wm8753_enum[15]);
+
+/* Mic Selector Mux */
+static const struct snd_kcontrol_new wm8753_mic_sel_mux_controls =
+SOC_DAPM_ENUM("Route", wm8753_enum[25]);
+
+static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8753_PWR1, 5, 0),
+SND_SOC_DAPM_MIXER("Left Mixer", WM8753_PWR4, 0, 0,
+ &wm8753_left_mixer_controls[0], ARRAY_SIZE(wm8753_left_mixer_controls)),
+SND_SOC_DAPM_PGA("Left Out 1", WM8753_PWR3, 8, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left Out 2", WM8753_PWR3, 6, 0, NULL, 0),
+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", WM8753_PWR1, 3, 0),
+SND_SOC_DAPM_OUTPUT("LOUT1"),
+SND_SOC_DAPM_OUTPUT("LOUT2"),
+SND_SOC_DAPM_MIXER("Right Mixer", WM8753_PWR4, 1, 0,
+ &wm8753_right_mixer_controls[0], ARRAY_SIZE(wm8753_right_mixer_controls)),
+SND_SOC_DAPM_PGA("Right Out 1", WM8753_PWR3, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Out 2", WM8753_PWR3, 5, 0, NULL, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", WM8753_PWR1, 2, 0),
+SND_SOC_DAPM_OUTPUT("ROUT1"),
+SND_SOC_DAPM_OUTPUT("ROUT2"),
+SND_SOC_DAPM_MIXER("Mono Mixer", WM8753_PWR4, 2, 0,
+ &wm8753_mono_mixer_controls[0], ARRAY_SIZE(wm8753_mono_mixer_controls)),
+SND_SOC_DAPM_PGA("Mono Out 1", WM8753_PWR3, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Mono Out 2", WM8753_PWR3, 1, 0, NULL, 0),
+SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", WM8753_PWR1, 4, 0),
+SND_SOC_DAPM_OUTPUT("MONO1"),
+SND_SOC_DAPM_MUX("Mono 2 Mux", SND_SOC_NOPM, 0, 0, &wm8753_mono2_controls),
+SND_SOC_DAPM_OUTPUT("MONO2"),
+SND_SOC_DAPM_MIXER("Out3 Left + Right", -1, 0, 0, NULL, 0),
+SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8753_out3_controls),
+SND_SOC_DAPM_PGA("Out 3", WM8753_PWR3, 4, 0, NULL, 0),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_MUX("Out4 Mux", SND_SOC_NOPM, 0, 0, &wm8753_out4_controls),
+SND_SOC_DAPM_PGA("Out 4", WM8753_PWR3, 3, 0, NULL, 0),
+SND_SOC_DAPM_OUTPUT("OUT4"),
+SND_SOC_DAPM_MIXER("Playback Mixer", WM8753_PWR4, 3, 0,
+ &wm8753_record_mixer_controls[0],
+ ARRAY_SIZE(wm8753_record_mixer_controls)),
+SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8753_PWR2, 3, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8753_PWR2, 2, 0),
+SND_SOC_DAPM_MUX("Capture Left Mixer", SND_SOC_NOPM, 0, 0,
+ &wm8753_adc_mono_controls),
+SND_SOC_DAPM_MUX("Capture Right Mixer", SND_SOC_NOPM, 0, 0,
+ &wm8753_adc_mono_controls),
+SND_SOC_DAPM_MUX("Capture Left Mux", SND_SOC_NOPM, 0, 0,
+ &wm8753_adc_left_controls),
+SND_SOC_DAPM_MUX("Capture Right Mux", SND_SOC_NOPM, 0, 0,
+ &wm8753_adc_right_controls),
+SND_SOC_DAPM_MUX("Mic Sidetone Mux", SND_SOC_NOPM, 0, 0,
+ &wm8753_mic_mux_controls),
+SND_SOC_DAPM_PGA("Left Capture Volume", WM8753_PWR2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Capture Volume", WM8753_PWR2, 4, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("ALC Mixer", WM8753_PWR2, 6, 0,
+ &wm8753_alc_mixer_controls[0], ARRAY_SIZE(wm8753_alc_mixer_controls)),
+SND_SOC_DAPM_MUX("Line Left Mux", SND_SOC_NOPM, 0, 0,
+ &wm8753_line_left_controls),
+SND_SOC_DAPM_MUX("Line Right Mux", SND_SOC_NOPM, 0, 0,
+ &wm8753_line_right_controls),
+SND_SOC_DAPM_MUX("Line Mono Mux", SND_SOC_NOPM, 0, 0,
+ &wm8753_line_mono_controls),
+SND_SOC_DAPM_MUX("Line Mixer", WM8753_PWR2, 0, 0,
+ &wm8753_line_mux_mix_controls),
+SND_SOC_DAPM_MUX("Rx Mixer", WM8753_PWR2, 1, 0,
+ &wm8753_rx_mux_mix_controls),
+SND_SOC_DAPM_PGA("Mic 1 Volume", WM8753_PWR2, 8, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Mic 2 Volume", WM8753_PWR2, 7, 0, NULL, 0),
+SND_SOC_DAPM_MUX("Mic Selection Mux", SND_SOC_NOPM, 0, 0,
+ &wm8753_mic_sel_mux_controls),
+SND_SOC_DAPM_INPUT("LINE1"),
+SND_SOC_DAPM_INPUT("LINE2"),
+SND_SOC_DAPM_INPUT("RXP"),
+SND_SOC_DAPM_INPUT("RXN"),
+SND_SOC_DAPM_INPUT("ACIN"),
+SND_SOC_DAPM_OUTPUT("ACOP"),
+SND_SOC_DAPM_INPUT("MIC1N"),
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2N"),
+SND_SOC_DAPM_INPUT("MIC2"),
+SND_SOC_DAPM_VMID("VREF"),
+};
+
+static const struct snd_soc_dapm_route wm8753_dapm_routes[] = {
+ /* left mixer */
+ {"Left Mixer", "Left Playback Switch", "Left DAC"},
+ {"Left Mixer", "Voice Playback Switch", "Voice DAC"},
+ {"Left Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
+ {"Left Mixer", "Bypass Playback Switch", "Line Left Mux"},
+
+ /* right mixer */
+ {"Right Mixer", "Right Playback Switch", "Right DAC"},
+ {"Right Mixer", "Voice Playback Switch", "Voice DAC"},
+ {"Right Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
+ {"Right Mixer", "Bypass Playback Switch", "Line Right Mux"},
+
+ /* mono mixer */
+ {"Mono Mixer", "Voice Playback Switch", "Voice DAC"},
+ {"Mono Mixer", "Left Playback Switch", "Left DAC"},
+ {"Mono Mixer", "Right Playback Switch", "Right DAC"},
+ {"Mono Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
+ {"Mono Mixer", "Bypass Playback Switch", "Line Mono Mux"},
+
+ /* left out */
+ {"Left Out 1", NULL, "Left Mixer"},
+ {"Left Out 2", NULL, "Left Mixer"},
+ {"LOUT1", NULL, "Left Out 1"},
+ {"LOUT2", NULL, "Left Out 2"},
+
+ /* right out */
+ {"Right Out 1", NULL, "Right Mixer"},
+ {"Right Out 2", NULL, "Right Mixer"},
+ {"ROUT1", NULL, "Right Out 1"},
+ {"ROUT2", NULL, "Right Out 2"},
+
+ /* mono 1 out */
+ {"Mono Out 1", NULL, "Mono Mixer"},
+ {"MONO1", NULL, "Mono Out 1"},
+
+ /* mono 2 out */
+ {"Mono 2 Mux", "Left + Right", "Out3 Left + Right"},
+ {"Mono 2 Mux", "Inverted Mono 1", "MONO1"},
+ {"Mono 2 Mux", "Left", "Left Mixer"},
+ {"Mono 2 Mux", "Right", "Right Mixer"},
+ {"Mono Out 2", NULL, "Mono 2 Mux"},
+ {"MONO2", NULL, "Mono Out 2"},
+
+ /* out 3 */
+ {"Out3 Left + Right", NULL, "Left Mixer"},
+ {"Out3 Left + Right", NULL, "Right Mixer"},
+ {"Out3 Mux", "VREF", "VREF"},
+ {"Out3 Mux", "Left + Right", "Out3 Left + Right"},
+ {"Out3 Mux", "ROUT2", "ROUT2"},
+ {"Out 3", NULL, "Out3 Mux"},
+ {"OUT3", NULL, "Out 3"},
+
+ /* out 4 */
+ {"Out4 Mux", "VREF", "VREF"},
+ {"Out4 Mux", "Capture ST", "Playback Mixer"},
+ {"Out4 Mux", "LOUT2", "LOUT2"},
+ {"Out 4", NULL, "Out4 Mux"},
+ {"OUT4", NULL, "Out 4"},
+
+ /* record mixer */
+ {"Playback Mixer", "Left Capture Switch", "Left Mixer"},
+ {"Playback Mixer", "Voice Capture Switch", "Mono Mixer"},
+ {"Playback Mixer", "Right Capture Switch", "Right Mixer"},
+
+ /* Mic/SideTone Mux */
+ {"Mic Sidetone Mux", "Left PGA", "Left Capture Volume"},
+ {"Mic Sidetone Mux", "Right PGA", "Right Capture Volume"},
+ {"Mic Sidetone Mux", "Mic 1", "Mic 1 Volume"},
+ {"Mic Sidetone Mux", "Mic 2", "Mic 2 Volume"},
+
+ /* Capture Left Mux */
+ {"Capture Left Mux", "PGA", "Left Capture Volume"},
+ {"Capture Left Mux", "Line or RXP-RXN", "Line Left Mux"},
+ {"Capture Left Mux", "Line", "LINE1"},
+
+ /* Capture Right Mux */
+ {"Capture Right Mux", "PGA", "Right Capture Volume"},
+ {"Capture Right Mux", "Line or RXP-RXN", "Line Right Mux"},
+ {"Capture Right Mux", "Sidetone", "Playback Mixer"},
+
+ /* Mono Capture mixer-mux */
+ {"Capture Right Mixer", "Stereo", "Capture Right Mux"},
+ {"Capture Left Mixer", "Stereo", "Capture Left Mux"},
+ {"Capture Left Mixer", "Analogue Mix Left", "Capture Left Mux"},
+ {"Capture Left Mixer", "Analogue Mix Left", "Capture Right Mux"},
+ {"Capture Right Mixer", "Analogue Mix Right", "Capture Left Mux"},
+ {"Capture Right Mixer", "Analogue Mix Right", "Capture Right Mux"},
+ {"Capture Left Mixer", "Digital Mono Mix", "Capture Left Mux"},
+ {"Capture Left Mixer", "Digital Mono Mix", "Capture Right Mux"},
+ {"Capture Right Mixer", "Digital Mono Mix", "Capture Left Mux"},
+ {"Capture Right Mixer", "Digital Mono Mix", "Capture Right Mux"},
+
+ /* ADC */
+ {"Left ADC", NULL, "Capture Left Mixer"},
+ {"Right ADC", NULL, "Capture Right Mixer"},
+
+ /* Left Capture Volume */
+ {"Left Capture Volume", NULL, "ACIN"},
+
+ /* Right Capture Volume */
+ {"Right Capture Volume", NULL, "Mic 2 Volume"},
+
+ /* ALC Mixer */
+ {"ALC Mixer", "Line Capture Switch", "Line Mixer"},
+ {"ALC Mixer", "Mic2 Capture Switch", "Mic 2 Volume"},
+ {"ALC Mixer", "Mic1 Capture Switch", "Mic 1 Volume"},
+ {"ALC Mixer", "Rx Capture Switch", "Rx Mixer"},
+
+ /* Line Left Mux */
+ {"Line Left Mux", "Line 1", "LINE1"},
+ {"Line Left Mux", "Rx Mix", "Rx Mixer"},
+
+ /* Line Right Mux */
+ {"Line Right Mux", "Line 2", "LINE2"},
+ {"Line Right Mux", "Rx Mix", "Rx Mixer"},
+
+ /* Line Mono Mux */
+ {"Line Mono Mux", "Line Mix", "Line Mixer"},
+ {"Line Mono Mux", "Rx Mix", "Rx Mixer"},
+
+ /* Line Mixer/Mux */
+ {"Line Mixer", "Line 1 + 2", "LINE1"},
+ {"Line Mixer", "Line 1 - 2", "LINE1"},
+ {"Line Mixer", "Line 1 + 2", "LINE2"},
+ {"Line Mixer", "Line 1 - 2", "LINE2"},
+ {"Line Mixer", "Line 1", "LINE1"},
+ {"Line Mixer", "Line 2", "LINE2"},
+
+ /* Rx Mixer/Mux */
+ {"Rx Mixer", "RXP - RXN", "RXP"},
+ {"Rx Mixer", "RXP + RXN", "RXP"},
+ {"Rx Mixer", "RXP - RXN", "RXN"},
+ {"Rx Mixer", "RXP + RXN", "RXN"},
+ {"Rx Mixer", "RXP", "RXP"},
+ {"Rx Mixer", "RXN", "RXN"},
+
+ /* Mic 1 Volume */
+ {"Mic 1 Volume", NULL, "MIC1N"},
+ {"Mic 1 Volume", NULL, "Mic Selection Mux"},
+
+ /* Mic 2 Volume */
+ {"Mic 2 Volume", NULL, "MIC2N"},
+ {"Mic 2 Volume", NULL, "MIC2"},
+
+ /* Mic Selector Mux */
+ {"Mic Selection Mux", "Mic 1", "MIC1"},
+ {"Mic Selection Mux", "Mic 2", "MIC2N"},
+ {"Mic Selection Mux", "Mic 3", "MIC2"},
+
+ /* ACOP */
+ {"ACOP", NULL, "ALC Mixer"},
+};
+
+/* PLL divisors */
+struct _pll_div {
+ u32 div2:1;
+ u32 n:4;
+ u32 k:24;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 22) * 10)
+
+static void pll_factors(struct _pll_div *pll_div, unsigned int target,
+ unsigned int source)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div->div2 = 1;
+ Ndiv = target / source;
+ } else
+ pll_div->div2 = 0;
+
+ if ((Ndiv < 6) || (Ndiv > 12))
+ printk(KERN_WARNING
+ "wm8753: unsupported N = %u\n", Ndiv);
+
+ pll_div->n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div->k = K;
+}
+
+static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ u16 reg, enable;
+ int offset;
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ if (pll_id < WM8753_PLL1 || pll_id > WM8753_PLL2)
+ return -ENODEV;
+
+ if (pll_id == WM8753_PLL1) {
+ offset = 0;
+ enable = 0x10;
+ reg = snd_soc_read(codec, WM8753_CLOCK) & 0xffef;
+ } else {
+ offset = 4;
+ enable = 0x8;
+ reg = snd_soc_read(codec, WM8753_CLOCK) & 0xfff7;
+ }
+
+ if (!freq_in || !freq_out) {
+ /* disable PLL */
+ snd_soc_write(codec, WM8753_PLL1CTL1 + offset, 0x0026);
+ snd_soc_write(codec, WM8753_CLOCK, reg);
+ return 0;
+ } else {
+ u16 value = 0;
+ struct _pll_div pll_div;
+
+ pll_factors(&pll_div, freq_out * 8, freq_in);
+
+ /* set up N and K PLL divisor ratios */
+ /* bits 8:5 = PLL_N, bits 3:0 = PLL_K[21:18] */
+ value = (pll_div.n << 5) + ((pll_div.k & 0x3c0000) >> 18);
+ snd_soc_write(codec, WM8753_PLL1CTL2 + offset, value);
+
+ /* bits 8:0 = PLL_K[17:9] */
+ value = (pll_div.k & 0x03fe00) >> 9;
+ snd_soc_write(codec, WM8753_PLL1CTL3 + offset, value);
+
+ /* bits 8:0 = PLL_K[8:0] */
+ value = pll_div.k & 0x0001ff;
+ snd_soc_write(codec, WM8753_PLL1CTL4 + offset, value);
+
+ /* set PLL as input and enable */
+ snd_soc_write(codec, WM8753_PLL1CTL1 + offset, 0x0027 |
+ (pll_div.div2 << 3));
+ snd_soc_write(codec, WM8753_CLOCK, reg | enable);
+ }
+ return 0;
+}
+
+struct _coeff_div {
+ u32 mclk;
+ u32 rate;
+ u8 sr:5;
+ u8 usb:1;
+};
+
+/* codec hifi mclk (after PLL) clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ /* 8k */
+ {12288000, 8000, 0x6, 0x0},
+ {11289600, 8000, 0x16, 0x0},
+ {18432000, 8000, 0x7, 0x0},
+ {16934400, 8000, 0x17, 0x0},
+ {12000000, 8000, 0x6, 0x1},
+
+ /* 11.025k */
+ {11289600, 11025, 0x18, 0x0},
+ {16934400, 11025, 0x19, 0x0},
+ {12000000, 11025, 0x19, 0x1},
+
+ /* 16k */
+ {12288000, 16000, 0xa, 0x0},
+ {18432000, 16000, 0xb, 0x0},
+ {12000000, 16000, 0xa, 0x1},
+
+ /* 22.05k */
+ {11289600, 22050, 0x1a, 0x0},
+ {16934400, 22050, 0x1b, 0x0},
+ {12000000, 22050, 0x1b, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 0xc, 0x0},
+ {18432000, 32000, 0xd, 0x0},
+ {12000000, 32000, 0xa, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 0x10, 0x0},
+ {16934400, 44100, 0x11, 0x0},
+ {12000000, 44100, 0x11, 0x1},
+
+ /* 48k */
+ {12288000, 48000, 0x0, 0x0},
+ {18432000, 48000, 0x1, 0x0},
+ {12000000, 48000, 0x0, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 0x1e, 0x0},
+ {16934400, 88200, 0x1f, 0x0},
+ {12000000, 88200, 0x1f, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 0xe, 0x0},
+ {18432000, 96000, 0xf, 0x0},
+ {12000000, 96000, 0xe, 0x1},
+};
+
+static int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Clock after PLL and dividers
+ */
+static int wm8753_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ if (clk_id == WM8753_MCLK) {
+ wm8753->sysclk = freq;
+ return 0;
+ } else if (clk_id == WM8753_PCMCLK) {
+ wm8753->pcmclk = freq;
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Set's ADC and Voice DAC format.
+ */
+static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec *codec,
+ unsigned int fmt)
+{
+ u16 voice = snd_soc_read(codec, WM8753_PCM) & 0x01ec;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ voice |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ voice |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ voice |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ voice |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8753_PCM, voice);
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+ u16 voice = snd_soc_read(codec, WM8753_PCM) & 0x01f3;
+ u16 srate = snd_soc_read(codec, WM8753_SRATE1) & 0x017f;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ voice |= 0x0004;
+ break;
+ case 24:
+ voice |= 0x0008;
+ break;
+ case 32:
+ voice |= 0x000c;
+ break;
+ }
+
+ /* sample rate */
+ if (params_rate(params) * 384 == wm8753->pcmclk)
+ srate |= 0x80;
+ snd_soc_write(codec, WM8753_SRATE1, srate);
+
+ snd_soc_write(codec, WM8753_PCM, voice);
+ return 0;
+}
+
+/*
+ * Set's PCM dai fmt and BCLK.
+ */
+static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec *codec,
+ unsigned int fmt)
+{
+ u16 voice, ioctl;
+
+ voice = snd_soc_read(codec, WM8753_PCM) & 0x011f;
+ ioctl = snd_soc_read(codec, WM8753_IOCTL) & 0x015d;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ioctl |= 0x2;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ voice |= 0x0040;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ voice |= 0x0080;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ voice &= ~0x0010;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ voice |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ voice |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ voice |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8753_PCM, voice);
+ snd_soc_write(codec, WM8753_IOCTL, ioctl);
+ return 0;
+}
+
+static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8753_PCMDIV:
+ reg = snd_soc_read(codec, WM8753_CLOCK) & 0x003f;
+ snd_soc_write(codec, WM8753_CLOCK, reg | div);
+ break;
+ case WM8753_BCLKDIV:
+ reg = snd_soc_read(codec, WM8753_SRATE2) & 0x01c7;
+ snd_soc_write(codec, WM8753_SRATE2, reg | div);
+ break;
+ case WM8753_VXCLKDIV:
+ reg = snd_soc_read(codec, WM8753_SRATE2) & 0x003f;
+ snd_soc_write(codec, WM8753_SRATE2, reg | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Set's HiFi DAC format.
+ */
+static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec *codec,
+ unsigned int fmt)
+{
+ u16 hifi = snd_soc_read(codec, WM8753_HIFI) & 0x01e0;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ hifi |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ hifi |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ hifi |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ hifi |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8753_HIFI, hifi);
+ return 0;
+}
+
+/*
+ * Set's I2S DAI format.
+ */
+static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec *codec,
+ unsigned int fmt)
+{
+ u16 ioctl, hifi;
+
+ hifi = snd_soc_read(codec, WM8753_HIFI) & 0x011f;
+ ioctl = snd_soc_read(codec, WM8753_IOCTL) & 0x00ae;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ioctl |= 0x1;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ hifi |= 0x0040;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ hifi |= 0x0080;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ hifi &= ~0x0010;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ hifi |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ hifi |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ hifi |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8753_HIFI, hifi);
+ snd_soc_write(codec, WM8753_IOCTL, ioctl);
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+ u16 srate = snd_soc_read(codec, WM8753_SRATE1) & 0x01c0;
+ u16 hifi = snd_soc_read(codec, WM8753_HIFI) & 0x01f3;
+ int coeff;
+
+ /* is digital filter coefficient valid ? */
+ coeff = get_coeff(wm8753->sysclk, params_rate(params));
+ if (coeff < 0) {
+ printk(KERN_ERR "wm8753 invalid MCLK or rate\n");
+ return coeff;
+ }
+ snd_soc_write(codec, WM8753_SRATE1, srate | (coeff_div[coeff].sr << 1) |
+ coeff_div[coeff].usb);
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ hifi |= 0x0004;
+ break;
+ case 24:
+ hifi |= 0x0008;
+ break;
+ case 32:
+ hifi |= 0x000c;
+ break;
+ }
+
+ snd_soc_write(codec, WM8753_HIFI, hifi);
+ return 0;
+}
+
+static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec *codec,
+ unsigned int fmt)
+{
+ u16 clock;
+
+ /* set clk source as pcmclk */
+ clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
+ snd_soc_write(codec, WM8753_CLOCK, clock);
+
+ return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
+}
+
+static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec *codec,
+ unsigned int fmt)
+{
+ return wm8753_hdac_set_dai_fmt(codec, fmt);
+}
+
+static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec *codec,
+ unsigned int fmt)
+{
+ u16 clock;
+
+ /* set clk source as pcmclk */
+ clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
+ snd_soc_write(codec, WM8753_CLOCK, clock);
+
+ return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
+}
+
+static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec *codec,
+ unsigned int fmt)
+{
+ u16 clock;
+
+ /* set clk source as mclk */
+ clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
+ snd_soc_write(codec, WM8753_CLOCK, clock | 0x4);
+
+ if (wm8753_hdac_set_dai_fmt(codec, fmt) < 0)
+ return -EINVAL;
+ return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
+}
+
+static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec,
+ unsigned int fmt)
+{
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ switch (wm8753->dai_func) {
+ case 0:
+ ret = wm8753_mode1h_set_dai_fmt(codec, fmt);
+ break;
+ case 1:
+ ret = wm8753_mode2_set_dai_fmt(codec, fmt);
+ break;
+ case 2:
+ case 3:
+ ret = wm8753_mode3_4_set_dai_fmt(codec, fmt);
+ break;
+ default:
+ break;
+ }
+ if (ret)
+ return ret;
+
+ return wm8753_i2s_set_dai_fmt(codec, fmt);
+}
+
+static int wm8753_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+
+ wm8753->hifi_fmt = fmt;
+
+ return wm8753_hifi_write_dai_fmt(codec, fmt);
+};
+
+static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec,
+ unsigned int fmt)
+{
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if (wm8753->dai_func != 0)
+ return 0;
+
+ ret = wm8753_mode1v_set_dai_fmt(codec, fmt);
+ if (ret)
+ return ret;
+ ret = wm8753_pcm_set_dai_fmt(codec, fmt);
+ if (ret)
+ return ret;
+
+ return 0;
+};
+
+static int wm8753_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+
+ wm8753->voice_fmt = fmt;
+
+ return wm8753_voice_write_dai_fmt(codec, fmt);
+};
+
+static int wm8753_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8753_DAC) & 0xfff7;
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+
+ /* the digital mute covers the HiFi and Voice DAC's on the WM8753.
+ * make sure we check if they are not both active when we mute */
+ if (mute && wm8753->dai_func == 1) {
+ if (!snd_soc_codec_is_active(codec))
+ snd_soc_write(codec, WM8753_DAC, mute_reg | 0x8);
+ } else {
+ if (mute)
+ snd_soc_write(codec, WM8753_DAC, mute_reg | 0x8);
+ else
+ snd_soc_write(codec, WM8753_DAC, mute_reg);
+ }
+
+ return 0;
+}
+
+static void wm8753_charge_work(struct work_struct *work)
+{
+ struct wm8753_priv *wm8753 =
+ container_of(work, struct wm8753_priv, charge_work.work);
+
+ /* Set to 500k */
+ regmap_update_bits(wm8753->regmap, WM8753_PWR1, 0x0180, 0x0100);
+}
+
+static int wm8753_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+ u16 pwr_reg = snd_soc_read(codec, WM8753_PWR1) & 0xfe3e;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* set vmid to 50k and unmute dac */
+ snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x00c0);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* Wait until fully charged */
+ flush_delayed_work(&wm8753->charge_work);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ /* set vmid to 5k for quick power up */
+ snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x01c1);
+ schedule_delayed_work(&wm8753->charge_work,
+ msecs_to_jiffies(caps_charge));
+ } else {
+ /* mute dac and set vmid to 500k, enable VREF */
+ snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x0141);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ cancel_delayed_work_sync(&wm8753->charge_work);
+ snd_soc_write(codec, WM8753_PWR1, 0x0001);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8753_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define WM8753_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+/*
+ * The WM8753 supports up to 4 different and mutually exclusive DAI
+ * configurations. This gives 2 PCM's available for use, hifi and voice.
+ * NOTE: The Voice PCM cannot play or capture audio to the CPU as it's DAI
+ * is connected between the wm8753 and a BT codec or GSM modem.
+ *
+ * 1. Voice over PCM DAI - HIFI DAC over HIFI DAI
+ * 2. Voice over HIFI DAI - HIFI disabled
+ * 3. Voice disabled - HIFI over HIFI
+ * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
+ */
+static const struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode = {
+ .hw_params = wm8753_i2s_hw_params,
+ .digital_mute = wm8753_mute,
+ .set_fmt = wm8753_hifi_set_dai_fmt,
+ .set_clkdiv = wm8753_set_dai_clkdiv,
+ .set_pll = wm8753_set_dai_pll,
+ .set_sysclk = wm8753_set_dai_sysclk,
+};
+
+static const struct snd_soc_dai_ops wm8753_dai_ops_voice_mode = {
+ .hw_params = wm8753_pcm_hw_params,
+ .digital_mute = wm8753_mute,
+ .set_fmt = wm8753_voice_set_dai_fmt,
+ .set_clkdiv = wm8753_set_dai_clkdiv,
+ .set_pll = wm8753_set_dai_pll,
+ .set_sysclk = wm8753_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver wm8753_dai[] = {
+/* DAI HiFi mode 1 */
+{ .name = "wm8753-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8753_RATES,
+ .formats = WM8753_FORMATS
+ },
+ .capture = { /* dummy for fast DAI switching */
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8753_RATES,
+ .formats = WM8753_FORMATS
+ },
+ .ops = &wm8753_dai_ops_hifi_mode,
+},
+/* DAI Voice mode 1 */
+{ .name = "wm8753-voice",
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = WM8753_RATES,
+ .formats = WM8753_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8753_RATES,
+ .formats = WM8753_FORMATS,
+ },
+ .ops = &wm8753_dai_ops_voice_mode,
+},
+};
+
+static int wm8753_resume(struct snd_soc_codec *codec)
+{
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_sync(wm8753->regmap);
+
+ return 0;
+}
+
+static int wm8753_probe(struct snd_soc_codec *codec)
+{
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ INIT_DELAYED_WORK(&wm8753->charge_work, wm8753_charge_work);
+
+ ret = wm8753_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
+
+ wm8753->dai_func = 0;
+
+ /* set the update bits */
+ snd_soc_update_bits(codec, WM8753_LDAC, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8753_RDAC, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8753_LADC, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8753_RADC, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8753_LOUT1V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8753_ROUT1V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8753_LOUT2V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8753_ROUT2V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8753_LINVOL, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8753_RINVOL, 0x0100, 0x0100);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8753 = {
+ .probe = wm8753_probe,
+ .resume = wm8753_resume,
+ .set_bias_level = wm8753_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8753_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8753_snd_controls),
+ .dapm_widgets = wm8753_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8753_dapm_widgets),
+ .dapm_routes = wm8753_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8753_dapm_routes),
+};
+
+static const struct of_device_id wm8753_of_match[] = {
+ { .compatible = "wlf,wm8753", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8753_of_match);
+
+static const struct regmap_config wm8753_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = WM8753_ADCTL2,
+ .writeable_reg = wm8753_writeable,
+ .volatile_reg = wm8753_volatile,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm8753_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8753_reg_defaults),
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8753_spi_probe(struct spi_device *spi)
+{
+ struct wm8753_priv *wm8753;
+ int ret;
+
+ wm8753 = devm_kzalloc(&spi->dev, sizeof(struct wm8753_priv),
+ GFP_KERNEL);
+ if (wm8753 == NULL)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, wm8753);
+
+ wm8753->regmap = devm_regmap_init_spi(spi, &wm8753_regmap);
+ if (IS_ERR(wm8753->regmap)) {
+ ret = PTR_ERR(wm8753->regmap);
+ dev_err(&spi->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&spi->dev, &soc_codec_dev_wm8753,
+ wm8753_dai, ARRAY_SIZE(wm8753_dai));
+ if (ret != 0)
+ dev_err(&spi->dev, "Failed to register CODEC: %d\n", ret);
+
+ return ret;
+}
+
+static int wm8753_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver wm8753_spi_driver = {
+ .driver = {
+ .name = "wm8753",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8753_of_match,
+ },
+ .probe = wm8753_spi_probe,
+ .remove = wm8753_spi_remove,
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8753_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8753_priv *wm8753;
+ int ret;
+
+ wm8753 = devm_kzalloc(&i2c->dev, sizeof(struct wm8753_priv),
+ GFP_KERNEL);
+ if (wm8753 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm8753);
+
+ wm8753->regmap = devm_regmap_init_i2c(i2c, &wm8753_regmap);
+ if (IS_ERR(wm8753->regmap)) {
+ ret = PTR_ERR(wm8753->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8753,
+ wm8753_dai, ARRAY_SIZE(wm8753_dai));
+ if (ret != 0)
+ dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+
+ return ret;
+}
+
+static int wm8753_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8753_i2c_id[] = {
+ { "wm8753", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id);
+
+static struct i2c_driver wm8753_i2c_driver = {
+ .driver = {
+ .name = "wm8753",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8753_of_match,
+ },
+ .probe = wm8753_i2c_probe,
+ .remove = wm8753_i2c_remove,
+ .id_table = wm8753_i2c_id,
+};
+#endif
+
+static int __init wm8753_modinit(void)
+{
+ int ret = 0;
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8753_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8753 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8753_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8753 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(wm8753_modinit);
+
+static void __exit wm8753_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8753_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8753_spi_driver);
+#endif
+}
+module_exit(wm8753_exit);
+
+MODULE_DESCRIPTION("ASoC WM8753 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8753.h b/kernel/sound/soc/codecs/wm8753.h
new file mode 100644
index 000000000..94edac144
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8753.h
@@ -0,0 +1,118 @@
+/*
+ * wm8753.h -- audio driver for WM8753
+ *
+ * Copyright 2003 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.
+ *
+ */
+
+#ifndef _WM8753_H
+#define _WM8753_H
+
+/* WM8753 register space */
+
+#define WM8753_DAC 0x01
+#define WM8753_ADC 0x02
+#define WM8753_PCM 0x03
+#define WM8753_HIFI 0x04
+#define WM8753_IOCTL 0x05
+#define WM8753_SRATE1 0x06
+#define WM8753_SRATE2 0x07
+#define WM8753_LDAC 0x08
+#define WM8753_RDAC 0x09
+#define WM8753_BASS 0x0a
+#define WM8753_TREBLE 0x0b
+#define WM8753_ALC1 0x0c
+#define WM8753_ALC2 0x0d
+#define WM8753_ALC3 0x0e
+#define WM8753_NGATE 0x0f
+#define WM8753_LADC 0x10
+#define WM8753_RADC 0x11
+#define WM8753_ADCTL1 0x12
+#define WM8753_3D 0x13
+#define WM8753_PWR1 0x14
+#define WM8753_PWR2 0x15
+#define WM8753_PWR3 0x16
+#define WM8753_PWR4 0x17
+#define WM8753_ID 0x18
+#define WM8753_INTPOL 0x19
+#define WM8753_INTEN 0x1a
+#define WM8753_GPIO1 0x1b
+#define WM8753_GPIO2 0x1c
+#define WM8753_RESET 0x1f
+#define WM8753_RECMIX1 0x20
+#define WM8753_RECMIX2 0x21
+#define WM8753_LOUTM1 0x22
+#define WM8753_LOUTM2 0x23
+#define WM8753_ROUTM1 0x24
+#define WM8753_ROUTM2 0x25
+#define WM8753_MOUTM1 0x26
+#define WM8753_MOUTM2 0x27
+#define WM8753_LOUT1V 0x28
+#define WM8753_ROUT1V 0x29
+#define WM8753_LOUT2V 0x2a
+#define WM8753_ROUT2V 0x2b
+#define WM8753_MOUTV 0x2c
+#define WM8753_OUTCTL 0x2d
+#define WM8753_ADCIN 0x2e
+#define WM8753_INCTL1 0x2f
+#define WM8753_INCTL2 0x30
+#define WM8753_LINVOL 0x31
+#define WM8753_RINVOL 0x32
+#define WM8753_MICBIAS 0x33
+#define WM8753_CLOCK 0x34
+#define WM8753_PLL1CTL1 0x35
+#define WM8753_PLL1CTL2 0x36
+#define WM8753_PLL1CTL3 0x37
+#define WM8753_PLL1CTL4 0x38
+#define WM8753_PLL2CTL1 0x39
+#define WM8753_PLL2CTL2 0x3a
+#define WM8753_PLL2CTL3 0x3b
+#define WM8753_PLL2CTL4 0x3c
+#define WM8753_BIASCTL 0x3d
+#define WM8753_ADCTL2 0x3f
+
+#define WM8753_PLL1 0
+#define WM8753_PLL2 1
+
+/* clock inputs */
+#define WM8753_MCLK 0
+#define WM8753_PCMCLK 1
+
+/* clock divider id's */
+#define WM8753_PCMDIV 0
+#define WM8753_BCLKDIV 1
+#define WM8753_VXCLKDIV 2
+
+/* PCM clock dividers */
+#define WM8753_PCM_DIV_1 (0 << 6)
+#define WM8753_PCM_DIV_3 (2 << 6)
+#define WM8753_PCM_DIV_5_5 (3 << 6)
+#define WM8753_PCM_DIV_2 (4 << 6)
+#define WM8753_PCM_DIV_4 (5 << 6)
+#define WM8753_PCM_DIV_6 (6 << 6)
+#define WM8753_PCM_DIV_8 (7 << 6)
+
+/* BCLK clock dividers */
+#define WM8753_BCLK_DIV_1 (0 << 3)
+#define WM8753_BCLK_DIV_2 (1 << 3)
+#define WM8753_BCLK_DIV_4 (2 << 3)
+#define WM8753_BCLK_DIV_8 (3 << 3)
+#define WM8753_BCLK_DIV_16 (4 << 3)
+
+/* VXCLK clock dividers */
+#define WM8753_VXCLK_DIV_1 (0 << 6)
+#define WM8753_VXCLK_DIV_2 (1 << 6)
+#define WM8753_VXCLK_DIV_4 (2 << 6)
+#define WM8753_VXCLK_DIV_8 (3 << 6)
+#define WM8753_VXCLK_DIV_16 (4 << 6)
+
+#define WM8753_DAI_HIFI 0
+#define WM8753_DAI_VOICE 1
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8770.c b/kernel/sound/soc/codecs/wm8770.c
new file mode 100644
index 000000000..53e977da2
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8770.c
@@ -0,0 +1,718 @@
+/*
+ * wm8770.c -- WM8770 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.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 "wm8770.h"
+
+#define WM8770_NUM_SUPPLIES 3
+static const char *wm8770_supply_names[WM8770_NUM_SUPPLIES] = {
+ "AVDD1",
+ "AVDD2",
+ "DVDD"
+};
+
+static const struct reg_default wm8770_reg_defaults[] = {
+ { 0, 0x7f },
+ { 1, 0x7f },
+ { 2, 0x7f },
+ { 3, 0x7f },
+ { 4, 0x7f },
+ { 5, 0x7f },
+ { 6, 0x7f },
+ { 7, 0x7f },
+ { 8, 0x7f },
+ { 9, 0xff },
+ { 10, 0xff },
+ { 11, 0xff },
+ { 12, 0xff },
+ { 13, 0xff },
+ { 14, 0xff },
+ { 15, 0xff },
+ { 16, 0xff },
+ { 17, 0xff },
+ { 18, 0 },
+ { 19, 0x90 },
+ { 20, 0 },
+ { 21, 0 },
+ { 22, 0x22 },
+ { 23, 0x22 },
+ { 24, 0x3e },
+ { 25, 0xc },
+ { 26, 0xc },
+ { 27, 0x100 },
+ { 28, 0x189 },
+ { 29, 0x189 },
+ { 30, 0x8770 },
+};
+
+static bool wm8770_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8770_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+struct wm8770_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[WM8770_NUM_SUPPLIES];
+ struct notifier_block disable_nb[WM8770_NUM_SUPPLIES];
+ struct snd_soc_codec *codec;
+ int sysclk;
+};
+
+static int vout12supply_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+static int vout34supply_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+/*
+ * We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define WM8770_REGULATOR_EVENT(n) \
+static int wm8770_regulator_event_##n(struct notifier_block *nb, \
+ unsigned long event, void *data) \
+{ \
+ struct wm8770_priv *wm8770 = container_of(nb, struct wm8770_priv, \
+ disable_nb[n]); \
+ if (event & REGULATOR_EVENT_DISABLE) { \
+ regcache_mark_dirty(wm8770->regmap); \
+ } \
+ return 0; \
+}
+
+WM8770_REGULATOR_EVENT(0)
+WM8770_REGULATOR_EVENT(1)
+WM8770_REGULATOR_EVENT(2)
+
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(dac_dig_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(dac_alg_tlv, -12700, 100, 1);
+
+static const char *dac_phase_text[][2] = {
+ { "DAC1 Normal", "DAC1 Inverted" },
+ { "DAC2 Normal", "DAC2 Inverted" },
+ { "DAC3 Normal", "DAC3 Inverted" },
+ { "DAC4 Normal", "DAC4 Inverted" },
+};
+
+static const struct soc_enum dac_phase[] = {
+ SOC_ENUM_DOUBLE(WM8770_DACPHASE, 0, 1, 2, dac_phase_text[0]),
+ SOC_ENUM_DOUBLE(WM8770_DACPHASE, 2, 3, 2, dac_phase_text[1]),
+ SOC_ENUM_DOUBLE(WM8770_DACPHASE, 4, 5, 2, dac_phase_text[2]),
+ SOC_ENUM_DOUBLE(WM8770_DACPHASE, 6, 7, 2, dac_phase_text[3]),
+};
+
+static const struct snd_kcontrol_new wm8770_snd_controls[] = {
+ /* global DAC playback controls */
+ SOC_SINGLE_TLV("DAC Playback Volume", WM8770_MSDIGVOL, 0, 255, 0,
+ dac_dig_tlv),
+ SOC_SINGLE("DAC Playback Switch", WM8770_DACMUTE, 4, 1, 1),
+ SOC_SINGLE("DAC Playback ZC Switch", WM8770_DACCTRL1, 0, 1, 0),
+
+ /* global VOUT playback controls */
+ SOC_SINGLE_TLV("VOUT Playback Volume", WM8770_MSALGVOL, 0, 127, 0,
+ dac_alg_tlv),
+ SOC_SINGLE("VOUT Playback ZC Switch", WM8770_MSALGVOL, 7, 1, 0),
+
+ /* VOUT1/2/3/4 specific controls */
+ SOC_DOUBLE_R_TLV("VOUT1 Playback Volume", WM8770_VOUT1LVOL,
+ WM8770_VOUT1RVOL, 0, 127, 0, dac_alg_tlv),
+ SOC_DOUBLE_R("VOUT1 Playback ZC Switch", WM8770_VOUT1LVOL,
+ WM8770_VOUT1RVOL, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("VOUT2 Playback Volume", WM8770_VOUT2LVOL,
+ WM8770_VOUT2RVOL, 0, 127, 0, dac_alg_tlv),
+ SOC_DOUBLE_R("VOUT2 Playback ZC Switch", WM8770_VOUT2LVOL,
+ WM8770_VOUT2RVOL, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("VOUT3 Playback Volume", WM8770_VOUT3LVOL,
+ WM8770_VOUT3RVOL, 0, 127, 0, dac_alg_tlv),
+ SOC_DOUBLE_R("VOUT3 Playback ZC Switch", WM8770_VOUT3LVOL,
+ WM8770_VOUT3RVOL, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("VOUT4 Playback Volume", WM8770_VOUT4LVOL,
+ WM8770_VOUT4RVOL, 0, 127, 0, dac_alg_tlv),
+ SOC_DOUBLE_R("VOUT4 Playback ZC Switch", WM8770_VOUT4LVOL,
+ WM8770_VOUT4RVOL, 7, 1, 0),
+
+ /* DAC1/2/3/4 specific controls */
+ SOC_DOUBLE_R_TLV("DAC1 Playback Volume", WM8770_DAC1LVOL,
+ WM8770_DAC1RVOL, 0, 255, 0, dac_dig_tlv),
+ SOC_SINGLE("DAC1 Deemphasis Switch", WM8770_DACCTRL2, 0, 1, 0),
+ SOC_ENUM("DAC1 Phase", dac_phase[0]),
+ SOC_DOUBLE_R_TLV("DAC2 Playback Volume", WM8770_DAC2LVOL,
+ WM8770_DAC2RVOL, 0, 255, 0, dac_dig_tlv),
+ SOC_SINGLE("DAC2 Deemphasis Switch", WM8770_DACCTRL2, 1, 1, 0),
+ SOC_ENUM("DAC2 Phase", dac_phase[1]),
+ SOC_DOUBLE_R_TLV("DAC3 Playback Volume", WM8770_DAC3LVOL,
+ WM8770_DAC3RVOL, 0, 255, 0, dac_dig_tlv),
+ SOC_SINGLE("DAC3 Deemphasis Switch", WM8770_DACCTRL2, 2, 1, 0),
+ SOC_ENUM("DAC3 Phase", dac_phase[2]),
+ SOC_DOUBLE_R_TLV("DAC4 Playback Volume", WM8770_DAC4LVOL,
+ WM8770_DAC4RVOL, 0, 255, 0, dac_dig_tlv),
+ SOC_SINGLE("DAC4 Deemphasis Switch", WM8770_DACCTRL2, 3, 1, 0),
+ SOC_ENUM("DAC4 Phase", dac_phase[3]),
+
+ /* ADC specific controls */
+ SOC_DOUBLE_R_TLV("Capture Volume", WM8770_ADCLCTRL, WM8770_ADCRCTRL,
+ 0, 31, 0, adc_tlv),
+ SOC_DOUBLE_R("Capture Switch", WM8770_ADCLCTRL, WM8770_ADCRCTRL,
+ 5, 1, 1),
+
+ /* other controls */
+ SOC_SINGLE("ADC 128x Oversampling Switch", WM8770_MSTRCTRL, 3, 1, 0),
+ SOC_SINGLE("ADC Highpass Filter Switch", WM8770_IFACECTRL, 8, 1, 1)
+};
+
+static const char *ain_text[] = {
+ "AIN1", "AIN2", "AIN3", "AIN4",
+ "AIN5", "AIN6", "AIN7", "AIN8"
+};
+
+static SOC_ENUM_DOUBLE_DECL(ain_enum,
+ WM8770_ADCMUX, 0, 4, ain_text);
+
+static const struct snd_kcontrol_new ain_mux =
+ SOC_DAPM_ENUM("Capture Mux", ain_enum);
+
+static const struct snd_kcontrol_new vout1_mix_controls[] = {
+ SOC_DAPM_SINGLE("DAC1 Switch", WM8770_OUTMUX1, 0, 1, 0),
+ SOC_DAPM_SINGLE("AUX1 Switch", WM8770_OUTMUX1, 1, 1, 0),
+ SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX1, 2, 1, 0)
+};
+
+static const struct snd_kcontrol_new vout2_mix_controls[] = {
+ SOC_DAPM_SINGLE("DAC2 Switch", WM8770_OUTMUX1, 3, 1, 0),
+ SOC_DAPM_SINGLE("AUX2 Switch", WM8770_OUTMUX1, 4, 1, 0),
+ SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX1, 5, 1, 0)
+};
+
+static const struct snd_kcontrol_new vout3_mix_controls[] = {
+ SOC_DAPM_SINGLE("DAC3 Switch", WM8770_OUTMUX2, 0, 1, 0),
+ SOC_DAPM_SINGLE("AUX3 Switch", WM8770_OUTMUX2, 1, 1, 0),
+ SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX2, 2, 1, 0)
+};
+
+static const struct snd_kcontrol_new vout4_mix_controls[] = {
+ SOC_DAPM_SINGLE("DAC4 Switch", WM8770_OUTMUX2, 3, 1, 0),
+ SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX2, 4, 1, 0)
+};
+
+static const struct snd_soc_dapm_widget wm8770_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("AUX1"),
+ SND_SOC_DAPM_INPUT("AUX2"),
+ SND_SOC_DAPM_INPUT("AUX3"),
+
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("AIN3"),
+ SND_SOC_DAPM_INPUT("AIN4"),
+ SND_SOC_DAPM_INPUT("AIN5"),
+ SND_SOC_DAPM_INPUT("AIN6"),
+ SND_SOC_DAPM_INPUT("AIN7"),
+ SND_SOC_DAPM_INPUT("AIN8"),
+
+ SND_SOC_DAPM_MUX("Capture Mux", WM8770_ADCMUX, 8, 1, &ain_mux),
+
+ SND_SOC_DAPM_ADC("ADC", "Capture", WM8770_PWDNCTRL, 1, 1),
+
+ SND_SOC_DAPM_DAC("DAC1", "Playback", WM8770_PWDNCTRL, 2, 1),
+ SND_SOC_DAPM_DAC("DAC2", "Playback", WM8770_PWDNCTRL, 3, 1),
+ SND_SOC_DAPM_DAC("DAC3", "Playback", WM8770_PWDNCTRL, 4, 1),
+ SND_SOC_DAPM_DAC("DAC4", "Playback", WM8770_PWDNCTRL, 5, 1),
+
+ SND_SOC_DAPM_SUPPLY("VOUT12 Supply", SND_SOC_NOPM, 0, 0,
+ vout12supply_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VOUT34 Supply", SND_SOC_NOPM, 0, 0,
+ vout34supply_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("VOUT1 Mixer", SND_SOC_NOPM, 0, 0,
+ vout1_mix_controls, ARRAY_SIZE(vout1_mix_controls)),
+ SND_SOC_DAPM_MIXER("VOUT2 Mixer", SND_SOC_NOPM, 0, 0,
+ vout2_mix_controls, ARRAY_SIZE(vout2_mix_controls)),
+ SND_SOC_DAPM_MIXER("VOUT3 Mixer", SND_SOC_NOPM, 0, 0,
+ vout3_mix_controls, ARRAY_SIZE(vout3_mix_controls)),
+ SND_SOC_DAPM_MIXER("VOUT4 Mixer", SND_SOC_NOPM, 0, 0,
+ vout4_mix_controls, ARRAY_SIZE(vout4_mix_controls)),
+
+ SND_SOC_DAPM_OUTPUT("VOUT1"),
+ SND_SOC_DAPM_OUTPUT("VOUT2"),
+ SND_SOC_DAPM_OUTPUT("VOUT3"),
+ SND_SOC_DAPM_OUTPUT("VOUT4")
+};
+
+static const struct snd_soc_dapm_route wm8770_intercon[] = {
+ { "Capture Mux", "AIN1", "AIN1" },
+ { "Capture Mux", "AIN2", "AIN2" },
+ { "Capture Mux", "AIN3", "AIN3" },
+ { "Capture Mux", "AIN4", "AIN4" },
+ { "Capture Mux", "AIN5", "AIN5" },
+ { "Capture Mux", "AIN6", "AIN6" },
+ { "Capture Mux", "AIN7", "AIN7" },
+ { "Capture Mux", "AIN8", "AIN8" },
+
+ { "ADC", NULL, "Capture Mux" },
+
+ { "VOUT1 Mixer", NULL, "VOUT12 Supply" },
+ { "VOUT1 Mixer", "DAC1 Switch", "DAC1" },
+ { "VOUT1 Mixer", "AUX1 Switch", "AUX1" },
+ { "VOUT1 Mixer", "Bypass Switch", "Capture Mux" },
+
+ { "VOUT2 Mixer", NULL, "VOUT12 Supply" },
+ { "VOUT2 Mixer", "DAC2 Switch", "DAC2" },
+ { "VOUT2 Mixer", "AUX2 Switch", "AUX2" },
+ { "VOUT2 Mixer", "Bypass Switch", "Capture Mux" },
+
+ { "VOUT3 Mixer", NULL, "VOUT34 Supply" },
+ { "VOUT3 Mixer", "DAC3 Switch", "DAC3" },
+ { "VOUT3 Mixer", "AUX3 Switch", "AUX3" },
+ { "VOUT3 Mixer", "Bypass Switch", "Capture Mux" },
+
+ { "VOUT4 Mixer", NULL, "VOUT34 Supply" },
+ { "VOUT4 Mixer", "DAC4 Switch", "DAC4" },
+ { "VOUT4 Mixer", "Bypass Switch", "Capture Mux" },
+
+ { "VOUT1", NULL, "VOUT1 Mixer" },
+ { "VOUT2", NULL, "VOUT2 Mixer" },
+ { "VOUT3", NULL, "VOUT3 Mixer" },
+ { "VOUT4", NULL, "VOUT4 Mixer" }
+};
+
+static int vout12supply_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, WM8770_OUTMUX1, 0x180, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, WM8770_OUTMUX1, 0x180, 0x180);
+ break;
+ }
+
+ return 0;
+}
+
+static int vout34supply_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, WM8770_OUTMUX2, 0x180, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, WM8770_OUTMUX2, 0x180, 0x180);
+ break;
+ }
+
+ return 0;
+}
+
+static int wm8770_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, WM8770_RESET, 0);
+}
+
+static int wm8770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec;
+ int iface, master;
+
+ codec = dai->codec;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ master = 0x100;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ master = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ iface = 0;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x2;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0xc;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x8;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8770_IFACECTRL, 0xf, iface);
+ snd_soc_update_bits(codec, WM8770_MSTRCTRL, 0x100, master);
+
+ return 0;
+}
+
+static const int mclk_ratios[] = {
+ 128,
+ 192,
+ 256,
+ 384,
+ 512,
+ 768
+};
+
+static int wm8770_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec;
+ struct wm8770_priv *wm8770;
+ int i;
+ int iface;
+ int shift;
+ int ratio;
+
+ codec = dai->codec;
+ wm8770 = snd_soc_codec_get_drvdata(codec);
+
+ iface = 0;
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface |= 0x10;
+ break;
+ case 24:
+ iface |= 0x20;
+ break;
+ case 32:
+ iface |= 0x30;
+ break;
+ }
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ i = 0;
+ shift = 4;
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ i = 2;
+ shift = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Only need to set MCLK/LRCLK ratio if we're master */
+ if (snd_soc_read(codec, WM8770_MSTRCTRL) & 0x100) {
+ for (; i < ARRAY_SIZE(mclk_ratios); ++i) {
+ ratio = wm8770->sysclk / params_rate(params);
+ if (ratio == mclk_ratios[i])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(mclk_ratios)) {
+ dev_err(codec->dev,
+ "Unable to configure MCLK ratio %d/%d\n",
+ wm8770->sysclk, params_rate(params));
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "MCLK is %dfs\n", mclk_ratios[i]);
+
+ snd_soc_update_bits(codec, WM8770_MSTRCTRL, 0x7 << shift,
+ i << shift);
+ }
+
+ snd_soc_update_bits(codec, WM8770_IFACECTRL, 0x30, iface);
+
+ return 0;
+}
+
+static int wm8770_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec;
+
+ codec = dai->codec;
+ return snd_soc_update_bits(codec, WM8770_DACMUTE, 0x10,
+ !!mute << 4);
+}
+
+static int wm8770_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec;
+ struct wm8770_priv *wm8770;
+
+ codec = dai->codec;
+ wm8770 = snd_soc_codec_get_drvdata(codec);
+ wm8770->sysclk = freq;
+ return 0;
+}
+
+static int wm8770_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+ struct wm8770_priv *wm8770;
+
+ wm8770 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ 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(wm8770->supplies),
+ wm8770->supplies);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ regcache_sync(wm8770->regmap);
+
+ /* global powerup */
+ snd_soc_write(codec, WM8770_PWDNCTRL, 0);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* global powerdown */
+ snd_soc_write(codec, WM8770_PWDNCTRL, 1);
+ regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies),
+ wm8770->supplies);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8770_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8770_dai_ops = {
+ .digital_mute = wm8770_mute,
+ .hw_params = wm8770_hw_params,
+ .set_fmt = wm8770_set_fmt,
+ .set_sysclk = wm8770_set_sysclk,
+};
+
+static struct snd_soc_dai_driver wm8770_dai = {
+ .name = "wm8770-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = WM8770_FORMATS
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = WM8770_FORMATS
+ },
+ .ops = &wm8770_dai_ops,
+ .symmetric_rates = 1
+};
+
+static int wm8770_probe(struct snd_soc_codec *codec)
+{
+ struct wm8770_priv *wm8770;
+ int ret;
+
+ wm8770 = snd_soc_codec_get_drvdata(codec);
+ wm8770->codec = codec;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies),
+ wm8770->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm8770_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ goto err_reg_enable;
+ }
+
+ /* latch the volume update bits */
+ snd_soc_update_bits(codec, WM8770_MSDIGVOL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8770_MSALGVOL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8770_VOUT1RVOL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8770_VOUT2RVOL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8770_VOUT3RVOL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8770_VOUT4RVOL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8770_DAC1RVOL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8770_DAC2RVOL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8770_DAC3RVOL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8770_DAC4RVOL, 0x100, 0x100);
+
+ /* mute all DACs */
+ snd_soc_update_bits(codec, WM8770_DACMUTE, 0x10, 0x10);
+
+err_reg_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies), wm8770->supplies);
+ return ret;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8770 = {
+ .probe = wm8770_probe,
+ .set_bias_level = wm8770_set_bias_level,
+ .idle_bias_off = true,
+
+ .controls = wm8770_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8770_snd_controls),
+ .dapm_widgets = wm8770_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8770_dapm_widgets),
+ .dapm_routes = wm8770_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8770_intercon),
+};
+
+static const struct of_device_id wm8770_of_match[] = {
+ { .compatible = "wlf,wm8770", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8770_of_match);
+
+static const struct regmap_config wm8770_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8770_RESET,
+
+ .reg_defaults = wm8770_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8770_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8770_volatile_reg,
+};
+
+static int wm8770_spi_probe(struct spi_device *spi)
+{
+ struct wm8770_priv *wm8770;
+ int ret, i;
+
+ wm8770 = devm_kzalloc(&spi->dev, sizeof(struct wm8770_priv),
+ GFP_KERNEL);
+ if (!wm8770)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++)
+ wm8770->supplies[i].supply = wm8770_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8770->supplies),
+ wm8770->supplies);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ wm8770->disable_nb[0].notifier_call = wm8770_regulator_event_0;
+ wm8770->disable_nb[1].notifier_call = wm8770_regulator_event_1;
+ wm8770->disable_nb[2].notifier_call = wm8770_regulator_event_2;
+
+ /* This should really be moved into the regulator core */
+ for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) {
+ ret = regulator_register_notifier(wm8770->supplies[i].consumer,
+ &wm8770->disable_nb[i]);
+ if (ret) {
+ dev_err(&spi->dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ }
+ }
+
+ wm8770->regmap = devm_regmap_init_spi(spi, &wm8770_regmap);
+ if (IS_ERR(wm8770->regmap))
+ return PTR_ERR(wm8770->regmap);
+
+ spi_set_drvdata(spi, wm8770);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8770, &wm8770_dai, 1);
+
+ return ret;
+}
+
+static int wm8770_spi_remove(struct spi_device *spi)
+{
+ struct wm8770_priv *wm8770 = spi_get_drvdata(spi);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8770->supplies); ++i)
+ regulator_unregister_notifier(wm8770->supplies[i].consumer,
+ &wm8770->disable_nb[i]);
+
+ snd_soc_unregister_codec(&spi->dev);
+
+ return 0;
+}
+
+static struct spi_driver wm8770_spi_driver = {
+ .driver = {
+ .name = "wm8770",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8770_of_match,
+ },
+ .probe = wm8770_spi_probe,
+ .remove = wm8770_spi_remove
+};
+
+module_spi_driver(wm8770_spi_driver);
+
+MODULE_DESCRIPTION("ASoC WM8770 driver");
+MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8770.h b/kernel/sound/soc/codecs/wm8770.h
new file mode 100644
index 000000000..5f1b3bda6
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8770.h
@@ -0,0 +1,51 @@
+/*
+ * wm8770.h -- WM8770 ASoC driver
+ *
+ * Copyright 2010 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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 _WM8770_H
+#define _WM8770_H
+
+/* Registers */
+#define WM8770_VOUT1LVOL 0
+#define WM8770_VOUT1RVOL 0x1
+#define WM8770_VOUT2LVOL 0x2
+#define WM8770_VOUT2RVOL 0x3
+#define WM8770_VOUT3LVOL 0x4
+#define WM8770_VOUT3RVOL 0x5
+#define WM8770_VOUT4LVOL 0x6
+#define WM8770_VOUT4RVOL 0x7
+#define WM8770_MSALGVOL 0x8
+#define WM8770_DAC1LVOL 0x9
+#define WM8770_DAC1RVOL 0xa
+#define WM8770_DAC2LVOL 0xb
+#define WM8770_DAC2RVOL 0xc
+#define WM8770_DAC3LVOL 0xd
+#define WM8770_DAC3RVOL 0xe
+#define WM8770_DAC4LVOL 0xf
+#define WM8770_DAC4RVOL 0x10
+#define WM8770_MSDIGVOL 0x11
+#define WM8770_DACPHASE 0x12
+#define WM8770_DACCTRL1 0x13
+#define WM8770_DACMUTE 0x14
+#define WM8770_DACCTRL2 0x15
+#define WM8770_IFACECTRL 0x16
+#define WM8770_MSTRCTRL 0x17
+#define WM8770_PWDNCTRL 0x18
+#define WM8770_ADCLCTRL 0x19
+#define WM8770_ADCRCTRL 0x1a
+#define WM8770_ADCMUX 0x1b
+#define WM8770_OUTMUX1 0x1c
+#define WM8770_OUTMUX2 0x1d
+#define WM8770_RESET 0x31
+
+#define WM8770_CACHEREGNUM 0x20
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8776.c b/kernel/sound/soc/codecs/wm8776.c
new file mode 100644
index 000000000..c13050b77
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8776.c
@@ -0,0 +1,583 @@
+/*
+ * wm8776.c -- WM8776 ALSA SoC Audio driver
+ *
+ * Copyright 2009-12 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.
+ *
+ * TODO: Input ALC/limiter support
+ */
+
+#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/of_device.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.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 "wm8776.h"
+
+enum wm8776_chip_type {
+ WM8775 = 1,
+ WM8776,
+};
+
+/* codec private data */
+struct wm8776_priv {
+ struct regmap *regmap;
+ int sysclk[2];
+};
+
+static const struct reg_default wm8776_reg_defaults[] = {
+ { 0, 0x79 },
+ { 1, 0x79 },
+ { 2, 0x79 },
+ { 3, 0xff },
+ { 4, 0xff },
+ { 5, 0xff },
+ { 6, 0x00 },
+ { 7, 0x90 },
+ { 8, 0x00 },
+ { 9, 0x00 },
+ { 10, 0x22 },
+ { 11, 0x22 },
+ { 12, 0x22 },
+ { 13, 0x08 },
+ { 14, 0xcf },
+ { 15, 0xcf },
+ { 16, 0x7b },
+ { 17, 0x00 },
+ { 18, 0x32 },
+ { 19, 0x00 },
+ { 20, 0xa6 },
+ { 21, 0x01 },
+ { 22, 0x01 },
+};
+
+static bool wm8776_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8776_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int wm8776_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, WM8776_RESET, 0);
+}
+
+static const DECLARE_TLV_DB_SCALE(hp_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1);
+
+static const struct snd_kcontrol_new wm8776_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8776_HPLVOL, WM8776_HPRVOL,
+ 0, 127, 0, hp_tlv),
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8776_DACLVOL, WM8776_DACRVOL,
+ 0, 255, 0, dac_tlv),
+SOC_SINGLE("Digital Playback ZC Switch", WM8776_DACCTRL1, 0, 1, 0),
+
+SOC_SINGLE("Deemphasis Switch", WM8776_DACCTRL2, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("Capture Volume", WM8776_ADCLVOL, WM8776_ADCRVOL,
+ 0, 255, 0, adc_tlv),
+SOC_DOUBLE("Capture Switch", WM8776_ADCMUX, 7, 6, 1, 1),
+SOC_DOUBLE_R("Capture ZC Switch", WM8776_ADCLVOL, WM8776_ADCRVOL, 8, 1, 0),
+SOC_SINGLE("Capture HPF Switch", WM8776_ADCIFCTRL, 8, 1, 1),
+};
+
+static const struct snd_kcontrol_new inmix_controls[] = {
+SOC_DAPM_SINGLE("AIN1 Switch", WM8776_ADCMUX, 0, 1, 0),
+SOC_DAPM_SINGLE("AIN2 Switch", WM8776_ADCMUX, 1, 1, 0),
+SOC_DAPM_SINGLE("AIN3 Switch", WM8776_ADCMUX, 2, 1, 0),
+SOC_DAPM_SINGLE("AIN4 Switch", WM8776_ADCMUX, 3, 1, 0),
+SOC_DAPM_SINGLE("AIN5 Switch", WM8776_ADCMUX, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new outmix_controls[] = {
+SOC_DAPM_SINGLE("DAC Switch", WM8776_OUTMUX, 0, 1, 0),
+SOC_DAPM_SINGLE("AUX Switch", WM8776_OUTMUX, 1, 1, 0),
+SOC_DAPM_SINGLE("Bypass Switch", WM8776_OUTMUX, 2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8776_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AUX"),
+
+SND_SOC_DAPM_INPUT("AIN1"),
+SND_SOC_DAPM_INPUT("AIN2"),
+SND_SOC_DAPM_INPUT("AIN3"),
+SND_SOC_DAPM_INPUT("AIN4"),
+SND_SOC_DAPM_INPUT("AIN5"),
+
+SND_SOC_DAPM_MIXER("Input Mixer", WM8776_PWRDOWN, 6, 1,
+ inmix_controls, ARRAY_SIZE(inmix_controls)),
+
+SND_SOC_DAPM_ADC("ADC", "Capture", WM8776_PWRDOWN, 1, 1),
+SND_SOC_DAPM_DAC("DAC", "Playback", WM8776_PWRDOWN, 2, 1),
+
+SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
+ outmix_controls, ARRAY_SIZE(outmix_controls)),
+
+SND_SOC_DAPM_PGA("Headphone PGA", WM8776_PWRDOWN, 3, 1, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("VOUT"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+};
+
+static const struct snd_soc_dapm_route routes[] = {
+ { "Input Mixer", "AIN1 Switch", "AIN1" },
+ { "Input Mixer", "AIN2 Switch", "AIN2" },
+ { "Input Mixer", "AIN3 Switch", "AIN3" },
+ { "Input Mixer", "AIN4 Switch", "AIN4" },
+ { "Input Mixer", "AIN5 Switch", "AIN5" },
+
+ { "ADC", NULL, "Input Mixer" },
+
+ { "Output Mixer", "DAC Switch", "DAC" },
+ { "Output Mixer", "AUX Switch", "AUX" },
+ { "Output Mixer", "Bypass Switch", "Input Mixer" },
+
+ { "VOUT", NULL, "Output Mixer" },
+
+ { "Headphone PGA", NULL, "Output Mixer" },
+
+ { "HPOUTL", NULL, "Headphone PGA" },
+ { "HPOUTR", NULL, "Headphone PGA" },
+};
+
+static int wm8776_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int reg, iface, master;
+
+ switch (dai->driver->id) {
+ case WM8776_DAI_DAC:
+ reg = WM8776_DACIFCTRL;
+ master = 0x80;
+ break;
+ case WM8776_DAI_ADC:
+ reg = WM8776_ADCIFCTRL;
+ master = 0x100;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ iface = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ master = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x00c;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x008;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x004;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Finally, write out the values */
+ snd_soc_update_bits(codec, reg, 0xf, iface);
+ snd_soc_update_bits(codec, WM8776_MSTRCTRL, 0x180, master);
+
+ return 0;
+}
+
+static int mclk_ratios[] = {
+ 128,
+ 192,
+ 256,
+ 384,
+ 512,
+ 768,
+};
+
+static int wm8776_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec);
+ int iface_reg, iface;
+ int ratio_shift, master;
+ int i;
+
+ switch (dai->driver->id) {
+ case WM8776_DAI_DAC:
+ iface_reg = WM8776_DACIFCTRL;
+ master = 0x80;
+ ratio_shift = 4;
+ break;
+ case WM8776_DAI_ADC:
+ iface_reg = WM8776_ADCIFCTRL;
+ master = 0x100;
+ ratio_shift = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set word length */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ iface = 0;
+ break;
+ case 20:
+ iface = 0x10;
+ break;
+ case 24:
+ iface = 0x20;
+ break;
+ case 32:
+ iface = 0x30;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported sample size: %i\n",
+ snd_pcm_format_width(params_format(params)));
+ return -EINVAL;
+ }
+
+ /* Only need to set MCLK/LRCLK ratio if we're master */
+ if (snd_soc_read(codec, WM8776_MSTRCTRL) & master) {
+ for (i = 0; i < ARRAY_SIZE(mclk_ratios); i++) {
+ if (wm8776->sysclk[dai->driver->id] / params_rate(params)
+ == mclk_ratios[i])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(mclk_ratios)) {
+ dev_err(codec->dev,
+ "Unable to configure MCLK ratio %d/%d\n",
+ wm8776->sysclk[dai->driver->id], params_rate(params));
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "MCLK is %dfs\n", mclk_ratios[i]);
+
+ snd_soc_update_bits(codec, WM8776_MSTRCTRL,
+ 0x7 << ratio_shift, i << ratio_shift);
+ } else {
+ dev_dbg(codec->dev, "DAI in slave mode\n");
+ }
+
+ snd_soc_update_bits(codec, iface_reg, 0x30, iface);
+
+ return 0;
+}
+
+static int wm8776_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ return snd_soc_write(codec, WM8776_DACMUTE, !!mute);
+}
+
+static int wm8776_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec);
+
+ if (WARN_ON(dai->driver->id >= ARRAY_SIZE(wm8776->sysclk)))
+ return -EINVAL;
+
+ wm8776->sysclk[dai->driver->id] = freq;
+
+ return 0;
+}
+
+static int wm8776_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ regcache_sync(wm8776->regmap);
+
+ /* Disable the global powerdown; DAPM does the rest */
+ snd_soc_update_bits(codec, WM8776_PWRDOWN, 1, 0);
+ }
+
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, WM8776_PWRDOWN, 1, 1);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8776_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8776_dac_ops = {
+ .digital_mute = wm8776_mute,
+ .hw_params = wm8776_hw_params,
+ .set_fmt = wm8776_set_fmt,
+ .set_sysclk = wm8776_set_sysclk,
+};
+
+static const struct snd_soc_dai_ops wm8776_adc_ops = {
+ .hw_params = wm8776_hw_params,
+ .set_fmt = wm8776_set_fmt,
+ .set_sysclk = wm8776_set_sysclk,
+};
+
+static struct snd_soc_dai_driver wm8776_dai[] = {
+ {
+ .name = "wm8776-hifi-playback",
+ .id = WM8776_DAI_DAC,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 32000,
+ .rate_max = 192000,
+ .formats = WM8776_FORMATS,
+ },
+ .ops = &wm8776_dac_ops,
+ },
+ {
+ .name = "wm8776-hifi-capture",
+ .id = WM8776_DAI_ADC,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 32000,
+ .rate_max = 96000,
+ .formats = WM8776_FORMATS,
+ },
+ .ops = &wm8776_adc_ops,
+ },
+};
+
+static int wm8776_probe(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+
+ ret = wm8776_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
+
+ /* Latch the update bits; right channel only since we always
+ * update both. */
+ snd_soc_update_bits(codec, WM8776_HPRVOL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8776_DACRVOL, 0x100, 0x100);
+
+ return ret;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8776 = {
+ .probe = wm8776_probe,
+ .set_bias_level = wm8776_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8776_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8776_snd_controls),
+ .dapm_widgets = wm8776_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8776_dapm_widgets),
+ .dapm_routes = routes,
+ .num_dapm_routes = ARRAY_SIZE(routes),
+};
+
+static const struct of_device_id wm8776_of_match[] = {
+ { .compatible = "wlf,wm8776", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8776_of_match);
+
+static const struct regmap_config wm8776_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8776_RESET,
+
+ .reg_defaults = wm8776_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8776_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8776_volatile,
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8776_spi_probe(struct spi_device *spi)
+{
+ struct wm8776_priv *wm8776;
+ int ret;
+
+ wm8776 = devm_kzalloc(&spi->dev, sizeof(struct wm8776_priv),
+ GFP_KERNEL);
+ if (wm8776 == NULL)
+ return -ENOMEM;
+
+ wm8776->regmap = devm_regmap_init_spi(spi, &wm8776_regmap);
+ if (IS_ERR(wm8776->regmap))
+ return PTR_ERR(wm8776->regmap);
+
+ spi_set_drvdata(spi, wm8776);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai));
+
+ return ret;
+}
+
+static int wm8776_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver wm8776_spi_driver = {
+ .driver = {
+ .name = "wm8776",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8776_of_match,
+ },
+ .probe = wm8776_spi_probe,
+ .remove = wm8776_spi_remove,
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8776_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8776_priv *wm8776;
+ int ret;
+
+ wm8776 = devm_kzalloc(&i2c->dev, sizeof(struct wm8776_priv),
+ GFP_KERNEL);
+ if (wm8776 == NULL)
+ return -ENOMEM;
+
+ wm8776->regmap = devm_regmap_init_i2c(i2c, &wm8776_regmap);
+ if (IS_ERR(wm8776->regmap))
+ return PTR_ERR(wm8776->regmap);
+
+ i2c_set_clientdata(i2c, wm8776);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai));
+
+ return ret;
+}
+
+static int wm8776_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8776_i2c_id[] = {
+ { "wm8775", WM8775 },
+ { "wm8776", WM8776 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id);
+
+static struct i2c_driver wm8776_i2c_driver = {
+ .driver = {
+ .name = "wm8776",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8776_of_match,
+ },
+ .probe = wm8776_i2c_probe,
+ .remove = wm8776_i2c_remove,
+ .id_table = wm8776_i2c_id,
+};
+#endif
+
+static int __init wm8776_modinit(void)
+{
+ int ret = 0;
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8776_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8776 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8776_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8776 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(wm8776_modinit);
+
+static void __exit wm8776_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8776_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8776_spi_driver);
+#endif
+}
+module_exit(wm8776_exit);
+
+MODULE_DESCRIPTION("ASoC WM8776 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8776.h b/kernel/sound/soc/codecs/wm8776.h
new file mode 100644
index 000000000..4cf1c8e0b
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8776.h
@@ -0,0 +1,48 @@
+/*
+ * wm8776.h -- WM8776 ASoC driver
+ *
+ * Copyright 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8776_H
+#define _WM8776_H
+
+/* Registers */
+
+#define WM8776_HPLVOL 0x00
+#define WM8776_HPRVOL 0x01
+#define WM8776_HPMASTER 0x02
+#define WM8776_DACLVOL 0x03
+#define WM8776_DACRVOL 0x04
+#define WM8776_DACMASTER 0x05
+#define WM8776_PHASESWAP 0x06
+#define WM8776_DACCTRL1 0x07
+#define WM8776_DACMUTE 0x08
+#define WM8776_DACCTRL2 0x09
+#define WM8776_DACIFCTRL 0x0a
+#define WM8776_ADCIFCTRL 0x0b
+#define WM8776_MSTRCTRL 0x0c
+#define WM8776_PWRDOWN 0x0d
+#define WM8776_ADCLVOL 0x0e
+#define WM8776_ADCRVOL 0x0f
+#define WM8776_ALCCTRL1 0x10
+#define WM8776_ALCCTRL2 0x11
+#define WM8776_ALCCTRL3 0x12
+#define WM8776_NOISEGATE 0x13
+#define WM8776_LIMITER 0x14
+#define WM8776_ADCMUX 0x15
+#define WM8776_OUTMUX 0x16
+#define WM8776_RESET 0x17
+
+#define WM8776_CACHEREGNUM 0x17
+
+#define WM8776_DAI_DAC 0
+#define WM8776_DAI_ADC 1
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8782.c b/kernel/sound/soc/codecs/wm8782.c
new file mode 100644
index 000000000..fb55fd845
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8782.c
@@ -0,0 +1,84 @@
+/*
+ * sound/soc/codecs/wm8782.c
+ * simple, strap-pin configured 24bit 2ch ADC
+ *
+ * Copyright: 2011 Raumfeld GmbH
+ * Author: Johannes Stezenbach <js@sig21.net>
+ *
+ * based on ad73311.c
+ * Copyright: Analog Device 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>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget wm8782_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+};
+
+static const struct snd_soc_dapm_route wm8782_dapm_routes[] = {
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
+};
+
+static struct snd_soc_dai_driver wm8782_dai = {
+ .name = "wm8782",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ /* For configurations with FSAMPEN=0 */
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8782 = {
+ .dapm_widgets = wm8782_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8782_dapm_widgets),
+ .dapm_routes = wm8782_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8782_dapm_routes),
+};
+
+static int wm8782_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_wm8782, &wm8782_dai, 1);
+}
+
+static int wm8782_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver wm8782_codec_driver = {
+ .driver = {
+ .name = "wm8782",
+ },
+ .probe = wm8782_probe,
+ .remove = wm8782_remove,
+};
+
+module_platform_driver(wm8782_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM8782 driver");
+MODULE_AUTHOR("Johannes Stezenbach <js@sig21.net>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8804-i2c.c b/kernel/sound/soc/codecs/wm8804-i2c.c
new file mode 100644
index 000000000..6596f5f3a
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8804-i2c.c
@@ -0,0 +1,65 @@
+/*
+ * wm8804-i2c.c -- WM8804 S/PDIF transceiver driver - I2C
+ *
+ * Copyright 2015 Cirrus Logic Inc
+ *
+ * Author: Charles Keepax <ckeepax@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/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#include "wm8804.h"
+
+static int wm8804_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(i2c, &wm8804_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return wm8804_probe(&i2c->dev, regmap);
+}
+
+static int wm8804_i2c_remove(struct i2c_client *i2c)
+{
+ wm8804_remove(&i2c->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8804_i2c_id[] = {
+ { "wm8804", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id);
+
+static const struct of_device_id wm8804_of_match[] = {
+ { .compatible = "wlf,wm8804", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8804_of_match);
+
+static struct i2c_driver wm8804_i2c_driver = {
+ .driver = {
+ .name = "wm8804",
+ .owner = THIS_MODULE,
+ .pm = &wm8804_pm,
+ .of_match_table = wm8804_of_match,
+ },
+ .probe = wm8804_i2c_probe,
+ .remove = wm8804_i2c_remove,
+ .id_table = wm8804_i2c_id
+};
+
+module_i2c_driver(wm8804_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8804 driver - I2C");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8804-spi.c b/kernel/sound/soc/codecs/wm8804-spi.c
new file mode 100644
index 000000000..407a3cf39
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8804-spi.c
@@ -0,0 +1,57 @@
+/*
+ * wm8804-spi.c -- WM8804 S/PDIF transceiver driver - SPI
+ *
+ * Copyright 2015 Cirrus Logic Inc
+ *
+ * Author: Charles Keepax <ckeepax@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/init.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "wm8804.h"
+
+static int wm8804_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &wm8804_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return wm8804_probe(&spi->dev, regmap);
+}
+
+static int wm8804_spi_remove(struct spi_device *spi)
+{
+ wm8804_remove(&spi->dev);
+ return 0;
+}
+
+static const struct of_device_id wm8804_of_match[] = {
+ { .compatible = "wlf,wm8804", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8804_of_match);
+
+static struct spi_driver wm8804_spi_driver = {
+ .driver = {
+ .name = "wm8804",
+ .owner = THIS_MODULE,
+ .pm = &wm8804_pm,
+ .of_match_table = wm8804_of_match,
+ },
+ .probe = wm8804_spi_probe,
+ .remove = wm8804_spi_remove
+};
+
+module_spi_driver(wm8804_spi_driver);
+
+MODULE_DESCRIPTION("ASoC WM8804 driver - SPI");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8804.c b/kernel/sound/soc/codecs/wm8804.c
new file mode 100644
index 000000000..1e403f67c
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8804.c
@@ -0,0 +1,731 @@
+/*
+ * wm8804.c -- WM8804 S/PDIF transceiver driver
+ *
+ * Copyright 2010-11 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/gpio/consumer.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.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 <sound/soc-dapm.h>
+
+#include "wm8804.h"
+
+#define WM8804_NUM_SUPPLIES 2
+static const char *wm8804_supply_names[WM8804_NUM_SUPPLIES] = {
+ "PVDD",
+ "DVDD"
+};
+
+static const struct reg_default wm8804_reg_defaults[] = {
+ { 3, 0x21 }, /* R3 - PLL1 */
+ { 4, 0xFD }, /* R4 - PLL2 */
+ { 5, 0x36 }, /* R5 - PLL3 */
+ { 6, 0x07 }, /* R6 - PLL4 */
+ { 7, 0x16 }, /* R7 - PLL5 */
+ { 8, 0x18 }, /* R8 - PLL6 */
+ { 9, 0xFF }, /* R9 - SPDMODE */
+ { 10, 0x00 }, /* R10 - INTMASK */
+ { 18, 0x00 }, /* R18 - SPDTX1 */
+ { 19, 0x00 }, /* R19 - SPDTX2 */
+ { 20, 0x00 }, /* R20 - SPDTX3 */
+ { 21, 0x71 }, /* R21 - SPDTX4 */
+ { 22, 0x0B }, /* R22 - SPDTX5 */
+ { 23, 0x70 }, /* R23 - GPO0 */
+ { 24, 0x57 }, /* R24 - GPO1 */
+ { 26, 0x42 }, /* R26 - GPO2 */
+ { 27, 0x06 }, /* R27 - AIFTX */
+ { 28, 0x06 }, /* R28 - AIFRX */
+ { 29, 0x80 }, /* R29 - SPDRX1 */
+ { 30, 0x07 }, /* R30 - PWRDN */
+};
+
+struct wm8804_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES];
+ struct notifier_block disable_nb[WM8804_NUM_SUPPLIES];
+ int mclk_div;
+
+ struct gpio_desc *reset;
+
+ int aif_pwr;
+};
+
+static int txsrc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+static int wm8804_aif_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+/*
+ * We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define WM8804_REGULATOR_EVENT(n) \
+static int wm8804_regulator_event_##n(struct notifier_block *nb, \
+ unsigned long event, void *data) \
+{ \
+ struct wm8804_priv *wm8804 = container_of(nb, struct wm8804_priv, \
+ disable_nb[n]); \
+ if (event & REGULATOR_EVENT_DISABLE) { \
+ regcache_mark_dirty(wm8804->regmap); \
+ } \
+ return 0; \
+}
+
+WM8804_REGULATOR_EVENT(0)
+WM8804_REGULATOR_EVENT(1)
+
+static const char *txsrc_text[] = { "S/PDIF RX", "AIF" };
+static const SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text);
+
+static const struct snd_kcontrol_new wm8804_tx_source_mux[] = {
+ SOC_DAPM_ENUM_EXT("Input Source", txsrc,
+ snd_soc_dapm_get_enum_double, txsrc_put),
+};
+
+static const struct snd_soc_dapm_widget wm8804_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("SPDIF Out"),
+SND_SOC_DAPM_INPUT("SPDIF In"),
+
+SND_SOC_DAPM_PGA("SPDIFTX", WM8804_PWRDN, 2, 1, NULL, 0),
+SND_SOC_DAPM_PGA("SPDIFRX", WM8804_PWRDN, 1, 1, NULL, 0),
+
+SND_SOC_DAPM_MUX("Tx Source", SND_SOC_NOPM, 6, 0, wm8804_tx_source_mux),
+
+SND_SOC_DAPM_AIF_OUT_E("AIFTX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_AIF_IN_E("AIFRX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route wm8804_dapm_routes[] = {
+ { "AIFRX", NULL, "Playback" },
+ { "Tx Source", "AIF", "AIFRX" },
+
+ { "SPDIFRX", NULL, "SPDIF In" },
+ { "Tx Source", "S/PDIF RX", "SPDIFRX" },
+
+ { "SPDIFTX", NULL, "Tx Source" },
+ { "SPDIF Out", NULL, "SPDIFTX" },
+
+ { "AIFTX", NULL, "SPDIFRX" },
+ { "Capture", NULL, "AIFTX" },
+};
+
+static int wm8804_aif_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm8804_priv *wm8804 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* power up the aif */
+ if (!wm8804->aif_pwr)
+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x0);
+ wm8804->aif_pwr++;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* power down only both paths are disabled */
+ wm8804->aif_pwr--;
+ if (!wm8804->aif_pwr)
+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x10);
+ break;
+ }
+
+ return 0;
+}
+
+static int txsrc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val = ucontrol->value.enumerated.item[0] << e->shift_l;
+ unsigned int mask = 1 << e->shift_l;
+ unsigned int txpwr;
+
+ if (val != 0 && val != mask)
+ return -EINVAL;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (snd_soc_test_bits(codec, e->reg, mask, val)) {
+ /* save the current power state of the transmitter */
+ txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4;
+
+ /* power down the transmitter */
+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4);
+
+ /* set the tx source */
+ snd_soc_update_bits(codec, e->reg, mask, val);
+
+ /* restore the transmitter's configuration */
+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr);
+ }
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
+static bool wm8804_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8804_RST_DEVID1:
+ case WM8804_DEVID2:
+ case WM8804_DEVREV:
+ case WM8804_INTSTAT:
+ case WM8804_SPDSTAT:
+ case WM8804_RXCHAN1:
+ case WM8804_RXCHAN2:
+ case WM8804_RXCHAN3:
+ case WM8804_RXCHAN4:
+ case WM8804_RXCHAN5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int wm8804_soft_reset(struct wm8804_priv *wm8804)
+{
+ return regmap_write(wm8804->regmap, WM8804_RST_DEVID1, 0x0);
+}
+
+static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec;
+ u16 format, master, bcp, lrp;
+
+ codec = dai->codec;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = 0x2;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ format = 0x0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = 0x1;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ format = 0x3;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown dai format\n");
+ return -EINVAL;
+ }
+
+ /* set data format */
+ snd_soc_update_bits(codec, WM8804_AIFTX, 0x3, format);
+ snd_soc_update_bits(codec, WM8804_AIFRX, 0x3, format);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ master = 0;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown master/slave configuration\n");
+ return -EINVAL;
+ }
+
+ /* set master/slave mode */
+ snd_soc_update_bits(codec, WM8804_AIFRX, 0x40, master << 6);
+
+ bcp = lrp = 0;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bcp = lrp = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bcp = 1;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrp = 1;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown polarity configuration\n");
+ return -EINVAL;
+ }
+
+ /* set frame inversion */
+ snd_soc_update_bits(codec, WM8804_AIFTX, 0x10 | 0x20,
+ (bcp << 4) | (lrp << 5));
+ snd_soc_update_bits(codec, WM8804_AIFRX, 0x10 | 0x20,
+ (bcp << 4) | (lrp << 5));
+ return 0;
+}
+
+static int wm8804_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec;
+ u16 blen;
+
+ codec = dai->codec;
+
+ switch (params_width(params)) {
+ case 16:
+ blen = 0x0;
+ break;
+ case 20:
+ blen = 0x1;
+ break;
+ case 24:
+ blen = 0x2;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported word length: %u\n",
+ params_width(params));
+ return -EINVAL;
+ }
+
+ /* set word length */
+ snd_soc_update_bits(codec, WM8804_AIFTX, 0xc, blen << 2);
+ snd_soc_update_bits(codec, WM8804_AIFRX, 0xc, blen << 2);
+
+ return 0;
+}
+
+struct pll_div {
+ u32 prescale:1;
+ u32 mclkdiv:1;
+ u32 freqmode:2;
+ u32 n:4;
+ u32 k:22;
+};
+
+/* PLL rate to output rate divisions */
+static struct {
+ unsigned int div;
+ unsigned int freqmode;
+ unsigned int mclkdiv;
+} post_table[] = {
+ { 2, 0, 0 },
+ { 4, 0, 1 },
+ { 4, 1, 0 },
+ { 8, 1, 1 },
+ { 8, 2, 0 },
+ { 16, 2, 1 },
+ { 12, 3, 0 },
+ { 24, 3, 1 }
+};
+
+#define FIXED_PLL_SIZE ((1ULL << 22) * 10)
+static int pll_factors(struct pll_div *pll_div, unsigned int target,
+ unsigned int source, unsigned int mclk_div)
+{
+ u64 Kpart;
+ unsigned long int K, Ndiv, Nmod, tmp;
+ int i;
+
+ /*
+ * Scale the output frequency up; the PLL should run in the
+ * region of 90-100MHz.
+ */
+ for (i = 0; i < ARRAY_SIZE(post_table); i++) {
+ tmp = target * post_table[i].div;
+ if ((tmp >= 90000000 && tmp <= 100000000) &&
+ (mclk_div == post_table[i].mclkdiv)) {
+ pll_div->freqmode = post_table[i].freqmode;
+ pll_div->mclkdiv = post_table[i].mclkdiv;
+ target *= post_table[i].div;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(post_table)) {
+ pr_err("%s: Unable to scale output frequency: %uHz\n",
+ __func__, target);
+ return -EINVAL;
+ }
+
+ pll_div->prescale = 0;
+ Ndiv = target / source;
+ if (Ndiv < 5) {
+ source >>= 1;
+ pll_div->prescale = 1;
+ Ndiv = target / source;
+ }
+
+ if (Ndiv < 5 || Ndiv > 13) {
+ pr_err("%s: WM8804 N value is not within the recommended range: %lu\n",
+ __func__, Ndiv);
+ return -EINVAL;
+ }
+ pll_div->n = Ndiv;
+
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (u64)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xffffffff;
+ if ((K % 10) >= 5)
+ K += 5;
+ K /= 10;
+ pll_div->k = K;
+
+ return 0;
+}
+
+static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8804_priv *wm8804 = snd_soc_codec_get_drvdata(codec);
+ bool change;
+
+ if (!freq_in || !freq_out) {
+ /* disable the PLL */
+ regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN,
+ 0x1, 0x1, &change);
+ if (change)
+ pm_runtime_put(wm8804->dev);
+ } else {
+ int ret;
+ struct pll_div pll_div;
+
+ ret = pll_factors(&pll_div, freq_out, freq_in,
+ wm8804->mclk_div);
+ if (ret)
+ return ret;
+
+ /* power down the PLL before reprogramming it */
+ regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN,
+ 0x1, 0x1, &change);
+ if (!change)
+ pm_runtime_get_sync(wm8804->dev);
+
+ /* set PLLN and PRESCALE */
+ snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10,
+ pll_div.n | (pll_div.prescale << 4));
+ /* set mclkdiv and freqmode */
+ snd_soc_update_bits(codec, WM8804_PLL5, 0x3 | 0x8,
+ pll_div.freqmode | (pll_div.mclkdiv << 3));
+ /* set PLLK */
+ snd_soc_write(codec, WM8804_PLL1, pll_div.k & 0xff);
+ snd_soc_write(codec, WM8804_PLL2, (pll_div.k >> 8) & 0xff);
+ snd_soc_write(codec, WM8804_PLL3, pll_div.k >> 16);
+
+ /* power up the PLL */
+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0);
+ }
+
+ return 0;
+}
+
+static int wm8804_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec;
+
+ codec = dai->codec;
+
+ switch (clk_id) {
+ case WM8804_TX_CLKSRC_MCLK:
+ if ((freq >= 10000000 && freq <= 14400000)
+ || (freq >= 16280000 && freq <= 27000000))
+ snd_soc_update_bits(codec, WM8804_PLL6, 0x80, 0x80);
+ else {
+ dev_err(dai->dev, "OSCCLOCK is not within the "
+ "recommended range: %uHz\n", freq);
+ return -EINVAL;
+ }
+ break;
+ case WM8804_TX_CLKSRC_PLL:
+ snd_soc_update_bits(codec, WM8804_PLL6, 0x80, 0);
+ break;
+ case WM8804_CLKOUT_SRC_CLK1:
+ snd_soc_update_bits(codec, WM8804_PLL6, 0x8, 0);
+ break;
+ case WM8804_CLKOUT_SRC_OSCCLK:
+ snd_soc_update_bits(codec, WM8804_PLL6, 0x8, 0x8);
+ break;
+ default:
+ dev_err(dai->dev, "Unknown clock source: %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8804_set_clkdiv(struct snd_soc_dai *dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec;
+ struct wm8804_priv *wm8804;
+
+ codec = dai->codec;
+ switch (div_id) {
+ case WM8804_CLKOUT_DIV:
+ snd_soc_update_bits(codec, WM8804_PLL5, 0x30,
+ (div & 0x3) << 4);
+ break;
+ case WM8804_MCLK_DIV:
+ wm8804 = snd_soc_codec_get_drvdata(codec);
+ wm8804->mclk_div = div;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown clock divider: %d\n", div_id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wm8804_dai_ops = {
+ .hw_params = wm8804_hw_params,
+ .set_fmt = wm8804_set_fmt,
+ .set_sysclk = wm8804_set_sysclk,
+ .set_clkdiv = wm8804_set_clkdiv,
+ .set_pll = wm8804_set_pll
+};
+
+#define WM8804_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define WM8804_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+static struct snd_soc_dai_driver wm8804_dai = {
+ .name = "wm8804-spdif",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8804_RATES,
+ .formats = WM8804_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8804_RATES,
+ .formats = WM8804_FORMATS,
+ },
+ .ops = &wm8804_dai_ops,
+ .symmetric_rates = 1
+};
+
+static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = {
+ .idle_bias_off = true,
+
+ .dapm_widgets = wm8804_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets),
+ .dapm_routes = wm8804_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8804_dapm_routes),
+};
+
+const struct regmap_config wm8804_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = WM8804_MAX_REGISTER,
+ .volatile_reg = wm8804_volatile,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm8804_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8804_reg_defaults),
+};
+EXPORT_SYMBOL_GPL(wm8804_regmap_config);
+
+int wm8804_probe(struct device *dev, struct regmap *regmap)
+{
+ struct wm8804_priv *wm8804;
+ unsigned int id1, id2;
+ int i, ret;
+
+ wm8804 = devm_kzalloc(dev, sizeof(*wm8804), GFP_KERNEL);
+ if (!wm8804)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, wm8804);
+
+ wm8804->dev = dev;
+ wm8804->regmap = regmap;
+
+ wm8804->reset = devm_gpiod_get_optional(dev, "wlf,reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(wm8804->reset)) {
+ ret = PTR_ERR(wm8804->reset);
+ dev_err(dev, "Failed to get reset line: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++)
+ wm8804->supplies[i].supply = wm8804_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8804->supplies),
+ wm8804->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ wm8804->disable_nb[0].notifier_call = wm8804_regulator_event_0;
+ wm8804->disable_nb[1].notifier_call = wm8804_regulator_event_1;
+
+ /* This should really be moved into the regulator core */
+ for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) {
+ struct regulator *regulator = wm8804->supplies[i].consumer;
+
+ ret = devm_regulator_register_notifier(regulator,
+ &wm8804->disable_nb[i]);
+ if (ret != 0) {
+ dev_err(dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies),
+ wm8804->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (wm8804->reset)
+ gpiod_set_value_cansleep(wm8804->reset, 1);
+
+ ret = regmap_read(regmap, WM8804_RST_DEVID1, &id1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device ID: %d\n", ret);
+ goto err_reg_enable;
+ }
+
+ ret = regmap_read(regmap, WM8804_DEVID2, &id2);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device ID: %d\n", ret);
+ goto err_reg_enable;
+ }
+
+ id2 = (id2 << 8) | id1;
+
+ if (id2 != 0x8805) {
+ dev_err(dev, "Invalid device ID: %#x\n", id2);
+ ret = -EINVAL;
+ goto err_reg_enable;
+ }
+
+ ret = regmap_read(regmap, WM8804_DEVREV, &id1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_reg_enable;
+ }
+ dev_info(dev, "revision %c\n", id1 + 'A');
+
+ if (!wm8804->reset) {
+ ret = wm8804_soft_reset(wm8804);
+ if (ret < 0) {
+ dev_err(dev, "Failed to issue reset: %d\n", ret);
+ goto err_reg_enable;
+ }
+ }
+
+ ret = snd_soc_register_codec(dev, &soc_codec_dev_wm8804,
+ &wm8804_dai, 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register CODEC: %d\n", ret);
+ goto err_reg_enable;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+
+err_reg_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm8804_probe);
+
+void wm8804_remove(struct device *dev)
+{
+ pm_runtime_disable(dev);
+ snd_soc_unregister_codec(dev);
+}
+EXPORT_SYMBOL_GPL(wm8804_remove);
+
+#if IS_ENABLED(CONFIG_PM)
+static int wm8804_runtime_resume(struct device *dev)
+{
+ struct wm8804_priv *wm8804 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies),
+ wm8804->supplies);
+ if (ret) {
+ dev_err(wm8804->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_sync(wm8804->regmap);
+
+ /* Power up OSCCLK */
+ regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x0);
+
+ return 0;
+}
+
+static int wm8804_runtime_suspend(struct device *dev)
+{
+ struct wm8804_priv *wm8804 = dev_get_drvdata(dev);
+
+ /* Power down OSCCLK */
+ regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x8);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies),
+ wm8804->supplies);
+
+ return 0;
+}
+#endif
+
+const struct dev_pm_ops wm8804_pm = {
+ SET_RUNTIME_PM_OPS(wm8804_runtime_suspend, wm8804_runtime_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(wm8804_pm);
+
+MODULE_DESCRIPTION("ASoC WM8804 driver");
+MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8804.h b/kernel/sound/soc/codecs/wm8804.h
new file mode 100644
index 000000000..aa72fa66c
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8804.h
@@ -0,0 +1,73 @@
+/*
+ * wm8804.h -- WM8804 S/PDIF transceiver driver
+ *
+ * Copyright 2010 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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 _WM8804_H
+#define _WM8804_H
+
+#include <linux/regmap.h>
+
+/*
+ * Register values.
+ */
+#define WM8804_RST_DEVID1 0x00
+#define WM8804_DEVID2 0x01
+#define WM8804_DEVREV 0x02
+#define WM8804_PLL1 0x03
+#define WM8804_PLL2 0x04
+#define WM8804_PLL3 0x05
+#define WM8804_PLL4 0x06
+#define WM8804_PLL5 0x07
+#define WM8804_PLL6 0x08
+#define WM8804_SPDMODE 0x09
+#define WM8804_INTMASK 0x0A
+#define WM8804_INTSTAT 0x0B
+#define WM8804_SPDSTAT 0x0C
+#define WM8804_RXCHAN1 0x0D
+#define WM8804_RXCHAN2 0x0E
+#define WM8804_RXCHAN3 0x0F
+#define WM8804_RXCHAN4 0x10
+#define WM8804_RXCHAN5 0x11
+#define WM8804_SPDTX1 0x12
+#define WM8804_SPDTX2 0x13
+#define WM8804_SPDTX3 0x14
+#define WM8804_SPDTX4 0x15
+#define WM8804_SPDTX5 0x16
+#define WM8804_GPO0 0x17
+#define WM8804_GPO1 0x18
+#define WM8804_GPO2 0x1A
+#define WM8804_AIFTX 0x1B
+#define WM8804_AIFRX 0x1C
+#define WM8804_SPDRX1 0x1D
+#define WM8804_PWRDN 0x1E
+
+#define WM8804_REGISTER_COUNT 30
+#define WM8804_MAX_REGISTER 0x1E
+
+#define WM8804_TX_CLKSRC_MCLK 1
+#define WM8804_TX_CLKSRC_PLL 2
+
+#define WM8804_CLKOUT_SRC_CLK1 3
+#define WM8804_CLKOUT_SRC_OSCCLK 4
+
+#define WM8804_CLKOUT_DIV 1
+#define WM8804_MCLK_DIV 2
+
+#define WM8804_MCLKDIV_256FS 0
+#define WM8804_MCLKDIV_128FS 1
+
+extern const struct regmap_config wm8804_regmap_config;
+extern const struct dev_pm_ops wm8804_pm;
+
+int wm8804_probe(struct device *dev, struct regmap *regmap);
+void wm8804_remove(struct device *dev);
+
+#endif /* _WM8804_H */
diff --git a/kernel/sound/soc/codecs/wm8900.c b/kernel/sound/soc/codecs/wm8900.c
new file mode 100644
index 000000000..2eb986c19
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8900.c
@@ -0,0 +1,1358 @@
+/*
+ * wm8900.c -- WM8900 ALSA Soc Audio driver
+ *
+ * Copyright 2007, 2008 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.
+ *
+ * TODO:
+ * - Tristating.
+ * - TDM.
+ * - Jack detect.
+ * - FLL source configuration, currently only MCLK is supported.
+ */
+
+#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/spi/spi.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 "wm8900.h"
+
+/* WM8900 register space */
+#define WM8900_REG_RESET 0x0
+#define WM8900_REG_ID 0x0
+#define WM8900_REG_POWER1 0x1
+#define WM8900_REG_POWER2 0x2
+#define WM8900_REG_POWER3 0x3
+#define WM8900_REG_AUDIO1 0x4
+#define WM8900_REG_AUDIO2 0x5
+#define WM8900_REG_CLOCKING1 0x6
+#define WM8900_REG_CLOCKING2 0x7
+#define WM8900_REG_AUDIO3 0x8
+#define WM8900_REG_AUDIO4 0x9
+#define WM8900_REG_DACCTRL 0xa
+#define WM8900_REG_LDAC_DV 0xb
+#define WM8900_REG_RDAC_DV 0xc
+#define WM8900_REG_SIDETONE 0xd
+#define WM8900_REG_ADCCTRL 0xe
+#define WM8900_REG_LADC_DV 0xf
+#define WM8900_REG_RADC_DV 0x10
+#define WM8900_REG_GPIO 0x12
+#define WM8900_REG_INCTL 0x15
+#define WM8900_REG_LINVOL 0x16
+#define WM8900_REG_RINVOL 0x17
+#define WM8900_REG_INBOOSTMIX1 0x18
+#define WM8900_REG_INBOOSTMIX2 0x19
+#define WM8900_REG_ADCPATH 0x1a
+#define WM8900_REG_AUXBOOST 0x1b
+#define WM8900_REG_ADDCTL 0x1e
+#define WM8900_REG_FLLCTL1 0x24
+#define WM8900_REG_FLLCTL2 0x25
+#define WM8900_REG_FLLCTL3 0x26
+#define WM8900_REG_FLLCTL4 0x27
+#define WM8900_REG_FLLCTL5 0x28
+#define WM8900_REG_FLLCTL6 0x29
+#define WM8900_REG_LOUTMIXCTL1 0x2c
+#define WM8900_REG_ROUTMIXCTL1 0x2d
+#define WM8900_REG_BYPASS1 0x2e
+#define WM8900_REG_BYPASS2 0x2f
+#define WM8900_REG_AUXOUT_CTL 0x30
+#define WM8900_REG_LOUT1CTL 0x33
+#define WM8900_REG_ROUT1CTL 0x34
+#define WM8900_REG_LOUT2CTL 0x35
+#define WM8900_REG_ROUT2CTL 0x36
+#define WM8900_REG_HPCTL1 0x3a
+#define WM8900_REG_OUTBIASCTL 0x73
+
+#define WM8900_MAXREG 0x80
+
+#define WM8900_REG_ADDCTL_OUT1_DIS 0x80
+#define WM8900_REG_ADDCTL_OUT2_DIS 0x40
+#define WM8900_REG_ADDCTL_VMID_DIS 0x20
+#define WM8900_REG_ADDCTL_BIAS_SRC 0x10
+#define WM8900_REG_ADDCTL_VMID_SOFTST 0x04
+#define WM8900_REG_ADDCTL_TEMP_SD 0x02
+
+#define WM8900_REG_GPIO_TEMP_ENA 0x2
+
+#define WM8900_REG_POWER1_STARTUP_BIAS_ENA 0x0100
+#define WM8900_REG_POWER1_BIAS_ENA 0x0008
+#define WM8900_REG_POWER1_VMID_BUF_ENA 0x0004
+#define WM8900_REG_POWER1_FLL_ENA 0x0040
+
+#define WM8900_REG_POWER2_SYSCLK_ENA 0x8000
+#define WM8900_REG_POWER2_ADCL_ENA 0x0002
+#define WM8900_REG_POWER2_ADCR_ENA 0x0001
+
+#define WM8900_REG_POWER3_DACL_ENA 0x0002
+#define WM8900_REG_POWER3_DACR_ENA 0x0001
+
+#define WM8900_REG_AUDIO1_AIF_FMT_MASK 0x0018
+#define WM8900_REG_AUDIO1_LRCLK_INV 0x0080
+#define WM8900_REG_AUDIO1_BCLK_INV 0x0100
+
+#define WM8900_REG_CLOCKING1_BCLK_DIR 0x1
+#define WM8900_REG_CLOCKING1_MCLK_SRC 0x100
+#define WM8900_REG_CLOCKING1_BCLK_MASK 0x01e
+#define WM8900_REG_CLOCKING1_OPCLK_MASK 0x7000
+
+#define WM8900_REG_CLOCKING2_ADC_CLKDIV 0xe0
+#define WM8900_REG_CLOCKING2_DAC_CLKDIV 0x1c
+
+#define WM8900_REG_DACCTRL_MUTE 0x004
+#define WM8900_REG_DACCTRL_DAC_SB_FILT 0x100
+#define WM8900_REG_DACCTRL_AIF_LRCLKRATE 0x400
+
+#define WM8900_REG_AUDIO3_ADCLRC_DIR 0x0800
+
+#define WM8900_REG_AUDIO4_DACLRC_DIR 0x0800
+
+#define WM8900_REG_FLLCTL1_OSC_ENA 0x100
+
+#define WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF 0x100
+
+#define WM8900_REG_HPCTL1_HP_IPSTAGE_ENA 0x80
+#define WM8900_REG_HPCTL1_HP_OPSTAGE_ENA 0x40
+#define WM8900_REG_HPCTL1_HP_CLAMP_IP 0x20
+#define WM8900_REG_HPCTL1_HP_CLAMP_OP 0x10
+#define WM8900_REG_HPCTL1_HP_SHORT 0x08
+#define WM8900_REG_HPCTL1_HP_SHORT2 0x04
+
+#define WM8900_LRC_MASK 0x03ff
+
+struct wm8900_priv {
+ struct regmap *regmap;
+
+ u32 fll_in; /* FLL input frequency */
+ u32 fll_out; /* FLL output frequency */
+};
+
+/*
+ * wm8900 register cache. We can't read the entire register space and we
+ * have slow control buses so we cache the registers.
+ */
+static const struct reg_default wm8900_reg_defaults[] = {
+ { 1, 0x0000 },
+ { 2, 0xc000 },
+ { 3, 0x0000 },
+ { 4, 0x4050 },
+ { 5, 0x4000 },
+ { 6, 0x0008 },
+ { 7, 0x0000 },
+ { 8, 0x0040 },
+ { 9, 0x0040 },
+ { 10, 0x1004 },
+ { 11, 0x00c0 },
+ { 12, 0x00c0 },
+ { 13, 0x0000 },
+ { 14, 0x0100 },
+ { 15, 0x00c0 },
+ { 16, 0x00c0 },
+ { 17, 0x0000 },
+ { 18, 0xb001 },
+ { 19, 0x0000 },
+ { 20, 0x0000 },
+ { 21, 0x0044 },
+ { 22, 0x004c },
+ { 23, 0x004c },
+ { 24, 0x0044 },
+ { 25, 0x0044 },
+ { 26, 0x0000 },
+ { 27, 0x0044 },
+ { 28, 0x0000 },
+ { 29, 0x0000 },
+ { 30, 0x0002 },
+ { 31, 0x0000 },
+ { 32, 0x0000 },
+ { 33, 0x0000 },
+ { 34, 0x0000 },
+ { 35, 0x0000 },
+ { 36, 0x0008 },
+ { 37, 0x0000 },
+ { 38, 0x0000 },
+ { 39, 0x0008 },
+ { 40, 0x0097 },
+ { 41, 0x0100 },
+ { 42, 0x0000 },
+ { 43, 0x0000 },
+ { 44, 0x0050 },
+ { 45, 0x0050 },
+ { 46, 0x0055 },
+ { 47, 0x0055 },
+ { 48, 0x0055 },
+ { 49, 0x0000 },
+ { 50, 0x0000 },
+ { 51, 0x0079 },
+ { 52, 0x0079 },
+ { 53, 0x0079 },
+ { 54, 0x0079 },
+ { 55, 0x0000 },
+};
+
+static bool wm8900_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8900_REG_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void wm8900_reset(struct snd_soc_codec *codec)
+{
+ snd_soc_write(codec, WM8900_REG_RESET, 0);
+}
+
+static int wm8900_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);
+ u16 hpctl1 = snd_soc_read(codec, WM8900_REG_HPCTL1);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Clamp headphone outputs */
+ hpctl1 = WM8900_REG_HPCTL1_HP_CLAMP_IP |
+ WM8900_REG_HPCTL1_HP_CLAMP_OP;
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ /* Enable the input stage */
+ hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_IP;
+ hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT |
+ WM8900_REG_HPCTL1_HP_SHORT2 |
+ WM8900_REG_HPCTL1_HP_IPSTAGE_ENA;
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+
+ msleep(400);
+
+ /* Enable the output stage */
+ hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_OP;
+ hpctl1 |= WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+
+ /* Remove the shorts */
+ hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT2;
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT;
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Short the output */
+ hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT;
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+
+ /* Disable the output stage */
+ hpctl1 &= ~WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+
+ /* Clamp the outputs and power down input */
+ hpctl1 |= WM8900_REG_HPCTL1_HP_CLAMP_IP |
+ WM8900_REG_HPCTL1_HP_CLAMP_OP;
+ hpctl1 &= ~WM8900_REG_HPCTL1_HP_IPSTAGE_ENA;
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable everything */
+ snd_soc_write(codec, WM8900_REG_HPCTL1, 0);
+ break;
+
+ default:
+ WARN(1, "Invalid event %d\n", event);
+ break;
+ }
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -5700, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 0);
+
+static const DECLARE_TLV_DB_SCALE(in_boost_tlv, -1200, 600, 0);
+
+static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1200, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -7200, 75, 1);
+
+static const DECLARE_TLV_DB_SCALE(adc_svol_tlv, -3600, 300, 0);
+
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1);
+
+static const char *mic_bias_level_txt[] = { "0.9*AVDD", "0.65*AVDD" };
+
+static SOC_ENUM_SINGLE_DECL(mic_bias_level,
+ WM8900_REG_INCTL, 8, mic_bias_level_txt);
+
+static const char *dac_mute_rate_txt[] = { "Fast", "Slow" };
+
+static SOC_ENUM_SINGLE_DECL(dac_mute_rate,
+ WM8900_REG_DACCTRL, 7, dac_mute_rate_txt);
+
+static const char *dac_deemphasis_txt[] = {
+ "Disabled", "32kHz", "44.1kHz", "48kHz"
+};
+
+static SOC_ENUM_SINGLE_DECL(dac_deemphasis,
+ WM8900_REG_DACCTRL, 4, dac_deemphasis_txt);
+
+static const char *adc_hpf_cut_txt[] = {
+ "Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"
+};
+
+static SOC_ENUM_SINGLE_DECL(adc_hpf_cut,
+ WM8900_REG_ADCCTRL, 5, adc_hpf_cut_txt);
+
+static const char *lr_txt[] = {
+ "Left", "Right"
+};
+
+static SOC_ENUM_SINGLE_DECL(aifl_src,
+ WM8900_REG_AUDIO1, 15, lr_txt);
+
+static SOC_ENUM_SINGLE_DECL(aifr_src,
+ WM8900_REG_AUDIO1, 14, lr_txt);
+
+static SOC_ENUM_SINGLE_DECL(dacl_src,
+ WM8900_REG_AUDIO2, 15, lr_txt);
+
+static SOC_ENUM_SINGLE_DECL(dacr_src,
+ WM8900_REG_AUDIO2, 14, lr_txt);
+
+static const char *sidetone_txt[] = {
+ "Disabled", "Left ADC", "Right ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(dacl_sidetone,
+ WM8900_REG_SIDETONE, 2, sidetone_txt);
+
+static SOC_ENUM_SINGLE_DECL(dacr_sidetone,
+ WM8900_REG_SIDETONE, 0, sidetone_txt);
+
+static const struct snd_kcontrol_new wm8900_snd_controls[] = {
+SOC_ENUM("Mic Bias Level", mic_bias_level),
+
+SOC_SINGLE_TLV("Left Input PGA Volume", WM8900_REG_LINVOL, 0, 31, 0,
+ in_pga_tlv),
+SOC_SINGLE("Left Input PGA Switch", WM8900_REG_LINVOL, 6, 1, 1),
+SOC_SINGLE("Left Input PGA ZC Switch", WM8900_REG_LINVOL, 7, 1, 0),
+
+SOC_SINGLE_TLV("Right Input PGA Volume", WM8900_REG_RINVOL, 0, 31, 0,
+ in_pga_tlv),
+SOC_SINGLE("Right Input PGA Switch", WM8900_REG_RINVOL, 6, 1, 1),
+SOC_SINGLE("Right Input PGA ZC Switch", WM8900_REG_RINVOL, 7, 1, 0),
+
+SOC_SINGLE("DAC Soft Mute Switch", WM8900_REG_DACCTRL, 6, 1, 1),
+SOC_ENUM("DAC Mute Rate", dac_mute_rate),
+SOC_SINGLE("DAC Mono Switch", WM8900_REG_DACCTRL, 9, 1, 0),
+SOC_ENUM("DAC Deemphasis", dac_deemphasis),
+SOC_SINGLE("DAC Sigma-Delta Modulator Clock Switch", WM8900_REG_DACCTRL,
+ 12, 1, 0),
+
+SOC_SINGLE("ADC HPF Switch", WM8900_REG_ADCCTRL, 8, 1, 0),
+SOC_ENUM("ADC HPF Cut-Off", adc_hpf_cut),
+SOC_DOUBLE("ADC Invert Switch", WM8900_REG_ADCCTRL, 1, 0, 1, 0),
+SOC_SINGLE_TLV("Left ADC Sidetone Volume", WM8900_REG_SIDETONE, 9, 12, 0,
+ adc_svol_tlv),
+SOC_SINGLE_TLV("Right ADC Sidetone Volume", WM8900_REG_SIDETONE, 5, 12, 0,
+ adc_svol_tlv),
+SOC_ENUM("Left Digital Audio Source", aifl_src),
+SOC_ENUM("Right Digital Audio Source", aifr_src),
+
+SOC_SINGLE_TLV("DAC Input Boost Volume", WM8900_REG_AUDIO2, 10, 4, 0,
+ dac_boost_tlv),
+SOC_ENUM("Left DAC Source", dacl_src),
+SOC_ENUM("Right DAC Source", dacr_src),
+SOC_ENUM("Left DAC Sidetone", dacl_sidetone),
+SOC_ENUM("Right DAC Sidetone", dacr_sidetone),
+SOC_DOUBLE("DAC Invert Switch", WM8900_REG_DACCTRL, 1, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("Digital Playback Volume",
+ WM8900_REG_LDAC_DV, WM8900_REG_RDAC_DV,
+ 1, 96, 0, dac_tlv),
+SOC_DOUBLE_R_TLV("Digital Capture Volume",
+ WM8900_REG_LADC_DV, WM8900_REG_RADC_DV, 1, 119, 0, adc_tlv),
+
+SOC_SINGLE_TLV("LINPUT3 Bypass Volume", WM8900_REG_LOUTMIXCTL1, 4, 7, 0,
+ out_mix_tlv),
+SOC_SINGLE_TLV("RINPUT3 Bypass Volume", WM8900_REG_ROUTMIXCTL1, 4, 7, 0,
+ out_mix_tlv),
+SOC_SINGLE_TLV("Left AUX Bypass Volume", WM8900_REG_AUXOUT_CTL, 4, 7, 0,
+ out_mix_tlv),
+SOC_SINGLE_TLV("Right AUX Bypass Volume", WM8900_REG_AUXOUT_CTL, 0, 7, 0,
+ out_mix_tlv),
+
+SOC_SINGLE_TLV("LeftIn to RightOut Mixer Volume", WM8900_REG_BYPASS1, 0, 7, 0,
+ out_mix_tlv),
+SOC_SINGLE_TLV("LeftIn to LeftOut Mixer Volume", WM8900_REG_BYPASS1, 4, 7, 0,
+ out_mix_tlv),
+SOC_SINGLE_TLV("RightIn to LeftOut Mixer Volume", WM8900_REG_BYPASS2, 0, 7, 0,
+ out_mix_tlv),
+SOC_SINGLE_TLV("RightIn to RightOut Mixer Volume", WM8900_REG_BYPASS2, 4, 7, 0,
+ out_mix_tlv),
+
+SOC_SINGLE_TLV("IN2L Boost Volume", WM8900_REG_INBOOSTMIX1, 0, 3, 0,
+ in_boost_tlv),
+SOC_SINGLE_TLV("IN3L Boost Volume", WM8900_REG_INBOOSTMIX1, 4, 3, 0,
+ in_boost_tlv),
+SOC_SINGLE_TLV("IN2R Boost Volume", WM8900_REG_INBOOSTMIX2, 0, 3, 0,
+ in_boost_tlv),
+SOC_SINGLE_TLV("IN3R Boost Volume", WM8900_REG_INBOOSTMIX2, 4, 3, 0,
+ in_boost_tlv),
+SOC_SINGLE_TLV("Left AUX Boost Volume", WM8900_REG_AUXBOOST, 4, 3, 0,
+ in_boost_tlv),
+SOC_SINGLE_TLV("Right AUX Boost Volume", WM8900_REG_AUXBOOST, 0, 3, 0,
+ in_boost_tlv),
+
+SOC_DOUBLE_R_TLV("LINEOUT1 Volume", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL,
+ 0, 63, 0, out_pga_tlv),
+SOC_DOUBLE_R("LINEOUT1 Switch", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL,
+ 6, 1, 1),
+SOC_DOUBLE_R("LINEOUT1 ZC Switch", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL,
+ 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("LINEOUT2 Volume",
+ WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL,
+ 0, 63, 0, out_pga_tlv),
+SOC_DOUBLE_R("LINEOUT2 Switch",
+ WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, 6, 1, 1),
+SOC_DOUBLE_R("LINEOUT2 ZC Switch",
+ WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, 7, 1, 0),
+SOC_SINGLE("LINEOUT2 LP -12dB", WM8900_REG_LOUTMIXCTL1,
+ 0, 1, 1),
+
+};
+
+static const struct snd_kcontrol_new wm8900_dapm_loutput2_control =
+SOC_DAPM_SINGLE("LINEOUT2L Switch", WM8900_REG_POWER3, 6, 1, 0);
+
+static const struct snd_kcontrol_new wm8900_dapm_routput2_control =
+SOC_DAPM_SINGLE("LINEOUT2R Switch", WM8900_REG_POWER3, 5, 1, 0);
+
+static const struct snd_kcontrol_new wm8900_loutmix_controls[] = {
+SOC_DAPM_SINGLE("LINPUT3 Bypass Switch", WM8900_REG_LOUTMIXCTL1, 7, 1, 0),
+SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 7, 1, 0),
+SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 3, 1, 0),
+SOC_DAPM_SINGLE("DACL Switch", WM8900_REG_LOUTMIXCTL1, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_routmix_controls[] = {
+SOC_DAPM_SINGLE("RINPUT3 Bypass Switch", WM8900_REG_ROUTMIXCTL1, 7, 1, 0),
+SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 3, 1, 0),
+SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 3, 1, 0),
+SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 7, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8900_REG_ROUTMIXCTL1, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_linmix_controls[] = {
+SOC_DAPM_SINGLE("LINPUT2 Switch", WM8900_REG_INBOOSTMIX1, 2, 1, 1),
+SOC_DAPM_SINGLE("LINPUT3 Switch", WM8900_REG_INBOOSTMIX1, 6, 1, 1),
+SOC_DAPM_SINGLE("AUX Switch", WM8900_REG_AUXBOOST, 6, 1, 1),
+SOC_DAPM_SINGLE("Input PGA Switch", WM8900_REG_ADCPATH, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_rinmix_controls[] = {
+SOC_DAPM_SINGLE("RINPUT2 Switch", WM8900_REG_INBOOSTMIX2, 2, 1, 1),
+SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INBOOSTMIX2, 6, 1, 1),
+SOC_DAPM_SINGLE("AUX Switch", WM8900_REG_AUXBOOST, 2, 1, 1),
+SOC_DAPM_SINGLE("Input PGA Switch", WM8900_REG_ADCPATH, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_linpga_controls[] = {
+SOC_DAPM_SINGLE("LINPUT1 Switch", WM8900_REG_INCTL, 6, 1, 0),
+SOC_DAPM_SINGLE("LINPUT2 Switch", WM8900_REG_INCTL, 5, 1, 0),
+SOC_DAPM_SINGLE("LINPUT3 Switch", WM8900_REG_INCTL, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_rinpga_controls[] = {
+SOC_DAPM_SINGLE("RINPUT1 Switch", WM8900_REG_INCTL, 2, 1, 0),
+SOC_DAPM_SINGLE("RINPUT2 Switch", WM8900_REG_INCTL, 1, 1, 0),
+SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INCTL, 0, 1, 0),
+};
+
+static const char *wm8900_lp_mux[] = { "Disabled", "Enabled" };
+
+static SOC_ENUM_SINGLE_DECL(wm8900_lineout2_lp_mux,
+ WM8900_REG_LOUTMIXCTL1, 1, wm8900_lp_mux);
+
+static const struct snd_kcontrol_new wm8900_lineout2_lp =
+SOC_DAPM_ENUM("Route", wm8900_lineout2_lp_mux);
+
+static const struct snd_soc_dapm_widget wm8900_dapm_widgets[] = {
+
+/* Externally visible pins */
+SND_SOC_DAPM_OUTPUT("LINEOUT1L"),
+SND_SOC_DAPM_OUTPUT("LINEOUT1R"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2L"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2R"),
+SND_SOC_DAPM_OUTPUT("HP_L"),
+SND_SOC_DAPM_OUTPUT("HP_R"),
+
+SND_SOC_DAPM_INPUT("RINPUT1"),
+SND_SOC_DAPM_INPUT("LINPUT1"),
+SND_SOC_DAPM_INPUT("RINPUT2"),
+SND_SOC_DAPM_INPUT("LINPUT2"),
+SND_SOC_DAPM_INPUT("RINPUT3"),
+SND_SOC_DAPM_INPUT("LINPUT3"),
+SND_SOC_DAPM_INPUT("AUX"),
+
+SND_SOC_DAPM_VMID("VMID"),
+
+/* Input */
+SND_SOC_DAPM_MIXER("Left Input PGA", WM8900_REG_POWER2, 3, 0,
+ wm8900_linpga_controls,
+ ARRAY_SIZE(wm8900_linpga_controls)),
+SND_SOC_DAPM_MIXER("Right Input PGA", WM8900_REG_POWER2, 2, 0,
+ wm8900_rinpga_controls,
+ ARRAY_SIZE(wm8900_rinpga_controls)),
+
+SND_SOC_DAPM_MIXER("Left Input Mixer", WM8900_REG_POWER2, 5, 0,
+ wm8900_linmix_controls,
+ ARRAY_SIZE(wm8900_linmix_controls)),
+SND_SOC_DAPM_MIXER("Right Input Mixer", WM8900_REG_POWER2, 4, 0,
+ wm8900_rinmix_controls,
+ ARRAY_SIZE(wm8900_rinmix_controls)),
+
+SND_SOC_DAPM_SUPPLY("Mic Bias", WM8900_REG_POWER1, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8900_REG_POWER2, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8900_REG_POWER2, 0, 0),
+
+/* Output */
+SND_SOC_DAPM_DAC("DACL", "Left HiFi Playback", WM8900_REG_POWER3, 1, 0),
+SND_SOC_DAPM_DAC("DACR", "Right HiFi Playback", WM8900_REG_POWER3, 0, 0),
+
+SND_SOC_DAPM_PGA_E("Headphone Amplifier", WM8900_REG_POWER3, 7, 0, NULL, 0,
+ wm8900_hp_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_PGA("LINEOUT1L PGA", WM8900_REG_POWER2, 8, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT1R PGA", WM8900_REG_POWER2, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("LINEOUT2 LP", SND_SOC_NOPM, 0, 0, &wm8900_lineout2_lp),
+SND_SOC_DAPM_PGA("LINEOUT2L PGA", WM8900_REG_POWER3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT2R PGA", WM8900_REG_POWER3, 5, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", WM8900_REG_POWER3, 3, 0,
+ wm8900_loutmix_controls,
+ ARRAY_SIZE(wm8900_loutmix_controls)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", WM8900_REG_POWER3, 2, 0,
+ wm8900_routmix_controls,
+ ARRAY_SIZE(wm8900_routmix_controls)),
+};
+
+/* Target, Path, Source */
+static const struct snd_soc_dapm_route wm8900_dapm_routes[] = {
+/* Inputs */
+{"Left Input PGA", "LINPUT1 Switch", "LINPUT1"},
+{"Left Input PGA", "LINPUT2 Switch", "LINPUT2"},
+{"Left Input PGA", "LINPUT3 Switch", "LINPUT3"},
+
+{"Right Input PGA", "RINPUT1 Switch", "RINPUT1"},
+{"Right Input PGA", "RINPUT2 Switch", "RINPUT2"},
+{"Right Input PGA", "RINPUT3 Switch", "RINPUT3"},
+
+{"Left Input Mixer", "LINPUT2 Switch", "LINPUT2"},
+{"Left Input Mixer", "LINPUT3 Switch", "LINPUT3"},
+{"Left Input Mixer", "AUX Switch", "AUX"},
+{"Left Input Mixer", "Input PGA Switch", "Left Input PGA"},
+
+{"Right Input Mixer", "RINPUT2 Switch", "RINPUT2"},
+{"Right Input Mixer", "RINPUT3 Switch", "RINPUT3"},
+{"Right Input Mixer", "AUX Switch", "AUX"},
+{"Right Input Mixer", "Input PGA Switch", "Right Input PGA"},
+
+{"ADCL", NULL, "Left Input Mixer"},
+{"ADCR", NULL, "Right Input Mixer"},
+
+/* Outputs */
+{"LINEOUT1L", NULL, "LINEOUT1L PGA"},
+{"LINEOUT1L PGA", NULL, "Left Output Mixer"},
+{"LINEOUT1R", NULL, "LINEOUT1R PGA"},
+{"LINEOUT1R PGA", NULL, "Right Output Mixer"},
+
+{"LINEOUT2L PGA", NULL, "Left Output Mixer"},
+{"LINEOUT2 LP", "Disabled", "LINEOUT2L PGA"},
+{"LINEOUT2 LP", "Enabled", "Left Output Mixer"},
+{"LINEOUT2L", NULL, "LINEOUT2 LP"},
+
+{"LINEOUT2R PGA", NULL, "Right Output Mixer"},
+{"LINEOUT2 LP", "Disabled", "LINEOUT2R PGA"},
+{"LINEOUT2 LP", "Enabled", "Right Output Mixer"},
+{"LINEOUT2R", NULL, "LINEOUT2 LP"},
+
+{"Left Output Mixer", "LINPUT3 Bypass Switch", "LINPUT3"},
+{"Left Output Mixer", "AUX Bypass Switch", "AUX"},
+{"Left Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"},
+{"Left Output Mixer", "Right Input Mixer Switch", "Right Input Mixer"},
+{"Left Output Mixer", "DACL Switch", "DACL"},
+
+{"Right Output Mixer", "RINPUT3 Bypass Switch", "RINPUT3"},
+{"Right Output Mixer", "AUX Bypass Switch", "AUX"},
+{"Right Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"},
+{"Right Output Mixer", "Right Input Mixer Switch", "Right Input Mixer"},
+{"Right Output Mixer", "DACR Switch", "DACR"},
+
+/* Note that the headphone output stage needs to be connected
+ * externally to LINEOUT2 via DC blocking capacitors. Other
+ * configurations are not supported.
+ *
+ * Note also that left and right headphone paths are treated as a
+ * mono path.
+ */
+{"Headphone Amplifier", NULL, "LINEOUT2 LP"},
+{"Headphone Amplifier", NULL, "LINEOUT2 LP"},
+{"HP_L", NULL, "Headphone Amplifier"},
+{"HP_R", NULL, "Headphone Amplifier"},
+};
+
+static int wm8900_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 reg;
+
+ reg = snd_soc_read(codec, WM8900_REG_AUDIO1) & ~0x60;
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ reg |= 0x20;
+ break;
+ case 24:
+ reg |= 0x40;
+ break;
+ case 32:
+ reg |= 0x60;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8900_REG_AUDIO1, reg);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg = snd_soc_read(codec, WM8900_REG_DACCTRL);
+
+ if (params_rate(params) <= 24000)
+ reg |= WM8900_REG_DACCTRL_DAC_SB_FILT;
+ else
+ reg &= ~WM8900_REG_DACCTRL_DAC_SB_FILT;
+
+ snd_soc_write(codec, WM8900_REG_DACCTRL, reg);
+ }
+
+ return 0;
+}
+
+/* FLL divisors */
+struct _fll_div {
+ u16 fll_ratio;
+ u16 fllclk_div;
+ u16 fll_slow_lock_ref;
+ u16 n;
+ u16 k;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod, target;
+ unsigned int div;
+
+ if (WARN_ON(!Fout))
+ return -EINVAL;
+
+ /* The FLL must run at 90-100MHz which is then scaled down to
+ * the output value by FLLCLK_DIV. */
+ target = Fout;
+ div = 1;
+ while (target < 90000000) {
+ div *= 2;
+ target *= 2;
+ }
+
+ if (target > 100000000)
+ printk(KERN_WARNING "wm8900: FLL rate %u out of range, Fref=%u"
+ " Fout=%u\n", target, Fref, Fout);
+ if (div > 32) {
+ printk(KERN_ERR "wm8900: Invalid FLL division rate %u, "
+ "Fref=%u, Fout=%u, target=%u\n",
+ div, Fref, Fout, target);
+ return -EINVAL;
+ }
+
+ fll_div->fllclk_div = div >> 2;
+
+ if (Fref < 48000)
+ fll_div->fll_slow_lock_ref = 1;
+ else
+ fll_div->fll_slow_lock_ref = 0;
+
+ Ndiv = target / Fref;
+
+ if (Fref < 1000000)
+ fll_div->fll_ratio = 8;
+ else
+ fll_div->fll_ratio = 1;
+
+ fll_div->n = Ndiv / fll_div->fll_ratio;
+ Nmod = (target / fll_div->fll_ratio) % Fref;
+
+ /* Calculate fractional part - scale up so we can round. */
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, Fref);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ fll_div->k = K / 10;
+
+ if (WARN_ON(target != Fout * (fll_div->fllclk_div << 2)) ||
+ WARN_ON(!K && target != Fref * fll_div->fll_ratio * fll_div->n))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int wm8900_set_fll(struct snd_soc_codec *codec,
+ int fll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ struct wm8900_priv *wm8900 = snd_soc_codec_get_drvdata(codec);
+ struct _fll_div fll_div;
+
+ if (wm8900->fll_in == freq_in && wm8900->fll_out == freq_out)
+ return 0;
+
+ /* The digital side should be disabled during any change. */
+ snd_soc_update_bits(codec, WM8900_REG_POWER1,
+ WM8900_REG_POWER1_FLL_ENA, 0);
+
+ /* Disable the FLL? */
+ if (!freq_in || !freq_out) {
+ snd_soc_update_bits(codec, WM8900_REG_CLOCKING1,
+ WM8900_REG_CLOCKING1_MCLK_SRC, 0);
+ snd_soc_update_bits(codec, WM8900_REG_FLLCTL1,
+ WM8900_REG_FLLCTL1_OSC_ENA, 0);
+ wm8900->fll_in = freq_in;
+ wm8900->fll_out = freq_out;
+
+ return 0;
+ }
+
+ if (fll_factors(&fll_div, freq_in, freq_out) != 0)
+ goto reenable;
+
+ wm8900->fll_in = freq_in;
+ wm8900->fll_out = freq_out;
+
+ /* The osclilator *MUST* be enabled before we enable the
+ * digital circuit. */
+ snd_soc_write(codec, WM8900_REG_FLLCTL1,
+ fll_div.fll_ratio | WM8900_REG_FLLCTL1_OSC_ENA);
+
+ snd_soc_write(codec, WM8900_REG_FLLCTL4, fll_div.n >> 5);
+ snd_soc_write(codec, WM8900_REG_FLLCTL5,
+ (fll_div.fllclk_div << 6) | (fll_div.n & 0x1f));
+
+ if (fll_div.k) {
+ snd_soc_write(codec, WM8900_REG_FLLCTL2,
+ (fll_div.k >> 8) | 0x100);
+ snd_soc_write(codec, WM8900_REG_FLLCTL3, fll_div.k & 0xff);
+ } else
+ snd_soc_write(codec, WM8900_REG_FLLCTL2, 0);
+
+ if (fll_div.fll_slow_lock_ref)
+ snd_soc_write(codec, WM8900_REG_FLLCTL6,
+ WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF);
+ else
+ snd_soc_write(codec, WM8900_REG_FLLCTL6, 0);
+
+ snd_soc_update_bits(codec, WM8900_REG_POWER1,
+ WM8900_REG_POWER1_FLL_ENA,
+ WM8900_REG_POWER1_FLL_ENA);
+
+reenable:
+ snd_soc_update_bits(codec, WM8900_REG_CLOCKING1,
+ WM8900_REG_CLOCKING1_MCLK_SRC,
+ WM8900_REG_CLOCKING1_MCLK_SRC);
+ return 0;
+}
+
+static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ return wm8900_set_fll(codec_dai->codec, pll_id, freq_in, freq_out);
+}
+
+static int wm8900_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ switch (div_id) {
+ case WM8900_BCLK_DIV:
+ snd_soc_update_bits(codec, WM8900_REG_CLOCKING1,
+ WM8900_REG_CLOCKING1_BCLK_MASK, div);
+ break;
+ case WM8900_OPCLK_DIV:
+ snd_soc_update_bits(codec, WM8900_REG_CLOCKING1,
+ WM8900_REG_CLOCKING1_OPCLK_MASK, div);
+ break;
+ case WM8900_DAC_LRCLK:
+ snd_soc_update_bits(codec, WM8900_REG_AUDIO4,
+ WM8900_LRC_MASK, div);
+ break;
+ case WM8900_ADC_LRCLK:
+ snd_soc_update_bits(codec, WM8900_REG_AUDIO3,
+ WM8900_LRC_MASK, div);
+ break;
+ case WM8900_DAC_CLKDIV:
+ snd_soc_update_bits(codec, WM8900_REG_CLOCKING2,
+ WM8900_REG_CLOCKING2_DAC_CLKDIV, div);
+ break;
+ case WM8900_ADC_CLKDIV:
+ snd_soc_update_bits(codec, WM8900_REG_CLOCKING2,
+ WM8900_REG_CLOCKING2_ADC_CLKDIV, div);
+ break;
+ case WM8900_LRCLK_MODE:
+ snd_soc_update_bits(codec, WM8900_REG_DACCTRL,
+ WM8900_REG_DACCTRL_AIF_LRCLKRATE, div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int wm8900_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int clocking1, aif1, aif3, aif4;
+
+ clocking1 = snd_soc_read(codec, WM8900_REG_CLOCKING1);
+ aif1 = snd_soc_read(codec, WM8900_REG_AUDIO1);
+ aif3 = snd_soc_read(codec, WM8900_REG_AUDIO3);
+ aif4 = snd_soc_read(codec, WM8900_REG_AUDIO4);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR;
+ aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR;
+ aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR;
+ aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR;
+ aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR;
+ aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR;
+ aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR;
+ aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR;
+ aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ aif1 |= WM8900_REG_AUDIO1_AIF_FMT_MASK;
+ aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aif1 |= WM8900_REG_AUDIO1_AIF_FMT_MASK;
+ aif1 |= WM8900_REG_AUDIO1_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK;
+ aif1 |= 0x10;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK;
+ aif1 |= 0x8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8900_REG_AUDIO1_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV;
+ aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif1 |= WM8900_REG_AUDIO1_BCLK_INV;
+ aif1 |= WM8900_REG_AUDIO1_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8900_REG_AUDIO1_BCLK_INV;
+ aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV;
+ aif1 |= WM8900_REG_AUDIO1_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8900_REG_CLOCKING1, clocking1);
+ snd_soc_write(codec, WM8900_REG_AUDIO1, aif1);
+ snd_soc_write(codec, WM8900_REG_AUDIO3, aif3);
+ snd_soc_write(codec, WM8900_REG_AUDIO4, aif4);
+
+ return 0;
+}
+
+static int wm8900_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ reg = snd_soc_read(codec, WM8900_REG_DACCTRL);
+
+ if (mute)
+ reg |= WM8900_REG_DACCTRL_MUTE;
+ else
+ reg &= ~WM8900_REG_DACCTRL_MUTE;
+
+ snd_soc_write(codec, WM8900_REG_DACCTRL, reg);
+
+ return 0;
+}
+
+#define WM8900_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define WM8900_PCM_FORMATS \
+ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
+ SNDRV_PCM_FORMAT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8900_dai_ops = {
+ .hw_params = wm8900_hw_params,
+ .set_clkdiv = wm8900_set_dai_clkdiv,
+ .set_pll = wm8900_set_dai_pll,
+ .set_fmt = wm8900_set_dai_fmt,
+ .digital_mute = wm8900_digital_mute,
+};
+
+static struct snd_soc_dai_driver wm8900_dai = {
+ .name = "wm8900-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8900_RATES,
+ .formats = WM8900_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8900_RATES,
+ .formats = WM8900_PCM_FORMATS,
+ },
+ .ops = &wm8900_dai_ops,
+};
+
+static int wm8900_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* Enable thermal shutdown */
+ snd_soc_update_bits(codec, WM8900_REG_GPIO,
+ WM8900_REG_GPIO_TEMP_ENA,
+ WM8900_REG_GPIO_TEMP_ENA);
+ snd_soc_update_bits(codec, WM8900_REG_ADDCTL,
+ WM8900_REG_ADDCTL_TEMP_SD,
+ WM8900_REG_ADDCTL_TEMP_SD);
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ /* Charge capacitors if initial power up */
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ /* STARTUP_BIAS_ENA on */
+ snd_soc_write(codec, WM8900_REG_POWER1,
+ WM8900_REG_POWER1_STARTUP_BIAS_ENA);
+
+ /* Startup bias mode */
+ snd_soc_write(codec, WM8900_REG_ADDCTL,
+ WM8900_REG_ADDCTL_BIAS_SRC |
+ WM8900_REG_ADDCTL_VMID_SOFTST);
+
+ /* VMID 2x50k */
+ snd_soc_write(codec, WM8900_REG_POWER1,
+ WM8900_REG_POWER1_STARTUP_BIAS_ENA | 0x1);
+
+ /* Allow capacitors to charge */
+ schedule_timeout_interruptible(msecs_to_jiffies(400));
+
+ /* Enable bias */
+ snd_soc_write(codec, WM8900_REG_POWER1,
+ WM8900_REG_POWER1_STARTUP_BIAS_ENA |
+ WM8900_REG_POWER1_BIAS_ENA | 0x1);
+
+ snd_soc_write(codec, WM8900_REG_ADDCTL, 0);
+
+ snd_soc_write(codec, WM8900_REG_POWER1,
+ WM8900_REG_POWER1_BIAS_ENA | 0x1);
+ }
+
+ reg = snd_soc_read(codec, WM8900_REG_POWER1);
+ snd_soc_write(codec, WM8900_REG_POWER1,
+ (reg & WM8900_REG_POWER1_FLL_ENA) |
+ WM8900_REG_POWER1_BIAS_ENA | 0x1);
+ snd_soc_write(codec, WM8900_REG_POWER2,
+ WM8900_REG_POWER2_SYSCLK_ENA);
+ snd_soc_write(codec, WM8900_REG_POWER3, 0);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Startup bias enable */
+ reg = snd_soc_read(codec, WM8900_REG_POWER1);
+ snd_soc_write(codec, WM8900_REG_POWER1,
+ reg & WM8900_REG_POWER1_STARTUP_BIAS_ENA);
+ snd_soc_write(codec, WM8900_REG_ADDCTL,
+ WM8900_REG_ADDCTL_BIAS_SRC |
+ WM8900_REG_ADDCTL_VMID_SOFTST);
+
+ /* Discharge caps */
+ snd_soc_write(codec, WM8900_REG_POWER1,
+ WM8900_REG_POWER1_STARTUP_BIAS_ENA);
+ schedule_timeout_interruptible(msecs_to_jiffies(500));
+
+ /* Remove clamp */
+ snd_soc_write(codec, WM8900_REG_HPCTL1, 0);
+
+ /* Power down */
+ snd_soc_write(codec, WM8900_REG_ADDCTL, 0);
+ snd_soc_write(codec, WM8900_REG_POWER1, 0);
+ snd_soc_write(codec, WM8900_REG_POWER2, 0);
+ snd_soc_write(codec, WM8900_REG_POWER3, 0);
+
+ /* Need to let things settle before stopping the clock
+ * to ensure that restart works, see "Stopping the
+ * master clock" in the datasheet. */
+ schedule_timeout_interruptible(msecs_to_jiffies(1));
+ snd_soc_write(codec, WM8900_REG_POWER2,
+ WM8900_REG_POWER2_SYSCLK_ENA);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int wm8900_suspend(struct snd_soc_codec *codec)
+{
+ struct wm8900_priv *wm8900 = snd_soc_codec_get_drvdata(codec);
+ int fll_out = wm8900->fll_out;
+ int fll_in = wm8900->fll_in;
+ int ret;
+
+ /* Stop the FLL in an orderly fashion */
+ ret = wm8900_set_fll(codec, 0, 0, 0);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to stop FLL\n");
+ return ret;
+ }
+
+ wm8900->fll_out = fll_out;
+ wm8900->fll_in = fll_in;
+
+ wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int wm8900_resume(struct snd_soc_codec *codec)
+{
+ struct wm8900_priv *wm8900 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ wm8900_reset(codec);
+
+ ret = regcache_sync(wm8900->regmap);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to restore cache: %d\n", ret);
+ return ret;
+ }
+
+ wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Restart the FLL? */
+ if (wm8900->fll_out) {
+ int fll_out = wm8900->fll_out;
+ int fll_in = wm8900->fll_in;
+
+ wm8900->fll_in = 0;
+ wm8900->fll_out = 0;
+
+ ret = wm8900_set_fll(codec, 0, fll_in, fll_out);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to restart FLL\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int wm8900_probe(struct snd_soc_codec *codec)
+{
+ int reg;
+
+ reg = snd_soc_read(codec, WM8900_REG_ID);
+ if (reg != 0x8900) {
+ dev_err(codec->dev, "Device is not a WM8900 - ID %x\n", reg);
+ return -ENODEV;
+ }
+
+ wm8900_reset(codec);
+
+ /* Turn the chip on */
+ wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Latch the volume update bits */
+ snd_soc_update_bits(codec, WM8900_REG_LINVOL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8900_REG_RINVOL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8900_REG_LOUT1CTL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8900_REG_ROUT1CTL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8900_REG_LOUT2CTL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8900_REG_ROUT2CTL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8900_REG_LDAC_DV, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8900_REG_RDAC_DV, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8900_REG_LADC_DV, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8900_REG_RADC_DV, 0x100, 0x100);
+
+ /* Set the DAC and mixer output bias */
+ snd_soc_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8900 = {
+ .probe = wm8900_probe,
+ .suspend = wm8900_suspend,
+ .resume = wm8900_resume,
+ .set_bias_level = wm8900_set_bias_level,
+
+ .controls = wm8900_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8900_snd_controls),
+ .dapm_widgets = wm8900_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8900_dapm_widgets),
+ .dapm_routes = wm8900_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8900_dapm_routes),
+};
+
+static const struct regmap_config wm8900_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = WM8900_MAXREG,
+
+ .reg_defaults = wm8900_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8900_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8900_volatile_register,
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8900_spi_probe(struct spi_device *spi)
+{
+ struct wm8900_priv *wm8900;
+ int ret;
+
+ wm8900 = devm_kzalloc(&spi->dev, sizeof(struct wm8900_priv),
+ GFP_KERNEL);
+ if (wm8900 == NULL)
+ return -ENOMEM;
+
+ wm8900->regmap = devm_regmap_init_spi(spi, &wm8900_regmap);
+ if (IS_ERR(wm8900->regmap))
+ return PTR_ERR(wm8900->regmap);
+
+ spi_set_drvdata(spi, wm8900);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8900, &wm8900_dai, 1);
+
+ return ret;
+}
+
+static int wm8900_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver wm8900_spi_driver = {
+ .driver = {
+ .name = "wm8900",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8900_spi_probe,
+ .remove = wm8900_spi_remove,
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8900_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8900_priv *wm8900;
+ int ret;
+
+ wm8900 = devm_kzalloc(&i2c->dev, sizeof(struct wm8900_priv),
+ GFP_KERNEL);
+ if (wm8900 == NULL)
+ return -ENOMEM;
+
+ wm8900->regmap = devm_regmap_init_i2c(i2c, &wm8900_regmap);
+ if (IS_ERR(wm8900->regmap))
+ return PTR_ERR(wm8900->regmap);
+
+ i2c_set_clientdata(i2c, wm8900);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8900, &wm8900_dai, 1);
+
+ return ret;
+}
+
+static int wm8900_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8900_i2c_id[] = {
+ { "wm8900", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8900_i2c_id);
+
+static struct i2c_driver wm8900_i2c_driver = {
+ .driver = {
+ .name = "wm8900",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8900_i2c_probe,
+ .remove = wm8900_i2c_remove,
+ .id_table = wm8900_i2c_id,
+};
+#endif
+
+static int __init wm8900_modinit(void)
+{
+ int ret = 0;
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8900_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8900 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8900_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8900 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(wm8900_modinit);
+
+static void __exit wm8900_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8900_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8900_spi_driver);
+#endif
+}
+module_exit(wm8900_exit);
+
+MODULE_DESCRIPTION("ASoC WM8900 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8900.h b/kernel/sound/soc/codecs/wm8900.h
new file mode 100644
index 000000000..583f257e7
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8900.h
@@ -0,0 +1,55 @@
+/*
+ * wm8900.h -- WM890 Soc Audio driver
+ *
+ * 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 _WM8900_H
+#define _WM8900_H
+
+#define WM8900_FLL 1
+
+#define WM8900_BCLK_DIV 1
+#define WM8900_ADC_CLKDIV 2
+#define WM8900_DAC_CLKDIV 3
+#define WM8900_ADC_LRCLK 4
+#define WM8900_DAC_LRCLK 5
+#define WM8900_OPCLK_DIV 6
+#define WM8900_LRCLK_MODE 7
+
+#define WM8900_BCLK_DIV_1 0x00
+#define WM8900_BCLK_DIV_1_5 0x02
+#define WM8900_BCLK_DIV_2 0x04
+#define WM8900_BCLK_DIV_3 0x06
+#define WM8900_BCLK_DIV_4 0x08
+#define WM8900_BCLK_DIV_5_5 0x0a
+#define WM8900_BCLK_DIV_6 0x0c
+#define WM8900_BCLK_DIV_8 0x0e
+#define WM8900_BCLK_DIV_11 0x10
+#define WM8900_BCLK_DIV_12 0x12
+#define WM8900_BCLK_DIV_16 0x14
+#define WM8900_BCLK_DIV_22 0x16
+#define WM8900_BCLK_DIV_24 0x18
+#define WM8900_BCLK_DIV_32 0x1a
+#define WM8900_BCLK_DIV_44 0x1c
+#define WM8900_BCLK_DIV_48 0x1e
+
+#define WM8900_ADC_CLKDIV_1 0x00
+#define WM8900_ADC_CLKDIV_1_5 0x20
+#define WM8900_ADC_CLKDIV_2 0x40
+#define WM8900_ADC_CLKDIV_3 0x60
+#define WM8900_ADC_CLKDIV_4 0x80
+#define WM8900_ADC_CLKDIV_5_5 0xa0
+#define WM8900_ADC_CLKDIV_6 0xc0
+
+#define WM8900_DAC_CLKDIV_1 0x00
+#define WM8900_DAC_CLKDIV_1_5 0x04
+#define WM8900_DAC_CLKDIV_2 0x08
+#define WM8900_DAC_CLKDIV_3 0x0c
+#define WM8900_DAC_CLKDIV_4 0x10
+#define WM8900_DAC_CLKDIV_5_5 0x14
+#define WM8900_DAC_CLKDIV_6 0x18
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8903.c b/kernel/sound/soc/codecs/wm8903.c
new file mode 100644
index 000000000..04b04f8e1
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8903.c
@@ -0,0 +1,2210 @@
+/*
+ * wm8903.c -- WM8903 ALSA SoC Audio driver
+ *
+ * Copyright 2008-12 Wolfson Microelectronics
+ * Copyright 2011-2012 NVIDIA, Inc.
+ *
+ * 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.
+ *
+ * TODO:
+ * - TDM mode configuration.
+ * - Digital microphone support.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/wm8903.h>
+#include <trace/events/asoc.h>
+
+#include "wm8903.h"
+
+/* Register defaults at reset */
+static const struct reg_default wm8903_reg_defaults[] = {
+ { 4, 0x0018 }, /* R4 - Bias Control 0 */
+ { 5, 0x0000 }, /* R5 - VMID Control 0 */
+ { 6, 0x0000 }, /* R6 - Mic Bias Control 0 */
+ { 8, 0x0001 }, /* R8 - Analogue DAC 0 */
+ { 10, 0x0001 }, /* R10 - Analogue ADC 0 */
+ { 12, 0x0000 }, /* R12 - Power Management 0 */
+ { 13, 0x0000 }, /* R13 - Power Management 1 */
+ { 14, 0x0000 }, /* R14 - Power Management 2 */
+ { 15, 0x0000 }, /* R15 - Power Management 3 */
+ { 16, 0x0000 }, /* R16 - Power Management 4 */
+ { 17, 0x0000 }, /* R17 - Power Management 5 */
+ { 18, 0x0000 }, /* R18 - Power Management 6 */
+ { 20, 0x0400 }, /* R20 - Clock Rates 0 */
+ { 21, 0x0D07 }, /* R21 - Clock Rates 1 */
+ { 22, 0x0000 }, /* R22 - Clock Rates 2 */
+ { 24, 0x0050 }, /* R24 - Audio Interface 0 */
+ { 25, 0x0242 }, /* R25 - Audio Interface 1 */
+ { 26, 0x0008 }, /* R26 - Audio Interface 2 */
+ { 27, 0x0022 }, /* R27 - Audio Interface 3 */
+ { 30, 0x00C0 }, /* R30 - DAC Digital Volume Left */
+ { 31, 0x00C0 }, /* R31 - DAC Digital Volume Right */
+ { 32, 0x0000 }, /* R32 - DAC Digital 0 */
+ { 33, 0x0000 }, /* R33 - DAC Digital 1 */
+ { 36, 0x00C0 }, /* R36 - ADC Digital Volume Left */
+ { 37, 0x00C0 }, /* R37 - ADC Digital Volume Right */
+ { 38, 0x0000 }, /* R38 - ADC Digital 0 */
+ { 39, 0x0073 }, /* R39 - Digital Microphone 0 */
+ { 40, 0x09BF }, /* R40 - DRC 0 */
+ { 41, 0x3241 }, /* R41 - DRC 1 */
+ { 42, 0x0020 }, /* R42 - DRC 2 */
+ { 43, 0x0000 }, /* R43 - DRC 3 */
+ { 44, 0x0085 }, /* R44 - Analogue Left Input 0 */
+ { 45, 0x0085 }, /* R45 - Analogue Right Input 0 */
+ { 46, 0x0044 }, /* R46 - Analogue Left Input 1 */
+ { 47, 0x0044 }, /* R47 - Analogue Right Input 1 */
+ { 50, 0x0008 }, /* R50 - Analogue Left Mix 0 */
+ { 51, 0x0004 }, /* R51 - Analogue Right Mix 0 */
+ { 52, 0x0000 }, /* R52 - Analogue Spk Mix Left 0 */
+ { 53, 0x0000 }, /* R53 - Analogue Spk Mix Left 1 */
+ { 54, 0x0000 }, /* R54 - Analogue Spk Mix Right 0 */
+ { 55, 0x0000 }, /* R55 - Analogue Spk Mix Right 1 */
+ { 57, 0x002D }, /* R57 - Analogue OUT1 Left */
+ { 58, 0x002D }, /* R58 - Analogue OUT1 Right */
+ { 59, 0x0039 }, /* R59 - Analogue OUT2 Left */
+ { 60, 0x0039 }, /* R60 - Analogue OUT2 Right */
+ { 62, 0x0139 }, /* R62 - Analogue OUT3 Left */
+ { 63, 0x0139 }, /* R63 - Analogue OUT3 Right */
+ { 64, 0x0000 }, /* R65 - Analogue SPK Output Control 0 */
+ { 67, 0x0010 }, /* R67 - DC Servo 0 */
+ { 69, 0x00A4 }, /* R69 - DC Servo 2 */
+ { 90, 0x0000 }, /* R90 - Analogue HP 0 */
+ { 94, 0x0000 }, /* R94 - Analogue Lineout 0 */
+ { 98, 0x0000 }, /* R98 - Charge Pump 0 */
+ { 104, 0x0000 }, /* R104 - Class W 0 */
+ { 108, 0x0000 }, /* R108 - Write Sequencer 0 */
+ { 109, 0x0000 }, /* R109 - Write Sequencer 1 */
+ { 110, 0x0000 }, /* R110 - Write Sequencer 2 */
+ { 111, 0x0000 }, /* R111 - Write Sequencer 3 */
+ { 112, 0x0000 }, /* R112 - Write Sequencer 4 */
+ { 114, 0x0000 }, /* R114 - Control Interface */
+ { 116, 0x00A8 }, /* R116 - GPIO Control 1 */
+ { 117, 0x00A8 }, /* R117 - GPIO Control 2 */
+ { 118, 0x00A8 }, /* R118 - GPIO Control 3 */
+ { 119, 0x0220 }, /* R119 - GPIO Control 4 */
+ { 120, 0x01A0 }, /* R120 - GPIO Control 5 */
+ { 122, 0xFFFF }, /* R122 - Interrupt Status 1 Mask */
+ { 123, 0x0000 }, /* R123 - Interrupt Polarity 1 */
+ { 126, 0x0000 }, /* R126 - Interrupt Control */
+ { 129, 0x0000 }, /* R129 - Control Interface Test 1 */
+ { 149, 0x6810 }, /* R149 - Charge Pump Test 1 */
+ { 164, 0x0028 }, /* R164 - Clock Rate Test 4 */
+ { 172, 0x0000 }, /* R172 - Analogue Output Bias 0 */
+};
+
+struct wm8903_priv {
+ struct wm8903_platform_data *pdata;
+ struct device *dev;
+ struct regmap *regmap;
+
+ int sysclk;
+ int irq;
+
+ struct mutex lock;
+ int fs;
+ int deemph;
+
+ int dcs_pending;
+ int dcs_cache[4];
+
+ /* Reference count */
+ int class_w_users;
+
+ struct snd_soc_jack *mic_jack;
+ int mic_det;
+ int mic_short;
+ int mic_last_report;
+ int mic_delay;
+
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio_chip;
+#endif
+};
+
+static bool wm8903_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8903_SW_RESET_AND_ID:
+ case WM8903_REVISION_NUMBER:
+ case WM8903_BIAS_CONTROL_0:
+ case WM8903_VMID_CONTROL_0:
+ case WM8903_MIC_BIAS_CONTROL_0:
+ case WM8903_ANALOGUE_DAC_0:
+ case WM8903_ANALOGUE_ADC_0:
+ case WM8903_POWER_MANAGEMENT_0:
+ case WM8903_POWER_MANAGEMENT_1:
+ case WM8903_POWER_MANAGEMENT_2:
+ case WM8903_POWER_MANAGEMENT_3:
+ case WM8903_POWER_MANAGEMENT_4:
+ case WM8903_POWER_MANAGEMENT_5:
+ case WM8903_POWER_MANAGEMENT_6:
+ case WM8903_CLOCK_RATES_0:
+ case WM8903_CLOCK_RATES_1:
+ case WM8903_CLOCK_RATES_2:
+ case WM8903_AUDIO_INTERFACE_0:
+ case WM8903_AUDIO_INTERFACE_1:
+ case WM8903_AUDIO_INTERFACE_2:
+ case WM8903_AUDIO_INTERFACE_3:
+ case WM8903_DAC_DIGITAL_VOLUME_LEFT:
+ case WM8903_DAC_DIGITAL_VOLUME_RIGHT:
+ case WM8903_DAC_DIGITAL_0:
+ case WM8903_DAC_DIGITAL_1:
+ case WM8903_ADC_DIGITAL_VOLUME_LEFT:
+ case WM8903_ADC_DIGITAL_VOLUME_RIGHT:
+ case WM8903_ADC_DIGITAL_0:
+ case WM8903_DIGITAL_MICROPHONE_0:
+ case WM8903_DRC_0:
+ case WM8903_DRC_1:
+ case WM8903_DRC_2:
+ case WM8903_DRC_3:
+ case WM8903_ANALOGUE_LEFT_INPUT_0:
+ case WM8903_ANALOGUE_RIGHT_INPUT_0:
+ case WM8903_ANALOGUE_LEFT_INPUT_1:
+ case WM8903_ANALOGUE_RIGHT_INPUT_1:
+ case WM8903_ANALOGUE_LEFT_MIX_0:
+ case WM8903_ANALOGUE_RIGHT_MIX_0:
+ case WM8903_ANALOGUE_SPK_MIX_LEFT_0:
+ case WM8903_ANALOGUE_SPK_MIX_LEFT_1:
+ case WM8903_ANALOGUE_SPK_MIX_RIGHT_0:
+ case WM8903_ANALOGUE_SPK_MIX_RIGHT_1:
+ case WM8903_ANALOGUE_OUT1_LEFT:
+ case WM8903_ANALOGUE_OUT1_RIGHT:
+ case WM8903_ANALOGUE_OUT2_LEFT:
+ case WM8903_ANALOGUE_OUT2_RIGHT:
+ case WM8903_ANALOGUE_OUT3_LEFT:
+ case WM8903_ANALOGUE_OUT3_RIGHT:
+ case WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0:
+ case WM8903_DC_SERVO_0:
+ case WM8903_DC_SERVO_2:
+ case WM8903_DC_SERVO_READBACK_1:
+ case WM8903_DC_SERVO_READBACK_2:
+ case WM8903_DC_SERVO_READBACK_3:
+ case WM8903_DC_SERVO_READBACK_4:
+ case WM8903_ANALOGUE_HP_0:
+ case WM8903_ANALOGUE_LINEOUT_0:
+ case WM8903_CHARGE_PUMP_0:
+ case WM8903_CLASS_W_0:
+ case WM8903_WRITE_SEQUENCER_0:
+ case WM8903_WRITE_SEQUENCER_1:
+ case WM8903_WRITE_SEQUENCER_2:
+ case WM8903_WRITE_SEQUENCER_3:
+ case WM8903_WRITE_SEQUENCER_4:
+ case WM8903_CONTROL_INTERFACE:
+ case WM8903_GPIO_CONTROL_1:
+ case WM8903_GPIO_CONTROL_2:
+ case WM8903_GPIO_CONTROL_3:
+ case WM8903_GPIO_CONTROL_4:
+ case WM8903_GPIO_CONTROL_5:
+ case WM8903_INTERRUPT_STATUS_1:
+ case WM8903_INTERRUPT_STATUS_1_MASK:
+ case WM8903_INTERRUPT_POLARITY_1:
+ case WM8903_INTERRUPT_CONTROL:
+ case WM8903_CLOCK_RATE_TEST_4:
+ case WM8903_ANALOGUE_OUTPUT_BIAS_0:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wm8903_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8903_SW_RESET_AND_ID:
+ case WM8903_REVISION_NUMBER:
+ case WM8903_INTERRUPT_STATUS_1:
+ case WM8903_WRITE_SEQUENCER_4:
+ case WM8903_DC_SERVO_READBACK_1:
+ case WM8903_DC_SERVO_READBACK_2:
+ case WM8903_DC_SERVO_READBACK_3:
+ case WM8903_DC_SERVO_READBACK_4:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static int wm8903_cp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ WARN_ON(event != SND_SOC_DAPM_POST_PMU);
+ mdelay(4);
+
+ return 0;
+}
+
+static int wm8903_dcs_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ wm8903->dcs_pending |= 1 << w->shift;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, WM8903_DC_SERVO_0,
+ 1 << w->shift, 0);
+ break;
+ }
+
+ return 0;
+}
+
+#define WM8903_DCS_MODE_WRITE_STOP 0
+#define WM8903_DCS_MODE_START_STOP 2
+
+static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
+ enum snd_soc_dapm_type event, int subseq)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+ int dcs_mode = WM8903_DCS_MODE_WRITE_STOP;
+ int i, val;
+
+ /* Complete any pending DC servo starts */
+ if (wm8903->dcs_pending) {
+ dev_dbg(codec->dev, "Starting DC servo for %x\n",
+ wm8903->dcs_pending);
+
+ /* If we've no cached values then we need to do startup */
+ for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) {
+ if (!(wm8903->dcs_pending & (1 << i)))
+ continue;
+
+ if (wm8903->dcs_cache[i]) {
+ dev_dbg(codec->dev,
+ "Restore DC servo %d value %x\n",
+ 3 - i, wm8903->dcs_cache[i]);
+
+ snd_soc_write(codec, WM8903_DC_SERVO_4 + i,
+ wm8903->dcs_cache[i] & 0xff);
+ } else {
+ dev_dbg(codec->dev,
+ "Calibrate DC servo %d\n", 3 - i);
+ dcs_mode = WM8903_DCS_MODE_START_STOP;
+ }
+ }
+
+ /* Don't trust the cache for analogue */
+ if (wm8903->class_w_users)
+ dcs_mode = WM8903_DCS_MODE_START_STOP;
+
+ snd_soc_update_bits(codec, WM8903_DC_SERVO_2,
+ WM8903_DCS_MODE_MASK, dcs_mode);
+
+ snd_soc_update_bits(codec, WM8903_DC_SERVO_0,
+ WM8903_DCS_ENA_MASK, wm8903->dcs_pending);
+
+ switch (dcs_mode) {
+ case WM8903_DCS_MODE_WRITE_STOP:
+ break;
+
+ case WM8903_DCS_MODE_START_STOP:
+ msleep(270);
+
+ /* Cache the measured offsets for digital */
+ if (wm8903->class_w_users)
+ break;
+
+ for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) {
+ if (!(wm8903->dcs_pending & (1 << i)))
+ continue;
+
+ val = snd_soc_read(codec,
+ WM8903_DC_SERVO_READBACK_1 + i);
+ dev_dbg(codec->dev, "DC servo %d: %x\n",
+ 3 - i, val);
+ wm8903->dcs_cache[i] = val;
+ }
+ break;
+
+ default:
+ pr_warn("DCS mode %d delay not set\n", dcs_mode);
+ break;
+ }
+
+ wm8903->dcs_pending = 0;
+ }
+}
+
+/*
+ * When used with DAC outputs only the WM8903 charge pump supports
+ * operation in class W mode, providing very low power consumption
+ * when used with digital sources. Enable and disable this mode
+ * automatically depending on the mixer configuration.
+ *
+ * All the relevant controls are simple switches.
+ */
+static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+ u16 reg;
+ int ret;
+
+ reg = snd_soc_read(codec, WM8903_CLASS_W_0);
+
+ /* Turn it off if we're about to enable bypass */
+ if (ucontrol->value.integer.value[0]) {
+ if (wm8903->class_w_users == 0) {
+ dev_dbg(codec->dev, "Disabling Class W\n");
+ snd_soc_write(codec, WM8903_CLASS_W_0, reg &
+ ~(WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V));
+ }
+ wm8903->class_w_users++;
+ }
+
+ /* Implement the change */
+ ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ /* If we've just disabled the last bypass path turn Class W on */
+ if (!ucontrol->value.integer.value[0]) {
+ if (wm8903->class_w_users == 1) {
+ dev_dbg(codec->dev, "Enabling Class W\n");
+ snd_soc_write(codec, WM8903_CLASS_W_0, reg |
+ WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V);
+ }
+ wm8903->class_w_users--;
+ }
+
+ dev_dbg(codec->dev, "Bypass use count now %d\n",
+ wm8903->class_w_users);
+
+ return ret;
+}
+
+#define SOC_DAPM_SINGLE_W(xname, reg, shift, max, invert) \
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, wm8903_class_w_put)
+
+
+static int wm8903_deemph[] = { 0, 32000, 44100, 48000 };
+
+static int wm8903_set_deemph(struct snd_soc_codec *codec)
+{
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+ int val, i, best;
+
+ /* If we're using deemphasis select the nearest available sample
+ * rate.
+ */
+ if (wm8903->deemph) {
+ best = 1;
+ for (i = 2; i < ARRAY_SIZE(wm8903_deemph); i++) {
+ if (abs(wm8903_deemph[i] - wm8903->fs) <
+ abs(wm8903_deemph[best] - wm8903->fs))
+ best = i;
+ }
+
+ val = best << WM8903_DEEMPH_SHIFT;
+ } else {
+ best = 0;
+ val = 0;
+ }
+
+ dev_dbg(codec->dev, "Set deemphasis %d (%dHz)\n",
+ best, wm8903_deemph[best]);
+
+ return snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1,
+ WM8903_DEEMPH_MASK, val);
+}
+
+static int wm8903_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8903->deemph;
+
+ return 0;
+}
+
+static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+ int deemph = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ mutex_lock(&wm8903->lock);
+ if (wm8903->deemph != deemph) {
+ wm8903->deemph = deemph;
+
+ wm8903_set_deemph(codec);
+
+ ret = 1;
+ }
+ mutex_unlock(&wm8903->lock);
+
+ return ret;
+}
+
+/* ALSA can only do steps of .01dB */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+
+static const DECLARE_TLV_DB_SCALE(digital_sidetone_tlv, -3600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(drc_tlv_thresh, 0, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_tlv_amp, -2250, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_tlv_min, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(drc_tlv_max, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(drc_tlv_startup, -300, 50, 0);
+
+static const char *hpf_mode_text[] = {
+ "Hi-fi", "Voice 1", "Voice 2", "Voice 3"
+};
+
+static SOC_ENUM_SINGLE_DECL(hpf_mode,
+ WM8903_ADC_DIGITAL_0, 5, hpf_mode_text);
+
+static const char *osr_text[] = {
+ "Low power", "High performance"
+};
+
+static SOC_ENUM_SINGLE_DECL(adc_osr,
+ WM8903_ANALOGUE_ADC_0, 0, osr_text);
+
+static SOC_ENUM_SINGLE_DECL(dac_osr,
+ WM8903_DAC_DIGITAL_1, 0, osr_text);
+
+static const char *drc_slope_text[] = {
+ "1", "1/2", "1/4", "1/8", "1/16", "0"
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_slope_r0,
+ WM8903_DRC_2, 3, drc_slope_text);
+
+static SOC_ENUM_SINGLE_DECL(drc_slope_r1,
+ WM8903_DRC_2, 0, drc_slope_text);
+
+static const char *drc_attack_text[] = {
+ "instantaneous",
+ "363us", "762us", "1.45ms", "2.9ms", "5.8ms", "11.6ms", "23.2ms",
+ "46.4ms", "92.8ms", "185.6ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_attack,
+ WM8903_DRC_1, 12, drc_attack_text);
+
+static const char *drc_decay_text[] = {
+ "186ms", "372ms", "743ms", "1.49s", "2.97s", "5.94s", "11.89s",
+ "23.87s", "47.56s"
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_decay,
+ WM8903_DRC_1, 8, drc_decay_text);
+
+static const char *drc_ff_delay_text[] = {
+ "5 samples", "9 samples"
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_ff_delay,
+ WM8903_DRC_0, 5, drc_ff_delay_text);
+
+static const char *drc_qr_decay_text[] = {
+ "0.725ms", "1.45ms", "5.8ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_qr_decay,
+ WM8903_DRC_1, 4, drc_qr_decay_text);
+
+static const char *drc_smoothing_text[] = {
+ "Low", "Medium", "High"
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_smoothing,
+ WM8903_DRC_0, 11, drc_smoothing_text);
+
+static const char *soft_mute_text[] = {
+ "Fast (fs/2)", "Slow (fs/32)"
+};
+
+static SOC_ENUM_SINGLE_DECL(soft_mute,
+ WM8903_DAC_DIGITAL_1, 10, soft_mute_text);
+
+static const char *mute_mode_text[] = {
+ "Hard", "Soft"
+};
+
+static SOC_ENUM_SINGLE_DECL(mute_mode,
+ WM8903_DAC_DIGITAL_1, 9, mute_mode_text);
+
+static const char *companding_text[] = {
+ "ulaw", "alaw"
+};
+
+static SOC_ENUM_SINGLE_DECL(dac_companding,
+ WM8903_AUDIO_INTERFACE_0, 0, companding_text);
+
+static SOC_ENUM_SINGLE_DECL(adc_companding,
+ WM8903_AUDIO_INTERFACE_0, 2, companding_text);
+
+static const char *input_mode_text[] = {
+ "Single-Ended", "Differential Line", "Differential Mic"
+};
+
+static SOC_ENUM_SINGLE_DECL(linput_mode_enum,
+ WM8903_ANALOGUE_LEFT_INPUT_1, 0, input_mode_text);
+
+static SOC_ENUM_SINGLE_DECL(rinput_mode_enum,
+ WM8903_ANALOGUE_RIGHT_INPUT_1, 0, input_mode_text);
+
+static const char *linput_mux_text[] = {
+ "IN1L", "IN2L", "IN3L"
+};
+
+static SOC_ENUM_SINGLE_DECL(linput_enum,
+ WM8903_ANALOGUE_LEFT_INPUT_1, 2, linput_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(linput_inv_enum,
+ WM8903_ANALOGUE_LEFT_INPUT_1, 4, linput_mux_text);
+
+static const char *rinput_mux_text[] = {
+ "IN1R", "IN2R", "IN3R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rinput_enum,
+ WM8903_ANALOGUE_RIGHT_INPUT_1, 2, rinput_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rinput_inv_enum,
+ WM8903_ANALOGUE_RIGHT_INPUT_1, 4, rinput_mux_text);
+
+
+static const char *sidetone_text[] = {
+ "None", "Left", "Right"
+};
+
+static SOC_ENUM_SINGLE_DECL(lsidetone_enum,
+ WM8903_DAC_DIGITAL_0, 2, sidetone_text);
+
+static SOC_ENUM_SINGLE_DECL(rsidetone_enum,
+ WM8903_DAC_DIGITAL_0, 0, sidetone_text);
+
+static const char *adcinput_text[] = {
+ "ADC", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(adcinput_enum,
+ WM8903_CLOCK_RATE_TEST_4, 9, adcinput_text);
+
+static const char *aif_text[] = {
+ "Left", "Right"
+};
+
+static SOC_ENUM_SINGLE_DECL(lcapture_enum,
+ WM8903_AUDIO_INTERFACE_0, 7, aif_text);
+
+static SOC_ENUM_SINGLE_DECL(rcapture_enum,
+ WM8903_AUDIO_INTERFACE_0, 6, aif_text);
+
+static SOC_ENUM_SINGLE_DECL(lplay_enum,
+ WM8903_AUDIO_INTERFACE_0, 5, aif_text);
+
+static SOC_ENUM_SINGLE_DECL(rplay_enum,
+ WM8903_AUDIO_INTERFACE_0, 4, aif_text);
+
+static const struct snd_kcontrol_new wm8903_snd_controls[] = {
+
+/* Input PGAs - No TLV since the scale depends on PGA mode */
+SOC_SINGLE("Left Input PGA Switch", WM8903_ANALOGUE_LEFT_INPUT_0,
+ 7, 1, 1),
+SOC_SINGLE("Left Input PGA Volume", WM8903_ANALOGUE_LEFT_INPUT_0,
+ 0, 31, 0),
+SOC_SINGLE("Left Input PGA Common Mode Switch", WM8903_ANALOGUE_LEFT_INPUT_1,
+ 6, 1, 0),
+
+SOC_SINGLE("Right Input PGA Switch", WM8903_ANALOGUE_RIGHT_INPUT_0,
+ 7, 1, 1),
+SOC_SINGLE("Right Input PGA Volume", WM8903_ANALOGUE_RIGHT_INPUT_0,
+ 0, 31, 0),
+SOC_SINGLE("Right Input PGA Common Mode Switch", WM8903_ANALOGUE_RIGHT_INPUT_1,
+ 6, 1, 0),
+
+/* ADCs */
+SOC_ENUM("ADC OSR", adc_osr),
+SOC_SINGLE("HPF Switch", WM8903_ADC_DIGITAL_0, 4, 1, 0),
+SOC_ENUM("HPF Mode", hpf_mode),
+SOC_SINGLE("DRC Switch", WM8903_DRC_0, 15, 1, 0),
+SOC_ENUM("DRC Compressor Slope R0", drc_slope_r0),
+SOC_ENUM("DRC Compressor Slope R1", drc_slope_r1),
+SOC_SINGLE_TLV("DRC Compressor Threshold Volume", WM8903_DRC_3, 5, 124, 1,
+ drc_tlv_thresh),
+SOC_SINGLE_TLV("DRC Volume", WM8903_DRC_3, 0, 30, 1, drc_tlv_amp),
+SOC_SINGLE_TLV("DRC Minimum Gain Volume", WM8903_DRC_1, 2, 3, 1, drc_tlv_min),
+SOC_SINGLE_TLV("DRC Maximum Gain Volume", WM8903_DRC_1, 0, 3, 0, drc_tlv_max),
+SOC_ENUM("DRC Attack Rate", drc_attack),
+SOC_ENUM("DRC Decay Rate", drc_decay),
+SOC_ENUM("DRC FF Delay", drc_ff_delay),
+SOC_SINGLE("DRC Anticlip Switch", WM8903_DRC_0, 1, 1, 0),
+SOC_SINGLE("DRC QR Switch", WM8903_DRC_0, 2, 1, 0),
+SOC_SINGLE_TLV("DRC QR Threshold Volume", WM8903_DRC_0, 6, 3, 0, drc_tlv_max),
+SOC_ENUM("DRC QR Decay Rate", drc_qr_decay),
+SOC_SINGLE("DRC Smoothing Switch", WM8903_DRC_0, 3, 1, 0),
+SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8903_DRC_0, 0, 1, 0),
+SOC_ENUM("DRC Smoothing Threshold", drc_smoothing),
+SOC_SINGLE_TLV("DRC Startup Volume", WM8903_DRC_0, 6, 18, 0, drc_tlv_startup),
+
+SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8903_ADC_DIGITAL_VOLUME_LEFT,
+ WM8903_ADC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv),
+SOC_ENUM("ADC Companding Mode", adc_companding),
+SOC_SINGLE("ADC Companding Switch", WM8903_AUDIO_INTERFACE_0, 3, 1, 0),
+
+SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8903_DAC_DIGITAL_0, 4, 8,
+ 12, 0, digital_sidetone_tlv),
+
+/* DAC */
+SOC_ENUM("DAC OSR", dac_osr),
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT,
+ WM8903_DAC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv),
+SOC_ENUM("DAC Soft Mute Rate", soft_mute),
+SOC_ENUM("DAC Mute Mode", mute_mode),
+SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0),
+SOC_ENUM("DAC Companding Mode", dac_companding),
+SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0),
+SOC_SINGLE_TLV("DAC Boost Volume", WM8903_AUDIO_INTERFACE_0, 9, 3, 0,
+ dac_boost_tlv),
+SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
+ wm8903_get_deemph, wm8903_put_deemph),
+
+/* Headphones */
+SOC_DOUBLE_R("Headphone Switch",
+ WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT,
+ 8, 1, 1),
+SOC_DOUBLE_R("Headphone ZC Switch",
+ WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT,
+ 6, 1, 0),
+SOC_DOUBLE_R_TLV("Headphone Volume",
+ WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT,
+ 0, 63, 0, out_tlv),
+
+/* Line out */
+SOC_DOUBLE_R("Line Out Switch",
+ WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT,
+ 8, 1, 1),
+SOC_DOUBLE_R("Line Out ZC Switch",
+ WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT,
+ 6, 1, 0),
+SOC_DOUBLE_R_TLV("Line Out Volume",
+ WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT,
+ 0, 63, 0, out_tlv),
+
+/* Speaker */
+SOC_DOUBLE_R("Speaker Switch",
+ WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 8, 1, 1),
+SOC_DOUBLE_R("Speaker ZC Switch",
+ WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 6, 1, 0),
+SOC_DOUBLE_R_TLV("Speaker Volume",
+ WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT,
+ 0, 63, 0, out_tlv),
+};
+
+static const struct snd_kcontrol_new linput_mode_mux =
+ SOC_DAPM_ENUM("Left Input Mode Mux", linput_mode_enum);
+
+static const struct snd_kcontrol_new rinput_mode_mux =
+ SOC_DAPM_ENUM("Right Input Mode Mux", rinput_mode_enum);
+
+static const struct snd_kcontrol_new linput_mux =
+ SOC_DAPM_ENUM("Left Input Mux", linput_enum);
+
+static const struct snd_kcontrol_new linput_inv_mux =
+ SOC_DAPM_ENUM("Left Inverting Input Mux", linput_inv_enum);
+
+static const struct snd_kcontrol_new rinput_mux =
+ SOC_DAPM_ENUM("Right Input Mux", rinput_enum);
+
+static const struct snd_kcontrol_new rinput_inv_mux =
+ SOC_DAPM_ENUM("Right Inverting Input Mux", rinput_inv_enum);
+
+static const struct snd_kcontrol_new lsidetone_mux =
+ SOC_DAPM_ENUM("DACL Sidetone Mux", lsidetone_enum);
+
+static const struct snd_kcontrol_new rsidetone_mux =
+ SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum);
+
+static const struct snd_kcontrol_new adcinput_mux =
+ SOC_DAPM_ENUM("ADC Input", adcinput_enum);
+
+static const struct snd_kcontrol_new lcapture_mux =
+ SOC_DAPM_ENUM("Left Capture Mux", lcapture_enum);
+
+static const struct snd_kcontrol_new rcapture_mux =
+ SOC_DAPM_ENUM("Right Capture Mux", rcapture_enum);
+
+static const struct snd_kcontrol_new lplay_mux =
+ SOC_DAPM_ENUM("Left Playback Mux", lplay_enum);
+
+static const struct snd_kcontrol_new rplay_mux =
+ SOC_DAPM_ENUM("Right Playback Mux", rplay_enum);
+
+static const struct snd_kcontrol_new left_output_mixer[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0),
+SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
+SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_output_mixer[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 3, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 2, 1, 0),
+SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
+SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_speaker_mixer[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 3, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 2, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 1, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_mixer[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 3, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 2, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
+ 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+SND_SOC_DAPM_INPUT("IN3L"),
+SND_SOC_DAPM_INPUT("IN3R"),
+SND_SOC_DAPM_INPUT("DMICDAT"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+SND_SOC_DAPM_OUTPUT("LOP"),
+SND_SOC_DAPM_OUTPUT("LON"),
+SND_SOC_DAPM_OUTPUT("ROP"),
+SND_SOC_DAPM_OUTPUT("RON"),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS", WM8903_MIC_BIAS_CONTROL_0, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("Left Input Mux", SND_SOC_NOPM, 0, 0, &linput_mux),
+SND_SOC_DAPM_MUX("Left Input Inverting Mux", SND_SOC_NOPM, 0, 0,
+ &linput_inv_mux),
+SND_SOC_DAPM_MUX("Left Input Mode Mux", SND_SOC_NOPM, 0, 0, &linput_mode_mux),
+
+SND_SOC_DAPM_MUX("Right Input Mux", SND_SOC_NOPM, 0, 0, &rinput_mux),
+SND_SOC_DAPM_MUX("Right Input Inverting Mux", SND_SOC_NOPM, 0, 0,
+ &rinput_inv_mux),
+SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux),
+
+SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("Left ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux),
+SND_SOC_DAPM_MUX("Right ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8903_POWER_MANAGEMENT_6, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8903_POWER_MANAGEMENT_6, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lcapture_mux),
+SND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rcapture_mux),
+
+SND_SOC_DAPM_AIF_OUT("AIFTXL", "Left HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFTXR", "Right HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &lsidetone_mux),
+SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &rsidetone_mux),
+
+SND_SOC_DAPM_AIF_IN("AIFRXL", "Left Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFRXR", "Right Playback", 0, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Playback Mux", SND_SOC_NOPM, 0, 0, &lplay_mux),
+SND_SOC_DAPM_MUX("Right Playback Mux", SND_SOC_NOPM, 0, 0, &rplay_mux),
+
+SND_SOC_DAPM_DAC("DACL", NULL, WM8903_POWER_MANAGEMENT_6, 3, 0),
+SND_SOC_DAPM_DAC("DACR", NULL, WM8903_POWER_MANAGEMENT_6, 2, 0),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", WM8903_POWER_MANAGEMENT_1, 1, 0,
+ left_output_mixer, ARRAY_SIZE(left_output_mixer)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", WM8903_POWER_MANAGEMENT_1, 0, 0,
+ right_output_mixer, ARRAY_SIZE(right_output_mixer)),
+
+SND_SOC_DAPM_MIXER("Left Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 1, 0,
+ left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
+SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0,
+ right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
+
+SND_SOC_DAPM_PGA_S("Left Headphone Output PGA", 0, WM8903_POWER_MANAGEMENT_2,
+ 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("Right Headphone Output PGA", 0, WM8903_POWER_MANAGEMENT_2,
+ 0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("Left Line Output PGA", 0, WM8903_POWER_MANAGEMENT_3, 1, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA_S("Right Line Output PGA", 0, WM8903_POWER_MANAGEMENT_3, 0, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA_S("HPL_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPL_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPL_ENA_DLY", 2, WM8903_ANALOGUE_HP_0, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPL_ENA", 1, WM8903_ANALOGUE_HP_0, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPR_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPR_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPR_ENA_DLY", 2, WM8903_ANALOGUE_HP_0, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPR_ENA", 1, WM8903_ANALOGUE_HP_0, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("LINEOUTL_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 7, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 6, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_DLY", 2, WM8903_ANALOGUE_LINEOUT_0, 5, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTL_ENA", 1, WM8903_ANALOGUE_LINEOUT_0, 4, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTR_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 3, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 2, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_DLY", 2, WM8903_ANALOGUE_LINEOUT_0, 1, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTR_ENA", 1, WM8903_ANALOGUE_LINEOUT_0, 0, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("DCS Master", WM8903_DC_SERVO_0, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPL_DCS", 3, SND_SOC_NOPM, 3, 0, wm8903_dcs_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_S("HPR_DCS", 3, SND_SOC_NOPM, 2, 0, wm8903_dcs_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_S("LINEOUTL_DCS", 3, SND_SOC_NOPM, 1, 0, wm8903_dcs_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_S("LINEOUTR_DCS", 3, SND_SOC_NOPM, 0, 0, wm8903_dcs_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("Charge Pump", WM8903_CHARGE_PUMP_0, 0, 0,
+ wm8903_cp_event, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8903_CLOCK_RATES_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route wm8903_intercon[] = {
+
+ { "CLK_DSP", NULL, "CLK_SYS" },
+ { "MICBIAS", NULL, "CLK_SYS" },
+ { "HPL_DCS", NULL, "CLK_SYS" },
+ { "HPR_DCS", NULL, "CLK_SYS" },
+ { "LINEOUTL_DCS", NULL, "CLK_SYS" },
+ { "LINEOUTR_DCS", NULL, "CLK_SYS" },
+
+ { "Left Input Mux", "IN1L", "IN1L" },
+ { "Left Input Mux", "IN2L", "IN2L" },
+ { "Left Input Mux", "IN3L", "IN3L" },
+
+ { "Left Input Inverting Mux", "IN1L", "IN1L" },
+ { "Left Input Inverting Mux", "IN2L", "IN2L" },
+ { "Left Input Inverting Mux", "IN3L", "IN3L" },
+
+ { "Right Input Mux", "IN1R", "IN1R" },
+ { "Right Input Mux", "IN2R", "IN2R" },
+ { "Right Input Mux", "IN3R", "IN3R" },
+
+ { "Right Input Inverting Mux", "IN1R", "IN1R" },
+ { "Right Input Inverting Mux", "IN2R", "IN2R" },
+ { "Right Input Inverting Mux", "IN3R", "IN3R" },
+
+ { "Left Input Mode Mux", "Single-Ended", "Left Input Inverting Mux" },
+ { "Left Input Mode Mux", "Differential Line",
+ "Left Input Mux" },
+ { "Left Input Mode Mux", "Differential Line",
+ "Left Input Inverting Mux" },
+ { "Left Input Mode Mux", "Differential Mic",
+ "Left Input Mux" },
+ { "Left Input Mode Mux", "Differential Mic",
+ "Left Input Inverting Mux" },
+
+ { "Right Input Mode Mux", "Single-Ended",
+ "Right Input Inverting Mux" },
+ { "Right Input Mode Mux", "Differential Line",
+ "Right Input Mux" },
+ { "Right Input Mode Mux", "Differential Line",
+ "Right Input Inverting Mux" },
+ { "Right Input Mode Mux", "Differential Mic",
+ "Right Input Mux" },
+ { "Right Input Mode Mux", "Differential Mic",
+ "Right Input Inverting Mux" },
+
+ { "Left Input PGA", NULL, "Left Input Mode Mux" },
+ { "Right Input PGA", NULL, "Right Input Mode Mux" },
+
+ { "Left ADC Input", "ADC", "Left Input PGA" },
+ { "Left ADC Input", "DMIC", "DMICDAT" },
+ { "Right ADC Input", "ADC", "Right Input PGA" },
+ { "Right ADC Input", "DMIC", "DMICDAT" },
+
+ { "Left Capture Mux", "Left", "ADCL" },
+ { "Left Capture Mux", "Right", "ADCR" },
+
+ { "Right Capture Mux", "Left", "ADCL" },
+ { "Right Capture Mux", "Right", "ADCR" },
+
+ { "AIFTXL", NULL, "Left Capture Mux" },
+ { "AIFTXR", NULL, "Right Capture Mux" },
+
+ { "ADCL", NULL, "Left ADC Input" },
+ { "ADCL", NULL, "CLK_DSP" },
+ { "ADCR", NULL, "Right ADC Input" },
+ { "ADCR", NULL, "CLK_DSP" },
+
+ { "Left Playback Mux", "Left", "AIFRXL" },
+ { "Left Playback Mux", "Right", "AIFRXR" },
+
+ { "Right Playback Mux", "Left", "AIFRXL" },
+ { "Right Playback Mux", "Right", "AIFRXR" },
+
+ { "DACL Sidetone", "Left", "ADCL" },
+ { "DACL Sidetone", "Right", "ADCR" },
+ { "DACR Sidetone", "Left", "ADCL" },
+ { "DACR Sidetone", "Right", "ADCR" },
+
+ { "DACL", NULL, "Left Playback Mux" },
+ { "DACL", NULL, "DACL Sidetone" },
+ { "DACL", NULL, "CLK_DSP" },
+
+ { "DACR", NULL, "Right Playback Mux" },
+ { "DACR", NULL, "DACR Sidetone" },
+ { "DACR", NULL, "CLK_DSP" },
+
+ { "Left Output Mixer", "Left Bypass Switch", "Left Input PGA" },
+ { "Left Output Mixer", "Right Bypass Switch", "Right Input PGA" },
+ { "Left Output Mixer", "DACL Switch", "DACL" },
+ { "Left Output Mixer", "DACR Switch", "DACR" },
+
+ { "Right Output Mixer", "Left Bypass Switch", "Left Input PGA" },
+ { "Right Output Mixer", "Right Bypass Switch", "Right Input PGA" },
+ { "Right Output Mixer", "DACL Switch", "DACL" },
+ { "Right Output Mixer", "DACR Switch", "DACR" },
+
+ { "Left Speaker Mixer", "Left Bypass Switch", "Left Input PGA" },
+ { "Left Speaker Mixer", "Right Bypass Switch", "Right Input PGA" },
+ { "Left Speaker Mixer", "DACL Switch", "DACL" },
+ { "Left Speaker Mixer", "DACR Switch", "DACR" },
+
+ { "Right Speaker Mixer", "Left Bypass Switch", "Left Input PGA" },
+ { "Right Speaker Mixer", "Right Bypass Switch", "Right Input PGA" },
+ { "Right Speaker Mixer", "DACL Switch", "DACL" },
+ { "Right Speaker Mixer", "DACR Switch", "DACR" },
+
+ { "Left Line Output PGA", NULL, "Left Output Mixer" },
+ { "Right Line Output PGA", NULL, "Right Output Mixer" },
+
+ { "Left Headphone Output PGA", NULL, "Left Output Mixer" },
+ { "Right Headphone Output PGA", NULL, "Right Output Mixer" },
+
+ { "Left Speaker PGA", NULL, "Left Speaker Mixer" },
+ { "Right Speaker PGA", NULL, "Right Speaker Mixer" },
+
+ { "HPL_ENA", NULL, "Left Headphone Output PGA" },
+ { "HPR_ENA", NULL, "Right Headphone Output PGA" },
+ { "HPL_ENA_DLY", NULL, "HPL_ENA" },
+ { "HPR_ENA_DLY", NULL, "HPR_ENA" },
+ { "LINEOUTL_ENA", NULL, "Left Line Output PGA" },
+ { "LINEOUTR_ENA", NULL, "Right Line Output PGA" },
+ { "LINEOUTL_ENA_DLY", NULL, "LINEOUTL_ENA" },
+ { "LINEOUTR_ENA_DLY", NULL, "LINEOUTR_ENA" },
+
+ { "HPL_DCS", NULL, "DCS Master" },
+ { "HPR_DCS", NULL, "DCS Master" },
+ { "LINEOUTL_DCS", NULL, "DCS Master" },
+ { "LINEOUTR_DCS", NULL, "DCS Master" },
+
+ { "HPL_DCS", NULL, "HPL_ENA_DLY" },
+ { "HPR_DCS", NULL, "HPR_ENA_DLY" },
+ { "LINEOUTL_DCS", NULL, "LINEOUTL_ENA_DLY" },
+ { "LINEOUTR_DCS", NULL, "LINEOUTR_ENA_DLY" },
+
+ { "HPL_ENA_OUTP", NULL, "HPL_DCS" },
+ { "HPR_ENA_OUTP", NULL, "HPR_DCS" },
+ { "LINEOUTL_ENA_OUTP", NULL, "LINEOUTL_DCS" },
+ { "LINEOUTR_ENA_OUTP", NULL, "LINEOUTR_DCS" },
+
+ { "HPL_RMV_SHORT", NULL, "HPL_ENA_OUTP" },
+ { "HPR_RMV_SHORT", NULL, "HPR_ENA_OUTP" },
+ { "LINEOUTL_RMV_SHORT", NULL, "LINEOUTL_ENA_OUTP" },
+ { "LINEOUTR_RMV_SHORT", NULL, "LINEOUTR_ENA_OUTP" },
+
+ { "HPOUTL", NULL, "HPL_RMV_SHORT" },
+ { "HPOUTR", NULL, "HPR_RMV_SHORT" },
+ { "LINEOUTL", NULL, "LINEOUTL_RMV_SHORT" },
+ { "LINEOUTR", NULL, "LINEOUTR_RMV_SHORT" },
+
+ { "LOP", NULL, "Left Speaker PGA" },
+ { "LON", NULL, "Left Speaker PGA" },
+
+ { "ROP", NULL, "Right Speaker PGA" },
+ { "RON", NULL, "Right Speaker PGA" },
+
+ { "Charge Pump", NULL, "CLK_DSP" },
+
+ { "Left Headphone Output PGA", NULL, "Charge Pump" },
+ { "Right Headphone Output PGA", NULL, "Charge Pump" },
+ { "Left Line Output PGA", NULL, "Charge Pump" },
+ { "Right Line Output PGA", NULL, "Charge Pump" },
+};
+
+static int wm8903_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ WM8903_VMID_RES_MASK,
+ WM8903_VMID_RES_50K);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+ WM8903_POBCTRL | WM8903_ISEL_MASK |
+ WM8903_STARTUP_BIAS_ENA |
+ WM8903_BIAS_ENA,
+ WM8903_POBCTRL |
+ (2 << WM8903_ISEL_SHIFT) |
+ WM8903_STARTUP_BIAS_ENA);
+
+ snd_soc_update_bits(codec,
+ WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0,
+ WM8903_SPK_DISCHARGE,
+ WM8903_SPK_DISCHARGE);
+
+ msleep(33);
+
+ snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5,
+ WM8903_SPKL_ENA | WM8903_SPKR_ENA,
+ WM8903_SPKL_ENA | WM8903_SPKR_ENA);
+
+ snd_soc_update_bits(codec,
+ WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0,
+ WM8903_SPK_DISCHARGE, 0);
+
+ snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ WM8903_VMID_TIE_ENA |
+ WM8903_BUFIO_ENA |
+ WM8903_VMID_IO_ENA |
+ WM8903_VMID_SOFT_MASK |
+ WM8903_VMID_RES_MASK |
+ WM8903_VMID_BUF_ENA,
+ WM8903_VMID_TIE_ENA |
+ WM8903_BUFIO_ENA |
+ WM8903_VMID_IO_ENA |
+ (2 << WM8903_VMID_SOFT_SHIFT) |
+ WM8903_VMID_RES_250K |
+ WM8903_VMID_BUF_ENA);
+
+ msleep(129);
+
+ snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5,
+ WM8903_SPKL_ENA | WM8903_SPKR_ENA,
+ 0);
+
+ snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ WM8903_VMID_SOFT_MASK, 0);
+
+ snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ WM8903_VMID_RES_MASK,
+ WM8903_VMID_RES_50K);
+
+ snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+ WM8903_BIAS_ENA | WM8903_POBCTRL,
+ WM8903_BIAS_ENA);
+
+ /* By default no bypass paths are enabled so
+ * enable Class W support.
+ */
+ dev_dbg(codec->dev, "Enabling Class W\n");
+ snd_soc_update_bits(codec, WM8903_CLASS_W_0,
+ WM8903_CP_DYN_FREQ |
+ WM8903_CP_DYN_V,
+ WM8903_CP_DYN_FREQ |
+ WM8903_CP_DYN_V);
+ }
+
+ snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ WM8903_VMID_RES_MASK,
+ WM8903_VMID_RES_250K);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+ WM8903_BIAS_ENA, 0);
+
+ snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ WM8903_VMID_SOFT_MASK,
+ 2 << WM8903_VMID_SOFT_SHIFT);
+
+ snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ WM8903_VMID_BUF_ENA, 0);
+
+ msleep(290);
+
+ snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+ WM8903_VMID_TIE_ENA | WM8903_BUFIO_ENA |
+ WM8903_VMID_IO_ENA | WM8903_VMID_RES_MASK |
+ WM8903_VMID_SOFT_MASK |
+ WM8903_VMID_BUF_ENA, 0);
+
+ snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+ WM8903_STARTUP_BIAS_ENA, 0);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int wm8903_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+
+ wm8903->sysclk = freq;
+
+ return 0;
+}
+
+static int wm8903_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 aif1 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_1);
+
+ aif1 &= ~(WM8903_LRCLK_DIR | WM8903_BCLK_DIR | WM8903_AIF_FMT_MASK |
+ WM8903_AIF_LRCLK_INV | WM8903_AIF_BCLK_INV);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ aif1 |= WM8903_LRCLK_DIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif1 |= WM8903_LRCLK_DIR | WM8903_BCLK_DIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ aif1 |= WM8903_BCLK_DIR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ aif1 |= 0x3;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aif1 |= 0x3 | WM8903_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aif1 |= 0x2;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ aif1 |= 0x1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8903_AIF_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif1 |= WM8903_AIF_BCLK_INV | WM8903_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8903_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif1 |= WM8903_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
+
+ return 0;
+}
+
+static int wm8903_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ reg = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);
+
+ if (mute)
+ reg |= WM8903_DAC_MUTE;
+ else
+ reg &= ~WM8903_DAC_MUTE;
+
+ snd_soc_write(codec, WM8903_DAC_DIGITAL_1, reg);
+
+ return 0;
+}
+
+/* Lookup table for CLK_SYS/fs ratio. 256fs or more is recommended
+ * for optimal performance so we list the lower rates first and match
+ * on the last match we find. */
+static struct {
+ int div;
+ int rate;
+ int mode;
+ int mclk_div;
+} clk_sys_ratios[] = {
+ { 64, 0x0, 0x0, 1 },
+ { 68, 0x0, 0x1, 1 },
+ { 125, 0x0, 0x2, 1 },
+ { 128, 0x1, 0x0, 1 },
+ { 136, 0x1, 0x1, 1 },
+ { 192, 0x2, 0x0, 1 },
+ { 204, 0x2, 0x1, 1 },
+
+ { 64, 0x0, 0x0, 2 },
+ { 68, 0x0, 0x1, 2 },
+ { 125, 0x0, 0x2, 2 },
+ { 128, 0x1, 0x0, 2 },
+ { 136, 0x1, 0x1, 2 },
+ { 192, 0x2, 0x0, 2 },
+ { 204, 0x2, 0x1, 2 },
+
+ { 250, 0x2, 0x2, 1 },
+ { 256, 0x3, 0x0, 1 },
+ { 272, 0x3, 0x1, 1 },
+ { 384, 0x4, 0x0, 1 },
+ { 408, 0x4, 0x1, 1 },
+ { 375, 0x4, 0x2, 1 },
+ { 512, 0x5, 0x0, 1 },
+ { 544, 0x5, 0x1, 1 },
+ { 500, 0x5, 0x2, 1 },
+ { 768, 0x6, 0x0, 1 },
+ { 816, 0x6, 0x1, 1 },
+ { 750, 0x6, 0x2, 1 },
+ { 1024, 0x7, 0x0, 1 },
+ { 1088, 0x7, 0x1, 1 },
+ { 1000, 0x7, 0x2, 1 },
+ { 1408, 0x8, 0x0, 1 },
+ { 1496, 0x8, 0x1, 1 },
+ { 1536, 0x9, 0x0, 1 },
+ { 1632, 0x9, 0x1, 1 },
+ { 1500, 0x9, 0x2, 1 },
+
+ { 250, 0x2, 0x2, 2 },
+ { 256, 0x3, 0x0, 2 },
+ { 272, 0x3, 0x1, 2 },
+ { 384, 0x4, 0x0, 2 },
+ { 408, 0x4, 0x1, 2 },
+ { 375, 0x4, 0x2, 2 },
+ { 512, 0x5, 0x0, 2 },
+ { 544, 0x5, 0x1, 2 },
+ { 500, 0x5, 0x2, 2 },
+ { 768, 0x6, 0x0, 2 },
+ { 816, 0x6, 0x1, 2 },
+ { 750, 0x6, 0x2, 2 },
+ { 1024, 0x7, 0x0, 2 },
+ { 1088, 0x7, 0x1, 2 },
+ { 1000, 0x7, 0x2, 2 },
+ { 1408, 0x8, 0x0, 2 },
+ { 1496, 0x8, 0x1, 2 },
+ { 1536, 0x9, 0x0, 2 },
+ { 1632, 0x9, 0x1, 2 },
+ { 1500, 0x9, 0x2, 2 },
+};
+
+/* CLK_SYS/BCLK ratios - multiplied by 10 due to .5s */
+static struct {
+ int ratio;
+ int div;
+} bclk_divs[] = {
+ { 10, 0 },
+ { 20, 2 },
+ { 30, 3 },
+ { 40, 4 },
+ { 50, 5 },
+ { 60, 7 },
+ { 80, 8 },
+ { 100, 9 },
+ { 120, 11 },
+ { 160, 12 },
+ { 200, 13 },
+ { 220, 14 },
+ { 240, 15 },
+ { 300, 17 },
+ { 320, 18 },
+ { 440, 19 },
+ { 480, 20 },
+};
+
+/* Sample rates for DSP */
+static struct {
+ int rate;
+ int value;
+} sample_rates[] = {
+ { 8000, 0 },
+ { 11025, 1 },
+ { 12000, 2 },
+ { 16000, 3 },
+ { 22050, 4 },
+ { 24000, 5 },
+ { 32000, 6 },
+ { 44100, 7 },
+ { 48000, 8 },
+ { 88200, 9 },
+ { 96000, 10 },
+ { 0, 0 },
+};
+
+static int wm8903_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+ int fs = params_rate(params);
+ int bclk;
+ int bclk_div;
+ int i;
+ int dsp_config;
+ int clk_config;
+ int best_val;
+ int cur_val;
+ int clk_sys;
+
+ u16 aif1 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_1);
+ u16 aif2 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_2);
+ u16 aif3 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_3);
+ u16 clock0 = snd_soc_read(codec, WM8903_CLOCK_RATES_0);
+ u16 clock1 = snd_soc_read(codec, WM8903_CLOCK_RATES_1);
+ u16 dac_digital1 = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);
+
+ /* Enable sloping stopband filter for low sample rates */
+ if (fs <= 24000)
+ dac_digital1 |= WM8903_DAC_SB_FILT;
+ else
+ dac_digital1 &= ~WM8903_DAC_SB_FILT;
+
+ /* Configure sample rate logic for DSP - choose nearest rate */
+ dsp_config = 0;
+ best_val = abs(sample_rates[dsp_config].rate - fs);
+ for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+ cur_val = abs(sample_rates[i].rate - fs);
+ if (cur_val <= best_val) {
+ dsp_config = i;
+ best_val = cur_val;
+ }
+ }
+
+ dev_dbg(codec->dev, "DSP fs = %dHz\n", sample_rates[dsp_config].rate);
+ clock1 &= ~WM8903_SAMPLE_RATE_MASK;
+ clock1 |= sample_rates[dsp_config].value;
+
+ aif1 &= ~WM8903_AIF_WL_MASK;
+ bclk = 2 * fs;
+ switch (params_width(params)) {
+ case 16:
+ bclk *= 16;
+ break;
+ case 20:
+ bclk *= 20;
+ aif1 |= 0x4;
+ break;
+ case 24:
+ bclk *= 24;
+ aif1 |= 0x8;
+ break;
+ case 32:
+ bclk *= 32;
+ aif1 |= 0xc;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "MCLK = %dHz, target sample rate = %dHz\n",
+ wm8903->sysclk, fs);
+
+ /* We may not have an MCLK which allows us to generate exactly
+ * the clock we want, particularly with USB derived inputs, so
+ * approximate.
+ */
+ clk_config = 0;
+ best_val = abs((wm8903->sysclk /
+ (clk_sys_ratios[0].mclk_div *
+ clk_sys_ratios[0].div)) - fs);
+ for (i = 1; i < ARRAY_SIZE(clk_sys_ratios); i++) {
+ cur_val = abs((wm8903->sysclk /
+ (clk_sys_ratios[i].mclk_div *
+ clk_sys_ratios[i].div)) - fs);
+
+ if (cur_val <= best_val) {
+ clk_config = i;
+ best_val = cur_val;
+ }
+ }
+
+ if (clk_sys_ratios[clk_config].mclk_div == 2) {
+ clock0 |= WM8903_MCLKDIV2;
+ clk_sys = wm8903->sysclk / 2;
+ } else {
+ clock0 &= ~WM8903_MCLKDIV2;
+ clk_sys = wm8903->sysclk;
+ }
+
+ clock1 &= ~(WM8903_CLK_SYS_RATE_MASK |
+ WM8903_CLK_SYS_MODE_MASK);
+ clock1 |= clk_sys_ratios[clk_config].rate << WM8903_CLK_SYS_RATE_SHIFT;
+ clock1 |= clk_sys_ratios[clk_config].mode << WM8903_CLK_SYS_MODE_SHIFT;
+
+ dev_dbg(codec->dev, "CLK_SYS_RATE=%x, CLK_SYS_MODE=%x div=%d\n",
+ clk_sys_ratios[clk_config].rate,
+ clk_sys_ratios[clk_config].mode,
+ clk_sys_ratios[clk_config].div);
+
+ dev_dbg(codec->dev, "Actual CLK_SYS = %dHz\n", clk_sys);
+
+ /* We may not get quite the right frequency if using
+ * approximate clocks so look for the closest match that is
+ * higher than the target (we need to ensure that there enough
+ * BCLKs to clock out the samples).
+ */
+ bclk_div = 0;
+ best_val = ((clk_sys * 10) / bclk_divs[0].ratio) - bclk;
+ i = 1;
+ while (i < ARRAY_SIZE(bclk_divs)) {
+ cur_val = ((clk_sys * 10) / bclk_divs[i].ratio) - bclk;
+ if (cur_val < 0) /* BCLK table is sorted */
+ break;
+ bclk_div = i;
+ best_val = cur_val;
+ i++;
+ }
+
+ aif2 &= ~WM8903_BCLK_DIV_MASK;
+ aif3 &= ~WM8903_LRCLK_RATE_MASK;
+
+ dev_dbg(codec->dev, "BCLK ratio %d for %dHz - actual BCLK = %dHz\n",
+ bclk_divs[bclk_div].ratio / 10, bclk,
+ (clk_sys * 10) / bclk_divs[bclk_div].ratio);
+
+ aif2 |= bclk_divs[bclk_div].div;
+ aif3 |= bclk / fs;
+
+ wm8903->fs = params_rate(params);
+ wm8903_set_deemph(codec);
+
+ snd_soc_write(codec, WM8903_CLOCK_RATES_0, clock0);
+ snd_soc_write(codec, WM8903_CLOCK_RATES_1, clock1);
+ snd_soc_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
+ snd_soc_write(codec, WM8903_AUDIO_INTERFACE_2, aif2);
+ snd_soc_write(codec, WM8903_AUDIO_INTERFACE_3, aif3);
+ snd_soc_write(codec, WM8903_DAC_DIGITAL_1, dac_digital1);
+
+ return 0;
+}
+
+/**
+ * wm8903_mic_detect - Enable microphone detection via the WM8903 IRQ
+ *
+ * @codec: WM8903 codec
+ * @jack: jack to report detection events on
+ * @det: value to report for presence detection
+ * @shrt: value to report for short detection
+ *
+ * Enable microphone detection via IRQ on the WM8903. If GPIOs are
+ * being used to bring out signals to the processor then only platform
+ * data configuration is needed for WM8903 and processor GPIOs should
+ * be configured using snd_soc_jack_add_gpios() instead.
+ *
+ * The current threasholds for detection should be configured using
+ * micdet_cfg in the platform data. Using this function will force on
+ * the microphone bias for the device.
+ */
+int wm8903_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ int det, int shrt)
+{
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+ int irq_mask = WM8903_MICDET_EINT | WM8903_MICSHRT_EINT;
+
+ dev_dbg(codec->dev, "Enabling microphone detection: %x %x\n",
+ det, shrt);
+
+ /* Store the configuration */
+ wm8903->mic_jack = jack;
+ wm8903->mic_det = det;
+ wm8903->mic_short = shrt;
+
+ /* Enable interrupts we've got a report configured for */
+ if (det)
+ irq_mask &= ~WM8903_MICDET_EINT;
+ if (shrt)
+ irq_mask &= ~WM8903_MICSHRT_EINT;
+
+ snd_soc_update_bits(codec, WM8903_INTERRUPT_STATUS_1_MASK,
+ WM8903_MICDET_EINT | WM8903_MICSHRT_EINT,
+ irq_mask);
+
+ if (det || shrt) {
+ /* Enable mic detection, this may not have been set through
+ * platform data (eg, if the defaults are OK). */
+ snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0,
+ WM8903_WSEQ_ENA, WM8903_WSEQ_ENA);
+ snd_soc_update_bits(codec, WM8903_MIC_BIAS_CONTROL_0,
+ WM8903_MICDET_ENA, WM8903_MICDET_ENA);
+ } else {
+ snd_soc_update_bits(codec, WM8903_MIC_BIAS_CONTROL_0,
+ WM8903_MICDET_ENA, 0);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8903_mic_detect);
+
+static irqreturn_t wm8903_irq(int irq, void *data)
+{
+ struct wm8903_priv *wm8903 = data;
+ int mic_report, ret;
+ unsigned int int_val, mask, int_pol;
+
+ ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_STATUS_1_MASK,
+ &mask);
+ if (ret != 0) {
+ dev_err(wm8903->dev, "Failed to read IRQ mask: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_STATUS_1, &int_val);
+ if (ret != 0) {
+ dev_err(wm8903->dev, "Failed to read IRQ status: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ int_val &= ~mask;
+
+ if (int_val & WM8903_WSEQ_BUSY_EINT) {
+ dev_warn(wm8903->dev, "Write sequencer done\n");
+ }
+
+ /*
+ * The rest is microphone jack detection. We need to manually
+ * invert the polarity of the interrupt after each event - to
+ * simplify the code keep track of the last state we reported
+ * and just invert the relevant bits in both the report and
+ * the polarity register.
+ */
+ mic_report = wm8903->mic_last_report;
+ ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_POLARITY_1,
+ &int_pol);
+ if (ret != 0) {
+ dev_err(wm8903->dev, "Failed to read interrupt polarity: %d\n",
+ ret);
+ return IRQ_HANDLED;
+ }
+
+#ifndef CONFIG_SND_SOC_WM8903_MODULE
+ if (int_val & (WM8903_MICSHRT_EINT | WM8903_MICDET_EINT))
+ trace_snd_soc_jack_irq(dev_name(wm8903->dev));
+#endif
+
+ if (int_val & WM8903_MICSHRT_EINT) {
+ dev_dbg(wm8903->dev, "Microphone short (pol=%x)\n", int_pol);
+
+ mic_report ^= wm8903->mic_short;
+ int_pol ^= WM8903_MICSHRT_INV;
+ }
+
+ if (int_val & WM8903_MICDET_EINT) {
+ dev_dbg(wm8903->dev, "Microphone detect (pol=%x)\n", int_pol);
+
+ mic_report ^= wm8903->mic_det;
+ int_pol ^= WM8903_MICDET_INV;
+
+ msleep(wm8903->mic_delay);
+ }
+
+ regmap_update_bits(wm8903->regmap, WM8903_INTERRUPT_POLARITY_1,
+ WM8903_MICSHRT_INV | WM8903_MICDET_INV, int_pol);
+
+ snd_soc_jack_report(wm8903->mic_jack, mic_report,
+ wm8903->mic_short | wm8903->mic_det);
+
+ wm8903->mic_last_report = mic_report;
+
+ return IRQ_HANDLED;
+}
+
+#define WM8903_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+#define WM8903_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define WM8903_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8903_dai_ops = {
+ .hw_params = wm8903_hw_params,
+ .digital_mute = wm8903_digital_mute,
+ .set_fmt = wm8903_set_dai_fmt,
+ .set_sysclk = wm8903_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver wm8903_dai = {
+ .name = "wm8903-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8903_PLAYBACK_RATES,
+ .formats = WM8903_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8903_CAPTURE_RATES,
+ .formats = WM8903_FORMATS,
+ },
+ .ops = &wm8903_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int wm8903_resume(struct snd_soc_codec *codec)
+{
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_sync(wm8903->regmap);
+
+ return 0;
+}
+
+#ifdef CONFIG_GPIOLIB
+static inline struct wm8903_priv *gpio_to_wm8903(struct gpio_chip *chip)
+{
+ return container_of(chip, struct wm8903_priv, gpio_chip);
+}
+
+static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ if (offset >= WM8903_NUM_GPIO)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+ unsigned int mask, val;
+ int ret;
+
+ mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK;
+ val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) |
+ WM8903_GP1_DIR;
+
+ ret = regmap_update_bits(wm8903->regmap,
+ WM8903_GPIO_CONTROL_1 + offset, mask, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+ unsigned int reg;
+
+ regmap_read(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, &reg);
+
+ return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT;
+}
+
+static int wm8903_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+ unsigned int mask, val;
+ int ret;
+
+ mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK | WM8903_GP1_LVL_MASK;
+ val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) |
+ (value << WM8903_GP2_LVL_SHIFT);
+
+ ret = regmap_update_bits(wm8903->regmap,
+ WM8903_GPIO_CONTROL_1 + offset, mask, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+
+ regmap_update_bits(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset,
+ WM8903_GP1_LVL_MASK,
+ !!value << WM8903_GP1_LVL_SHIFT);
+}
+
+static struct gpio_chip wm8903_template_chip = {
+ .label = "wm8903",
+ .owner = THIS_MODULE,
+ .request = wm8903_gpio_request,
+ .direction_input = wm8903_gpio_direction_in,
+ .get = wm8903_gpio_get,
+ .direction_output = wm8903_gpio_direction_out,
+ .set = wm8903_gpio_set,
+ .can_sleep = 1,
+};
+
+static void wm8903_init_gpio(struct wm8903_priv *wm8903)
+{
+ struct wm8903_platform_data *pdata = wm8903->pdata;
+ int ret;
+
+ wm8903->gpio_chip = wm8903_template_chip;
+ wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO;
+ wm8903->gpio_chip.dev = wm8903->dev;
+
+ if (pdata->gpio_base)
+ wm8903->gpio_chip.base = pdata->gpio_base;
+ else
+ wm8903->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&wm8903->gpio_chip);
+ if (ret != 0)
+ dev_err(wm8903->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
+static void wm8903_free_gpio(struct wm8903_priv *wm8903)
+{
+ gpiochip_remove(&wm8903->gpio_chip);
+}
+#else
+static void wm8903_init_gpio(struct wm8903_priv *wm8903)
+{
+}
+
+static void wm8903_free_gpio(struct wm8903_priv *wm8903)
+{
+}
+#endif
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8903 = {
+ .resume = wm8903_resume,
+ .set_bias_level = wm8903_set_bias_level,
+ .seq_notifier = wm8903_seq_notifier,
+ .suspend_bias_off = true,
+
+ .controls = wm8903_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8903_snd_controls),
+ .dapm_widgets = wm8903_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8903_dapm_widgets),
+ .dapm_routes = wm8903_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8903_intercon),
+};
+
+static const struct regmap_config wm8903_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = WM8903_MAX_REGISTER,
+ .volatile_reg = wm8903_volatile_register,
+ .readable_reg = wm8903_readable_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm8903_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8903_reg_defaults),
+};
+
+static int wm8903_set_pdata_irq_trigger(struct i2c_client *i2c,
+ struct wm8903_platform_data *pdata)
+{
+ struct irq_data *irq_data = irq_get_irq_data(i2c->irq);
+ if (!irq_data) {
+ dev_err(&i2c->dev, "Invalid IRQ: %d\n",
+ i2c->irq);
+ return -EINVAL;
+ }
+
+ switch (irqd_get_trigger_type(irq_data)) {
+ case IRQ_TYPE_NONE:
+ default:
+ /*
+ * We assume the controller imposes no restrictions,
+ * so we are able to select active-high
+ */
+ /* Fall-through */
+ case IRQ_TYPE_LEVEL_HIGH:
+ pdata->irq_active_low = false;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ pdata->irq_active_low = true;
+ break;
+ }
+
+ return 0;
+}
+
+static int wm8903_set_pdata_from_of(struct i2c_client *i2c,
+ struct wm8903_platform_data *pdata)
+{
+ const struct device_node *np = i2c->dev.of_node;
+ u32 val32;
+ int i;
+
+ if (of_property_read_u32(np, "micdet-cfg", &val32) >= 0)
+ pdata->micdet_cfg = val32;
+
+ if (of_property_read_u32(np, "micdet-delay", &val32) >= 0)
+ pdata->micdet_delay = val32;
+
+ if (of_property_read_u32_array(np, "gpio-cfg", pdata->gpio_cfg,
+ ARRAY_SIZE(pdata->gpio_cfg)) >= 0) {
+ /*
+ * In device tree: 0 means "write 0",
+ * 0xffffffff means "don't touch".
+ *
+ * In platform data: 0 means "don't touch",
+ * 0x8000 means "write 0".
+ *
+ * Note: WM8903_GPIO_CONFIG_ZERO == 0x8000.
+ *
+ * Convert from DT to pdata representation here,
+ * so no other code needs to change.
+ */
+ for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) {
+ if (pdata->gpio_cfg[i] == 0) {
+ pdata->gpio_cfg[i] = WM8903_GPIO_CONFIG_ZERO;
+ } else if (pdata->gpio_cfg[i] == 0xffffffff) {
+ pdata->gpio_cfg[i] = 0;
+ } else if (pdata->gpio_cfg[i] > 0x7fff) {
+ dev_err(&i2c->dev, "Invalid gpio-cfg[%d] %x\n",
+ i, pdata->gpio_cfg[i]);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int wm8903_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct wm8903_priv *wm8903;
+ int trigger;
+ bool mic_gpio = false;
+ unsigned int val, irq_pol;
+ int ret, i;
+
+ wm8903 = devm_kzalloc(&i2c->dev, sizeof(struct wm8903_priv),
+ GFP_KERNEL);
+ if (wm8903 == NULL)
+ return -ENOMEM;
+
+ mutex_init(&wm8903->lock);
+ wm8903->dev = &i2c->dev;
+
+ wm8903->regmap = devm_regmap_init_i2c(i2c, &wm8903_regmap);
+ if (IS_ERR(wm8903->regmap)) {
+ ret = PTR_ERR(wm8903->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, wm8903);
+
+ /* If no platform data was supplied, create storage for defaults */
+ if (pdata) {
+ wm8903->pdata = pdata;
+ } else {
+ wm8903->pdata = devm_kzalloc(&i2c->dev,
+ sizeof(struct wm8903_platform_data),
+ GFP_KERNEL);
+ if (wm8903->pdata == NULL) {
+ dev_err(&i2c->dev, "Failed to allocate pdata\n");
+ return -ENOMEM;
+ }
+
+ if (i2c->irq) {
+ ret = wm8903_set_pdata_irq_trigger(i2c, wm8903->pdata);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (i2c->dev.of_node) {
+ ret = wm8903_set_pdata_from_of(i2c, wm8903->pdata);
+ if (ret != 0)
+ return ret;
+ }
+ }
+
+ pdata = wm8903->pdata;
+
+ ret = regmap_read(wm8903->regmap, WM8903_SW_RESET_AND_ID, &val);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret);
+ goto err;
+ }
+ if (val != 0x8903) {
+ dev_err(&i2c->dev, "Device with ID %x is not a WM8903\n", val);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = regmap_read(wm8903->regmap, WM8903_REVISION_NUMBER, &val);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to read chip revision: %d\n", ret);
+ goto err;
+ }
+ dev_info(&i2c->dev, "WM8903 revision %c\n",
+ (val & WM8903_CHIP_REV_MASK) + 'A');
+
+ /* Reset the device */
+ regmap_write(wm8903->regmap, WM8903_SW_RESET_AND_ID, 0x8903);
+
+ wm8903_init_gpio(wm8903);
+
+ /* Set up GPIO pin state, detect if any are MIC detect outputs */
+ for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) {
+ if ((!pdata->gpio_cfg[i]) ||
+ (pdata->gpio_cfg[i] > WM8903_GPIO_CONFIG_ZERO))
+ continue;
+
+ regmap_write(wm8903->regmap, WM8903_GPIO_CONTROL_1 + i,
+ pdata->gpio_cfg[i] & 0x7fff);
+
+ val = (pdata->gpio_cfg[i] & WM8903_GP1_FN_MASK)
+ >> WM8903_GP1_FN_SHIFT;
+
+ switch (val) {
+ case WM8903_GPn_FN_MICBIAS_CURRENT_DETECT:
+ case WM8903_GPn_FN_MICBIAS_SHORT_DETECT:
+ mic_gpio = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Set up microphone detection */
+ regmap_write(wm8903->regmap, WM8903_MIC_BIAS_CONTROL_0,
+ pdata->micdet_cfg);
+
+ /* Microphone detection needs the WSEQ clock */
+ if (pdata->micdet_cfg)
+ regmap_update_bits(wm8903->regmap, WM8903_WRITE_SEQUENCER_0,
+ WM8903_WSEQ_ENA, WM8903_WSEQ_ENA);
+
+ /* If microphone detection is enabled by pdata but
+ * detected via IRQ then interrupts can be lost before
+ * the machine driver has set up microphone detection
+ * IRQs as the IRQs are clear on read. The detection
+ * will be enabled when the machine driver configures.
+ */
+ WARN_ON(!mic_gpio && (pdata->micdet_cfg & WM8903_MICDET_ENA));
+
+ wm8903->mic_delay = pdata->micdet_delay;
+
+ if (i2c->irq) {
+ if (pdata->irq_active_low) {
+ trigger = IRQF_TRIGGER_LOW;
+ irq_pol = WM8903_IRQ_POL;
+ } else {
+ trigger = IRQF_TRIGGER_HIGH;
+ irq_pol = 0;
+ }
+
+ regmap_update_bits(wm8903->regmap, WM8903_INTERRUPT_CONTROL,
+ WM8903_IRQ_POL, irq_pol);
+
+ ret = request_threaded_irq(i2c->irq, NULL, wm8903_irq,
+ trigger | IRQF_ONESHOT,
+ "wm8903", wm8903);
+ if (ret != 0) {
+ dev_err(wm8903->dev, "Failed to request IRQ: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* Enable write sequencer interrupts */
+ regmap_update_bits(wm8903->regmap,
+ WM8903_INTERRUPT_STATUS_1_MASK,
+ WM8903_IM_WSEQ_BUSY_EINT, 0);
+ }
+
+ /* Latch volume update bits */
+ regmap_update_bits(wm8903->regmap, WM8903_ADC_DIGITAL_VOLUME_LEFT,
+ WM8903_ADCVU, WM8903_ADCVU);
+ regmap_update_bits(wm8903->regmap, WM8903_ADC_DIGITAL_VOLUME_RIGHT,
+ WM8903_ADCVU, WM8903_ADCVU);
+
+ regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_VOLUME_LEFT,
+ WM8903_DACVU, WM8903_DACVU);
+ regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_VOLUME_RIGHT,
+ WM8903_DACVU, WM8903_DACVU);
+
+ regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT1_LEFT,
+ WM8903_HPOUTVU, WM8903_HPOUTVU);
+ regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT1_RIGHT,
+ WM8903_HPOUTVU, WM8903_HPOUTVU);
+
+ regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT2_LEFT,
+ WM8903_LINEOUTVU, WM8903_LINEOUTVU);
+ regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT2_RIGHT,
+ WM8903_LINEOUTVU, WM8903_LINEOUTVU);
+
+ regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT3_LEFT,
+ WM8903_SPKVU, WM8903_SPKVU);
+ regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT3_RIGHT,
+ WM8903_SPKVU, WM8903_SPKVU);
+
+ /* Enable DAC soft mute by default */
+ regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_1,
+ WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE,
+ WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8903, &wm8903_dai, 1);
+ if (ret != 0)
+ goto err;
+
+ return 0;
+err:
+ return ret;
+}
+
+static int wm8903_i2c_remove(struct i2c_client *client)
+{
+ struct wm8903_priv *wm8903 = i2c_get_clientdata(client);
+
+ if (client->irq)
+ free_irq(client->irq, wm8903);
+ wm8903_free_gpio(wm8903);
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+static const struct of_device_id wm8903_of_match[] = {
+ { .compatible = "wlf,wm8903", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, wm8903_of_match);
+
+static const struct i2c_device_id wm8903_i2c_id[] = {
+ { "wm8903", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id);
+
+static struct i2c_driver wm8903_i2c_driver = {
+ .driver = {
+ .name = "wm8903",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8903_of_match,
+ },
+ .probe = wm8903_i2c_probe,
+ .remove = wm8903_i2c_remove,
+ .id_table = wm8903_i2c_id,
+};
+
+module_i2c_driver(wm8903_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8903 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.cm>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8903.h b/kernel/sound/soc/codecs/wm8903.h
new file mode 100644
index 000000000..db949311c
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8903.h
@@ -0,0 +1,1225 @@
+/*
+ * wm8903.h - WM8903 audio codec interface
+ *
+ * Copyright 2008 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.
+ */
+
+#ifndef _WM8903_H
+#define _WM8903_H
+
+#include <linux/i2c.h>
+
+extern int wm8903_mic_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack,
+ int det, int shrt);
+
+
+/*
+ * Register values.
+ */
+#define WM8903_SW_RESET_AND_ID 0x00
+#define WM8903_REVISION_NUMBER 0x01
+#define WM8903_BIAS_CONTROL_0 0x04
+#define WM8903_VMID_CONTROL_0 0x05
+#define WM8903_MIC_BIAS_CONTROL_0 0x06
+#define WM8903_ANALOGUE_DAC_0 0x08
+#define WM8903_ANALOGUE_ADC_0 0x0A
+#define WM8903_POWER_MANAGEMENT_0 0x0C
+#define WM8903_POWER_MANAGEMENT_1 0x0D
+#define WM8903_POWER_MANAGEMENT_2 0x0E
+#define WM8903_POWER_MANAGEMENT_3 0x0F
+#define WM8903_POWER_MANAGEMENT_4 0x10
+#define WM8903_POWER_MANAGEMENT_5 0x11
+#define WM8903_POWER_MANAGEMENT_6 0x12
+#define WM8903_CLOCK_RATES_0 0x14
+#define WM8903_CLOCK_RATES_1 0x15
+#define WM8903_CLOCK_RATES_2 0x16
+#define WM8903_AUDIO_INTERFACE_0 0x18
+#define WM8903_AUDIO_INTERFACE_1 0x19
+#define WM8903_AUDIO_INTERFACE_2 0x1A
+#define WM8903_AUDIO_INTERFACE_3 0x1B
+#define WM8903_DAC_DIGITAL_VOLUME_LEFT 0x1E
+#define WM8903_DAC_DIGITAL_VOLUME_RIGHT 0x1F
+#define WM8903_DAC_DIGITAL_0 0x20
+#define WM8903_DAC_DIGITAL_1 0x21
+#define WM8903_ADC_DIGITAL_VOLUME_LEFT 0x24
+#define WM8903_ADC_DIGITAL_VOLUME_RIGHT 0x25
+#define WM8903_ADC_DIGITAL_0 0x26
+#define WM8903_DIGITAL_MICROPHONE_0 0x27
+#define WM8903_DRC_0 0x28
+#define WM8903_DRC_1 0x29
+#define WM8903_DRC_2 0x2A
+#define WM8903_DRC_3 0x2B
+#define WM8903_ANALOGUE_LEFT_INPUT_0 0x2C
+#define WM8903_ANALOGUE_RIGHT_INPUT_0 0x2D
+#define WM8903_ANALOGUE_LEFT_INPUT_1 0x2E
+#define WM8903_ANALOGUE_RIGHT_INPUT_1 0x2F
+#define WM8903_ANALOGUE_LEFT_MIX_0 0x32
+#define WM8903_ANALOGUE_RIGHT_MIX_0 0x33
+#define WM8903_ANALOGUE_SPK_MIX_LEFT_0 0x34
+#define WM8903_ANALOGUE_SPK_MIX_LEFT_1 0x35
+#define WM8903_ANALOGUE_SPK_MIX_RIGHT_0 0x36
+#define WM8903_ANALOGUE_SPK_MIX_RIGHT_1 0x37
+#define WM8903_ANALOGUE_OUT1_LEFT 0x39
+#define WM8903_ANALOGUE_OUT1_RIGHT 0x3A
+#define WM8903_ANALOGUE_OUT2_LEFT 0x3B
+#define WM8903_ANALOGUE_OUT2_RIGHT 0x3C
+#define WM8903_ANALOGUE_OUT3_LEFT 0x3E
+#define WM8903_ANALOGUE_OUT3_RIGHT 0x3F
+#define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0 0x41
+#define WM8903_DC_SERVO_0 0x43
+#define WM8903_DC_SERVO_2 0x45
+#define WM8903_DC_SERVO_4 0x47
+#define WM8903_DC_SERVO_5 0x48
+#define WM8903_DC_SERVO_6 0x49
+#define WM8903_DC_SERVO_7 0x4A
+#define WM8903_DC_SERVO_READBACK_1 0x51
+#define WM8903_DC_SERVO_READBACK_2 0x52
+#define WM8903_DC_SERVO_READBACK_3 0x53
+#define WM8903_DC_SERVO_READBACK_4 0x54
+#define WM8903_ANALOGUE_HP_0 0x5A
+#define WM8903_ANALOGUE_LINEOUT_0 0x5E
+#define WM8903_CHARGE_PUMP_0 0x62
+#define WM8903_CLASS_W_0 0x68
+#define WM8903_WRITE_SEQUENCER_0 0x6C
+#define WM8903_WRITE_SEQUENCER_1 0x6D
+#define WM8903_WRITE_SEQUENCER_2 0x6E
+#define WM8903_WRITE_SEQUENCER_3 0x6F
+#define WM8903_WRITE_SEQUENCER_4 0x70
+#define WM8903_CONTROL_INTERFACE 0x72
+#define WM8903_GPIO_CONTROL_1 0x74
+#define WM8903_GPIO_CONTROL_2 0x75
+#define WM8903_GPIO_CONTROL_3 0x76
+#define WM8903_GPIO_CONTROL_4 0x77
+#define WM8903_GPIO_CONTROL_5 0x78
+#define WM8903_INTERRUPT_STATUS_1 0x79
+#define WM8903_INTERRUPT_STATUS_1_MASK 0x7A
+#define WM8903_INTERRUPT_POLARITY_1 0x7B
+#define WM8903_INTERRUPT_CONTROL 0x7E
+#define WM8903_CLOCK_RATE_TEST_4 0xA4
+#define WM8903_ANALOGUE_OUTPUT_BIAS_0 0xAC
+
+#define WM8903_REGISTER_COUNT 75
+#define WM8903_MAX_REGISTER 0xAC
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - SW Reset and ID
+ */
+#define WM8903_SW_RESET_DEV_ID1_MASK 0xFFFF /* SW_RESET_DEV_ID1 - [15:0] */
+#define WM8903_SW_RESET_DEV_ID1_SHIFT 0 /* SW_RESET_DEV_ID1 - [15:0] */
+#define WM8903_SW_RESET_DEV_ID1_WIDTH 16 /* SW_RESET_DEV_ID1 - [15:0] */
+
+/*
+ * R1 (0x01) - Revision Number
+ */
+#define WM8903_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */
+#define WM8903_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */
+#define WM8903_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */
+
+/*
+ * R4 (0x04) - Bias Control 0
+ */
+#define WM8903_POBCTRL 0x0010 /* POBCTRL */
+#define WM8903_POBCTRL_MASK 0x0010 /* POBCTRL */
+#define WM8903_POBCTRL_SHIFT 4 /* POBCTRL */
+#define WM8903_POBCTRL_WIDTH 1 /* POBCTRL */
+#define WM8903_ISEL_MASK 0x000C /* ISEL - [3:2] */
+#define WM8903_ISEL_SHIFT 2 /* ISEL - [3:2] */
+#define WM8903_ISEL_WIDTH 2 /* ISEL - [3:2] */
+#define WM8903_STARTUP_BIAS_ENA 0x0002 /* STARTUP_BIAS_ENA */
+#define WM8903_STARTUP_BIAS_ENA_MASK 0x0002 /* STARTUP_BIAS_ENA */
+#define WM8903_STARTUP_BIAS_ENA_SHIFT 1 /* STARTUP_BIAS_ENA */
+#define WM8903_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */
+#define WM8903_BIAS_ENA 0x0001 /* BIAS_ENA */
+#define WM8903_BIAS_ENA_MASK 0x0001 /* BIAS_ENA */
+#define WM8903_BIAS_ENA_SHIFT 0 /* BIAS_ENA */
+#define WM8903_BIAS_ENA_WIDTH 1 /* BIAS_ENA */
+
+/*
+ * R5 (0x05) - VMID Control 0
+ */
+#define WM8903_VMID_TIE_ENA 0x0080 /* VMID_TIE_ENA */
+#define WM8903_VMID_TIE_ENA_MASK 0x0080 /* VMID_TIE_ENA */
+#define WM8903_VMID_TIE_ENA_SHIFT 7 /* VMID_TIE_ENA */
+#define WM8903_VMID_TIE_ENA_WIDTH 1 /* VMID_TIE_ENA */
+#define WM8903_BUFIO_ENA 0x0040 /* BUFIO_ENA */
+#define WM8903_BUFIO_ENA_MASK 0x0040 /* BUFIO_ENA */
+#define WM8903_BUFIO_ENA_SHIFT 6 /* BUFIO_ENA */
+#define WM8903_BUFIO_ENA_WIDTH 1 /* BUFIO_ENA */
+#define WM8903_VMID_IO_ENA 0x0020 /* VMID_IO_ENA */
+#define WM8903_VMID_IO_ENA_MASK 0x0020 /* VMID_IO_ENA */
+#define WM8903_VMID_IO_ENA_SHIFT 5 /* VMID_IO_ENA */
+#define WM8903_VMID_IO_ENA_WIDTH 1 /* VMID_IO_ENA */
+#define WM8903_VMID_SOFT_MASK 0x0018 /* VMID_SOFT - [4:3] */
+#define WM8903_VMID_SOFT_SHIFT 3 /* VMID_SOFT - [4:3] */
+#define WM8903_VMID_SOFT_WIDTH 2 /* VMID_SOFT - [4:3] */
+#define WM8903_VMID_RES_MASK 0x0006 /* VMID_RES - [2:1] */
+#define WM8903_VMID_RES_SHIFT 1 /* VMID_RES - [2:1] */
+#define WM8903_VMID_RES_WIDTH 2 /* VMID_RES - [2:1] */
+#define WM8903_VMID_BUF_ENA 0x0001 /* VMID_BUF_ENA */
+#define WM8903_VMID_BUF_ENA_MASK 0x0001 /* VMID_BUF_ENA */
+#define WM8903_VMID_BUF_ENA_SHIFT 0 /* VMID_BUF_ENA */
+#define WM8903_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */
+
+#define WM8903_VMID_RES_50K 2
+#define WM8903_VMID_RES_250K 3
+#define WM8903_VMID_RES_5K 6
+
+/*
+ * R8 (0x08) - Analogue DAC 0
+ */
+#define WM8903_DACBIAS_SEL_MASK 0x0018 /* DACBIAS_SEL - [4:3] */
+#define WM8903_DACBIAS_SEL_SHIFT 3 /* DACBIAS_SEL - [4:3] */
+#define WM8903_DACBIAS_SEL_WIDTH 2 /* DACBIAS_SEL - [4:3] */
+#define WM8903_DACVMID_BIAS_SEL_MASK 0x0006 /* DACVMID_BIAS_SEL - [2:1] */
+#define WM8903_DACVMID_BIAS_SEL_SHIFT 1 /* DACVMID_BIAS_SEL - [2:1] */
+#define WM8903_DACVMID_BIAS_SEL_WIDTH 2 /* DACVMID_BIAS_SEL - [2:1] */
+
+/*
+ * R10 (0x0A) - Analogue ADC 0
+ */
+#define WM8903_ADC_OSR128 0x0001 /* ADC_OSR128 */
+#define WM8903_ADC_OSR128_MASK 0x0001 /* ADC_OSR128 */
+#define WM8903_ADC_OSR128_SHIFT 0 /* ADC_OSR128 */
+#define WM8903_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */
+
+/*
+ * R12 (0x0C) - Power Management 0
+ */
+#define WM8903_INL_ENA 0x0002 /* INL_ENA */
+#define WM8903_INL_ENA_MASK 0x0002 /* INL_ENA */
+#define WM8903_INL_ENA_SHIFT 1 /* INL_ENA */
+#define WM8903_INL_ENA_WIDTH 1 /* INL_ENA */
+#define WM8903_INR_ENA 0x0001 /* INR_ENA */
+#define WM8903_INR_ENA_MASK 0x0001 /* INR_ENA */
+#define WM8903_INR_ENA_SHIFT 0 /* INR_ENA */
+#define WM8903_INR_ENA_WIDTH 1 /* INR_ENA */
+
+/*
+ * R13 (0x0D) - Power Management 1
+ */
+#define WM8903_MIXOUTL_ENA 0x0002 /* MIXOUTL_ENA */
+#define WM8903_MIXOUTL_ENA_MASK 0x0002 /* MIXOUTL_ENA */
+#define WM8903_MIXOUTL_ENA_SHIFT 1 /* MIXOUTL_ENA */
+#define WM8903_MIXOUTL_ENA_WIDTH 1 /* MIXOUTL_ENA */
+#define WM8903_MIXOUTR_ENA 0x0001 /* MIXOUTR_ENA */
+#define WM8903_MIXOUTR_ENA_MASK 0x0001 /* MIXOUTR_ENA */
+#define WM8903_MIXOUTR_ENA_SHIFT 0 /* MIXOUTR_ENA */
+#define WM8903_MIXOUTR_ENA_WIDTH 1 /* MIXOUTR_ENA */
+
+/*
+ * R14 (0x0E) - Power Management 2
+ */
+#define WM8903_HPL_PGA_ENA 0x0002 /* HPL_PGA_ENA */
+#define WM8903_HPL_PGA_ENA_MASK 0x0002 /* HPL_PGA_ENA */
+#define WM8903_HPL_PGA_ENA_SHIFT 1 /* HPL_PGA_ENA */
+#define WM8903_HPL_PGA_ENA_WIDTH 1 /* HPL_PGA_ENA */
+#define WM8903_HPR_PGA_ENA 0x0001 /* HPR_PGA_ENA */
+#define WM8903_HPR_PGA_ENA_MASK 0x0001 /* HPR_PGA_ENA */
+#define WM8903_HPR_PGA_ENA_SHIFT 0 /* HPR_PGA_ENA */
+#define WM8903_HPR_PGA_ENA_WIDTH 1 /* HPR_PGA_ENA */
+
+/*
+ * R15 (0x0F) - Power Management 3
+ */
+#define WM8903_LINEOUTL_PGA_ENA 0x0002 /* LINEOUTL_PGA_ENA */
+#define WM8903_LINEOUTL_PGA_ENA_MASK 0x0002 /* LINEOUTL_PGA_ENA */
+#define WM8903_LINEOUTL_PGA_ENA_SHIFT 1 /* LINEOUTL_PGA_ENA */
+#define WM8903_LINEOUTL_PGA_ENA_WIDTH 1 /* LINEOUTL_PGA_ENA */
+#define WM8903_LINEOUTR_PGA_ENA 0x0001 /* LINEOUTR_PGA_ENA */
+#define WM8903_LINEOUTR_PGA_ENA_MASK 0x0001 /* LINEOUTR_PGA_ENA */
+#define WM8903_LINEOUTR_PGA_ENA_SHIFT 0 /* LINEOUTR_PGA_ENA */
+#define WM8903_LINEOUTR_PGA_ENA_WIDTH 1 /* LINEOUTR_PGA_ENA */
+
+/*
+ * R16 (0x10) - Power Management 4
+ */
+#define WM8903_MIXSPKL_ENA 0x0002 /* MIXSPKL_ENA */
+#define WM8903_MIXSPKL_ENA_MASK 0x0002 /* MIXSPKL_ENA */
+#define WM8903_MIXSPKL_ENA_SHIFT 1 /* MIXSPKL_ENA */
+#define WM8903_MIXSPKL_ENA_WIDTH 1 /* MIXSPKL_ENA */
+#define WM8903_MIXSPKR_ENA 0x0001 /* MIXSPKR_ENA */
+#define WM8903_MIXSPKR_ENA_MASK 0x0001 /* MIXSPKR_ENA */
+#define WM8903_MIXSPKR_ENA_SHIFT 0 /* MIXSPKR_ENA */
+#define WM8903_MIXSPKR_ENA_WIDTH 1 /* MIXSPKR_ENA */
+
+/*
+ * R17 (0x11) - Power Management 5
+ */
+#define WM8903_SPKL_ENA 0x0002 /* SPKL_ENA */
+#define WM8903_SPKL_ENA_MASK 0x0002 /* SPKL_ENA */
+#define WM8903_SPKL_ENA_SHIFT 1 /* SPKL_ENA */
+#define WM8903_SPKL_ENA_WIDTH 1 /* SPKL_ENA */
+#define WM8903_SPKR_ENA 0x0001 /* SPKR_ENA */
+#define WM8903_SPKR_ENA_MASK 0x0001 /* SPKR_ENA */
+#define WM8903_SPKR_ENA_SHIFT 0 /* SPKR_ENA */
+#define WM8903_SPKR_ENA_WIDTH 1 /* SPKR_ENA */
+
+/*
+ * R18 (0x12) - Power Management 6
+ */
+#define WM8903_DACL_ENA 0x0008 /* DACL_ENA */
+#define WM8903_DACL_ENA_MASK 0x0008 /* DACL_ENA */
+#define WM8903_DACL_ENA_SHIFT 3 /* DACL_ENA */
+#define WM8903_DACL_ENA_WIDTH 1 /* DACL_ENA */
+#define WM8903_DACR_ENA 0x0004 /* DACR_ENA */
+#define WM8903_DACR_ENA_MASK 0x0004 /* DACR_ENA */
+#define WM8903_DACR_ENA_SHIFT 2 /* DACR_ENA */
+#define WM8903_DACR_ENA_WIDTH 1 /* DACR_ENA */
+#define WM8903_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8903_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */
+#define WM8903_ADCL_ENA_SHIFT 1 /* ADCL_ENA */
+#define WM8903_ADCL_ENA_WIDTH 1 /* ADCL_ENA */
+#define WM8903_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8903_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */
+#define WM8903_ADCR_ENA_SHIFT 0 /* ADCR_ENA */
+#define WM8903_ADCR_ENA_WIDTH 1 /* ADCR_ENA */
+
+/*
+ * R20 (0x14) - Clock Rates 0
+ */
+#define WM8903_MCLKDIV2 0x0001 /* MCLKDIV2 */
+#define WM8903_MCLKDIV2_MASK 0x0001 /* MCLKDIV2 */
+#define WM8903_MCLKDIV2_SHIFT 0 /* MCLKDIV2 */
+#define WM8903_MCLKDIV2_WIDTH 1 /* MCLKDIV2 */
+
+/*
+ * R21 (0x15) - Clock Rates 1
+ */
+#define WM8903_CLK_SYS_RATE_MASK 0x3C00 /* CLK_SYS_RATE - [13:10] */
+#define WM8903_CLK_SYS_RATE_SHIFT 10 /* CLK_SYS_RATE - [13:10] */
+#define WM8903_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [13:10] */
+#define WM8903_CLK_SYS_MODE_MASK 0x0300 /* CLK_SYS_MODE - [9:8] */
+#define WM8903_CLK_SYS_MODE_SHIFT 8 /* CLK_SYS_MODE - [9:8] */
+#define WM8903_CLK_SYS_MODE_WIDTH 2 /* CLK_SYS_MODE - [9:8] */
+#define WM8903_SAMPLE_RATE_MASK 0x000F /* SAMPLE_RATE - [3:0] */
+#define WM8903_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [3:0] */
+#define WM8903_SAMPLE_RATE_WIDTH 4 /* SAMPLE_RATE - [3:0] */
+
+/*
+ * R22 (0x16) - Clock Rates 2
+ */
+#define WM8903_CLK_SYS_ENA 0x0004 /* CLK_SYS_ENA */
+#define WM8903_CLK_SYS_ENA_MASK 0x0004 /* CLK_SYS_ENA */
+#define WM8903_CLK_SYS_ENA_SHIFT 2 /* CLK_SYS_ENA */
+#define WM8903_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */
+#define WM8903_CLK_DSP_ENA 0x0002 /* CLK_DSP_ENA */
+#define WM8903_CLK_DSP_ENA_MASK 0x0002 /* CLK_DSP_ENA */
+#define WM8903_CLK_DSP_ENA_SHIFT 1 /* CLK_DSP_ENA */
+#define WM8903_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */
+#define WM8903_TO_ENA 0x0001 /* TO_ENA */
+#define WM8903_TO_ENA_MASK 0x0001 /* TO_ENA */
+#define WM8903_TO_ENA_SHIFT 0 /* TO_ENA */
+#define WM8903_TO_ENA_WIDTH 1 /* TO_ENA */
+
+/*
+ * R24 (0x18) - Audio Interface 0
+ */
+#define WM8903_DACL_DATINV 0x1000 /* DACL_DATINV */
+#define WM8903_DACL_DATINV_MASK 0x1000 /* DACL_DATINV */
+#define WM8903_DACL_DATINV_SHIFT 12 /* DACL_DATINV */
+#define WM8903_DACL_DATINV_WIDTH 1 /* DACL_DATINV */
+#define WM8903_DACR_DATINV 0x0800 /* DACR_DATINV */
+#define WM8903_DACR_DATINV_MASK 0x0800 /* DACR_DATINV */
+#define WM8903_DACR_DATINV_SHIFT 11 /* DACR_DATINV */
+#define WM8903_DACR_DATINV_WIDTH 1 /* DACR_DATINV */
+#define WM8903_DAC_BOOST_MASK 0x0600 /* DAC_BOOST - [10:9] */
+#define WM8903_DAC_BOOST_SHIFT 9 /* DAC_BOOST - [10:9] */
+#define WM8903_DAC_BOOST_WIDTH 2 /* DAC_BOOST - [10:9] */
+#define WM8903_LOOPBACK 0x0100 /* LOOPBACK */
+#define WM8903_LOOPBACK_MASK 0x0100 /* LOOPBACK */
+#define WM8903_LOOPBACK_SHIFT 8 /* LOOPBACK */
+#define WM8903_LOOPBACK_WIDTH 1 /* LOOPBACK */
+#define WM8903_AIFADCL_SRC 0x0080 /* AIFADCL_SRC */
+#define WM8903_AIFADCL_SRC_MASK 0x0080 /* AIFADCL_SRC */
+#define WM8903_AIFADCL_SRC_SHIFT 7 /* AIFADCL_SRC */
+#define WM8903_AIFADCL_SRC_WIDTH 1 /* AIFADCL_SRC */
+#define WM8903_AIFADCR_SRC 0x0040 /* AIFADCR_SRC */
+#define WM8903_AIFADCR_SRC_MASK 0x0040 /* AIFADCR_SRC */
+#define WM8903_AIFADCR_SRC_SHIFT 6 /* AIFADCR_SRC */
+#define WM8903_AIFADCR_SRC_WIDTH 1 /* AIFADCR_SRC */
+#define WM8903_AIFDACL_SRC 0x0020 /* AIFDACL_SRC */
+#define WM8903_AIFDACL_SRC_MASK 0x0020 /* AIFDACL_SRC */
+#define WM8903_AIFDACL_SRC_SHIFT 5 /* AIFDACL_SRC */
+#define WM8903_AIFDACL_SRC_WIDTH 1 /* AIFDACL_SRC */
+#define WM8903_AIFDACR_SRC 0x0010 /* AIFDACR_SRC */
+#define WM8903_AIFDACR_SRC_MASK 0x0010 /* AIFDACR_SRC */
+#define WM8903_AIFDACR_SRC_SHIFT 4 /* AIFDACR_SRC */
+#define WM8903_AIFDACR_SRC_WIDTH 1 /* AIFDACR_SRC */
+#define WM8903_ADC_COMP 0x0008 /* ADC_COMP */
+#define WM8903_ADC_COMP_MASK 0x0008 /* ADC_COMP */
+#define WM8903_ADC_COMP_SHIFT 3 /* ADC_COMP */
+#define WM8903_ADC_COMP_WIDTH 1 /* ADC_COMP */
+#define WM8903_ADC_COMPMODE 0x0004 /* ADC_COMPMODE */
+#define WM8903_ADC_COMPMODE_MASK 0x0004 /* ADC_COMPMODE */
+#define WM8903_ADC_COMPMODE_SHIFT 2 /* ADC_COMPMODE */
+#define WM8903_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */
+#define WM8903_DAC_COMP 0x0002 /* DAC_COMP */
+#define WM8903_DAC_COMP_MASK 0x0002 /* DAC_COMP */
+#define WM8903_DAC_COMP_SHIFT 1 /* DAC_COMP */
+#define WM8903_DAC_COMP_WIDTH 1 /* DAC_COMP */
+#define WM8903_DAC_COMPMODE 0x0001 /* DAC_COMPMODE */
+#define WM8903_DAC_COMPMODE_MASK 0x0001 /* DAC_COMPMODE */
+#define WM8903_DAC_COMPMODE_SHIFT 0 /* DAC_COMPMODE */
+#define WM8903_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */
+
+/*
+ * R25 (0x19) - Audio Interface 1
+ */
+#define WM8903_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */
+#define WM8903_AIFDAC_TDM_MASK 0x2000 /* AIFDAC_TDM */
+#define WM8903_AIFDAC_TDM_SHIFT 13 /* AIFDAC_TDM */
+#define WM8903_AIFDAC_TDM_WIDTH 1 /* AIFDAC_TDM */
+#define WM8903_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8903_AIFDAC_TDM_CHAN_MASK 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8903_AIFDAC_TDM_CHAN_SHIFT 12 /* AIFDAC_TDM_CHAN */
+#define WM8903_AIFDAC_TDM_CHAN_WIDTH 1 /* AIFDAC_TDM_CHAN */
+#define WM8903_AIFADC_TDM 0x0800 /* AIFADC_TDM */
+#define WM8903_AIFADC_TDM_MASK 0x0800 /* AIFADC_TDM */
+#define WM8903_AIFADC_TDM_SHIFT 11 /* AIFADC_TDM */
+#define WM8903_AIFADC_TDM_WIDTH 1 /* AIFADC_TDM */
+#define WM8903_AIFADC_TDM_CHAN 0x0400 /* AIFADC_TDM_CHAN */
+#define WM8903_AIFADC_TDM_CHAN_MASK 0x0400 /* AIFADC_TDM_CHAN */
+#define WM8903_AIFADC_TDM_CHAN_SHIFT 10 /* AIFADC_TDM_CHAN */
+#define WM8903_AIFADC_TDM_CHAN_WIDTH 1 /* AIFADC_TDM_CHAN */
+#define WM8903_LRCLK_DIR 0x0200 /* LRCLK_DIR */
+#define WM8903_LRCLK_DIR_MASK 0x0200 /* LRCLK_DIR */
+#define WM8903_LRCLK_DIR_SHIFT 9 /* LRCLK_DIR */
+#define WM8903_LRCLK_DIR_WIDTH 1 /* LRCLK_DIR */
+#define WM8903_AIF_BCLK_INV 0x0080 /* AIF_BCLK_INV */
+#define WM8903_AIF_BCLK_INV_MASK 0x0080 /* AIF_BCLK_INV */
+#define WM8903_AIF_BCLK_INV_SHIFT 7 /* AIF_BCLK_INV */
+#define WM8903_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */
+#define WM8903_BCLK_DIR 0x0040 /* BCLK_DIR */
+#define WM8903_BCLK_DIR_MASK 0x0040 /* BCLK_DIR */
+#define WM8903_BCLK_DIR_SHIFT 6 /* BCLK_DIR */
+#define WM8903_BCLK_DIR_WIDTH 1 /* BCLK_DIR */
+#define WM8903_AIF_LRCLK_INV 0x0010 /* AIF_LRCLK_INV */
+#define WM8903_AIF_LRCLK_INV_MASK 0x0010 /* AIF_LRCLK_INV */
+#define WM8903_AIF_LRCLK_INV_SHIFT 4 /* AIF_LRCLK_INV */
+#define WM8903_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */
+#define WM8903_AIF_WL_MASK 0x000C /* AIF_WL - [3:2] */
+#define WM8903_AIF_WL_SHIFT 2 /* AIF_WL - [3:2] */
+#define WM8903_AIF_WL_WIDTH 2 /* AIF_WL - [3:2] */
+#define WM8903_AIF_FMT_MASK 0x0003 /* AIF_FMT - [1:0] */
+#define WM8903_AIF_FMT_SHIFT 0 /* AIF_FMT - [1:0] */
+#define WM8903_AIF_FMT_WIDTH 2 /* AIF_FMT - [1:0] */
+
+/*
+ * R26 (0x1A) - Audio Interface 2
+ */
+#define WM8903_BCLK_DIV_MASK 0x001F /* BCLK_DIV - [4:0] */
+#define WM8903_BCLK_DIV_SHIFT 0 /* BCLK_DIV - [4:0] */
+#define WM8903_BCLK_DIV_WIDTH 5 /* BCLK_DIV - [4:0] */
+
+/*
+ * R27 (0x1B) - Audio Interface 3
+ */
+#define WM8903_LRCLK_RATE_MASK 0x07FF /* LRCLK_RATE - [10:0] */
+#define WM8903_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [10:0] */
+#define WM8903_LRCLK_RATE_WIDTH 11 /* LRCLK_RATE - [10:0] */
+
+/*
+ * R30 (0x1E) - DAC Digital Volume Left
+ */
+#define WM8903_DACVU 0x0100 /* DACVU */
+#define WM8903_DACVU_MASK 0x0100 /* DACVU */
+#define WM8903_DACVU_SHIFT 8 /* DACVU */
+#define WM8903_DACVU_WIDTH 1 /* DACVU */
+#define WM8903_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */
+#define WM8903_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */
+#define WM8903_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */
+
+/*
+ * R31 (0x1F) - DAC Digital Volume Right
+ */
+#define WM8903_DACVU 0x0100 /* DACVU */
+#define WM8903_DACVU_MASK 0x0100 /* DACVU */
+#define WM8903_DACVU_SHIFT 8 /* DACVU */
+#define WM8903_DACVU_WIDTH 1 /* DACVU */
+#define WM8903_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */
+#define WM8903_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */
+#define WM8903_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */
+
+/*
+ * R32 (0x20) - DAC Digital 0
+ */
+#define WM8903_ADCL_DAC_SVOL_MASK 0x0F00 /* ADCL_DAC_SVOL - [11:8] */
+#define WM8903_ADCL_DAC_SVOL_SHIFT 8 /* ADCL_DAC_SVOL - [11:8] */
+#define WM8903_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [11:8] */
+#define WM8903_ADCR_DAC_SVOL_MASK 0x00F0 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8903_ADCR_DAC_SVOL_SHIFT 4 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8903_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8903_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */
+#define WM8903_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */
+#define WM8903_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */
+#define WM8903_ADC_TO_DACR_MASK 0x0003 /* ADC_TO_DACR - [1:0] */
+#define WM8903_ADC_TO_DACR_SHIFT 0 /* ADC_TO_DACR - [1:0] */
+#define WM8903_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [1:0] */
+
+/*
+ * R33 (0x21) - DAC Digital 1
+ */
+#define WM8903_DAC_MONO 0x1000 /* DAC_MONO */
+#define WM8903_DAC_MONO_MASK 0x1000 /* DAC_MONO */
+#define WM8903_DAC_MONO_SHIFT 12 /* DAC_MONO */
+#define WM8903_DAC_MONO_WIDTH 1 /* DAC_MONO */
+#define WM8903_DAC_SB_FILT 0x0800 /* DAC_SB_FILT */
+#define WM8903_DAC_SB_FILT_MASK 0x0800 /* DAC_SB_FILT */
+#define WM8903_DAC_SB_FILT_SHIFT 11 /* DAC_SB_FILT */
+#define WM8903_DAC_SB_FILT_WIDTH 1 /* DAC_SB_FILT */
+#define WM8903_DAC_MUTERATE 0x0400 /* DAC_MUTERATE */
+#define WM8903_DAC_MUTERATE_MASK 0x0400 /* DAC_MUTERATE */
+#define WM8903_DAC_MUTERATE_SHIFT 10 /* DAC_MUTERATE */
+#define WM8903_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
+#define WM8903_DAC_MUTEMODE 0x0200 /* DAC_MUTEMODE */
+#define WM8903_DAC_MUTEMODE_MASK 0x0200 /* DAC_MUTEMODE */
+#define WM8903_DAC_MUTEMODE_SHIFT 9 /* DAC_MUTEMODE */
+#define WM8903_DAC_MUTEMODE_WIDTH 1 /* DAC_MUTEMODE */
+#define WM8903_DAC_MUTE 0x0008 /* DAC_MUTE */
+#define WM8903_DAC_MUTE_MASK 0x0008 /* DAC_MUTE */
+#define WM8903_DAC_MUTE_SHIFT 3 /* DAC_MUTE */
+#define WM8903_DAC_MUTE_WIDTH 1 /* DAC_MUTE */
+#define WM8903_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */
+#define WM8903_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */
+#define WM8903_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */
+
+/*
+ * R36 (0x24) - ADC Digital Volume Left
+ */
+#define WM8903_ADCVU 0x0100 /* ADCVU */
+#define WM8903_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8903_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8903_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8903_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */
+#define WM8903_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */
+#define WM8903_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */
+
+/*
+ * R37 (0x25) - ADC Digital Volume Right
+ */
+#define WM8903_ADCVU 0x0100 /* ADCVU */
+#define WM8903_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8903_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8903_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8903_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */
+#define WM8903_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */
+#define WM8903_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */
+
+/*
+ * R38 (0x26) - ADC Digital 0
+ */
+#define WM8903_ADC_HPF_CUT_MASK 0x0060 /* ADC_HPF_CUT - [6:5] */
+#define WM8903_ADC_HPF_CUT_SHIFT 5 /* ADC_HPF_CUT - [6:5] */
+#define WM8903_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [6:5] */
+#define WM8903_ADC_HPF_ENA 0x0010 /* ADC_HPF_ENA */
+#define WM8903_ADC_HPF_ENA_MASK 0x0010 /* ADC_HPF_ENA */
+#define WM8903_ADC_HPF_ENA_SHIFT 4 /* ADC_HPF_ENA */
+#define WM8903_ADC_HPF_ENA_WIDTH 1 /* ADC_HPF_ENA */
+#define WM8903_ADCL_DATINV 0x0002 /* ADCL_DATINV */
+#define WM8903_ADCL_DATINV_MASK 0x0002 /* ADCL_DATINV */
+#define WM8903_ADCL_DATINV_SHIFT 1 /* ADCL_DATINV */
+#define WM8903_ADCL_DATINV_WIDTH 1 /* ADCL_DATINV */
+#define WM8903_ADCR_DATINV 0x0001 /* ADCR_DATINV */
+#define WM8903_ADCR_DATINV_MASK 0x0001 /* ADCR_DATINV */
+#define WM8903_ADCR_DATINV_SHIFT 0 /* ADCR_DATINV */
+#define WM8903_ADCR_DATINV_WIDTH 1 /* ADCR_DATINV */
+
+/*
+ * R39 (0x27) - Digital Microphone 0
+ */
+#define WM8903_DIGMIC_MODE_SEL 0x0100 /* DIGMIC_MODE_SEL */
+#define WM8903_DIGMIC_MODE_SEL_MASK 0x0100 /* DIGMIC_MODE_SEL */
+#define WM8903_DIGMIC_MODE_SEL_SHIFT 8 /* DIGMIC_MODE_SEL */
+#define WM8903_DIGMIC_MODE_SEL_WIDTH 1 /* DIGMIC_MODE_SEL */
+#define WM8903_DIGMIC_CLK_SEL_L_MASK 0x00C0 /* DIGMIC_CLK_SEL_L - [7:6] */
+#define WM8903_DIGMIC_CLK_SEL_L_SHIFT 6 /* DIGMIC_CLK_SEL_L - [7:6] */
+#define WM8903_DIGMIC_CLK_SEL_L_WIDTH 2 /* DIGMIC_CLK_SEL_L - [7:6] */
+#define WM8903_DIGMIC_CLK_SEL_R_MASK 0x0030 /* DIGMIC_CLK_SEL_R - [5:4] */
+#define WM8903_DIGMIC_CLK_SEL_R_SHIFT 4 /* DIGMIC_CLK_SEL_R - [5:4] */
+#define WM8903_DIGMIC_CLK_SEL_R_WIDTH 2 /* DIGMIC_CLK_SEL_R - [5:4] */
+#define WM8903_DIGMIC_CLK_SEL_RT_MASK 0x000C /* DIGMIC_CLK_SEL_RT - [3:2] */
+#define WM8903_DIGMIC_CLK_SEL_RT_SHIFT 2 /* DIGMIC_CLK_SEL_RT - [3:2] */
+#define WM8903_DIGMIC_CLK_SEL_RT_WIDTH 2 /* DIGMIC_CLK_SEL_RT - [3:2] */
+#define WM8903_DIGMIC_CLK_SEL_MASK 0x0003 /* DIGMIC_CLK_SEL - [1:0] */
+#define WM8903_DIGMIC_CLK_SEL_SHIFT 0 /* DIGMIC_CLK_SEL - [1:0] */
+#define WM8903_DIGMIC_CLK_SEL_WIDTH 2 /* DIGMIC_CLK_SEL - [1:0] */
+
+/*
+ * R40 (0x28) - DRC 0
+ */
+#define WM8903_DRC_ENA 0x8000 /* DRC_ENA */
+#define WM8903_DRC_ENA_MASK 0x8000 /* DRC_ENA */
+#define WM8903_DRC_ENA_SHIFT 15 /* DRC_ENA */
+#define WM8903_DRC_ENA_WIDTH 1 /* DRC_ENA */
+#define WM8903_DRC_THRESH_HYST_MASK 0x1800 /* DRC_THRESH_HYST - [12:11] */
+#define WM8903_DRC_THRESH_HYST_SHIFT 11 /* DRC_THRESH_HYST - [12:11] */
+#define WM8903_DRC_THRESH_HYST_WIDTH 2 /* DRC_THRESH_HYST - [12:11] */
+#define WM8903_DRC_STARTUP_GAIN_MASK 0x07C0 /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8903_DRC_STARTUP_GAIN_SHIFT 6 /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8903_DRC_STARTUP_GAIN_WIDTH 5 /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8903_DRC_FF_DELAY 0x0020 /* DRC_FF_DELAY */
+#define WM8903_DRC_FF_DELAY_MASK 0x0020 /* DRC_FF_DELAY */
+#define WM8903_DRC_FF_DELAY_SHIFT 5 /* DRC_FF_DELAY */
+#define WM8903_DRC_FF_DELAY_WIDTH 1 /* DRC_FF_DELAY */
+#define WM8903_DRC_SMOOTH_ENA 0x0008 /* DRC_SMOOTH_ENA */
+#define WM8903_DRC_SMOOTH_ENA_MASK 0x0008 /* DRC_SMOOTH_ENA */
+#define WM8903_DRC_SMOOTH_ENA_SHIFT 3 /* DRC_SMOOTH_ENA */
+#define WM8903_DRC_SMOOTH_ENA_WIDTH 1 /* DRC_SMOOTH_ENA */
+#define WM8903_DRC_QR_ENA 0x0004 /* DRC_QR_ENA */
+#define WM8903_DRC_QR_ENA_MASK 0x0004 /* DRC_QR_ENA */
+#define WM8903_DRC_QR_ENA_SHIFT 2 /* DRC_QR_ENA */
+#define WM8903_DRC_QR_ENA_WIDTH 1 /* DRC_QR_ENA */
+#define WM8903_DRC_ANTICLIP_ENA 0x0002 /* DRC_ANTICLIP_ENA */
+#define WM8903_DRC_ANTICLIP_ENA_MASK 0x0002 /* DRC_ANTICLIP_ENA */
+#define WM8903_DRC_ANTICLIP_ENA_SHIFT 1 /* DRC_ANTICLIP_ENA */
+#define WM8903_DRC_ANTICLIP_ENA_WIDTH 1 /* DRC_ANTICLIP_ENA */
+#define WM8903_DRC_HYST_ENA 0x0001 /* DRC_HYST_ENA */
+#define WM8903_DRC_HYST_ENA_MASK 0x0001 /* DRC_HYST_ENA */
+#define WM8903_DRC_HYST_ENA_SHIFT 0 /* DRC_HYST_ENA */
+#define WM8903_DRC_HYST_ENA_WIDTH 1 /* DRC_HYST_ENA */
+
+/*
+ * R41 (0x29) - DRC 1
+ */
+#define WM8903_DRC_ATTACK_RATE_MASK 0xF000 /* DRC_ATTACK_RATE - [15:12] */
+#define WM8903_DRC_ATTACK_RATE_SHIFT 12 /* DRC_ATTACK_RATE - [15:12] */
+#define WM8903_DRC_ATTACK_RATE_WIDTH 4 /* DRC_ATTACK_RATE - [15:12] */
+#define WM8903_DRC_DECAY_RATE_MASK 0x0F00 /* DRC_DECAY_RATE - [11:8] */
+#define WM8903_DRC_DECAY_RATE_SHIFT 8 /* DRC_DECAY_RATE - [11:8] */
+#define WM8903_DRC_DECAY_RATE_WIDTH 4 /* DRC_DECAY_RATE - [11:8] */
+#define WM8903_DRC_THRESH_QR_MASK 0x00C0 /* DRC_THRESH_QR - [7:6] */
+#define WM8903_DRC_THRESH_QR_SHIFT 6 /* DRC_THRESH_QR - [7:6] */
+#define WM8903_DRC_THRESH_QR_WIDTH 2 /* DRC_THRESH_QR - [7:6] */
+#define WM8903_DRC_RATE_QR_MASK 0x0030 /* DRC_RATE_QR - [5:4] */
+#define WM8903_DRC_RATE_QR_SHIFT 4 /* DRC_RATE_QR - [5:4] */
+#define WM8903_DRC_RATE_QR_WIDTH 2 /* DRC_RATE_QR - [5:4] */
+#define WM8903_DRC_MINGAIN_MASK 0x000C /* DRC_MINGAIN - [3:2] */
+#define WM8903_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [3:2] */
+#define WM8903_DRC_MINGAIN_WIDTH 2 /* DRC_MINGAIN - [3:2] */
+#define WM8903_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */
+#define WM8903_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */
+#define WM8903_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R42 (0x2A) - DRC 2
+ */
+#define WM8903_DRC_R0_SLOPE_COMP_MASK 0x0038 /* DRC_R0_SLOPE_COMP - [5:3] */
+#define WM8903_DRC_R0_SLOPE_COMP_SHIFT 3 /* DRC_R0_SLOPE_COMP - [5:3] */
+#define WM8903_DRC_R0_SLOPE_COMP_WIDTH 3 /* DRC_R0_SLOPE_COMP - [5:3] */
+#define WM8903_DRC_R1_SLOPE_COMP_MASK 0x0007 /* DRC_R1_SLOPE_COMP - [2:0] */
+#define WM8903_DRC_R1_SLOPE_COMP_SHIFT 0 /* DRC_R1_SLOPE_COMP - [2:0] */
+#define WM8903_DRC_R1_SLOPE_COMP_WIDTH 3 /* DRC_R1_SLOPE_COMP - [2:0] */
+
+/*
+ * R43 (0x2B) - DRC 3
+ */
+#define WM8903_DRC_THRESH_COMP_MASK 0x07E0 /* DRC_THRESH_COMP - [10:5] */
+#define WM8903_DRC_THRESH_COMP_SHIFT 5 /* DRC_THRESH_COMP - [10:5] */
+#define WM8903_DRC_THRESH_COMP_WIDTH 6 /* DRC_THRESH_COMP - [10:5] */
+#define WM8903_DRC_AMP_COMP_MASK 0x001F /* DRC_AMP_COMP - [4:0] */
+#define WM8903_DRC_AMP_COMP_SHIFT 0 /* DRC_AMP_COMP - [4:0] */
+#define WM8903_DRC_AMP_COMP_WIDTH 5 /* DRC_AMP_COMP - [4:0] */
+
+/*
+ * R44 (0x2C) - Analogue Left Input 0
+ */
+#define WM8903_LINMUTE 0x0080 /* LINMUTE */
+#define WM8903_LINMUTE_MASK 0x0080 /* LINMUTE */
+#define WM8903_LINMUTE_SHIFT 7 /* LINMUTE */
+#define WM8903_LINMUTE_WIDTH 1 /* LINMUTE */
+#define WM8903_LIN_VOL_MASK 0x001F /* LIN_VOL - [4:0] */
+#define WM8903_LIN_VOL_SHIFT 0 /* LIN_VOL - [4:0] */
+#define WM8903_LIN_VOL_WIDTH 5 /* LIN_VOL - [4:0] */
+
+/*
+ * R45 (0x2D) - Analogue Right Input 0
+ */
+#define WM8903_RINMUTE 0x0080 /* RINMUTE */
+#define WM8903_RINMUTE_MASK 0x0080 /* RINMUTE */
+#define WM8903_RINMUTE_SHIFT 7 /* RINMUTE */
+#define WM8903_RINMUTE_WIDTH 1 /* RINMUTE */
+#define WM8903_RIN_VOL_MASK 0x001F /* RIN_VOL - [4:0] */
+#define WM8903_RIN_VOL_SHIFT 0 /* RIN_VOL - [4:0] */
+#define WM8903_RIN_VOL_WIDTH 5 /* RIN_VOL - [4:0] */
+
+/*
+ * R46 (0x2E) - Analogue Left Input 1
+ */
+#define WM8903_INL_CM_ENA 0x0040 /* INL_CM_ENA */
+#define WM8903_INL_CM_ENA_MASK 0x0040 /* INL_CM_ENA */
+#define WM8903_INL_CM_ENA_SHIFT 6 /* INL_CM_ENA */
+#define WM8903_INL_CM_ENA_WIDTH 1 /* INL_CM_ENA */
+#define WM8903_L_IP_SEL_N_MASK 0x0030 /* L_IP_SEL_N - [5:4] */
+#define WM8903_L_IP_SEL_N_SHIFT 4 /* L_IP_SEL_N - [5:4] */
+#define WM8903_L_IP_SEL_N_WIDTH 2 /* L_IP_SEL_N - [5:4] */
+#define WM8903_L_IP_SEL_P_MASK 0x000C /* L_IP_SEL_P - [3:2] */
+#define WM8903_L_IP_SEL_P_SHIFT 2 /* L_IP_SEL_P - [3:2] */
+#define WM8903_L_IP_SEL_P_WIDTH 2 /* L_IP_SEL_P - [3:2] */
+#define WM8903_L_MODE_MASK 0x0003 /* L_MODE - [1:0] */
+#define WM8903_L_MODE_SHIFT 0 /* L_MODE - [1:0] */
+#define WM8903_L_MODE_WIDTH 2 /* L_MODE - [1:0] */
+
+/*
+ * R47 (0x2F) - Analogue Right Input 1
+ */
+#define WM8903_INR_CM_ENA 0x0040 /* INR_CM_ENA */
+#define WM8903_INR_CM_ENA_MASK 0x0040 /* INR_CM_ENA */
+#define WM8903_INR_CM_ENA_SHIFT 6 /* INR_CM_ENA */
+#define WM8903_INR_CM_ENA_WIDTH 1 /* INR_CM_ENA */
+#define WM8903_R_IP_SEL_N_MASK 0x0030 /* R_IP_SEL_N - [5:4] */
+#define WM8903_R_IP_SEL_N_SHIFT 4 /* R_IP_SEL_N - [5:4] */
+#define WM8903_R_IP_SEL_N_WIDTH 2 /* R_IP_SEL_N - [5:4] */
+#define WM8903_R_IP_SEL_P_MASK 0x000C /* R_IP_SEL_P - [3:2] */
+#define WM8903_R_IP_SEL_P_SHIFT 2 /* R_IP_SEL_P - [3:2] */
+#define WM8903_R_IP_SEL_P_WIDTH 2 /* R_IP_SEL_P - [3:2] */
+#define WM8903_R_MODE_MASK 0x0003 /* R_MODE - [1:0] */
+#define WM8903_R_MODE_SHIFT 0 /* R_MODE - [1:0] */
+#define WM8903_R_MODE_WIDTH 2 /* R_MODE - [1:0] */
+
+/*
+ * R50 (0x32) - Analogue Left Mix 0
+ */
+#define WM8903_DACL_TO_MIXOUTL 0x0008 /* DACL_TO_MIXOUTL */
+#define WM8903_DACL_TO_MIXOUTL_MASK 0x0008 /* DACL_TO_MIXOUTL */
+#define WM8903_DACL_TO_MIXOUTL_SHIFT 3 /* DACL_TO_MIXOUTL */
+#define WM8903_DACL_TO_MIXOUTL_WIDTH 1 /* DACL_TO_MIXOUTL */
+#define WM8903_DACR_TO_MIXOUTL 0x0004 /* DACR_TO_MIXOUTL */
+#define WM8903_DACR_TO_MIXOUTL_MASK 0x0004 /* DACR_TO_MIXOUTL */
+#define WM8903_DACR_TO_MIXOUTL_SHIFT 2 /* DACR_TO_MIXOUTL */
+#define WM8903_DACR_TO_MIXOUTL_WIDTH 1 /* DACR_TO_MIXOUTL */
+#define WM8903_BYPASSL_TO_MIXOUTL 0x0002 /* BYPASSL_TO_MIXOUTL */
+#define WM8903_BYPASSL_TO_MIXOUTL_MASK 0x0002 /* BYPASSL_TO_MIXOUTL */
+#define WM8903_BYPASSL_TO_MIXOUTL_SHIFT 1 /* BYPASSL_TO_MIXOUTL */
+#define WM8903_BYPASSL_TO_MIXOUTL_WIDTH 1 /* BYPASSL_TO_MIXOUTL */
+#define WM8903_BYPASSR_TO_MIXOUTL 0x0001 /* BYPASSR_TO_MIXOUTL */
+#define WM8903_BYPASSR_TO_MIXOUTL_MASK 0x0001 /* BYPASSR_TO_MIXOUTL */
+#define WM8903_BYPASSR_TO_MIXOUTL_SHIFT 0 /* BYPASSR_TO_MIXOUTL */
+#define WM8903_BYPASSR_TO_MIXOUTL_WIDTH 1 /* BYPASSR_TO_MIXOUTL */
+
+/*
+ * R51 (0x33) - Analogue Right Mix 0
+ */
+#define WM8903_DACL_TO_MIXOUTR 0x0008 /* DACL_TO_MIXOUTR */
+#define WM8903_DACL_TO_MIXOUTR_MASK 0x0008 /* DACL_TO_MIXOUTR */
+#define WM8903_DACL_TO_MIXOUTR_SHIFT 3 /* DACL_TO_MIXOUTR */
+#define WM8903_DACL_TO_MIXOUTR_WIDTH 1 /* DACL_TO_MIXOUTR */
+#define WM8903_DACR_TO_MIXOUTR 0x0004 /* DACR_TO_MIXOUTR */
+#define WM8903_DACR_TO_MIXOUTR_MASK 0x0004 /* DACR_TO_MIXOUTR */
+#define WM8903_DACR_TO_MIXOUTR_SHIFT 2 /* DACR_TO_MIXOUTR */
+#define WM8903_DACR_TO_MIXOUTR_WIDTH 1 /* DACR_TO_MIXOUTR */
+#define WM8903_BYPASSL_TO_MIXOUTR 0x0002 /* BYPASSL_TO_MIXOUTR */
+#define WM8903_BYPASSL_TO_MIXOUTR_MASK 0x0002 /* BYPASSL_TO_MIXOUTR */
+#define WM8903_BYPASSL_TO_MIXOUTR_SHIFT 1 /* BYPASSL_TO_MIXOUTR */
+#define WM8903_BYPASSL_TO_MIXOUTR_WIDTH 1 /* BYPASSL_TO_MIXOUTR */
+#define WM8903_BYPASSR_TO_MIXOUTR 0x0001 /* BYPASSR_TO_MIXOUTR */
+#define WM8903_BYPASSR_TO_MIXOUTR_MASK 0x0001 /* BYPASSR_TO_MIXOUTR */
+#define WM8903_BYPASSR_TO_MIXOUTR_SHIFT 0 /* BYPASSR_TO_MIXOUTR */
+#define WM8903_BYPASSR_TO_MIXOUTR_WIDTH 1 /* BYPASSR_TO_MIXOUTR */
+
+/*
+ * R52 (0x34) - Analogue Spk Mix Left 0
+ */
+#define WM8903_DACL_TO_MIXSPKL 0x0008 /* DACL_TO_MIXSPKL */
+#define WM8903_DACL_TO_MIXSPKL_MASK 0x0008 /* DACL_TO_MIXSPKL */
+#define WM8903_DACL_TO_MIXSPKL_SHIFT 3 /* DACL_TO_MIXSPKL */
+#define WM8903_DACL_TO_MIXSPKL_WIDTH 1 /* DACL_TO_MIXSPKL */
+#define WM8903_DACR_TO_MIXSPKL 0x0004 /* DACR_TO_MIXSPKL */
+#define WM8903_DACR_TO_MIXSPKL_MASK 0x0004 /* DACR_TO_MIXSPKL */
+#define WM8903_DACR_TO_MIXSPKL_SHIFT 2 /* DACR_TO_MIXSPKL */
+#define WM8903_DACR_TO_MIXSPKL_WIDTH 1 /* DACR_TO_MIXSPKL */
+#define WM8903_BYPASSL_TO_MIXSPKL 0x0002 /* BYPASSL_TO_MIXSPKL */
+#define WM8903_BYPASSL_TO_MIXSPKL_MASK 0x0002 /* BYPASSL_TO_MIXSPKL */
+#define WM8903_BYPASSL_TO_MIXSPKL_SHIFT 1 /* BYPASSL_TO_MIXSPKL */
+#define WM8903_BYPASSL_TO_MIXSPKL_WIDTH 1 /* BYPASSL_TO_MIXSPKL */
+#define WM8903_BYPASSR_TO_MIXSPKL 0x0001 /* BYPASSR_TO_MIXSPKL */
+#define WM8903_BYPASSR_TO_MIXSPKL_MASK 0x0001 /* BYPASSR_TO_MIXSPKL */
+#define WM8903_BYPASSR_TO_MIXSPKL_SHIFT 0 /* BYPASSR_TO_MIXSPKL */
+#define WM8903_BYPASSR_TO_MIXSPKL_WIDTH 1 /* BYPASSR_TO_MIXSPKL */
+
+/*
+ * R53 (0x35) - Analogue Spk Mix Left 1
+ */
+#define WM8903_DACL_MIXSPKL_VOL 0x0008 /* DACL_MIXSPKL_VOL */
+#define WM8903_DACL_MIXSPKL_VOL_MASK 0x0008 /* DACL_MIXSPKL_VOL */
+#define WM8903_DACL_MIXSPKL_VOL_SHIFT 3 /* DACL_MIXSPKL_VOL */
+#define WM8903_DACL_MIXSPKL_VOL_WIDTH 1 /* DACL_MIXSPKL_VOL */
+#define WM8903_DACR_MIXSPKL_VOL 0x0004 /* DACR_MIXSPKL_VOL */
+#define WM8903_DACR_MIXSPKL_VOL_MASK 0x0004 /* DACR_MIXSPKL_VOL */
+#define WM8903_DACR_MIXSPKL_VOL_SHIFT 2 /* DACR_MIXSPKL_VOL */
+#define WM8903_DACR_MIXSPKL_VOL_WIDTH 1 /* DACR_MIXSPKL_VOL */
+#define WM8903_BYPASSL_MIXSPKL_VOL 0x0002 /* BYPASSL_MIXSPKL_VOL */
+#define WM8903_BYPASSL_MIXSPKL_VOL_MASK 0x0002 /* BYPASSL_MIXSPKL_VOL */
+#define WM8903_BYPASSL_MIXSPKL_VOL_SHIFT 1 /* BYPASSL_MIXSPKL_VOL */
+#define WM8903_BYPASSL_MIXSPKL_VOL_WIDTH 1 /* BYPASSL_MIXSPKL_VOL */
+#define WM8903_BYPASSR_MIXSPKL_VOL 0x0001 /* BYPASSR_MIXSPKL_VOL */
+#define WM8903_BYPASSR_MIXSPKL_VOL_MASK 0x0001 /* BYPASSR_MIXSPKL_VOL */
+#define WM8903_BYPASSR_MIXSPKL_VOL_SHIFT 0 /* BYPASSR_MIXSPKL_VOL */
+#define WM8903_BYPASSR_MIXSPKL_VOL_WIDTH 1 /* BYPASSR_MIXSPKL_VOL */
+
+/*
+ * R54 (0x36) - Analogue Spk Mix Right 0
+ */
+#define WM8903_DACL_TO_MIXSPKR 0x0008 /* DACL_TO_MIXSPKR */
+#define WM8903_DACL_TO_MIXSPKR_MASK 0x0008 /* DACL_TO_MIXSPKR */
+#define WM8903_DACL_TO_MIXSPKR_SHIFT 3 /* DACL_TO_MIXSPKR */
+#define WM8903_DACL_TO_MIXSPKR_WIDTH 1 /* DACL_TO_MIXSPKR */
+#define WM8903_DACR_TO_MIXSPKR 0x0004 /* DACR_TO_MIXSPKR */
+#define WM8903_DACR_TO_MIXSPKR_MASK 0x0004 /* DACR_TO_MIXSPKR */
+#define WM8903_DACR_TO_MIXSPKR_SHIFT 2 /* DACR_TO_MIXSPKR */
+#define WM8903_DACR_TO_MIXSPKR_WIDTH 1 /* DACR_TO_MIXSPKR */
+#define WM8903_BYPASSL_TO_MIXSPKR 0x0002 /* BYPASSL_TO_MIXSPKR */
+#define WM8903_BYPASSL_TO_MIXSPKR_MASK 0x0002 /* BYPASSL_TO_MIXSPKR */
+#define WM8903_BYPASSL_TO_MIXSPKR_SHIFT 1 /* BYPASSL_TO_MIXSPKR */
+#define WM8903_BYPASSL_TO_MIXSPKR_WIDTH 1 /* BYPASSL_TO_MIXSPKR */
+#define WM8903_BYPASSR_TO_MIXSPKR 0x0001 /* BYPASSR_TO_MIXSPKR */
+#define WM8903_BYPASSR_TO_MIXSPKR_MASK 0x0001 /* BYPASSR_TO_MIXSPKR */
+#define WM8903_BYPASSR_TO_MIXSPKR_SHIFT 0 /* BYPASSR_TO_MIXSPKR */
+#define WM8903_BYPASSR_TO_MIXSPKR_WIDTH 1 /* BYPASSR_TO_MIXSPKR */
+
+/*
+ * R55 (0x37) - Analogue Spk Mix Right 1
+ */
+#define WM8903_DACL_MIXSPKR_VOL 0x0008 /* DACL_MIXSPKR_VOL */
+#define WM8903_DACL_MIXSPKR_VOL_MASK 0x0008 /* DACL_MIXSPKR_VOL */
+#define WM8903_DACL_MIXSPKR_VOL_SHIFT 3 /* DACL_MIXSPKR_VOL */
+#define WM8903_DACL_MIXSPKR_VOL_WIDTH 1 /* DACL_MIXSPKR_VOL */
+#define WM8903_DACR_MIXSPKR_VOL 0x0004 /* DACR_MIXSPKR_VOL */
+#define WM8903_DACR_MIXSPKR_VOL_MASK 0x0004 /* DACR_MIXSPKR_VOL */
+#define WM8903_DACR_MIXSPKR_VOL_SHIFT 2 /* DACR_MIXSPKR_VOL */
+#define WM8903_DACR_MIXSPKR_VOL_WIDTH 1 /* DACR_MIXSPKR_VOL */
+#define WM8903_BYPASSL_MIXSPKR_VOL 0x0002 /* BYPASSL_MIXSPKR_VOL */
+#define WM8903_BYPASSL_MIXSPKR_VOL_MASK 0x0002 /* BYPASSL_MIXSPKR_VOL */
+#define WM8903_BYPASSL_MIXSPKR_VOL_SHIFT 1 /* BYPASSL_MIXSPKR_VOL */
+#define WM8903_BYPASSL_MIXSPKR_VOL_WIDTH 1 /* BYPASSL_MIXSPKR_VOL */
+#define WM8903_BYPASSR_MIXSPKR_VOL 0x0001 /* BYPASSR_MIXSPKR_VOL */
+#define WM8903_BYPASSR_MIXSPKR_VOL_MASK 0x0001 /* BYPASSR_MIXSPKR_VOL */
+#define WM8903_BYPASSR_MIXSPKR_VOL_SHIFT 0 /* BYPASSR_MIXSPKR_VOL */
+#define WM8903_BYPASSR_MIXSPKR_VOL_WIDTH 1 /* BYPASSR_MIXSPKR_VOL */
+
+/*
+ * R57 (0x39) - Analogue OUT1 Left
+ */
+#define WM8903_HPL_MUTE 0x0100 /* HPL_MUTE */
+#define WM8903_HPL_MUTE_MASK 0x0100 /* HPL_MUTE */
+#define WM8903_HPL_MUTE_SHIFT 8 /* HPL_MUTE */
+#define WM8903_HPL_MUTE_WIDTH 1 /* HPL_MUTE */
+#define WM8903_HPOUTVU 0x0080 /* HPOUTVU */
+#define WM8903_HPOUTVU_MASK 0x0080 /* HPOUTVU */
+#define WM8903_HPOUTVU_SHIFT 7 /* HPOUTVU */
+#define WM8903_HPOUTVU_WIDTH 1 /* HPOUTVU */
+#define WM8903_HPOUTLZC 0x0040 /* HPOUTLZC */
+#define WM8903_HPOUTLZC_MASK 0x0040 /* HPOUTLZC */
+#define WM8903_HPOUTLZC_SHIFT 6 /* HPOUTLZC */
+#define WM8903_HPOUTLZC_WIDTH 1 /* HPOUTLZC */
+#define WM8903_HPOUTL_VOL_MASK 0x003F /* HPOUTL_VOL - [5:0] */
+#define WM8903_HPOUTL_VOL_SHIFT 0 /* HPOUTL_VOL - [5:0] */
+#define WM8903_HPOUTL_VOL_WIDTH 6 /* HPOUTL_VOL - [5:0] */
+
+/*
+ * R58 (0x3A) - Analogue OUT1 Right
+ */
+#define WM8903_HPR_MUTE 0x0100 /* HPR_MUTE */
+#define WM8903_HPR_MUTE_MASK 0x0100 /* HPR_MUTE */
+#define WM8903_HPR_MUTE_SHIFT 8 /* HPR_MUTE */
+#define WM8903_HPR_MUTE_WIDTH 1 /* HPR_MUTE */
+#define WM8903_HPOUTVU 0x0080 /* HPOUTVU */
+#define WM8903_HPOUTVU_MASK 0x0080 /* HPOUTVU */
+#define WM8903_HPOUTVU_SHIFT 7 /* HPOUTVU */
+#define WM8903_HPOUTVU_WIDTH 1 /* HPOUTVU */
+#define WM8903_HPOUTRZC 0x0040 /* HPOUTRZC */
+#define WM8903_HPOUTRZC_MASK 0x0040 /* HPOUTRZC */
+#define WM8903_HPOUTRZC_SHIFT 6 /* HPOUTRZC */
+#define WM8903_HPOUTRZC_WIDTH 1 /* HPOUTRZC */
+#define WM8903_HPOUTR_VOL_MASK 0x003F /* HPOUTR_VOL - [5:0] */
+#define WM8903_HPOUTR_VOL_SHIFT 0 /* HPOUTR_VOL - [5:0] */
+#define WM8903_HPOUTR_VOL_WIDTH 6 /* HPOUTR_VOL - [5:0] */
+
+/*
+ * R59 (0x3B) - Analogue OUT2 Left
+ */
+#define WM8903_LINEOUTL_MUTE 0x0100 /* LINEOUTL_MUTE */
+#define WM8903_LINEOUTL_MUTE_MASK 0x0100 /* LINEOUTL_MUTE */
+#define WM8903_LINEOUTL_MUTE_SHIFT 8 /* LINEOUTL_MUTE */
+#define WM8903_LINEOUTL_MUTE_WIDTH 1 /* LINEOUTL_MUTE */
+#define WM8903_LINEOUTVU 0x0080 /* LINEOUTVU */
+#define WM8903_LINEOUTVU_MASK 0x0080 /* LINEOUTVU */
+#define WM8903_LINEOUTVU_SHIFT 7 /* LINEOUTVU */
+#define WM8903_LINEOUTVU_WIDTH 1 /* LINEOUTVU */
+#define WM8903_LINEOUTLZC 0x0040 /* LINEOUTLZC */
+#define WM8903_LINEOUTLZC_MASK 0x0040 /* LINEOUTLZC */
+#define WM8903_LINEOUTLZC_SHIFT 6 /* LINEOUTLZC */
+#define WM8903_LINEOUTLZC_WIDTH 1 /* LINEOUTLZC */
+#define WM8903_LINEOUTL_VOL_MASK 0x003F /* LINEOUTL_VOL - [5:0] */
+#define WM8903_LINEOUTL_VOL_SHIFT 0 /* LINEOUTL_VOL - [5:0] */
+#define WM8903_LINEOUTL_VOL_WIDTH 6 /* LINEOUTL_VOL - [5:0] */
+
+/*
+ * R60 (0x3C) - Analogue OUT2 Right
+ */
+#define WM8903_LINEOUTR_MUTE 0x0100 /* LINEOUTR_MUTE */
+#define WM8903_LINEOUTR_MUTE_MASK 0x0100 /* LINEOUTR_MUTE */
+#define WM8903_LINEOUTR_MUTE_SHIFT 8 /* LINEOUTR_MUTE */
+#define WM8903_LINEOUTR_MUTE_WIDTH 1 /* LINEOUTR_MUTE */
+#define WM8903_LINEOUTVU 0x0080 /* LINEOUTVU */
+#define WM8903_LINEOUTVU_MASK 0x0080 /* LINEOUTVU */
+#define WM8903_LINEOUTVU_SHIFT 7 /* LINEOUTVU */
+#define WM8903_LINEOUTVU_WIDTH 1 /* LINEOUTVU */
+#define WM8903_LINEOUTRZC 0x0040 /* LINEOUTRZC */
+#define WM8903_LINEOUTRZC_MASK 0x0040 /* LINEOUTRZC */
+#define WM8903_LINEOUTRZC_SHIFT 6 /* LINEOUTRZC */
+#define WM8903_LINEOUTRZC_WIDTH 1 /* LINEOUTRZC */
+#define WM8903_LINEOUTR_VOL_MASK 0x003F /* LINEOUTR_VOL - [5:0] */
+#define WM8903_LINEOUTR_VOL_SHIFT 0 /* LINEOUTR_VOL - [5:0] */
+#define WM8903_LINEOUTR_VOL_WIDTH 6 /* LINEOUTR_VOL - [5:0] */
+
+/*
+ * R62 (0x3E) - Analogue OUT3 Left
+ */
+#define WM8903_SPKL_MUTE 0x0100 /* SPKL_MUTE */
+#define WM8903_SPKL_MUTE_MASK 0x0100 /* SPKL_MUTE */
+#define WM8903_SPKL_MUTE_SHIFT 8 /* SPKL_MUTE */
+#define WM8903_SPKL_MUTE_WIDTH 1 /* SPKL_MUTE */
+#define WM8903_SPKVU 0x0080 /* SPKVU */
+#define WM8903_SPKVU_MASK 0x0080 /* SPKVU */
+#define WM8903_SPKVU_SHIFT 7 /* SPKVU */
+#define WM8903_SPKVU_WIDTH 1 /* SPKVU */
+#define WM8903_SPKLZC 0x0040 /* SPKLZC */
+#define WM8903_SPKLZC_MASK 0x0040 /* SPKLZC */
+#define WM8903_SPKLZC_SHIFT 6 /* SPKLZC */
+#define WM8903_SPKLZC_WIDTH 1 /* SPKLZC */
+#define WM8903_SPKL_VOL_MASK 0x003F /* SPKL_VOL - [5:0] */
+#define WM8903_SPKL_VOL_SHIFT 0 /* SPKL_VOL - [5:0] */
+#define WM8903_SPKL_VOL_WIDTH 6 /* SPKL_VOL - [5:0] */
+
+/*
+ * R63 (0x3F) - Analogue OUT3 Right
+ */
+#define WM8903_SPKR_MUTE 0x0100 /* SPKR_MUTE */
+#define WM8903_SPKR_MUTE_MASK 0x0100 /* SPKR_MUTE */
+#define WM8903_SPKR_MUTE_SHIFT 8 /* SPKR_MUTE */
+#define WM8903_SPKR_MUTE_WIDTH 1 /* SPKR_MUTE */
+#define WM8903_SPKVU 0x0080 /* SPKVU */
+#define WM8903_SPKVU_MASK 0x0080 /* SPKVU */
+#define WM8903_SPKVU_SHIFT 7 /* SPKVU */
+#define WM8903_SPKVU_WIDTH 1 /* SPKVU */
+#define WM8903_SPKRZC 0x0040 /* SPKRZC */
+#define WM8903_SPKRZC_MASK 0x0040 /* SPKRZC */
+#define WM8903_SPKRZC_SHIFT 6 /* SPKRZC */
+#define WM8903_SPKRZC_WIDTH 1 /* SPKRZC */
+#define WM8903_SPKR_VOL_MASK 0x003F /* SPKR_VOL - [5:0] */
+#define WM8903_SPKR_VOL_SHIFT 0 /* SPKR_VOL - [5:0] */
+#define WM8903_SPKR_VOL_WIDTH 6 /* SPKR_VOL - [5:0] */
+
+/*
+ * R65 (0x41) - Analogue SPK Output Control 0
+ */
+#define WM8903_SPK_DISCHARGE 0x0002 /* SPK_DISCHARGE */
+#define WM8903_SPK_DISCHARGE_MASK 0x0002 /* SPK_DISCHARGE */
+#define WM8903_SPK_DISCHARGE_SHIFT 1 /* SPK_DISCHARGE */
+#define WM8903_SPK_DISCHARGE_WIDTH 1 /* SPK_DISCHARGE */
+#define WM8903_VROI 0x0001 /* VROI */
+#define WM8903_VROI_MASK 0x0001 /* VROI */
+#define WM8903_VROI_SHIFT 0 /* VROI */
+#define WM8903_VROI_WIDTH 1 /* VROI */
+
+/*
+ * R67 (0x43) - DC Servo 0
+ */
+#define WM8903_DCS_MASTER_ENA 0x0010 /* DCS_MASTER_ENA */
+#define WM8903_DCS_MASTER_ENA_MASK 0x0010 /* DCS_MASTER_ENA */
+#define WM8903_DCS_MASTER_ENA_SHIFT 4 /* DCS_MASTER_ENA */
+#define WM8903_DCS_MASTER_ENA_WIDTH 1 /* DCS_MASTER_ENA */
+#define WM8903_DCS_ENA_MASK 0x000F /* DCS_ENA - [3:0] */
+#define WM8903_DCS_ENA_SHIFT 0 /* DCS_ENA - [3:0] */
+#define WM8903_DCS_ENA_WIDTH 4 /* DCS_ENA - [3:0] */
+
+/*
+ * R69 (0x45) - DC Servo 2
+ */
+#define WM8903_DCS_MODE_MASK 0x0003 /* DCS_MODE - [1:0] */
+#define WM8903_DCS_MODE_SHIFT 0 /* DCS_MODE - [1:0] */
+#define WM8903_DCS_MODE_WIDTH 2 /* DCS_MODE - [1:0] */
+
+/*
+ * R90 (0x5A) - Analogue HP 0
+ */
+#define WM8903_HPL_RMV_SHORT 0x0080 /* HPL_RMV_SHORT */
+#define WM8903_HPL_RMV_SHORT_MASK 0x0080 /* HPL_RMV_SHORT */
+#define WM8903_HPL_RMV_SHORT_SHIFT 7 /* HPL_RMV_SHORT */
+#define WM8903_HPL_RMV_SHORT_WIDTH 1 /* HPL_RMV_SHORT */
+#define WM8903_HPL_ENA_OUTP 0x0040 /* HPL_ENA_OUTP */
+#define WM8903_HPL_ENA_OUTP_MASK 0x0040 /* HPL_ENA_OUTP */
+#define WM8903_HPL_ENA_OUTP_SHIFT 6 /* HPL_ENA_OUTP */
+#define WM8903_HPL_ENA_OUTP_WIDTH 1 /* HPL_ENA_OUTP */
+#define WM8903_HPL_ENA_DLY 0x0020 /* HPL_ENA_DLY */
+#define WM8903_HPL_ENA_DLY_MASK 0x0020 /* HPL_ENA_DLY */
+#define WM8903_HPL_ENA_DLY_SHIFT 5 /* HPL_ENA_DLY */
+#define WM8903_HPL_ENA_DLY_WIDTH 1 /* HPL_ENA_DLY */
+#define WM8903_HPL_ENA 0x0010 /* HPL_ENA */
+#define WM8903_HPL_ENA_MASK 0x0010 /* HPL_ENA */
+#define WM8903_HPL_ENA_SHIFT 4 /* HPL_ENA */
+#define WM8903_HPL_ENA_WIDTH 1 /* HPL_ENA */
+#define WM8903_HPR_RMV_SHORT 0x0008 /* HPR_RMV_SHORT */
+#define WM8903_HPR_RMV_SHORT_MASK 0x0008 /* HPR_RMV_SHORT */
+#define WM8903_HPR_RMV_SHORT_SHIFT 3 /* HPR_RMV_SHORT */
+#define WM8903_HPR_RMV_SHORT_WIDTH 1 /* HPR_RMV_SHORT */
+#define WM8903_HPR_ENA_OUTP 0x0004 /* HPR_ENA_OUTP */
+#define WM8903_HPR_ENA_OUTP_MASK 0x0004 /* HPR_ENA_OUTP */
+#define WM8903_HPR_ENA_OUTP_SHIFT 2 /* HPR_ENA_OUTP */
+#define WM8903_HPR_ENA_OUTP_WIDTH 1 /* HPR_ENA_OUTP */
+#define WM8903_HPR_ENA_DLY 0x0002 /* HPR_ENA_DLY */
+#define WM8903_HPR_ENA_DLY_MASK 0x0002 /* HPR_ENA_DLY */
+#define WM8903_HPR_ENA_DLY_SHIFT 1 /* HPR_ENA_DLY */
+#define WM8903_HPR_ENA_DLY_WIDTH 1 /* HPR_ENA_DLY */
+#define WM8903_HPR_ENA 0x0001 /* HPR_ENA */
+#define WM8903_HPR_ENA_MASK 0x0001 /* HPR_ENA */
+#define WM8903_HPR_ENA_SHIFT 0 /* HPR_ENA */
+#define WM8903_HPR_ENA_WIDTH 1 /* HPR_ENA */
+
+/*
+ * R94 (0x5E) - Analogue Lineout 0
+ */
+#define WM8903_LINEOUTL_RMV_SHORT 0x0080 /* LINEOUTL_RMV_SHORT */
+#define WM8903_LINEOUTL_RMV_SHORT_MASK 0x0080 /* LINEOUTL_RMV_SHORT */
+#define WM8903_LINEOUTL_RMV_SHORT_SHIFT 7 /* LINEOUTL_RMV_SHORT */
+#define WM8903_LINEOUTL_RMV_SHORT_WIDTH 1 /* LINEOUTL_RMV_SHORT */
+#define WM8903_LINEOUTL_ENA_OUTP 0x0040 /* LINEOUTL_ENA_OUTP */
+#define WM8903_LINEOUTL_ENA_OUTP_MASK 0x0040 /* LINEOUTL_ENA_OUTP */
+#define WM8903_LINEOUTL_ENA_OUTP_SHIFT 6 /* LINEOUTL_ENA_OUTP */
+#define WM8903_LINEOUTL_ENA_OUTP_WIDTH 1 /* LINEOUTL_ENA_OUTP */
+#define WM8903_LINEOUTL_ENA_DLY 0x0020 /* LINEOUTL_ENA_DLY */
+#define WM8903_LINEOUTL_ENA_DLY_MASK 0x0020 /* LINEOUTL_ENA_DLY */
+#define WM8903_LINEOUTL_ENA_DLY_SHIFT 5 /* LINEOUTL_ENA_DLY */
+#define WM8903_LINEOUTL_ENA_DLY_WIDTH 1 /* LINEOUTL_ENA_DLY */
+#define WM8903_LINEOUTL_ENA 0x0010 /* LINEOUTL_ENA */
+#define WM8903_LINEOUTL_ENA_MASK 0x0010 /* LINEOUTL_ENA */
+#define WM8903_LINEOUTL_ENA_SHIFT 4 /* LINEOUTL_ENA */
+#define WM8903_LINEOUTL_ENA_WIDTH 1 /* LINEOUTL_ENA */
+#define WM8903_LINEOUTR_RMV_SHORT 0x0008 /* LINEOUTR_RMV_SHORT */
+#define WM8903_LINEOUTR_RMV_SHORT_MASK 0x0008 /* LINEOUTR_RMV_SHORT */
+#define WM8903_LINEOUTR_RMV_SHORT_SHIFT 3 /* LINEOUTR_RMV_SHORT */
+#define WM8903_LINEOUTR_RMV_SHORT_WIDTH 1 /* LINEOUTR_RMV_SHORT */
+#define WM8903_LINEOUTR_ENA_OUTP 0x0004 /* LINEOUTR_ENA_OUTP */
+#define WM8903_LINEOUTR_ENA_OUTP_MASK 0x0004 /* LINEOUTR_ENA_OUTP */
+#define WM8903_LINEOUTR_ENA_OUTP_SHIFT 2 /* LINEOUTR_ENA_OUTP */
+#define WM8903_LINEOUTR_ENA_OUTP_WIDTH 1 /* LINEOUTR_ENA_OUTP */
+#define WM8903_LINEOUTR_ENA_DLY 0x0002 /* LINEOUTR_ENA_DLY */
+#define WM8903_LINEOUTR_ENA_DLY_MASK 0x0002 /* LINEOUTR_ENA_DLY */
+#define WM8903_LINEOUTR_ENA_DLY_SHIFT 1 /* LINEOUTR_ENA_DLY */
+#define WM8903_LINEOUTR_ENA_DLY_WIDTH 1 /* LINEOUTR_ENA_DLY */
+#define WM8903_LINEOUTR_ENA 0x0001 /* LINEOUTR_ENA */
+#define WM8903_LINEOUTR_ENA_MASK 0x0001 /* LINEOUTR_ENA */
+#define WM8903_LINEOUTR_ENA_SHIFT 0 /* LINEOUTR_ENA */
+#define WM8903_LINEOUTR_ENA_WIDTH 1 /* LINEOUTR_ENA */
+
+/*
+ * R98 (0x62) - Charge Pump 0
+ */
+#define WM8903_CP_ENA 0x0001 /* CP_ENA */
+#define WM8903_CP_ENA_MASK 0x0001 /* CP_ENA */
+#define WM8903_CP_ENA_SHIFT 0 /* CP_ENA */
+#define WM8903_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R104 (0x68) - Class W 0
+ */
+#define WM8903_CP_DYN_FREQ 0x0002 /* CP_DYN_FREQ */
+#define WM8903_CP_DYN_FREQ_MASK 0x0002 /* CP_DYN_FREQ */
+#define WM8903_CP_DYN_FREQ_SHIFT 1 /* CP_DYN_FREQ */
+#define WM8903_CP_DYN_FREQ_WIDTH 1 /* CP_DYN_FREQ */
+#define WM8903_CP_DYN_V 0x0001 /* CP_DYN_V */
+#define WM8903_CP_DYN_V_MASK 0x0001 /* CP_DYN_V */
+#define WM8903_CP_DYN_V_SHIFT 0 /* CP_DYN_V */
+#define WM8903_CP_DYN_V_WIDTH 1 /* CP_DYN_V */
+
+/*
+ * R108 (0x6C) - Write Sequencer 0
+ */
+#define WM8903_WSEQ_ENA 0x0100 /* WSEQ_ENA */
+#define WM8903_WSEQ_ENA_MASK 0x0100 /* WSEQ_ENA */
+#define WM8903_WSEQ_ENA_SHIFT 8 /* WSEQ_ENA */
+#define WM8903_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM8903_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8903_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8903_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R109 (0x6D) - Write Sequencer 1
+ */
+#define WM8903_WSEQ_DATA_WIDTH_MASK 0x7000 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8903_WSEQ_DATA_WIDTH_SHIFT 12 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8903_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8903_WSEQ_DATA_START_MASK 0x0F00 /* WSEQ_DATA_START - [11:8] */
+#define WM8903_WSEQ_DATA_START_SHIFT 8 /* WSEQ_DATA_START - [11:8] */
+#define WM8903_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [11:8] */
+#define WM8903_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */
+#define WM8903_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */
+#define WM8903_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R110 (0x6E) - Write Sequencer 2
+ */
+#define WM8903_WSEQ_EOS 0x4000 /* WSEQ_EOS */
+#define WM8903_WSEQ_EOS_MASK 0x4000 /* WSEQ_EOS */
+#define WM8903_WSEQ_EOS_SHIFT 14 /* WSEQ_EOS */
+#define WM8903_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */
+#define WM8903_WSEQ_DELAY_MASK 0x0F00 /* WSEQ_DELAY - [11:8] */
+#define WM8903_WSEQ_DELAY_SHIFT 8 /* WSEQ_DELAY - [11:8] */
+#define WM8903_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [11:8] */
+#define WM8903_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */
+#define WM8903_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */
+#define WM8903_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */
+
+/*
+ * R111 (0x6F) - Write Sequencer 3
+ */
+#define WM8903_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM8903_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM8903_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM8903_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8903_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM8903_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM8903_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM8903_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8903_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */
+#define WM8903_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */
+#define WM8903_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R112 (0x70) - Write Sequencer 4
+ */
+#define WM8903_WSEQ_CURRENT_INDEX_MASK 0x03F0 /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8903_WSEQ_CURRENT_INDEX_SHIFT 4 /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8903_WSEQ_CURRENT_INDEX_WIDTH 6 /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8903_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */
+#define WM8903_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */
+#define WM8903_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */
+#define WM8903_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+
+/*
+ * R114 (0x72) - Control Interface
+ */
+#define WM8903_MASK_WRITE_ENA 0x0001 /* MASK_WRITE_ENA */
+#define WM8903_MASK_WRITE_ENA_MASK 0x0001 /* MASK_WRITE_ENA */
+#define WM8903_MASK_WRITE_ENA_SHIFT 0 /* MASK_WRITE_ENA */
+#define WM8903_MASK_WRITE_ENA_WIDTH 1 /* MASK_WRITE_ENA */
+
+/*
+ * R121 (0x79) - Interrupt Status 1
+ */
+#define WM8903_MICSHRT_EINT 0x8000 /* MICSHRT_EINT */
+#define WM8903_MICSHRT_EINT_MASK 0x8000 /* MICSHRT_EINT */
+#define WM8903_MICSHRT_EINT_SHIFT 15 /* MICSHRT_EINT */
+#define WM8903_MICSHRT_EINT_WIDTH 1 /* MICSHRT_EINT */
+#define WM8903_MICDET_EINT 0x4000 /* MICDET_EINT */
+#define WM8903_MICDET_EINT_MASK 0x4000 /* MICDET_EINT */
+#define WM8903_MICDET_EINT_SHIFT 14 /* MICDET_EINT */
+#define WM8903_MICDET_EINT_WIDTH 1 /* MICDET_EINT */
+#define WM8903_WSEQ_BUSY_EINT 0x2000 /* WSEQ_BUSY_EINT */
+#define WM8903_WSEQ_BUSY_EINT_MASK 0x2000 /* WSEQ_BUSY_EINT */
+#define WM8903_WSEQ_BUSY_EINT_SHIFT 13 /* WSEQ_BUSY_EINT */
+#define WM8903_WSEQ_BUSY_EINT_WIDTH 1 /* WSEQ_BUSY_EINT */
+#define WM8903_GP5_EINT 0x0010 /* GP5_EINT */
+#define WM8903_GP5_EINT_MASK 0x0010 /* GP5_EINT */
+#define WM8903_GP5_EINT_SHIFT 4 /* GP5_EINT */
+#define WM8903_GP5_EINT_WIDTH 1 /* GP5_EINT */
+#define WM8903_GP4_EINT 0x0008 /* GP4_EINT */
+#define WM8903_GP4_EINT_MASK 0x0008 /* GP4_EINT */
+#define WM8903_GP4_EINT_SHIFT 3 /* GP4_EINT */
+#define WM8903_GP4_EINT_WIDTH 1 /* GP4_EINT */
+#define WM8903_GP3_EINT 0x0004 /* GP3_EINT */
+#define WM8903_GP3_EINT_MASK 0x0004 /* GP3_EINT */
+#define WM8903_GP3_EINT_SHIFT 2 /* GP3_EINT */
+#define WM8903_GP3_EINT_WIDTH 1 /* GP3_EINT */
+#define WM8903_GP2_EINT 0x0002 /* GP2_EINT */
+#define WM8903_GP2_EINT_MASK 0x0002 /* GP2_EINT */
+#define WM8903_GP2_EINT_SHIFT 1 /* GP2_EINT */
+#define WM8903_GP2_EINT_WIDTH 1 /* GP2_EINT */
+#define WM8903_GP1_EINT 0x0001 /* GP1_EINT */
+#define WM8903_GP1_EINT_MASK 0x0001 /* GP1_EINT */
+#define WM8903_GP1_EINT_SHIFT 0 /* GP1_EINT */
+#define WM8903_GP1_EINT_WIDTH 1 /* GP1_EINT */
+
+/*
+ * R122 (0x7A) - Interrupt Status 1 Mask
+ */
+#define WM8903_IM_MICSHRT_EINT 0x8000 /* IM_MICSHRT_EINT */
+#define WM8903_IM_MICSHRT_EINT_MASK 0x8000 /* IM_MICSHRT_EINT */
+#define WM8903_IM_MICSHRT_EINT_SHIFT 15 /* IM_MICSHRT_EINT */
+#define WM8903_IM_MICSHRT_EINT_WIDTH 1 /* IM_MICSHRT_EINT */
+#define WM8903_IM_MICDET_EINT 0x4000 /* IM_MICDET_EINT */
+#define WM8903_IM_MICDET_EINT_MASK 0x4000 /* IM_MICDET_EINT */
+#define WM8903_IM_MICDET_EINT_SHIFT 14 /* IM_MICDET_EINT */
+#define WM8903_IM_MICDET_EINT_WIDTH 1 /* IM_MICDET_EINT */
+#define WM8903_IM_WSEQ_BUSY_EINT 0x2000 /* IM_WSEQ_BUSY_EINT */
+#define WM8903_IM_WSEQ_BUSY_EINT_MASK 0x2000 /* IM_WSEQ_BUSY_EINT */
+#define WM8903_IM_WSEQ_BUSY_EINT_SHIFT 13 /* IM_WSEQ_BUSY_EINT */
+#define WM8903_IM_WSEQ_BUSY_EINT_WIDTH 1 /* IM_WSEQ_BUSY_EINT */
+#define WM8903_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */
+#define WM8903_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */
+#define WM8903_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */
+#define WM8903_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */
+#define WM8903_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */
+#define WM8903_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */
+#define WM8903_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */
+#define WM8903_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */
+#define WM8903_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */
+#define WM8903_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */
+#define WM8903_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */
+#define WM8903_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */
+#define WM8903_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */
+#define WM8903_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */
+#define WM8903_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */
+#define WM8903_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */
+#define WM8903_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */
+#define WM8903_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */
+#define WM8903_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */
+#define WM8903_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */
+
+/*
+ * R123 (0x7B) - Interrupt Polarity 1
+ */
+#define WM8903_MICSHRT_INV 0x8000 /* MICSHRT_INV */
+#define WM8903_MICSHRT_INV_MASK 0x8000 /* MICSHRT_INV */
+#define WM8903_MICSHRT_INV_SHIFT 15 /* MICSHRT_INV */
+#define WM8903_MICSHRT_INV_WIDTH 1 /* MICSHRT_INV */
+#define WM8903_MICDET_INV 0x4000 /* MICDET_INV */
+#define WM8903_MICDET_INV_MASK 0x4000 /* MICDET_INV */
+#define WM8903_MICDET_INV_SHIFT 14 /* MICDET_INV */
+#define WM8903_MICDET_INV_WIDTH 1 /* MICDET_INV */
+
+/*
+ * R126 (0x7E) - Interrupt Control
+ */
+#define WM8903_IRQ_POL 0x0001 /* IRQ_POL */
+#define WM8903_IRQ_POL_MASK 0x0001 /* IRQ_POL */
+#define WM8903_IRQ_POL_SHIFT 0 /* IRQ_POL */
+#define WM8903_IRQ_POL_WIDTH 1 /* IRQ_POL */
+
+/*
+ * R164 (0xA4) - Clock Rate Test 4
+ */
+#define WM8903_ADC_DIG_MIC 0x0200 /* ADC_DIG_MIC */
+#define WM8903_ADC_DIG_MIC_MASK 0x0200 /* ADC_DIG_MIC */
+#define WM8903_ADC_DIG_MIC_SHIFT 9 /* ADC_DIG_MIC */
+#define WM8903_ADC_DIG_MIC_WIDTH 1 /* ADC_DIG_MIC */
+
+/*
+ * R172 (0xAC) - Analogue Output Bias 0
+ */
+#define WM8903_PGA_BIAS_MASK 0x0070 /* PGA_BIAS - [6:4] */
+#define WM8903_PGA_BIAS_SHIFT 4 /* PGA_BIAS - [6:4] */
+#define WM8903_PGA_BIAS_WIDTH 3 /* PGA_BIAS - [6:4] */
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8904.c b/kernel/sound/soc/codecs/wm8904.c
new file mode 100644
index 000000000..215e93c1d
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8904.c
@@ -0,0 +1,2308 @@
+/*
+ * wm8904.c -- WM8904 ALSA SoC Audio driver
+ *
+ * Copyright 2009-12 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/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/regmap.h>
+#include <linux/regulator/consumer.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 <sound/wm8904.h>
+
+#include "wm8904.h"
+
+enum wm8904_type {
+ WM8904,
+ WM8912,
+};
+
+#define WM8904_NUM_DCS_CHANNELS 4
+
+#define WM8904_NUM_SUPPLIES 5
+static const char *wm8904_supply_names[WM8904_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD",
+ "AVDD",
+ "CPVDD",
+ "MICVDD",
+};
+
+/* codec private data */
+struct wm8904_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+
+ enum wm8904_type devtype;
+
+ struct regulator_bulk_data supplies[WM8904_NUM_SUPPLIES];
+
+ struct wm8904_pdata *pdata;
+
+ int deemph;
+
+ /* Platform provided DRC configuration */
+ const char **drc_texts;
+ int drc_cfg;
+ struct soc_enum drc_enum;
+
+ /* Platform provided ReTune mobile configuration */
+ int num_retune_mobile_texts;
+ const char **retune_mobile_texts;
+ int retune_mobile_cfg;
+ struct soc_enum retune_mobile_enum;
+
+ /* FLL setup */
+ int fll_src;
+ int fll_fref;
+ int fll_fout;
+
+ /* Clocking configuration */
+ unsigned int mclk_rate;
+ int sysclk_src;
+ unsigned int sysclk_rate;
+
+ int tdm_width;
+ int tdm_slots;
+ int bclk;
+ int fs;
+
+ /* DC servo configuration - cached offset values */
+ int dcs_state[WM8904_NUM_DCS_CHANNELS];
+};
+
+static const struct reg_default wm8904_reg_defaults[] = {
+ { 4, 0x0018 }, /* R4 - Bias Control 0 */
+ { 5, 0x0000 }, /* R5 - VMID Control 0 */
+ { 6, 0x0000 }, /* R6 - Mic Bias Control 0 */
+ { 7, 0x0000 }, /* R7 - Mic Bias Control 1 */
+ { 8, 0x0001 }, /* R8 - Analogue DAC 0 */
+ { 9, 0x9696 }, /* R9 - mic Filter Control */
+ { 10, 0x0001 }, /* R10 - Analogue ADC 0 */
+ { 12, 0x0000 }, /* R12 - Power Management 0 */
+ { 14, 0x0000 }, /* R14 - Power Management 2 */
+ { 15, 0x0000 }, /* R15 - Power Management 3 */
+ { 18, 0x0000 }, /* R18 - Power Management 6 */
+ { 20, 0x945E }, /* R20 - Clock Rates 0 */
+ { 21, 0x0C05 }, /* R21 - Clock Rates 1 */
+ { 22, 0x0006 }, /* R22 - Clock Rates 2 */
+ { 24, 0x0050 }, /* R24 - Audio Interface 0 */
+ { 25, 0x000A }, /* R25 - Audio Interface 1 */
+ { 26, 0x00E4 }, /* R26 - Audio Interface 2 */
+ { 27, 0x0040 }, /* R27 - Audio Interface 3 */
+ { 30, 0x00C0 }, /* R30 - DAC Digital Volume Left */
+ { 31, 0x00C0 }, /* R31 - DAC Digital Volume Right */
+ { 32, 0x0000 }, /* R32 - DAC Digital 0 */
+ { 33, 0x0008 }, /* R33 - DAC Digital 1 */
+ { 36, 0x00C0 }, /* R36 - ADC Digital Volume Left */
+ { 37, 0x00C0 }, /* R37 - ADC Digital Volume Right */
+ { 38, 0x0010 }, /* R38 - ADC Digital 0 */
+ { 39, 0x0000 }, /* R39 - Digital Microphone 0 */
+ { 40, 0x01AF }, /* R40 - DRC 0 */
+ { 41, 0x3248 }, /* R41 - DRC 1 */
+ { 42, 0x0000 }, /* R42 - DRC 2 */
+ { 43, 0x0000 }, /* R43 - DRC 3 */
+ { 44, 0x0085 }, /* R44 - Analogue Left Input 0 */
+ { 45, 0x0085 }, /* R45 - Analogue Right Input 0 */
+ { 46, 0x0044 }, /* R46 - Analogue Left Input 1 */
+ { 47, 0x0044 }, /* R47 - Analogue Right Input 1 */
+ { 57, 0x002D }, /* R57 - Analogue OUT1 Left */
+ { 58, 0x002D }, /* R58 - Analogue OUT1 Right */
+ { 59, 0x0039 }, /* R59 - Analogue OUT2 Left */
+ { 60, 0x0039 }, /* R60 - Analogue OUT2 Right */
+ { 61, 0x0000 }, /* R61 - Analogue OUT12 ZC */
+ { 67, 0x0000 }, /* R67 - DC Servo 0 */
+ { 69, 0xAAAA }, /* R69 - DC Servo 2 */
+ { 71, 0xAAAA }, /* R71 - DC Servo 4 */
+ { 72, 0xAAAA }, /* R72 - DC Servo 5 */
+ { 90, 0x0000 }, /* R90 - Analogue HP 0 */
+ { 94, 0x0000 }, /* R94 - Analogue Lineout 0 */
+ { 98, 0x0000 }, /* R98 - Charge Pump 0 */
+ { 104, 0x0004 }, /* R104 - Class W 0 */
+ { 108, 0x0000 }, /* R108 - Write Sequencer 0 */
+ { 109, 0x0000 }, /* R109 - Write Sequencer 1 */
+ { 110, 0x0000 }, /* R110 - Write Sequencer 2 */
+ { 111, 0x0000 }, /* R111 - Write Sequencer 3 */
+ { 112, 0x0000 }, /* R112 - Write Sequencer 4 */
+ { 116, 0x0000 }, /* R116 - FLL Control 1 */
+ { 117, 0x0007 }, /* R117 - FLL Control 2 */
+ { 118, 0x0000 }, /* R118 - FLL Control 3 */
+ { 119, 0x2EE0 }, /* R119 - FLL Control 4 */
+ { 120, 0x0004 }, /* R120 - FLL Control 5 */
+ { 121, 0x0014 }, /* R121 - GPIO Control 1 */
+ { 122, 0x0010 }, /* R122 - GPIO Control 2 */
+ { 123, 0x0010 }, /* R123 - GPIO Control 3 */
+ { 124, 0x0000 }, /* R124 - GPIO Control 4 */
+ { 126, 0x0000 }, /* R126 - Digital Pulls */
+ { 128, 0xFFFF }, /* R128 - Interrupt Status Mask */
+ { 129, 0x0000 }, /* R129 - Interrupt Polarity */
+ { 130, 0x0000 }, /* R130 - Interrupt Debounce */
+ { 134, 0x0000 }, /* R134 - EQ1 */
+ { 135, 0x000C }, /* R135 - EQ2 */
+ { 136, 0x000C }, /* R136 - EQ3 */
+ { 137, 0x000C }, /* R137 - EQ4 */
+ { 138, 0x000C }, /* R138 - EQ5 */
+ { 139, 0x000C }, /* R139 - EQ6 */
+ { 140, 0x0FCA }, /* R140 - EQ7 */
+ { 141, 0x0400 }, /* R141 - EQ8 */
+ { 142, 0x00D8 }, /* R142 - EQ9 */
+ { 143, 0x1EB5 }, /* R143 - EQ10 */
+ { 144, 0xF145 }, /* R144 - EQ11 */
+ { 145, 0x0B75 }, /* R145 - EQ12 */
+ { 146, 0x01C5 }, /* R146 - EQ13 */
+ { 147, 0x1C58 }, /* R147 - EQ14 */
+ { 148, 0xF373 }, /* R148 - EQ15 */
+ { 149, 0x0A54 }, /* R149 - EQ16 */
+ { 150, 0x0558 }, /* R150 - EQ17 */
+ { 151, 0x168E }, /* R151 - EQ18 */
+ { 152, 0xF829 }, /* R152 - EQ19 */
+ { 153, 0x07AD }, /* R153 - EQ20 */
+ { 154, 0x1103 }, /* R154 - EQ21 */
+ { 155, 0x0564 }, /* R155 - EQ22 */
+ { 156, 0x0559 }, /* R156 - EQ23 */
+ { 157, 0x4000 }, /* R157 - EQ24 */
+ { 161, 0x0000 }, /* R161 - Control Interface Test 1 */
+ { 204, 0x0000 }, /* R204 - Analogue Output Bias 0 */
+ { 247, 0x0000 }, /* R247 - FLL NCO Test 0 */
+ { 248, 0x0019 }, /* R248 - FLL NCO Test 1 */
+};
+
+static bool wm8904_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8904_SW_RESET_AND_ID:
+ case WM8904_REVISION:
+ case WM8904_DC_SERVO_1:
+ case WM8904_DC_SERVO_6:
+ case WM8904_DC_SERVO_7:
+ case WM8904_DC_SERVO_8:
+ case WM8904_DC_SERVO_9:
+ case WM8904_DC_SERVO_READBACK_0:
+ case WM8904_INTERRUPT_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wm8904_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8904_SW_RESET_AND_ID:
+ case WM8904_REVISION:
+ case WM8904_BIAS_CONTROL_0:
+ case WM8904_VMID_CONTROL_0:
+ case WM8904_MIC_BIAS_CONTROL_0:
+ case WM8904_MIC_BIAS_CONTROL_1:
+ case WM8904_ANALOGUE_DAC_0:
+ case WM8904_MIC_FILTER_CONTROL:
+ case WM8904_ANALOGUE_ADC_0:
+ case WM8904_POWER_MANAGEMENT_0:
+ case WM8904_POWER_MANAGEMENT_2:
+ case WM8904_POWER_MANAGEMENT_3:
+ case WM8904_POWER_MANAGEMENT_6:
+ case WM8904_CLOCK_RATES_0:
+ case WM8904_CLOCK_RATES_1:
+ case WM8904_CLOCK_RATES_2:
+ case WM8904_AUDIO_INTERFACE_0:
+ case WM8904_AUDIO_INTERFACE_1:
+ case WM8904_AUDIO_INTERFACE_2:
+ case WM8904_AUDIO_INTERFACE_3:
+ case WM8904_DAC_DIGITAL_VOLUME_LEFT:
+ case WM8904_DAC_DIGITAL_VOLUME_RIGHT:
+ case WM8904_DAC_DIGITAL_0:
+ case WM8904_DAC_DIGITAL_1:
+ case WM8904_ADC_DIGITAL_VOLUME_LEFT:
+ case WM8904_ADC_DIGITAL_VOLUME_RIGHT:
+ case WM8904_ADC_DIGITAL_0:
+ case WM8904_DIGITAL_MICROPHONE_0:
+ case WM8904_DRC_0:
+ case WM8904_DRC_1:
+ case WM8904_DRC_2:
+ case WM8904_DRC_3:
+ case WM8904_ANALOGUE_LEFT_INPUT_0:
+ case WM8904_ANALOGUE_RIGHT_INPUT_0:
+ case WM8904_ANALOGUE_LEFT_INPUT_1:
+ case WM8904_ANALOGUE_RIGHT_INPUT_1:
+ case WM8904_ANALOGUE_OUT1_LEFT:
+ case WM8904_ANALOGUE_OUT1_RIGHT:
+ case WM8904_ANALOGUE_OUT2_LEFT:
+ case WM8904_ANALOGUE_OUT2_RIGHT:
+ case WM8904_ANALOGUE_OUT12_ZC:
+ case WM8904_DC_SERVO_0:
+ case WM8904_DC_SERVO_1:
+ case WM8904_DC_SERVO_2:
+ case WM8904_DC_SERVO_4:
+ case WM8904_DC_SERVO_5:
+ case WM8904_DC_SERVO_6:
+ case WM8904_DC_SERVO_7:
+ case WM8904_DC_SERVO_8:
+ case WM8904_DC_SERVO_9:
+ case WM8904_DC_SERVO_READBACK_0:
+ case WM8904_ANALOGUE_HP_0:
+ case WM8904_ANALOGUE_LINEOUT_0:
+ case WM8904_CHARGE_PUMP_0:
+ case WM8904_CLASS_W_0:
+ case WM8904_WRITE_SEQUENCER_0:
+ case WM8904_WRITE_SEQUENCER_1:
+ case WM8904_WRITE_SEQUENCER_2:
+ case WM8904_WRITE_SEQUENCER_3:
+ case WM8904_WRITE_SEQUENCER_4:
+ case WM8904_FLL_CONTROL_1:
+ case WM8904_FLL_CONTROL_2:
+ case WM8904_FLL_CONTROL_3:
+ case WM8904_FLL_CONTROL_4:
+ case WM8904_FLL_CONTROL_5:
+ case WM8904_GPIO_CONTROL_1:
+ case WM8904_GPIO_CONTROL_2:
+ case WM8904_GPIO_CONTROL_3:
+ case WM8904_GPIO_CONTROL_4:
+ case WM8904_DIGITAL_PULLS:
+ case WM8904_INTERRUPT_STATUS:
+ case WM8904_INTERRUPT_STATUS_MASK:
+ case WM8904_INTERRUPT_POLARITY:
+ case WM8904_INTERRUPT_DEBOUNCE:
+ case WM8904_EQ1:
+ case WM8904_EQ2:
+ case WM8904_EQ3:
+ case WM8904_EQ4:
+ case WM8904_EQ5:
+ case WM8904_EQ6:
+ case WM8904_EQ7:
+ case WM8904_EQ8:
+ case WM8904_EQ9:
+ case WM8904_EQ10:
+ case WM8904_EQ11:
+ case WM8904_EQ12:
+ case WM8904_EQ13:
+ case WM8904_EQ14:
+ case WM8904_EQ15:
+ case WM8904_EQ16:
+ case WM8904_EQ17:
+ case WM8904_EQ18:
+ case WM8904_EQ19:
+ case WM8904_EQ20:
+ case WM8904_EQ21:
+ case WM8904_EQ22:
+ case WM8904_EQ23:
+ case WM8904_EQ24:
+ case WM8904_CONTROL_INTERFACE_TEST_1:
+ case WM8904_ADC_TEST_0:
+ case WM8904_ANALOGUE_OUTPUT_BIAS_0:
+ case WM8904_FLL_NCO_TEST_0:
+ case WM8904_FLL_NCO_TEST_1:
+ return true;
+ default:
+ return true;
+ }
+}
+
+static int wm8904_configure_clocking(struct snd_soc_codec *codec)
+{
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ unsigned int clock0, clock2, rate;
+
+ /* Gate the clock while we're updating to avoid misclocking */
+ clock2 = snd_soc_read(codec, WM8904_CLOCK_RATES_2);
+ snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+ WM8904_SYSCLK_SRC, 0);
+
+ /* This should be done on init() for bypass paths */
+ switch (wm8904->sysclk_src) {
+ case WM8904_CLK_MCLK:
+ dev_dbg(codec->dev, "Using %dHz MCLK\n", wm8904->mclk_rate);
+
+ clock2 &= ~WM8904_SYSCLK_SRC;
+ rate = wm8904->mclk_rate;
+
+ /* Ensure the FLL is stopped */
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+ break;
+
+ case WM8904_CLK_FLL:
+ dev_dbg(codec->dev, "Using %dHz FLL clock\n",
+ wm8904->fll_fout);
+
+ clock2 |= WM8904_SYSCLK_SRC;
+ rate = wm8904->fll_fout;
+ break;
+
+ default:
+ dev_err(codec->dev, "System clock not configured\n");
+ return -EINVAL;
+ }
+
+ /* SYSCLK shouldn't be over 13.5MHz */
+ if (rate > 13500000) {
+ clock0 = WM8904_MCLK_DIV;
+ wm8904->sysclk_rate = rate / 2;
+ } else {
+ clock0 = 0;
+ wm8904->sysclk_rate = rate;
+ }
+
+ snd_soc_update_bits(codec, WM8904_CLOCK_RATES_0, WM8904_MCLK_DIV,
+ clock0);
+
+ snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+ WM8904_CLK_SYS_ENA | WM8904_SYSCLK_SRC, clock2);
+
+ dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm8904->sysclk_rate);
+
+ return 0;
+}
+
+static void wm8904_set_drc(struct snd_soc_codec *codec)
+{
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_pdata *pdata = wm8904->pdata;
+ int save, i;
+
+ /* Save any enables; the configuration should clear them. */
+ save = snd_soc_read(codec, WM8904_DRC_0);
+
+ for (i = 0; i < WM8904_DRC_REGS; i++)
+ snd_soc_update_bits(codec, WM8904_DRC_0 + i, 0xffff,
+ pdata->drc_cfgs[wm8904->drc_cfg].regs[i]);
+
+ /* Reenable the DRC */
+ snd_soc_update_bits(codec, WM8904_DRC_0,
+ WM8904_DRC_ENA | WM8904_DRC_DAC_PATH, save);
+}
+
+static int wm8904_put_drc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_pdata *pdata = wm8904->pdata;
+ int value = ucontrol->value.integer.value[0];
+
+ if (value >= pdata->num_drc_cfgs)
+ return -EINVAL;
+
+ wm8904->drc_cfg = value;
+
+ wm8904_set_drc(codec);
+
+ return 0;
+}
+
+static int wm8904_get_drc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8904->drc_cfg;
+
+ return 0;
+}
+
+static void wm8904_set_retune_mobile(struct snd_soc_codec *codec)
+{
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_pdata *pdata = wm8904->pdata;
+ int best, best_val, save, i, cfg;
+
+ if (!pdata || !wm8904->num_retune_mobile_texts)
+ return;
+
+ /* Find the version of the currently selected configuration
+ * with the nearest sample rate. */
+ cfg = wm8904->retune_mobile_cfg;
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8904->retune_mobile_texts[cfg]) == 0 &&
+ abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8904->fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8904->fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "ReTune Mobile %s/%dHz for %dHz sample rate\n",
+ pdata->retune_mobile_cfgs[best].name,
+ pdata->retune_mobile_cfgs[best].rate,
+ wm8904->fs);
+
+ /* The EQ will be disabled while reconfiguring it, remember the
+ * current configuration.
+ */
+ save = snd_soc_read(codec, WM8904_EQ1);
+
+ for (i = 0; i < WM8904_EQ_REGS; i++)
+ snd_soc_update_bits(codec, WM8904_EQ1 + i, 0xffff,
+ pdata->retune_mobile_cfgs[best].regs[i]);
+
+ snd_soc_update_bits(codec, WM8904_EQ1, WM8904_EQ_ENA, save);
+}
+
+static int wm8904_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_pdata *pdata = wm8904->pdata;
+ int value = ucontrol->value.integer.value[0];
+
+ if (value >= pdata->num_retune_mobile_cfgs)
+ return -EINVAL;
+
+ wm8904->retune_mobile_cfg = value;
+
+ wm8904_set_retune_mobile(codec);
+
+ return 0;
+}
+
+static int wm8904_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8904->retune_mobile_cfg;
+
+ return 0;
+}
+
+static int deemph_settings[] = { 0, 32000, 44100, 48000 };
+
+static int wm8904_set_deemph(struct snd_soc_codec *codec)
+{
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ int val, i, best;
+
+ /* If we're using deemphasis select the nearest available sample
+ * rate.
+ */
+ if (wm8904->deemph) {
+ best = 1;
+ for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
+ if (abs(deemph_settings[i] - wm8904->fs) <
+ abs(deemph_settings[best] - wm8904->fs))
+ best = i;
+ }
+
+ val = best << WM8904_DEEMPH_SHIFT;
+ } else {
+ val = 0;
+ }
+
+ dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+
+ return snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_1,
+ WM8904_DEEMPH_MASK, val);
+}
+
+static int wm8904_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8904->deemph;
+ return 0;
+}
+
+static int wm8904_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ int deemph = ucontrol->value.integer.value[0];
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ wm8904->deemph = deemph;
+
+ return wm8904_set_deemph(codec);
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+
+static const char *input_mode_text[] = {
+ "Single-Ended", "Differential Line", "Differential Mic"
+};
+
+static SOC_ENUM_SINGLE_DECL(lin_mode,
+ WM8904_ANALOGUE_LEFT_INPUT_1, 0,
+ input_mode_text);
+
+static SOC_ENUM_SINGLE_DECL(rin_mode,
+ WM8904_ANALOGUE_RIGHT_INPUT_1, 0,
+ input_mode_text);
+
+static const char *hpf_mode_text[] = {
+ "Hi-fi", "Voice 1", "Voice 2", "Voice 3"
+};
+
+static SOC_ENUM_SINGLE_DECL(hpf_mode, WM8904_ADC_DIGITAL_0, 5,
+ hpf_mode_text);
+
+static int wm8904_adc_osr_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int val;
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ if (ucontrol->value.integer.value[0])
+ val = 0;
+ else
+ val = WM8904_ADC_128_OSR_TST_MODE | WM8904_ADC_BIASX1P5;
+
+ snd_soc_update_bits(codec, WM8904_ADC_TEST_0,
+ WM8904_ADC_128_OSR_TST_MODE | WM8904_ADC_BIASX1P5,
+ val);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new wm8904_adc_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8904_ADC_DIGITAL_VOLUME_LEFT,
+ WM8904_ADC_DIGITAL_VOLUME_RIGHT, 1, 119, 0, digital_tlv),
+
+SOC_ENUM("Left Caputure Mode", lin_mode),
+SOC_ENUM("Right Capture Mode", rin_mode),
+
+/* No TLV since it depends on mode */
+SOC_DOUBLE_R("Capture Volume", WM8904_ANALOGUE_LEFT_INPUT_0,
+ WM8904_ANALOGUE_RIGHT_INPUT_0, 0, 31, 0),
+SOC_DOUBLE_R("Capture Switch", WM8904_ANALOGUE_LEFT_INPUT_0,
+ WM8904_ANALOGUE_RIGHT_INPUT_0, 7, 1, 1),
+
+SOC_SINGLE("High Pass Filter Switch", WM8904_ADC_DIGITAL_0, 4, 1, 0),
+SOC_ENUM("High Pass Filter Mode", hpf_mode),
+SOC_SINGLE_EXT("ADC 128x OSR Switch", WM8904_ANALOGUE_ADC_0, 0, 1, 0,
+ snd_soc_get_volsw, wm8904_adc_osr_put),
+};
+
+static const char *drc_path_text[] = {
+ "ADC", "DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_path, WM8904_DRC_0, 14, drc_path_text);
+
+static const struct snd_kcontrol_new wm8904_dac_snd_controls[] = {
+SOC_SINGLE_TLV("Digital Playback Boost Volume",
+ WM8904_AUDIO_INTERFACE_0, 9, 3, 0, dac_boost_tlv),
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8904_DAC_DIGITAL_VOLUME_LEFT,
+ WM8904_DAC_DIGITAL_VOLUME_RIGHT, 1, 96, 0, digital_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Volume", WM8904_ANALOGUE_OUT1_LEFT,
+ WM8904_ANALOGUE_OUT1_RIGHT, 0, 63, 0, out_tlv),
+SOC_DOUBLE_R("Headphone Switch", WM8904_ANALOGUE_OUT1_LEFT,
+ WM8904_ANALOGUE_OUT1_RIGHT, 8, 1, 1),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8904_ANALOGUE_OUT1_LEFT,
+ WM8904_ANALOGUE_OUT1_RIGHT, 6, 1, 0),
+
+SOC_DOUBLE_R_TLV("Line Output Volume", WM8904_ANALOGUE_OUT2_LEFT,
+ WM8904_ANALOGUE_OUT2_RIGHT, 0, 63, 0, out_tlv),
+SOC_DOUBLE_R("Line Output Switch", WM8904_ANALOGUE_OUT2_LEFT,
+ WM8904_ANALOGUE_OUT2_RIGHT, 8, 1, 1),
+SOC_DOUBLE_R("Line Output ZC Switch", WM8904_ANALOGUE_OUT2_LEFT,
+ WM8904_ANALOGUE_OUT2_RIGHT, 6, 1, 0),
+
+SOC_SINGLE("EQ Switch", WM8904_EQ1, 0, 1, 0),
+SOC_SINGLE("DRC Switch", WM8904_DRC_0, 15, 1, 0),
+SOC_ENUM("DRC Path", drc_path),
+SOC_SINGLE("DAC OSRx2 Switch", WM8904_DAC_DIGITAL_1, 6, 1, 0),
+SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
+ wm8904_get_deemph, wm8904_put_deemph),
+};
+
+static const struct snd_kcontrol_new wm8904_snd_controls[] = {
+SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8904_DAC_DIGITAL_0, 4, 8, 15, 0,
+ sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new wm8904_eq_controls[] = {
+SOC_SINGLE_TLV("EQ1 Volume", WM8904_EQ2, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Volume", WM8904_EQ3, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Volume", WM8904_EQ4, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Volume", WM8904_EQ5, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ5 Volume", WM8904_EQ6, 0, 24, 0, eq_tlv),
+};
+
+static int cp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (WARN_ON(event != SND_SOC_DAPM_POST_PMU))
+ return -EINVAL;
+
+ /* Maximum startup time */
+ udelay(500);
+
+ return 0;
+}
+
+static int sysclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* If we're using the FLL then we only start it when
+ * required; we assume that the configuration has been
+ * done previously and all we need to do is kick it
+ * off.
+ */
+ switch (wm8904->sysclk_src) {
+ case WM8904_CLK_FLL:
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ WM8904_FLL_OSC_ENA,
+ WM8904_FLL_OSC_ENA);
+
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ WM8904_FLL_ENA,
+ WM8904_FLL_ENA);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int out_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ int reg, val;
+ int dcs_mask;
+ int dcs_l, dcs_r;
+ int dcs_l_reg, dcs_r_reg;
+ int timeout;
+ int pwr_reg;
+
+ /* This code is shared between HP and LINEOUT; we do all our
+ * power management in stereo pairs to avoid latency issues so
+ * we reuse shift to identify which rather than strcmp() the
+ * name. */
+ reg = w->shift;
+
+ switch (reg) {
+ case WM8904_ANALOGUE_HP_0:
+ pwr_reg = WM8904_POWER_MANAGEMENT_2;
+ dcs_mask = WM8904_DCS_ENA_CHAN_0 | WM8904_DCS_ENA_CHAN_1;
+ dcs_r_reg = WM8904_DC_SERVO_8;
+ dcs_l_reg = WM8904_DC_SERVO_9;
+ dcs_l = 0;
+ dcs_r = 1;
+ break;
+ case WM8904_ANALOGUE_LINEOUT_0:
+ pwr_reg = WM8904_POWER_MANAGEMENT_3;
+ dcs_mask = WM8904_DCS_ENA_CHAN_2 | WM8904_DCS_ENA_CHAN_3;
+ dcs_r_reg = WM8904_DC_SERVO_6;
+ dcs_l_reg = WM8904_DC_SERVO_7;
+ dcs_l = 2;
+ dcs_r = 3;
+ break;
+ default:
+ WARN(1, "Invalid reg %d\n", reg);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Power on the PGAs */
+ snd_soc_update_bits(codec, pwr_reg,
+ WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA,
+ WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA);
+
+ /* Power on the amplifier */
+ snd_soc_update_bits(codec, reg,
+ WM8904_HPL_ENA | WM8904_HPR_ENA,
+ WM8904_HPL_ENA | WM8904_HPR_ENA);
+
+
+ /* Enable the first stage */
+ snd_soc_update_bits(codec, reg,
+ WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY,
+ WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY);
+
+ /* Power up the DC servo */
+ snd_soc_update_bits(codec, WM8904_DC_SERVO_0,
+ dcs_mask, dcs_mask);
+
+ /* Either calibrate the DC servo or restore cached state
+ * if we have that.
+ */
+ if (wm8904->dcs_state[dcs_l] || wm8904->dcs_state[dcs_r]) {
+ dev_dbg(codec->dev, "Restoring DC servo state\n");
+
+ snd_soc_write(codec, dcs_l_reg,
+ wm8904->dcs_state[dcs_l]);
+ snd_soc_write(codec, dcs_r_reg,
+ wm8904->dcs_state[dcs_r]);
+
+ snd_soc_write(codec, WM8904_DC_SERVO_1, dcs_mask);
+
+ timeout = 20;
+ } else {
+ dev_dbg(codec->dev, "Calibrating DC servo\n");
+
+ snd_soc_write(codec, WM8904_DC_SERVO_1,
+ dcs_mask << WM8904_DCS_TRIG_STARTUP_0_SHIFT);
+
+ timeout = 500;
+ }
+
+ /* Wait for DC servo to complete */
+ dcs_mask <<= WM8904_DCS_CAL_COMPLETE_SHIFT;
+ do {
+ val = snd_soc_read(codec, WM8904_DC_SERVO_READBACK_0);
+ if ((val & dcs_mask) == dcs_mask)
+ break;
+
+ msleep(1);
+ } while (--timeout);
+
+ if ((val & dcs_mask) != dcs_mask)
+ dev_warn(codec->dev, "DC servo timed out\n");
+ else
+ dev_dbg(codec->dev, "DC servo ready\n");
+
+ /* Enable the output stage */
+ snd_soc_update_bits(codec, reg,
+ WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP,
+ WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ /* Unshort the output itself */
+ snd_soc_update_bits(codec, reg,
+ WM8904_HPL_RMV_SHORT |
+ WM8904_HPR_RMV_SHORT,
+ WM8904_HPL_RMV_SHORT |
+ WM8904_HPR_RMV_SHORT);
+
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Short the output */
+ snd_soc_update_bits(codec, reg,
+ WM8904_HPL_RMV_SHORT |
+ WM8904_HPR_RMV_SHORT, 0);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ /* Cache the DC servo configuration; this will be
+ * invalidated if we change the configuration. */
+ wm8904->dcs_state[dcs_l] = snd_soc_read(codec, dcs_l_reg);
+ wm8904->dcs_state[dcs_r] = snd_soc_read(codec, dcs_r_reg);
+
+ snd_soc_update_bits(codec, WM8904_DC_SERVO_0,
+ dcs_mask, 0);
+
+ /* Disable the amplifier input and output stages */
+ snd_soc_update_bits(codec, reg,
+ WM8904_HPL_ENA | WM8904_HPR_ENA |
+ WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY |
+ WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP,
+ 0);
+
+ /* PGAs too */
+ snd_soc_update_bits(codec, pwr_reg,
+ WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA,
+ 0);
+ break;
+ }
+
+ return 0;
+}
+
+static const char *lin_text[] = {
+ "IN1L", "IN2L", "IN3L"
+};
+
+static SOC_ENUM_SINGLE_DECL(lin_enum, WM8904_ANALOGUE_LEFT_INPUT_1, 2,
+ lin_text);
+
+static const struct snd_kcontrol_new lin_mux =
+ SOC_DAPM_ENUM("Left Capture Mux", lin_enum);
+
+static SOC_ENUM_SINGLE_DECL(lin_inv_enum, WM8904_ANALOGUE_LEFT_INPUT_1, 4,
+ lin_text);
+
+static const struct snd_kcontrol_new lin_inv_mux =
+ SOC_DAPM_ENUM("Left Capture Inveting Mux", lin_inv_enum);
+
+static const char *rin_text[] = {
+ "IN1R", "IN2R", "IN3R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rin_enum, WM8904_ANALOGUE_RIGHT_INPUT_1, 2,
+ rin_text);
+
+static const struct snd_kcontrol_new rin_mux =
+ SOC_DAPM_ENUM("Right Capture Mux", rin_enum);
+
+static SOC_ENUM_SINGLE_DECL(rin_inv_enum, WM8904_ANALOGUE_RIGHT_INPUT_1, 4,
+ rin_text);
+
+static const struct snd_kcontrol_new rin_inv_mux =
+ SOC_DAPM_ENUM("Right Capture Inveting Mux", rin_inv_enum);
+
+static const char *aif_text[] = {
+ "Left", "Right"
+};
+
+static SOC_ENUM_SINGLE_DECL(aifoutl_enum, WM8904_AUDIO_INTERFACE_0, 7,
+ aif_text);
+
+static const struct snd_kcontrol_new aifoutl_mux =
+ SOC_DAPM_ENUM("AIFOUTL Mux", aifoutl_enum);
+
+static SOC_ENUM_SINGLE_DECL(aifoutr_enum, WM8904_AUDIO_INTERFACE_0, 6,
+ aif_text);
+
+static const struct snd_kcontrol_new aifoutr_mux =
+ SOC_DAPM_ENUM("AIFOUTR Mux", aifoutr_enum);
+
+static SOC_ENUM_SINGLE_DECL(aifinl_enum, WM8904_AUDIO_INTERFACE_0, 5,
+ aif_text);
+
+static const struct snd_kcontrol_new aifinl_mux =
+ SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum);
+
+static SOC_ENUM_SINGLE_DECL(aifinr_enum, WM8904_AUDIO_INTERFACE_0, 4,
+ aif_text);
+
+static const struct snd_kcontrol_new aifinr_mux =
+ SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum);
+
+static const struct snd_soc_dapm_widget wm8904_core_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", WM8904_CLOCK_RATES_2, 2, 0, sysclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8904_CLOCK_RATES_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("TOCLK", WM8904_CLOCK_RATES_2, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8904_adc_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+SND_SOC_DAPM_INPUT("IN3L"),
+SND_SOC_DAPM_INPUT("IN3R"),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS", WM8904_MIC_BIAS_CONTROL_0, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lin_mux),
+SND_SOC_DAPM_MUX("Left Capture Inverting Mux", SND_SOC_NOPM, 0, 0,
+ &lin_inv_mux),
+SND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rin_mux),
+SND_SOC_DAPM_MUX("Right Capture Inverting Mux", SND_SOC_NOPM, 0, 0,
+ &rin_inv_mux),
+
+SND_SOC_DAPM_PGA("Left Capture PGA", WM8904_POWER_MANAGEMENT_0, 1, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("Right Capture PGA", WM8904_POWER_MANAGEMENT_0, 0, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8904_POWER_MANAGEMENT_6, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8904_POWER_MANAGEMENT_6, 0, 0),
+
+SND_SOC_DAPM_MUX("AIFOUTL Mux", SND_SOC_NOPM, 0, 0, &aifoutl_mux),
+SND_SOC_DAPM_MUX("AIFOUTR Mux", SND_SOC_NOPM, 0, 0, &aifoutr_mux),
+
+SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8904_dac_dapm_widgets[] = {
+SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &aifinl_mux),
+SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &aifinr_mux),
+
+SND_SOC_DAPM_DAC("DACL", NULL, WM8904_POWER_MANAGEMENT_6, 3, 0),
+SND_SOC_DAPM_DAC("DACR", NULL, WM8904_POWER_MANAGEMENT_6, 2, 0),
+
+SND_SOC_DAPM_SUPPLY("Charge pump", WM8904_CHARGE_PUMP_0, 0, 0, cp_event,
+ SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("HPL PGA", SND_SOC_NOPM, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("HPR PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("LINEL PGA", SND_SOC_NOPM, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LINER PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM, WM8904_ANALOGUE_HP_0,
+ 0, NULL, 0, out_pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_PGA_E("Line Output", SND_SOC_NOPM, WM8904_ANALOGUE_LINEOUT_0,
+ 0, NULL, 0, out_pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+};
+
+static const char *out_mux_text[] = {
+ "DAC", "Bypass"
+};
+
+static SOC_ENUM_SINGLE_DECL(hpl_enum, WM8904_ANALOGUE_OUT12_ZC, 3,
+ out_mux_text);
+
+static const struct snd_kcontrol_new hpl_mux =
+ SOC_DAPM_ENUM("HPL Mux", hpl_enum);
+
+static SOC_ENUM_SINGLE_DECL(hpr_enum, WM8904_ANALOGUE_OUT12_ZC, 2,
+ out_mux_text);
+
+static const struct snd_kcontrol_new hpr_mux =
+ SOC_DAPM_ENUM("HPR Mux", hpr_enum);
+
+static SOC_ENUM_SINGLE_DECL(linel_enum, WM8904_ANALOGUE_OUT12_ZC, 1,
+ out_mux_text);
+
+static const struct snd_kcontrol_new linel_mux =
+ SOC_DAPM_ENUM("LINEL Mux", linel_enum);
+
+static SOC_ENUM_SINGLE_DECL(liner_enum, WM8904_ANALOGUE_OUT12_ZC, 0,
+ out_mux_text);
+
+static const struct snd_kcontrol_new liner_mux =
+ SOC_DAPM_ENUM("LINER Mux", liner_enum);
+
+static const char *sidetone_text[] = {
+ "None", "Left", "Right"
+};
+
+static SOC_ENUM_SINGLE_DECL(dacl_sidetone_enum, WM8904_DAC_DIGITAL_0, 2,
+ sidetone_text);
+
+static const struct snd_kcontrol_new dacl_sidetone_mux =
+ SOC_DAPM_ENUM("Left Sidetone Mux", dacl_sidetone_enum);
+
+static SOC_ENUM_SINGLE_DECL(dacr_sidetone_enum, WM8904_DAC_DIGITAL_0, 0,
+ sidetone_text);
+
+static const struct snd_kcontrol_new dacr_sidetone_mux =
+ SOC_DAPM_ENUM("Right Sidetone Mux", dacr_sidetone_enum);
+
+static const struct snd_soc_dapm_widget wm8904_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("Class G", WM8904_CLASS_W_0, 0, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Left Bypass", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Bypass", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &dacl_sidetone_mux),
+SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &dacr_sidetone_mux),
+
+SND_SOC_DAPM_MUX("HPL Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
+SND_SOC_DAPM_MUX("HPR Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+SND_SOC_DAPM_MUX("LINEL Mux", SND_SOC_NOPM, 0, 0, &linel_mux),
+SND_SOC_DAPM_MUX("LINER Mux", SND_SOC_NOPM, 0, 0, &liner_mux),
+};
+
+static const struct snd_soc_dapm_route core_intercon[] = {
+ { "CLK_DSP", NULL, "SYSCLK" },
+ { "TOCLK", NULL, "SYSCLK" },
+};
+
+static const struct snd_soc_dapm_route adc_intercon[] = {
+ { "Left Capture Mux", "IN1L", "IN1L" },
+ { "Left Capture Mux", "IN2L", "IN2L" },
+ { "Left Capture Mux", "IN3L", "IN3L" },
+
+ { "Left Capture Inverting Mux", "IN1L", "IN1L" },
+ { "Left Capture Inverting Mux", "IN2L", "IN2L" },
+ { "Left Capture Inverting Mux", "IN3L", "IN3L" },
+
+ { "Right Capture Mux", "IN1R", "IN1R" },
+ { "Right Capture Mux", "IN2R", "IN2R" },
+ { "Right Capture Mux", "IN3R", "IN3R" },
+
+ { "Right Capture Inverting Mux", "IN1R", "IN1R" },
+ { "Right Capture Inverting Mux", "IN2R", "IN2R" },
+ { "Right Capture Inverting Mux", "IN3R", "IN3R" },
+
+ { "Left Capture PGA", NULL, "Left Capture Mux" },
+ { "Left Capture PGA", NULL, "Left Capture Inverting Mux" },
+
+ { "Right Capture PGA", NULL, "Right Capture Mux" },
+ { "Right Capture PGA", NULL, "Right Capture Inverting Mux" },
+
+ { "AIFOUTL Mux", "Left", "ADCL" },
+ { "AIFOUTL Mux", "Right", "ADCR" },
+ { "AIFOUTR Mux", "Left", "ADCL" },
+ { "AIFOUTR Mux", "Right", "ADCR" },
+
+ { "AIFOUTL", NULL, "AIFOUTL Mux" },
+ { "AIFOUTR", NULL, "AIFOUTR Mux" },
+
+ { "ADCL", NULL, "CLK_DSP" },
+ { "ADCL", NULL, "Left Capture PGA" },
+
+ { "ADCR", NULL, "CLK_DSP" },
+ { "ADCR", NULL, "Right Capture PGA" },
+};
+
+static const struct snd_soc_dapm_route dac_intercon[] = {
+ { "DACL Mux", "Left", "AIFINL" },
+ { "DACL Mux", "Right", "AIFINR" },
+
+ { "DACR Mux", "Left", "AIFINL" },
+ { "DACR Mux", "Right", "AIFINR" },
+
+ { "DACL", NULL, "DACL Mux" },
+ { "DACL", NULL, "CLK_DSP" },
+
+ { "DACR", NULL, "DACR Mux" },
+ { "DACR", NULL, "CLK_DSP" },
+
+ { "Charge pump", NULL, "SYSCLK" },
+
+ { "Headphone Output", NULL, "HPL PGA" },
+ { "Headphone Output", NULL, "HPR PGA" },
+ { "Headphone Output", NULL, "Charge pump" },
+ { "Headphone Output", NULL, "TOCLK" },
+
+ { "Line Output", NULL, "LINEL PGA" },
+ { "Line Output", NULL, "LINER PGA" },
+ { "Line Output", NULL, "Charge pump" },
+ { "Line Output", NULL, "TOCLK" },
+
+ { "HPOUTL", NULL, "Headphone Output" },
+ { "HPOUTR", NULL, "Headphone Output" },
+
+ { "LINEOUTL", NULL, "Line Output" },
+ { "LINEOUTR", NULL, "Line Output" },
+};
+
+static const struct snd_soc_dapm_route wm8904_intercon[] = {
+ { "Left Sidetone", "Left", "ADCL" },
+ { "Left Sidetone", "Right", "ADCR" },
+ { "DACL", NULL, "Left Sidetone" },
+
+ { "Right Sidetone", "Left", "ADCL" },
+ { "Right Sidetone", "Right", "ADCR" },
+ { "DACR", NULL, "Right Sidetone" },
+
+ { "Left Bypass", NULL, "Class G" },
+ { "Left Bypass", NULL, "Left Capture PGA" },
+
+ { "Right Bypass", NULL, "Class G" },
+ { "Right Bypass", NULL, "Right Capture PGA" },
+
+ { "HPL Mux", "DAC", "DACL" },
+ { "HPL Mux", "Bypass", "Left Bypass" },
+
+ { "HPR Mux", "DAC", "DACR" },
+ { "HPR Mux", "Bypass", "Right Bypass" },
+
+ { "LINEL Mux", "DAC", "DACL" },
+ { "LINEL Mux", "Bypass", "Left Bypass" },
+
+ { "LINER Mux", "DAC", "DACR" },
+ { "LINER Mux", "Bypass", "Right Bypass" },
+
+ { "HPL PGA", NULL, "HPL Mux" },
+ { "HPR PGA", NULL, "HPR Mux" },
+
+ { "LINEL PGA", NULL, "LINEL Mux" },
+ { "LINER PGA", NULL, "LINER Mux" },
+};
+
+static const struct snd_soc_dapm_route wm8912_intercon[] = {
+ { "HPL PGA", NULL, "DACL" },
+ { "HPR PGA", NULL, "DACR" },
+
+ { "LINEL PGA", NULL, "DACL" },
+ { "LINER PGA", NULL, "DACR" },
+};
+
+static int wm8904_add_widgets(struct snd_soc_codec *codec)
+{
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ snd_soc_dapm_new_controls(dapm, wm8904_core_dapm_widgets,
+ ARRAY_SIZE(wm8904_core_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, core_intercon,
+ ARRAY_SIZE(core_intercon));
+
+ switch (wm8904->devtype) {
+ case WM8904:
+ snd_soc_add_codec_controls(codec, wm8904_adc_snd_controls,
+ ARRAY_SIZE(wm8904_adc_snd_controls));
+ snd_soc_add_codec_controls(codec, wm8904_dac_snd_controls,
+ ARRAY_SIZE(wm8904_dac_snd_controls));
+ snd_soc_add_codec_controls(codec, wm8904_snd_controls,
+ ARRAY_SIZE(wm8904_snd_controls));
+
+ snd_soc_dapm_new_controls(dapm, wm8904_adc_dapm_widgets,
+ ARRAY_SIZE(wm8904_adc_dapm_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8904_dac_dapm_widgets,
+ ARRAY_SIZE(wm8904_dac_dapm_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8904_dapm_widgets,
+ ARRAY_SIZE(wm8904_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, adc_intercon,
+ ARRAY_SIZE(adc_intercon));
+ snd_soc_dapm_add_routes(dapm, dac_intercon,
+ ARRAY_SIZE(dac_intercon));
+ snd_soc_dapm_add_routes(dapm, wm8904_intercon,
+ ARRAY_SIZE(wm8904_intercon));
+ break;
+
+ case WM8912:
+ snd_soc_add_codec_controls(codec, wm8904_dac_snd_controls,
+ ARRAY_SIZE(wm8904_dac_snd_controls));
+
+ snd_soc_dapm_new_controls(dapm, wm8904_dac_dapm_widgets,
+ ARRAY_SIZE(wm8904_dac_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, dac_intercon,
+ ARRAY_SIZE(dac_intercon));
+ snd_soc_dapm_add_routes(dapm, wm8912_intercon,
+ ARRAY_SIZE(wm8912_intercon));
+ break;
+ }
+
+ return 0;
+}
+
+static struct {
+ int ratio;
+ unsigned int clk_sys_rate;
+} clk_sys_rates[] = {
+ { 64, 0 },
+ { 128, 1 },
+ { 192, 2 },
+ { 256, 3 },
+ { 384, 4 },
+ { 512, 5 },
+ { 786, 6 },
+ { 1024, 7 },
+ { 1408, 8 },
+ { 1536, 9 },
+};
+
+static struct {
+ int rate;
+ int sample_rate;
+} sample_rates[] = {
+ { 8000, 0 },
+ { 11025, 1 },
+ { 12000, 1 },
+ { 16000, 2 },
+ { 22050, 3 },
+ { 24000, 3 },
+ { 32000, 4 },
+ { 44100, 5 },
+ { 48000, 5 },
+};
+
+static struct {
+ int div; /* *10 due to .5s */
+ int bclk_div;
+} bclk_divs[] = {
+ { 10, 0 },
+ { 15, 1 },
+ { 20, 2 },
+ { 30, 3 },
+ { 40, 4 },
+ { 50, 5 },
+ { 55, 6 },
+ { 60, 7 },
+ { 80, 8 },
+ { 100, 9 },
+ { 110, 10 },
+ { 120, 11 },
+ { 160, 12 },
+ { 200, 13 },
+ { 220, 14 },
+ { 240, 16 },
+ { 200, 17 },
+ { 320, 18 },
+ { 440, 19 },
+ { 480, 20 },
+};
+
+
+static int wm8904_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ int ret, i, best, best_val, cur_val;
+ unsigned int aif1 = 0;
+ unsigned int aif2 = 0;
+ unsigned int aif3 = 0;
+ unsigned int clock1 = 0;
+ unsigned int dac_digital1 = 0;
+
+ /* What BCLK do we need? */
+ wm8904->fs = params_rate(params);
+ if (wm8904->tdm_slots) {
+ dev_dbg(codec->dev, "Configuring for %d %d bit TDM slots\n",
+ wm8904->tdm_slots, wm8904->tdm_width);
+ wm8904->bclk = snd_soc_calc_bclk(wm8904->fs,
+ wm8904->tdm_width, 2,
+ wm8904->tdm_slots);
+ } else {
+ wm8904->bclk = snd_soc_params_to_bclk(params);
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ aif1 |= 0x40;
+ break;
+ case 24:
+ aif1 |= 0x80;
+ break;
+ case 32:
+ aif1 |= 0xc0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8904->bclk);
+
+ ret = wm8904_configure_clocking(codec);
+ if (ret != 0)
+ return ret;
+
+ /* Select nearest CLK_SYS_RATE */
+ best = 0;
+ best_val = abs((wm8904->sysclk_rate / clk_sys_rates[0].ratio)
+ - wm8904->fs);
+ for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) {
+ cur_val = abs((wm8904->sysclk_rate /
+ clk_sys_rates[i].ratio) - wm8904->fs);
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+ dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n",
+ clk_sys_rates[best].ratio);
+ clock1 |= (clk_sys_rates[best].clk_sys_rate
+ << WM8904_CLK_SYS_RATE_SHIFT);
+
+ /* SAMPLE_RATE */
+ best = 0;
+ best_val = abs(wm8904->fs - sample_rates[0].rate);
+ for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+ /* Closest match */
+ cur_val = abs(wm8904->fs - sample_rates[i].rate);
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+ dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n",
+ sample_rates[best].rate);
+ clock1 |= (sample_rates[best].sample_rate
+ << WM8904_SAMPLE_RATE_SHIFT);
+
+ /* Enable sloping stopband filter for low sample rates */
+ if (wm8904->fs <= 24000)
+ dac_digital1 |= WM8904_DAC_SB_FILT;
+
+ /* BCLK_DIV */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ cur_val = ((wm8904->sysclk_rate * 10) / bclk_divs[i].div)
+ - wm8904->bclk;
+ if (cur_val < 0) /* Table is sorted */
+ break;
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+ wm8904->bclk = (wm8904->sysclk_rate * 10) / bclk_divs[best].div;
+ dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
+ bclk_divs[best].div, wm8904->bclk);
+ aif2 |= bclk_divs[best].bclk_div;
+
+ /* LRCLK is a simple fraction of BCLK */
+ dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm8904->bclk / wm8904->fs);
+ aif3 |= wm8904->bclk / wm8904->fs;
+
+ /* Apply the settings */
+ snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_1,
+ WM8904_DAC_SB_FILT, dac_digital1);
+ snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_1,
+ WM8904_AIF_WL_MASK, aif1);
+ snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_2,
+ WM8904_BCLK_DIV_MASK, aif2);
+ snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_3,
+ WM8904_LRCLK_RATE_MASK, aif3);
+ snd_soc_update_bits(codec, WM8904_CLOCK_RATES_1,
+ WM8904_SAMPLE_RATE_MASK |
+ WM8904_CLK_SYS_RATE_MASK, clock1);
+
+ /* Update filters for the new settings */
+ wm8904_set_retune_mobile(codec);
+ wm8904_set_deemph(codec);
+
+ return 0;
+}
+
+
+static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8904_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case WM8904_CLK_MCLK:
+ priv->sysclk_src = clk_id;
+ priv->mclk_rate = freq;
+ break;
+
+ case WM8904_CLK_FLL:
+ priv->sysclk_src = clk_id;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+ wm8904_configure_clocking(codec);
+
+ return 0;
+}
+
+static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int aif1 = 0;
+ unsigned int aif3 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ aif3 |= WM8904_LRCLK_DIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ aif1 |= WM8904_BCLK_DIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif1 |= WM8904_BCLK_DIR;
+ aif3 |= WM8904_LRCLK_DIR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_B:
+ aif1 |= 0x3 | WM8904_AIF_LRCLK_INV;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif1 |= 0x3;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aif1 |= 0x2;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif1 |= 0x1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8904_AIF_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif1 |= WM8904_AIF_BCLK_INV | WM8904_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8904_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif1 |= WM8904_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_1,
+ WM8904_AIF_BCLK_INV | WM8904_AIF_LRCLK_INV |
+ WM8904_AIF_FMT_MASK | WM8904_BCLK_DIR, aif1);
+ snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_3,
+ WM8904_LRCLK_DIR, aif3);
+
+ return 0;
+}
+
+
+static int wm8904_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ int aif1 = 0;
+
+ /* Don't need to validate anything if we're turning off TDM */
+ if (slots == 0)
+ goto out;
+
+ /* Note that we allow configurations we can't handle ourselves -
+ * for example, we can generate clocks for slots 2 and up even if
+ * we can't use those slots ourselves.
+ */
+ aif1 |= WM8904_AIFADC_TDM | WM8904_AIFDAC_TDM;
+
+ switch (rx_mask) {
+ case 3:
+ break;
+ case 0xc:
+ aif1 |= WM8904_AIFADC_TDM_CHAN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ switch (tx_mask) {
+ case 3:
+ break;
+ case 0xc:
+ aif1 |= WM8904_AIFDAC_TDM_CHAN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+out:
+ wm8904->tdm_width = slot_width;
+ wm8904->tdm_slots = slots / 2;
+
+ snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_1,
+ WM8904_AIFADC_TDM | WM8904_AIFADC_TDM_CHAN |
+ WM8904_AIFDAC_TDM | WM8904_AIFDAC_TDM_CHAN, aif1);
+
+ return 0;
+}
+
+struct _fll_div {
+ u16 fll_fratio;
+ u16 fll_outdiv;
+ u16 fll_clk_ref_div;
+ u16 n;
+ u16 k;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fll_fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod, target;
+ unsigned int div;
+ int i;
+
+ /* Fref must be <=13.5MHz */
+ div = 1;
+ fll_div->fll_clk_ref_div = 0;
+ while ((Fref / div) > 13500000) {
+ div *= 2;
+ fll_div->fll_clk_ref_div++;
+
+ if (div > 8) {
+ pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+ }
+
+ pr_debug("Fref=%u Fout=%u\n", Fref, Fout);
+
+ /* Apply the division for our remaining calculations */
+ Fref /= div;
+
+ /* Fvco should be 90-100MHz; don't check the upper bound */
+ div = 4;
+ while (Fout * div < 90000000) {
+ div++;
+ if (div > 64) {
+ pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ target = Fout * div;
+ fll_div->fll_outdiv = div - 1;
+
+ pr_debug("Fvco=%dHz\n", target);
+
+ /* Find an appropriate FLL_FRATIO and factor it out of the target */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+ target /= fll_fratios[i].ratio;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_fratios)) {
+ pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+ return -EINVAL;
+ }
+
+ /* Now, calculate N.K */
+ Ndiv = target / Fref;
+
+ fll_div->n = Ndiv;
+ Nmod = target % Fref;
+ pr_debug("Nmod=%d\n", Nmod);
+
+ /* Calculate fractional part - scale up so we can round. */
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, Fref);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ fll_div->k = K / 10;
+
+ pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n",
+ fll_div->n, fll_div->k,
+ fll_div->fll_fratio, fll_div->fll_outdiv,
+ fll_div->fll_clk_ref_div);
+
+ return 0;
+}
+
+static int wm8904_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct _fll_div fll_div;
+ int ret, val;
+ int clock2, fll1;
+
+ /* Any change? */
+ if (source == wm8904->fll_src && Fref == wm8904->fll_fref &&
+ Fout == wm8904->fll_fout)
+ return 0;
+
+ clock2 = snd_soc_read(codec, WM8904_CLOCK_RATES_2);
+
+ if (Fout == 0) {
+ dev_dbg(codec->dev, "FLL disabled\n");
+
+ wm8904->fll_fref = 0;
+ wm8904->fll_fout = 0;
+
+ /* Gate SYSCLK to avoid glitches */
+ snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+ WM8904_CLK_SYS_ENA, 0);
+
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+
+ goto out;
+ }
+
+ /* Validate the FLL ID */
+ switch (source) {
+ case WM8904_FLL_MCLK:
+ case WM8904_FLL_LRCLK:
+ case WM8904_FLL_BCLK:
+ ret = fll_factors(&fll_div, Fref, Fout);
+ if (ret != 0)
+ return ret;
+ break;
+
+ case WM8904_FLL_FREE_RUNNING:
+ dev_dbg(codec->dev, "Using free running FLL\n");
+ /* Force 12MHz and output/4 for now */
+ Fout = 12000000;
+ Fref = 12000000;
+
+ memset(&fll_div, 0, sizeof(fll_div));
+ fll_div.fll_outdiv = 3;
+ break;
+
+ default:
+ dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
+ return -EINVAL;
+ }
+
+ /* Save current state then disable the FLL and SYSCLK to avoid
+ * misclocking */
+ fll1 = snd_soc_read(codec, WM8904_FLL_CONTROL_1);
+ snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+ WM8904_CLK_SYS_ENA, 0);
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+
+ /* Unlock forced oscilator control to switch it on/off */
+ snd_soc_update_bits(codec, WM8904_CONTROL_INTERFACE_TEST_1,
+ WM8904_USER_KEY, WM8904_USER_KEY);
+
+ if (fll_id == WM8904_FLL_FREE_RUNNING) {
+ val = WM8904_FLL_FRC_NCO;
+ } else {
+ val = 0;
+ }
+
+ snd_soc_update_bits(codec, WM8904_FLL_NCO_TEST_1, WM8904_FLL_FRC_NCO,
+ val);
+ snd_soc_update_bits(codec, WM8904_CONTROL_INTERFACE_TEST_1,
+ WM8904_USER_KEY, 0);
+
+ switch (fll_id) {
+ case WM8904_FLL_MCLK:
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_5,
+ WM8904_FLL_CLK_REF_SRC_MASK, 0);
+ break;
+
+ case WM8904_FLL_LRCLK:
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_5,
+ WM8904_FLL_CLK_REF_SRC_MASK, 1);
+ break;
+
+ case WM8904_FLL_BCLK:
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_5,
+ WM8904_FLL_CLK_REF_SRC_MASK, 2);
+ break;
+ }
+
+ if (fll_div.k)
+ val = WM8904_FLL_FRACN_ENA;
+ else
+ val = 0;
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ WM8904_FLL_FRACN_ENA, val);
+
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_2,
+ WM8904_FLL_OUTDIV_MASK | WM8904_FLL_FRATIO_MASK,
+ (fll_div.fll_outdiv << WM8904_FLL_OUTDIV_SHIFT) |
+ (fll_div.fll_fratio << WM8904_FLL_FRATIO_SHIFT));
+
+ snd_soc_write(codec, WM8904_FLL_CONTROL_3, fll_div.k);
+
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_4, WM8904_FLL_N_MASK,
+ fll_div.n << WM8904_FLL_N_SHIFT);
+
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_5,
+ WM8904_FLL_CLK_REF_DIV_MASK,
+ fll_div.fll_clk_ref_div
+ << WM8904_FLL_CLK_REF_DIV_SHIFT);
+
+ dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+
+ wm8904->fll_fref = Fref;
+ wm8904->fll_fout = Fout;
+ wm8904->fll_src = source;
+
+ /* Enable the FLL if it was previously active */
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ WM8904_FLL_OSC_ENA, fll1);
+ snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+ WM8904_FLL_ENA, fll1);
+
+out:
+ /* Reenable SYSCLK if it was previously active */
+ snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+ WM8904_CLK_SYS_ENA, clock2);
+
+ return 0;
+}
+
+static int wm8904_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int val;
+
+ if (mute)
+ val = WM8904_DAC_MUTE;
+ else
+ val = 0;
+
+ snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_1, WM8904_DAC_MUTE, val);
+
+ return 0;
+}
+
+static int wm8904_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ clk_prepare_enable(wm8904->mclk);
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID resistance 2*50k */
+ snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+ WM8904_VMID_RES_MASK,
+ 0x1 << WM8904_VMID_RES_SHIFT);
+
+ /* Normal bias current */
+ snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+ WM8904_ISEL_MASK, 2 << WM8904_ISEL_SHIFT);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies),
+ wm8904->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ regcache_cache_only(wm8904->regmap, false);
+ regcache_sync(wm8904->regmap);
+
+ /* Enable bias */
+ snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+ WM8904_BIAS_ENA, WM8904_BIAS_ENA);
+
+ /* Enable VMID, VMID buffering, 2*5k resistance */
+ snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+ WM8904_VMID_ENA |
+ WM8904_VMID_RES_MASK,
+ WM8904_VMID_ENA |
+ 0x3 << WM8904_VMID_RES_SHIFT);
+
+ /* Let VMID ramp */
+ msleep(1);
+ }
+
+ /* Maintain VMID with 2*250k */
+ snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+ WM8904_VMID_RES_MASK,
+ 0x2 << WM8904_VMID_RES_SHIFT);
+
+ /* Bias current *0.5 */
+ snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+ WM8904_ISEL_MASK, 0);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Turn off VMID */
+ snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+ WM8904_VMID_RES_MASK | WM8904_VMID_ENA, 0);
+
+ /* Stop bias generation */
+ snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+ WM8904_BIAS_ENA, 0);
+
+ regcache_cache_only(wm8904->regmap, true);
+ regcache_mark_dirty(wm8904->regmap);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies),
+ wm8904->supplies);
+ clk_disable_unprepare(wm8904->mclk);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8904_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8904_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8904_dai_ops = {
+ .set_sysclk = wm8904_set_sysclk,
+ .set_fmt = wm8904_set_fmt,
+ .set_tdm_slot = wm8904_set_tdm_slot,
+ .set_pll = wm8904_set_fll,
+ .hw_params = wm8904_hw_params,
+ .digital_mute = wm8904_digital_mute,
+};
+
+static struct snd_soc_dai_driver wm8904_dai = {
+ .name = "wm8904-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8904_RATES,
+ .formats = WM8904_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8904_RATES,
+ .formats = WM8904_FORMATS,
+ },
+ .ops = &wm8904_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static void wm8904_handle_retune_mobile_pdata(struct snd_soc_codec *codec)
+{
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_pdata *pdata = wm8904->pdata;
+ struct snd_kcontrol_new control =
+ SOC_ENUM_EXT("EQ Mode",
+ wm8904->retune_mobile_enum,
+ wm8904_get_retune_mobile_enum,
+ wm8904_put_retune_mobile_enum);
+ int ret, i, j;
+ const char **t;
+
+ /* We need an array of texts for the enum API but the number
+ * of texts is likely to be less than the number of
+ * configurations due to the sample rate dependency of the
+ * configurations. */
+ wm8904->num_retune_mobile_texts = 0;
+ wm8904->retune_mobile_texts = NULL;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ for (j = 0; j < wm8904->num_retune_mobile_texts; j++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8904->retune_mobile_texts[j]) == 0)
+ break;
+ }
+
+ if (j != wm8904->num_retune_mobile_texts)
+ continue;
+
+ /* Expand the array... */
+ t = krealloc(wm8904->retune_mobile_texts,
+ sizeof(char *) *
+ (wm8904->num_retune_mobile_texts + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* ...store the new entry... */
+ t[wm8904->num_retune_mobile_texts] =
+ pdata->retune_mobile_cfgs[i].name;
+
+ /* ...and remember the new version. */
+ wm8904->num_retune_mobile_texts++;
+ wm8904->retune_mobile_texts = t;
+ }
+
+ dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+ wm8904->num_retune_mobile_texts);
+
+ wm8904->retune_mobile_enum.items = wm8904->num_retune_mobile_texts;
+ wm8904->retune_mobile_enum.texts = wm8904->retune_mobile_texts;
+
+ ret = snd_soc_add_codec_controls(codec, &control, 1);
+ if (ret != 0)
+ dev_err(codec->dev,
+ "Failed to add ReTune Mobile control: %d\n", ret);
+}
+
+static void wm8904_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ struct wm8904_pdata *pdata = wm8904->pdata;
+ int ret, i;
+
+ if (!pdata) {
+ snd_soc_add_codec_controls(codec, wm8904_eq_controls,
+ ARRAY_SIZE(wm8904_eq_controls));
+ return;
+ }
+
+ dev_dbg(codec->dev, "%d DRC configurations\n", pdata->num_drc_cfgs);
+
+ if (pdata->num_drc_cfgs) {
+ struct snd_kcontrol_new control =
+ SOC_ENUM_EXT("DRC Mode", wm8904->drc_enum,
+ wm8904_get_drc_enum, wm8904_put_drc_enum);
+
+ /* We need an array of texts for the enum API */
+ wm8904->drc_texts = kmalloc(sizeof(char *)
+ * pdata->num_drc_cfgs, GFP_KERNEL);
+ if (!wm8904->drc_texts)
+ return;
+
+ for (i = 0; i < pdata->num_drc_cfgs; i++)
+ wm8904->drc_texts[i] = pdata->drc_cfgs[i].name;
+
+ wm8904->drc_enum.items = pdata->num_drc_cfgs;
+ wm8904->drc_enum.texts = wm8904->drc_texts;
+
+ ret = snd_soc_add_codec_controls(codec, &control, 1);
+ if (ret != 0)
+ dev_err(codec->dev,
+ "Failed to add DRC mode control: %d\n", ret);
+
+ wm8904_set_drc(codec);
+ }
+
+ dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
+ pdata->num_retune_mobile_cfgs);
+
+ if (pdata->num_retune_mobile_cfgs)
+ wm8904_handle_retune_mobile_pdata(codec);
+ else
+ snd_soc_add_codec_controls(codec, wm8904_eq_controls,
+ ARRAY_SIZE(wm8904_eq_controls));
+}
+
+
+static int wm8904_probe(struct snd_soc_codec *codec)
+{
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+
+ switch (wm8904->devtype) {
+ case WM8904:
+ break;
+ case WM8912:
+ memset(&wm8904_dai.capture, 0, sizeof(wm8904_dai.capture));
+ break;
+ default:
+ dev_err(codec->dev, "Unknown device type %d\n",
+ wm8904->devtype);
+ return -EINVAL;
+ }
+
+ wm8904_handle_pdata(codec);
+
+ wm8904_add_widgets(codec);
+
+ return 0;
+}
+
+static int wm8904_remove(struct snd_soc_codec *codec)
+{
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+
+ kfree(wm8904->retune_mobile_texts);
+ kfree(wm8904->drc_texts);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8904 = {
+ .probe = wm8904_probe,
+ .remove = wm8904_remove,
+ .set_bias_level = wm8904_set_bias_level,
+ .idle_bias_off = true,
+};
+
+static const struct regmap_config wm8904_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = WM8904_MAX_REGISTER,
+ .volatile_reg = wm8904_volatile_register,
+ .readable_reg = wm8904_readable_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm8904_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8904_reg_defaults),
+};
+
+#ifdef CONFIG_OF
+static enum wm8904_type wm8904_data = WM8904;
+static enum wm8904_type wm8912_data = WM8912;
+
+static const struct of_device_id wm8904_of_match[] = {
+ {
+ .compatible = "wlf,wm8904",
+ .data = &wm8904_data,
+ }, {
+ .compatible = "wlf,wm8912",
+ .data = &wm8912_data,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, wm8904_of_match);
+#endif
+
+static int wm8904_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8904_priv *wm8904;
+ unsigned int val;
+ int ret, i;
+
+ wm8904 = devm_kzalloc(&i2c->dev, sizeof(struct wm8904_priv),
+ GFP_KERNEL);
+ if (wm8904 == NULL)
+ return -ENOMEM;
+
+ wm8904->mclk = devm_clk_get(&i2c->dev, "mclk");
+ if (IS_ERR(wm8904->mclk)) {
+ ret = PTR_ERR(wm8904->mclk);
+ dev_err(&i2c->dev, "Failed to get MCLK\n");
+ return ret;
+ }
+
+ wm8904->regmap = devm_regmap_init_i2c(i2c, &wm8904_regmap);
+ if (IS_ERR(wm8904->regmap)) {
+ ret = PTR_ERR(wm8904->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (i2c->dev.of_node) {
+ const struct of_device_id *match;
+
+ match = of_match_node(wm8904_of_match, i2c->dev.of_node);
+ if (match == NULL)
+ return -EINVAL;
+ wm8904->devtype = *((enum wm8904_type *)match->data);
+ } else {
+ wm8904->devtype = id->driver_data;
+ }
+
+ i2c_set_clientdata(i2c, wm8904);
+ wm8904->pdata = i2c->dev.platform_data;
+
+ for (i = 0; i < ARRAY_SIZE(wm8904->supplies); i++)
+ wm8904->supplies[i].supply = wm8904_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8904->supplies),
+ wm8904->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies),
+ wm8904->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(wm8904->regmap, WM8904_SW_RESET_AND_ID, &val);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret);
+ goto err_enable;
+ }
+ if (val != 0x8904) {
+ dev_err(&i2c->dev, "Device is not a WM8904, ID is %x\n", val);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ ret = regmap_read(wm8904->regmap, WM8904_REVISION, &val);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_enable;
+ }
+ dev_info(&i2c->dev, "revision %c\n", val + 'A');
+
+ ret = regmap_write(wm8904->regmap, WM8904_SW_RESET_AND_ID, 0);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
+ goto err_enable;
+ }
+
+ /* Change some default settings - latch VU and enable ZC */
+ regmap_update_bits(wm8904->regmap, WM8904_ADC_DIGITAL_VOLUME_LEFT,
+ WM8904_ADC_VU, WM8904_ADC_VU);
+ regmap_update_bits(wm8904->regmap, WM8904_ADC_DIGITAL_VOLUME_RIGHT,
+ WM8904_ADC_VU, WM8904_ADC_VU);
+ regmap_update_bits(wm8904->regmap, WM8904_DAC_DIGITAL_VOLUME_LEFT,
+ WM8904_DAC_VU, WM8904_DAC_VU);
+ regmap_update_bits(wm8904->regmap, WM8904_DAC_DIGITAL_VOLUME_RIGHT,
+ WM8904_DAC_VU, WM8904_DAC_VU);
+ regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT1_LEFT,
+ WM8904_HPOUT_VU | WM8904_HPOUTLZC,
+ WM8904_HPOUT_VU | WM8904_HPOUTLZC);
+ regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT1_RIGHT,
+ WM8904_HPOUT_VU | WM8904_HPOUTRZC,
+ WM8904_HPOUT_VU | WM8904_HPOUTRZC);
+ regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT2_LEFT,
+ WM8904_LINEOUT_VU | WM8904_LINEOUTLZC,
+ WM8904_LINEOUT_VU | WM8904_LINEOUTLZC);
+ regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT2_RIGHT,
+ WM8904_LINEOUT_VU | WM8904_LINEOUTRZC,
+ WM8904_LINEOUT_VU | WM8904_LINEOUTRZC);
+ regmap_update_bits(wm8904->regmap, WM8904_CLOCK_RATES_0,
+ WM8904_SR_MODE, 0);
+
+ /* Apply configuration from the platform data. */
+ if (wm8904->pdata) {
+ for (i = 0; i < WM8904_GPIO_REGS; i++) {
+ if (!wm8904->pdata->gpio_cfg[i])
+ continue;
+
+ regmap_update_bits(wm8904->regmap,
+ WM8904_GPIO_CONTROL_1 + i,
+ 0xffff,
+ wm8904->pdata->gpio_cfg[i]);
+ }
+
+ /* Zero is the default value for these anyway */
+ for (i = 0; i < WM8904_MIC_REGS; i++)
+ regmap_update_bits(wm8904->regmap,
+ WM8904_MIC_BIAS_CONTROL_0 + i,
+ 0xffff,
+ wm8904->pdata->mic_cfg[i]);
+ }
+
+ /* Set Class W by default - this will be managed by the Class
+ * G widget at runtime where bypass paths are available.
+ */
+ regmap_update_bits(wm8904->regmap, WM8904_CLASS_W_0,
+ WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR);
+
+ /* Use normal bias source */
+ regmap_update_bits(wm8904->regmap, WM8904_BIAS_CONTROL_0,
+ WM8904_POBCTRL, 0);
+
+ /* Can leave the device powered off until we need it */
+ regcache_cache_only(wm8904->regmap, true);
+ regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8904, &wm8904_dai, 1);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
+ return ret;
+}
+
+static int wm8904_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8904_i2c_id[] = {
+ { "wm8904", WM8904 },
+ { "wm8912", WM8912 },
+ { "wm8918", WM8904 }, /* Actually a subset, updates to follow */
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8904_i2c_id);
+
+static struct i2c_driver wm8904_i2c_driver = {
+ .driver = {
+ .name = "wm8904",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(wm8904_of_match),
+ },
+ .probe = wm8904_i2c_probe,
+ .remove = wm8904_i2c_remove,
+ .id_table = wm8904_i2c_id,
+};
+
+module_i2c_driver(wm8904_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8904 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8904.h b/kernel/sound/soc/codecs/wm8904.h
new file mode 100644
index 000000000..c29a0e813
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8904.h
@@ -0,0 +1,1592 @@
+/*
+ * wm8904.h -- WM8904 ASoC driver
+ *
+ * Copyright 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8904_H
+#define _WM8904_H
+
+#define WM8904_CLK_MCLK 1
+#define WM8904_CLK_FLL 2
+
+#define WM8904_FLL_MCLK 1
+#define WM8904_FLL_BCLK 2
+#define WM8904_FLL_LRCLK 3
+#define WM8904_FLL_FREE_RUNNING 4
+
+/*
+ * Register values.
+ */
+#define WM8904_SW_RESET_AND_ID 0x00
+#define WM8904_REVISION 0x01
+#define WM8904_BIAS_CONTROL_0 0x04
+#define WM8904_VMID_CONTROL_0 0x05
+#define WM8904_MIC_BIAS_CONTROL_0 0x06
+#define WM8904_MIC_BIAS_CONTROL_1 0x07
+#define WM8904_ANALOGUE_DAC_0 0x08
+#define WM8904_MIC_FILTER_CONTROL 0x09
+#define WM8904_ANALOGUE_ADC_0 0x0A
+#define WM8904_POWER_MANAGEMENT_0 0x0C
+#define WM8904_POWER_MANAGEMENT_2 0x0E
+#define WM8904_POWER_MANAGEMENT_3 0x0F
+#define WM8904_POWER_MANAGEMENT_6 0x12
+#define WM8904_CLOCK_RATES_0 0x14
+#define WM8904_CLOCK_RATES_1 0x15
+#define WM8904_CLOCK_RATES_2 0x16
+#define WM8904_AUDIO_INTERFACE_0 0x18
+#define WM8904_AUDIO_INTERFACE_1 0x19
+#define WM8904_AUDIO_INTERFACE_2 0x1A
+#define WM8904_AUDIO_INTERFACE_3 0x1B
+#define WM8904_DAC_DIGITAL_VOLUME_LEFT 0x1E
+#define WM8904_DAC_DIGITAL_VOLUME_RIGHT 0x1F
+#define WM8904_DAC_DIGITAL_0 0x20
+#define WM8904_DAC_DIGITAL_1 0x21
+#define WM8904_ADC_DIGITAL_VOLUME_LEFT 0x24
+#define WM8904_ADC_DIGITAL_VOLUME_RIGHT 0x25
+#define WM8904_ADC_DIGITAL_0 0x26
+#define WM8904_DIGITAL_MICROPHONE_0 0x27
+#define WM8904_DRC_0 0x28
+#define WM8904_DRC_1 0x29
+#define WM8904_DRC_2 0x2A
+#define WM8904_DRC_3 0x2B
+#define WM8904_ANALOGUE_LEFT_INPUT_0 0x2C
+#define WM8904_ANALOGUE_RIGHT_INPUT_0 0x2D
+#define WM8904_ANALOGUE_LEFT_INPUT_1 0x2E
+#define WM8904_ANALOGUE_RIGHT_INPUT_1 0x2F
+#define WM8904_ANALOGUE_OUT1_LEFT 0x39
+#define WM8904_ANALOGUE_OUT1_RIGHT 0x3A
+#define WM8904_ANALOGUE_OUT2_LEFT 0x3B
+#define WM8904_ANALOGUE_OUT2_RIGHT 0x3C
+#define WM8904_ANALOGUE_OUT12_ZC 0x3D
+#define WM8904_DC_SERVO_0 0x43
+#define WM8904_DC_SERVO_1 0x44
+#define WM8904_DC_SERVO_2 0x45
+#define WM8904_DC_SERVO_4 0x47
+#define WM8904_DC_SERVO_5 0x48
+#define WM8904_DC_SERVO_6 0x49
+#define WM8904_DC_SERVO_7 0x4A
+#define WM8904_DC_SERVO_8 0x4B
+#define WM8904_DC_SERVO_9 0x4C
+#define WM8904_DC_SERVO_READBACK_0 0x4D
+#define WM8904_ANALOGUE_HP_0 0x5A
+#define WM8904_ANALOGUE_LINEOUT_0 0x5E
+#define WM8904_CHARGE_PUMP_0 0x62
+#define WM8904_CLASS_W_0 0x68
+#define WM8904_WRITE_SEQUENCER_0 0x6C
+#define WM8904_WRITE_SEQUENCER_1 0x6D
+#define WM8904_WRITE_SEQUENCER_2 0x6E
+#define WM8904_WRITE_SEQUENCER_3 0x6F
+#define WM8904_WRITE_SEQUENCER_4 0x70
+#define WM8904_FLL_CONTROL_1 0x74
+#define WM8904_FLL_CONTROL_2 0x75
+#define WM8904_FLL_CONTROL_3 0x76
+#define WM8904_FLL_CONTROL_4 0x77
+#define WM8904_FLL_CONTROL_5 0x78
+#define WM8904_GPIO_CONTROL_1 0x79
+#define WM8904_GPIO_CONTROL_2 0x7A
+#define WM8904_GPIO_CONTROL_3 0x7B
+#define WM8904_GPIO_CONTROL_4 0x7C
+#define WM8904_DIGITAL_PULLS 0x7E
+#define WM8904_INTERRUPT_STATUS 0x7F
+#define WM8904_INTERRUPT_STATUS_MASK 0x80
+#define WM8904_INTERRUPT_POLARITY 0x81
+#define WM8904_INTERRUPT_DEBOUNCE 0x82
+#define WM8904_EQ1 0x86
+#define WM8904_EQ2 0x87
+#define WM8904_EQ3 0x88
+#define WM8904_EQ4 0x89
+#define WM8904_EQ5 0x8A
+#define WM8904_EQ6 0x8B
+#define WM8904_EQ7 0x8C
+#define WM8904_EQ8 0x8D
+#define WM8904_EQ9 0x8E
+#define WM8904_EQ10 0x8F
+#define WM8904_EQ11 0x90
+#define WM8904_EQ12 0x91
+#define WM8904_EQ13 0x92
+#define WM8904_EQ14 0x93
+#define WM8904_EQ15 0x94
+#define WM8904_EQ16 0x95
+#define WM8904_EQ17 0x96
+#define WM8904_EQ18 0x97
+#define WM8904_EQ19 0x98
+#define WM8904_EQ20 0x99
+#define WM8904_EQ21 0x9A
+#define WM8904_EQ22 0x9B
+#define WM8904_EQ23 0x9C
+#define WM8904_EQ24 0x9D
+#define WM8904_CONTROL_INTERFACE_TEST_1 0xA1
+#define WM8904_ADC_TEST_0 0xC6
+#define WM8904_ANALOGUE_OUTPUT_BIAS_0 0xCC
+#define WM8904_FLL_NCO_TEST_0 0xF7
+#define WM8904_FLL_NCO_TEST_1 0xF8
+
+#define WM8904_REGISTER_COUNT 101
+#define WM8904_MAX_REGISTER 0xF8
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - SW Reset and ID
+ */
+#define WM8904_SW_RST_DEV_ID1_MASK 0xFFFF /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8904_SW_RST_DEV_ID1_SHIFT 0 /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8904_SW_RST_DEV_ID1_WIDTH 16 /* SW_RST_DEV_ID1 - [15:0] */
+
+/*
+ * R1 (0x01) - Revision
+ */
+#define WM8904_REVISION_MASK 0x000F /* REVISION - [3:0] */
+#define WM8904_REVISION_SHIFT 0 /* REVISION - [3:0] */
+#define WM8904_REVISION_WIDTH 16 /* REVISION - [3:0] */
+
+/*
+ * R4 (0x04) - Bias Control 0
+ */
+#define WM8904_POBCTRL 0x0010 /* POBCTRL */
+#define WM8904_POBCTRL_MASK 0x0010 /* POBCTRL */
+#define WM8904_POBCTRL_SHIFT 4 /* POBCTRL */
+#define WM8904_POBCTRL_WIDTH 1 /* POBCTRL */
+#define WM8904_ISEL_MASK 0x000C /* ISEL - [3:2] */
+#define WM8904_ISEL_SHIFT 2 /* ISEL - [3:2] */
+#define WM8904_ISEL_WIDTH 2 /* ISEL - [3:2] */
+#define WM8904_STARTUP_BIAS_ENA 0x0002 /* STARTUP_BIAS_ENA */
+#define WM8904_STARTUP_BIAS_ENA_MASK 0x0002 /* STARTUP_BIAS_ENA */
+#define WM8904_STARTUP_BIAS_ENA_SHIFT 1 /* STARTUP_BIAS_ENA */
+#define WM8904_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */
+#define WM8904_BIAS_ENA 0x0001 /* BIAS_ENA */
+#define WM8904_BIAS_ENA_MASK 0x0001 /* BIAS_ENA */
+#define WM8904_BIAS_ENA_SHIFT 0 /* BIAS_ENA */
+#define WM8904_BIAS_ENA_WIDTH 1 /* BIAS_ENA */
+
+/*
+ * R5 (0x05) - VMID Control 0
+ */
+#define WM8904_VMID_BUF_ENA 0x0040 /* VMID_BUF_ENA */
+#define WM8904_VMID_BUF_ENA_MASK 0x0040 /* VMID_BUF_ENA */
+#define WM8904_VMID_BUF_ENA_SHIFT 6 /* VMID_BUF_ENA */
+#define WM8904_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */
+#define WM8904_VMID_RES_MASK 0x0006 /* VMID_RES - [2:1] */
+#define WM8904_VMID_RES_SHIFT 1 /* VMID_RES - [2:1] */
+#define WM8904_VMID_RES_WIDTH 2 /* VMID_RES - [2:1] */
+#define WM8904_VMID_ENA 0x0001 /* VMID_ENA */
+#define WM8904_VMID_ENA_MASK 0x0001 /* VMID_ENA */
+#define WM8904_VMID_ENA_SHIFT 0 /* VMID_ENA */
+#define WM8904_VMID_ENA_WIDTH 1 /* VMID_ENA */
+
+/*
+ * R8 (0x08) - Analogue DAC 0
+ */
+#define WM8904_DAC_BIAS_SEL_MASK 0x0018 /* DAC_BIAS_SEL - [4:3] */
+#define WM8904_DAC_BIAS_SEL_SHIFT 3 /* DAC_BIAS_SEL - [4:3] */
+#define WM8904_DAC_BIAS_SEL_WIDTH 2 /* DAC_BIAS_SEL - [4:3] */
+#define WM8904_DAC_VMID_BIAS_SEL_MASK 0x0006 /* DAC_VMID_BIAS_SEL - [2:1] */
+#define WM8904_DAC_VMID_BIAS_SEL_SHIFT 1 /* DAC_VMID_BIAS_SEL - [2:1] */
+#define WM8904_DAC_VMID_BIAS_SEL_WIDTH 2 /* DAC_VMID_BIAS_SEL - [2:1] */
+
+/*
+ * R9 (0x09) - mic Filter Control
+ */
+#define WM8904_MIC_DET_SET_THRESHOLD_MASK 0xF000 /* MIC_DET_SET_THRESHOLD - [15:12] */
+#define WM8904_MIC_DET_SET_THRESHOLD_SHIFT 12 /* MIC_DET_SET_THRESHOLD - [15:12] */
+#define WM8904_MIC_DET_SET_THRESHOLD_WIDTH 4 /* MIC_DET_SET_THRESHOLD - [15:12] */
+#define WM8904_MIC_DET_RESET_THRESHOLD_MASK 0x0F00 /* MIC_DET_RESET_THRESHOLD - [11:8] */
+#define WM8904_MIC_DET_RESET_THRESHOLD_SHIFT 8 /* MIC_DET_RESET_THRESHOLD - [11:8] */
+#define WM8904_MIC_DET_RESET_THRESHOLD_WIDTH 4 /* MIC_DET_RESET_THRESHOLD - [11:8] */
+#define WM8904_MIC_SHORT_SET_THRESHOLD_MASK 0x00F0 /* MIC_SHORT_SET_THRESHOLD - [7:4] */
+#define WM8904_MIC_SHORT_SET_THRESHOLD_SHIFT 4 /* MIC_SHORT_SET_THRESHOLD - [7:4] */
+#define WM8904_MIC_SHORT_SET_THRESHOLD_WIDTH 4 /* MIC_SHORT_SET_THRESHOLD - [7:4] */
+#define WM8904_MIC_SHORT_RESET_THRESHOLD_MASK 0x000F /* MIC_SHORT_RESET_THRESHOLD - [3:0] */
+#define WM8904_MIC_SHORT_RESET_THRESHOLD_SHIFT 0 /* MIC_SHORT_RESET_THRESHOLD - [3:0] */
+#define WM8904_MIC_SHORT_RESET_THRESHOLD_WIDTH 4 /* MIC_SHORT_RESET_THRESHOLD - [3:0] */
+
+/*
+ * R10 (0x0A) - Analogue ADC 0
+ */
+#define WM8904_ADC_OSR128 0x0001 /* ADC_OSR128 */
+#define WM8904_ADC_OSR128_MASK 0x0001 /* ADC_OSR128 */
+#define WM8904_ADC_OSR128_SHIFT 0 /* ADC_OSR128 */
+#define WM8904_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */
+
+/*
+ * R12 (0x0C) - Power Management 0
+ */
+#define WM8904_INL_ENA 0x0002 /* INL_ENA */
+#define WM8904_INL_ENA_MASK 0x0002 /* INL_ENA */
+#define WM8904_INL_ENA_SHIFT 1 /* INL_ENA */
+#define WM8904_INL_ENA_WIDTH 1 /* INL_ENA */
+#define WM8904_INR_ENA 0x0001 /* INR_ENA */
+#define WM8904_INR_ENA_MASK 0x0001 /* INR_ENA */
+#define WM8904_INR_ENA_SHIFT 0 /* INR_ENA */
+#define WM8904_INR_ENA_WIDTH 1 /* INR_ENA */
+
+/*
+ * R14 (0x0E) - Power Management 2
+ */
+#define WM8904_HPL_PGA_ENA 0x0002 /* HPL_PGA_ENA */
+#define WM8904_HPL_PGA_ENA_MASK 0x0002 /* HPL_PGA_ENA */
+#define WM8904_HPL_PGA_ENA_SHIFT 1 /* HPL_PGA_ENA */
+#define WM8904_HPL_PGA_ENA_WIDTH 1 /* HPL_PGA_ENA */
+#define WM8904_HPR_PGA_ENA 0x0001 /* HPR_PGA_ENA */
+#define WM8904_HPR_PGA_ENA_MASK 0x0001 /* HPR_PGA_ENA */
+#define WM8904_HPR_PGA_ENA_SHIFT 0 /* HPR_PGA_ENA */
+#define WM8904_HPR_PGA_ENA_WIDTH 1 /* HPR_PGA_ENA */
+
+/*
+ * R15 (0x0F) - Power Management 3
+ */
+#define WM8904_LINEOUTL_PGA_ENA 0x0002 /* LINEOUTL_PGA_ENA */
+#define WM8904_LINEOUTL_PGA_ENA_MASK 0x0002 /* LINEOUTL_PGA_ENA */
+#define WM8904_LINEOUTL_PGA_ENA_SHIFT 1 /* LINEOUTL_PGA_ENA */
+#define WM8904_LINEOUTL_PGA_ENA_WIDTH 1 /* LINEOUTL_PGA_ENA */
+#define WM8904_LINEOUTR_PGA_ENA 0x0001 /* LINEOUTR_PGA_ENA */
+#define WM8904_LINEOUTR_PGA_ENA_MASK 0x0001 /* LINEOUTR_PGA_ENA */
+#define WM8904_LINEOUTR_PGA_ENA_SHIFT 0 /* LINEOUTR_PGA_ENA */
+#define WM8904_LINEOUTR_PGA_ENA_WIDTH 1 /* LINEOUTR_PGA_ENA */
+
+/*
+ * R18 (0x12) - Power Management 6
+ */
+#define WM8904_DACL_ENA 0x0008 /* DACL_ENA */
+#define WM8904_DACL_ENA_MASK 0x0008 /* DACL_ENA */
+#define WM8904_DACL_ENA_SHIFT 3 /* DACL_ENA */
+#define WM8904_DACL_ENA_WIDTH 1 /* DACL_ENA */
+#define WM8904_DACR_ENA 0x0004 /* DACR_ENA */
+#define WM8904_DACR_ENA_MASK 0x0004 /* DACR_ENA */
+#define WM8904_DACR_ENA_SHIFT 2 /* DACR_ENA */
+#define WM8904_DACR_ENA_WIDTH 1 /* DACR_ENA */
+#define WM8904_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8904_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */
+#define WM8904_ADCL_ENA_SHIFT 1 /* ADCL_ENA */
+#define WM8904_ADCL_ENA_WIDTH 1 /* ADCL_ENA */
+#define WM8904_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8904_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */
+#define WM8904_ADCR_ENA_SHIFT 0 /* ADCR_ENA */
+#define WM8904_ADCR_ENA_WIDTH 1 /* ADCR_ENA */
+
+/*
+ * R20 (0x14) - Clock Rates 0
+ */
+#define WM8904_TOCLK_RATE_DIV16 0x4000 /* TOCLK_RATE_DIV16 */
+#define WM8904_TOCLK_RATE_DIV16_MASK 0x4000 /* TOCLK_RATE_DIV16 */
+#define WM8904_TOCLK_RATE_DIV16_SHIFT 14 /* TOCLK_RATE_DIV16 */
+#define WM8904_TOCLK_RATE_DIV16_WIDTH 1 /* TOCLK_RATE_DIV16 */
+#define WM8904_TOCLK_RATE_X4 0x2000 /* TOCLK_RATE_X4 */
+#define WM8904_TOCLK_RATE_X4_MASK 0x2000 /* TOCLK_RATE_X4 */
+#define WM8904_TOCLK_RATE_X4_SHIFT 13 /* TOCLK_RATE_X4 */
+#define WM8904_TOCLK_RATE_X4_WIDTH 1 /* TOCLK_RATE_X4 */
+#define WM8904_SR_MODE 0x1000 /* SR_MODE */
+#define WM8904_SR_MODE_MASK 0x1000 /* SR_MODE */
+#define WM8904_SR_MODE_SHIFT 12 /* SR_MODE */
+#define WM8904_SR_MODE_WIDTH 1 /* SR_MODE */
+#define WM8904_MCLK_DIV 0x0001 /* MCLK_DIV */
+#define WM8904_MCLK_DIV_MASK 0x0001 /* MCLK_DIV */
+#define WM8904_MCLK_DIV_SHIFT 0 /* MCLK_DIV */
+#define WM8904_MCLK_DIV_WIDTH 1 /* MCLK_DIV */
+
+/*
+ * R21 (0x15) - Clock Rates 1
+ */
+#define WM8904_CLK_SYS_RATE_MASK 0x3C00 /* CLK_SYS_RATE - [13:10] */
+#define WM8904_CLK_SYS_RATE_SHIFT 10 /* CLK_SYS_RATE - [13:10] */
+#define WM8904_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [13:10] */
+#define WM8904_SAMPLE_RATE_MASK 0x0007 /* SAMPLE_RATE - [2:0] */
+#define WM8904_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [2:0] */
+#define WM8904_SAMPLE_RATE_WIDTH 3 /* SAMPLE_RATE - [2:0] */
+
+/*
+ * R22 (0x16) - Clock Rates 2
+ */
+#define WM8904_MCLK_INV 0x8000 /* MCLK_INV */
+#define WM8904_MCLK_INV_MASK 0x8000 /* MCLK_INV */
+#define WM8904_MCLK_INV_SHIFT 15 /* MCLK_INV */
+#define WM8904_MCLK_INV_WIDTH 1 /* MCLK_INV */
+#define WM8904_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */
+#define WM8904_SYSCLK_SRC_MASK 0x4000 /* SYSCLK_SRC */
+#define WM8904_SYSCLK_SRC_SHIFT 14 /* SYSCLK_SRC */
+#define WM8904_SYSCLK_SRC_WIDTH 1 /* SYSCLK_SRC */
+#define WM8904_TOCLK_RATE 0x1000 /* TOCLK_RATE */
+#define WM8904_TOCLK_RATE_MASK 0x1000 /* TOCLK_RATE */
+#define WM8904_TOCLK_RATE_SHIFT 12 /* TOCLK_RATE */
+#define WM8904_TOCLK_RATE_WIDTH 1 /* TOCLK_RATE */
+#define WM8904_OPCLK_ENA 0x0008 /* OPCLK_ENA */
+#define WM8904_OPCLK_ENA_MASK 0x0008 /* OPCLK_ENA */
+#define WM8904_OPCLK_ENA_SHIFT 3 /* OPCLK_ENA */
+#define WM8904_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */
+#define WM8904_CLK_SYS_ENA 0x0004 /* CLK_SYS_ENA */
+#define WM8904_CLK_SYS_ENA_MASK 0x0004 /* CLK_SYS_ENA */
+#define WM8904_CLK_SYS_ENA_SHIFT 2 /* CLK_SYS_ENA */
+#define WM8904_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */
+#define WM8904_CLK_DSP_ENA 0x0002 /* CLK_DSP_ENA */
+#define WM8904_CLK_DSP_ENA_MASK 0x0002 /* CLK_DSP_ENA */
+#define WM8904_CLK_DSP_ENA_SHIFT 1 /* CLK_DSP_ENA */
+#define WM8904_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */
+#define WM8904_TOCLK_ENA 0x0001 /* TOCLK_ENA */
+#define WM8904_TOCLK_ENA_MASK 0x0001 /* TOCLK_ENA */
+#define WM8904_TOCLK_ENA_SHIFT 0 /* TOCLK_ENA */
+#define WM8904_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */
+
+/*
+ * R24 (0x18) - Audio Interface 0
+ */
+#define WM8904_DACL_DATINV 0x1000 /* DACL_DATINV */
+#define WM8904_DACL_DATINV_MASK 0x1000 /* DACL_DATINV */
+#define WM8904_DACL_DATINV_SHIFT 12 /* DACL_DATINV */
+#define WM8904_DACL_DATINV_WIDTH 1 /* DACL_DATINV */
+#define WM8904_DACR_DATINV 0x0800 /* DACR_DATINV */
+#define WM8904_DACR_DATINV_MASK 0x0800 /* DACR_DATINV */
+#define WM8904_DACR_DATINV_SHIFT 11 /* DACR_DATINV */
+#define WM8904_DACR_DATINV_WIDTH 1 /* DACR_DATINV */
+#define WM8904_DAC_BOOST_MASK 0x0600 /* DAC_BOOST - [10:9] */
+#define WM8904_DAC_BOOST_SHIFT 9 /* DAC_BOOST - [10:9] */
+#define WM8904_DAC_BOOST_WIDTH 2 /* DAC_BOOST - [10:9] */
+#define WM8904_LOOPBACK 0x0100 /* LOOPBACK */
+#define WM8904_LOOPBACK_MASK 0x0100 /* LOOPBACK */
+#define WM8904_LOOPBACK_SHIFT 8 /* LOOPBACK */
+#define WM8904_LOOPBACK_WIDTH 1 /* LOOPBACK */
+#define WM8904_AIFADCL_SRC 0x0080 /* AIFADCL_SRC */
+#define WM8904_AIFADCL_SRC_MASK 0x0080 /* AIFADCL_SRC */
+#define WM8904_AIFADCL_SRC_SHIFT 7 /* AIFADCL_SRC */
+#define WM8904_AIFADCL_SRC_WIDTH 1 /* AIFADCL_SRC */
+#define WM8904_AIFADCR_SRC 0x0040 /* AIFADCR_SRC */
+#define WM8904_AIFADCR_SRC_MASK 0x0040 /* AIFADCR_SRC */
+#define WM8904_AIFADCR_SRC_SHIFT 6 /* AIFADCR_SRC */
+#define WM8904_AIFADCR_SRC_WIDTH 1 /* AIFADCR_SRC */
+#define WM8904_AIFDACL_SRC 0x0020 /* AIFDACL_SRC */
+#define WM8904_AIFDACL_SRC_MASK 0x0020 /* AIFDACL_SRC */
+#define WM8904_AIFDACL_SRC_SHIFT 5 /* AIFDACL_SRC */
+#define WM8904_AIFDACL_SRC_WIDTH 1 /* AIFDACL_SRC */
+#define WM8904_AIFDACR_SRC 0x0010 /* AIFDACR_SRC */
+#define WM8904_AIFDACR_SRC_MASK 0x0010 /* AIFDACR_SRC */
+#define WM8904_AIFDACR_SRC_SHIFT 4 /* AIFDACR_SRC */
+#define WM8904_AIFDACR_SRC_WIDTH 1 /* AIFDACR_SRC */
+#define WM8904_ADC_COMP 0x0008 /* ADC_COMP */
+#define WM8904_ADC_COMP_MASK 0x0008 /* ADC_COMP */
+#define WM8904_ADC_COMP_SHIFT 3 /* ADC_COMP */
+#define WM8904_ADC_COMP_WIDTH 1 /* ADC_COMP */
+#define WM8904_ADC_COMPMODE 0x0004 /* ADC_COMPMODE */
+#define WM8904_ADC_COMPMODE_MASK 0x0004 /* ADC_COMPMODE */
+#define WM8904_ADC_COMPMODE_SHIFT 2 /* ADC_COMPMODE */
+#define WM8904_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */
+#define WM8904_DAC_COMP 0x0002 /* DAC_COMP */
+#define WM8904_DAC_COMP_MASK 0x0002 /* DAC_COMP */
+#define WM8904_DAC_COMP_SHIFT 1 /* DAC_COMP */
+#define WM8904_DAC_COMP_WIDTH 1 /* DAC_COMP */
+#define WM8904_DAC_COMPMODE 0x0001 /* DAC_COMPMODE */
+#define WM8904_DAC_COMPMODE_MASK 0x0001 /* DAC_COMPMODE */
+#define WM8904_DAC_COMPMODE_SHIFT 0 /* DAC_COMPMODE */
+#define WM8904_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */
+
+/*
+ * R25 (0x19) - Audio Interface 1
+ */
+#define WM8904_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */
+#define WM8904_AIFDAC_TDM_MASK 0x2000 /* AIFDAC_TDM */
+#define WM8904_AIFDAC_TDM_SHIFT 13 /* AIFDAC_TDM */
+#define WM8904_AIFDAC_TDM_WIDTH 1 /* AIFDAC_TDM */
+#define WM8904_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8904_AIFDAC_TDM_CHAN_MASK 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8904_AIFDAC_TDM_CHAN_SHIFT 12 /* AIFDAC_TDM_CHAN */
+#define WM8904_AIFDAC_TDM_CHAN_WIDTH 1 /* AIFDAC_TDM_CHAN */
+#define WM8904_AIFADC_TDM 0x0800 /* AIFADC_TDM */
+#define WM8904_AIFADC_TDM_MASK 0x0800 /* AIFADC_TDM */
+#define WM8904_AIFADC_TDM_SHIFT 11 /* AIFADC_TDM */
+#define WM8904_AIFADC_TDM_WIDTH 1 /* AIFADC_TDM */
+#define WM8904_AIFADC_TDM_CHAN 0x0400 /* AIFADC_TDM_CHAN */
+#define WM8904_AIFADC_TDM_CHAN_MASK 0x0400 /* AIFADC_TDM_CHAN */
+#define WM8904_AIFADC_TDM_CHAN_SHIFT 10 /* AIFADC_TDM_CHAN */
+#define WM8904_AIFADC_TDM_CHAN_WIDTH 1 /* AIFADC_TDM_CHAN */
+#define WM8904_AIF_TRIS 0x0100 /* AIF_TRIS */
+#define WM8904_AIF_TRIS_MASK 0x0100 /* AIF_TRIS */
+#define WM8904_AIF_TRIS_SHIFT 8 /* AIF_TRIS */
+#define WM8904_AIF_TRIS_WIDTH 1 /* AIF_TRIS */
+#define WM8904_AIF_BCLK_INV 0x0080 /* AIF_BCLK_INV */
+#define WM8904_AIF_BCLK_INV_MASK 0x0080 /* AIF_BCLK_INV */
+#define WM8904_AIF_BCLK_INV_SHIFT 7 /* AIF_BCLK_INV */
+#define WM8904_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */
+#define WM8904_BCLK_DIR 0x0040 /* BCLK_DIR */
+#define WM8904_BCLK_DIR_MASK 0x0040 /* BCLK_DIR */
+#define WM8904_BCLK_DIR_SHIFT 6 /* BCLK_DIR */
+#define WM8904_BCLK_DIR_WIDTH 1 /* BCLK_DIR */
+#define WM8904_AIF_LRCLK_INV 0x0010 /* AIF_LRCLK_INV */
+#define WM8904_AIF_LRCLK_INV_MASK 0x0010 /* AIF_LRCLK_INV */
+#define WM8904_AIF_LRCLK_INV_SHIFT 4 /* AIF_LRCLK_INV */
+#define WM8904_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */
+#define WM8904_AIF_WL_MASK 0x000C /* AIF_WL - [3:2] */
+#define WM8904_AIF_WL_SHIFT 2 /* AIF_WL - [3:2] */
+#define WM8904_AIF_WL_WIDTH 2 /* AIF_WL - [3:2] */
+#define WM8904_AIF_FMT_MASK 0x0003 /* AIF_FMT - [1:0] */
+#define WM8904_AIF_FMT_SHIFT 0 /* AIF_FMT - [1:0] */
+#define WM8904_AIF_FMT_WIDTH 2 /* AIF_FMT - [1:0] */
+
+/*
+ * R26 (0x1A) - Audio Interface 2
+ */
+#define WM8904_OPCLK_DIV_MASK 0x0F00 /* OPCLK_DIV - [11:8] */
+#define WM8904_OPCLK_DIV_SHIFT 8 /* OPCLK_DIV - [11:8] */
+#define WM8904_OPCLK_DIV_WIDTH 4 /* OPCLK_DIV - [11:8] */
+#define WM8904_BCLK_DIV_MASK 0x001F /* BCLK_DIV - [4:0] */
+#define WM8904_BCLK_DIV_SHIFT 0 /* BCLK_DIV - [4:0] */
+#define WM8904_BCLK_DIV_WIDTH 5 /* BCLK_DIV - [4:0] */
+
+/*
+ * R27 (0x1B) - Audio Interface 3
+ */
+#define WM8904_LRCLK_DIR 0x0800 /* LRCLK_DIR */
+#define WM8904_LRCLK_DIR_MASK 0x0800 /* LRCLK_DIR */
+#define WM8904_LRCLK_DIR_SHIFT 11 /* LRCLK_DIR */
+#define WM8904_LRCLK_DIR_WIDTH 1 /* LRCLK_DIR */
+#define WM8904_LRCLK_RATE_MASK 0x07FF /* LRCLK_RATE - [10:0] */
+#define WM8904_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [10:0] */
+#define WM8904_LRCLK_RATE_WIDTH 11 /* LRCLK_RATE - [10:0] */
+
+/*
+ * R30 (0x1E) - DAC Digital Volume Left
+ */
+#define WM8904_DAC_VU 0x0100 /* DAC_VU */
+#define WM8904_DAC_VU_MASK 0x0100 /* DAC_VU */
+#define WM8904_DAC_VU_SHIFT 8 /* DAC_VU */
+#define WM8904_DAC_VU_WIDTH 1 /* DAC_VU */
+#define WM8904_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */
+#define WM8904_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */
+#define WM8904_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */
+
+/*
+ * R31 (0x1F) - DAC Digital Volume Right
+ */
+#define WM8904_DAC_VU 0x0100 /* DAC_VU */
+#define WM8904_DAC_VU_MASK 0x0100 /* DAC_VU */
+#define WM8904_DAC_VU_SHIFT 8 /* DAC_VU */
+#define WM8904_DAC_VU_WIDTH 1 /* DAC_VU */
+#define WM8904_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */
+#define WM8904_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */
+#define WM8904_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */
+
+/*
+ * R32 (0x20) - DAC Digital 0
+ */
+#define WM8904_ADCL_DAC_SVOL_MASK 0x0F00 /* ADCL_DAC_SVOL - [11:8] */
+#define WM8904_ADCL_DAC_SVOL_SHIFT 8 /* ADCL_DAC_SVOL - [11:8] */
+#define WM8904_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [11:8] */
+#define WM8904_ADCR_DAC_SVOL_MASK 0x00F0 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8904_ADCR_DAC_SVOL_SHIFT 4 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8904_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8904_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */
+#define WM8904_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */
+#define WM8904_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */
+#define WM8904_ADC_TO_DACR_MASK 0x0003 /* ADC_TO_DACR - [1:0] */
+#define WM8904_ADC_TO_DACR_SHIFT 0 /* ADC_TO_DACR - [1:0] */
+#define WM8904_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [1:0] */
+
+/*
+ * R33 (0x21) - DAC Digital 1
+ */
+#define WM8904_DAC_MONO 0x1000 /* DAC_MONO */
+#define WM8904_DAC_MONO_MASK 0x1000 /* DAC_MONO */
+#define WM8904_DAC_MONO_SHIFT 12 /* DAC_MONO */
+#define WM8904_DAC_MONO_WIDTH 1 /* DAC_MONO */
+#define WM8904_DAC_SB_FILT 0x0800 /* DAC_SB_FILT */
+#define WM8904_DAC_SB_FILT_MASK 0x0800 /* DAC_SB_FILT */
+#define WM8904_DAC_SB_FILT_SHIFT 11 /* DAC_SB_FILT */
+#define WM8904_DAC_SB_FILT_WIDTH 1 /* DAC_SB_FILT */
+#define WM8904_DAC_MUTERATE 0x0400 /* DAC_MUTERATE */
+#define WM8904_DAC_MUTERATE_MASK 0x0400 /* DAC_MUTERATE */
+#define WM8904_DAC_MUTERATE_SHIFT 10 /* DAC_MUTERATE */
+#define WM8904_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
+#define WM8904_DAC_UNMUTE_RAMP 0x0200 /* DAC_UNMUTE_RAMP */
+#define WM8904_DAC_UNMUTE_RAMP_MASK 0x0200 /* DAC_UNMUTE_RAMP */
+#define WM8904_DAC_UNMUTE_RAMP_SHIFT 9 /* DAC_UNMUTE_RAMP */
+#define WM8904_DAC_UNMUTE_RAMP_WIDTH 1 /* DAC_UNMUTE_RAMP */
+#define WM8904_DAC_OSR128 0x0040 /* DAC_OSR128 */
+#define WM8904_DAC_OSR128_MASK 0x0040 /* DAC_OSR128 */
+#define WM8904_DAC_OSR128_SHIFT 6 /* DAC_OSR128 */
+#define WM8904_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */
+#define WM8904_DAC_MUTE 0x0008 /* DAC_MUTE */
+#define WM8904_DAC_MUTE_MASK 0x0008 /* DAC_MUTE */
+#define WM8904_DAC_MUTE_SHIFT 3 /* DAC_MUTE */
+#define WM8904_DAC_MUTE_WIDTH 1 /* DAC_MUTE */
+#define WM8904_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */
+#define WM8904_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */
+#define WM8904_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */
+
+/*
+ * R36 (0x24) - ADC Digital Volume Left
+ */
+#define WM8904_ADC_VU 0x0100 /* ADC_VU */
+#define WM8904_ADC_VU_MASK 0x0100 /* ADC_VU */
+#define WM8904_ADC_VU_SHIFT 8 /* ADC_VU */
+#define WM8904_ADC_VU_WIDTH 1 /* ADC_VU */
+#define WM8904_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */
+#define WM8904_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */
+#define WM8904_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */
+
+/*
+ * R37 (0x25) - ADC Digital Volume Right
+ */
+#define WM8904_ADC_VU 0x0100 /* ADC_VU */
+#define WM8904_ADC_VU_MASK 0x0100 /* ADC_VU */
+#define WM8904_ADC_VU_SHIFT 8 /* ADC_VU */
+#define WM8904_ADC_VU_WIDTH 1 /* ADC_VU */
+#define WM8904_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */
+#define WM8904_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */
+#define WM8904_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */
+
+/*
+ * R38 (0x26) - ADC Digital 0
+ */
+#define WM8904_ADC_HPF_CUT_MASK 0x0060 /* ADC_HPF_CUT - [6:5] */
+#define WM8904_ADC_HPF_CUT_SHIFT 5 /* ADC_HPF_CUT - [6:5] */
+#define WM8904_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [6:5] */
+#define WM8904_ADC_HPF 0x0010 /* ADC_HPF */
+#define WM8904_ADC_HPF_MASK 0x0010 /* ADC_HPF */
+#define WM8904_ADC_HPF_SHIFT 4 /* ADC_HPF */
+#define WM8904_ADC_HPF_WIDTH 1 /* ADC_HPF */
+#define WM8904_ADCL_DATINV 0x0002 /* ADCL_DATINV */
+#define WM8904_ADCL_DATINV_MASK 0x0002 /* ADCL_DATINV */
+#define WM8904_ADCL_DATINV_SHIFT 1 /* ADCL_DATINV */
+#define WM8904_ADCL_DATINV_WIDTH 1 /* ADCL_DATINV */
+#define WM8904_ADCR_DATINV 0x0001 /* ADCR_DATINV */
+#define WM8904_ADCR_DATINV_MASK 0x0001 /* ADCR_DATINV */
+#define WM8904_ADCR_DATINV_SHIFT 0 /* ADCR_DATINV */
+#define WM8904_ADCR_DATINV_WIDTH 1 /* ADCR_DATINV */
+
+/*
+ * R39 (0x27) - Digital Microphone 0
+ */
+#define WM8904_DMIC_ENA 0x1000 /* DMIC_ENA */
+#define WM8904_DMIC_ENA_MASK 0x1000 /* DMIC_ENA */
+#define WM8904_DMIC_ENA_SHIFT 12 /* DMIC_ENA */
+#define WM8904_DMIC_ENA_WIDTH 1 /* DMIC_ENA */
+#define WM8904_DMIC_SRC 0x0800 /* DMIC_SRC */
+#define WM8904_DMIC_SRC_MASK 0x0800 /* DMIC_SRC */
+#define WM8904_DMIC_SRC_SHIFT 11 /* DMIC_SRC */
+#define WM8904_DMIC_SRC_WIDTH 1 /* DMIC_SRC */
+
+/*
+ * R40 (0x28) - DRC 0
+ */
+#define WM8904_DRC_ENA 0x8000 /* DRC_ENA */
+#define WM8904_DRC_ENA_MASK 0x8000 /* DRC_ENA */
+#define WM8904_DRC_ENA_SHIFT 15 /* DRC_ENA */
+#define WM8904_DRC_ENA_WIDTH 1 /* DRC_ENA */
+#define WM8904_DRC_DAC_PATH 0x4000 /* DRC_DAC_PATH */
+#define WM8904_DRC_DAC_PATH_MASK 0x4000 /* DRC_DAC_PATH */
+#define WM8904_DRC_DAC_PATH_SHIFT 14 /* DRC_DAC_PATH */
+#define WM8904_DRC_DAC_PATH_WIDTH 1 /* DRC_DAC_PATH */
+#define WM8904_DRC_GS_HYST_LVL_MASK 0x1800 /* DRC_GS_HYST_LVL - [12:11] */
+#define WM8904_DRC_GS_HYST_LVL_SHIFT 11 /* DRC_GS_HYST_LVL - [12:11] */
+#define WM8904_DRC_GS_HYST_LVL_WIDTH 2 /* DRC_GS_HYST_LVL - [12:11] */
+#define WM8904_DRC_STARTUP_GAIN_MASK 0x07C0 /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8904_DRC_STARTUP_GAIN_SHIFT 6 /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8904_DRC_STARTUP_GAIN_WIDTH 5 /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8904_DRC_FF_DELAY 0x0020 /* DRC_FF_DELAY */
+#define WM8904_DRC_FF_DELAY_MASK 0x0020 /* DRC_FF_DELAY */
+#define WM8904_DRC_FF_DELAY_SHIFT 5 /* DRC_FF_DELAY */
+#define WM8904_DRC_FF_DELAY_WIDTH 1 /* DRC_FF_DELAY */
+#define WM8904_DRC_GS_ENA 0x0008 /* DRC_GS_ENA */
+#define WM8904_DRC_GS_ENA_MASK 0x0008 /* DRC_GS_ENA */
+#define WM8904_DRC_GS_ENA_SHIFT 3 /* DRC_GS_ENA */
+#define WM8904_DRC_GS_ENA_WIDTH 1 /* DRC_GS_ENA */
+#define WM8904_DRC_QR 0x0004 /* DRC_QR */
+#define WM8904_DRC_QR_MASK 0x0004 /* DRC_QR */
+#define WM8904_DRC_QR_SHIFT 2 /* DRC_QR */
+#define WM8904_DRC_QR_WIDTH 1 /* DRC_QR */
+#define WM8904_DRC_ANTICLIP 0x0002 /* DRC_ANTICLIP */
+#define WM8904_DRC_ANTICLIP_MASK 0x0002 /* DRC_ANTICLIP */
+#define WM8904_DRC_ANTICLIP_SHIFT 1 /* DRC_ANTICLIP */
+#define WM8904_DRC_ANTICLIP_WIDTH 1 /* DRC_ANTICLIP */
+#define WM8904_DRC_GS_HYST 0x0001 /* DRC_GS_HYST */
+#define WM8904_DRC_GS_HYST_MASK 0x0001 /* DRC_GS_HYST */
+#define WM8904_DRC_GS_HYST_SHIFT 0 /* DRC_GS_HYST */
+#define WM8904_DRC_GS_HYST_WIDTH 1 /* DRC_GS_HYST */
+
+/*
+ * R41 (0x29) - DRC 1
+ */
+#define WM8904_DRC_ATK_MASK 0xF000 /* DRC_ATK - [15:12] */
+#define WM8904_DRC_ATK_SHIFT 12 /* DRC_ATK - [15:12] */
+#define WM8904_DRC_ATK_WIDTH 4 /* DRC_ATK - [15:12] */
+#define WM8904_DRC_DCY_MASK 0x0F00 /* DRC_DCY - [11:8] */
+#define WM8904_DRC_DCY_SHIFT 8 /* DRC_DCY - [11:8] */
+#define WM8904_DRC_DCY_WIDTH 4 /* DRC_DCY - [11:8] */
+#define WM8904_DRC_QR_THR_MASK 0x00C0 /* DRC_QR_THR - [7:6] */
+#define WM8904_DRC_QR_THR_SHIFT 6 /* DRC_QR_THR - [7:6] */
+#define WM8904_DRC_QR_THR_WIDTH 2 /* DRC_QR_THR - [7:6] */
+#define WM8904_DRC_QR_DCY_MASK 0x0030 /* DRC_QR_DCY - [5:4] */
+#define WM8904_DRC_QR_DCY_SHIFT 4 /* DRC_QR_DCY - [5:4] */
+#define WM8904_DRC_QR_DCY_WIDTH 2 /* DRC_QR_DCY - [5:4] */
+#define WM8904_DRC_MINGAIN_MASK 0x000C /* DRC_MINGAIN - [3:2] */
+#define WM8904_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [3:2] */
+#define WM8904_DRC_MINGAIN_WIDTH 2 /* DRC_MINGAIN - [3:2] */
+#define WM8904_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */
+#define WM8904_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */
+#define WM8904_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R42 (0x2A) - DRC 2
+ */
+#define WM8904_DRC_HI_COMP_MASK 0x0038 /* DRC_HI_COMP - [5:3] */
+#define WM8904_DRC_HI_COMP_SHIFT 3 /* DRC_HI_COMP - [5:3] */
+#define WM8904_DRC_HI_COMP_WIDTH 3 /* DRC_HI_COMP - [5:3] */
+#define WM8904_DRC_LO_COMP_MASK 0x0007 /* DRC_LO_COMP - [2:0] */
+#define WM8904_DRC_LO_COMP_SHIFT 0 /* DRC_LO_COMP - [2:0] */
+#define WM8904_DRC_LO_COMP_WIDTH 3 /* DRC_LO_COMP - [2:0] */
+
+/*
+ * R43 (0x2B) - DRC 3
+ */
+#define WM8904_DRC_KNEE_IP_MASK 0x07E0 /* DRC_KNEE_IP - [10:5] */
+#define WM8904_DRC_KNEE_IP_SHIFT 5 /* DRC_KNEE_IP - [10:5] */
+#define WM8904_DRC_KNEE_IP_WIDTH 6 /* DRC_KNEE_IP - [10:5] */
+#define WM8904_DRC_KNEE_OP_MASK 0x001F /* DRC_KNEE_OP - [4:0] */
+#define WM8904_DRC_KNEE_OP_SHIFT 0 /* DRC_KNEE_OP - [4:0] */
+#define WM8904_DRC_KNEE_OP_WIDTH 5 /* DRC_KNEE_OP - [4:0] */
+
+/*
+ * R44 (0x2C) - Analogue Left Input 0
+ */
+#define WM8904_LINMUTE 0x0080 /* LINMUTE */
+#define WM8904_LINMUTE_MASK 0x0080 /* LINMUTE */
+#define WM8904_LINMUTE_SHIFT 7 /* LINMUTE */
+#define WM8904_LINMUTE_WIDTH 1 /* LINMUTE */
+#define WM8904_LIN_VOL_MASK 0x001F /* LIN_VOL - [4:0] */
+#define WM8904_LIN_VOL_SHIFT 0 /* LIN_VOL - [4:0] */
+#define WM8904_LIN_VOL_WIDTH 5 /* LIN_VOL - [4:0] */
+
+/*
+ * R45 (0x2D) - Analogue Right Input 0
+ */
+#define WM8904_RINMUTE 0x0080 /* RINMUTE */
+#define WM8904_RINMUTE_MASK 0x0080 /* RINMUTE */
+#define WM8904_RINMUTE_SHIFT 7 /* RINMUTE */
+#define WM8904_RINMUTE_WIDTH 1 /* RINMUTE */
+#define WM8904_RIN_VOL_MASK 0x001F /* RIN_VOL - [4:0] */
+#define WM8904_RIN_VOL_SHIFT 0 /* RIN_VOL - [4:0] */
+#define WM8904_RIN_VOL_WIDTH 5 /* RIN_VOL - [4:0] */
+
+/*
+ * R46 (0x2E) - Analogue Left Input 1
+ */
+#define WM8904_INL_CM_ENA 0x0040 /* INL_CM_ENA */
+#define WM8904_INL_CM_ENA_MASK 0x0040 /* INL_CM_ENA */
+#define WM8904_INL_CM_ENA_SHIFT 6 /* INL_CM_ENA */
+#define WM8904_INL_CM_ENA_WIDTH 1 /* INL_CM_ENA */
+#define WM8904_L_IP_SEL_N_MASK 0x0030 /* L_IP_SEL_N - [5:4] */
+#define WM8904_L_IP_SEL_N_SHIFT 4 /* L_IP_SEL_N - [5:4] */
+#define WM8904_L_IP_SEL_N_WIDTH 2 /* L_IP_SEL_N - [5:4] */
+#define WM8904_L_IP_SEL_P_MASK 0x000C /* L_IP_SEL_P - [3:2] */
+#define WM8904_L_IP_SEL_P_SHIFT 2 /* L_IP_SEL_P - [3:2] */
+#define WM8904_L_IP_SEL_P_WIDTH 2 /* L_IP_SEL_P - [3:2] */
+#define WM8904_L_MODE_MASK 0x0003 /* L_MODE - [1:0] */
+#define WM8904_L_MODE_SHIFT 0 /* L_MODE - [1:0] */
+#define WM8904_L_MODE_WIDTH 2 /* L_MODE - [1:0] */
+
+/*
+ * R47 (0x2F) - Analogue Right Input 1
+ */
+#define WM8904_INR_CM_ENA 0x0040 /* INR_CM_ENA */
+#define WM8904_INR_CM_ENA_MASK 0x0040 /* INR_CM_ENA */
+#define WM8904_INR_CM_ENA_SHIFT 6 /* INR_CM_ENA */
+#define WM8904_INR_CM_ENA_WIDTH 1 /* INR_CM_ENA */
+#define WM8904_R_IP_SEL_N_MASK 0x0030 /* R_IP_SEL_N - [5:4] */
+#define WM8904_R_IP_SEL_N_SHIFT 4 /* R_IP_SEL_N - [5:4] */
+#define WM8904_R_IP_SEL_N_WIDTH 2 /* R_IP_SEL_N - [5:4] */
+#define WM8904_R_IP_SEL_P_MASK 0x000C /* R_IP_SEL_P - [3:2] */
+#define WM8904_R_IP_SEL_P_SHIFT 2 /* R_IP_SEL_P - [3:2] */
+#define WM8904_R_IP_SEL_P_WIDTH 2 /* R_IP_SEL_P - [3:2] */
+#define WM8904_R_MODE_MASK 0x0003 /* R_MODE - [1:0] */
+#define WM8904_R_MODE_SHIFT 0 /* R_MODE - [1:0] */
+#define WM8904_R_MODE_WIDTH 2 /* R_MODE - [1:0] */
+
+/*
+ * R57 (0x39) - Analogue OUT1 Left
+ */
+#define WM8904_HPOUTL_MUTE 0x0100 /* HPOUTL_MUTE */
+#define WM8904_HPOUTL_MUTE_MASK 0x0100 /* HPOUTL_MUTE */
+#define WM8904_HPOUTL_MUTE_SHIFT 8 /* HPOUTL_MUTE */
+#define WM8904_HPOUTL_MUTE_WIDTH 1 /* HPOUTL_MUTE */
+#define WM8904_HPOUT_VU 0x0080 /* HPOUT_VU */
+#define WM8904_HPOUT_VU_MASK 0x0080 /* HPOUT_VU */
+#define WM8904_HPOUT_VU_SHIFT 7 /* HPOUT_VU */
+#define WM8904_HPOUT_VU_WIDTH 1 /* HPOUT_VU */
+#define WM8904_HPOUTLZC 0x0040 /* HPOUTLZC */
+#define WM8904_HPOUTLZC_MASK 0x0040 /* HPOUTLZC */
+#define WM8904_HPOUTLZC_SHIFT 6 /* HPOUTLZC */
+#define WM8904_HPOUTLZC_WIDTH 1 /* HPOUTLZC */
+#define WM8904_HPOUTL_VOL_MASK 0x003F /* HPOUTL_VOL - [5:0] */
+#define WM8904_HPOUTL_VOL_SHIFT 0 /* HPOUTL_VOL - [5:0] */
+#define WM8904_HPOUTL_VOL_WIDTH 6 /* HPOUTL_VOL - [5:0] */
+
+/*
+ * R58 (0x3A) - Analogue OUT1 Right
+ */
+#define WM8904_HPOUTR_MUTE 0x0100 /* HPOUTR_MUTE */
+#define WM8904_HPOUTR_MUTE_MASK 0x0100 /* HPOUTR_MUTE */
+#define WM8904_HPOUTR_MUTE_SHIFT 8 /* HPOUTR_MUTE */
+#define WM8904_HPOUTR_MUTE_WIDTH 1 /* HPOUTR_MUTE */
+#define WM8904_HPOUT_VU 0x0080 /* HPOUT_VU */
+#define WM8904_HPOUT_VU_MASK 0x0080 /* HPOUT_VU */
+#define WM8904_HPOUT_VU_SHIFT 7 /* HPOUT_VU */
+#define WM8904_HPOUT_VU_WIDTH 1 /* HPOUT_VU */
+#define WM8904_HPOUTRZC 0x0040 /* HPOUTRZC */
+#define WM8904_HPOUTRZC_MASK 0x0040 /* HPOUTRZC */
+#define WM8904_HPOUTRZC_SHIFT 6 /* HPOUTRZC */
+#define WM8904_HPOUTRZC_WIDTH 1 /* HPOUTRZC */
+#define WM8904_HPOUTR_VOL_MASK 0x003F /* HPOUTR_VOL - [5:0] */
+#define WM8904_HPOUTR_VOL_SHIFT 0 /* HPOUTR_VOL - [5:0] */
+#define WM8904_HPOUTR_VOL_WIDTH 6 /* HPOUTR_VOL - [5:0] */
+
+/*
+ * R59 (0x3B) - Analogue OUT2 Left
+ */
+#define WM8904_LINEOUTL_MUTE 0x0100 /* LINEOUTL_MUTE */
+#define WM8904_LINEOUTL_MUTE_MASK 0x0100 /* LINEOUTL_MUTE */
+#define WM8904_LINEOUTL_MUTE_SHIFT 8 /* LINEOUTL_MUTE */
+#define WM8904_LINEOUTL_MUTE_WIDTH 1 /* LINEOUTL_MUTE */
+#define WM8904_LINEOUT_VU 0x0080 /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_MASK 0x0080 /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_SHIFT 7 /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_WIDTH 1 /* LINEOUT_VU */
+#define WM8904_LINEOUTLZC 0x0040 /* LINEOUTLZC */
+#define WM8904_LINEOUTLZC_MASK 0x0040 /* LINEOUTLZC */
+#define WM8904_LINEOUTLZC_SHIFT 6 /* LINEOUTLZC */
+#define WM8904_LINEOUTLZC_WIDTH 1 /* LINEOUTLZC */
+#define WM8904_LINEOUTL_VOL_MASK 0x003F /* LINEOUTL_VOL - [5:0] */
+#define WM8904_LINEOUTL_VOL_SHIFT 0 /* LINEOUTL_VOL - [5:0] */
+#define WM8904_LINEOUTL_VOL_WIDTH 6 /* LINEOUTL_VOL - [5:0] */
+
+/*
+ * R60 (0x3C) - Analogue OUT2 Right
+ */
+#define WM8904_LINEOUTR_MUTE 0x0100 /* LINEOUTR_MUTE */
+#define WM8904_LINEOUTR_MUTE_MASK 0x0100 /* LINEOUTR_MUTE */
+#define WM8904_LINEOUTR_MUTE_SHIFT 8 /* LINEOUTR_MUTE */
+#define WM8904_LINEOUTR_MUTE_WIDTH 1 /* LINEOUTR_MUTE */
+#define WM8904_LINEOUT_VU 0x0080 /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_MASK 0x0080 /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_SHIFT 7 /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_WIDTH 1 /* LINEOUT_VU */
+#define WM8904_LINEOUTRZC 0x0040 /* LINEOUTRZC */
+#define WM8904_LINEOUTRZC_MASK 0x0040 /* LINEOUTRZC */
+#define WM8904_LINEOUTRZC_SHIFT 6 /* LINEOUTRZC */
+#define WM8904_LINEOUTRZC_WIDTH 1 /* LINEOUTRZC */
+#define WM8904_LINEOUTR_VOL_MASK 0x003F /* LINEOUTR_VOL - [5:0] */
+#define WM8904_LINEOUTR_VOL_SHIFT 0 /* LINEOUTR_VOL - [5:0] */
+#define WM8904_LINEOUTR_VOL_WIDTH 6 /* LINEOUTR_VOL - [5:0] */
+
+/*
+ * R61 (0x3D) - Analogue OUT12 ZC
+ */
+#define WM8904_HPL_BYP_ENA 0x0008 /* HPL_BYP_ENA */
+#define WM8904_HPL_BYP_ENA_MASK 0x0008 /* HPL_BYP_ENA */
+#define WM8904_HPL_BYP_ENA_SHIFT 3 /* HPL_BYP_ENA */
+#define WM8904_HPL_BYP_ENA_WIDTH 1 /* HPL_BYP_ENA */
+#define WM8904_HPR_BYP_ENA 0x0004 /* HPR_BYP_ENA */
+#define WM8904_HPR_BYP_ENA_MASK 0x0004 /* HPR_BYP_ENA */
+#define WM8904_HPR_BYP_ENA_SHIFT 2 /* HPR_BYP_ENA */
+#define WM8904_HPR_BYP_ENA_WIDTH 1 /* HPR_BYP_ENA */
+#define WM8904_LINEOUTL_BYP_ENA 0x0002 /* LINEOUTL_BYP_ENA */
+#define WM8904_LINEOUTL_BYP_ENA_MASK 0x0002 /* LINEOUTL_BYP_ENA */
+#define WM8904_LINEOUTL_BYP_ENA_SHIFT 1 /* LINEOUTL_BYP_ENA */
+#define WM8904_LINEOUTL_BYP_ENA_WIDTH 1 /* LINEOUTL_BYP_ENA */
+#define WM8904_LINEOUTR_BYP_ENA 0x0001 /* LINEOUTR_BYP_ENA */
+#define WM8904_LINEOUTR_BYP_ENA_MASK 0x0001 /* LINEOUTR_BYP_ENA */
+#define WM8904_LINEOUTR_BYP_ENA_SHIFT 0 /* LINEOUTR_BYP_ENA */
+#define WM8904_LINEOUTR_BYP_ENA_WIDTH 1 /* LINEOUTR_BYP_ENA */
+
+/*
+ * R67 (0x43) - DC Servo 0
+ */
+#define WM8904_DCS_ENA_CHAN_3 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8904_DCS_ENA_CHAN_3_MASK 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8904_DCS_ENA_CHAN_3_SHIFT 3 /* DCS_ENA_CHAN_3 */
+#define WM8904_DCS_ENA_CHAN_3_WIDTH 1 /* DCS_ENA_CHAN_3 */
+#define WM8904_DCS_ENA_CHAN_2 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8904_DCS_ENA_CHAN_2_MASK 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8904_DCS_ENA_CHAN_2_SHIFT 2 /* DCS_ENA_CHAN_2 */
+#define WM8904_DCS_ENA_CHAN_2_WIDTH 1 /* DCS_ENA_CHAN_2 */
+#define WM8904_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8904_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8904_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */
+#define WM8904_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */
+#define WM8904_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8904_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8904_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */
+#define WM8904_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */
+
+/*
+ * R68 (0x44) - DC Servo 1
+ */
+#define WM8904_DCS_TRIG_SINGLE_3 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8904_DCS_TRIG_SINGLE_3_MASK 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8904_DCS_TRIG_SINGLE_3_SHIFT 15 /* DCS_TRIG_SINGLE_3 */
+#define WM8904_DCS_TRIG_SINGLE_3_WIDTH 1 /* DCS_TRIG_SINGLE_3 */
+#define WM8904_DCS_TRIG_SINGLE_2 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8904_DCS_TRIG_SINGLE_2_MASK 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8904_DCS_TRIG_SINGLE_2_SHIFT 14 /* DCS_TRIG_SINGLE_2 */
+#define WM8904_DCS_TRIG_SINGLE_2_WIDTH 1 /* DCS_TRIG_SINGLE_2 */
+#define WM8904_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8904_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8904_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */
+#define WM8904_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */
+#define WM8904_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8904_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8904_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */
+#define WM8904_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */
+#define WM8904_DCS_TRIG_SERIES_3 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8904_DCS_TRIG_SERIES_3_MASK 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8904_DCS_TRIG_SERIES_3_SHIFT 11 /* DCS_TRIG_SERIES_3 */
+#define WM8904_DCS_TRIG_SERIES_3_WIDTH 1 /* DCS_TRIG_SERIES_3 */
+#define WM8904_DCS_TRIG_SERIES_2 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8904_DCS_TRIG_SERIES_2_MASK 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8904_DCS_TRIG_SERIES_2_SHIFT 10 /* DCS_TRIG_SERIES_2 */
+#define WM8904_DCS_TRIG_SERIES_2_WIDTH 1 /* DCS_TRIG_SERIES_2 */
+#define WM8904_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8904_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8904_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */
+#define WM8904_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */
+#define WM8904_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8904_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8904_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */
+#define WM8904_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */
+#define WM8904_DCS_TRIG_STARTUP_3 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8904_DCS_TRIG_STARTUP_3_MASK 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8904_DCS_TRIG_STARTUP_3_SHIFT 7 /* DCS_TRIG_STARTUP_3 */
+#define WM8904_DCS_TRIG_STARTUP_3_WIDTH 1 /* DCS_TRIG_STARTUP_3 */
+#define WM8904_DCS_TRIG_STARTUP_2 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8904_DCS_TRIG_STARTUP_2_MASK 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8904_DCS_TRIG_STARTUP_2_SHIFT 6 /* DCS_TRIG_STARTUP_2 */
+#define WM8904_DCS_TRIG_STARTUP_2_WIDTH 1 /* DCS_TRIG_STARTUP_2 */
+#define WM8904_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8904_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8904_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */
+#define WM8904_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */
+#define WM8904_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8904_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8904_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */
+#define WM8904_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */
+#define WM8904_DCS_TRIG_DAC_WR_3 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8904_DCS_TRIG_DAC_WR_3_MASK 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8904_DCS_TRIG_DAC_WR_3_SHIFT 3 /* DCS_TRIG_DAC_WR_3 */
+#define WM8904_DCS_TRIG_DAC_WR_3_WIDTH 1 /* DCS_TRIG_DAC_WR_3 */
+#define WM8904_DCS_TRIG_DAC_WR_2 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8904_DCS_TRIG_DAC_WR_2_MASK 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8904_DCS_TRIG_DAC_WR_2_SHIFT 2 /* DCS_TRIG_DAC_WR_2 */
+#define WM8904_DCS_TRIG_DAC_WR_2_WIDTH 1 /* DCS_TRIG_DAC_WR_2 */
+#define WM8904_DCS_TRIG_DAC_WR_1 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8904_DCS_TRIG_DAC_WR_1_MASK 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8904_DCS_TRIG_DAC_WR_1_SHIFT 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8904_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8904_DCS_TRIG_DAC_WR_0 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8904_DCS_TRIG_DAC_WR_0_MASK 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8904_DCS_TRIG_DAC_WR_0_SHIFT 0 /* DCS_TRIG_DAC_WR_0 */
+#define WM8904_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */
+
+/*
+ * R69 (0x45) - DC Servo 2
+ */
+#define WM8904_DCS_TIMER_PERIOD_23_MASK 0x0F00 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8904_DCS_TIMER_PERIOD_23_SHIFT 8 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8904_DCS_TIMER_PERIOD_23_WIDTH 4 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8904_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8904_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8904_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R71 (0x47) - DC Servo 4
+ */
+#define WM8904_DCS_SERIES_NO_23_MASK 0x007F /* DCS_SERIES_NO_23 - [6:0] */
+#define WM8904_DCS_SERIES_NO_23_SHIFT 0 /* DCS_SERIES_NO_23 - [6:0] */
+#define WM8904_DCS_SERIES_NO_23_WIDTH 7 /* DCS_SERIES_NO_23 - [6:0] */
+
+/*
+ * R72 (0x48) - DC Servo 5
+ */
+#define WM8904_DCS_SERIES_NO_01_MASK 0x007F /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8904_DCS_SERIES_NO_01_SHIFT 0 /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8904_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [6:0] */
+
+/*
+ * R73 (0x49) - DC Servo 6
+ */
+#define WM8904_DCS_DAC_WR_VAL_3_MASK 0x00FF /* DCS_DAC_WR_VAL_3 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_3_SHIFT 0 /* DCS_DAC_WR_VAL_3 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_3_WIDTH 8 /* DCS_DAC_WR_VAL_3 - [7:0] */
+
+/*
+ * R74 (0x4A) - DC Servo 7
+ */
+#define WM8904_DCS_DAC_WR_VAL_2_MASK 0x00FF /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_2_SHIFT 0 /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_2_WIDTH 8 /* DCS_DAC_WR_VAL_2 - [7:0] */
+
+/*
+ * R75 (0x4B) - DC Servo 8
+ */
+#define WM8904_DCS_DAC_WR_VAL_1_MASK 0x00FF /* DCS_DAC_WR_VAL_1 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_1_SHIFT 0 /* DCS_DAC_WR_VAL_1 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [7:0] */
+
+/*
+ * R76 (0x4C) - DC Servo 9
+ */
+#define WM8904_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R77 (0x4D) - DC Servo Readback 0
+ */
+#define WM8904_DCS_CAL_COMPLETE_MASK 0x0F00 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8904_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8904_DCS_CAL_COMPLETE_WIDTH 4 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8904_DCS_DAC_WR_COMPLETE_MASK 0x00F0 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8904_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8904_DCS_DAC_WR_COMPLETE_WIDTH 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8904_DCS_STARTUP_COMPLETE_MASK 0x000F /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8904_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8904_DCS_STARTUP_COMPLETE_WIDTH 4 /* DCS_STARTUP_COMPLETE - [3:0] */
+
+/*
+ * R90 (0x5A) - Analogue HP 0
+ */
+#define WM8904_HPL_RMV_SHORT 0x0080 /* HPL_RMV_SHORT */
+#define WM8904_HPL_RMV_SHORT_MASK 0x0080 /* HPL_RMV_SHORT */
+#define WM8904_HPL_RMV_SHORT_SHIFT 7 /* HPL_RMV_SHORT */
+#define WM8904_HPL_RMV_SHORT_WIDTH 1 /* HPL_RMV_SHORT */
+#define WM8904_HPL_ENA_OUTP 0x0040 /* HPL_ENA_OUTP */
+#define WM8904_HPL_ENA_OUTP_MASK 0x0040 /* HPL_ENA_OUTP */
+#define WM8904_HPL_ENA_OUTP_SHIFT 6 /* HPL_ENA_OUTP */
+#define WM8904_HPL_ENA_OUTP_WIDTH 1 /* HPL_ENA_OUTP */
+#define WM8904_HPL_ENA_DLY 0x0020 /* HPL_ENA_DLY */
+#define WM8904_HPL_ENA_DLY_MASK 0x0020 /* HPL_ENA_DLY */
+#define WM8904_HPL_ENA_DLY_SHIFT 5 /* HPL_ENA_DLY */
+#define WM8904_HPL_ENA_DLY_WIDTH 1 /* HPL_ENA_DLY */
+#define WM8904_HPL_ENA 0x0010 /* HPL_ENA */
+#define WM8904_HPL_ENA_MASK 0x0010 /* HPL_ENA */
+#define WM8904_HPL_ENA_SHIFT 4 /* HPL_ENA */
+#define WM8904_HPL_ENA_WIDTH 1 /* HPL_ENA */
+#define WM8904_HPR_RMV_SHORT 0x0008 /* HPR_RMV_SHORT */
+#define WM8904_HPR_RMV_SHORT_MASK 0x0008 /* HPR_RMV_SHORT */
+#define WM8904_HPR_RMV_SHORT_SHIFT 3 /* HPR_RMV_SHORT */
+#define WM8904_HPR_RMV_SHORT_WIDTH 1 /* HPR_RMV_SHORT */
+#define WM8904_HPR_ENA_OUTP 0x0004 /* HPR_ENA_OUTP */
+#define WM8904_HPR_ENA_OUTP_MASK 0x0004 /* HPR_ENA_OUTP */
+#define WM8904_HPR_ENA_OUTP_SHIFT 2 /* HPR_ENA_OUTP */
+#define WM8904_HPR_ENA_OUTP_WIDTH 1 /* HPR_ENA_OUTP */
+#define WM8904_HPR_ENA_DLY 0x0002 /* HPR_ENA_DLY */
+#define WM8904_HPR_ENA_DLY_MASK 0x0002 /* HPR_ENA_DLY */
+#define WM8904_HPR_ENA_DLY_SHIFT 1 /* HPR_ENA_DLY */
+#define WM8904_HPR_ENA_DLY_WIDTH 1 /* HPR_ENA_DLY */
+#define WM8904_HPR_ENA 0x0001 /* HPR_ENA */
+#define WM8904_HPR_ENA_MASK 0x0001 /* HPR_ENA */
+#define WM8904_HPR_ENA_SHIFT 0 /* HPR_ENA */
+#define WM8904_HPR_ENA_WIDTH 1 /* HPR_ENA */
+
+/*
+ * R94 (0x5E) - Analogue Lineout 0
+ */
+#define WM8904_LINEOUTL_RMV_SHORT 0x0080 /* LINEOUTL_RMV_SHORT */
+#define WM8904_LINEOUTL_RMV_SHORT_MASK 0x0080 /* LINEOUTL_RMV_SHORT */
+#define WM8904_LINEOUTL_RMV_SHORT_SHIFT 7 /* LINEOUTL_RMV_SHORT */
+#define WM8904_LINEOUTL_RMV_SHORT_WIDTH 1 /* LINEOUTL_RMV_SHORT */
+#define WM8904_LINEOUTL_ENA_OUTP 0x0040 /* LINEOUTL_ENA_OUTP */
+#define WM8904_LINEOUTL_ENA_OUTP_MASK 0x0040 /* LINEOUTL_ENA_OUTP */
+#define WM8904_LINEOUTL_ENA_OUTP_SHIFT 6 /* LINEOUTL_ENA_OUTP */
+#define WM8904_LINEOUTL_ENA_OUTP_WIDTH 1 /* LINEOUTL_ENA_OUTP */
+#define WM8904_LINEOUTL_ENA_DLY 0x0020 /* LINEOUTL_ENA_DLY */
+#define WM8904_LINEOUTL_ENA_DLY_MASK 0x0020 /* LINEOUTL_ENA_DLY */
+#define WM8904_LINEOUTL_ENA_DLY_SHIFT 5 /* LINEOUTL_ENA_DLY */
+#define WM8904_LINEOUTL_ENA_DLY_WIDTH 1 /* LINEOUTL_ENA_DLY */
+#define WM8904_LINEOUTL_ENA 0x0010 /* LINEOUTL_ENA */
+#define WM8904_LINEOUTL_ENA_MASK 0x0010 /* LINEOUTL_ENA */
+#define WM8904_LINEOUTL_ENA_SHIFT 4 /* LINEOUTL_ENA */
+#define WM8904_LINEOUTL_ENA_WIDTH 1 /* LINEOUTL_ENA */
+#define WM8904_LINEOUTR_RMV_SHORT 0x0008 /* LINEOUTR_RMV_SHORT */
+#define WM8904_LINEOUTR_RMV_SHORT_MASK 0x0008 /* LINEOUTR_RMV_SHORT */
+#define WM8904_LINEOUTR_RMV_SHORT_SHIFT 3 /* LINEOUTR_RMV_SHORT */
+#define WM8904_LINEOUTR_RMV_SHORT_WIDTH 1 /* LINEOUTR_RMV_SHORT */
+#define WM8904_LINEOUTR_ENA_OUTP 0x0004 /* LINEOUTR_ENA_OUTP */
+#define WM8904_LINEOUTR_ENA_OUTP_MASK 0x0004 /* LINEOUTR_ENA_OUTP */
+#define WM8904_LINEOUTR_ENA_OUTP_SHIFT 2 /* LINEOUTR_ENA_OUTP */
+#define WM8904_LINEOUTR_ENA_OUTP_WIDTH 1 /* LINEOUTR_ENA_OUTP */
+#define WM8904_LINEOUTR_ENA_DLY 0x0002 /* LINEOUTR_ENA_DLY */
+#define WM8904_LINEOUTR_ENA_DLY_MASK 0x0002 /* LINEOUTR_ENA_DLY */
+#define WM8904_LINEOUTR_ENA_DLY_SHIFT 1 /* LINEOUTR_ENA_DLY */
+#define WM8904_LINEOUTR_ENA_DLY_WIDTH 1 /* LINEOUTR_ENA_DLY */
+#define WM8904_LINEOUTR_ENA 0x0001 /* LINEOUTR_ENA */
+#define WM8904_LINEOUTR_ENA_MASK 0x0001 /* LINEOUTR_ENA */
+#define WM8904_LINEOUTR_ENA_SHIFT 0 /* LINEOUTR_ENA */
+#define WM8904_LINEOUTR_ENA_WIDTH 1 /* LINEOUTR_ENA */
+
+/*
+ * R98 (0x62) - Charge Pump 0
+ */
+#define WM8904_CP_ENA 0x0001 /* CP_ENA */
+#define WM8904_CP_ENA_MASK 0x0001 /* CP_ENA */
+#define WM8904_CP_ENA_SHIFT 0 /* CP_ENA */
+#define WM8904_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R104 (0x68) - Class W 0
+ */
+#define WM8904_CP_DYN_PWR 0x0001 /* CP_DYN_PWR */
+#define WM8904_CP_DYN_PWR_MASK 0x0001 /* CP_DYN_PWR */
+#define WM8904_CP_DYN_PWR_SHIFT 0 /* CP_DYN_PWR */
+#define WM8904_CP_DYN_PWR_WIDTH 1 /* CP_DYN_PWR */
+
+/*
+ * R108 (0x6C) - Write Sequencer 0
+ */
+#define WM8904_WSEQ_ENA 0x0100 /* WSEQ_ENA */
+#define WM8904_WSEQ_ENA_MASK 0x0100 /* WSEQ_ENA */
+#define WM8904_WSEQ_ENA_SHIFT 8 /* WSEQ_ENA */
+#define WM8904_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM8904_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8904_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8904_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R109 (0x6D) - Write Sequencer 1
+ */
+#define WM8904_WSEQ_DATA_WIDTH_MASK 0x7000 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8904_WSEQ_DATA_WIDTH_SHIFT 12 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8904_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8904_WSEQ_DATA_START_MASK 0x0F00 /* WSEQ_DATA_START - [11:8] */
+#define WM8904_WSEQ_DATA_START_SHIFT 8 /* WSEQ_DATA_START - [11:8] */
+#define WM8904_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [11:8] */
+#define WM8904_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */
+#define WM8904_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */
+#define WM8904_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R110 (0x6E) - Write Sequencer 2
+ */
+#define WM8904_WSEQ_EOS 0x4000 /* WSEQ_EOS */
+#define WM8904_WSEQ_EOS_MASK 0x4000 /* WSEQ_EOS */
+#define WM8904_WSEQ_EOS_SHIFT 14 /* WSEQ_EOS */
+#define WM8904_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */
+#define WM8904_WSEQ_DELAY_MASK 0x0F00 /* WSEQ_DELAY - [11:8] */
+#define WM8904_WSEQ_DELAY_SHIFT 8 /* WSEQ_DELAY - [11:8] */
+#define WM8904_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [11:8] */
+#define WM8904_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */
+#define WM8904_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */
+#define WM8904_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */
+
+/*
+ * R111 (0x6F) - Write Sequencer 3
+ */
+#define WM8904_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM8904_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM8904_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM8904_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8904_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM8904_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM8904_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM8904_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8904_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */
+#define WM8904_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */
+#define WM8904_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R112 (0x70) - Write Sequencer 4
+ */
+#define WM8904_WSEQ_CURRENT_INDEX_MASK 0x03F0 /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8904_WSEQ_CURRENT_INDEX_SHIFT 4 /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8904_WSEQ_CURRENT_INDEX_WIDTH 6 /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8904_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */
+#define WM8904_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */
+#define WM8904_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */
+#define WM8904_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+
+/*
+ * R116 (0x74) - FLL Control 1
+ */
+#define WM8904_FLL_FRACN_ENA 0x0004 /* FLL_FRACN_ENA */
+#define WM8904_FLL_FRACN_ENA_MASK 0x0004 /* FLL_FRACN_ENA */
+#define WM8904_FLL_FRACN_ENA_SHIFT 2 /* FLL_FRACN_ENA */
+#define WM8904_FLL_FRACN_ENA_WIDTH 1 /* FLL_FRACN_ENA */
+#define WM8904_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */
+#define WM8904_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */
+#define WM8904_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */
+#define WM8904_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */
+#define WM8904_FLL_ENA 0x0001 /* FLL_ENA */
+#define WM8904_FLL_ENA_MASK 0x0001 /* FLL_ENA */
+#define WM8904_FLL_ENA_SHIFT 0 /* FLL_ENA */
+#define WM8904_FLL_ENA_WIDTH 1 /* FLL_ENA */
+
+/*
+ * R117 (0x75) - FLL Control 2
+ */
+#define WM8904_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */
+#define WM8904_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */
+#define WM8904_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */
+#define WM8904_FLL_CTRL_RATE_MASK 0x0070 /* FLL_CTRL_RATE - [6:4] */
+#define WM8904_FLL_CTRL_RATE_SHIFT 4 /* FLL_CTRL_RATE - [6:4] */
+#define WM8904_FLL_CTRL_RATE_WIDTH 3 /* FLL_CTRL_RATE - [6:4] */
+#define WM8904_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
+#define WM8904_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
+#define WM8904_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
+
+/*
+ * R118 (0x76) - FLL Control 3
+ */
+#define WM8904_FLL_K_MASK 0xFFFF /* FLL_K - [15:0] */
+#define WM8904_FLL_K_SHIFT 0 /* FLL_K - [15:0] */
+#define WM8904_FLL_K_WIDTH 16 /* FLL_K - [15:0] */
+
+/*
+ * R119 (0x77) - FLL Control 4
+ */
+#define WM8904_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */
+#define WM8904_FLL_N_SHIFT 5 /* FLL_N - [14:5] */
+#define WM8904_FLL_N_WIDTH 10 /* FLL_N - [14:5] */
+#define WM8904_FLL_GAIN_MASK 0x000F /* FLL_GAIN - [3:0] */
+#define WM8904_FLL_GAIN_SHIFT 0 /* FLL_GAIN - [3:0] */
+#define WM8904_FLL_GAIN_WIDTH 4 /* FLL_GAIN - [3:0] */
+
+/*
+ * R120 (0x78) - FLL Control 5
+ */
+#define WM8904_FLL_CLK_REF_DIV_MASK 0x0018 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8904_FLL_CLK_REF_DIV_SHIFT 3 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8904_FLL_CLK_REF_DIV_WIDTH 2 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8904_FLL_CLK_REF_SRC_MASK 0x0003 /* FLL_CLK_REF_SRC - [1:0] */
+#define WM8904_FLL_CLK_REF_SRC_SHIFT 0 /* FLL_CLK_REF_SRC - [1:0] */
+#define WM8904_FLL_CLK_REF_SRC_WIDTH 2 /* FLL_CLK_REF_SRC - [1:0] */
+
+/*
+ * R126 (0x7E) - Digital Pulls
+ */
+#define WM8904_MCLK_PU 0x0080 /* MCLK_PU */
+#define WM8904_MCLK_PU_MASK 0x0080 /* MCLK_PU */
+#define WM8904_MCLK_PU_SHIFT 7 /* MCLK_PU */
+#define WM8904_MCLK_PU_WIDTH 1 /* MCLK_PU */
+#define WM8904_MCLK_PD 0x0040 /* MCLK_PD */
+#define WM8904_MCLK_PD_MASK 0x0040 /* MCLK_PD */
+#define WM8904_MCLK_PD_SHIFT 6 /* MCLK_PD */
+#define WM8904_MCLK_PD_WIDTH 1 /* MCLK_PD */
+#define WM8904_DACDAT_PU 0x0020 /* DACDAT_PU */
+#define WM8904_DACDAT_PU_MASK 0x0020 /* DACDAT_PU */
+#define WM8904_DACDAT_PU_SHIFT 5 /* DACDAT_PU */
+#define WM8904_DACDAT_PU_WIDTH 1 /* DACDAT_PU */
+#define WM8904_DACDAT_PD 0x0010 /* DACDAT_PD */
+#define WM8904_DACDAT_PD_MASK 0x0010 /* DACDAT_PD */
+#define WM8904_DACDAT_PD_SHIFT 4 /* DACDAT_PD */
+#define WM8904_DACDAT_PD_WIDTH 1 /* DACDAT_PD */
+#define WM8904_LRCLK_PU 0x0008 /* LRCLK_PU */
+#define WM8904_LRCLK_PU_MASK 0x0008 /* LRCLK_PU */
+#define WM8904_LRCLK_PU_SHIFT 3 /* LRCLK_PU */
+#define WM8904_LRCLK_PU_WIDTH 1 /* LRCLK_PU */
+#define WM8904_LRCLK_PD 0x0004 /* LRCLK_PD */
+#define WM8904_LRCLK_PD_MASK 0x0004 /* LRCLK_PD */
+#define WM8904_LRCLK_PD_SHIFT 2 /* LRCLK_PD */
+#define WM8904_LRCLK_PD_WIDTH 1 /* LRCLK_PD */
+#define WM8904_BCLK_PU 0x0002 /* BCLK_PU */
+#define WM8904_BCLK_PU_MASK 0x0002 /* BCLK_PU */
+#define WM8904_BCLK_PU_SHIFT 1 /* BCLK_PU */
+#define WM8904_BCLK_PU_WIDTH 1 /* BCLK_PU */
+#define WM8904_BCLK_PD 0x0001 /* BCLK_PD */
+#define WM8904_BCLK_PD_MASK 0x0001 /* BCLK_PD */
+#define WM8904_BCLK_PD_SHIFT 0 /* BCLK_PD */
+#define WM8904_BCLK_PD_WIDTH 1 /* BCLK_PD */
+
+/*
+ * R127 (0x7F) - Interrupt Status
+ */
+#define WM8904_IRQ 0x0400 /* IRQ */
+#define WM8904_IRQ_MASK 0x0400 /* IRQ */
+#define WM8904_IRQ_SHIFT 10 /* IRQ */
+#define WM8904_IRQ_WIDTH 1 /* IRQ */
+#define WM8904_GPIO_BCLK_EINT 0x0200 /* GPIO_BCLK_EINT */
+#define WM8904_GPIO_BCLK_EINT_MASK 0x0200 /* GPIO_BCLK_EINT */
+#define WM8904_GPIO_BCLK_EINT_SHIFT 9 /* GPIO_BCLK_EINT */
+#define WM8904_GPIO_BCLK_EINT_WIDTH 1 /* GPIO_BCLK_EINT */
+#define WM8904_WSEQ_EINT 0x0100 /* WSEQ_EINT */
+#define WM8904_WSEQ_EINT_MASK 0x0100 /* WSEQ_EINT */
+#define WM8904_WSEQ_EINT_SHIFT 8 /* WSEQ_EINT */
+#define WM8904_WSEQ_EINT_WIDTH 1 /* WSEQ_EINT */
+#define WM8904_GPIO3_EINT 0x0080 /* GPIO3_EINT */
+#define WM8904_GPIO3_EINT_MASK 0x0080 /* GPIO3_EINT */
+#define WM8904_GPIO3_EINT_SHIFT 7 /* GPIO3_EINT */
+#define WM8904_GPIO3_EINT_WIDTH 1 /* GPIO3_EINT */
+#define WM8904_GPIO2_EINT 0x0040 /* GPIO2_EINT */
+#define WM8904_GPIO2_EINT_MASK 0x0040 /* GPIO2_EINT */
+#define WM8904_GPIO2_EINT_SHIFT 6 /* GPIO2_EINT */
+#define WM8904_GPIO2_EINT_WIDTH 1 /* GPIO2_EINT */
+#define WM8904_GPIO1_EINT 0x0020 /* GPIO1_EINT */
+#define WM8904_GPIO1_EINT_MASK 0x0020 /* GPIO1_EINT */
+#define WM8904_GPIO1_EINT_SHIFT 5 /* GPIO1_EINT */
+#define WM8904_GPIO1_EINT_WIDTH 1 /* GPIO1_EINT */
+#define WM8904_GPI8_EINT 0x0010 /* GPI8_EINT */
+#define WM8904_GPI8_EINT_MASK 0x0010 /* GPI8_EINT */
+#define WM8904_GPI8_EINT_SHIFT 4 /* GPI8_EINT */
+#define WM8904_GPI8_EINT_WIDTH 1 /* GPI8_EINT */
+#define WM8904_GPI7_EINT 0x0008 /* GPI7_EINT */
+#define WM8904_GPI7_EINT_MASK 0x0008 /* GPI7_EINT */
+#define WM8904_GPI7_EINT_SHIFT 3 /* GPI7_EINT */
+#define WM8904_GPI7_EINT_WIDTH 1 /* GPI7_EINT */
+#define WM8904_FLL_LOCK_EINT 0x0004 /* FLL_LOCK_EINT */
+#define WM8904_FLL_LOCK_EINT_MASK 0x0004 /* FLL_LOCK_EINT */
+#define WM8904_FLL_LOCK_EINT_SHIFT 2 /* FLL_LOCK_EINT */
+#define WM8904_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */
+#define WM8904_MIC_SHRT_EINT 0x0002 /* MIC_SHRT_EINT */
+#define WM8904_MIC_SHRT_EINT_MASK 0x0002 /* MIC_SHRT_EINT */
+#define WM8904_MIC_SHRT_EINT_SHIFT 1 /* MIC_SHRT_EINT */
+#define WM8904_MIC_SHRT_EINT_WIDTH 1 /* MIC_SHRT_EINT */
+#define WM8904_MIC_DET_EINT 0x0001 /* MIC_DET_EINT */
+#define WM8904_MIC_DET_EINT_MASK 0x0001 /* MIC_DET_EINT */
+#define WM8904_MIC_DET_EINT_SHIFT 0 /* MIC_DET_EINT */
+#define WM8904_MIC_DET_EINT_WIDTH 1 /* MIC_DET_EINT */
+
+/*
+ * R128 (0x80) - Interrupt Status Mask
+ */
+#define WM8904_IM_GPIO_BCLK_EINT 0x0200 /* IM_GPIO_BCLK_EINT */
+#define WM8904_IM_GPIO_BCLK_EINT_MASK 0x0200 /* IM_GPIO_BCLK_EINT */
+#define WM8904_IM_GPIO_BCLK_EINT_SHIFT 9 /* IM_GPIO_BCLK_EINT */
+#define WM8904_IM_GPIO_BCLK_EINT_WIDTH 1 /* IM_GPIO_BCLK_EINT */
+#define WM8904_IM_WSEQ_EINT 0x0100 /* IM_WSEQ_EINT */
+#define WM8904_IM_WSEQ_EINT_MASK 0x0100 /* IM_WSEQ_EINT */
+#define WM8904_IM_WSEQ_EINT_SHIFT 8 /* IM_WSEQ_EINT */
+#define WM8904_IM_WSEQ_EINT_WIDTH 1 /* IM_WSEQ_EINT */
+#define WM8904_IM_GPIO3_EINT 0x0080 /* IM_GPIO3_EINT */
+#define WM8904_IM_GPIO3_EINT_MASK 0x0080 /* IM_GPIO3_EINT */
+#define WM8904_IM_GPIO3_EINT_SHIFT 7 /* IM_GPIO3_EINT */
+#define WM8904_IM_GPIO3_EINT_WIDTH 1 /* IM_GPIO3_EINT */
+#define WM8904_IM_GPIO2_EINT 0x0040 /* IM_GPIO2_EINT */
+#define WM8904_IM_GPIO2_EINT_MASK 0x0040 /* IM_GPIO2_EINT */
+#define WM8904_IM_GPIO2_EINT_SHIFT 6 /* IM_GPIO2_EINT */
+#define WM8904_IM_GPIO2_EINT_WIDTH 1 /* IM_GPIO2_EINT */
+#define WM8904_IM_GPIO1_EINT 0x0020 /* IM_GPIO1_EINT */
+#define WM8904_IM_GPIO1_EINT_MASK 0x0020 /* IM_GPIO1_EINT */
+#define WM8904_IM_GPIO1_EINT_SHIFT 5 /* IM_GPIO1_EINT */
+#define WM8904_IM_GPIO1_EINT_WIDTH 1 /* IM_GPIO1_EINT */
+#define WM8904_IM_GPI8_EINT 0x0010 /* IM_GPI8_EINT */
+#define WM8904_IM_GPI8_EINT_MASK 0x0010 /* IM_GPI8_EINT */
+#define WM8904_IM_GPI8_EINT_SHIFT 4 /* IM_GPI8_EINT */
+#define WM8904_IM_GPI8_EINT_WIDTH 1 /* IM_GPI8_EINT */
+#define WM8904_IM_GPI7_EINT 0x0008 /* IM_GPI7_EINT */
+#define WM8904_IM_GPI7_EINT_MASK 0x0008 /* IM_GPI7_EINT */
+#define WM8904_IM_GPI7_EINT_SHIFT 3 /* IM_GPI7_EINT */
+#define WM8904_IM_GPI7_EINT_WIDTH 1 /* IM_GPI7_EINT */
+#define WM8904_IM_FLL_LOCK_EINT 0x0004 /* IM_FLL_LOCK_EINT */
+#define WM8904_IM_FLL_LOCK_EINT_MASK 0x0004 /* IM_FLL_LOCK_EINT */
+#define WM8904_IM_FLL_LOCK_EINT_SHIFT 2 /* IM_FLL_LOCK_EINT */
+#define WM8904_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */
+#define WM8904_IM_MIC_SHRT_EINT 0x0002 /* IM_MIC_SHRT_EINT */
+#define WM8904_IM_MIC_SHRT_EINT_MASK 0x0002 /* IM_MIC_SHRT_EINT */
+#define WM8904_IM_MIC_SHRT_EINT_SHIFT 1 /* IM_MIC_SHRT_EINT */
+#define WM8904_IM_MIC_SHRT_EINT_WIDTH 1 /* IM_MIC_SHRT_EINT */
+#define WM8904_IM_MIC_DET_EINT 0x0001 /* IM_MIC_DET_EINT */
+#define WM8904_IM_MIC_DET_EINT_MASK 0x0001 /* IM_MIC_DET_EINT */
+#define WM8904_IM_MIC_DET_EINT_SHIFT 0 /* IM_MIC_DET_EINT */
+#define WM8904_IM_MIC_DET_EINT_WIDTH 1 /* IM_MIC_DET_EINT */
+
+/*
+ * R129 (0x81) - Interrupt Polarity
+ */
+#define WM8904_GPIO_BCLK_EINT_POL 0x0200 /* GPIO_BCLK_EINT_POL */
+#define WM8904_GPIO_BCLK_EINT_POL_MASK 0x0200 /* GPIO_BCLK_EINT_POL */
+#define WM8904_GPIO_BCLK_EINT_POL_SHIFT 9 /* GPIO_BCLK_EINT_POL */
+#define WM8904_GPIO_BCLK_EINT_POL_WIDTH 1 /* GPIO_BCLK_EINT_POL */
+#define WM8904_WSEQ_EINT_POL 0x0100 /* WSEQ_EINT_POL */
+#define WM8904_WSEQ_EINT_POL_MASK 0x0100 /* WSEQ_EINT_POL */
+#define WM8904_WSEQ_EINT_POL_SHIFT 8 /* WSEQ_EINT_POL */
+#define WM8904_WSEQ_EINT_POL_WIDTH 1 /* WSEQ_EINT_POL */
+#define WM8904_GPIO3_EINT_POL 0x0080 /* GPIO3_EINT_POL */
+#define WM8904_GPIO3_EINT_POL_MASK 0x0080 /* GPIO3_EINT_POL */
+#define WM8904_GPIO3_EINT_POL_SHIFT 7 /* GPIO3_EINT_POL */
+#define WM8904_GPIO3_EINT_POL_WIDTH 1 /* GPIO3_EINT_POL */
+#define WM8904_GPIO2_EINT_POL 0x0040 /* GPIO2_EINT_POL */
+#define WM8904_GPIO2_EINT_POL_MASK 0x0040 /* GPIO2_EINT_POL */
+#define WM8904_GPIO2_EINT_POL_SHIFT 6 /* GPIO2_EINT_POL */
+#define WM8904_GPIO2_EINT_POL_WIDTH 1 /* GPIO2_EINT_POL */
+#define WM8904_GPIO1_EINT_POL 0x0020 /* GPIO1_EINT_POL */
+#define WM8904_GPIO1_EINT_POL_MASK 0x0020 /* GPIO1_EINT_POL */
+#define WM8904_GPIO1_EINT_POL_SHIFT 5 /* GPIO1_EINT_POL */
+#define WM8904_GPIO1_EINT_POL_WIDTH 1 /* GPIO1_EINT_POL */
+#define WM8904_GPI8_EINT_POL 0x0010 /* GPI8_EINT_POL */
+#define WM8904_GPI8_EINT_POL_MASK 0x0010 /* GPI8_EINT_POL */
+#define WM8904_GPI8_EINT_POL_SHIFT 4 /* GPI8_EINT_POL */
+#define WM8904_GPI8_EINT_POL_WIDTH 1 /* GPI8_EINT_POL */
+#define WM8904_GPI7_EINT_POL 0x0008 /* GPI7_EINT_POL */
+#define WM8904_GPI7_EINT_POL_MASK 0x0008 /* GPI7_EINT_POL */
+#define WM8904_GPI7_EINT_POL_SHIFT 3 /* GPI7_EINT_POL */
+#define WM8904_GPI7_EINT_POL_WIDTH 1 /* GPI7_EINT_POL */
+#define WM8904_FLL_LOCK_EINT_POL 0x0004 /* FLL_LOCK_EINT_POL */
+#define WM8904_FLL_LOCK_EINT_POL_MASK 0x0004 /* FLL_LOCK_EINT_POL */
+#define WM8904_FLL_LOCK_EINT_POL_SHIFT 2 /* FLL_LOCK_EINT_POL */
+#define WM8904_FLL_LOCK_EINT_POL_WIDTH 1 /* FLL_LOCK_EINT_POL */
+#define WM8904_MIC_SHRT_EINT_POL 0x0002 /* MIC_SHRT_EINT_POL */
+#define WM8904_MIC_SHRT_EINT_POL_MASK 0x0002 /* MIC_SHRT_EINT_POL */
+#define WM8904_MIC_SHRT_EINT_POL_SHIFT 1 /* MIC_SHRT_EINT_POL */
+#define WM8904_MIC_SHRT_EINT_POL_WIDTH 1 /* MIC_SHRT_EINT_POL */
+#define WM8904_MIC_DET_EINT_POL 0x0001 /* MIC_DET_EINT_POL */
+#define WM8904_MIC_DET_EINT_POL_MASK 0x0001 /* MIC_DET_EINT_POL */
+#define WM8904_MIC_DET_EINT_POL_SHIFT 0 /* MIC_DET_EINT_POL */
+#define WM8904_MIC_DET_EINT_POL_WIDTH 1 /* MIC_DET_EINT_POL */
+
+/*
+ * R130 (0x82) - Interrupt Debounce
+ */
+#define WM8904_GPIO_BCLK_EINT_DB 0x0200 /* GPIO_BCLK_EINT_DB */
+#define WM8904_GPIO_BCLK_EINT_DB_MASK 0x0200 /* GPIO_BCLK_EINT_DB */
+#define WM8904_GPIO_BCLK_EINT_DB_SHIFT 9 /* GPIO_BCLK_EINT_DB */
+#define WM8904_GPIO_BCLK_EINT_DB_WIDTH 1 /* GPIO_BCLK_EINT_DB */
+#define WM8904_WSEQ_EINT_DB 0x0100 /* WSEQ_EINT_DB */
+#define WM8904_WSEQ_EINT_DB_MASK 0x0100 /* WSEQ_EINT_DB */
+#define WM8904_WSEQ_EINT_DB_SHIFT 8 /* WSEQ_EINT_DB */
+#define WM8904_WSEQ_EINT_DB_WIDTH 1 /* WSEQ_EINT_DB */
+#define WM8904_GPIO3_EINT_DB 0x0080 /* GPIO3_EINT_DB */
+#define WM8904_GPIO3_EINT_DB_MASK 0x0080 /* GPIO3_EINT_DB */
+#define WM8904_GPIO3_EINT_DB_SHIFT 7 /* GPIO3_EINT_DB */
+#define WM8904_GPIO3_EINT_DB_WIDTH 1 /* GPIO3_EINT_DB */
+#define WM8904_GPIO2_EINT_DB 0x0040 /* GPIO2_EINT_DB */
+#define WM8904_GPIO2_EINT_DB_MASK 0x0040 /* GPIO2_EINT_DB */
+#define WM8904_GPIO2_EINT_DB_SHIFT 6 /* GPIO2_EINT_DB */
+#define WM8904_GPIO2_EINT_DB_WIDTH 1 /* GPIO2_EINT_DB */
+#define WM8904_GPIO1_EINT_DB 0x0020 /* GPIO1_EINT_DB */
+#define WM8904_GPIO1_EINT_DB_MASK 0x0020 /* GPIO1_EINT_DB */
+#define WM8904_GPIO1_EINT_DB_SHIFT 5 /* GPIO1_EINT_DB */
+#define WM8904_GPIO1_EINT_DB_WIDTH 1 /* GPIO1_EINT_DB */
+#define WM8904_GPI8_EINT_DB 0x0010 /* GPI8_EINT_DB */
+#define WM8904_GPI8_EINT_DB_MASK 0x0010 /* GPI8_EINT_DB */
+#define WM8904_GPI8_EINT_DB_SHIFT 4 /* GPI8_EINT_DB */
+#define WM8904_GPI8_EINT_DB_WIDTH 1 /* GPI8_EINT_DB */
+#define WM8904_GPI7_EINT_DB 0x0008 /* GPI7_EINT_DB */
+#define WM8904_GPI7_EINT_DB_MASK 0x0008 /* GPI7_EINT_DB */
+#define WM8904_GPI7_EINT_DB_SHIFT 3 /* GPI7_EINT_DB */
+#define WM8904_GPI7_EINT_DB_WIDTH 1 /* GPI7_EINT_DB */
+#define WM8904_FLL_LOCK_EINT_DB 0x0004 /* FLL_LOCK_EINT_DB */
+#define WM8904_FLL_LOCK_EINT_DB_MASK 0x0004 /* FLL_LOCK_EINT_DB */
+#define WM8904_FLL_LOCK_EINT_DB_SHIFT 2 /* FLL_LOCK_EINT_DB */
+#define WM8904_FLL_LOCK_EINT_DB_WIDTH 1 /* FLL_LOCK_EINT_DB */
+#define WM8904_MIC_SHRT_EINT_DB 0x0002 /* MIC_SHRT_EINT_DB */
+#define WM8904_MIC_SHRT_EINT_DB_MASK 0x0002 /* MIC_SHRT_EINT_DB */
+#define WM8904_MIC_SHRT_EINT_DB_SHIFT 1 /* MIC_SHRT_EINT_DB */
+#define WM8904_MIC_SHRT_EINT_DB_WIDTH 1 /* MIC_SHRT_EINT_DB */
+#define WM8904_MIC_DET_EINT_DB 0x0001 /* MIC_DET_EINT_DB */
+#define WM8904_MIC_DET_EINT_DB_MASK 0x0001 /* MIC_DET_EINT_DB */
+#define WM8904_MIC_DET_EINT_DB_SHIFT 0 /* MIC_DET_EINT_DB */
+#define WM8904_MIC_DET_EINT_DB_WIDTH 1 /* MIC_DET_EINT_DB */
+
+/*
+ * R134 (0x86) - EQ1
+ */
+#define WM8904_EQ_ENA 0x0001 /* EQ_ENA */
+#define WM8904_EQ_ENA_MASK 0x0001 /* EQ_ENA */
+#define WM8904_EQ_ENA_SHIFT 0 /* EQ_ENA */
+#define WM8904_EQ_ENA_WIDTH 1 /* EQ_ENA */
+
+/*
+ * R135 (0x87) - EQ2
+ */
+#define WM8904_EQ_B1_GAIN_MASK 0x001F /* EQ_B1_GAIN - [4:0] */
+#define WM8904_EQ_B1_GAIN_SHIFT 0 /* EQ_B1_GAIN - [4:0] */
+#define WM8904_EQ_B1_GAIN_WIDTH 5 /* EQ_B1_GAIN - [4:0] */
+
+/*
+ * R136 (0x88) - EQ3
+ */
+#define WM8904_EQ_B2_GAIN_MASK 0x001F /* EQ_B2_GAIN - [4:0] */
+#define WM8904_EQ_B2_GAIN_SHIFT 0 /* EQ_B2_GAIN - [4:0] */
+#define WM8904_EQ_B2_GAIN_WIDTH 5 /* EQ_B2_GAIN - [4:0] */
+
+/*
+ * R137 (0x89) - EQ4
+ */
+#define WM8904_EQ_B3_GAIN_MASK 0x001F /* EQ_B3_GAIN - [4:0] */
+#define WM8904_EQ_B3_GAIN_SHIFT 0 /* EQ_B3_GAIN - [4:0] */
+#define WM8904_EQ_B3_GAIN_WIDTH 5 /* EQ_B3_GAIN - [4:0] */
+
+/*
+ * R138 (0x8A) - EQ5
+ */
+#define WM8904_EQ_B4_GAIN_MASK 0x001F /* EQ_B4_GAIN - [4:0] */
+#define WM8904_EQ_B4_GAIN_SHIFT 0 /* EQ_B4_GAIN - [4:0] */
+#define WM8904_EQ_B4_GAIN_WIDTH 5 /* EQ_B4_GAIN - [4:0] */
+
+/*
+ * R139 (0x8B) - EQ6
+ */
+#define WM8904_EQ_B5_GAIN_MASK 0x001F /* EQ_B5_GAIN - [4:0] */
+#define WM8904_EQ_B5_GAIN_SHIFT 0 /* EQ_B5_GAIN - [4:0] */
+#define WM8904_EQ_B5_GAIN_WIDTH 5 /* EQ_B5_GAIN - [4:0] */
+
+/*
+ * R140 (0x8C) - EQ7
+ */
+#define WM8904_EQ_B1_A_MASK 0xFFFF /* EQ_B1_A - [15:0] */
+#define WM8904_EQ_B1_A_SHIFT 0 /* EQ_B1_A - [15:0] */
+#define WM8904_EQ_B1_A_WIDTH 16 /* EQ_B1_A - [15:0] */
+
+/*
+ * R141 (0x8D) - EQ8
+ */
+#define WM8904_EQ_B1_B_MASK 0xFFFF /* EQ_B1_B - [15:0] */
+#define WM8904_EQ_B1_B_SHIFT 0 /* EQ_B1_B - [15:0] */
+#define WM8904_EQ_B1_B_WIDTH 16 /* EQ_B1_B - [15:0] */
+
+/*
+ * R142 (0x8E) - EQ9
+ */
+#define WM8904_EQ_B1_PG_MASK 0xFFFF /* EQ_B1_PG - [15:0] */
+#define WM8904_EQ_B1_PG_SHIFT 0 /* EQ_B1_PG - [15:0] */
+#define WM8904_EQ_B1_PG_WIDTH 16 /* EQ_B1_PG - [15:0] */
+
+/*
+ * R143 (0x8F) - EQ10
+ */
+#define WM8904_EQ_B2_A_MASK 0xFFFF /* EQ_B2_A - [15:0] */
+#define WM8904_EQ_B2_A_SHIFT 0 /* EQ_B2_A - [15:0] */
+#define WM8904_EQ_B2_A_WIDTH 16 /* EQ_B2_A - [15:0] */
+
+/*
+ * R144 (0x90) - EQ11
+ */
+#define WM8904_EQ_B2_B_MASK 0xFFFF /* EQ_B2_B - [15:0] */
+#define WM8904_EQ_B2_B_SHIFT 0 /* EQ_B2_B - [15:0] */
+#define WM8904_EQ_B2_B_WIDTH 16 /* EQ_B2_B - [15:0] */
+
+/*
+ * R145 (0x91) - EQ12
+ */
+#define WM8904_EQ_B2_C_MASK 0xFFFF /* EQ_B2_C - [15:0] */
+#define WM8904_EQ_B2_C_SHIFT 0 /* EQ_B2_C - [15:0] */
+#define WM8904_EQ_B2_C_WIDTH 16 /* EQ_B2_C - [15:0] */
+
+/*
+ * R146 (0x92) - EQ13
+ */
+#define WM8904_EQ_B2_PG_MASK 0xFFFF /* EQ_B2_PG - [15:0] */
+#define WM8904_EQ_B2_PG_SHIFT 0 /* EQ_B2_PG - [15:0] */
+#define WM8904_EQ_B2_PG_WIDTH 16 /* EQ_B2_PG - [15:0] */
+
+/*
+ * R147 (0x93) - EQ14
+ */
+#define WM8904_EQ_B3_A_MASK 0xFFFF /* EQ_B3_A - [15:0] */
+#define WM8904_EQ_B3_A_SHIFT 0 /* EQ_B3_A - [15:0] */
+#define WM8904_EQ_B3_A_WIDTH 16 /* EQ_B3_A - [15:0] */
+
+/*
+ * R148 (0x94) - EQ15
+ */
+#define WM8904_EQ_B3_B_MASK 0xFFFF /* EQ_B3_B - [15:0] */
+#define WM8904_EQ_B3_B_SHIFT 0 /* EQ_B3_B - [15:0] */
+#define WM8904_EQ_B3_B_WIDTH 16 /* EQ_B3_B - [15:0] */
+
+/*
+ * R149 (0x95) - EQ16
+ */
+#define WM8904_EQ_B3_C_MASK 0xFFFF /* EQ_B3_C - [15:0] */
+#define WM8904_EQ_B3_C_SHIFT 0 /* EQ_B3_C - [15:0] */
+#define WM8904_EQ_B3_C_WIDTH 16 /* EQ_B3_C - [15:0] */
+
+/*
+ * R150 (0x96) - EQ17
+ */
+#define WM8904_EQ_B3_PG_MASK 0xFFFF /* EQ_B3_PG - [15:0] */
+#define WM8904_EQ_B3_PG_SHIFT 0 /* EQ_B3_PG - [15:0] */
+#define WM8904_EQ_B3_PG_WIDTH 16 /* EQ_B3_PG - [15:0] */
+
+/*
+ * R151 (0x97) - EQ18
+ */
+#define WM8904_EQ_B4_A_MASK 0xFFFF /* EQ_B4_A - [15:0] */
+#define WM8904_EQ_B4_A_SHIFT 0 /* EQ_B4_A - [15:0] */
+#define WM8904_EQ_B4_A_WIDTH 16 /* EQ_B4_A - [15:0] */
+
+/*
+ * R152 (0x98) - EQ19
+ */
+#define WM8904_EQ_B4_B_MASK 0xFFFF /* EQ_B4_B - [15:0] */
+#define WM8904_EQ_B4_B_SHIFT 0 /* EQ_B4_B - [15:0] */
+#define WM8904_EQ_B4_B_WIDTH 16 /* EQ_B4_B - [15:0] */
+
+/*
+ * R153 (0x99) - EQ20
+ */
+#define WM8904_EQ_B4_C_MASK 0xFFFF /* EQ_B4_C - [15:0] */
+#define WM8904_EQ_B4_C_SHIFT 0 /* EQ_B4_C - [15:0] */
+#define WM8904_EQ_B4_C_WIDTH 16 /* EQ_B4_C - [15:0] */
+
+/*
+ * R154 (0x9A) - EQ21
+ */
+#define WM8904_EQ_B4_PG_MASK 0xFFFF /* EQ_B4_PG - [15:0] */
+#define WM8904_EQ_B4_PG_SHIFT 0 /* EQ_B4_PG - [15:0] */
+#define WM8904_EQ_B4_PG_WIDTH 16 /* EQ_B4_PG - [15:0] */
+
+/*
+ * R155 (0x9B) - EQ22
+ */
+#define WM8904_EQ_B5_A_MASK 0xFFFF /* EQ_B5_A - [15:0] */
+#define WM8904_EQ_B5_A_SHIFT 0 /* EQ_B5_A - [15:0] */
+#define WM8904_EQ_B5_A_WIDTH 16 /* EQ_B5_A - [15:0] */
+
+/*
+ * R156 (0x9C) - EQ23
+ */
+#define WM8904_EQ_B5_B_MASK 0xFFFF /* EQ_B5_B - [15:0] */
+#define WM8904_EQ_B5_B_SHIFT 0 /* EQ_B5_B - [15:0] */
+#define WM8904_EQ_B5_B_WIDTH 16 /* EQ_B5_B - [15:0] */
+
+/*
+ * R157 (0x9D) - EQ24
+ */
+#define WM8904_EQ_B5_PG_MASK 0xFFFF /* EQ_B5_PG - [15:0] */
+#define WM8904_EQ_B5_PG_SHIFT 0 /* EQ_B5_PG - [15:0] */
+#define WM8904_EQ_B5_PG_WIDTH 16 /* EQ_B5_PG - [15:0] */
+
+/*
+ * R161 (0xA1) - Control Interface Test 1
+ */
+#define WM8904_USER_KEY 0x0002 /* USER_KEY */
+#define WM8904_USER_KEY_MASK 0x0002 /* USER_KEY */
+#define WM8904_USER_KEY_SHIFT 1 /* USER_KEY */
+#define WM8904_USER_KEY_WIDTH 1 /* USER_KEY */
+
+/*
+ * R198 (0xC6) - ADC Test 0
+ */
+#define WM8904_ADC_128_OSR_TST_MODE 0x0004 /* ADC_128_OSR_TST_MODE */
+#define WM8904_ADC_128_OSR_TST_MODE_SHIFT 2 /* ADC_128_OSR_TST_MODE */
+#define WM8904_ADC_128_OSR_TST_MODE_WIDTH 1 /* ADC_128_OSR_TST_MODE */
+#define WM8904_ADC_BIASX1P5 0x0001 /* ADC_BIASX1P5 */
+#define WM8904_ADC_BIASX1P5_SHIFT 0 /* ADC_BIASX1P5 */
+#define WM8904_ADC_BIASX1P5_WIDTH 1 /* ADC_BIASX1P5 */
+
+/*
+ * R204 (0xCC) - Analogue Output Bias 0
+ */
+#define WM8904_PGA_BIAS_MASK 0x0070 /* PGA_BIAS - [6:4] */
+#define WM8904_PGA_BIAS_SHIFT 4 /* PGA_BIAS - [6:4] */
+#define WM8904_PGA_BIAS_WIDTH 3 /* PGA_BIAS - [6:4] */
+
+/*
+ * R247 (0xF7) - FLL NCO Test 0
+ */
+#define WM8904_FLL_FRC_NCO 0x0001 /* FLL_FRC_NCO */
+#define WM8904_FLL_FRC_NCO_MASK 0x0001 /* FLL_FRC_NCO */
+#define WM8904_FLL_FRC_NCO_SHIFT 0 /* FLL_FRC_NCO */
+#define WM8904_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */
+
+/*
+ * R248 (0xF8) - FLL NCO Test 1
+ */
+#define WM8904_FLL_FRC_NCO_VAL_MASK 0x003F /* FLL_FRC_NCO_VAL - [5:0] */
+#define WM8904_FLL_FRC_NCO_VAL_SHIFT 0 /* FLL_FRC_NCO_VAL - [5:0] */
+#define WM8904_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [5:0] */
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8940.c b/kernel/sound/soc/codecs/wm8940.c
new file mode 100644
index 000000000..e4142b430
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8940.c
@@ -0,0 +1,803 @@
+/*
+ * wm8940.c -- WM8940 ALSA Soc Audio driver
+ *
+ * Author: Jonathan Cameron <jic23@cam.ac.uk>
+ *
+ * Based on wm8510.c
+ * Copyright 2006 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Not currently handled:
+ * Notch filter control
+ * AUXMode (inverting vs mixer)
+ * No means to obtain current gain if alc enabled.
+ * No use made of gpio
+ * Fast VMID discharge for power down
+ * Soft Start
+ * DLR and ALR Swaps not enabled
+ * Digital Sidetone not supported
+ */
+#include <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 "wm8940.h"
+
+struct wm8940_priv {
+ unsigned int sysclk;
+ struct regmap *regmap;
+};
+
+static bool wm8940_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8940_SOFTRESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wm8940_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8940_SOFTRESET:
+ case WM8940_POWER1:
+ case WM8940_POWER2:
+ case WM8940_POWER3:
+ case WM8940_IFACE:
+ case WM8940_COMPANDINGCTL:
+ case WM8940_CLOCK:
+ case WM8940_ADDCNTRL:
+ case WM8940_GPIO:
+ case WM8940_CTLINT:
+ case WM8940_DAC:
+ case WM8940_DACVOL:
+ case WM8940_ADC:
+ case WM8940_ADCVOL:
+ case WM8940_NOTCH1:
+ case WM8940_NOTCH2:
+ case WM8940_NOTCH3:
+ case WM8940_NOTCH4:
+ case WM8940_NOTCH5:
+ case WM8940_NOTCH6:
+ case WM8940_NOTCH7:
+ case WM8940_NOTCH8:
+ case WM8940_DACLIM1:
+ case WM8940_DACLIM2:
+ case WM8940_ALC1:
+ case WM8940_ALC2:
+ case WM8940_ALC3:
+ case WM8940_NOISEGATE:
+ case WM8940_PLLN:
+ case WM8940_PLLK1:
+ case WM8940_PLLK2:
+ case WM8940_PLLK3:
+ case WM8940_ALC4:
+ case WM8940_INPUTCTL:
+ case WM8940_PGAGAIN:
+ case WM8940_ADCBOOST:
+ case WM8940_OUTPUTCTL:
+ case WM8940_SPKMIX:
+ case WM8940_SPKVOL:
+ case WM8940_MONOMIX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default wm8940_reg_defaults[] = {
+ { 0x1, 0x0000 }, /* Power 1 */
+ { 0x2, 0x0000 }, /* Power 2 */
+ { 0x3, 0x0000 }, /* Power 3 */
+ { 0x4, 0x0010 }, /* Interface Control */
+ { 0x5, 0x0000 }, /* Companding Control */
+ { 0x6, 0x0140 }, /* Clock Control */
+ { 0x7, 0x0000 }, /* Additional Controls */
+ { 0x8, 0x0000 }, /* GPIO Control */
+ { 0x9, 0x0002 }, /* Auto Increment Control */
+ { 0xa, 0x0000 }, /* DAC Control */
+ { 0xb, 0x00FF }, /* DAC Volume */
+
+ { 0xe, 0x0100 }, /* ADC Control */
+ { 0xf, 0x00FF }, /* ADC Volume */
+ { 0x10, 0x0000 }, /* Notch Filter 1 Control 1 */
+ { 0x11, 0x0000 }, /* Notch Filter 1 Control 2 */
+ { 0x12, 0x0000 }, /* Notch Filter 2 Control 1 */
+ { 0x13, 0x0000 }, /* Notch Filter 2 Control 2 */
+ { 0x14, 0x0000 }, /* Notch Filter 3 Control 1 */
+ { 0x15, 0x0000 }, /* Notch Filter 3 Control 2 */
+ { 0x16, 0x0000 }, /* Notch Filter 4 Control 1 */
+ { 0x17, 0x0000 }, /* Notch Filter 4 Control 2 */
+ { 0x18, 0x0032 }, /* DAC Limit Control 1 */
+ { 0x19, 0x0000 }, /* DAC Limit Control 2 */
+
+ { 0x20, 0x0038 }, /* ALC Control 1 */
+ { 0x21, 0x000B }, /* ALC Control 2 */
+ { 0x22, 0x0032 }, /* ALC Control 3 */
+ { 0x23, 0x0000 }, /* Noise Gate */
+ { 0x24, 0x0041 }, /* PLLN */
+ { 0x25, 0x000C }, /* PLLK1 */
+ { 0x26, 0x0093 }, /* PLLK2 */
+ { 0x27, 0x00E9 }, /* PLLK3 */
+
+ { 0x2a, 0x0030 }, /* ALC Control 4 */
+
+ { 0x2c, 0x0002 }, /* Input Control */
+ { 0x2d, 0x0050 }, /* PGA Gain */
+
+ { 0x2f, 0x0002 }, /* ADC Boost Control */
+
+ { 0x31, 0x0002 }, /* Output Control */
+ { 0x32, 0x0000 }, /* Speaker Mixer Control */
+
+ { 0x36, 0x0079 }, /* Speaker Volume */
+
+ { 0x38, 0x0000 }, /* Mono Mixer Control */
+};
+
+static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" };
+static SOC_ENUM_SINGLE_DECL(wm8940_adc_companding_enum,
+ WM8940_COMPANDINGCTL, 1, wm8940_companding);
+static SOC_ENUM_SINGLE_DECL(wm8940_dac_companding_enum,
+ WM8940_COMPANDINGCTL, 3, wm8940_companding);
+
+static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"};
+static SOC_ENUM_SINGLE_DECL(wm8940_alc_mode_enum,
+ WM8940_ALC3, 8, wm8940_alc_mode_text);
+
+static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"};
+static SOC_ENUM_SINGLE_DECL(wm8940_mic_bias_level_enum,
+ WM8940_INPUTCTL, 8, wm8940_mic_bias_level_text);
+
+static const char *wm8940_filter_mode_text[] = {"Audio", "Application"};
+static SOC_ENUM_SINGLE_DECL(wm8940_filter_mode_enum,
+ WM8940_ADC, 7, wm8940_filter_mode_text);
+
+static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1);
+static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1);
+static DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0);
+
+static const struct snd_kcontrol_new wm8940_snd_controls[] = {
+ SOC_SINGLE("Digital Loopback Switch", WM8940_COMPANDINGCTL,
+ 6, 1, 0),
+ SOC_ENUM("DAC Companding", wm8940_dac_companding_enum),
+ SOC_ENUM("ADC Companding", wm8940_adc_companding_enum),
+
+ SOC_ENUM("ALC Mode", wm8940_alc_mode_enum),
+ SOC_SINGLE("ALC Switch", WM8940_ALC1, 8, 1, 0),
+ SOC_SINGLE_TLV("ALC Capture Max Gain", WM8940_ALC1,
+ 3, 7, 1, wm8940_alc_max_tlv),
+ SOC_SINGLE_TLV("ALC Capture Min Gain", WM8940_ALC1,
+ 0, 7, 0, wm8940_alc_min_tlv),
+ SOC_SINGLE_TLV("ALC Capture Target", WM8940_ALC2,
+ 0, 14, 0, wm8940_alc_tar_tlv),
+ SOC_SINGLE("ALC Capture Hold", WM8940_ALC2, 4, 10, 0),
+ SOC_SINGLE("ALC Capture Decay", WM8940_ALC3, 4, 10, 0),
+ SOC_SINGLE("ALC Capture Attach", WM8940_ALC3, 0, 10, 0),
+ SOC_SINGLE("ALC ZC Switch", WM8940_ALC4, 1, 1, 0),
+ SOC_SINGLE("ALC Capture Noise Gate Switch", WM8940_NOISEGATE,
+ 3, 1, 0),
+ SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8940_NOISEGATE,
+ 0, 7, 0),
+
+ SOC_SINGLE("DAC Playback Limiter Switch", WM8940_DACLIM1, 8, 1, 0),
+ SOC_SINGLE("DAC Playback Limiter Attack", WM8940_DACLIM1, 0, 9, 0),
+ SOC_SINGLE("DAC Playback Limiter Decay", WM8940_DACLIM1, 4, 11, 0),
+ SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8940_DACLIM2,
+ 4, 9, 1, wm8940_lim_thresh_tlv),
+ SOC_SINGLE_TLV("DAC Playback Limiter Boost", WM8940_DACLIM2,
+ 0, 12, 0, wm8940_lim_boost_tlv),
+
+ SOC_SINGLE("Capture PGA ZC Switch", WM8940_PGAGAIN, 7, 1, 0),
+ SOC_SINGLE_TLV("Capture PGA Volume", WM8940_PGAGAIN,
+ 0, 63, 0, wm8940_pga_vol_tlv),
+ SOC_SINGLE_TLV("Digital Playback Volume", WM8940_DACVOL,
+ 0, 255, 0, wm8940_adc_tlv),
+ SOC_SINGLE_TLV("Digital Capture Volume", WM8940_ADCVOL,
+ 0, 255, 0, wm8940_adc_tlv),
+ SOC_ENUM("Mic Bias Level", wm8940_mic_bias_level_enum),
+ SOC_SINGLE_TLV("Capture Boost Volue", WM8940_ADCBOOST,
+ 8, 1, 0, wm8940_capture_boost_vol_tlv),
+ SOC_SINGLE_TLV("Speaker Playback Volume", WM8940_SPKVOL,
+ 0, 63, 0, wm8940_spk_vol_tlv),
+ SOC_SINGLE("Speaker Playback Switch", WM8940_SPKVOL, 6, 1, 1),
+
+ SOC_SINGLE_TLV("Speaker Mixer Line Bypass Volume", WM8940_SPKVOL,
+ 8, 1, 1, wm8940_att_tlv),
+ SOC_SINGLE("Speaker Playback ZC Switch", WM8940_SPKVOL, 7, 1, 0),
+
+ SOC_SINGLE("Mono Out Switch", WM8940_MONOMIX, 6, 1, 1),
+ SOC_SINGLE_TLV("Mono Mixer Line Bypass Volume", WM8940_MONOMIX,
+ 7, 1, 1, wm8940_att_tlv),
+
+ SOC_SINGLE("High Pass Filter Switch", WM8940_ADC, 8, 1, 0),
+ SOC_ENUM("High Pass Filter Mode", wm8940_filter_mode_enum),
+ SOC_SINGLE("High Pass Filter Cut Off", WM8940_ADC, 4, 7, 0),
+ SOC_SINGLE("ADC Inversion Switch", WM8940_ADC, 0, 1, 0),
+ SOC_SINGLE("DAC Inversion Switch", WM8940_DAC, 0, 1, 0),
+ SOC_SINGLE("DAC Auto Mute Switch", WM8940_DAC, 2, 1, 0),
+ SOC_SINGLE("ZC Timeout Clock Switch", WM8940_ADDCNTRL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8940_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_SPKMIX, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_SPKMIX, 5, 1, 0),
+ SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_SPKMIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8940_mono_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_MONOMIX, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_MONOMIX, 2, 1, 0),
+ SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_MONOMIX, 0, 1, 0),
+};
+
+static DECLARE_TLV_DB_SCALE(wm8940_boost_vol_tlv, -1500, 300, 1);
+static const struct snd_kcontrol_new wm8940_input_boost_controls[] = {
+ SOC_DAPM_SINGLE("Mic PGA Switch", WM8940_PGAGAIN, 6, 1, 1),
+ SOC_DAPM_SINGLE_TLV("Aux Volume", WM8940_ADCBOOST,
+ 0, 7, 0, wm8940_boost_vol_tlv),
+ SOC_DAPM_SINGLE_TLV("Mic Volume", WM8940_ADCBOOST,
+ 4, 7, 0, wm8940_boost_vol_tlv),
+};
+
+static const struct snd_kcontrol_new wm8940_micpga_controls[] = {
+ SOC_DAPM_SINGLE("AUX Switch", WM8940_INPUTCTL, 2, 1, 0),
+ SOC_DAPM_SINGLE("MICP Switch", WM8940_INPUTCTL, 0, 1, 0),
+ SOC_DAPM_SINGLE("MICN Switch", WM8940_INPUTCTL, 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Speaker Mixer", WM8940_POWER3, 2, 0,
+ &wm8940_speaker_mixer_controls[0],
+ ARRAY_SIZE(wm8940_speaker_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono Mixer", WM8940_POWER3, 3, 0,
+ &wm8940_mono_mixer_controls[0],
+ ARRAY_SIZE(wm8940_mono_mixer_controls)),
+ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8940_POWER3, 0, 0),
+
+ SND_SOC_DAPM_PGA("SpkN Out", WM8940_POWER3, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SpkP Out", WM8940_POWER3, 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out", WM8940_POWER3, 7, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("MONOOUT"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+
+ SND_SOC_DAPM_PGA("Aux Input", WM8940_POWER1, 6, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8940_POWER2, 0, 0),
+ SND_SOC_DAPM_MIXER("Mic PGA", WM8940_POWER2, 2, 0,
+ &wm8940_micpga_controls[0],
+ ARRAY_SIZE(wm8940_micpga_controls)),
+ SND_SOC_DAPM_MIXER("Boost Mixer", WM8940_POWER2, 4, 0,
+ &wm8940_input_boost_controls[0],
+ ARRAY_SIZE(wm8940_input_boost_controls)),
+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8940_POWER1, 4, 0),
+
+ SND_SOC_DAPM_INPUT("MICN"),
+ SND_SOC_DAPM_INPUT("MICP"),
+ SND_SOC_DAPM_INPUT("AUX"),
+};
+
+static const struct snd_soc_dapm_route wm8940_dapm_routes[] = {
+ /* Mono output mixer */
+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+ /* Speaker output mixer */
+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+ /* Outputs */
+ {"Mono Out", NULL, "Mono Mixer"},
+ {"MONOOUT", NULL, "Mono Out"},
+ {"SpkN Out", NULL, "Speaker Mixer"},
+ {"SpkP Out", NULL, "Speaker Mixer"},
+ {"SPKOUTN", NULL, "SpkN Out"},
+ {"SPKOUTP", NULL, "SpkP Out"},
+
+ /* Microphone PGA */
+ {"Mic PGA", "MICN Switch", "MICN"},
+ {"Mic PGA", "MICP Switch", "MICP"},
+ {"Mic PGA", "AUX Switch", "AUX"},
+
+ /* Boost Mixer */
+ {"Boost Mixer", "Mic PGA Switch", "Mic PGA"},
+ {"Boost Mixer", "Mic Volume", "MICP"},
+ {"Boost Mixer", "Aux Volume", "Aux Input"},
+
+ {"ADC", NULL, "Boost Mixer"},
+};
+
+#define wm8940_reset(c) snd_soc_write(c, WM8940_SOFTRESET, 0);
+
+static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFE67;
+ u16 clk = snd_soc_read(codec, WM8940_CLOCK) & 0x1fe;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ clk |= 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_write(codec, WM8940_CLOCK, clk);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= (2 << 3);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= (1 << 3);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= (3 << 3);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= (3 << 3) | (1 << 7);
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= (1 << 7);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= (1 << 8);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= (1 << 8) | (1 << 7);
+ break;
+ }
+
+ snd_soc_write(codec, WM8940_IFACE, iface);
+
+ return 0;
+}
+
+static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFD9F;
+ u16 addcntrl = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFF1;
+ u16 companding = snd_soc_read(codec,
+ WM8940_COMPANDINGCTL) & 0xFFDF;
+ int ret;
+
+ /* LoutR control */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
+ && params_channels(params) == 2)
+ iface |= (1 << 9);
+
+ switch (params_rate(params)) {
+ case 8000:
+ addcntrl |= (0x5 << 1);
+ break;
+ case 11025:
+ addcntrl |= (0x4 << 1);
+ break;
+ case 16000:
+ addcntrl |= (0x3 << 1);
+ break;
+ case 22050:
+ addcntrl |= (0x2 << 1);
+ break;
+ case 32000:
+ addcntrl |= (0x1 << 1);
+ break;
+ case 44100:
+ case 48000:
+ break;
+ }
+ ret = snd_soc_write(codec, WM8940_ADDCNTRL, addcntrl);
+ if (ret)
+ goto error_ret;
+
+ switch (params_width(params)) {
+ case 8:
+ companding = companding | (1 << 5);
+ break;
+ case 16:
+ break;
+ case 20:
+ iface |= (1 << 5);
+ break;
+ case 24:
+ iface |= (2 << 5);
+ break;
+ case 32:
+ iface |= (3 << 5);
+ break;
+ }
+ ret = snd_soc_write(codec, WM8940_COMPANDINGCTL, companding);
+ if (ret)
+ goto error_ret;
+ ret = snd_soc_write(codec, WM8940_IFACE, iface);
+
+error_ret:
+ return ret;
+}
+
+static int wm8940_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8940_DAC) & 0xffbf;
+
+ if (mute)
+ mute_reg |= 0x40;
+
+ return snd_soc_write(codec, WM8940_DAC, mute_reg);
+}
+
+static int wm8940_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
+ u16 val;
+ u16 pwr_reg = snd_soc_read(codec, WM8940_POWER1) & 0x1F0;
+ int ret = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* ensure bufioen and biasen */
+ pwr_reg |= (1 << 2) | (1 << 3);
+ /* Enable thermal shutdown */
+ val = snd_soc_read(codec, WM8940_OUTPUTCTL);
+ ret = snd_soc_write(codec, WM8940_OUTPUTCTL, val | 0x2);
+ if (ret)
+ break;
+ /* set vmid to 75k */
+ ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* ensure bufioen and biasen */
+ pwr_reg |= (1 << 2) | (1 << 3);
+ ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regcache_sync(wm8940->regmap);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* ensure bufioen and biasen */
+ pwr_reg |= (1 << 2) | (1 << 3);
+ /* set vmid to 300k for standby */
+ ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x2);
+ break;
+ case SND_SOC_BIAS_OFF:
+ ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return ret;
+}
+
+struct pll_ {
+ unsigned int pre_scale:2;
+ unsigned int n:4;
+ unsigned int k;
+};
+
+static struct pll_ pll_div;
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+static void pll_factors(unsigned int target, unsigned int source)
+{
+ unsigned long long Kpart;
+ unsigned int K, Ndiv, Nmod;
+ /* The left shift ist to avoid accuracy loss when right shifting */
+ Ndiv = target / source;
+
+ if (Ndiv > 12) {
+ source <<= 1;
+ /* Multiply by 2 */
+ pll_div.pre_scale = 0;
+ Ndiv = target / source;
+ } else if (Ndiv < 3) {
+ source >>= 2;
+ /* Divide by 4 */
+ pll_div.pre_scale = 3;
+ Ndiv = target / source;
+ } else if (Ndiv < 6) {
+ source >>= 1;
+ /* divide by 2 */
+ pll_div.pre_scale = 2;
+ Ndiv = target / source;
+ } else
+ pll_div.pre_scale = 1;
+
+ if ((Ndiv < 6) || (Ndiv > 12))
+ printk(KERN_WARNING
+ "WM8940 N value %d outwith recommended range!d\n",
+ Ndiv);
+
+ pll_div.n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div.k = K;
+}
+
+/* Untested at the moment */
+static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ /* Turn off PLL */
+ reg = snd_soc_read(codec, WM8940_POWER1);
+ snd_soc_write(codec, WM8940_POWER1, reg & 0x1df);
+
+ if (freq_in == 0 || freq_out == 0) {
+ /* Clock CODEC directly from MCLK */
+ reg = snd_soc_read(codec, WM8940_CLOCK);
+ snd_soc_write(codec, WM8940_CLOCK, reg & 0x0ff);
+ /* Pll power down */
+ snd_soc_write(codec, WM8940_PLLN, (1 << 7));
+ return 0;
+ }
+
+ /* Pll is followed by a frequency divide by 4 */
+ pll_factors(freq_out*4, freq_in);
+ if (pll_div.k)
+ snd_soc_write(codec, WM8940_PLLN,
+ (pll_div.pre_scale << 4) | pll_div.n | (1 << 6));
+ else /* No factional component */
+ snd_soc_write(codec, WM8940_PLLN,
+ (pll_div.pre_scale << 4) | pll_div.n);
+ snd_soc_write(codec, WM8940_PLLK1, pll_div.k >> 18);
+ snd_soc_write(codec, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8940_PLLK3, pll_div.k & 0x1ff);
+ /* Enable the PLL */
+ reg = snd_soc_read(codec, WM8940_POWER1);
+ snd_soc_write(codec, WM8940_POWER1, reg | 0x020);
+
+ /* Run CODEC from PLL instead of MCLK */
+ reg = snd_soc_read(codec, WM8940_CLOCK);
+ snd_soc_write(codec, WM8940_CLOCK, reg | 0x100);
+
+ return 0;
+}
+
+static int wm8940_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 wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
+
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ wm8940->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+ int ret = 0;
+
+ switch (div_id) {
+ case WM8940_BCLKDIV:
+ reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFFE3;
+ ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 2));
+ break;
+ case WM8940_MCLKDIV:
+ reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFF1F;
+ ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 5));
+ break;
+ case WM8940_OPCLKDIV:
+ reg = snd_soc_read(codec, WM8940_GPIO) & 0xFFCF;
+ ret = snd_soc_write(codec, WM8940_GPIO, reg | (div << 4));
+ break;
+ }
+ return ret;
+}
+
+#define WM8940_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8940_dai_ops = {
+ .hw_params = wm8940_i2s_hw_params,
+ .set_sysclk = wm8940_set_dai_sysclk,
+ .digital_mute = wm8940_mute,
+ .set_fmt = wm8940_set_dai_fmt,
+ .set_clkdiv = wm8940_set_dai_clkdiv,
+ .set_pll = wm8940_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver wm8940_dai = {
+ .name = "wm8940-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8940_RATES,
+ .formats = WM8940_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8940_RATES,
+ .formats = WM8940_FORMATS,
+ },
+ .ops = &wm8940_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int wm8940_probe(struct snd_soc_codec *codec)
+{
+ struct wm8940_setup_data *pdata = codec->dev->platform_data;
+ int ret;
+ u16 reg;
+
+ ret = wm8940_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ return ret;
+ }
+
+ wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ ret = snd_soc_write(codec, WM8940_POWER1, 0x180);
+ if (ret < 0)
+ return ret;
+
+ if (!pdata)
+ dev_warn(codec->dev, "No platform data supplied\n");
+ else {
+ reg = snd_soc_read(codec, WM8940_OUTPUTCTL);
+ ret = snd_soc_write(codec, WM8940_OUTPUTCTL, reg | pdata->vroi);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8940 = {
+ .probe = wm8940_probe,
+ .set_bias_level = wm8940_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8940_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8940_snd_controls),
+ .dapm_widgets = wm8940_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8940_dapm_widgets),
+ .dapm_routes = wm8940_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8940_dapm_routes),
+};
+
+static const struct regmap_config wm8940_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = WM8940_MONOMIX,
+ .reg_defaults = wm8940_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8940_reg_defaults),
+
+ .readable_reg = wm8940_readable_register,
+ .volatile_reg = wm8940_volatile_register,
+};
+
+static int wm8940_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8940_priv *wm8940;
+ int ret;
+
+ wm8940 = devm_kzalloc(&i2c->dev, sizeof(struct wm8940_priv),
+ GFP_KERNEL);
+ if (wm8940 == NULL)
+ return -ENOMEM;
+
+ wm8940->regmap = devm_regmap_init_i2c(i2c, &wm8940_regmap);
+ if (IS_ERR(wm8940->regmap))
+ return PTR_ERR(wm8940->regmap);
+
+ i2c_set_clientdata(i2c, wm8940);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8940, &wm8940_dai, 1);
+
+ return ret;
+}
+
+static int wm8940_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm8940_i2c_id[] = {
+ { "wm8940", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id);
+
+static struct i2c_driver wm8940_i2c_driver = {
+ .driver = {
+ .name = "wm8940",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8940_i2c_probe,
+ .remove = wm8940_i2c_remove,
+ .id_table = wm8940_i2c_id,
+};
+
+module_i2c_driver(wm8940_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8940 driver");
+MODULE_AUTHOR("Jonathan Cameron");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8940.h b/kernel/sound/soc/codecs/wm8940.h
new file mode 100644
index 000000000..907fe192e
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8940.h
@@ -0,0 +1,102 @@
+/*
+ * wm8940.h -- WM8940 Soc Audio driver
+ *
+ * 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 _WM8940_H
+#define _WM8940_H
+
+struct wm8940_setup_data {
+ /* Vref to analogue output resistance */
+#define WM8940_VROI_1K 0
+#define WM8940_VROI_30K 1
+ unsigned int vroi:1;
+};
+
+/* WM8940 register space */
+#define WM8940_SOFTRESET 0x00
+#define WM8940_POWER1 0x01
+#define WM8940_POWER2 0x02
+#define WM8940_POWER3 0x03
+#define WM8940_IFACE 0x04
+#define WM8940_COMPANDINGCTL 0x05
+#define WM8940_CLOCK 0x06
+#define WM8940_ADDCNTRL 0x07
+#define WM8940_GPIO 0x08
+#define WM8940_CTLINT 0x09
+#define WM8940_DAC 0x0A
+#define WM8940_DACVOL 0x0B
+
+#define WM8940_ADC 0x0E
+#define WM8940_ADCVOL 0x0F
+#define WM8940_NOTCH1 0x10
+#define WM8940_NOTCH2 0x11
+#define WM8940_NOTCH3 0x12
+#define WM8940_NOTCH4 0x13
+#define WM8940_NOTCH5 0x14
+#define WM8940_NOTCH6 0x15
+#define WM8940_NOTCH7 0x16
+#define WM8940_NOTCH8 0x17
+#define WM8940_DACLIM1 0x18
+#define WM8940_DACLIM2 0x19
+
+#define WM8940_ALC1 0x20
+#define WM8940_ALC2 0x21
+#define WM8940_ALC3 0x22
+#define WM8940_NOISEGATE 0x23
+#define WM8940_PLLN 0x24
+#define WM8940_PLLK1 0x25
+#define WM8940_PLLK2 0x26
+#define WM8940_PLLK3 0x27
+
+#define WM8940_ALC4 0x2A
+
+#define WM8940_INPUTCTL 0x2C
+#define WM8940_PGAGAIN 0x2D
+
+#define WM8940_ADCBOOST 0x2F
+
+#define WM8940_OUTPUTCTL 0x31
+#define WM8940_SPKMIX 0x32
+
+#define WM8940_SPKVOL 0x36
+
+#define WM8940_MONOMIX 0x38
+
+#define WM8940_CACHEREGNUM 0x57
+
+
+/* Clock divider Id's */
+#define WM8940_BCLKDIV 0
+#define WM8940_MCLKDIV 1
+#define WM8940_OPCLKDIV 2
+
+/* MCLK clock dividers */
+#define WM8940_MCLKDIV_1 0
+#define WM8940_MCLKDIV_1_5 1
+#define WM8940_MCLKDIV_2 2
+#define WM8940_MCLKDIV_3 3
+#define WM8940_MCLKDIV_4 4
+#define WM8940_MCLKDIV_6 5
+#define WM8940_MCLKDIV_8 6
+#define WM8940_MCLKDIV_12 7
+
+/* BCLK clock dividers */
+#define WM8940_BCLKDIV_1 0
+#define WM8940_BCLKDIV_2 1
+#define WM8940_BCLKDIV_4 2
+#define WM8940_BCLKDIV_8 3
+#define WM8940_BCLKDIV_16 4
+#define WM8940_BCLKDIV_32 5
+
+/* PLL Out Dividers */
+#define WM8940_OPCLKDIV_1 0
+#define WM8940_OPCLKDIV_2 1
+#define WM8940_OPCLKDIV_3 2
+#define WM8940_OPCLKDIV_4 3
+
+#endif /* _WM8940_H */
+
diff --git a/kernel/sound/soc/codecs/wm8955.c b/kernel/sound/soc/codecs/wm8955.c
new file mode 100644
index 000000000..00bec915d
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8955.c
@@ -0,0 +1,1024 @@
+/*
+ * wm8955.c -- WM8955 ALSA SoC Audio driver
+ *
+ * Copyright 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 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/i2c.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.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 <sound/wm8955.h>
+
+#include "wm8955.h"
+
+#define WM8955_NUM_SUPPLIES 4
+static const char *wm8955_supply_names[WM8955_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD",
+ "HPVDD",
+ "AVDD",
+};
+
+/* codec private data */
+struct wm8955_priv {
+ struct regmap *regmap;
+
+ unsigned int mclk_rate;
+
+ int deemph;
+ int fs;
+
+ struct regulator_bulk_data supplies[WM8955_NUM_SUPPLIES];
+};
+
+static const struct reg_default wm8955_reg_defaults[] = {
+ { 2, 0x0079 }, /* R2 - LOUT1 volume */
+ { 3, 0x0079 }, /* R3 - ROUT1 volume */
+ { 5, 0x0008 }, /* R5 - DAC Control */
+ { 7, 0x000A }, /* R7 - Audio Interface */
+ { 8, 0x0000 }, /* R8 - Sample Rate */
+ { 10, 0x00FF }, /* R10 - Left DAC volume */
+ { 11, 0x00FF }, /* R11 - Right DAC volume */
+ { 12, 0x000F }, /* R12 - Bass control */
+ { 13, 0x000F }, /* R13 - Treble control */
+ { 23, 0x00C1 }, /* R23 - Additional control (1) */
+ { 24, 0x0000 }, /* R24 - Additional control (2) */
+ { 25, 0x0000 }, /* R25 - Power Management (1) */
+ { 26, 0x0000 }, /* R26 - Power Management (2) */
+ { 27, 0x0000 }, /* R27 - Additional Control (3) */
+ { 34, 0x0050 }, /* R34 - Left out Mix (1) */
+ { 35, 0x0050 }, /* R35 - Left out Mix (2) */
+ { 36, 0x0050 }, /* R36 - Right out Mix (1) */
+ { 37, 0x0050 }, /* R37 - Right Out Mix (2) */
+ { 38, 0x0050 }, /* R38 - Mono out Mix (1) */
+ { 39, 0x0050 }, /* R39 - Mono out Mix (2) */
+ { 40, 0x0079 }, /* R40 - LOUT2 volume */
+ { 41, 0x0079 }, /* R41 - ROUT2 volume */
+ { 42, 0x0079 }, /* R42 - MONOOUT volume */
+ { 43, 0x0000 }, /* R43 - Clocking / PLL */
+ { 44, 0x0103 }, /* R44 - PLL Control 1 */
+ { 45, 0x0024 }, /* R45 - PLL Control 2 */
+ { 46, 0x01BA }, /* R46 - PLL Control 3 */
+ { 59, 0x0000 }, /* R59 - PLL Control 4 */
+};
+
+static bool wm8955_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8955_LOUT1_VOLUME:
+ case WM8955_ROUT1_VOLUME:
+ case WM8955_DAC_CONTROL:
+ case WM8955_AUDIO_INTERFACE:
+ case WM8955_SAMPLE_RATE:
+ case WM8955_LEFT_DAC_VOLUME:
+ case WM8955_RIGHT_DAC_VOLUME:
+ case WM8955_BASS_CONTROL:
+ case WM8955_TREBLE_CONTROL:
+ case WM8955_RESET:
+ case WM8955_ADDITIONAL_CONTROL_1:
+ case WM8955_ADDITIONAL_CONTROL_2:
+ case WM8955_POWER_MANAGEMENT_1:
+ case WM8955_POWER_MANAGEMENT_2:
+ case WM8955_ADDITIONAL_CONTROL_3:
+ case WM8955_LEFT_OUT_MIX_1:
+ case WM8955_LEFT_OUT_MIX_2:
+ case WM8955_RIGHT_OUT_MIX_1:
+ case WM8955_RIGHT_OUT_MIX_2:
+ case WM8955_MONO_OUT_MIX_1:
+ case WM8955_MONO_OUT_MIX_2:
+ case WM8955_LOUT2_VOLUME:
+ case WM8955_ROUT2_VOLUME:
+ case WM8955_MONOOUT_VOLUME:
+ case WM8955_CLOCKING_PLL:
+ case WM8955_PLL_CONTROL_1:
+ case WM8955_PLL_CONTROL_2:
+ case WM8955_PLL_CONTROL_3:
+ case WM8955_PLL_CONTROL_4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wm8955_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8955_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int wm8955_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, WM8955_RESET, 0);
+}
+
+struct pll_factors {
+ int n;
+ int k;
+ int outdiv;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 22) * 10)
+
+static int wm8995_pll_factors(struct device *dev,
+ int Fref, int Fout, struct pll_factors *pll)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod, target;
+
+ dev_dbg(dev, "Fref=%u Fout=%u\n", Fref, Fout);
+
+ /* The oscilator should run at should be 90-100MHz, and
+ * there's a divide by 4 plus an optional divide by 2 in the
+ * output path to generate the system clock. The clock table
+ * is sortd so we should always generate a suitable target. */
+ target = Fout * 4;
+ if (target < 90000000) {
+ pll->outdiv = 1;
+ target *= 2;
+ } else {
+ pll->outdiv = 0;
+ }
+
+ WARN_ON(target < 90000000 || target > 100000000);
+
+ dev_dbg(dev, "Fvco=%dHz\n", target);
+
+ /* Now, calculate N.K */
+ Ndiv = target / Fref;
+
+ pll->n = Ndiv;
+ Nmod = target % Fref;
+ dev_dbg(dev, "Nmod=%d\n", Nmod);
+
+ /* Calculate fractional part - scale up so we can round. */
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, Fref);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ pll->k = K / 10;
+
+ dev_dbg(dev, "N=%x K=%x OUTDIV=%x\n", pll->n, pll->k, pll->outdiv);
+
+ return 0;
+}
+
+/* Lookup table specifying SRATE (table 25 in datasheet); some of the
+ * output frequencies have been rounded to the standard frequencies
+ * they are intended to match where the error is slight. */
+static struct {
+ int mclk;
+ int fs;
+ int usb;
+ int sr;
+} clock_cfgs[] = {
+ { 18432000, 8000, 0, 3, },
+ { 18432000, 12000, 0, 9, },
+ { 18432000, 16000, 0, 11, },
+ { 18432000, 24000, 0, 29, },
+ { 18432000, 32000, 0, 13, },
+ { 18432000, 48000, 0, 1, },
+ { 18432000, 96000, 0, 15, },
+
+ { 16934400, 8018, 0, 19, },
+ { 16934400, 11025, 0, 25, },
+ { 16934400, 22050, 0, 27, },
+ { 16934400, 44100, 0, 17, },
+ { 16934400, 88200, 0, 31, },
+
+ { 12000000, 8000, 1, 2, },
+ { 12000000, 11025, 1, 25, },
+ { 12000000, 12000, 1, 8, },
+ { 12000000, 16000, 1, 10, },
+ { 12000000, 22050, 1, 27, },
+ { 12000000, 24000, 1, 28, },
+ { 12000000, 32000, 1, 12, },
+ { 12000000, 44100, 1, 17, },
+ { 12000000, 48000, 1, 0, },
+ { 12000000, 88200, 1, 31, },
+ { 12000000, 96000, 1, 14, },
+
+ { 12288000, 8000, 0, 2, },
+ { 12288000, 12000, 0, 8, },
+ { 12288000, 16000, 0, 10, },
+ { 12288000, 24000, 0, 28, },
+ { 12288000, 32000, 0, 12, },
+ { 12288000, 48000, 0, 0, },
+ { 12288000, 96000, 0, 14, },
+
+ { 12289600, 8018, 0, 18, },
+ { 12289600, 11025, 0, 24, },
+ { 12289600, 22050, 0, 26, },
+ { 11289600, 44100, 0, 16, },
+ { 11289600, 88200, 0, 31, },
+};
+
+static int wm8955_configure_clocking(struct snd_soc_codec *codec)
+{
+ struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
+ int i, ret, val;
+ int clocking = 0;
+ int srate = 0;
+ int sr = -1;
+ struct pll_factors pll;
+
+ /* If we're not running a sample rate currently just pick one */
+ if (wm8955->fs == 0)
+ wm8955->fs = 8000;
+
+ /* Can we generate an exact output? */
+ for (i = 0; i < ARRAY_SIZE(clock_cfgs); i++) {
+ if (wm8955->fs != clock_cfgs[i].fs)
+ continue;
+ sr = i;
+
+ if (wm8955->mclk_rate == clock_cfgs[i].mclk)
+ break;
+ }
+
+ /* We should never get here with an unsupported sample rate */
+ if (sr == -1) {
+ dev_err(codec->dev, "Sample rate %dHz unsupported\n",
+ wm8955->fs);
+ WARN_ON(sr == -1);
+ return -EINVAL;
+ }
+
+ if (i == ARRAY_SIZE(clock_cfgs)) {
+ /* If we can't generate the right clock from MCLK then
+ * we should configure the PLL to supply us with an
+ * appropriate clock.
+ */
+ clocking |= WM8955_MCLKSEL;
+
+ /* Use the last divider configuration we saw for the
+ * sample rate. */
+ ret = wm8995_pll_factors(codec->dev, wm8955->mclk_rate,
+ clock_cfgs[sr].mclk, &pll);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Unable to generate %dHz from %dHz MCLK\n",
+ wm8955->fs, wm8955->mclk_rate);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8955_PLL_CONTROL_1,
+ WM8955_N_MASK | WM8955_K_21_18_MASK,
+ (pll.n << WM8955_N_SHIFT) |
+ pll.k >> 18);
+ snd_soc_update_bits(codec, WM8955_PLL_CONTROL_2,
+ WM8955_K_17_9_MASK,
+ (pll.k >> 9) & WM8955_K_17_9_MASK);
+ snd_soc_update_bits(codec, WM8955_PLL_CONTROL_2,
+ WM8955_K_8_0_MASK,
+ pll.k & WM8955_K_8_0_MASK);
+ if (pll.k)
+ snd_soc_update_bits(codec, WM8955_PLL_CONTROL_4,
+ WM8955_KEN, WM8955_KEN);
+ else
+ snd_soc_update_bits(codec, WM8955_PLL_CONTROL_4,
+ WM8955_KEN, 0);
+
+ if (pll.outdiv)
+ val = WM8955_PLL_RB | WM8955_PLLOUTDIV2;
+ else
+ val = WM8955_PLL_RB;
+
+ /* Now start the PLL running */
+ snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+ WM8955_PLL_RB | WM8955_PLLOUTDIV2, val);
+ snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+ WM8955_PLLEN, WM8955_PLLEN);
+ }
+
+ srate = clock_cfgs[sr].usb | (clock_cfgs[sr].sr << WM8955_SR_SHIFT);
+
+ snd_soc_update_bits(codec, WM8955_SAMPLE_RATE,
+ WM8955_USB | WM8955_SR_MASK, srate);
+ snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+ WM8955_MCLKSEL, clocking);
+
+ return 0;
+}
+
+static int wm8955_sysclk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int ret = 0;
+
+ /* Always disable the clocks - if we're doing reconfiguration this
+ * avoids misclocking.
+ */
+ snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+ WM8955_DIGENB, 0);
+ snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+ WM8955_PLL_RB | WM8955_PLLEN, 0);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = wm8955_configure_clocking(codec);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int deemph_settings[] = { 0, 32000, 44100, 48000 };
+
+static int wm8955_set_deemph(struct snd_soc_codec *codec)
+{
+ struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
+ int val, i, best;
+
+ /* If we're using deemphasis select the nearest available sample
+ * rate.
+ */
+ if (wm8955->deemph) {
+ best = 1;
+ for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
+ if (abs(deemph_settings[i] - wm8955->fs) <
+ abs(deemph_settings[best] - wm8955->fs))
+ best = i;
+ }
+
+ val = best << WM8955_DEEMPH_SHIFT;
+ } else {
+ val = 0;
+ }
+
+ dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+
+ return snd_soc_update_bits(codec, WM8955_DAC_CONTROL,
+ WM8955_DEEMPH_MASK, val);
+}
+
+static int wm8955_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8955->deemph;
+ return 0;
+}
+
+static int wm8955_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
+ int deemph = ucontrol->value.integer.value[0];
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ wm8955->deemph = deemph;
+
+ return wm8955_set_deemph(codec);
+}
+
+static const char *bass_mode_text[] = {
+ "Linear", "Adaptive",
+};
+
+static SOC_ENUM_SINGLE_DECL(bass_mode, WM8955_BASS_CONTROL, 7, bass_mode_text);
+
+static const char *bass_cutoff_text[] = {
+ "Low", "High"
+};
+
+static SOC_ENUM_SINGLE_DECL(bass_cutoff, WM8955_BASS_CONTROL, 6,
+ bass_cutoff_text);
+
+static const char *treble_cutoff_text[] = {
+ "High", "Low"
+};
+
+static SOC_ENUM_SINGLE_DECL(treble_cutoff, WM8955_TREBLE_CONTROL, 2,
+ treble_cutoff_text);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(atten_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mono_tlv, -2100, 300, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(treble_tlv, -1200, 150, 1);
+
+static const struct snd_kcontrol_new wm8955_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8955_LEFT_DAC_VOLUME,
+ WM8955_RIGHT_DAC_VOLUME, 0, 255, 0, digital_tlv),
+SOC_SINGLE_TLV("Playback Attenuation Volume", WM8955_DAC_CONTROL, 7, 1, 1,
+ atten_tlv),
+SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
+ wm8955_get_deemph, wm8955_put_deemph),
+
+SOC_ENUM("Bass Mode", bass_mode),
+SOC_ENUM("Bass Cutoff", bass_cutoff),
+SOC_SINGLE("Bass Volume", WM8955_BASS_CONTROL, 0, 15, 1),
+
+SOC_ENUM("Treble Cutoff", treble_cutoff),
+SOC_SINGLE_TLV("Treble Volume", WM8955_TREBLE_CONTROL, 0, 14, 1, treble_tlv),
+
+SOC_SINGLE_TLV("Left Bypass Volume", WM8955_LEFT_OUT_MIX_1, 4, 7, 1,
+ bypass_tlv),
+SOC_SINGLE_TLV("Left Mono Volume", WM8955_LEFT_OUT_MIX_2, 4, 7, 1,
+ bypass_tlv),
+
+SOC_SINGLE_TLV("Right Mono Volume", WM8955_RIGHT_OUT_MIX_1, 4, 7, 1,
+ bypass_tlv),
+SOC_SINGLE_TLV("Right Bypass Volume", WM8955_RIGHT_OUT_MIX_2, 4, 7, 1,
+ bypass_tlv),
+
+/* Not a stereo pair so they line up with the DAPM switches */
+SOC_SINGLE_TLV("Mono Left Bypass Volume", WM8955_MONO_OUT_MIX_1, 4, 7, 1,
+ mono_tlv),
+SOC_SINGLE_TLV("Mono Right Bypass Volume", WM8955_MONO_OUT_MIX_2, 4, 7, 1,
+ mono_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Volume", WM8955_LOUT1_VOLUME,
+ WM8955_ROUT1_VOLUME, 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8955_LOUT1_VOLUME,
+ WM8955_ROUT1_VOLUME, 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Speaker Volume", WM8955_LOUT2_VOLUME,
+ WM8955_ROUT2_VOLUME, 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Speaker ZC Switch", WM8955_LOUT2_VOLUME,
+ WM8955_ROUT2_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("Mono Volume", WM8955_MONOOUT_VOLUME, 0, 127, 0, out_tlv),
+SOC_SINGLE("Mono ZC Switch", WM8955_MONOOUT_VOLUME, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lmixer[] = {
+SOC_DAPM_SINGLE("Playback Switch", WM8955_LEFT_OUT_MIX_1, 8, 1, 0),
+SOC_DAPM_SINGLE("Bypass Switch", WM8955_LEFT_OUT_MIX_1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8955_LEFT_OUT_MIX_2, 8, 1, 0),
+SOC_DAPM_SINGLE("Mono Switch", WM8955_LEFT_OUT_MIX_2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new rmixer[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8955_RIGHT_OUT_MIX_1, 8, 1, 0),
+SOC_DAPM_SINGLE("Mono Switch", WM8955_RIGHT_OUT_MIX_1, 7, 1, 0),
+SOC_DAPM_SINGLE("Playback Switch", WM8955_RIGHT_OUT_MIX_2, 8, 1, 0),
+SOC_DAPM_SINGLE("Bypass Switch", WM8955_RIGHT_OUT_MIX_2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mmixer[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8955_MONO_OUT_MIX_1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8955_MONO_OUT_MIX_1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8955_MONO_OUT_MIX_2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8955_MONO_OUT_MIX_2, 7, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8955_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MONOIN-"),
+SND_SOC_DAPM_INPUT("MONOIN+"),
+SND_SOC_DAPM_INPUT("LINEINR"),
+SND_SOC_DAPM_INPUT("LINEINL"),
+
+SND_SOC_DAPM_PGA("Mono Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("SYSCLK", WM8955_POWER_MANAGEMENT_1, 0, 1, wm8955_sysclk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("TSDEN", WM8955_ADDITIONAL_CONTROL_1, 8, 0, NULL, 0),
+
+SND_SOC_DAPM_DAC("DACL", "Playback", WM8955_POWER_MANAGEMENT_2, 8, 0),
+SND_SOC_DAPM_DAC("DACR", "Playback", WM8955_POWER_MANAGEMENT_2, 7, 0),
+
+SND_SOC_DAPM_PGA("LOUT1 PGA", WM8955_POWER_MANAGEMENT_2, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ROUT1 PGA", WM8955_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LOUT2 PGA", WM8955_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ROUT2 PGA", WM8955_POWER_MANAGEMENT_2, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MOUT PGA", WM8955_POWER_MANAGEMENT_2, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("OUT3 PGA", WM8955_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
+
+/* The names are chosen to make the control names nice */
+SND_SOC_DAPM_MIXER("Left", SND_SOC_NOPM, 0, 0,
+ lmixer, ARRAY_SIZE(lmixer)),
+SND_SOC_DAPM_MIXER("Right", SND_SOC_NOPM, 0, 0,
+ rmixer, ARRAY_SIZE(rmixer)),
+SND_SOC_DAPM_MIXER("Mono", SND_SOC_NOPM, 0, 0,
+ mmixer, ARRAY_SIZE(mmixer)),
+
+SND_SOC_DAPM_OUTPUT("LOUT1"),
+SND_SOC_DAPM_OUTPUT("ROUT1"),
+SND_SOC_DAPM_OUTPUT("LOUT2"),
+SND_SOC_DAPM_OUTPUT("ROUT2"),
+SND_SOC_DAPM_OUTPUT("MONOOUT"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+};
+
+static const struct snd_soc_dapm_route wm8955_dapm_routes[] = {
+ { "DACL", NULL, "SYSCLK" },
+ { "DACR", NULL, "SYSCLK" },
+
+ { "Mono Input", NULL, "MONOIN-" },
+ { "Mono Input", NULL, "MONOIN+" },
+
+ { "Left", "Playback Switch", "DACL" },
+ { "Left", "Right Playback Switch", "DACR" },
+ { "Left", "Bypass Switch", "LINEINL" },
+ { "Left", "Mono Switch", "Mono Input" },
+
+ { "Right", "Playback Switch", "DACR" },
+ { "Right", "Left Playback Switch", "DACL" },
+ { "Right", "Bypass Switch", "LINEINR" },
+ { "Right", "Mono Switch", "Mono Input" },
+
+ { "Mono", "Left Playback Switch", "DACL" },
+ { "Mono", "Right Playback Switch", "DACR" },
+ { "Mono", "Left Bypass Switch", "LINEINL" },
+ { "Mono", "Right Bypass Switch", "LINEINR" },
+
+ { "LOUT1 PGA", NULL, "Left" },
+ { "LOUT1", NULL, "TSDEN" },
+ { "LOUT1", NULL, "LOUT1 PGA" },
+
+ { "ROUT1 PGA", NULL, "Right" },
+ { "ROUT1", NULL, "TSDEN" },
+ { "ROUT1", NULL, "ROUT1 PGA" },
+
+ { "LOUT2 PGA", NULL, "Left" },
+ { "LOUT2", NULL, "TSDEN" },
+ { "LOUT2", NULL, "LOUT2 PGA" },
+
+ { "ROUT2 PGA", NULL, "Right" },
+ { "ROUT2", NULL, "TSDEN" },
+ { "ROUT2", NULL, "ROUT2 PGA" },
+
+ { "MOUT PGA", NULL, "Mono" },
+ { "MONOOUT", NULL, "MOUT PGA" },
+
+ /* OUT3 not currently implemented */
+ { "OUT3", NULL, "OUT3 PGA" },
+};
+
+static int wm8955_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+ int wl;
+
+ switch (params_width(params)) {
+ case 16:
+ wl = 0;
+ break;
+ case 20:
+ wl = 0x4;
+ break;
+ case 24:
+ wl = 0x8;
+ break;
+ case 32:
+ wl = 0xc;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, WM8955_AUDIO_INTERFACE,
+ WM8955_WL_MASK, wl);
+
+ wm8955->fs = params_rate(params);
+ wm8955_set_deemph(codec);
+
+ /* If the chip is clocked then disable the clocks and force a
+ * reconfiguration, otherwise DAPM will power up the
+ * clocks for us later. */
+ ret = snd_soc_read(codec, WM8955_POWER_MANAGEMENT_1);
+ if (ret < 0)
+ return ret;
+ if (ret & WM8955_DIGENB) {
+ snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+ WM8955_DIGENB, 0);
+ snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+ WM8955_PLL_RB | WM8955_PLLEN, 0);
+
+ wm8955_configure_clocking(codec);
+ }
+
+ return 0;
+}
+
+
+static int wm8955_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8955_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int div;
+
+ switch (clk_id) {
+ case WM8955_CLK_MCLK:
+ if (freq > 15000000) {
+ priv->mclk_rate = freq /= 2;
+ div = WM8955_MCLKDIV2;
+ } else {
+ priv->mclk_rate = freq;
+ div = 0;
+ }
+
+ snd_soc_update_bits(codec, WM8955_SAMPLE_RATE,
+ WM8955_MCLKDIV2, div);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+ return 0;
+}
+
+static int wm8955_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 aif = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif |= WM8955_MS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_B:
+ aif |= WM8955_LRP;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif |= 0x3;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aif |= 0x2;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif |= 0x1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif |= WM8955_BCLKINV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif |= WM8955_BCLKINV | WM8955_LRP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif |= WM8955_BCLKINV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif |= WM8955_LRP;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8955_AUDIO_INTERFACE,
+ WM8955_MS | WM8955_FORMAT_MASK | WM8955_BCLKINV |
+ WM8955_LRP, aif);
+
+ return 0;
+}
+
+
+static int wm8955_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int val;
+
+ if (mute)
+ val = WM8955_DACMU;
+ else
+ val = 0;
+
+ snd_soc_update_bits(codec, WM8955_DAC_CONTROL, WM8955_DACMU, val);
+
+ return 0;
+}
+
+static int wm8955_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID resistance 2*50k */
+ snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+ WM8955_VMIDSEL_MASK,
+ 0x1 << WM8955_VMIDSEL_SHIFT);
+
+ /* Default bias current */
+ snd_soc_update_bits(codec, WM8955_ADDITIONAL_CONTROL_1,
+ WM8955_VSEL_MASK,
+ 0x2 << WM8955_VSEL_SHIFT);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies),
+ wm8955->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ regcache_sync(wm8955->regmap);
+
+ /* Enable VREF and VMID */
+ snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+ WM8955_VREF |
+ WM8955_VMIDSEL_MASK,
+ WM8955_VREF |
+ 0x3 << WM8955_VREF_SHIFT);
+
+ /* Let VMID ramp */
+ msleep(500);
+
+ /* High resistance VROI to maintain outputs */
+ snd_soc_update_bits(codec,
+ WM8955_ADDITIONAL_CONTROL_3,
+ WM8955_VROI, WM8955_VROI);
+ }
+
+ /* Maintain VMID with 2*250k */
+ snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+ WM8955_VMIDSEL_MASK,
+ 0x2 << WM8955_VMIDSEL_SHIFT);
+
+ /* Minimum bias current */
+ snd_soc_update_bits(codec, WM8955_ADDITIONAL_CONTROL_1,
+ WM8955_VSEL_MASK, 0);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Low resistance VROI to help discharge */
+ snd_soc_update_bits(codec,
+ WM8955_ADDITIONAL_CONTROL_3,
+ WM8955_VROI, 0);
+
+ /* Turn off VMID and VREF */
+ snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+ WM8955_VREF |
+ WM8955_VMIDSEL_MASK, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies),
+ wm8955->supplies);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8955_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8955_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8955_dai_ops = {
+ .set_sysclk = wm8955_set_sysclk,
+ .set_fmt = wm8955_set_fmt,
+ .hw_params = wm8955_hw_params,
+ .digital_mute = wm8955_digital_mute,
+};
+
+static struct snd_soc_dai_driver wm8955_dai = {
+ .name = "wm8955-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8955_RATES,
+ .formats = WM8955_FORMATS,
+ },
+ .ops = &wm8955_dai_ops,
+};
+
+static int wm8955_probe(struct snd_soc_codec *codec)
+{
+ struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
+ struct wm8955_pdata *pdata = dev_get_platdata(codec->dev);
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8955->supplies); i++)
+ wm8955->supplies[i].supply = wm8955_supply_names[i];
+
+ ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8955->supplies),
+ wm8955->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies),
+ wm8955->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm8955_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ goto err_enable;
+ }
+
+ /* Change some default settings - latch VU and enable ZC */
+ snd_soc_update_bits(codec, WM8955_LEFT_DAC_VOLUME,
+ WM8955_LDVU, WM8955_LDVU);
+ snd_soc_update_bits(codec, WM8955_RIGHT_DAC_VOLUME,
+ WM8955_RDVU, WM8955_RDVU);
+ snd_soc_update_bits(codec, WM8955_LOUT1_VOLUME,
+ WM8955_LO1VU | WM8955_LO1ZC,
+ WM8955_LO1VU | WM8955_LO1ZC);
+ snd_soc_update_bits(codec, WM8955_ROUT1_VOLUME,
+ WM8955_RO1VU | WM8955_RO1ZC,
+ WM8955_RO1VU | WM8955_RO1ZC);
+ snd_soc_update_bits(codec, WM8955_LOUT2_VOLUME,
+ WM8955_LO2VU | WM8955_LO2ZC,
+ WM8955_LO2VU | WM8955_LO2ZC);
+ snd_soc_update_bits(codec, WM8955_ROUT2_VOLUME,
+ WM8955_RO2VU | WM8955_RO2ZC,
+ WM8955_RO2VU | WM8955_RO2ZC);
+ snd_soc_update_bits(codec, WM8955_MONOOUT_VOLUME,
+ WM8955_MOZC, WM8955_MOZC);
+
+ /* Also enable adaptive bass boost by default */
+ snd_soc_update_bits(codec, WM8955_BASS_CONTROL, WM8955_BB, WM8955_BB);
+
+ /* Set platform data values */
+ if (pdata) {
+ if (pdata->out2_speaker)
+ snd_soc_update_bits(codec, WM8955_ADDITIONAL_CONTROL_2,
+ WM8955_ROUT2INV, WM8955_ROUT2INV);
+
+ if (pdata->monoin_diff)
+ snd_soc_update_bits(codec, WM8955_MONO_OUT_MIX_1,
+ WM8955_DMEN, WM8955_DMEN);
+ }
+
+ wm8955_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Bias level configuration will have done an extra enable */
+ regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
+
+ return 0;
+
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
+ return ret;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8955 = {
+ .probe = wm8955_probe,
+ .set_bias_level = wm8955_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8955_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8955_snd_controls),
+ .dapm_widgets = wm8955_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8955_dapm_widgets),
+ .dapm_routes = wm8955_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8955_dapm_routes),
+};
+
+static const struct regmap_config wm8955_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = WM8955_MAX_REGISTER,
+ .volatile_reg = wm8955_volatile,
+ .writeable_reg = wm8955_writeable,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm8955_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8955_reg_defaults),
+};
+
+static int wm8955_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8955_priv *wm8955;
+ int ret;
+
+ wm8955 = devm_kzalloc(&i2c->dev, sizeof(struct wm8955_priv),
+ GFP_KERNEL);
+ if (wm8955 == NULL)
+ return -ENOMEM;
+
+ wm8955->regmap = devm_regmap_init_i2c(i2c, &wm8955_regmap);
+ if (IS_ERR(wm8955->regmap)) {
+ ret = PTR_ERR(wm8955->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, wm8955);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8955, &wm8955_dai, 1);
+
+ return ret;
+}
+
+static int wm8955_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm8955_i2c_id[] = {
+ { "wm8955", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8955_i2c_id);
+
+static struct i2c_driver wm8955_i2c_driver = {
+ .driver = {
+ .name = "wm8955",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8955_i2c_probe,
+ .remove = wm8955_i2c_remove,
+ .id_table = wm8955_i2c_id,
+};
+
+module_i2c_driver(wm8955_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8955 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8955.h b/kernel/sound/soc/codecs/wm8955.h
new file mode 100644
index 000000000..d13fd5c5f
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8955.h
@@ -0,0 +1,486 @@
+/*
+ * wm8955.h -- WM8904 ASoC driver
+ *
+ * Copyright 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8955_H
+#define _WM8955_H
+
+#define WM8955_CLK_MCLK 1
+
+/*
+ * Register values.
+ */
+#define WM8955_LOUT1_VOLUME 0x02
+#define WM8955_ROUT1_VOLUME 0x03
+#define WM8955_DAC_CONTROL 0x05
+#define WM8955_AUDIO_INTERFACE 0x07
+#define WM8955_SAMPLE_RATE 0x08
+#define WM8955_LEFT_DAC_VOLUME 0x0A
+#define WM8955_RIGHT_DAC_VOLUME 0x0B
+#define WM8955_BASS_CONTROL 0x0C
+#define WM8955_TREBLE_CONTROL 0x0D
+#define WM8955_RESET 0x0F
+#define WM8955_ADDITIONAL_CONTROL_1 0x17
+#define WM8955_ADDITIONAL_CONTROL_2 0x18
+#define WM8955_POWER_MANAGEMENT_1 0x19
+#define WM8955_POWER_MANAGEMENT_2 0x1A
+#define WM8955_ADDITIONAL_CONTROL_3 0x1B
+#define WM8955_LEFT_OUT_MIX_1 0x22
+#define WM8955_LEFT_OUT_MIX_2 0x23
+#define WM8955_RIGHT_OUT_MIX_1 0x24
+#define WM8955_RIGHT_OUT_MIX_2 0x25
+#define WM8955_MONO_OUT_MIX_1 0x26
+#define WM8955_MONO_OUT_MIX_2 0x27
+#define WM8955_LOUT2_VOLUME 0x28
+#define WM8955_ROUT2_VOLUME 0x29
+#define WM8955_MONOOUT_VOLUME 0x2A
+#define WM8955_CLOCKING_PLL 0x2B
+#define WM8955_PLL_CONTROL_1 0x2C
+#define WM8955_PLL_CONTROL_2 0x2D
+#define WM8955_PLL_CONTROL_3 0x2E
+#define WM8955_PLL_CONTROL_4 0x3B
+
+#define WM8955_REGISTER_COUNT 29
+#define WM8955_MAX_REGISTER 0x3B
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R2 (0x02) - LOUT1 volume
+ */
+#define WM8955_LO1VU 0x0100 /* LO1VU */
+#define WM8955_LO1VU_MASK 0x0100 /* LO1VU */
+#define WM8955_LO1VU_SHIFT 8 /* LO1VU */
+#define WM8955_LO1VU_WIDTH 1 /* LO1VU */
+#define WM8955_LO1ZC 0x0080 /* LO1ZC */
+#define WM8955_LO1ZC_MASK 0x0080 /* LO1ZC */
+#define WM8955_LO1ZC_SHIFT 7 /* LO1ZC */
+#define WM8955_LO1ZC_WIDTH 1 /* LO1ZC */
+#define WM8955_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */
+#define WM8955_LOUTVOL_SHIFT 0 /* LOUTVOL - [6:0] */
+#define WM8955_LOUTVOL_WIDTH 7 /* LOUTVOL - [6:0] */
+
+/*
+ * R3 (0x03) - ROUT1 volume
+ */
+#define WM8955_RO1VU 0x0100 /* RO1VU */
+#define WM8955_RO1VU_MASK 0x0100 /* RO1VU */
+#define WM8955_RO1VU_SHIFT 8 /* RO1VU */
+#define WM8955_RO1VU_WIDTH 1 /* RO1VU */
+#define WM8955_RO1ZC 0x0080 /* RO1ZC */
+#define WM8955_RO1ZC_MASK 0x0080 /* RO1ZC */
+#define WM8955_RO1ZC_SHIFT 7 /* RO1ZC */
+#define WM8955_RO1ZC_WIDTH 1 /* RO1ZC */
+#define WM8955_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */
+#define WM8955_ROUTVOL_SHIFT 0 /* ROUTVOL - [6:0] */
+#define WM8955_ROUTVOL_WIDTH 7 /* ROUTVOL - [6:0] */
+
+/*
+ * R5 (0x05) - DAC Control
+ */
+#define WM8955_DAT 0x0080 /* DAT */
+#define WM8955_DAT_MASK 0x0080 /* DAT */
+#define WM8955_DAT_SHIFT 7 /* DAT */
+#define WM8955_DAT_WIDTH 1 /* DAT */
+#define WM8955_DACMU 0x0008 /* DACMU */
+#define WM8955_DACMU_MASK 0x0008 /* DACMU */
+#define WM8955_DACMU_SHIFT 3 /* DACMU */
+#define WM8955_DACMU_WIDTH 1 /* DACMU */
+#define WM8955_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */
+#define WM8955_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */
+#define WM8955_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */
+
+/*
+ * R7 (0x07) - Audio Interface
+ */
+#define WM8955_BCLKINV 0x0080 /* BCLKINV */
+#define WM8955_BCLKINV_MASK 0x0080 /* BCLKINV */
+#define WM8955_BCLKINV_SHIFT 7 /* BCLKINV */
+#define WM8955_BCLKINV_WIDTH 1 /* BCLKINV */
+#define WM8955_MS 0x0040 /* MS */
+#define WM8955_MS_MASK 0x0040 /* MS */
+#define WM8955_MS_SHIFT 6 /* MS */
+#define WM8955_MS_WIDTH 1 /* MS */
+#define WM8955_LRSWAP 0x0020 /* LRSWAP */
+#define WM8955_LRSWAP_MASK 0x0020 /* LRSWAP */
+#define WM8955_LRSWAP_SHIFT 5 /* LRSWAP */
+#define WM8955_LRSWAP_WIDTH 1 /* LRSWAP */
+#define WM8955_LRP 0x0010 /* LRP */
+#define WM8955_LRP_MASK 0x0010 /* LRP */
+#define WM8955_LRP_SHIFT 4 /* LRP */
+#define WM8955_LRP_WIDTH 1 /* LRP */
+#define WM8955_WL_MASK 0x000C /* WL - [3:2] */
+#define WM8955_WL_SHIFT 2 /* WL - [3:2] */
+#define WM8955_WL_WIDTH 2 /* WL - [3:2] */
+#define WM8955_FORMAT_MASK 0x0003 /* FORMAT - [1:0] */
+#define WM8955_FORMAT_SHIFT 0 /* FORMAT - [1:0] */
+#define WM8955_FORMAT_WIDTH 2 /* FORMAT - [1:0] */
+
+/*
+ * R8 (0x08) - Sample Rate
+ */
+#define WM8955_BCLKDIV2 0x0080 /* BCLKDIV2 */
+#define WM8955_BCLKDIV2_MASK 0x0080 /* BCLKDIV2 */
+#define WM8955_BCLKDIV2_SHIFT 7 /* BCLKDIV2 */
+#define WM8955_BCLKDIV2_WIDTH 1 /* BCLKDIV2 */
+#define WM8955_MCLKDIV2 0x0040 /* MCLKDIV2 */
+#define WM8955_MCLKDIV2_MASK 0x0040 /* MCLKDIV2 */
+#define WM8955_MCLKDIV2_SHIFT 6 /* MCLKDIV2 */
+#define WM8955_MCLKDIV2_WIDTH 1 /* MCLKDIV2 */
+#define WM8955_SR_MASK 0x003E /* SR - [5:1] */
+#define WM8955_SR_SHIFT 1 /* SR - [5:1] */
+#define WM8955_SR_WIDTH 5 /* SR - [5:1] */
+#define WM8955_USB 0x0001 /* USB */
+#define WM8955_USB_MASK 0x0001 /* USB */
+#define WM8955_USB_SHIFT 0 /* USB */
+#define WM8955_USB_WIDTH 1 /* USB */
+
+/*
+ * R10 (0x0A) - Left DAC volume
+ */
+#define WM8955_LDVU 0x0100 /* LDVU */
+#define WM8955_LDVU_MASK 0x0100 /* LDVU */
+#define WM8955_LDVU_SHIFT 8 /* LDVU */
+#define WM8955_LDVU_WIDTH 1 /* LDVU */
+#define WM8955_LDACVOL_MASK 0x00FF /* LDACVOL - [7:0] */
+#define WM8955_LDACVOL_SHIFT 0 /* LDACVOL - [7:0] */
+#define WM8955_LDACVOL_WIDTH 8 /* LDACVOL - [7:0] */
+
+/*
+ * R11 (0x0B) - Right DAC volume
+ */
+#define WM8955_RDVU 0x0100 /* RDVU */
+#define WM8955_RDVU_MASK 0x0100 /* RDVU */
+#define WM8955_RDVU_SHIFT 8 /* RDVU */
+#define WM8955_RDVU_WIDTH 1 /* RDVU */
+#define WM8955_RDACVOL_MASK 0x00FF /* RDACVOL - [7:0] */
+#define WM8955_RDACVOL_SHIFT 0 /* RDACVOL - [7:0] */
+#define WM8955_RDACVOL_WIDTH 8 /* RDACVOL - [7:0] */
+
+/*
+ * R12 (0x0C) - Bass control
+ */
+#define WM8955_BB 0x0080 /* BB */
+#define WM8955_BB_MASK 0x0080 /* BB */
+#define WM8955_BB_SHIFT 7 /* BB */
+#define WM8955_BB_WIDTH 1 /* BB */
+#define WM8955_BC 0x0040 /* BC */
+#define WM8955_BC_MASK 0x0040 /* BC */
+#define WM8955_BC_SHIFT 6 /* BC */
+#define WM8955_BC_WIDTH 1 /* BC */
+#define WM8955_BASS_MASK 0x000F /* BASS - [3:0] */
+#define WM8955_BASS_SHIFT 0 /* BASS - [3:0] */
+#define WM8955_BASS_WIDTH 4 /* BASS - [3:0] */
+
+/*
+ * R13 (0x0D) - Treble control
+ */
+#define WM8955_TC 0x0040 /* TC */
+#define WM8955_TC_MASK 0x0040 /* TC */
+#define WM8955_TC_SHIFT 6 /* TC */
+#define WM8955_TC_WIDTH 1 /* TC */
+#define WM8955_TRBL_MASK 0x000F /* TRBL - [3:0] */
+#define WM8955_TRBL_SHIFT 0 /* TRBL - [3:0] */
+#define WM8955_TRBL_WIDTH 4 /* TRBL - [3:0] */
+
+/*
+ * R15 (0x0F) - Reset
+ */
+#define WM8955_RESET_MASK 0x01FF /* RESET - [8:0] */
+#define WM8955_RESET_SHIFT 0 /* RESET - [8:0] */
+#define WM8955_RESET_WIDTH 9 /* RESET - [8:0] */
+
+/*
+ * R23 (0x17) - Additional control (1)
+ */
+#define WM8955_TSDEN 0x0100 /* TSDEN */
+#define WM8955_TSDEN_MASK 0x0100 /* TSDEN */
+#define WM8955_TSDEN_SHIFT 8 /* TSDEN */
+#define WM8955_TSDEN_WIDTH 1 /* TSDEN */
+#define WM8955_VSEL_MASK 0x00C0 /* VSEL - [7:6] */
+#define WM8955_VSEL_SHIFT 6 /* VSEL - [7:6] */
+#define WM8955_VSEL_WIDTH 2 /* VSEL - [7:6] */
+#define WM8955_DMONOMIX_MASK 0x0030 /* DMONOMIX - [5:4] */
+#define WM8955_DMONOMIX_SHIFT 4 /* DMONOMIX - [5:4] */
+#define WM8955_DMONOMIX_WIDTH 2 /* DMONOMIX - [5:4] */
+#define WM8955_DACINV 0x0002 /* DACINV */
+#define WM8955_DACINV_MASK 0x0002 /* DACINV */
+#define WM8955_DACINV_SHIFT 1 /* DACINV */
+#define WM8955_DACINV_WIDTH 1 /* DACINV */
+#define WM8955_TOEN 0x0001 /* TOEN */
+#define WM8955_TOEN_MASK 0x0001 /* TOEN */
+#define WM8955_TOEN_SHIFT 0 /* TOEN */
+#define WM8955_TOEN_WIDTH 1 /* TOEN */
+
+/*
+ * R24 (0x18) - Additional control (2)
+ */
+#define WM8955_OUT3SW_MASK 0x0180 /* OUT3SW - [8:7] */
+#define WM8955_OUT3SW_SHIFT 7 /* OUT3SW - [8:7] */
+#define WM8955_OUT3SW_WIDTH 2 /* OUT3SW - [8:7] */
+#define WM8955_ROUT2INV 0x0010 /* ROUT2INV */
+#define WM8955_ROUT2INV_MASK 0x0010 /* ROUT2INV */
+#define WM8955_ROUT2INV_SHIFT 4 /* ROUT2INV */
+#define WM8955_ROUT2INV_WIDTH 1 /* ROUT2INV */
+#define WM8955_DACOSR 0x0001 /* DACOSR */
+#define WM8955_DACOSR_MASK 0x0001 /* DACOSR */
+#define WM8955_DACOSR_SHIFT 0 /* DACOSR */
+#define WM8955_DACOSR_WIDTH 1 /* DACOSR */
+
+/*
+ * R25 (0x19) - Power Management (1)
+ */
+#define WM8955_VMIDSEL_MASK 0x0180 /* VMIDSEL - [8:7] */
+#define WM8955_VMIDSEL_SHIFT 7 /* VMIDSEL - [8:7] */
+#define WM8955_VMIDSEL_WIDTH 2 /* VMIDSEL - [8:7] */
+#define WM8955_VREF 0x0040 /* VREF */
+#define WM8955_VREF_MASK 0x0040 /* VREF */
+#define WM8955_VREF_SHIFT 6 /* VREF */
+#define WM8955_VREF_WIDTH 1 /* VREF */
+#define WM8955_DIGENB 0x0001 /* DIGENB */
+#define WM8955_DIGENB_MASK 0x0001 /* DIGENB */
+#define WM8955_DIGENB_SHIFT 0 /* DIGENB */
+#define WM8955_DIGENB_WIDTH 1 /* DIGENB */
+
+/*
+ * R26 (0x1A) - Power Management (2)
+ */
+#define WM8955_DACL 0x0100 /* DACL */
+#define WM8955_DACL_MASK 0x0100 /* DACL */
+#define WM8955_DACL_SHIFT 8 /* DACL */
+#define WM8955_DACL_WIDTH 1 /* DACL */
+#define WM8955_DACR 0x0080 /* DACR */
+#define WM8955_DACR_MASK 0x0080 /* DACR */
+#define WM8955_DACR_SHIFT 7 /* DACR */
+#define WM8955_DACR_WIDTH 1 /* DACR */
+#define WM8955_LOUT1 0x0040 /* LOUT1 */
+#define WM8955_LOUT1_MASK 0x0040 /* LOUT1 */
+#define WM8955_LOUT1_SHIFT 6 /* LOUT1 */
+#define WM8955_LOUT1_WIDTH 1 /* LOUT1 */
+#define WM8955_ROUT1 0x0020 /* ROUT1 */
+#define WM8955_ROUT1_MASK 0x0020 /* ROUT1 */
+#define WM8955_ROUT1_SHIFT 5 /* ROUT1 */
+#define WM8955_ROUT1_WIDTH 1 /* ROUT1 */
+#define WM8955_LOUT2 0x0010 /* LOUT2 */
+#define WM8955_LOUT2_MASK 0x0010 /* LOUT2 */
+#define WM8955_LOUT2_SHIFT 4 /* LOUT2 */
+#define WM8955_LOUT2_WIDTH 1 /* LOUT2 */
+#define WM8955_ROUT2 0x0008 /* ROUT2 */
+#define WM8955_ROUT2_MASK 0x0008 /* ROUT2 */
+#define WM8955_ROUT2_SHIFT 3 /* ROUT2 */
+#define WM8955_ROUT2_WIDTH 1 /* ROUT2 */
+#define WM8955_MONO 0x0004 /* MONO */
+#define WM8955_MONO_MASK 0x0004 /* MONO */
+#define WM8955_MONO_SHIFT 2 /* MONO */
+#define WM8955_MONO_WIDTH 1 /* MONO */
+#define WM8955_OUT3 0x0002 /* OUT3 */
+#define WM8955_OUT3_MASK 0x0002 /* OUT3 */
+#define WM8955_OUT3_SHIFT 1 /* OUT3 */
+#define WM8955_OUT3_WIDTH 1 /* OUT3 */
+
+/*
+ * R27 (0x1B) - Additional Control (3)
+ */
+#define WM8955_VROI 0x0040 /* VROI */
+#define WM8955_VROI_MASK 0x0040 /* VROI */
+#define WM8955_VROI_SHIFT 6 /* VROI */
+#define WM8955_VROI_WIDTH 1 /* VROI */
+
+/*
+ * R34 (0x22) - Left out Mix (1)
+ */
+#define WM8955_LD2LO 0x0100 /* LD2LO */
+#define WM8955_LD2LO_MASK 0x0100 /* LD2LO */
+#define WM8955_LD2LO_SHIFT 8 /* LD2LO */
+#define WM8955_LD2LO_WIDTH 1 /* LD2LO */
+#define WM8955_LI2LO 0x0080 /* LI2LO */
+#define WM8955_LI2LO_MASK 0x0080 /* LI2LO */
+#define WM8955_LI2LO_SHIFT 7 /* LI2LO */
+#define WM8955_LI2LO_WIDTH 1 /* LI2LO */
+#define WM8955_LI2LOVOL_MASK 0x0070 /* LI2LOVOL - [6:4] */
+#define WM8955_LI2LOVOL_SHIFT 4 /* LI2LOVOL - [6:4] */
+#define WM8955_LI2LOVOL_WIDTH 3 /* LI2LOVOL - [6:4] */
+
+/*
+ * R35 (0x23) - Left out Mix (2)
+ */
+#define WM8955_RD2LO 0x0100 /* RD2LO */
+#define WM8955_RD2LO_MASK 0x0100 /* RD2LO */
+#define WM8955_RD2LO_SHIFT 8 /* RD2LO */
+#define WM8955_RD2LO_WIDTH 1 /* RD2LO */
+#define WM8955_RI2LO 0x0080 /* RI2LO */
+#define WM8955_RI2LO_MASK 0x0080 /* RI2LO */
+#define WM8955_RI2LO_SHIFT 7 /* RI2LO */
+#define WM8955_RI2LO_WIDTH 1 /* RI2LO */
+#define WM8955_RI2LOVOL_MASK 0x0070 /* RI2LOVOL - [6:4] */
+#define WM8955_RI2LOVOL_SHIFT 4 /* RI2LOVOL - [6:4] */
+#define WM8955_RI2LOVOL_WIDTH 3 /* RI2LOVOL - [6:4] */
+
+/*
+ * R36 (0x24) - Right out Mix (1)
+ */
+#define WM8955_LD2RO 0x0100 /* LD2RO */
+#define WM8955_LD2RO_MASK 0x0100 /* LD2RO */
+#define WM8955_LD2RO_SHIFT 8 /* LD2RO */
+#define WM8955_LD2RO_WIDTH 1 /* LD2RO */
+#define WM8955_LI2RO 0x0080 /* LI2RO */
+#define WM8955_LI2RO_MASK 0x0080 /* LI2RO */
+#define WM8955_LI2RO_SHIFT 7 /* LI2RO */
+#define WM8955_LI2RO_WIDTH 1 /* LI2RO */
+#define WM8955_LI2ROVOL_MASK 0x0070 /* LI2ROVOL - [6:4] */
+#define WM8955_LI2ROVOL_SHIFT 4 /* LI2ROVOL - [6:4] */
+#define WM8955_LI2ROVOL_WIDTH 3 /* LI2ROVOL - [6:4] */
+
+/*
+ * R37 (0x25) - Right Out Mix (2)
+ */
+#define WM8955_RD2RO 0x0100 /* RD2RO */
+#define WM8955_RD2RO_MASK 0x0100 /* RD2RO */
+#define WM8955_RD2RO_SHIFT 8 /* RD2RO */
+#define WM8955_RD2RO_WIDTH 1 /* RD2RO */
+#define WM8955_RI2RO 0x0080 /* RI2RO */
+#define WM8955_RI2RO_MASK 0x0080 /* RI2RO */
+#define WM8955_RI2RO_SHIFT 7 /* RI2RO */
+#define WM8955_RI2RO_WIDTH 1 /* RI2RO */
+#define WM8955_RI2ROVOL_MASK 0x0070 /* RI2ROVOL - [6:4] */
+#define WM8955_RI2ROVOL_SHIFT 4 /* RI2ROVOL - [6:4] */
+#define WM8955_RI2ROVOL_WIDTH 3 /* RI2ROVOL - [6:4] */
+
+/*
+ * R38 (0x26) - Mono out Mix (1)
+ */
+#define WM8955_LD2MO 0x0100 /* LD2MO */
+#define WM8955_LD2MO_MASK 0x0100 /* LD2MO */
+#define WM8955_LD2MO_SHIFT 8 /* LD2MO */
+#define WM8955_LD2MO_WIDTH 1 /* LD2MO */
+#define WM8955_LI2MO 0x0080 /* LI2MO */
+#define WM8955_LI2MO_MASK 0x0080 /* LI2MO */
+#define WM8955_LI2MO_SHIFT 7 /* LI2MO */
+#define WM8955_LI2MO_WIDTH 1 /* LI2MO */
+#define WM8955_LI2MOVOL_MASK 0x0070 /* LI2MOVOL - [6:4] */
+#define WM8955_LI2MOVOL_SHIFT 4 /* LI2MOVOL - [6:4] */
+#define WM8955_LI2MOVOL_WIDTH 3 /* LI2MOVOL - [6:4] */
+#define WM8955_DMEN 0x0001 /* DMEN */
+#define WM8955_DMEN_MASK 0x0001 /* DMEN */
+#define WM8955_DMEN_SHIFT 0 /* DMEN */
+#define WM8955_DMEN_WIDTH 1 /* DMEN */
+
+/*
+ * R39 (0x27) - Mono out Mix (2)
+ */
+#define WM8955_RD2MO 0x0100 /* RD2MO */
+#define WM8955_RD2MO_MASK 0x0100 /* RD2MO */
+#define WM8955_RD2MO_SHIFT 8 /* RD2MO */
+#define WM8955_RD2MO_WIDTH 1 /* RD2MO */
+#define WM8955_RI2MO 0x0080 /* RI2MO */
+#define WM8955_RI2MO_MASK 0x0080 /* RI2MO */
+#define WM8955_RI2MO_SHIFT 7 /* RI2MO */
+#define WM8955_RI2MO_WIDTH 1 /* RI2MO */
+#define WM8955_RI2MOVOL_MASK 0x0070 /* RI2MOVOL - [6:4] */
+#define WM8955_RI2MOVOL_SHIFT 4 /* RI2MOVOL - [6:4] */
+#define WM8955_RI2MOVOL_WIDTH 3 /* RI2MOVOL - [6:4] */
+
+/*
+ * R40 (0x28) - LOUT2 volume
+ */
+#define WM8955_LO2VU 0x0100 /* LO2VU */
+#define WM8955_LO2VU_MASK 0x0100 /* LO2VU */
+#define WM8955_LO2VU_SHIFT 8 /* LO2VU */
+#define WM8955_LO2VU_WIDTH 1 /* LO2VU */
+#define WM8955_LO2ZC 0x0080 /* LO2ZC */
+#define WM8955_LO2ZC_MASK 0x0080 /* LO2ZC */
+#define WM8955_LO2ZC_SHIFT 7 /* LO2ZC */
+#define WM8955_LO2ZC_WIDTH 1 /* LO2ZC */
+#define WM8955_LOUT2VOL_MASK 0x007F /* LOUT2VOL - [6:0] */
+#define WM8955_LOUT2VOL_SHIFT 0 /* LOUT2VOL - [6:0] */
+#define WM8955_LOUT2VOL_WIDTH 7 /* LOUT2VOL - [6:0] */
+
+/*
+ * R41 (0x29) - ROUT2 volume
+ */
+#define WM8955_RO2VU 0x0100 /* RO2VU */
+#define WM8955_RO2VU_MASK 0x0100 /* RO2VU */
+#define WM8955_RO2VU_SHIFT 8 /* RO2VU */
+#define WM8955_RO2VU_WIDTH 1 /* RO2VU */
+#define WM8955_RO2ZC 0x0080 /* RO2ZC */
+#define WM8955_RO2ZC_MASK 0x0080 /* RO2ZC */
+#define WM8955_RO2ZC_SHIFT 7 /* RO2ZC */
+#define WM8955_RO2ZC_WIDTH 1 /* RO2ZC */
+#define WM8955_ROUT2VOL_MASK 0x007F /* ROUT2VOL - [6:0] */
+#define WM8955_ROUT2VOL_SHIFT 0 /* ROUT2VOL - [6:0] */
+#define WM8955_ROUT2VOL_WIDTH 7 /* ROUT2VOL - [6:0] */
+
+/*
+ * R42 (0x2A) - MONOOUT volume
+ */
+#define WM8955_MOZC 0x0080 /* MOZC */
+#define WM8955_MOZC_MASK 0x0080 /* MOZC */
+#define WM8955_MOZC_SHIFT 7 /* MOZC */
+#define WM8955_MOZC_WIDTH 1 /* MOZC */
+#define WM8955_MOUTVOL_MASK 0x007F /* MOUTVOL - [6:0] */
+#define WM8955_MOUTVOL_SHIFT 0 /* MOUTVOL - [6:0] */
+#define WM8955_MOUTVOL_WIDTH 7 /* MOUTVOL - [6:0] */
+
+/*
+ * R43 (0x2B) - Clocking / PLL
+ */
+#define WM8955_MCLKSEL 0x0100 /* MCLKSEL */
+#define WM8955_MCLKSEL_MASK 0x0100 /* MCLKSEL */
+#define WM8955_MCLKSEL_SHIFT 8 /* MCLKSEL */
+#define WM8955_MCLKSEL_WIDTH 1 /* MCLKSEL */
+#define WM8955_PLLOUTDIV2 0x0020 /* PLLOUTDIV2 */
+#define WM8955_PLLOUTDIV2_MASK 0x0020 /* PLLOUTDIV2 */
+#define WM8955_PLLOUTDIV2_SHIFT 5 /* PLLOUTDIV2 */
+#define WM8955_PLLOUTDIV2_WIDTH 1 /* PLLOUTDIV2 */
+#define WM8955_PLL_RB 0x0010 /* PLL_RB */
+#define WM8955_PLL_RB_MASK 0x0010 /* PLL_RB */
+#define WM8955_PLL_RB_SHIFT 4 /* PLL_RB */
+#define WM8955_PLL_RB_WIDTH 1 /* PLL_RB */
+#define WM8955_PLLEN 0x0008 /* PLLEN */
+#define WM8955_PLLEN_MASK 0x0008 /* PLLEN */
+#define WM8955_PLLEN_SHIFT 3 /* PLLEN */
+#define WM8955_PLLEN_WIDTH 1 /* PLLEN */
+
+/*
+ * R44 (0x2C) - PLL Control 1
+ */
+#define WM8955_N_MASK 0x01E0 /* N - [8:5] */
+#define WM8955_N_SHIFT 5 /* N - [8:5] */
+#define WM8955_N_WIDTH 4 /* N - [8:5] */
+#define WM8955_K_21_18_MASK 0x000F /* K(21:18) - [3:0] */
+#define WM8955_K_21_18_SHIFT 0 /* K(21:18) - [3:0] */
+#define WM8955_K_21_18_WIDTH 4 /* K(21:18) - [3:0] */
+
+/*
+ * R45 (0x2D) - PLL Control 2
+ */
+#define WM8955_K_17_9_MASK 0x01FF /* K(17:9) - [8:0] */
+#define WM8955_K_17_9_SHIFT 0 /* K(17:9) - [8:0] */
+#define WM8955_K_17_9_WIDTH 9 /* K(17:9) - [8:0] */
+
+/*
+ * R46 (0x2E) - PLL Control 3
+ */
+#define WM8955_K_8_0_MASK 0x01FF /* K(8:0) - [8:0] */
+#define WM8955_K_8_0_SHIFT 0 /* K(8:0) - [8:0] */
+#define WM8955_K_8_0_WIDTH 9 /* K(8:0) - [8:0] */
+
+/*
+ * R59 (0x3B) - PLL Control 4
+ */
+#define WM8955_KEN 0x0080 /* KEN */
+#define WM8955_KEN_MASK 0x0080 /* KEN */
+#define WM8955_KEN_SHIFT 7 /* KEN */
+#define WM8955_KEN_WIDTH 1 /* KEN */
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8958-dsp2.c b/kernel/sound/soc/codecs/wm8958-dsp2.c
new file mode 100644
index 000000000..c799cca5a
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8958-dsp2.c
@@ -0,0 +1,1031 @@
+/*
+ * wm8958-dsp2.c -- WM8958 DSP2 support
+ *
+ * Copyright 2011 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/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/slab.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <trace/events/asoc.h>
+
+#include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/mfd/wm8994/pdata.h>
+#include <linux/mfd/wm8994/gpio.h>
+
+#include "wm8994.h"
+
+#define WM_FW_BLOCK_INFO 0xff
+#define WM_FW_BLOCK_PM 0x00
+#define WM_FW_BLOCK_X 0x01
+#define WM_FW_BLOCK_Y 0x02
+#define WM_FW_BLOCK_Z 0x03
+#define WM_FW_BLOCK_I 0x06
+#define WM_FW_BLOCK_A 0x08
+#define WM_FW_BLOCK_C 0x0c
+
+static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name,
+ const struct firmware *fw, bool check)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ u64 data64;
+ u32 data32;
+ const u8 *data;
+ char *str;
+ size_t block_len, len;
+ int ret = 0;
+
+ /* Suppress unneeded downloads */
+ if (wm8994->cur_fw == fw)
+ return 0;
+
+ if (fw->size < 32) {
+ dev_err(codec->dev, "%s: firmware too short (%zd bytes)\n",
+ name, fw->size);
+ goto err;
+ }
+
+ if (memcmp(fw->data, "WMFW", 4) != 0) {
+ memcpy(&data32, fw->data, sizeof(data32));
+ data32 = be32_to_cpu(data32);
+ dev_err(codec->dev, "%s: firmware has bad file magic %08x\n",
+ name, data32);
+ goto err;
+ }
+
+ memcpy(&data32, fw->data + 4, sizeof(data32));
+ len = be32_to_cpu(data32);
+
+ memcpy(&data32, fw->data + 8, sizeof(data32));
+ data32 = be32_to_cpu(data32);
+ if ((data32 >> 24) & 0xff) {
+ dev_err(codec->dev, "%s: unsupported firmware version %d\n",
+ name, (data32 >> 24) & 0xff);
+ goto err;
+ }
+ if ((data32 & 0xffff) != 8958) {
+ dev_err(codec->dev, "%s: unsupported target device %d\n",
+ name, data32 & 0xffff);
+ goto err;
+ }
+ if (((data32 >> 16) & 0xff) != 0xc) {
+ dev_err(codec->dev, "%s: unsupported target core %d\n",
+ name, (data32 >> 16) & 0xff);
+ goto err;
+ }
+
+ if (check) {
+ memcpy(&data64, fw->data + 24, sizeof(u64));
+ dev_info(codec->dev, "%s timestamp %llx\n",
+ name, be64_to_cpu(data64));
+ } else {
+ snd_soc_write(codec, 0x102, 0x2);
+ snd_soc_write(codec, 0x900, 0x2);
+ }
+
+ data = fw->data + len;
+ len = fw->size - len;
+ while (len) {
+ if (len < 12) {
+ dev_err(codec->dev, "%s short data block of %zd\n",
+ name, len);
+ goto err;
+ }
+
+ memcpy(&data32, data + 4, sizeof(data32));
+ block_len = be32_to_cpu(data32);
+ if (block_len + 8 > len) {
+ dev_err(codec->dev, "%zd byte block longer than file\n",
+ block_len);
+ goto err;
+ }
+ if (block_len == 0) {
+ dev_err(codec->dev, "Zero length block\n");
+ goto err;
+ }
+
+ memcpy(&data32, data, sizeof(data32));
+ data32 = be32_to_cpu(data32);
+
+ switch ((data32 >> 24) & 0xff) {
+ case WM_FW_BLOCK_INFO:
+ /* Informational text */
+ if (!check)
+ break;
+
+ str = kzalloc(block_len + 1, GFP_KERNEL);
+ if (str) {
+ memcpy(str, data + 8, block_len);
+ dev_info(codec->dev, "%s: %s\n", name, str);
+ kfree(str);
+ } else {
+ dev_err(codec->dev, "Out of memory\n");
+ }
+ break;
+ case WM_FW_BLOCK_PM:
+ case WM_FW_BLOCK_X:
+ case WM_FW_BLOCK_Y:
+ case WM_FW_BLOCK_Z:
+ case WM_FW_BLOCK_I:
+ case WM_FW_BLOCK_A:
+ case WM_FW_BLOCK_C:
+ dev_dbg(codec->dev, "%s: %zd bytes of %x@%x\n", name,
+ block_len, (data32 >> 24) & 0xff,
+ data32 & 0xffffff);
+
+ if (check)
+ break;
+
+ data32 &= 0xffffff;
+
+ wm8994_bulk_write(wm8994->wm8994,
+ data32 & 0xffffff,
+ block_len / 2,
+ (void *)(data + 8));
+
+ break;
+ default:
+ dev_warn(codec->dev, "%s: unknown block type %d\n",
+ name, (data32 >> 24) & 0xff);
+ break;
+ }
+
+ /* Round up to the next 32 bit word */
+ block_len += block_len % 4;
+
+ data += block_len + 8;
+ len -= block_len + 8;
+ }
+
+ if (!check) {
+ dev_dbg(codec->dev, "%s: download done\n", name);
+ wm8994->cur_fw = fw;
+ } else {
+ dev_info(codec->dev, "%s: got firmware\n", name);
+ }
+
+ goto ok;
+
+err:
+ ret = -EINVAL;
+ok:
+ if (!check) {
+ snd_soc_write(codec, 0x900, 0x0);
+ snd_soc_write(codec, 0x102, 0x0);
+ }
+
+ return ret;
+}
+
+static void wm8958_dsp_start_mbc(struct snd_soc_codec *codec, int path)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ int i;
+
+ /* If the DSP is already running then noop */
+ if (snd_soc_read(codec, WM8958_DSP2_PROGRAM) & WM8958_DSP2_ENA)
+ return;
+
+ /* If we have MBC firmware download it */
+ if (wm8994->mbc)
+ wm8958_dsp2_fw(codec, "MBC", wm8994->mbc, false);
+
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, WM8958_DSP2_ENA);
+
+ /* If we've got user supplied MBC settings use them */
+ if (control->pdata.num_mbc_cfgs) {
+ struct wm8958_mbc_cfg *cfg
+ = &control->pdata.mbc_cfgs[wm8994->mbc_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)
+ snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1,
+ cfg->coeff_regs[i]);
+
+ for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
+ snd_soc_write(codec,
+ i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
+ cfg->cutoff_regs[i]);
+ }
+
+ /* Run the DSP */
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_RUNR);
+
+ /* And we're off! */
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_ENA |
+ WM8958_MBC_SEL_MASK,
+ path << WM8958_MBC_SEL_SHIFT |
+ WM8958_MBC_ENA);
+}
+
+static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ int i, ena;
+
+ if (wm8994->mbc_vss)
+ wm8958_dsp2_fw(codec, "MBC+VSS", wm8994->mbc_vss, false);
+
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, WM8958_DSP2_ENA);
+
+ /* If we've got user supplied settings use them */
+ if (control->pdata.num_mbc_cfgs) {
+ struct wm8958_mbc_cfg *cfg
+ = &control->pdata.mbc_cfgs[wm8994->mbc_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->combined_regs); i++)
+ snd_soc_write(codec, i + 0x2800,
+ cfg->combined_regs[i]);
+ }
+
+ if (control->pdata.num_vss_cfgs) {
+ struct wm8958_vss_cfg *cfg
+ = &control->pdata.vss_cfgs[wm8994->vss_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
+ snd_soc_write(codec, i + 0x2600, cfg->regs[i]);
+ }
+
+ if (control->pdata.num_vss_hpf_cfgs) {
+ struct wm8958_vss_hpf_cfg *cfg
+ = &control->pdata.vss_hpf_cfgs[wm8994->vss_hpf_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
+ snd_soc_write(codec, i + 0x2400, cfg->regs[i]);
+ }
+
+ /* Run the DSP */
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_RUNR);
+
+ /* Enable the algorithms we've selected */
+ ena = 0;
+ if (wm8994->mbc_ena[path])
+ ena |= 0x8;
+ if (wm8994->hpf2_ena[path])
+ ena |= 0x4;
+ if (wm8994->hpf1_ena[path])
+ ena |= 0x2;
+ if (wm8994->vss_ena[path])
+ ena |= 0x1;
+
+ snd_soc_write(codec, 0x2201, ena);
+
+ /* Switch the DSP into the data path */
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
+ path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
+}
+
+static void wm8958_dsp_start_enh_eq(struct snd_soc_codec *codec, int path)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ int i;
+
+ wm8958_dsp2_fw(codec, "ENH_EQ", wm8994->enh_eq, false);
+
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, WM8958_DSP2_ENA);
+
+ /* If we've got user supplied settings use them */
+ if (control->pdata.num_enh_eq_cfgs) {
+ struct wm8958_enh_eq_cfg *cfg
+ = &control->pdata.enh_eq_cfgs[wm8994->enh_eq_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
+ snd_soc_write(codec, i + 0x2200,
+ cfg->regs[i]);
+ }
+
+ /* Run the DSP */
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_RUNR);
+
+ /* Switch the DSP into the data path */
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
+ path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
+}
+
+static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5);
+ int ena, reg, aif;
+
+ switch (path) {
+ case 0:
+ pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);
+ aif = 0;
+ break;
+ case 1:
+ pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
+ aif = 0;
+ break;
+ case 2:
+ pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);
+ aif = 1;
+ break;
+ default:
+ WARN(1, "Invalid path %d\n", path);
+ return;
+ }
+
+ /* Do we have both an active AIF and an active algorithm? */
+ ena = wm8994->mbc_ena[path] || wm8994->vss_ena[path] ||
+ wm8994->hpf1_ena[path] || wm8994->hpf2_ena[path] ||
+ wm8994->enh_eq_ena[path];
+ if (!pwr_reg)
+ ena = 0;
+
+ reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM);
+
+ dev_dbg(codec->dev, "DSP path %d %d startup: %d, power: %x, DSP: %x\n",
+ path, wm8994->dsp_active, start, pwr_reg, reg);
+
+ if (start && ena) {
+ /* If the DSP is already running then noop */
+ if (reg & WM8958_DSP2_ENA)
+ return;
+
+ /* If either AIFnCLK is not yet enabled postpone */
+ if (!(snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
+ & WM8994_AIF1CLK_ENA_MASK) &&
+ !(snd_soc_read(codec, WM8994_AIF2_CLOCKING_1)
+ & WM8994_AIF2CLK_ENA_MASK))
+ return;
+
+ /* Switch the clock over to the appropriate AIF */
+ snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
+ aif << WM8958_DSP2CLK_SRC_SHIFT |
+ WM8958_DSP2CLK_ENA);
+
+ if (wm8994->enh_eq_ena[path])
+ wm8958_dsp_start_enh_eq(codec, path);
+ else if (wm8994->vss_ena[path] || wm8994->hpf1_ena[path] ||
+ wm8994->hpf2_ena[path])
+ wm8958_dsp_start_vss(codec, path);
+ else if (wm8994->mbc_ena[path])
+ wm8958_dsp_start_mbc(codec, path);
+
+ wm8994->dsp_active = path;
+
+ dev_dbg(codec->dev, "DSP running in path %d\n", path);
+ }
+
+ if (!start && wm8994->dsp_active == path) {
+ /* If the DSP is already stopped then noop */
+ if (!(reg & WM8958_DSP2_ENA))
+ return;
+
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_ENA, 0);
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_STOP);
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, 0);
+ snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ WM8958_DSP2CLK_ENA, 0);
+
+ wm8994->dsp_active = -1;
+
+ dev_dbg(codec->dev, "DSP stopped\n");
+ }
+}
+
+int wm8958_aif_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);
+ int i;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_PRE_PMU:
+ for (i = 0; i < 3; i++)
+ wm8958_dsp_apply(codec, i, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ case SND_SOC_DAPM_PRE_PMD:
+ for (i = 0; i < 3; i++)
+ wm8958_dsp_apply(codec, i, 0);
+ break;
+ }
+
+ return 0;
+}
+
+/* Check if DSP2 is in use on another AIF */
+static int wm8958_dsp2_busy(struct wm8994_priv *wm8994, int aif)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
+ if (i == aif)
+ continue;
+ if (wm8994->mbc_ena[i] || wm8994->vss_ena[i] ||
+ wm8994->hpf1_ena[i] || wm8994->hpf2_ena[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= control->pdata.num_mbc_cfgs)
+ return -EINVAL;
+
+ wm8994->mbc_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
+
+ return 0;
+}
+
+static int wm8958_mbc_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mbc = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
+
+ return 0;
+}
+
+static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mbc = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8994->mbc_ena[mbc] == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (wm8958_dsp2_busy(wm8994, mbc)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", mbc);
+ return -EBUSY;
+ }
+
+ if (wm8994->enh_eq_ena[mbc])
+ return -EBUSY;
+
+ wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, mbc, wm8994->mbc_ena[mbc]);
+
+ return 0;
+}
+
+#define WM8958_MBC_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_mbc_info, \
+ .get = wm8958_mbc_get, .put = wm8958_mbc_put, \
+ .private_value = xval }
+
+static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= control->pdata.num_vss_cfgs)
+ return -EINVAL;
+
+ wm8994->vss_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->vss_cfg;
+
+ return 0;
+}
+
+static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= control->pdata.num_vss_hpf_cfgs)
+ return -EINVAL;
+
+ wm8994->vss_hpf_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_vss_hpf_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->vss_hpf_cfg;
+
+ return 0;
+}
+
+static int wm8958_vss_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_vss_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int vss = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8994->vss_ena[vss];
+
+ return 0;
+}
+
+static int wm8958_vss_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int vss = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8994->vss_ena[vss] == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (!wm8994->mbc_vss)
+ return -ENODEV;
+
+ if (wm8958_dsp2_busy(wm8994, vss)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", vss);
+ return -EBUSY;
+ }
+
+ if (wm8994->enh_eq_ena[vss])
+ return -EBUSY;
+
+ wm8994->vss_ena[vss] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, vss, wm8994->vss_ena[vss]);
+
+ return 0;
+}
+
+
+#define WM8958_VSS_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_vss_info, \
+ .get = wm8958_vss_get, .put = wm8958_vss_put, \
+ .private_value = xval }
+
+static int wm8958_hpf_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_hpf_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int hpf = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (hpf < 3)
+ ucontrol->value.integer.value[0] = wm8994->hpf1_ena[hpf % 3];
+ else
+ ucontrol->value.integer.value[0] = wm8994->hpf2_ena[hpf % 3];
+
+ return 0;
+}
+
+static int wm8958_hpf_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int hpf = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (hpf < 3) {
+ if (wm8994->hpf1_ena[hpf % 3] ==
+ ucontrol->value.integer.value[0])
+ return 0;
+ } else {
+ if (wm8994->hpf2_ena[hpf % 3] ==
+ ucontrol->value.integer.value[0])
+ return 0;
+ }
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (!wm8994->mbc_vss)
+ return -ENODEV;
+
+ if (wm8958_dsp2_busy(wm8994, hpf % 3)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", hpf);
+ return -EBUSY;
+ }
+
+ if (wm8994->enh_eq_ena[hpf % 3])
+ return -EBUSY;
+
+ if (hpf < 3)
+ wm8994->hpf1_ena[hpf % 3] = ucontrol->value.integer.value[0];
+ else
+ wm8994->hpf2_ena[hpf % 3] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, hpf % 3, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+#define WM8958_HPF_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_hpf_info, \
+ .get = wm8958_hpf_get, .put = wm8958_hpf_put, \
+ .private_value = xval }
+
+static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= control->pdata.num_enh_eq_cfgs)
+ return -EINVAL;
+
+ wm8994->enh_eq_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_enh_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->enh_eq_cfg;
+
+ return 0;
+}
+
+static int wm8958_enh_eq_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_enh_eq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8994->enh_eq_ena[eq];
+
+ return 0;
+}
+
+static int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8994->enh_eq_ena[eq] == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (!wm8994->enh_eq)
+ return -ENODEV;
+
+ if (wm8958_dsp2_busy(wm8994, eq)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", eq);
+ return -EBUSY;
+ }
+
+ if (wm8994->mbc_ena[eq] || wm8994->vss_ena[eq] ||
+ wm8994->hpf1_ena[eq] || wm8994->hpf2_ena[eq])
+ return -EBUSY;
+
+ wm8994->enh_eq_ena[eq] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, eq, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+#define WM8958_ENH_EQ_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_enh_eq_info, \
+ .get = wm8958_enh_eq_get, .put = wm8958_enh_eq_put, \
+ .private_value = xval }
+
+static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = {
+WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
+WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
+WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
+};
+
+static const struct snd_kcontrol_new wm8958_vss_snd_controls[] = {
+WM8958_VSS_SWITCH("AIF1DAC1 VSS Switch", 0),
+WM8958_VSS_SWITCH("AIF1DAC2 VSS Switch", 1),
+WM8958_VSS_SWITCH("AIF2DAC VSS Switch", 2),
+WM8958_HPF_SWITCH("AIF1DAC1 HPF1 Switch", 0),
+WM8958_HPF_SWITCH("AIF1DAC2 HPF1 Switch", 1),
+WM8958_HPF_SWITCH("AIF2DAC HPF1 Switch", 2),
+WM8958_HPF_SWITCH("AIF1DAC1 HPF2 Switch", 3),
+WM8958_HPF_SWITCH("AIF1DAC2 HPF2 Switch", 4),
+WM8958_HPF_SWITCH("AIF2DAC HPF2 Switch", 5),
+};
+
+static const struct snd_kcontrol_new wm8958_enh_eq_snd_controls[] = {
+WM8958_ENH_EQ_SWITCH("AIF1DAC1 Enhanced EQ Switch", 0),
+WM8958_ENH_EQ_SWITCH("AIF1DAC2 Enhanced EQ Switch", 1),
+WM8958_ENH_EQ_SWITCH("AIF2DAC Enhanced EQ Switch", 2),
+};
+
+static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context)
+{
+ struct snd_soc_codec *codec = context;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (fw && (wm8958_dsp2_fw(codec, "ENH_EQ", fw, true) == 0)) {
+ mutex_lock(&wm8994->fw_lock);
+ wm8994->enh_eq = fw;
+ mutex_unlock(&wm8994->fw_lock);
+ }
+}
+
+static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)
+{
+ struct snd_soc_codec *codec = context;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (fw && (wm8958_dsp2_fw(codec, "MBC+VSS", fw, true) == 0)) {
+ mutex_lock(&wm8994->fw_lock);
+ wm8994->mbc_vss = fw;
+ mutex_unlock(&wm8994->fw_lock);
+ }
+}
+
+static void wm8958_mbc_loaded(const struct firmware *fw, void *context)
+{
+ struct snd_soc_codec *codec = context;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (fw && (wm8958_dsp2_fw(codec, "MBC", fw, true) == 0)) {
+ mutex_lock(&wm8994->fw_lock);
+ wm8994->mbc = fw;
+ mutex_unlock(&wm8994->fw_lock);
+ }
+}
+
+void wm8958_dsp2_init(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ struct wm8994_pdata *pdata = &control->pdata;
+ int ret, i;
+
+ wm8994->dsp_active = -1;
+
+ snd_soc_add_codec_controls(codec, wm8958_mbc_snd_controls,
+ ARRAY_SIZE(wm8958_mbc_snd_controls));
+ snd_soc_add_codec_controls(codec, wm8958_vss_snd_controls,
+ ARRAY_SIZE(wm8958_vss_snd_controls));
+ snd_soc_add_codec_controls(codec, wm8958_enh_eq_snd_controls,
+ ARRAY_SIZE(wm8958_enh_eq_snd_controls));
+
+
+ /* We don't *require* firmware and don't want to delay boot */
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "wm8958_mbc.wfw", codec->dev, GFP_KERNEL,
+ codec, wm8958_mbc_loaded);
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "wm8958_mbc_vss.wfw", codec->dev, GFP_KERNEL,
+ codec, wm8958_mbc_vss_loaded);
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "wm8958_enh_eq.wfw", codec->dev, GFP_KERNEL,
+ codec, wm8958_enh_eq_loaded);
+
+ if (pdata->num_mbc_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,
+ wm8958_get_mbc_enum, wm8958_put_mbc_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->mbc_texts = kmalloc(sizeof(char *)
+ * pdata->num_mbc_cfgs, GFP_KERNEL);
+ if (!wm8994->mbc_texts)
+ return;
+
+ for (i = 0; i < pdata->num_mbc_cfgs; i++)
+ wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;
+
+ wm8994->mbc_enum.items = pdata->num_mbc_cfgs;
+ wm8994->mbc_enum.texts = wm8994->mbc_texts;
+
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+ control, 1);
+ if (ret != 0)
+ dev_err(wm8994->hubs.codec->dev,
+ "Failed to add MBC mode controls: %d\n", ret);
+ }
+
+ if (pdata->num_vss_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("VSS Mode", wm8994->vss_enum,
+ wm8958_get_vss_enum, wm8958_put_vss_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->vss_texts = kmalloc(sizeof(char *)
+ * pdata->num_vss_cfgs, GFP_KERNEL);
+ if (!wm8994->vss_texts)
+ return;
+
+ for (i = 0; i < pdata->num_vss_cfgs; i++)
+ wm8994->vss_texts[i] = pdata->vss_cfgs[i].name;
+
+ wm8994->vss_enum.items = pdata->num_vss_cfgs;
+ wm8994->vss_enum.texts = wm8994->vss_texts;
+
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+ control, 1);
+ if (ret != 0)
+ dev_err(wm8994->hubs.codec->dev,
+ "Failed to add VSS mode controls: %d\n", ret);
+ }
+
+ if (pdata->num_vss_hpf_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("VSS HPF Mode", wm8994->vss_hpf_enum,
+ wm8958_get_vss_hpf_enum,
+ wm8958_put_vss_hpf_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->vss_hpf_texts = kmalloc(sizeof(char *)
+ * pdata->num_vss_hpf_cfgs, GFP_KERNEL);
+ if (!wm8994->vss_hpf_texts)
+ return;
+
+ for (i = 0; i < pdata->num_vss_hpf_cfgs; i++)
+ wm8994->vss_hpf_texts[i] = pdata->vss_hpf_cfgs[i].name;
+
+ wm8994->vss_hpf_enum.items = pdata->num_vss_hpf_cfgs;
+ wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts;
+
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+ control, 1);
+ if (ret != 0)
+ dev_err(wm8994->hubs.codec->dev,
+ "Failed to add VSS HPFmode controls: %d\n",
+ ret);
+ }
+
+ if (pdata->num_enh_eq_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("Enhanced EQ Mode", wm8994->enh_eq_enum,
+ wm8958_get_enh_eq_enum,
+ wm8958_put_enh_eq_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->enh_eq_texts = kmalloc(sizeof(char *)
+ * pdata->num_enh_eq_cfgs, GFP_KERNEL);
+ if (!wm8994->enh_eq_texts)
+ return;
+
+ for (i = 0; i < pdata->num_enh_eq_cfgs; i++)
+ wm8994->enh_eq_texts[i] = pdata->enh_eq_cfgs[i].name;
+
+ wm8994->enh_eq_enum.items = pdata->num_enh_eq_cfgs;
+ wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts;
+
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+ control, 1);
+ if (ret != 0)
+ dev_err(wm8994->hubs.codec->dev,
+ "Failed to add enhanced EQ controls: %d\n",
+ ret);
+ }
+}
diff --git a/kernel/sound/soc/codecs/wm8960.c b/kernel/sound/soc/codecs/wm8960.c
new file mode 100644
index 000000000..e97a7615d
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8960.c
@@ -0,0 +1,1128 @@
+/*
+ * wm8960.c -- WM8960 ALSA SoC Audio driver
+ *
+ * Copyright 2007-11 Wolfson Microelectronics, plc
+ *
+ * Author: 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>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/clk.h>
+#include <linux/i2c.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 <sound/wm8960.h>
+
+#include "wm8960.h"
+
+/* R25 - Power 1 */
+#define WM8960_VMID_MASK 0x180
+#define WM8960_VREF 0x40
+
+/* R26 - Power 2 */
+#define WM8960_PWR2_LOUT1 0x40
+#define WM8960_PWR2_ROUT1 0x20
+#define WM8960_PWR2_OUT3 0x02
+
+/* R28 - Anti-pop 1 */
+#define WM8960_POBCTRL 0x80
+#define WM8960_BUFDCOPEN 0x10
+#define WM8960_BUFIOEN 0x08
+#define WM8960_SOFT_ST 0x04
+#define WM8960_HPSTBY 0x01
+
+/* R29 - Anti-pop 2 */
+#define WM8960_DISOP 0x40
+#define WM8960_DRES_MASK 0x30
+
+/*
+ * wm8960 register cache
+ * We can't read the WM8960 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ */
+static const struct reg_default wm8960_reg_defaults[] = {
+ { 0x0, 0x00a7 },
+ { 0x1, 0x00a7 },
+ { 0x2, 0x0000 },
+ { 0x3, 0x0000 },
+ { 0x4, 0x0000 },
+ { 0x5, 0x0008 },
+ { 0x6, 0x0000 },
+ { 0x7, 0x000a },
+ { 0x8, 0x01c0 },
+ { 0x9, 0x0000 },
+ { 0xa, 0x00ff },
+ { 0xb, 0x00ff },
+
+ { 0x10, 0x0000 },
+ { 0x11, 0x007b },
+ { 0x12, 0x0100 },
+ { 0x13, 0x0032 },
+ { 0x14, 0x0000 },
+ { 0x15, 0x00c3 },
+ { 0x16, 0x00c3 },
+ { 0x17, 0x01c0 },
+ { 0x18, 0x0000 },
+ { 0x19, 0x0000 },
+ { 0x1a, 0x0000 },
+ { 0x1b, 0x0000 },
+ { 0x1c, 0x0000 },
+ { 0x1d, 0x0000 },
+
+ { 0x20, 0x0100 },
+ { 0x21, 0x0100 },
+ { 0x22, 0x0050 },
+
+ { 0x25, 0x0050 },
+ { 0x26, 0x0000 },
+ { 0x27, 0x0000 },
+ { 0x28, 0x0000 },
+ { 0x29, 0x0000 },
+ { 0x2a, 0x0040 },
+ { 0x2b, 0x0000 },
+ { 0x2c, 0x0000 },
+ { 0x2d, 0x0050 },
+ { 0x2e, 0x0050 },
+ { 0x2f, 0x0000 },
+ { 0x30, 0x0002 },
+ { 0x31, 0x0037 },
+
+ { 0x33, 0x0080 },
+ { 0x34, 0x0008 },
+ { 0x35, 0x0031 },
+ { 0x36, 0x0026 },
+ { 0x37, 0x00e9 },
+};
+
+static bool wm8960_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8960_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+struct wm8960_priv {
+ struct clk *mclk;
+ struct regmap *regmap;
+ int (*set_bias_level)(struct snd_soc_codec *,
+ enum snd_soc_bias_level level);
+ struct snd_soc_dapm_widget *lout1;
+ struct snd_soc_dapm_widget *rout1;
+ struct snd_soc_dapm_widget *out3;
+ bool deemph;
+ int playback_fs;
+ struct wm8960_data pdata;
+};
+
+#define wm8960_reset(c) regmap_write(c, WM8960_RESET, 0)
+
+/* enumerated controls */
+static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
+ "Right Inverted", "Stereo Inversion"};
+static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"};
+static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"};
+static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"};
+static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
+
+static const struct soc_enum wm8960_enum[] = {
+ SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
+ SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity),
+ SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff),
+ SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff),
+ SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc),
+ SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
+};
+
+static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
+
+static int wm8960_set_deemph(struct snd_soc_codec *codec)
+{
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ int val, i, best;
+
+ /* If we're using deemphasis select the nearest available sample
+ * rate.
+ */
+ if (wm8960->deemph) {
+ best = 1;
+ for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
+ if (abs(deemph_settings[i] - wm8960->playback_fs) <
+ abs(deemph_settings[best] - wm8960->playback_fs))
+ best = i;
+ }
+
+ val = best << 1;
+ } else {
+ val = 0;
+ }
+
+ dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+
+ return snd_soc_update_bits(codec, WM8960_DACCTL1,
+ 0x6, val);
+}
+
+static int wm8960_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8960->deemph;
+ return 0;
+}
+
+static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ int deemph = ucontrol->value.integer.value[0];
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ wm8960->deemph = deemph;
+
+ return wm8960_set_deemph(codec);
+}
+
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1);
+
+static const struct snd_kcontrol_new wm8960_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
+ 0, 63, 0, adc_tlv),
+SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
+ 6, 1, 0),
+SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
+ 7, 1, 0),
+
+SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume",
+ WM8960_INBMIX1, 4, 7, 0, boost_tlv),
+SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume",
+ WM8960_INBMIX1, 1, 7, 0, boost_tlv),
+SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume",
+ WM8960_INBMIX2, 4, 7, 0, boost_tlv),
+SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume",
+ WM8960_INBMIX2, 1, 7, 0, boost_tlv),
+
+SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC,
+ 0, 255, 0, dac_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1,
+ 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1,
+ 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2,
+ 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2,
+ 7, 1, 0),
+SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0),
+SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0),
+
+SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0),
+SOC_ENUM("ADC Polarity", wm8960_enum[0]),
+SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0),
+
+SOC_ENUM("DAC Polarity", wm8960_enum[2]),
+SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
+ wm8960_get_deemph, wm8960_put_deemph),
+
+SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]),
+SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]),
+SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0),
+SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0),
+
+SOC_ENUM("ALC Function", wm8960_enum[4]),
+SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0),
+SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1),
+SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0),
+SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0),
+SOC_ENUM("ALC Mode", wm8960_enum[5]),
+SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
+
+SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0),
+SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("ADC PCM Capture Volume", WM8960_LADC, WM8960_RADC,
+ 0, 255, 0, adc_tlv),
+
+SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume",
+ WM8960_BYPASS1, 4, 7, 1, bypass_tlv),
+SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume",
+ WM8960_LOUTMIX, 4, 7, 1, bypass_tlv),
+SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume",
+ WM8960_BYPASS2, 4, 7, 1, bypass_tlv),
+SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume",
+ WM8960_ROUTMIX, 4, 7, 1, bypass_tlv),
+};
+
+static const struct snd_kcontrol_new wm8960_lin_boost[] = {
+SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0),
+SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0),
+SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_lin[] = {
+SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_rin_boost[] = {
+SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0),
+SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0),
+SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_rin[] = {
+SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_loutput_mixer[] = {
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0),
+SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0),
+SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_routput_mixer[] = {
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0),
+SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0),
+SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_mono_out[] = {
+SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("LINPUT1"),
+SND_SOC_DAPM_INPUT("RINPUT1"),
+SND_SOC_DAPM_INPUT("LINPUT2"),
+SND_SOC_DAPM_INPUT("RINPUT2"),
+SND_SOC_DAPM_INPUT("LINPUT3"),
+SND_SOC_DAPM_INPUT("RINPUT3"),
+
+SND_SOC_DAPM_SUPPLY("MICB", WM8960_POWER1, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0,
+ wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)),
+SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0,
+ wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)),
+
+SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0,
+ wm8960_lin, ARRAY_SIZE(wm8960_lin)),
+SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0,
+ wm8960_rin, ARRAY_SIZE(wm8960_rin)),
+
+SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER1, 3, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER1, 2, 0),
+
+SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0,
+ &wm8960_loutput_mixer[0],
+ ARRAY_SIZE(wm8960_loutput_mixer)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0,
+ &wm8960_routput_mixer[0],
+ ARRAY_SIZE(wm8960_routput_mixer)),
+
+SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("SPK_LP"),
+SND_SOC_DAPM_OUTPUT("SPK_LN"),
+SND_SOC_DAPM_OUTPUT("HP_L"),
+SND_SOC_DAPM_OUTPUT("HP_R"),
+SND_SOC_DAPM_OUTPUT("SPK_RP"),
+SND_SOC_DAPM_OUTPUT("SPK_RN"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+};
+
+static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = {
+SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
+ &wm8960_mono_out[0],
+ ARRAY_SIZE(wm8960_mono_out)),
+};
+
+/* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */
+static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = {
+SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+ { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
+ { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
+ { "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" },
+
+ { "Left Input Mixer", "Boost Switch", "Left Boost Mixer", },
+ { "Left Input Mixer", NULL, "LINPUT1", }, /* Really Boost Switch */
+ { "Left Input Mixer", NULL, "LINPUT2" },
+ { "Left Input Mixer", NULL, "LINPUT3" },
+
+ { "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" },
+ { "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" },
+ { "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" },
+
+ { "Right Input Mixer", "Boost Switch", "Right Boost Mixer", },
+ { "Right Input Mixer", NULL, "RINPUT1", }, /* Really Boost Switch */
+ { "Right Input Mixer", NULL, "RINPUT2" },
+ { "Right Input Mixer", NULL, "RINPUT3" },
+
+ { "Left ADC", NULL, "Left Input Mixer" },
+ { "Right ADC", NULL, "Right Input Mixer" },
+
+ { "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" },
+ { "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer"} ,
+ { "Left Output Mixer", "PCM Playback Switch", "Left DAC" },
+
+ { "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" },
+ { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } ,
+ { "Right Output Mixer", "PCM Playback Switch", "Right DAC" },
+
+ { "LOUT1 PGA", NULL, "Left Output Mixer" },
+ { "ROUT1 PGA", NULL, "Right Output Mixer" },
+
+ { "HP_L", NULL, "LOUT1 PGA" },
+ { "HP_R", NULL, "ROUT1 PGA" },
+
+ { "Left Speaker PGA", NULL, "Left Output Mixer" },
+ { "Right Speaker PGA", NULL, "Right Output Mixer" },
+
+ { "Left Speaker Output", NULL, "Left Speaker PGA" },
+ { "Right Speaker Output", NULL, "Right Speaker PGA" },
+
+ { "SPK_LN", NULL, "Left Speaker Output" },
+ { "SPK_LP", NULL, "Left Speaker Output" },
+ { "SPK_RN", NULL, "Right Speaker Output" },
+ { "SPK_RP", NULL, "Right Speaker Output" },
+};
+
+static const struct snd_soc_dapm_route audio_paths_out3[] = {
+ { "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
+ { "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
+
+ { "OUT3", NULL, "Mono Output Mixer", }
+};
+
+static const struct snd_soc_dapm_route audio_paths_capless[] = {
+ { "HP_L", NULL, "OUT3 VMID" },
+ { "HP_R", NULL, "OUT3 VMID" },
+
+ { "OUT3 VMID", NULL, "Left Output Mixer" },
+ { "OUT3 VMID", NULL, "Right Output Mixer" },
+};
+
+static int wm8960_add_widgets(struct snd_soc_codec *codec)
+{
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ struct wm8960_data *pdata = &wm8960->pdata;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_widget *w;
+
+ snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,
+ ARRAY_SIZE(wm8960_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
+
+ /* In capless mode OUT3 is used to provide VMID for the
+ * headphone outputs, otherwise it is used as a mono mixer.
+ */
+ if (pdata && pdata->capless) {
+ snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_capless,
+ ARRAY_SIZE(wm8960_dapm_widgets_capless));
+
+ snd_soc_dapm_add_routes(dapm, audio_paths_capless,
+ ARRAY_SIZE(audio_paths_capless));
+ } else {
+ snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_out3,
+ ARRAY_SIZE(wm8960_dapm_widgets_out3));
+
+ snd_soc_dapm_add_routes(dapm, audio_paths_out3,
+ ARRAY_SIZE(audio_paths_out3));
+ }
+
+ /* We need to power up the headphone output stage out of
+ * sequence for capless mode. To save scanning the widget
+ * list each time to find the desired power state do so now
+ * and save the result.
+ */
+ list_for_each_entry(w, &codec->component.card->widgets, list) {
+ if (w->dapm != &codec->dapm)
+ continue;
+ if (strcmp(w->name, "LOUT1 PGA") == 0)
+ wm8960->lout1 = w;
+ if (strcmp(w->name, "ROUT1 PGA") == 0)
+ wm8960->rout1 = w;
+ if (strcmp(w->name, "OUT3 VMID") == 0)
+ wm8960->out3 = w;
+ }
+
+ return 0;
+}
+
+static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface |= 0x0040;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface */
+ snd_soc_write(codec, WM8960_IFACE1, iface);
+ return 0;
+}
+
+static struct {
+ int rate;
+ unsigned int val;
+} alc_rates[] = {
+ { 48000, 0 },
+ { 44100, 0 },
+ { 32000, 1 },
+ { 22050, 2 },
+ { 24000, 2 },
+ { 16000, 3 },
+ { 11025, 4 },
+ { 12000, 4 },
+ { 8000, 5 },
+};
+
+static int wm8960_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 wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
+ int i;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface |= 0x0004;
+ break;
+ case 24:
+ iface |= 0x0008;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported width %d\n",
+ params_width(params));
+ return -EINVAL;
+ }
+
+ /* Update filters for the new rate */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ wm8960->playback_fs = params_rate(params);
+ wm8960_set_deemph(codec);
+ } else {
+ for (i = 0; i < ARRAY_SIZE(alc_rates); i++)
+ if (alc_rates[i].rate == params_rate(params))
+ snd_soc_update_bits(codec,
+ WM8960_ADDCTL3, 0x7,
+ alc_rates[i].val);
+ }
+
+ /* set iface */
+ snd_soc_write(codec, WM8960_IFACE1, iface);
+ return 0;
+}
+
+static int wm8960_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute)
+ snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0x8);
+ else
+ snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0);
+ return 0;
+}
+
+static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ switch (codec->dapm.bias_level) {
+ case SND_SOC_BIAS_STANDBY:
+ if (!IS_ERR(wm8960->mclk)) {
+ ret = clk_prepare_enable(wm8960->mclk);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to enable MCLK: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ /* Set VMID to 2x50k */
+ snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80);
+ break;
+
+ case SND_SOC_BIAS_ON:
+ if (!IS_ERR(wm8960->mclk))
+ clk_disable_unprepare(wm8960->mclk);
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ regcache_sync(wm8960->regmap);
+
+ /* Enable anti-pop features */
+ snd_soc_write(codec, WM8960_APOP1,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN | WM8960_BUFIOEN);
+
+ /* Enable & ramp VMID at 2x50k */
+ snd_soc_update_bits(codec, WM8960_POWER1, 0x80, 0x80);
+ msleep(100);
+
+ /* Enable VREF */
+ snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VREF,
+ WM8960_VREF);
+
+ /* Disable anti-pop features */
+ snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
+ }
+
+ /* Set VMID to 2x250k */
+ snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x100);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Enable anti-pop features */
+ snd_soc_write(codec, WM8960_APOP1,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN | WM8960_BUFIOEN);
+
+ /* Disable VMID and VREF, let them discharge */
+ snd_soc_write(codec, WM8960_POWER1, 0);
+ msleep(600);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ int reg, ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ switch (codec->dapm.bias_level) {
+ case SND_SOC_BIAS_STANDBY:
+ /* Enable anti pop mode */
+ snd_soc_update_bits(codec, WM8960_APOP1,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN);
+
+ /* Enable LOUT1, ROUT1 and OUT3 if they're enabled */
+ reg = 0;
+ if (wm8960->lout1 && wm8960->lout1->power)
+ reg |= WM8960_PWR2_LOUT1;
+ if (wm8960->rout1 && wm8960->rout1->power)
+ reg |= WM8960_PWR2_ROUT1;
+ if (wm8960->out3 && wm8960->out3->power)
+ reg |= WM8960_PWR2_OUT3;
+ snd_soc_update_bits(codec, WM8960_POWER2,
+ WM8960_PWR2_LOUT1 |
+ WM8960_PWR2_ROUT1 |
+ WM8960_PWR2_OUT3, reg);
+
+ /* Enable VMID at 2*50k */
+ snd_soc_update_bits(codec, WM8960_POWER1,
+ WM8960_VMID_MASK, 0x80);
+
+ /* Ramp */
+ msleep(100);
+
+ /* Enable VREF */
+ snd_soc_update_bits(codec, WM8960_POWER1,
+ WM8960_VREF, WM8960_VREF);
+
+ msleep(100);
+
+ if (!IS_ERR(wm8960->mclk)) {
+ ret = clk_prepare_enable(wm8960->mclk);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to enable MCLK: %d\n",
+ ret);
+ return ret;
+ }
+ }
+ break;
+
+ case SND_SOC_BIAS_ON:
+ if (!IS_ERR(wm8960->mclk))
+ clk_disable_unprepare(wm8960->mclk);
+
+ /* Enable anti-pop mode */
+ snd_soc_update_bits(codec, WM8960_APOP1,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN);
+
+ /* Disable VMID and VREF */
+ snd_soc_update_bits(codec, WM8960_POWER1,
+ WM8960_VREF | WM8960_VMID_MASK, 0);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ regcache_sync(wm8960->regmap);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ switch (codec->dapm.bias_level) {
+ case SND_SOC_BIAS_PREPARE:
+ /* Disable HP discharge */
+ snd_soc_update_bits(codec, WM8960_APOP2,
+ WM8960_DISOP | WM8960_DRES_MASK,
+ 0);
+
+ /* Disable anti-pop features */
+ snd_soc_update_bits(codec, WM8960_APOP1,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+/* PLL divisors */
+struct _pll_div {
+ u32 pre_div:1;
+ u32 n:4;
+ u32 k:24;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+
+static int pll_factors(unsigned int source, unsigned int target,
+ struct _pll_div *pll_div)
+{
+ unsigned long long Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+ pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target);
+
+ /* Scale up target to PLL operating frequency */
+ target *= 4;
+
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div->pre_div = 1;
+ Ndiv = target / source;
+ } else
+ pll_div->pre_div = 0;
+
+ if ((Ndiv < 6) || (Ndiv > 12)) {
+ pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv);
+ return -EINVAL;
+ }
+
+ pll_div->n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div->k = K;
+
+ pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n",
+ pll_div->n, pll_div->k, pll_div->pre_div);
+
+ return 0;
+}
+
+static int wm8960_set_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;
+ u16 reg;
+ static struct _pll_div pll_div;
+ int ret;
+
+ if (freq_in && freq_out) {
+ ret = pll_factors(freq_in, freq_out, &pll_div);
+ if (ret != 0)
+ return ret;
+ }
+
+ /* Disable the PLL: even if we are changing the frequency the
+ * PLL needs to be disabled while we do so. */
+ snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0);
+ snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0);
+
+ if (!freq_in || !freq_out)
+ return 0;
+
+ reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f;
+ reg |= pll_div.pre_div << 4;
+ reg |= pll_div.n;
+
+ if (pll_div.k) {
+ reg |= 0x20;
+
+ snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff);
+ snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff);
+ snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff);
+ }
+ snd_soc_write(codec, WM8960_PLL1, reg);
+
+ /* Turn it on */
+ snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0x1);
+ msleep(250);
+ snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0x1);
+
+ return 0;
+}
+
+static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8960_SYSCLKDIV:
+ reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9;
+ snd_soc_write(codec, WM8960_CLOCK1, reg | div);
+ break;
+ case WM8960_DACDIV:
+ reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7;
+ snd_soc_write(codec, WM8960_CLOCK1, reg | div);
+ break;
+ case WM8960_OPCLKDIV:
+ reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f;
+ snd_soc_write(codec, WM8960_PLL1, reg | div);
+ break;
+ case WM8960_DCLKDIV:
+ reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f;
+ snd_soc_write(codec, WM8960_CLOCK2, reg | div);
+ break;
+ case WM8960_TOCLKSEL:
+ reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd;
+ snd_soc_write(codec, WM8960_ADDCTL1, reg | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8960_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+
+ return wm8960->set_bias_level(codec, level);
+}
+
+#define WM8960_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8960_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8960_dai_ops = {
+ .hw_params = wm8960_hw_params,
+ .digital_mute = wm8960_mute,
+ .set_fmt = wm8960_set_dai_fmt,
+ .set_clkdiv = wm8960_set_dai_clkdiv,
+ .set_pll = wm8960_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver wm8960_dai = {
+ .name = "wm8960-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8960_RATES,
+ .formats = WM8960_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8960_RATES,
+ .formats = WM8960_FORMATS,},
+ .ops = &wm8960_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int wm8960_probe(struct snd_soc_codec *codec)
+{
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ struct wm8960_data *pdata = &wm8960->pdata;
+
+ if (pdata->capless)
+ wm8960->set_bias_level = wm8960_set_bias_level_capless;
+ else
+ wm8960->set_bias_level = wm8960_set_bias_level_out3;
+
+ snd_soc_add_codec_controls(codec, wm8960_snd_controls,
+ ARRAY_SIZE(wm8960_snd_controls));
+ wm8960_add_widgets(codec);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
+ .probe = wm8960_probe,
+ .set_bias_level = wm8960_set_bias_level,
+ .suspend_bias_off = true,
+};
+
+static const struct regmap_config wm8960_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8960_PLL4,
+
+ .reg_defaults = wm8960_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8960_volatile,
+};
+
+static void wm8960_set_pdata_from_of(struct i2c_client *i2c,
+ struct wm8960_data *pdata)
+{
+ const struct device_node *np = i2c->dev.of_node;
+
+ if (of_property_read_bool(np, "wlf,capless"))
+ pdata->capless = true;
+
+ if (of_property_read_bool(np, "wlf,shared-lrclk"))
+ pdata->shared_lrclk = true;
+}
+
+static int wm8960_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8960_data *pdata = dev_get_platdata(&i2c->dev);
+ struct wm8960_priv *wm8960;
+ int ret;
+
+ wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv),
+ GFP_KERNEL);
+ if (wm8960 == NULL)
+ return -ENOMEM;
+
+ wm8960->mclk = devm_clk_get(&i2c->dev, "mclk");
+ if (IS_ERR(wm8960->mclk)) {
+ if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ }
+
+ wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap);
+ if (IS_ERR(wm8960->regmap))
+ return PTR_ERR(wm8960->regmap);
+
+ if (pdata)
+ memcpy(&wm8960->pdata, pdata, sizeof(struct wm8960_data));
+ else if (i2c->dev.of_node)
+ wm8960_set_pdata_from_of(i2c, &wm8960->pdata);
+
+ ret = wm8960_reset(wm8960->regmap);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to issue reset\n");
+ return ret;
+ }
+
+ if (wm8960->pdata.shared_lrclk) {
+ ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2,
+ 0x4, 0x4);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable LRCM: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ /* Latch the update bits */
+ regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x100, 0x100);
+ regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x100, 0x100);
+ regmap_update_bits(wm8960->regmap, WM8960_LADC, 0x100, 0x100);
+ regmap_update_bits(wm8960->regmap, WM8960_RADC, 0x100, 0x100);
+ regmap_update_bits(wm8960->regmap, WM8960_LDAC, 0x100, 0x100);
+ regmap_update_bits(wm8960->regmap, WM8960_RDAC, 0x100, 0x100);
+ regmap_update_bits(wm8960->regmap, WM8960_LOUT1, 0x100, 0x100);
+ regmap_update_bits(wm8960->regmap, WM8960_ROUT1, 0x100, 0x100);
+ regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100);
+ regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100);
+
+ i2c_set_clientdata(i2c, wm8960);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8960, &wm8960_dai, 1);
+
+ return ret;
+}
+
+static int wm8960_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8960_i2c_id[] = {
+ { "wm8960", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
+
+static const struct of_device_id wm8960_of_match[] = {
+ { .compatible = "wlf,wm8960", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8960_of_match);
+
+static struct i2c_driver wm8960_i2c_driver = {
+ .driver = {
+ .name = "wm8960",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8960_of_match,
+ },
+ .probe = wm8960_i2c_probe,
+ .remove = wm8960_i2c_remove,
+ .id_table = wm8960_i2c_id,
+};
+
+module_i2c_driver(wm8960_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8960 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8960.h b/kernel/sound/soc/codecs/wm8960.h
new file mode 100644
index 000000000..2d8163d70
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8960.h
@@ -0,0 +1,113 @@
+/*
+ * wm8960.h -- WM8960 Soc Audio driver
+ *
+ * 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 _WM8960_H
+#define _WM8960_H
+
+/* WM8960 register space */
+
+
+#define WM8960_CACHEREGNUM 56
+
+#define WM8960_LINVOL 0x0
+#define WM8960_RINVOL 0x1
+#define WM8960_LOUT1 0x2
+#define WM8960_ROUT1 0x3
+#define WM8960_CLOCK1 0x4
+#define WM8960_DACCTL1 0x5
+#define WM8960_DACCTL2 0x6
+#define WM8960_IFACE1 0x7
+#define WM8960_CLOCK2 0x8
+#define WM8960_IFACE2 0x9
+#define WM8960_LDAC 0xa
+#define WM8960_RDAC 0xb
+
+#define WM8960_RESET 0xf
+#define WM8960_3D 0x10
+#define WM8960_ALC1 0x11
+#define WM8960_ALC2 0x12
+#define WM8960_ALC3 0x13
+#define WM8960_NOISEG 0x14
+#define WM8960_LADC 0x15
+#define WM8960_RADC 0x16
+#define WM8960_ADDCTL1 0x17
+#define WM8960_ADDCTL2 0x18
+#define WM8960_POWER1 0x19
+#define WM8960_POWER2 0x1a
+#define WM8960_ADDCTL3 0x1b
+#define WM8960_APOP1 0x1c
+#define WM8960_APOP2 0x1d
+
+#define WM8960_LINPATH 0x20
+#define WM8960_RINPATH 0x21
+#define WM8960_LOUTMIX 0x22
+
+#define WM8960_ROUTMIX 0x25
+#define WM8960_MONOMIX1 0x26
+#define WM8960_MONOMIX2 0x27
+#define WM8960_LOUT2 0x28
+#define WM8960_ROUT2 0x29
+#define WM8960_MONO 0x2a
+#define WM8960_INBMIX1 0x2b
+#define WM8960_INBMIX2 0x2c
+#define WM8960_BYPASS1 0x2d
+#define WM8960_BYPASS2 0x2e
+#define WM8960_POWER3 0x2f
+#define WM8960_ADDCTL4 0x30
+#define WM8960_CLASSD1 0x31
+
+#define WM8960_CLASSD3 0x33
+#define WM8960_PLL1 0x34
+#define WM8960_PLL2 0x35
+#define WM8960_PLL3 0x36
+#define WM8960_PLL4 0x37
+
+
+/*
+ * WM8960 Clock dividers
+ */
+#define WM8960_SYSCLKDIV 0
+#define WM8960_DACDIV 1
+#define WM8960_OPCLKDIV 2
+#define WM8960_DCLKDIV 3
+#define WM8960_TOCLKSEL 4
+
+#define WM8960_SYSCLK_DIV_1 (0 << 1)
+#define WM8960_SYSCLK_DIV_2 (2 << 1)
+
+#define WM8960_SYSCLK_MCLK (0 << 0)
+#define WM8960_SYSCLK_PLL (1 << 0)
+
+#define WM8960_DAC_DIV_1 (0 << 3)
+#define WM8960_DAC_DIV_1_5 (1 << 3)
+#define WM8960_DAC_DIV_2 (2 << 3)
+#define WM8960_DAC_DIV_3 (3 << 3)
+#define WM8960_DAC_DIV_4 (4 << 3)
+#define WM8960_DAC_DIV_5_5 (5 << 3)
+#define WM8960_DAC_DIV_6 (6 << 3)
+
+#define WM8960_DCLK_DIV_1_5 (0 << 6)
+#define WM8960_DCLK_DIV_2 (1 << 6)
+#define WM8960_DCLK_DIV_3 (2 << 6)
+#define WM8960_DCLK_DIV_4 (3 << 6)
+#define WM8960_DCLK_DIV_6 (4 << 6)
+#define WM8960_DCLK_DIV_8 (5 << 6)
+#define WM8960_DCLK_DIV_12 (6 << 6)
+#define WM8960_DCLK_DIV_16 (7 << 6)
+
+#define WM8960_TOCLK_F19 (0 << 1)
+#define WM8960_TOCLK_F21 (1 << 1)
+
+#define WM8960_OPCLK_DIV_1 (0 << 0)
+#define WM8960_OPCLK_DIV_2 (1 << 0)
+#define WM8960_OPCLK_DIV_3 (2 << 0)
+#define WM8960_OPCLK_DIV_4 (3 << 0)
+#define WM8960_OPCLK_DIV_5_5 (4 << 0)
+#define WM8960_OPCLK_DIV_6 (5 << 0)
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8961.c b/kernel/sound/soc/codecs/wm8961.c
new file mode 100644
index 000000000..95e2c1bfc
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8961.c
@@ -0,0 +1,998 @@
+/*
+ * wm8961.c -- WM8961 ALSA SoC Audio driver
+ *
+ * Copyright 2009-10 Wolfson Microelectronics, plc
+ *
+ * Author: Mark Brown
+ *
+ * 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.
+ *
+ * Currently unimplemented features:
+ * - ALC
+ */
+
+#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/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 "wm8961.h"
+
+#define WM8961_MAX_REGISTER 0xFC
+
+static const struct reg_default wm8961_reg_defaults[] = {
+ { 0, 0x009F }, /* R0 - Left Input volume */
+ { 1, 0x009F }, /* R1 - Right Input volume */
+ { 2, 0x0000 }, /* R2 - LOUT1 volume */
+ { 3, 0x0000 }, /* R3 - ROUT1 volume */
+ { 4, 0x0020 }, /* R4 - Clocking1 */
+ { 5, 0x0008 }, /* R5 - ADC & DAC Control 1 */
+ { 6, 0x0000 }, /* R6 - ADC & DAC Control 2 */
+ { 7, 0x000A }, /* R7 - Audio Interface 0 */
+ { 8, 0x01F4 }, /* R8 - Clocking2 */
+ { 9, 0x0000 }, /* R9 - Audio Interface 1 */
+ { 10, 0x00FF }, /* R10 - Left DAC volume */
+ { 11, 0x00FF }, /* R11 - Right DAC volume */
+
+ { 14, 0x0040 }, /* R14 - Audio Interface 2 */
+
+ { 17, 0x007B }, /* R17 - ALC1 */
+ { 18, 0x0000 }, /* R18 - ALC2 */
+ { 19, 0x0032 }, /* R19 - ALC3 */
+ { 20, 0x0000 }, /* R20 - Noise Gate */
+ { 21, 0x00C0 }, /* R21 - Left ADC volume */
+ { 22, 0x00C0 }, /* R22 - Right ADC volume */
+ { 23, 0x0120 }, /* R23 - Additional control(1) */
+ { 24, 0x0000 }, /* R24 - Additional control(2) */
+ { 25, 0x0000 }, /* R25 - Pwr Mgmt (1) */
+ { 26, 0x0000 }, /* R26 - Pwr Mgmt (2) */
+ { 27, 0x0000 }, /* R27 - Additional Control (3) */
+ { 28, 0x0000 }, /* R28 - Anti-pop */
+
+ { 30, 0x005F }, /* R30 - Clocking 3 */
+
+ { 32, 0x0000 }, /* R32 - ADCL signal path */
+ { 33, 0x0000 }, /* R33 - ADCR signal path */
+
+ { 40, 0x0000 }, /* R40 - LOUT2 volume */
+ { 41, 0x0000 }, /* R41 - ROUT2 volume */
+
+ { 47, 0x0000 }, /* R47 - Pwr Mgmt (3) */
+ { 48, 0x0023 }, /* R48 - Additional Control (4) */
+ { 49, 0x0000 }, /* R49 - Class D Control 1 */
+
+ { 51, 0x0003 }, /* R51 - Class D Control 2 */
+
+ { 56, 0x0106 }, /* R56 - Clocking 4 */
+ { 57, 0x0000 }, /* R57 - DSP Sidetone 0 */
+ { 58, 0x0000 }, /* R58 - DSP Sidetone 1 */
+
+ { 60, 0x0000 }, /* R60 - DC Servo 0 */
+ { 61, 0x0000 }, /* R61 - DC Servo 1 */
+
+ { 63, 0x015E }, /* R63 - DC Servo 3 */
+
+ { 65, 0x0010 }, /* R65 - DC Servo 5 */
+
+ { 68, 0x0003 }, /* R68 - Analogue PGA Bias */
+ { 69, 0x0000 }, /* R69 - Analogue HP 0 */
+
+ { 71, 0x01FB }, /* R71 - Analogue HP 2 */
+ { 72, 0x0000 }, /* R72 - Charge Pump 1 */
+
+ { 82, 0x0000 }, /* R82 - Charge Pump B */
+
+ { 87, 0x0000 }, /* R87 - Write Sequencer 1 */
+ { 88, 0x0000 }, /* R88 - Write Sequencer 2 */
+ { 89, 0x0000 }, /* R89 - Write Sequencer 3 */
+ { 90, 0x0000 }, /* R90 - Write Sequencer 4 */
+ { 91, 0x0000 }, /* R91 - Write Sequencer 5 */
+ { 92, 0x0000 }, /* R92 - Write Sequencer 6 */
+ { 93, 0x0000 }, /* R93 - Write Sequencer 7 */
+
+ { 252, 0x0001 }, /* R252 - General test 1 */
+};
+
+struct wm8961_priv {
+ struct regmap *regmap;
+ int sysclk;
+};
+
+static bool wm8961_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8961_SOFTWARE_RESET:
+ case WM8961_WRITE_SEQUENCER_7:
+ case WM8961_DC_SERVO_1:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool wm8961_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8961_LEFT_INPUT_VOLUME:
+ case WM8961_RIGHT_INPUT_VOLUME:
+ case WM8961_LOUT1_VOLUME:
+ case WM8961_ROUT1_VOLUME:
+ case WM8961_CLOCKING1:
+ case WM8961_ADC_DAC_CONTROL_1:
+ case WM8961_ADC_DAC_CONTROL_2:
+ case WM8961_AUDIO_INTERFACE_0:
+ case WM8961_CLOCKING2:
+ case WM8961_AUDIO_INTERFACE_1:
+ case WM8961_LEFT_DAC_VOLUME:
+ case WM8961_RIGHT_DAC_VOLUME:
+ case WM8961_AUDIO_INTERFACE_2:
+ case WM8961_SOFTWARE_RESET:
+ case WM8961_ALC1:
+ case WM8961_ALC2:
+ case WM8961_ALC3:
+ case WM8961_NOISE_GATE:
+ case WM8961_LEFT_ADC_VOLUME:
+ case WM8961_RIGHT_ADC_VOLUME:
+ case WM8961_ADDITIONAL_CONTROL_1:
+ case WM8961_ADDITIONAL_CONTROL_2:
+ case WM8961_PWR_MGMT_1:
+ case WM8961_PWR_MGMT_2:
+ case WM8961_ADDITIONAL_CONTROL_3:
+ case WM8961_ANTI_POP:
+ case WM8961_CLOCKING_3:
+ case WM8961_ADCL_SIGNAL_PATH:
+ case WM8961_ADCR_SIGNAL_PATH:
+ case WM8961_LOUT2_VOLUME:
+ case WM8961_ROUT2_VOLUME:
+ case WM8961_PWR_MGMT_3:
+ case WM8961_ADDITIONAL_CONTROL_4:
+ case WM8961_CLASS_D_CONTROL_1:
+ case WM8961_CLASS_D_CONTROL_2:
+ case WM8961_CLOCKING_4:
+ case WM8961_DSP_SIDETONE_0:
+ case WM8961_DSP_SIDETONE_1:
+ case WM8961_DC_SERVO_0:
+ case WM8961_DC_SERVO_1:
+ case WM8961_DC_SERVO_3:
+ case WM8961_DC_SERVO_5:
+ case WM8961_ANALOGUE_PGA_BIAS:
+ case WM8961_ANALOGUE_HP_0:
+ case WM8961_ANALOGUE_HP_2:
+ case WM8961_CHARGE_PUMP_1:
+ case WM8961_CHARGE_PUMP_B:
+ case WM8961_WRITE_SEQUENCER_1:
+ case WM8961_WRITE_SEQUENCER_2:
+ case WM8961_WRITE_SEQUENCER_3:
+ case WM8961_WRITE_SEQUENCER_4:
+ case WM8961_WRITE_SEQUENCER_5:
+ case WM8961_WRITE_SEQUENCER_6:
+ case WM8961_WRITE_SEQUENCER_7:
+ case WM8961_GENERAL_TEST_1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * The headphone output supports special anti-pop sequences giving
+ * silent power up and power down.
+ */
+static int wm8961_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u16 hp_reg = snd_soc_read(codec, WM8961_ANALOGUE_HP_0);
+ u16 cp_reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_1);
+ u16 pwr_reg = snd_soc_read(codec, WM8961_PWR_MGMT_2);
+ u16 dcs_reg = snd_soc_read(codec, WM8961_DC_SERVO_1);
+ int timeout = 500;
+
+ if (event & SND_SOC_DAPM_POST_PMU) {
+ /* Make sure the output is shorted */
+ hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Enable the charge pump */
+ cp_reg |= WM8961_CP_ENA;
+ snd_soc_write(codec, WM8961_CHARGE_PUMP_1, cp_reg);
+ mdelay(5);
+
+ /* Enable the PGA */
+ pwr_reg |= WM8961_LOUT1_PGA | WM8961_ROUT1_PGA;
+ snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+
+ /* Enable the amplifier */
+ hp_reg |= WM8961_HPR_ENA | WM8961_HPL_ENA;
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Second stage enable */
+ hp_reg |= WM8961_HPR_ENA_DLY | WM8961_HPL_ENA_DLY;
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Enable the DC servo & trigger startup */
+ dcs_reg |=
+ WM8961_DCS_ENA_CHAN_HPR | WM8961_DCS_TRIG_STARTUP_HPR |
+ WM8961_DCS_ENA_CHAN_HPL | WM8961_DCS_TRIG_STARTUP_HPL;
+ dev_dbg(codec->dev, "Enabling DC servo\n");
+
+ snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg);
+ do {
+ msleep(1);
+ dcs_reg = snd_soc_read(codec, WM8961_DC_SERVO_1);
+ } while (--timeout &&
+ dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
+ WM8961_DCS_TRIG_STARTUP_HPL));
+ if (dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
+ WM8961_DCS_TRIG_STARTUP_HPL))
+ dev_err(codec->dev, "DC servo timed out\n");
+ else
+ dev_dbg(codec->dev, "DC servo startup complete\n");
+
+ /* Enable the output stage */
+ hp_reg |= WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP;
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Remove the short on the output stage */
+ hp_reg |= WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT;
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+ }
+
+ if (event & SND_SOC_DAPM_PRE_PMD) {
+ /* Short the output */
+ hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Disable the output stage */
+ hp_reg &= ~(WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP);
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Disable DC offset cancellation */
+ dcs_reg &= ~(WM8961_DCS_ENA_CHAN_HPR |
+ WM8961_DCS_ENA_CHAN_HPL);
+ snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg);
+
+ /* Finish up */
+ hp_reg &= ~(WM8961_HPR_ENA_DLY | WM8961_HPR_ENA |
+ WM8961_HPL_ENA_DLY | WM8961_HPL_ENA);
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Disable the PGA */
+ pwr_reg &= ~(WM8961_LOUT1_PGA | WM8961_ROUT1_PGA);
+ snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+
+ /* Disable the charge pump */
+ dev_dbg(codec->dev, "Disabling charge pump\n");
+ snd_soc_write(codec, WM8961_CHARGE_PUMP_1,
+ cp_reg & ~WM8961_CP_ENA);
+ }
+
+ return 0;
+}
+
+static int wm8961_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u16 pwr_reg = snd_soc_read(codec, WM8961_PWR_MGMT_2);
+ u16 spk_reg = snd_soc_read(codec, WM8961_CLASS_D_CONTROL_1);
+
+ if (event & SND_SOC_DAPM_POST_PMU) {
+ /* Enable the PGA */
+ pwr_reg |= WM8961_SPKL_PGA | WM8961_SPKR_PGA;
+ snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+
+ /* Enable the amplifier */
+ spk_reg |= WM8961_SPKL_ENA | WM8961_SPKR_ENA;
+ snd_soc_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg);
+ }
+
+ if (event & SND_SOC_DAPM_PRE_PMD) {
+ /* Disable the amplifier */
+ spk_reg &= ~(WM8961_SPKL_ENA | WM8961_SPKR_ENA);
+ snd_soc_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg);
+
+ /* Disable the PGA */
+ pwr_reg &= ~(WM8961_SPKL_PGA | WM8961_SPKR_PGA);
+ snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+ }
+
+ return 0;
+}
+
+static const char *adc_hpf_text[] = {
+ "Hi-fi", "Voice 1", "Voice 2", "Voice 3",
+};
+
+static SOC_ENUM_SINGLE_DECL(adc_hpf,
+ WM8961_ADC_DAC_CONTROL_2, 7, adc_hpf_text);
+
+static const char *dac_deemph_text[] = {
+ "None", "32kHz", "44.1kHz", "48kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(dac_deemph,
+ WM8961_ADC_DAC_CONTROL_1, 1, dac_deemph_text);
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(hp_sec_tlv, -700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
+static unsigned int boost_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(13, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(20, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(29, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -2325, 75, 0);
+
+static const struct snd_kcontrol_new wm8961_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Headphone Volume", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME,
+ 0, 127, 0, out_tlv),
+SOC_DOUBLE_TLV("Headphone Secondary Volume", WM8961_ANALOGUE_HP_2,
+ 6, 3, 7, 0, hp_sec_tlv),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME,
+ 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Speaker Volume", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME,
+ 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Speaker ZC Switch", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME,
+ 7, 1, 0),
+SOC_SINGLE("Speaker AC Gain", WM8961_CLASS_D_CONTROL_2, 0, 7, 0),
+
+SOC_SINGLE("DAC x128 OSR Switch", WM8961_ADC_DAC_CONTROL_2, 0, 1, 0),
+SOC_ENUM("DAC Deemphasis", dac_deemph),
+SOC_SINGLE("DAC Soft Mute Switch", WM8961_ADC_DAC_CONTROL_2, 3, 1, 0),
+
+SOC_DOUBLE_R_TLV("Sidetone Volume", WM8961_DSP_SIDETONE_0,
+ WM8961_DSP_SIDETONE_1, 4, 12, 0, sidetone_tlv),
+
+SOC_SINGLE("ADC High Pass Filter Switch", WM8961_ADC_DAC_CONTROL_1, 0, 1, 0),
+SOC_ENUM("ADC High Pass Filter Mode", adc_hpf),
+
+SOC_DOUBLE_R_TLV("Capture Volume",
+ WM8961_LEFT_ADC_VOLUME, WM8961_RIGHT_ADC_VOLUME,
+ 1, 119, 0, adc_tlv),
+SOC_DOUBLE_R_TLV("Capture Boost Volume",
+ WM8961_ADCL_SIGNAL_PATH, WM8961_ADCR_SIGNAL_PATH,
+ 4, 3, 0, boost_tlv),
+SOC_DOUBLE_R_TLV("Capture PGA Volume",
+ WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
+ 0, 62, 0, pga_tlv),
+SOC_DOUBLE_R("Capture PGA ZC Switch",
+ WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
+ 6, 1, 1),
+SOC_DOUBLE_R("Capture PGA Switch",
+ WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
+ 7, 1, 1),
+};
+
+static const char *sidetone_text[] = {
+ "None", "Left", "Right"
+};
+
+static SOC_ENUM_SINGLE_DECL(dacl_sidetone,
+ WM8961_DSP_SIDETONE_0, 2, sidetone_text);
+
+static SOC_ENUM_SINGLE_DECL(dacr_sidetone,
+ WM8961_DSP_SIDETONE_1, 2, sidetone_text);
+
+static const struct snd_kcontrol_new dacl_mux =
+ SOC_DAPM_ENUM("DACL Sidetone", dacl_sidetone);
+
+static const struct snd_kcontrol_new dacr_mux =
+ SOC_DAPM_ENUM("DACR Sidetone", dacr_sidetone);
+
+static const struct snd_soc_dapm_widget wm8961_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("LINPUT"),
+SND_SOC_DAPM_INPUT("RINPUT"),
+
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8961_CLOCKING2, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Left Input", WM8961_PWR_MGMT_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Input", WM8961_PWR_MGMT_1, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", WM8961_PWR_MGMT_1, 3, 0),
+SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", WM8961_PWR_MGMT_1, 2, 0),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS", WM8961_PWR_MGMT_1, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &dacl_mux),
+SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &dacr_mux),
+
+SND_SOC_DAPM_DAC("DACL", "HiFi Playback", WM8961_PWR_MGMT_2, 8, 0),
+SND_SOC_DAPM_DAC("DACR", "HiFi Playback", WM8961_PWR_MGMT_2, 7, 0),
+
+/* Handle as a mono path for DCS */
+SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM,
+ 4, 0, NULL, 0, wm8961_hp_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("Speaker Output", SND_SOC_NOPM,
+ 4, 0, NULL, 0, wm8961_spk_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("HP_L"),
+SND_SOC_DAPM_OUTPUT("HP_R"),
+SND_SOC_DAPM_OUTPUT("SPK_LN"),
+SND_SOC_DAPM_OUTPUT("SPK_LP"),
+SND_SOC_DAPM_OUTPUT("SPK_RN"),
+SND_SOC_DAPM_OUTPUT("SPK_RP"),
+};
+
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+ { "DACL", NULL, "CLK_DSP" },
+ { "DACL", NULL, "DACL Sidetone" },
+ { "DACR", NULL, "CLK_DSP" },
+ { "DACR", NULL, "DACR Sidetone" },
+
+ { "DACL Sidetone", "Left", "ADCL" },
+ { "DACL Sidetone", "Right", "ADCR" },
+
+ { "DACR Sidetone", "Left", "ADCL" },
+ { "DACR Sidetone", "Right", "ADCR" },
+
+ { "HP_L", NULL, "Headphone Output" },
+ { "HP_R", NULL, "Headphone Output" },
+ { "Headphone Output", NULL, "DACL" },
+ { "Headphone Output", NULL, "DACR" },
+
+ { "SPK_LN", NULL, "Speaker Output" },
+ { "SPK_LP", NULL, "Speaker Output" },
+ { "SPK_RN", NULL, "Speaker Output" },
+ { "SPK_RP", NULL, "Speaker Output" },
+
+ { "Speaker Output", NULL, "DACL" },
+ { "Speaker Output", NULL, "DACR" },
+
+ { "ADCL", NULL, "Left Input" },
+ { "ADCL", NULL, "CLK_DSP" },
+ { "ADCR", NULL, "Right Input" },
+ { "ADCR", NULL, "CLK_DSP" },
+
+ { "Left Input", NULL, "LINPUT" },
+ { "Right Input", NULL, "RINPUT" },
+
+};
+
+/* Values for CLK_SYS_RATE */
+static struct {
+ int ratio;
+ u16 val;
+} wm8961_clk_sys_ratio[] = {
+ { 64, 0 },
+ { 128, 1 },
+ { 192, 2 },
+ { 256, 3 },
+ { 384, 4 },
+ { 512, 5 },
+ { 768, 6 },
+ { 1024, 7 },
+ { 1408, 8 },
+ { 1536, 9 },
+};
+
+/* Values for SAMPLE_RATE */
+static struct {
+ int rate;
+ u16 val;
+} wm8961_srate[] = {
+ { 48000, 0 },
+ { 44100, 0 },
+ { 32000, 1 },
+ { 22050, 2 },
+ { 24000, 2 },
+ { 16000, 3 },
+ { 11250, 4 },
+ { 12000, 4 },
+ { 8000, 5 },
+};
+
+static int wm8961_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8961_priv *wm8961 = snd_soc_codec_get_drvdata(codec);
+ int i, best, target, fs;
+ u16 reg;
+
+ fs = params_rate(params);
+
+ if (!wm8961->sysclk) {
+ dev_err(codec->dev, "MCLK has not been specified\n");
+ return -EINVAL;
+ }
+
+ /* Find the closest sample rate for the filters */
+ best = 0;
+ for (i = 0; i < ARRAY_SIZE(wm8961_srate); i++) {
+ if (abs(wm8961_srate[i].rate - fs) <
+ abs(wm8961_srate[best].rate - fs))
+ best = i;
+ }
+ reg = snd_soc_read(codec, WM8961_ADDITIONAL_CONTROL_3);
+ reg &= ~WM8961_SAMPLE_RATE_MASK;
+ reg |= wm8961_srate[best].val;
+ snd_soc_write(codec, WM8961_ADDITIONAL_CONTROL_3, reg);
+ dev_dbg(codec->dev, "Selected SRATE %dHz for %dHz\n",
+ wm8961_srate[best].rate, fs);
+
+ /* Select a CLK_SYS/fs ratio equal to or higher than required */
+ target = wm8961->sysclk / fs;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && target < 64) {
+ dev_err(codec->dev,
+ "SYSCLK must be at least 64*fs for DAC\n");
+ return -EINVAL;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && target < 256) {
+ dev_err(codec->dev,
+ "SYSCLK must be at least 256*fs for ADC\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8961_clk_sys_ratio); i++) {
+ if (wm8961_clk_sys_ratio[i].ratio >= target)
+ break;
+ }
+ if (i == ARRAY_SIZE(wm8961_clk_sys_ratio)) {
+ dev_err(codec->dev, "Unable to generate CLK_SYS_RATE\n");
+ return -EINVAL;
+ }
+ dev_dbg(codec->dev, "Selected CLK_SYS_RATE of %d for %d/%d=%d\n",
+ wm8961_clk_sys_ratio[i].ratio, wm8961->sysclk, fs,
+ wm8961->sysclk / fs);
+
+ reg = snd_soc_read(codec, WM8961_CLOCKING_4);
+ reg &= ~WM8961_CLK_SYS_RATE_MASK;
+ reg |= wm8961_clk_sys_ratio[i].val << WM8961_CLK_SYS_RATE_SHIFT;
+ snd_soc_write(codec, WM8961_CLOCKING_4, reg);
+
+ reg = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_0);
+ reg &= ~WM8961_WL_MASK;
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ reg |= 1 << WM8961_WL_SHIFT;
+ break;
+ case 24:
+ reg |= 2 << WM8961_WL_SHIFT;
+ break;
+ case 32:
+ reg |= 3 << WM8961_WL_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, reg);
+
+ /* Sloping stop-band filter is recommended for <= 24kHz */
+ reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2);
+ if (fs <= 24000)
+ reg |= WM8961_DACSLOPE;
+ else
+ reg &= ~WM8961_DACSLOPE;
+ snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_2, reg);
+
+ return 0;
+}
+
+static int wm8961_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq,
+ int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8961_priv *wm8961 = snd_soc_codec_get_drvdata(codec);
+ u16 reg = snd_soc_read(codec, WM8961_CLOCKING1);
+
+ if (freq > 33000000) {
+ dev_err(codec->dev, "MCLK must be <33MHz\n");
+ return -EINVAL;
+ }
+
+ if (freq > 16500000) {
+ dev_dbg(codec->dev, "Using MCLK/2 for %dHz MCLK\n", freq);
+ reg |= WM8961_MCLKDIV;
+ freq /= 2;
+ } else {
+ dev_dbg(codec->dev, "Using MCLK/1 for %dHz MCLK\n", freq);
+ reg &= ~WM8961_MCLKDIV;
+ }
+
+ snd_soc_write(codec, WM8961_CLOCKING1, reg);
+
+ wm8961->sysclk = freq;
+
+ return 0;
+}
+
+static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 aif = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_0);
+
+ aif &= ~(WM8961_BCLKINV | WM8961_LRP |
+ WM8961_MS | WM8961_FORMAT_MASK);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif |= WM8961_MS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif |= 1;
+ break;
+
+ case SND_SOC_DAIFMT_I2S:
+ aif |= 2;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ aif |= WM8961_LRP;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif |= 3;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif |= WM8961_LRP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif |= WM8961_BCLKINV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif |= WM8961_BCLKINV | WM8961_LRP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, aif);
+}
+
+static int wm8961_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 reg = snd_soc_read(codec, WM8961_ADDITIONAL_CONTROL_2);
+
+ if (tristate)
+ reg |= WM8961_TRIS;
+ else
+ reg &= ~WM8961_TRIS;
+
+ return snd_soc_write(codec, WM8961_ADDITIONAL_CONTROL_2, reg);
+}
+
+static int wm8961_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_1);
+
+ if (mute)
+ reg |= WM8961_DACMU;
+ else
+ reg &= ~WM8961_DACMU;
+
+ msleep(17);
+
+ return snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_1, reg);
+}
+
+static int wm8961_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8961_BCLK:
+ reg = snd_soc_read(codec, WM8961_CLOCKING2);
+ reg &= ~WM8961_BCLKDIV_MASK;
+ reg |= div;
+ snd_soc_write(codec, WM8961_CLOCKING2, reg);
+ break;
+
+ case WM8961_LRCLK:
+ reg = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_2);
+ reg &= ~WM8961_LRCLK_RATE_MASK;
+ reg |= div;
+ snd_soc_write(codec, WM8961_AUDIO_INTERFACE_2, reg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8961_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg;
+
+ /* This is all slightly unusual since we have no bypass paths
+ * and the output amplifier structure means we can just slam
+ * the biases straight up rather than having to ramp them
+ * slowly.
+ */
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+ /* Enable bias generation */
+ reg = snd_soc_read(codec, WM8961_ANTI_POP);
+ reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN;
+ snd_soc_write(codec, WM8961_ANTI_POP, reg);
+
+ /* VMID=2*50k, VREF */
+ reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
+ reg &= ~WM8961_VMIDSEL_MASK;
+ reg |= (1 << WM8961_VMIDSEL_SHIFT) | WM8961_VREF;
+ snd_soc_write(codec, WM8961_PWR_MGMT_1, reg);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
+ /* VREF off */
+ reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
+ reg &= ~WM8961_VREF;
+ snd_soc_write(codec, WM8961_PWR_MGMT_1, reg);
+
+ /* Bias generation off */
+ reg = snd_soc_read(codec, WM8961_ANTI_POP);
+ reg &= ~(WM8961_BUFIOEN | WM8961_BUFDCOPEN);
+ snd_soc_write(codec, WM8961_ANTI_POP, reg);
+
+ /* VMID off */
+ reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
+ reg &= ~WM8961_VMIDSEL_MASK;
+ snd_soc_write(codec, WM8961_PWR_MGMT_1, reg);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+
+#define WM8961_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8961_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8961_dai_ops = {
+ .hw_params = wm8961_hw_params,
+ .set_sysclk = wm8961_set_sysclk,
+ .set_fmt = wm8961_set_fmt,
+ .digital_mute = wm8961_digital_mute,
+ .set_tristate = wm8961_set_tristate,
+ .set_clkdiv = wm8961_set_clkdiv,
+};
+
+static struct snd_soc_dai_driver wm8961_dai = {
+ .name = "wm8961-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8961_RATES,
+ .formats = WM8961_FORMATS,},
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8961_RATES,
+ .formats = WM8961_FORMATS,},
+ .ops = &wm8961_dai_ops,
+};
+
+static int wm8961_probe(struct snd_soc_codec *codec)
+{
+ u16 reg;
+
+ /* Enable class W */
+ reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_B);
+ reg |= WM8961_CP_DYN_PWR_MASK;
+ snd_soc_write(codec, WM8961_CHARGE_PUMP_B, reg);
+
+ /* Latch volume update bits (right channel only, we always
+ * write both out) and default ZC on. */
+ reg = snd_soc_read(codec, WM8961_ROUT1_VOLUME);
+ snd_soc_write(codec, WM8961_ROUT1_VOLUME,
+ reg | WM8961_LO1ZC | WM8961_OUT1VU);
+ snd_soc_write(codec, WM8961_LOUT1_VOLUME, reg | WM8961_LO1ZC);
+ reg = snd_soc_read(codec, WM8961_ROUT2_VOLUME);
+ snd_soc_write(codec, WM8961_ROUT2_VOLUME,
+ reg | WM8961_SPKRZC | WM8961_SPKVU);
+ snd_soc_write(codec, WM8961_LOUT2_VOLUME, reg | WM8961_SPKLZC);
+
+ reg = snd_soc_read(codec, WM8961_RIGHT_ADC_VOLUME);
+ snd_soc_write(codec, WM8961_RIGHT_ADC_VOLUME, reg | WM8961_ADCVU);
+ reg = snd_soc_read(codec, WM8961_RIGHT_INPUT_VOLUME);
+ snd_soc_write(codec, WM8961_RIGHT_INPUT_VOLUME, reg | WM8961_IPVU);
+
+ /* Use soft mute by default */
+ reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2);
+ reg |= WM8961_DACSMM;
+ snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_2, reg);
+
+ /* Use automatic clocking mode by default; for now this is all
+ * we support.
+ */
+ reg = snd_soc_read(codec, WM8961_CLOCKING_3);
+ reg &= ~WM8961_MANUAL_MODE;
+ snd_soc_write(codec, WM8961_CLOCKING_3, reg);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int wm8961_resume(struct snd_soc_codec *codec)
+{
+ snd_soc_cache_sync(codec);
+
+ return 0;
+}
+#else
+#define wm8961_resume NULL
+#endif
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8961 = {
+ .probe = wm8961_probe,
+ .resume = wm8961_resume,
+ .set_bias_level = wm8961_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8961_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8961_snd_controls),
+ .dapm_widgets = wm8961_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8961_dapm_widgets),
+ .dapm_routes = audio_paths,
+ .num_dapm_routes = ARRAY_SIZE(audio_paths),
+};
+
+static const struct regmap_config wm8961_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = WM8961_MAX_REGISTER,
+
+ .reg_defaults = wm8961_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8961_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8961_volatile,
+ .readable_reg = wm8961_readable,
+};
+
+static int wm8961_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8961_priv *wm8961;
+ unsigned int val;
+ int ret;
+
+ wm8961 = devm_kzalloc(&i2c->dev, sizeof(struct wm8961_priv),
+ GFP_KERNEL);
+ if (wm8961 == NULL)
+ return -ENOMEM;
+
+ wm8961->regmap = devm_regmap_init_i2c(i2c, &wm8961_regmap);
+ if (IS_ERR(wm8961->regmap))
+ return PTR_ERR(wm8961->regmap);
+
+ ret = regmap_read(wm8961->regmap, WM8961_SOFTWARE_RESET, &val);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret);
+ return ret;
+ }
+
+ if (val != 0x1801) {
+ dev_err(&i2c->dev, "Device is not a WM8961: ID=0x%x\n", val);
+ return -EINVAL;
+ }
+
+ /* This isn't volatile - readback doesn't correspond to write */
+ regcache_cache_bypass(wm8961->regmap, true);
+ ret = regmap_read(wm8961->regmap, WM8961_RIGHT_INPUT_VOLUME, &val);
+ regcache_cache_bypass(wm8961->regmap, false);
+
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to read chip revision: %d\n", ret);
+ return ret;
+ }
+
+ dev_info(&i2c->dev, "WM8961 family %d revision %c\n",
+ (val & WM8961_DEVICE_ID_MASK) >> WM8961_DEVICE_ID_SHIFT,
+ ((val & WM8961_CHIP_REV_MASK) >> WM8961_CHIP_REV_SHIFT)
+ + 'A');
+
+ ret = regmap_write(wm8961->regmap, WM8961_SOFTWARE_RESET, 0x1801);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, wm8961);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8961, &wm8961_dai, 1);
+
+ return ret;
+}
+
+static int wm8961_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm8961_i2c_id[] = {
+ { "wm8961", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id);
+
+static struct i2c_driver wm8961_i2c_driver = {
+ .driver = {
+ .name = "wm8961",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8961_i2c_probe,
+ .remove = wm8961_i2c_remove,
+ .id_table = wm8961_i2c_id,
+};
+
+module_i2c_driver(wm8961_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8961 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8961.h b/kernel/sound/soc/codecs/wm8961.h
new file mode 100644
index 000000000..1d736e570
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8961.h
@@ -0,0 +1,863 @@
+/*
+ * wm8961.h -- WM8961 Soc Audio driver
+ *
+ * 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 _WM8961_H
+#define _WM8961_H
+
+#include <sound/soc.h>
+
+#define WM8961_BCLK 1
+#define WM8961_LRCLK 2
+
+#define WM8961_BCLK_DIV_1 0
+#define WM8961_BCLK_DIV_1_5 1
+#define WM8961_BCLK_DIV_2 2
+#define WM8961_BCLK_DIV_3 3
+#define WM8961_BCLK_DIV_4 4
+#define WM8961_BCLK_DIV_5_5 5
+#define WM8961_BCLK_DIV_6 6
+#define WM8961_BCLK_DIV_8 7
+#define WM8961_BCLK_DIV_11 8
+#define WM8961_BCLK_DIV_12 9
+#define WM8961_BCLK_DIV_16 10
+#define WM8961_BCLK_DIV_24 11
+#define WM8961_BCLK_DIV_32 13
+
+
+/*
+ * Register values.
+ */
+#define WM8961_LEFT_INPUT_VOLUME 0x00
+#define WM8961_RIGHT_INPUT_VOLUME 0x01
+#define WM8961_LOUT1_VOLUME 0x02
+#define WM8961_ROUT1_VOLUME 0x03
+#define WM8961_CLOCKING1 0x04
+#define WM8961_ADC_DAC_CONTROL_1 0x05
+#define WM8961_ADC_DAC_CONTROL_2 0x06
+#define WM8961_AUDIO_INTERFACE_0 0x07
+#define WM8961_CLOCKING2 0x08
+#define WM8961_AUDIO_INTERFACE_1 0x09
+#define WM8961_LEFT_DAC_VOLUME 0x0A
+#define WM8961_RIGHT_DAC_VOLUME 0x0B
+#define WM8961_AUDIO_INTERFACE_2 0x0E
+#define WM8961_SOFTWARE_RESET 0x0F
+#define WM8961_ALC1 0x11
+#define WM8961_ALC2 0x12
+#define WM8961_ALC3 0x13
+#define WM8961_NOISE_GATE 0x14
+#define WM8961_LEFT_ADC_VOLUME 0x15
+#define WM8961_RIGHT_ADC_VOLUME 0x16
+#define WM8961_ADDITIONAL_CONTROL_1 0x17
+#define WM8961_ADDITIONAL_CONTROL_2 0x18
+#define WM8961_PWR_MGMT_1 0x19
+#define WM8961_PWR_MGMT_2 0x1A
+#define WM8961_ADDITIONAL_CONTROL_3 0x1B
+#define WM8961_ANTI_POP 0x1C
+#define WM8961_CLOCKING_3 0x1E
+#define WM8961_ADCL_SIGNAL_PATH 0x20
+#define WM8961_ADCR_SIGNAL_PATH 0x21
+#define WM8961_LOUT2_VOLUME 0x28
+#define WM8961_ROUT2_VOLUME 0x29
+#define WM8961_PWR_MGMT_3 0x2F
+#define WM8961_ADDITIONAL_CONTROL_4 0x30
+#define WM8961_CLASS_D_CONTROL_1 0x31
+#define WM8961_CLASS_D_CONTROL_2 0x33
+#define WM8961_CLOCKING_4 0x38
+#define WM8961_DSP_SIDETONE_0 0x39
+#define WM8961_DSP_SIDETONE_1 0x3A
+#define WM8961_DC_SERVO_0 0x3C
+#define WM8961_DC_SERVO_1 0x3D
+#define WM8961_DC_SERVO_3 0x3F
+#define WM8961_DC_SERVO_5 0x41
+#define WM8961_ANALOGUE_PGA_BIAS 0x44
+#define WM8961_ANALOGUE_HP_0 0x45
+#define WM8961_ANALOGUE_HP_2 0x47
+#define WM8961_CHARGE_PUMP_1 0x48
+#define WM8961_CHARGE_PUMP_B 0x52
+#define WM8961_WRITE_SEQUENCER_1 0x57
+#define WM8961_WRITE_SEQUENCER_2 0x58
+#define WM8961_WRITE_SEQUENCER_3 0x59
+#define WM8961_WRITE_SEQUENCER_4 0x5A
+#define WM8961_WRITE_SEQUENCER_5 0x5B
+#define WM8961_WRITE_SEQUENCER_6 0x5C
+#define WM8961_WRITE_SEQUENCER_7 0x5D
+#define WM8961_GENERAL_TEST_1 0xFC
+
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Left Input volume
+ */
+#define WM8961_IPVU 0x0100 /* IPVU */
+#define WM8961_IPVU_MASK 0x0100 /* IPVU */
+#define WM8961_IPVU_SHIFT 8 /* IPVU */
+#define WM8961_IPVU_WIDTH 1 /* IPVU */
+#define WM8961_LINMUTE 0x0080 /* LINMUTE */
+#define WM8961_LINMUTE_MASK 0x0080 /* LINMUTE */
+#define WM8961_LINMUTE_SHIFT 7 /* LINMUTE */
+#define WM8961_LINMUTE_WIDTH 1 /* LINMUTE */
+#define WM8961_LIZC 0x0040 /* LIZC */
+#define WM8961_LIZC_MASK 0x0040 /* LIZC */
+#define WM8961_LIZC_SHIFT 6 /* LIZC */
+#define WM8961_LIZC_WIDTH 1 /* LIZC */
+#define WM8961_LINVOL_MASK 0x003F /* LINVOL - [5:0] */
+#define WM8961_LINVOL_SHIFT 0 /* LINVOL - [5:0] */
+#define WM8961_LINVOL_WIDTH 6 /* LINVOL - [5:0] */
+
+/*
+ * R1 (0x01) - Right Input volume
+ */
+#define WM8961_DEVICE_ID_MASK 0xF000 /* DEVICE_ID - [15:12] */
+#define WM8961_DEVICE_ID_SHIFT 12 /* DEVICE_ID - [15:12] */
+#define WM8961_DEVICE_ID_WIDTH 4 /* DEVICE_ID - [15:12] */
+#define WM8961_CHIP_REV_MASK 0x0E00 /* CHIP_REV - [11:9] */
+#define WM8961_CHIP_REV_SHIFT 9 /* CHIP_REV - [11:9] */
+#define WM8961_CHIP_REV_WIDTH 3 /* CHIP_REV - [11:9] */
+#define WM8961_IPVU 0x0100 /* IPVU */
+#define WM8961_IPVU_MASK 0x0100 /* IPVU */
+#define WM8961_IPVU_SHIFT 8 /* IPVU */
+#define WM8961_IPVU_WIDTH 1 /* IPVU */
+#define WM8961_RINMUTE 0x0080 /* RINMUTE */
+#define WM8961_RINMUTE_MASK 0x0080 /* RINMUTE */
+#define WM8961_RINMUTE_SHIFT 7 /* RINMUTE */
+#define WM8961_RINMUTE_WIDTH 1 /* RINMUTE */
+#define WM8961_RIZC 0x0040 /* RIZC */
+#define WM8961_RIZC_MASK 0x0040 /* RIZC */
+#define WM8961_RIZC_SHIFT 6 /* RIZC */
+#define WM8961_RIZC_WIDTH 1 /* RIZC */
+#define WM8961_RINVOL_MASK 0x003F /* RINVOL - [5:0] */
+#define WM8961_RINVOL_SHIFT 0 /* RINVOL - [5:0] */
+#define WM8961_RINVOL_WIDTH 6 /* RINVOL - [5:0] */
+
+/*
+ * R2 (0x02) - LOUT1 volume
+ */
+#define WM8961_OUT1VU 0x0100 /* OUT1VU */
+#define WM8961_OUT1VU_MASK 0x0100 /* OUT1VU */
+#define WM8961_OUT1VU_SHIFT 8 /* OUT1VU */
+#define WM8961_OUT1VU_WIDTH 1 /* OUT1VU */
+#define WM8961_LO1ZC 0x0080 /* LO1ZC */
+#define WM8961_LO1ZC_MASK 0x0080 /* LO1ZC */
+#define WM8961_LO1ZC_SHIFT 7 /* LO1ZC */
+#define WM8961_LO1ZC_WIDTH 1 /* LO1ZC */
+#define WM8961_LOUT1VOL_MASK 0x007F /* LOUT1VOL - [6:0] */
+#define WM8961_LOUT1VOL_SHIFT 0 /* LOUT1VOL - [6:0] */
+#define WM8961_LOUT1VOL_WIDTH 7 /* LOUT1VOL - [6:0] */
+
+/*
+ * R3 (0x03) - ROUT1 volume
+ */
+#define WM8961_OUT1VU 0x0100 /* OUT1VU */
+#define WM8961_OUT1VU_MASK 0x0100 /* OUT1VU */
+#define WM8961_OUT1VU_SHIFT 8 /* OUT1VU */
+#define WM8961_OUT1VU_WIDTH 1 /* OUT1VU */
+#define WM8961_RO1ZC 0x0080 /* RO1ZC */
+#define WM8961_RO1ZC_MASK 0x0080 /* RO1ZC */
+#define WM8961_RO1ZC_SHIFT 7 /* RO1ZC */
+#define WM8961_RO1ZC_WIDTH 1 /* RO1ZC */
+#define WM8961_ROUT1VOL_MASK 0x007F /* ROUT1VOL - [6:0] */
+#define WM8961_ROUT1VOL_SHIFT 0 /* ROUT1VOL - [6:0] */
+#define WM8961_ROUT1VOL_WIDTH 7 /* ROUT1VOL - [6:0] */
+
+/*
+ * R4 (0x04) - Clocking1
+ */
+#define WM8961_ADCDIV_MASK 0x01C0 /* ADCDIV - [8:6] */
+#define WM8961_ADCDIV_SHIFT 6 /* ADCDIV - [8:6] */
+#define WM8961_ADCDIV_WIDTH 3 /* ADCDIV - [8:6] */
+#define WM8961_DACDIV_MASK 0x0038 /* DACDIV - [5:3] */
+#define WM8961_DACDIV_SHIFT 3 /* DACDIV - [5:3] */
+#define WM8961_DACDIV_WIDTH 3 /* DACDIV - [5:3] */
+#define WM8961_MCLKDIV 0x0004 /* MCLKDIV */
+#define WM8961_MCLKDIV_MASK 0x0004 /* MCLKDIV */
+#define WM8961_MCLKDIV_SHIFT 2 /* MCLKDIV */
+#define WM8961_MCLKDIV_WIDTH 1 /* MCLKDIV */
+
+/*
+ * R5 (0x05) - ADC & DAC Control 1
+ */
+#define WM8961_ADCPOL_MASK 0x0060 /* ADCPOL - [6:5] */
+#define WM8961_ADCPOL_SHIFT 5 /* ADCPOL - [6:5] */
+#define WM8961_ADCPOL_WIDTH 2 /* ADCPOL - [6:5] */
+#define WM8961_DACMU 0x0008 /* DACMU */
+#define WM8961_DACMU_MASK 0x0008 /* DACMU */
+#define WM8961_DACMU_SHIFT 3 /* DACMU */
+#define WM8961_DACMU_WIDTH 1 /* DACMU */
+#define WM8961_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */
+#define WM8961_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */
+#define WM8961_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */
+#define WM8961_ADCHPD 0x0001 /* ADCHPD */
+#define WM8961_ADCHPD_MASK 0x0001 /* ADCHPD */
+#define WM8961_ADCHPD_SHIFT 0 /* ADCHPD */
+#define WM8961_ADCHPD_WIDTH 1 /* ADCHPD */
+
+/*
+ * R6 (0x06) - ADC & DAC Control 2
+ */
+#define WM8961_ADC_HPF_CUT_MASK 0x0180 /* ADC_HPF_CUT - [8:7] */
+#define WM8961_ADC_HPF_CUT_SHIFT 7 /* ADC_HPF_CUT - [8:7] */
+#define WM8961_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [8:7] */
+#define WM8961_DACPOL_MASK 0x0060 /* DACPOL - [6:5] */
+#define WM8961_DACPOL_SHIFT 5 /* DACPOL - [6:5] */
+#define WM8961_DACPOL_WIDTH 2 /* DACPOL - [6:5] */
+#define WM8961_DACSMM 0x0008 /* DACSMM */
+#define WM8961_DACSMM_MASK 0x0008 /* DACSMM */
+#define WM8961_DACSMM_SHIFT 3 /* DACSMM */
+#define WM8961_DACSMM_WIDTH 1 /* DACSMM */
+#define WM8961_DACMR 0x0004 /* DACMR */
+#define WM8961_DACMR_MASK 0x0004 /* DACMR */
+#define WM8961_DACMR_SHIFT 2 /* DACMR */
+#define WM8961_DACMR_WIDTH 1 /* DACMR */
+#define WM8961_DACSLOPE 0x0002 /* DACSLOPE */
+#define WM8961_DACSLOPE_MASK 0x0002 /* DACSLOPE */
+#define WM8961_DACSLOPE_SHIFT 1 /* DACSLOPE */
+#define WM8961_DACSLOPE_WIDTH 1 /* DACSLOPE */
+#define WM8961_DAC_OSR128 0x0001 /* DAC_OSR128 */
+#define WM8961_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */
+#define WM8961_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */
+#define WM8961_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */
+
+/*
+ * R7 (0x07) - Audio Interface 0
+ */
+#define WM8961_ALRSWAP 0x0100 /* ALRSWAP */
+#define WM8961_ALRSWAP_MASK 0x0100 /* ALRSWAP */
+#define WM8961_ALRSWAP_SHIFT 8 /* ALRSWAP */
+#define WM8961_ALRSWAP_WIDTH 1 /* ALRSWAP */
+#define WM8961_BCLKINV 0x0080 /* BCLKINV */
+#define WM8961_BCLKINV_MASK 0x0080 /* BCLKINV */
+#define WM8961_BCLKINV_SHIFT 7 /* BCLKINV */
+#define WM8961_BCLKINV_WIDTH 1 /* BCLKINV */
+#define WM8961_MS 0x0040 /* MS */
+#define WM8961_MS_MASK 0x0040 /* MS */
+#define WM8961_MS_SHIFT 6 /* MS */
+#define WM8961_MS_WIDTH 1 /* MS */
+#define WM8961_DLRSWAP 0x0020 /* DLRSWAP */
+#define WM8961_DLRSWAP_MASK 0x0020 /* DLRSWAP */
+#define WM8961_DLRSWAP_SHIFT 5 /* DLRSWAP */
+#define WM8961_DLRSWAP_WIDTH 1 /* DLRSWAP */
+#define WM8961_LRP 0x0010 /* LRP */
+#define WM8961_LRP_MASK 0x0010 /* LRP */
+#define WM8961_LRP_SHIFT 4 /* LRP */
+#define WM8961_LRP_WIDTH 1 /* LRP */
+#define WM8961_WL_MASK 0x000C /* WL - [3:2] */
+#define WM8961_WL_SHIFT 2 /* WL - [3:2] */
+#define WM8961_WL_WIDTH 2 /* WL - [3:2] */
+#define WM8961_FORMAT_MASK 0x0003 /* FORMAT - [1:0] */
+#define WM8961_FORMAT_SHIFT 0 /* FORMAT - [1:0] */
+#define WM8961_FORMAT_WIDTH 2 /* FORMAT - [1:0] */
+
+/*
+ * R8 (0x08) - Clocking2
+ */
+#define WM8961_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */
+#define WM8961_DCLKDIV_SHIFT 6 /* DCLKDIV - [8:6] */
+#define WM8961_DCLKDIV_WIDTH 3 /* DCLKDIV - [8:6] */
+#define WM8961_CLK_SYS_ENA 0x0020 /* CLK_SYS_ENA */
+#define WM8961_CLK_SYS_ENA_MASK 0x0020 /* CLK_SYS_ENA */
+#define WM8961_CLK_SYS_ENA_SHIFT 5 /* CLK_SYS_ENA */
+#define WM8961_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */
+#define WM8961_CLK_DSP_ENA 0x0010 /* CLK_DSP_ENA */
+#define WM8961_CLK_DSP_ENA_MASK 0x0010 /* CLK_DSP_ENA */
+#define WM8961_CLK_DSP_ENA_SHIFT 4 /* CLK_DSP_ENA */
+#define WM8961_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */
+#define WM8961_BCLKDIV_MASK 0x000F /* BCLKDIV - [3:0] */
+#define WM8961_BCLKDIV_SHIFT 0 /* BCLKDIV - [3:0] */
+#define WM8961_BCLKDIV_WIDTH 4 /* BCLKDIV - [3:0] */
+
+/*
+ * R9 (0x09) - Audio Interface 1
+ */
+#define WM8961_DACCOMP_MASK 0x0018 /* DACCOMP - [4:3] */
+#define WM8961_DACCOMP_SHIFT 3 /* DACCOMP - [4:3] */
+#define WM8961_DACCOMP_WIDTH 2 /* DACCOMP - [4:3] */
+#define WM8961_ADCCOMP_MASK 0x0006 /* ADCCOMP - [2:1] */
+#define WM8961_ADCCOMP_SHIFT 1 /* ADCCOMP - [2:1] */
+#define WM8961_ADCCOMP_WIDTH 2 /* ADCCOMP - [2:1] */
+#define WM8961_LOOPBACK 0x0001 /* LOOPBACK */
+#define WM8961_LOOPBACK_MASK 0x0001 /* LOOPBACK */
+#define WM8961_LOOPBACK_SHIFT 0 /* LOOPBACK */
+#define WM8961_LOOPBACK_WIDTH 1 /* LOOPBACK */
+
+/*
+ * R10 (0x0A) - Left DAC volume
+ */
+#define WM8961_DACVU 0x0100 /* DACVU */
+#define WM8961_DACVU_MASK 0x0100 /* DACVU */
+#define WM8961_DACVU_SHIFT 8 /* DACVU */
+#define WM8961_DACVU_WIDTH 1 /* DACVU */
+#define WM8961_LDACVOL_MASK 0x00FF /* LDACVOL - [7:0] */
+#define WM8961_LDACVOL_SHIFT 0 /* LDACVOL - [7:0] */
+#define WM8961_LDACVOL_WIDTH 8 /* LDACVOL - [7:0] */
+
+/*
+ * R11 (0x0B) - Right DAC volume
+ */
+#define WM8961_DACVU 0x0100 /* DACVU */
+#define WM8961_DACVU_MASK 0x0100 /* DACVU */
+#define WM8961_DACVU_SHIFT 8 /* DACVU */
+#define WM8961_DACVU_WIDTH 1 /* DACVU */
+#define WM8961_RDACVOL_MASK 0x00FF /* RDACVOL - [7:0] */
+#define WM8961_RDACVOL_SHIFT 0 /* RDACVOL - [7:0] */
+#define WM8961_RDACVOL_WIDTH 8 /* RDACVOL - [7:0] */
+
+/*
+ * R14 (0x0E) - Audio Interface 2
+ */
+#define WM8961_LRCLK_RATE_MASK 0x01FF /* LRCLK_RATE - [8:0] */
+#define WM8961_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [8:0] */
+#define WM8961_LRCLK_RATE_WIDTH 9 /* LRCLK_RATE - [8:0] */
+
+/*
+ * R15 (0x0F) - Software Reset
+ */
+#define WM8961_SW_RST_DEV_ID1_MASK 0xFFFF /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8961_SW_RST_DEV_ID1_SHIFT 0 /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8961_SW_RST_DEV_ID1_WIDTH 16 /* SW_RST_DEV_ID1 - [15:0] */
+
+/*
+ * R17 (0x11) - ALC1
+ */
+#define WM8961_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */
+#define WM8961_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */
+#define WM8961_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */
+#define WM8961_MAXGAIN_MASK 0x0070 /* MAXGAIN - [6:4] */
+#define WM8961_MAXGAIN_SHIFT 4 /* MAXGAIN - [6:4] */
+#define WM8961_MAXGAIN_WIDTH 3 /* MAXGAIN - [6:4] */
+#define WM8961_ALCL_MASK 0x000F /* ALCL - [3:0] */
+#define WM8961_ALCL_SHIFT 0 /* ALCL - [3:0] */
+#define WM8961_ALCL_WIDTH 4 /* ALCL - [3:0] */
+
+/*
+ * R18 (0x12) - ALC2
+ */
+#define WM8961_ALCZC 0x0080 /* ALCZC */
+#define WM8961_ALCZC_MASK 0x0080 /* ALCZC */
+#define WM8961_ALCZC_SHIFT 7 /* ALCZC */
+#define WM8961_ALCZC_WIDTH 1 /* ALCZC */
+#define WM8961_MINGAIN_MASK 0x0070 /* MINGAIN - [6:4] */
+#define WM8961_MINGAIN_SHIFT 4 /* MINGAIN - [6:4] */
+#define WM8961_MINGAIN_WIDTH 3 /* MINGAIN - [6:4] */
+#define WM8961_HLD_MASK 0x000F /* HLD - [3:0] */
+#define WM8961_HLD_SHIFT 0 /* HLD - [3:0] */
+#define WM8961_HLD_WIDTH 4 /* HLD - [3:0] */
+
+/*
+ * R19 (0x13) - ALC3
+ */
+#define WM8961_ALCMODE 0x0100 /* ALCMODE */
+#define WM8961_ALCMODE_MASK 0x0100 /* ALCMODE */
+#define WM8961_ALCMODE_SHIFT 8 /* ALCMODE */
+#define WM8961_ALCMODE_WIDTH 1 /* ALCMODE */
+#define WM8961_DCY_MASK 0x00F0 /* DCY - [7:4] */
+#define WM8961_DCY_SHIFT 4 /* DCY - [7:4] */
+#define WM8961_DCY_WIDTH 4 /* DCY - [7:4] */
+#define WM8961_ATK_MASK 0x000F /* ATK - [3:0] */
+#define WM8961_ATK_SHIFT 0 /* ATK - [3:0] */
+#define WM8961_ATK_WIDTH 4 /* ATK - [3:0] */
+
+/*
+ * R20 (0x14) - Noise Gate
+ */
+#define WM8961_NGTH_MASK 0x00F8 /* NGTH - [7:3] */
+#define WM8961_NGTH_SHIFT 3 /* NGTH - [7:3] */
+#define WM8961_NGTH_WIDTH 5 /* NGTH - [7:3] */
+#define WM8961_NGG 0x0002 /* NGG */
+#define WM8961_NGG_MASK 0x0002 /* NGG */
+#define WM8961_NGG_SHIFT 1 /* NGG */
+#define WM8961_NGG_WIDTH 1 /* NGG */
+#define WM8961_NGAT 0x0001 /* NGAT */
+#define WM8961_NGAT_MASK 0x0001 /* NGAT */
+#define WM8961_NGAT_SHIFT 0 /* NGAT */
+#define WM8961_NGAT_WIDTH 1 /* NGAT */
+
+/*
+ * R21 (0x15) - Left ADC volume
+ */
+#define WM8961_ADCVU 0x0100 /* ADCVU */
+#define WM8961_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8961_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8961_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8961_LADCVOL_MASK 0x00FF /* LADCVOL - [7:0] */
+#define WM8961_LADCVOL_SHIFT 0 /* LADCVOL - [7:0] */
+#define WM8961_LADCVOL_WIDTH 8 /* LADCVOL - [7:0] */
+
+/*
+ * R22 (0x16) - Right ADC volume
+ */
+#define WM8961_ADCVU 0x0100 /* ADCVU */
+#define WM8961_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8961_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8961_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8961_RADCVOL_MASK 0x00FF /* RADCVOL - [7:0] */
+#define WM8961_RADCVOL_SHIFT 0 /* RADCVOL - [7:0] */
+#define WM8961_RADCVOL_WIDTH 8 /* RADCVOL - [7:0] */
+
+/*
+ * R23 (0x17) - Additional control(1)
+ */
+#define WM8961_TSDEN 0x0100 /* TSDEN */
+#define WM8961_TSDEN_MASK 0x0100 /* TSDEN */
+#define WM8961_TSDEN_SHIFT 8 /* TSDEN */
+#define WM8961_TSDEN_WIDTH 1 /* TSDEN */
+#define WM8961_DMONOMIX 0x0010 /* DMONOMIX */
+#define WM8961_DMONOMIX_MASK 0x0010 /* DMONOMIX */
+#define WM8961_DMONOMIX_SHIFT 4 /* DMONOMIX */
+#define WM8961_DMONOMIX_WIDTH 1 /* DMONOMIX */
+#define WM8961_TOEN 0x0001 /* TOEN */
+#define WM8961_TOEN_MASK 0x0001 /* TOEN */
+#define WM8961_TOEN_SHIFT 0 /* TOEN */
+#define WM8961_TOEN_WIDTH 1 /* TOEN */
+
+/*
+ * R24 (0x18) - Additional control(2)
+ */
+#define WM8961_TRIS 0x0008 /* TRIS */
+#define WM8961_TRIS_MASK 0x0008 /* TRIS */
+#define WM8961_TRIS_SHIFT 3 /* TRIS */
+#define WM8961_TRIS_WIDTH 1 /* TRIS */
+
+/*
+ * R25 (0x19) - Pwr Mgmt (1)
+ */
+#define WM8961_VMIDSEL_MASK 0x0180 /* VMIDSEL - [8:7] */
+#define WM8961_VMIDSEL_SHIFT 7 /* VMIDSEL - [8:7] */
+#define WM8961_VMIDSEL_WIDTH 2 /* VMIDSEL - [8:7] */
+#define WM8961_VREF 0x0040 /* VREF */
+#define WM8961_VREF_MASK 0x0040 /* VREF */
+#define WM8961_VREF_SHIFT 6 /* VREF */
+#define WM8961_VREF_WIDTH 1 /* VREF */
+#define WM8961_AINL 0x0020 /* AINL */
+#define WM8961_AINL_MASK 0x0020 /* AINL */
+#define WM8961_AINL_SHIFT 5 /* AINL */
+#define WM8961_AINL_WIDTH 1 /* AINL */
+#define WM8961_AINR 0x0010 /* AINR */
+#define WM8961_AINR_MASK 0x0010 /* AINR */
+#define WM8961_AINR_SHIFT 4 /* AINR */
+#define WM8961_AINR_WIDTH 1 /* AINR */
+#define WM8961_ADCL 0x0008 /* ADCL */
+#define WM8961_ADCL_MASK 0x0008 /* ADCL */
+#define WM8961_ADCL_SHIFT 3 /* ADCL */
+#define WM8961_ADCL_WIDTH 1 /* ADCL */
+#define WM8961_ADCR 0x0004 /* ADCR */
+#define WM8961_ADCR_MASK 0x0004 /* ADCR */
+#define WM8961_ADCR_SHIFT 2 /* ADCR */
+#define WM8961_ADCR_WIDTH 1 /* ADCR */
+#define WM8961_MICB 0x0002 /* MICB */
+#define WM8961_MICB_MASK 0x0002 /* MICB */
+#define WM8961_MICB_SHIFT 1 /* MICB */
+#define WM8961_MICB_WIDTH 1 /* MICB */
+
+/*
+ * R26 (0x1A) - Pwr Mgmt (2)
+ */
+#define WM8961_DACL 0x0100 /* DACL */
+#define WM8961_DACL_MASK 0x0100 /* DACL */
+#define WM8961_DACL_SHIFT 8 /* DACL */
+#define WM8961_DACL_WIDTH 1 /* DACL */
+#define WM8961_DACR 0x0080 /* DACR */
+#define WM8961_DACR_MASK 0x0080 /* DACR */
+#define WM8961_DACR_SHIFT 7 /* DACR */
+#define WM8961_DACR_WIDTH 1 /* DACR */
+#define WM8961_LOUT1_PGA 0x0040 /* LOUT1_PGA */
+#define WM8961_LOUT1_PGA_MASK 0x0040 /* LOUT1_PGA */
+#define WM8961_LOUT1_PGA_SHIFT 6 /* LOUT1_PGA */
+#define WM8961_LOUT1_PGA_WIDTH 1 /* LOUT1_PGA */
+#define WM8961_ROUT1_PGA 0x0020 /* ROUT1_PGA */
+#define WM8961_ROUT1_PGA_MASK 0x0020 /* ROUT1_PGA */
+#define WM8961_ROUT1_PGA_SHIFT 5 /* ROUT1_PGA */
+#define WM8961_ROUT1_PGA_WIDTH 1 /* ROUT1_PGA */
+#define WM8961_SPKL_PGA 0x0010 /* SPKL_PGA */
+#define WM8961_SPKL_PGA_MASK 0x0010 /* SPKL_PGA */
+#define WM8961_SPKL_PGA_SHIFT 4 /* SPKL_PGA */
+#define WM8961_SPKL_PGA_WIDTH 1 /* SPKL_PGA */
+#define WM8961_SPKR_PGA 0x0008 /* SPKR_PGA */
+#define WM8961_SPKR_PGA_MASK 0x0008 /* SPKR_PGA */
+#define WM8961_SPKR_PGA_SHIFT 3 /* SPKR_PGA */
+#define WM8961_SPKR_PGA_WIDTH 1 /* SPKR_PGA */
+
+/*
+ * R27 (0x1B) - Additional Control (3)
+ */
+#define WM8961_SAMPLE_RATE_MASK 0x0007 /* SAMPLE_RATE - [2:0] */
+#define WM8961_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [2:0] */
+#define WM8961_SAMPLE_RATE_WIDTH 3 /* SAMPLE_RATE - [2:0] */
+
+/*
+ * R28 (0x1C) - Anti-pop
+ */
+#define WM8961_BUFDCOPEN 0x0010 /* BUFDCOPEN */
+#define WM8961_BUFDCOPEN_MASK 0x0010 /* BUFDCOPEN */
+#define WM8961_BUFDCOPEN_SHIFT 4 /* BUFDCOPEN */
+#define WM8961_BUFDCOPEN_WIDTH 1 /* BUFDCOPEN */
+#define WM8961_BUFIOEN 0x0008 /* BUFIOEN */
+#define WM8961_BUFIOEN_MASK 0x0008 /* BUFIOEN */
+#define WM8961_BUFIOEN_SHIFT 3 /* BUFIOEN */
+#define WM8961_BUFIOEN_WIDTH 1 /* BUFIOEN */
+#define WM8961_SOFT_ST 0x0004 /* SOFT_ST */
+#define WM8961_SOFT_ST_MASK 0x0004 /* SOFT_ST */
+#define WM8961_SOFT_ST_SHIFT 2 /* SOFT_ST */
+#define WM8961_SOFT_ST_WIDTH 1 /* SOFT_ST */
+
+/*
+ * R30 (0x1E) - Clocking 3
+ */
+#define WM8961_CLK_TO_DIV_MASK 0x0180 /* CLK_TO_DIV - [8:7] */
+#define WM8961_CLK_TO_DIV_SHIFT 7 /* CLK_TO_DIV - [8:7] */
+#define WM8961_CLK_TO_DIV_WIDTH 2 /* CLK_TO_DIV - [8:7] */
+#define WM8961_CLK_256K_DIV_MASK 0x007E /* CLK_256K_DIV - [6:1] */
+#define WM8961_CLK_256K_DIV_SHIFT 1 /* CLK_256K_DIV - [6:1] */
+#define WM8961_CLK_256K_DIV_WIDTH 6 /* CLK_256K_DIV - [6:1] */
+#define WM8961_MANUAL_MODE 0x0001 /* MANUAL_MODE */
+#define WM8961_MANUAL_MODE_MASK 0x0001 /* MANUAL_MODE */
+#define WM8961_MANUAL_MODE_SHIFT 0 /* MANUAL_MODE */
+#define WM8961_MANUAL_MODE_WIDTH 1 /* MANUAL_MODE */
+
+/*
+ * R32 (0x20) - ADCL signal path
+ */
+#define WM8961_LMICBOOST_MASK 0x0030 /* LMICBOOST - [5:4] */
+#define WM8961_LMICBOOST_SHIFT 4 /* LMICBOOST - [5:4] */
+#define WM8961_LMICBOOST_WIDTH 2 /* LMICBOOST - [5:4] */
+
+/*
+ * R33 (0x21) - ADCR signal path
+ */
+#define WM8961_RMICBOOST_MASK 0x0030 /* RMICBOOST - [5:4] */
+#define WM8961_RMICBOOST_SHIFT 4 /* RMICBOOST - [5:4] */
+#define WM8961_RMICBOOST_WIDTH 2 /* RMICBOOST - [5:4] */
+
+/*
+ * R40 (0x28) - LOUT2 volume
+ */
+#define WM8961_SPKVU 0x0100 /* SPKVU */
+#define WM8961_SPKVU_MASK 0x0100 /* SPKVU */
+#define WM8961_SPKVU_SHIFT 8 /* SPKVU */
+#define WM8961_SPKVU_WIDTH 1 /* SPKVU */
+#define WM8961_SPKLZC 0x0080 /* SPKLZC */
+#define WM8961_SPKLZC_MASK 0x0080 /* SPKLZC */
+#define WM8961_SPKLZC_SHIFT 7 /* SPKLZC */
+#define WM8961_SPKLZC_WIDTH 1 /* SPKLZC */
+#define WM8961_SPKLVOL_MASK 0x007F /* SPKLVOL - [6:0] */
+#define WM8961_SPKLVOL_SHIFT 0 /* SPKLVOL - [6:0] */
+#define WM8961_SPKLVOL_WIDTH 7 /* SPKLVOL - [6:0] */
+
+/*
+ * R41 (0x29) - ROUT2 volume
+ */
+#define WM8961_SPKVU 0x0100 /* SPKVU */
+#define WM8961_SPKVU_MASK 0x0100 /* SPKVU */
+#define WM8961_SPKVU_SHIFT 8 /* SPKVU */
+#define WM8961_SPKVU_WIDTH 1 /* SPKVU */
+#define WM8961_SPKRZC 0x0080 /* SPKRZC */
+#define WM8961_SPKRZC_MASK 0x0080 /* SPKRZC */
+#define WM8961_SPKRZC_SHIFT 7 /* SPKRZC */
+#define WM8961_SPKRZC_WIDTH 1 /* SPKRZC */
+#define WM8961_SPKRVOL_MASK 0x007F /* SPKRVOL - [6:0] */
+#define WM8961_SPKRVOL_SHIFT 0 /* SPKRVOL - [6:0] */
+#define WM8961_SPKRVOL_WIDTH 7 /* SPKRVOL - [6:0] */
+
+/*
+ * R47 (0x2F) - Pwr Mgmt (3)
+ */
+#define WM8961_TEMP_SHUT 0x0002 /* TEMP_SHUT */
+#define WM8961_TEMP_SHUT_MASK 0x0002 /* TEMP_SHUT */
+#define WM8961_TEMP_SHUT_SHIFT 1 /* TEMP_SHUT */
+#define WM8961_TEMP_SHUT_WIDTH 1 /* TEMP_SHUT */
+#define WM8961_TEMP_WARN 0x0001 /* TEMP_WARN */
+#define WM8961_TEMP_WARN_MASK 0x0001 /* TEMP_WARN */
+#define WM8961_TEMP_WARN_SHIFT 0 /* TEMP_WARN */
+#define WM8961_TEMP_WARN_WIDTH 1 /* TEMP_WARN */
+
+/*
+ * R48 (0x30) - Additional Control (4)
+ */
+#define WM8961_TSENSEN 0x0002 /* TSENSEN */
+#define WM8961_TSENSEN_MASK 0x0002 /* TSENSEN */
+#define WM8961_TSENSEN_SHIFT 1 /* TSENSEN */
+#define WM8961_TSENSEN_WIDTH 1 /* TSENSEN */
+#define WM8961_MBSEL 0x0001 /* MBSEL */
+#define WM8961_MBSEL_MASK 0x0001 /* MBSEL */
+#define WM8961_MBSEL_SHIFT 0 /* MBSEL */
+#define WM8961_MBSEL_WIDTH 1 /* MBSEL */
+
+/*
+ * R49 (0x31) - Class D Control 1
+ */
+#define WM8961_SPKR_ENA 0x0080 /* SPKR_ENA */
+#define WM8961_SPKR_ENA_MASK 0x0080 /* SPKR_ENA */
+#define WM8961_SPKR_ENA_SHIFT 7 /* SPKR_ENA */
+#define WM8961_SPKR_ENA_WIDTH 1 /* SPKR_ENA */
+#define WM8961_SPKL_ENA 0x0040 /* SPKL_ENA */
+#define WM8961_SPKL_ENA_MASK 0x0040 /* SPKL_ENA */
+#define WM8961_SPKL_ENA_SHIFT 6 /* SPKL_ENA */
+#define WM8961_SPKL_ENA_WIDTH 1 /* SPKL_ENA */
+
+/*
+ * R51 (0x33) - Class D Control 2
+ */
+#define WM8961_CLASSD_ACGAIN_MASK 0x0007 /* CLASSD_ACGAIN - [2:0] */
+#define WM8961_CLASSD_ACGAIN_SHIFT 0 /* CLASSD_ACGAIN - [2:0] */
+#define WM8961_CLASSD_ACGAIN_WIDTH 3 /* CLASSD_ACGAIN - [2:0] */
+
+/*
+ * R56 (0x38) - Clocking 4
+ */
+#define WM8961_CLK_DCS_DIV_MASK 0x01E0 /* CLK_DCS_DIV - [8:5] */
+#define WM8961_CLK_DCS_DIV_SHIFT 5 /* CLK_DCS_DIV - [8:5] */
+#define WM8961_CLK_DCS_DIV_WIDTH 4 /* CLK_DCS_DIV - [8:5] */
+#define WM8961_CLK_SYS_RATE_MASK 0x001E /* CLK_SYS_RATE - [4:1] */
+#define WM8961_CLK_SYS_RATE_SHIFT 1 /* CLK_SYS_RATE - [4:1] */
+#define WM8961_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [4:1] */
+
+/*
+ * R57 (0x39) - DSP Sidetone 0
+ */
+#define WM8961_ADCR_DAC_SVOL_MASK 0x00F0 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8961_ADCR_DAC_SVOL_SHIFT 4 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8961_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8961_ADC_TO_DACR_MASK 0x000C /* ADC_TO_DACR - [3:2] */
+#define WM8961_ADC_TO_DACR_SHIFT 2 /* ADC_TO_DACR - [3:2] */
+#define WM8961_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [3:2] */
+
+/*
+ * R58 (0x3A) - DSP Sidetone 1
+ */
+#define WM8961_ADCL_DAC_SVOL_MASK 0x00F0 /* ADCL_DAC_SVOL - [7:4] */
+#define WM8961_ADCL_DAC_SVOL_SHIFT 4 /* ADCL_DAC_SVOL - [7:4] */
+#define WM8961_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [7:4] */
+#define WM8961_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */
+#define WM8961_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */
+#define WM8961_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */
+
+/*
+ * R60 (0x3C) - DC Servo 0
+ */
+#define WM8961_DCS_ENA_CHAN_INL 0x0080 /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_ENA_CHAN_INL_MASK 0x0080 /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_ENA_CHAN_INL_SHIFT 7 /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_ENA_CHAN_INL_WIDTH 1 /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL 0x0040 /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL_MASK 0x0040 /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL_SHIFT 6 /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL_WIDTH 1 /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_SERIES_INL 0x0010 /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_TRIG_SERIES_INL_MASK 0x0010 /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_TRIG_SERIES_INL_SHIFT 4 /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_TRIG_SERIES_INL_WIDTH 1 /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_ENA_CHAN_INR 0x0008 /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_ENA_CHAN_INR_MASK 0x0008 /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_ENA_CHAN_INR_SHIFT 3 /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_ENA_CHAN_INR_WIDTH 1 /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR 0x0004 /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR_MASK 0x0004 /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR_SHIFT 2 /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR_WIDTH 1 /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_SERIES_INR 0x0001 /* DCS_TRIG_SERIES_INR */
+#define WM8961_DCS_TRIG_SERIES_INR_MASK 0x0001 /* DCS_TRIG_SERIES_INR */
+#define WM8961_DCS_TRIG_SERIES_INR_SHIFT 0 /* DCS_TRIG_SERIES_INR */
+#define WM8961_DCS_TRIG_SERIES_INR_WIDTH 1 /* DCS_TRIG_SERIES_INR */
+
+/*
+ * R61 (0x3D) - DC Servo 1
+ */
+#define WM8961_DCS_ENA_CHAN_HPL 0x0080 /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_ENA_CHAN_HPL_MASK 0x0080 /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_ENA_CHAN_HPL_SHIFT 7 /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_ENA_CHAN_HPL_WIDTH 1 /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL 0x0040 /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL_MASK 0x0040 /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL_SHIFT 6 /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL_WIDTH 1 /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL 0x0010 /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL_MASK 0x0010 /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL_SHIFT 4 /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL_WIDTH 1 /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_ENA_CHAN_HPR 0x0008 /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_ENA_CHAN_HPR_MASK 0x0008 /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_ENA_CHAN_HPR_SHIFT 3 /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_ENA_CHAN_HPR_WIDTH 1 /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR 0x0004 /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR_MASK 0x0004 /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR_SHIFT 2 /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR_WIDTH 1 /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR 0x0001 /* DCS_TRIG_SERIES_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR_MASK 0x0001 /* DCS_TRIG_SERIES_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR_SHIFT 0 /* DCS_TRIG_SERIES_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR_WIDTH 1 /* DCS_TRIG_SERIES_HPR */
+
+/*
+ * R63 (0x3F) - DC Servo 3
+ */
+#define WM8961_DCS_FILT_BW_SERIES_MASK 0x0030 /* DCS_FILT_BW_SERIES - [5:4] */
+#define WM8961_DCS_FILT_BW_SERIES_SHIFT 4 /* DCS_FILT_BW_SERIES - [5:4] */
+#define WM8961_DCS_FILT_BW_SERIES_WIDTH 2 /* DCS_FILT_BW_SERIES - [5:4] */
+
+/*
+ * R65 (0x41) - DC Servo 5
+ */
+#define WM8961_DCS_SERIES_NO_HP_MASK 0x007F /* DCS_SERIES_NO_HP - [6:0] */
+#define WM8961_DCS_SERIES_NO_HP_SHIFT 0 /* DCS_SERIES_NO_HP - [6:0] */
+#define WM8961_DCS_SERIES_NO_HP_WIDTH 7 /* DCS_SERIES_NO_HP - [6:0] */
+
+/*
+ * R68 (0x44) - Analogue PGA Bias
+ */
+#define WM8961_HP_PGAS_BIAS_MASK 0x0007 /* HP_PGAS_BIAS - [2:0] */
+#define WM8961_HP_PGAS_BIAS_SHIFT 0 /* HP_PGAS_BIAS - [2:0] */
+#define WM8961_HP_PGAS_BIAS_WIDTH 3 /* HP_PGAS_BIAS - [2:0] */
+
+/*
+ * R69 (0x45) - Analogue HP 0
+ */
+#define WM8961_HPL_RMV_SHORT 0x0080 /* HPL_RMV_SHORT */
+#define WM8961_HPL_RMV_SHORT_MASK 0x0080 /* HPL_RMV_SHORT */
+#define WM8961_HPL_RMV_SHORT_SHIFT 7 /* HPL_RMV_SHORT */
+#define WM8961_HPL_RMV_SHORT_WIDTH 1 /* HPL_RMV_SHORT */
+#define WM8961_HPL_ENA_OUTP 0x0040 /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_OUTP_MASK 0x0040 /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_OUTP_SHIFT 6 /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_OUTP_WIDTH 1 /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_DLY 0x0020 /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA_DLY_MASK 0x0020 /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA_DLY_SHIFT 5 /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA_DLY_WIDTH 1 /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA 0x0010 /* HPL_ENA */
+#define WM8961_HPL_ENA_MASK 0x0010 /* HPL_ENA */
+#define WM8961_HPL_ENA_SHIFT 4 /* HPL_ENA */
+#define WM8961_HPL_ENA_WIDTH 1 /* HPL_ENA */
+#define WM8961_HPR_RMV_SHORT 0x0008 /* HPR_RMV_SHORT */
+#define WM8961_HPR_RMV_SHORT_MASK 0x0008 /* HPR_RMV_SHORT */
+#define WM8961_HPR_RMV_SHORT_SHIFT 3 /* HPR_RMV_SHORT */
+#define WM8961_HPR_RMV_SHORT_WIDTH 1 /* HPR_RMV_SHORT */
+#define WM8961_HPR_ENA_OUTP 0x0004 /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_OUTP_MASK 0x0004 /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_OUTP_SHIFT 2 /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_OUTP_WIDTH 1 /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_DLY 0x0002 /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA_DLY_MASK 0x0002 /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA_DLY_SHIFT 1 /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA_DLY_WIDTH 1 /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA 0x0001 /* HPR_ENA */
+#define WM8961_HPR_ENA_MASK 0x0001 /* HPR_ENA */
+#define WM8961_HPR_ENA_SHIFT 0 /* HPR_ENA */
+#define WM8961_HPR_ENA_WIDTH 1 /* HPR_ENA */
+
+/*
+ * R71 (0x47) - Analogue HP 2
+ */
+#define WM8961_HPL_VOL_MASK 0x01C0 /* HPL_VOL - [8:6] */
+#define WM8961_HPL_VOL_SHIFT 6 /* HPL_VOL - [8:6] */
+#define WM8961_HPL_VOL_WIDTH 3 /* HPL_VOL - [8:6] */
+#define WM8961_HPR_VOL_MASK 0x0038 /* HPR_VOL - [5:3] */
+#define WM8961_HPR_VOL_SHIFT 3 /* HPR_VOL - [5:3] */
+#define WM8961_HPR_VOL_WIDTH 3 /* HPR_VOL - [5:3] */
+#define WM8961_HP_BIAS_BOOST_MASK 0x0007 /* HP_BIAS_BOOST - [2:0] */
+#define WM8961_HP_BIAS_BOOST_SHIFT 0 /* HP_BIAS_BOOST - [2:0] */
+#define WM8961_HP_BIAS_BOOST_WIDTH 3 /* HP_BIAS_BOOST - [2:0] */
+
+/*
+ * R72 (0x48) - Charge Pump 1
+ */
+#define WM8961_CP_ENA 0x0001 /* CP_ENA */
+#define WM8961_CP_ENA_MASK 0x0001 /* CP_ENA */
+#define WM8961_CP_ENA_SHIFT 0 /* CP_ENA */
+#define WM8961_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R82 (0x52) - Charge Pump B
+ */
+#define WM8961_CP_DYN_PWR_MASK 0x0003 /* CP_DYN_PWR - [1:0] */
+#define WM8961_CP_DYN_PWR_SHIFT 0 /* CP_DYN_PWR - [1:0] */
+#define WM8961_CP_DYN_PWR_WIDTH 2 /* CP_DYN_PWR - [1:0] */
+
+/*
+ * R87 (0x57) - Write Sequencer 1
+ */
+#define WM8961_WSEQ_ENA 0x0020 /* WSEQ_ENA */
+#define WM8961_WSEQ_ENA_MASK 0x0020 /* WSEQ_ENA */
+#define WM8961_WSEQ_ENA_SHIFT 5 /* WSEQ_ENA */
+#define WM8961_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM8961_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8961_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8961_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R88 (0x58) - Write Sequencer 2
+ */
+#define WM8961_WSEQ_EOS 0x0100 /* WSEQ_EOS */
+#define WM8961_WSEQ_EOS_MASK 0x0100 /* WSEQ_EOS */
+#define WM8961_WSEQ_EOS_SHIFT 8 /* WSEQ_EOS */
+#define WM8961_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */
+#define WM8961_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */
+#define WM8961_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */
+#define WM8961_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R89 (0x59) - Write Sequencer 3
+ */
+#define WM8961_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */
+#define WM8961_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */
+#define WM8961_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */
+
+/*
+ * R90 (0x5A) - Write Sequencer 4
+ */
+#define WM8961_WSEQ_ABORT 0x0100 /* WSEQ_ABORT */
+#define WM8961_WSEQ_ABORT_MASK 0x0100 /* WSEQ_ABORT */
+#define WM8961_WSEQ_ABORT_SHIFT 8 /* WSEQ_ABORT */
+#define WM8961_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8961_WSEQ_START 0x0080 /* WSEQ_START */
+#define WM8961_WSEQ_START_MASK 0x0080 /* WSEQ_START */
+#define WM8961_WSEQ_START_SHIFT 7 /* WSEQ_START */
+#define WM8961_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8961_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */
+#define WM8961_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */
+#define WM8961_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R91 (0x5B) - Write Sequencer 5
+ */
+#define WM8961_WSEQ_DATA_WIDTH_MASK 0x0070 /* WSEQ_DATA_WIDTH - [6:4] */
+#define WM8961_WSEQ_DATA_WIDTH_SHIFT 4 /* WSEQ_DATA_WIDTH - [6:4] */
+#define WM8961_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [6:4] */
+#define WM8961_WSEQ_DATA_START_MASK 0x000F /* WSEQ_DATA_START - [3:0] */
+#define WM8961_WSEQ_DATA_START_SHIFT 0 /* WSEQ_DATA_START - [3:0] */
+#define WM8961_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [3:0] */
+
+/*
+ * R92 (0x5C) - Write Sequencer 6
+ */
+#define WM8961_WSEQ_DELAY_MASK 0x000F /* WSEQ_DELAY - [3:0] */
+#define WM8961_WSEQ_DELAY_SHIFT 0 /* WSEQ_DELAY - [3:0] */
+#define WM8961_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [3:0] */
+
+/*
+ * R93 (0x5D) - Write Sequencer 7
+ */
+#define WM8961_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */
+#define WM8961_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */
+#define WM8961_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */
+#define WM8961_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+
+/*
+ * R252 (0xFC) - General test 1
+ */
+#define WM8961_ARA_ENA 0x0002 /* ARA_ENA */
+#define WM8961_ARA_ENA_MASK 0x0002 /* ARA_ENA */
+#define WM8961_ARA_ENA_SHIFT 1 /* ARA_ENA */
+#define WM8961_ARA_ENA_WIDTH 1 /* ARA_ENA */
+#define WM8961_AUTO_INC 0x0001 /* AUTO_INC */
+#define WM8961_AUTO_INC_MASK 0x0001 /* AUTO_INC */
+#define WM8961_AUTO_INC_SHIFT 0 /* AUTO_INC */
+#define WM8961_AUTO_INC_WIDTH 1 /* AUTO_INC */
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8962.c b/kernel/sound/soc/codecs/wm8962.c
new file mode 100644
index 000000000..118b0034b
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8962.c
@@ -0,0 +1,3897 @@
+/*
+ * wm8962.c -- WM8962 ALSA SoC Audio driver
+ *
+ * Copyright 2010-2 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gcd.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/wm8962.h>
+#include <trace/events/asoc.h>
+
+#include "wm8962.h"
+
+#define WM8962_NUM_SUPPLIES 8
+static const char *wm8962_supply_names[WM8962_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD",
+ "AVDD",
+ "CPVDD",
+ "MICVDD",
+ "PLLVDD",
+ "SPKVDD1",
+ "SPKVDD2",
+};
+
+/* codec private data */
+struct wm8962_priv {
+ struct wm8962_pdata pdata;
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+
+ int sysclk;
+ int sysclk_rate;
+
+ int bclk; /* Desired BCLK */
+ int lrclk;
+
+ struct completion fll_lock;
+ int fll_src;
+ int fll_fref;
+ int fll_fout;
+
+ struct mutex dsp2_ena_lock;
+ u16 dsp2_ena;
+
+ struct delayed_work mic_work;
+ struct snd_soc_jack *jack;
+
+ struct regulator_bulk_data supplies[WM8962_NUM_SUPPLIES];
+ struct notifier_block disable_nb[WM8962_NUM_SUPPLIES];
+
+ struct input_dev *beep;
+ struct work_struct beep_work;
+ int beep_rate;
+
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio_chip;
+#endif
+
+ int irq;
+};
+
+/* We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define WM8962_REGULATOR_EVENT(n) \
+static int wm8962_regulator_event_##n(struct notifier_block *nb, \
+ unsigned long event, void *data) \
+{ \
+ struct wm8962_priv *wm8962 = container_of(nb, struct wm8962_priv, \
+ disable_nb[n]); \
+ if (event & REGULATOR_EVENT_DISABLE) { \
+ regcache_mark_dirty(wm8962->regmap); \
+ } \
+ return 0; \
+}
+
+WM8962_REGULATOR_EVENT(0)
+WM8962_REGULATOR_EVENT(1)
+WM8962_REGULATOR_EVENT(2)
+WM8962_REGULATOR_EVENT(3)
+WM8962_REGULATOR_EVENT(4)
+WM8962_REGULATOR_EVENT(5)
+WM8962_REGULATOR_EVENT(6)
+WM8962_REGULATOR_EVENT(7)
+
+static struct reg_default wm8962_reg[] = {
+ { 0, 0x009F }, /* R0 - Left Input volume */
+ { 1, 0x049F }, /* R1 - Right Input volume */
+ { 2, 0x0000 }, /* R2 - HPOUTL volume */
+ { 3, 0x0000 }, /* R3 - HPOUTR volume */
+
+ { 5, 0x0018 }, /* R5 - ADC & DAC Control 1 */
+ { 6, 0x2008 }, /* R6 - ADC & DAC Control 2 */
+ { 7, 0x000A }, /* R7 - Audio Interface 0 */
+
+ { 9, 0x0300 }, /* R9 - Audio Interface 1 */
+ { 10, 0x00C0 }, /* R10 - Left DAC volume */
+ { 11, 0x00C0 }, /* R11 - Right DAC volume */
+
+ { 14, 0x0040 }, /* R14 - Audio Interface 2 */
+ { 15, 0x6243 }, /* R15 - Software Reset */
+
+ { 17, 0x007B }, /* R17 - ALC1 */
+
+ { 19, 0x1C32 }, /* R19 - ALC3 */
+ { 20, 0x3200 }, /* R20 - Noise Gate */
+ { 21, 0x00C0 }, /* R21 - Left ADC volume */
+ { 22, 0x00C0 }, /* R22 - Right ADC volume */
+ { 23, 0x0160 }, /* R23 - Additional control(1) */
+ { 24, 0x0000 }, /* R24 - Additional control(2) */
+ { 25, 0x0000 }, /* R25 - Pwr Mgmt (1) */
+ { 26, 0x0000 }, /* R26 - Pwr Mgmt (2) */
+ { 27, 0x0010 }, /* R27 - Additional Control (3) */
+ { 28, 0x0000 }, /* R28 - Anti-pop */
+
+ { 30, 0x005E }, /* R30 - Clocking 3 */
+ { 31, 0x0000 }, /* R31 - Input mixer control (1) */
+ { 32, 0x0145 }, /* R32 - Left input mixer volume */
+ { 33, 0x0145 }, /* R33 - Right input mixer volume */
+ { 34, 0x0009 }, /* R34 - Input mixer control (2) */
+ { 35, 0x0003 }, /* R35 - Input bias control */
+ { 37, 0x0008 }, /* R37 - Left input PGA control */
+ { 38, 0x0008 }, /* R38 - Right input PGA control */
+
+ { 40, 0x0000 }, /* R40 - SPKOUTL volume */
+ { 41, 0x0000 }, /* R41 - SPKOUTR volume */
+
+ { 49, 0x0010 }, /* R49 - Class D Control 1 */
+ { 51, 0x0003 }, /* R51 - Class D Control 2 */
+
+ { 56, 0x0506 }, /* R56 - Clocking 4 */
+ { 57, 0x0000 }, /* R57 - DAC DSP Mixing (1) */
+ { 58, 0x0000 }, /* R58 - DAC DSP Mixing (2) */
+
+ { 60, 0x0300 }, /* R60 - DC Servo 0 */
+ { 61, 0x0300 }, /* R61 - DC Servo 1 */
+
+ { 64, 0x0810 }, /* R64 - DC Servo 4 */
+
+ { 68, 0x001B }, /* R68 - Analogue PGA Bias */
+ { 69, 0x0000 }, /* R69 - Analogue HP 0 */
+
+ { 71, 0x01FB }, /* R71 - Analogue HP 2 */
+ { 72, 0x0000 }, /* R72 - Charge Pump 1 */
+
+ { 82, 0x0004 }, /* R82 - Charge Pump B */
+
+ { 87, 0x0000 }, /* R87 - Write Sequencer Control 1 */
+
+ { 90, 0x0000 }, /* R90 - Write Sequencer Control 2 */
+
+ { 93, 0x0000 }, /* R93 - Write Sequencer Control 3 */
+ { 94, 0x0000 }, /* R94 - Control Interface */
+
+ { 99, 0x0000 }, /* R99 - Mixer Enables */
+ { 100, 0x0000 }, /* R100 - Headphone Mixer (1) */
+ { 101, 0x0000 }, /* R101 - Headphone Mixer (2) */
+ { 102, 0x013F }, /* R102 - Headphone Mixer (3) */
+ { 103, 0x013F }, /* R103 - Headphone Mixer (4) */
+
+ { 105, 0x0000 }, /* R105 - Speaker Mixer (1) */
+ { 106, 0x0000 }, /* R106 - Speaker Mixer (2) */
+ { 107, 0x013F }, /* R107 - Speaker Mixer (3) */
+ { 108, 0x013F }, /* R108 - Speaker Mixer (4) */
+ { 109, 0x0003 }, /* R109 - Speaker Mixer (5) */
+ { 110, 0x0002 }, /* R110 - Beep Generator (1) */
+
+ { 115, 0x0006 }, /* R115 - Oscillator Trim (3) */
+ { 116, 0x0026 }, /* R116 - Oscillator Trim (4) */
+
+ { 119, 0x0000 }, /* R119 - Oscillator Trim (7) */
+
+ { 124, 0x0011 }, /* R124 - Analogue Clocking1 */
+ { 125, 0x004B }, /* R125 - Analogue Clocking2 */
+ { 126, 0x000D }, /* R126 - Analogue Clocking3 */
+ { 127, 0x0000 }, /* R127 - PLL Software Reset */
+
+ { 131, 0x0000 }, /* R131 - PLL 4 */
+
+ { 136, 0x0067 }, /* R136 - PLL 9 */
+ { 137, 0x001C }, /* R137 - PLL 10 */
+ { 138, 0x0071 }, /* R138 - PLL 11 */
+ { 139, 0x00C7 }, /* R139 - PLL 12 */
+ { 140, 0x0067 }, /* R140 - PLL 13 */
+ { 141, 0x0048 }, /* R141 - PLL 14 */
+ { 142, 0x0022 }, /* R142 - PLL 15 */
+ { 143, 0x0097 }, /* R143 - PLL 16 */
+
+ { 155, 0x000C }, /* R155 - FLL Control (1) */
+ { 156, 0x0039 }, /* R156 - FLL Control (2) */
+ { 157, 0x0180 }, /* R157 - FLL Control (3) */
+
+ { 159, 0x0032 }, /* R159 - FLL Control (5) */
+ { 160, 0x0018 }, /* R160 - FLL Control (6) */
+ { 161, 0x007D }, /* R161 - FLL Control (7) */
+ { 162, 0x0008 }, /* R162 - FLL Control (8) */
+
+ { 252, 0x0005 }, /* R252 - General test 1 */
+
+ { 256, 0x0000 }, /* R256 - DF1 */
+ { 257, 0x0000 }, /* R257 - DF2 */
+ { 258, 0x0000 }, /* R258 - DF3 */
+ { 259, 0x0000 }, /* R259 - DF4 */
+ { 260, 0x0000 }, /* R260 - DF5 */
+ { 261, 0x0000 }, /* R261 - DF6 */
+ { 262, 0x0000 }, /* R262 - DF7 */
+
+ { 264, 0x0000 }, /* R264 - LHPF1 */
+ { 265, 0x0000 }, /* R265 - LHPF2 */
+
+ { 268, 0x0000 }, /* R268 - THREED1 */
+ { 269, 0x0000 }, /* R269 - THREED2 */
+ { 270, 0x0000 }, /* R270 - THREED3 */
+ { 271, 0x0000 }, /* R271 - THREED4 */
+
+ { 276, 0x000C }, /* R276 - DRC 1 */
+ { 277, 0x0925 }, /* R277 - DRC 2 */
+ { 278, 0x0000 }, /* R278 - DRC 3 */
+ { 279, 0x0000 }, /* R279 - DRC 4 */
+ { 280, 0x0000 }, /* R280 - DRC 5 */
+
+ { 285, 0x0000 }, /* R285 - Tloopback */
+
+ { 335, 0x0004 }, /* R335 - EQ1 */
+ { 336, 0x6318 }, /* R336 - EQ2 */
+ { 337, 0x6300 }, /* R337 - EQ3 */
+ { 338, 0x0FCA }, /* R338 - EQ4 */
+ { 339, 0x0400 }, /* R339 - EQ5 */
+ { 340, 0x00D8 }, /* R340 - EQ6 */
+ { 341, 0x1EB5 }, /* R341 - EQ7 */
+ { 342, 0xF145 }, /* R342 - EQ8 */
+ { 343, 0x0B75 }, /* R343 - EQ9 */
+ { 344, 0x01C5 }, /* R344 - EQ10 */
+ { 345, 0x1C58 }, /* R345 - EQ11 */
+ { 346, 0xF373 }, /* R346 - EQ12 */
+ { 347, 0x0A54 }, /* R347 - EQ13 */
+ { 348, 0x0558 }, /* R348 - EQ14 */
+ { 349, 0x168E }, /* R349 - EQ15 */
+ { 350, 0xF829 }, /* R350 - EQ16 */
+ { 351, 0x07AD }, /* R351 - EQ17 */
+ { 352, 0x1103 }, /* R352 - EQ18 */
+ { 353, 0x0564 }, /* R353 - EQ19 */
+ { 354, 0x0559 }, /* R354 - EQ20 */
+ { 355, 0x4000 }, /* R355 - EQ21 */
+ { 356, 0x6318 }, /* R356 - EQ22 */
+ { 357, 0x6300 }, /* R357 - EQ23 */
+ { 358, 0x0FCA }, /* R358 - EQ24 */
+ { 359, 0x0400 }, /* R359 - EQ25 */
+ { 360, 0x00D8 }, /* R360 - EQ26 */
+ { 361, 0x1EB5 }, /* R361 - EQ27 */
+ { 362, 0xF145 }, /* R362 - EQ28 */
+ { 363, 0x0B75 }, /* R363 - EQ29 */
+ { 364, 0x01C5 }, /* R364 - EQ30 */
+ { 365, 0x1C58 }, /* R365 - EQ31 */
+ { 366, 0xF373 }, /* R366 - EQ32 */
+ { 367, 0x0A54 }, /* R367 - EQ33 */
+ { 368, 0x0558 }, /* R368 - EQ34 */
+ { 369, 0x168E }, /* R369 - EQ35 */
+ { 370, 0xF829 }, /* R370 - EQ36 */
+ { 371, 0x07AD }, /* R371 - EQ37 */
+ { 372, 0x1103 }, /* R372 - EQ38 */
+ { 373, 0x0564 }, /* R373 - EQ39 */
+ { 374, 0x0559 }, /* R374 - EQ40 */
+ { 375, 0x4000 }, /* R375 - EQ41 */
+
+ { 513, 0x0000 }, /* R513 - GPIO 2 */
+ { 514, 0x0000 }, /* R514 - GPIO 3 */
+
+ { 516, 0x8100 }, /* R516 - GPIO 5 */
+ { 517, 0x8100 }, /* R517 - GPIO 6 */
+
+ { 568, 0x0030 }, /* R568 - Interrupt Status 1 Mask */
+ { 569, 0xFFED }, /* R569 - Interrupt Status 2 Mask */
+
+ { 576, 0x0000 }, /* R576 - Interrupt Control */
+
+ { 584, 0x002D }, /* R584 - IRQ Debounce */
+
+ { 586, 0x0000 }, /* R586 - MICINT Source Pol */
+
+ { 768, 0x1C00 }, /* R768 - DSP2 Power Management */
+
+ { 8192, 0x0000 }, /* R8192 - DSP2 Instruction RAM 0 */
+
+ { 9216, 0x0030 }, /* R9216 - DSP2 Address RAM 2 */
+ { 9217, 0x0000 }, /* R9217 - DSP2 Address RAM 1 */
+ { 9218, 0x0000 }, /* R9218 - DSP2 Address RAM 0 */
+
+ { 12288, 0x0000 }, /* R12288 - DSP2 Data1 RAM 1 */
+ { 12289, 0x0000 }, /* R12289 - DSP2 Data1 RAM 0 */
+
+ { 13312, 0x0000 }, /* R13312 - DSP2 Data2 RAM 1 */
+ { 13313, 0x0000 }, /* R13313 - DSP2 Data2 RAM 0 */
+
+ { 14336, 0x0000 }, /* R14336 - DSP2 Data3 RAM 1 */
+ { 14337, 0x0000 }, /* R14337 - DSP2 Data3 RAM 0 */
+
+ { 15360, 0x000A }, /* R15360 - DSP2 Coeff RAM 0 */
+
+ { 16384, 0x0000 }, /* R16384 - RETUNEADC_SHARED_COEFF_1 */
+ { 16385, 0x0000 }, /* R16385 - RETUNEADC_SHARED_COEFF_0 */
+ { 16386, 0x0000 }, /* R16386 - RETUNEDAC_SHARED_COEFF_1 */
+ { 16387, 0x0000 }, /* R16387 - RETUNEDAC_SHARED_COEFF_0 */
+ { 16388, 0x0000 }, /* R16388 - SOUNDSTAGE_ENABLES_1 */
+ { 16389, 0x0000 }, /* R16389 - SOUNDSTAGE_ENABLES_0 */
+
+ { 16896, 0x0002 }, /* R16896 - HDBASS_AI_1 */
+ { 16897, 0xBD12 }, /* R16897 - HDBASS_AI_0 */
+ { 16898, 0x007C }, /* R16898 - HDBASS_AR_1 */
+ { 16899, 0x586C }, /* R16899 - HDBASS_AR_0 */
+ { 16900, 0x0053 }, /* R16900 - HDBASS_B_1 */
+ { 16901, 0x8121 }, /* R16901 - HDBASS_B_0 */
+ { 16902, 0x003F }, /* R16902 - HDBASS_K_1 */
+ { 16903, 0x8BD8 }, /* R16903 - HDBASS_K_0 */
+ { 16904, 0x0032 }, /* R16904 - HDBASS_N1_1 */
+ { 16905, 0xF52D }, /* R16905 - HDBASS_N1_0 */
+ { 16906, 0x0065 }, /* R16906 - HDBASS_N2_1 */
+ { 16907, 0xAC8C }, /* R16907 - HDBASS_N2_0 */
+ { 16908, 0x006B }, /* R16908 - HDBASS_N3_1 */
+ { 16909, 0xE087 }, /* R16909 - HDBASS_N3_0 */
+ { 16910, 0x0072 }, /* R16910 - HDBASS_N4_1 */
+ { 16911, 0x1483 }, /* R16911 - HDBASS_N4_0 */
+ { 16912, 0x0072 }, /* R16912 - HDBASS_N5_1 */
+ { 16913, 0x1483 }, /* R16913 - HDBASS_N5_0 */
+ { 16914, 0x0043 }, /* R16914 - HDBASS_X1_1 */
+ { 16915, 0x3525 }, /* R16915 - HDBASS_X1_0 */
+ { 16916, 0x0006 }, /* R16916 - HDBASS_X2_1 */
+ { 16917, 0x6A4A }, /* R16917 - HDBASS_X2_0 */
+ { 16918, 0x0043 }, /* R16918 - HDBASS_X3_1 */
+ { 16919, 0x6079 }, /* R16919 - HDBASS_X3_0 */
+ { 16920, 0x0008 }, /* R16920 - HDBASS_ATK_1 */
+ { 16921, 0x0000 }, /* R16921 - HDBASS_ATK_0 */
+ { 16922, 0x0001 }, /* R16922 - HDBASS_DCY_1 */
+ { 16923, 0x0000 }, /* R16923 - HDBASS_DCY_0 */
+ { 16924, 0x0059 }, /* R16924 - HDBASS_PG_1 */
+ { 16925, 0x999A }, /* R16925 - HDBASS_PG_0 */
+
+ { 17048, 0x0083 }, /* R17408 - HPF_C_1 */
+ { 17049, 0x98AD }, /* R17409 - HPF_C_0 */
+
+ { 17920, 0x007F }, /* R17920 - ADCL_RETUNE_C1_1 */
+ { 17921, 0xFFFF }, /* R17921 - ADCL_RETUNE_C1_0 */
+ { 17922, 0x0000 }, /* R17922 - ADCL_RETUNE_C2_1 */
+ { 17923, 0x0000 }, /* R17923 - ADCL_RETUNE_C2_0 */
+ { 17924, 0x0000 }, /* R17924 - ADCL_RETUNE_C3_1 */
+ { 17925, 0x0000 }, /* R17925 - ADCL_RETUNE_C3_0 */
+ { 17926, 0x0000 }, /* R17926 - ADCL_RETUNE_C4_1 */
+ { 17927, 0x0000 }, /* R17927 - ADCL_RETUNE_C4_0 */
+ { 17928, 0x0000 }, /* R17928 - ADCL_RETUNE_C5_1 */
+ { 17929, 0x0000 }, /* R17929 - ADCL_RETUNE_C5_0 */
+ { 17930, 0x0000 }, /* R17930 - ADCL_RETUNE_C6_1 */
+ { 17931, 0x0000 }, /* R17931 - ADCL_RETUNE_C6_0 */
+ { 17932, 0x0000 }, /* R17932 - ADCL_RETUNE_C7_1 */
+ { 17933, 0x0000 }, /* R17933 - ADCL_RETUNE_C7_0 */
+ { 17934, 0x0000 }, /* R17934 - ADCL_RETUNE_C8_1 */
+ { 17935, 0x0000 }, /* R17935 - ADCL_RETUNE_C8_0 */
+ { 17936, 0x0000 }, /* R17936 - ADCL_RETUNE_C9_1 */
+ { 17937, 0x0000 }, /* R17937 - ADCL_RETUNE_C9_0 */
+ { 17938, 0x0000 }, /* R17938 - ADCL_RETUNE_C10_1 */
+ { 17939, 0x0000 }, /* R17939 - ADCL_RETUNE_C10_0 */
+ { 17940, 0x0000 }, /* R17940 - ADCL_RETUNE_C11_1 */
+ { 17941, 0x0000 }, /* R17941 - ADCL_RETUNE_C11_0 */
+ { 17942, 0x0000 }, /* R17942 - ADCL_RETUNE_C12_1 */
+ { 17943, 0x0000 }, /* R17943 - ADCL_RETUNE_C12_0 */
+ { 17944, 0x0000 }, /* R17944 - ADCL_RETUNE_C13_1 */
+ { 17945, 0x0000 }, /* R17945 - ADCL_RETUNE_C13_0 */
+ { 17946, 0x0000 }, /* R17946 - ADCL_RETUNE_C14_1 */
+ { 17947, 0x0000 }, /* R17947 - ADCL_RETUNE_C14_0 */
+ { 17948, 0x0000 }, /* R17948 - ADCL_RETUNE_C15_1 */
+ { 17949, 0x0000 }, /* R17949 - ADCL_RETUNE_C15_0 */
+ { 17950, 0x0000 }, /* R17950 - ADCL_RETUNE_C16_1 */
+ { 17951, 0x0000 }, /* R17951 - ADCL_RETUNE_C16_0 */
+ { 17952, 0x0000 }, /* R17952 - ADCL_RETUNE_C17_1 */
+ { 17953, 0x0000 }, /* R17953 - ADCL_RETUNE_C17_0 */
+ { 17954, 0x0000 }, /* R17954 - ADCL_RETUNE_C18_1 */
+ { 17955, 0x0000 }, /* R17955 - ADCL_RETUNE_C18_0 */
+ { 17956, 0x0000 }, /* R17956 - ADCL_RETUNE_C19_1 */
+ { 17957, 0x0000 }, /* R17957 - ADCL_RETUNE_C19_0 */
+ { 17958, 0x0000 }, /* R17958 - ADCL_RETUNE_C20_1 */
+ { 17959, 0x0000 }, /* R17959 - ADCL_RETUNE_C20_0 */
+ { 17960, 0x0000 }, /* R17960 - ADCL_RETUNE_C21_1 */
+ { 17961, 0x0000 }, /* R17961 - ADCL_RETUNE_C21_0 */
+ { 17962, 0x0000 }, /* R17962 - ADCL_RETUNE_C22_1 */
+ { 17963, 0x0000 }, /* R17963 - ADCL_RETUNE_C22_0 */
+ { 17964, 0x0000 }, /* R17964 - ADCL_RETUNE_C23_1 */
+ { 17965, 0x0000 }, /* R17965 - ADCL_RETUNE_C23_0 */
+ { 17966, 0x0000 }, /* R17966 - ADCL_RETUNE_C24_1 */
+ { 17967, 0x0000 }, /* R17967 - ADCL_RETUNE_C24_0 */
+ { 17968, 0x0000 }, /* R17968 - ADCL_RETUNE_C25_1 */
+ { 17969, 0x0000 }, /* R17969 - ADCL_RETUNE_C25_0 */
+ { 17970, 0x0000 }, /* R17970 - ADCL_RETUNE_C26_1 */
+ { 17971, 0x0000 }, /* R17971 - ADCL_RETUNE_C26_0 */
+ { 17972, 0x0000 }, /* R17972 - ADCL_RETUNE_C27_1 */
+ { 17973, 0x0000 }, /* R17973 - ADCL_RETUNE_C27_0 */
+ { 17974, 0x0000 }, /* R17974 - ADCL_RETUNE_C28_1 */
+ { 17975, 0x0000 }, /* R17975 - ADCL_RETUNE_C28_0 */
+ { 17976, 0x0000 }, /* R17976 - ADCL_RETUNE_C29_1 */
+ { 17977, 0x0000 }, /* R17977 - ADCL_RETUNE_C29_0 */
+ { 17978, 0x0000 }, /* R17978 - ADCL_RETUNE_C30_1 */
+ { 17979, 0x0000 }, /* R17979 - ADCL_RETUNE_C30_0 */
+ { 17980, 0x0000 }, /* R17980 - ADCL_RETUNE_C31_1 */
+ { 17981, 0x0000 }, /* R17981 - ADCL_RETUNE_C31_0 */
+ { 17982, 0x0000 }, /* R17982 - ADCL_RETUNE_C32_1 */
+ { 17983, 0x0000 }, /* R17983 - ADCL_RETUNE_C32_0 */
+
+ { 18432, 0x0020 }, /* R18432 - RETUNEADC_PG2_1 */
+ { 18433, 0x0000 }, /* R18433 - RETUNEADC_PG2_0 */
+ { 18434, 0x0040 }, /* R18434 - RETUNEADC_PG_1 */
+ { 18435, 0x0000 }, /* R18435 - RETUNEADC_PG_0 */
+
+ { 18944, 0x007F }, /* R18944 - ADCR_RETUNE_C1_1 */
+ { 18945, 0xFFFF }, /* R18945 - ADCR_RETUNE_C1_0 */
+ { 18946, 0x0000 }, /* R18946 - ADCR_RETUNE_C2_1 */
+ { 18947, 0x0000 }, /* R18947 - ADCR_RETUNE_C2_0 */
+ { 18948, 0x0000 }, /* R18948 - ADCR_RETUNE_C3_1 */
+ { 18949, 0x0000 }, /* R18949 - ADCR_RETUNE_C3_0 */
+ { 18950, 0x0000 }, /* R18950 - ADCR_RETUNE_C4_1 */
+ { 18951, 0x0000 }, /* R18951 - ADCR_RETUNE_C4_0 */
+ { 18952, 0x0000 }, /* R18952 - ADCR_RETUNE_C5_1 */
+ { 18953, 0x0000 }, /* R18953 - ADCR_RETUNE_C5_0 */
+ { 18954, 0x0000 }, /* R18954 - ADCR_RETUNE_C6_1 */
+ { 18955, 0x0000 }, /* R18955 - ADCR_RETUNE_C6_0 */
+ { 18956, 0x0000 }, /* R18956 - ADCR_RETUNE_C7_1 */
+ { 18957, 0x0000 }, /* R18957 - ADCR_RETUNE_C7_0 */
+ { 18958, 0x0000 }, /* R18958 - ADCR_RETUNE_C8_1 */
+ { 18959, 0x0000 }, /* R18959 - ADCR_RETUNE_C8_0 */
+ { 18960, 0x0000 }, /* R18960 - ADCR_RETUNE_C9_1 */
+ { 18961, 0x0000 }, /* R18961 - ADCR_RETUNE_C9_0 */
+ { 18962, 0x0000 }, /* R18962 - ADCR_RETUNE_C10_1 */
+ { 18963, 0x0000 }, /* R18963 - ADCR_RETUNE_C10_0 */
+ { 18964, 0x0000 }, /* R18964 - ADCR_RETUNE_C11_1 */
+ { 18965, 0x0000 }, /* R18965 - ADCR_RETUNE_C11_0 */
+ { 18966, 0x0000 }, /* R18966 - ADCR_RETUNE_C12_1 */
+ { 18967, 0x0000 }, /* R18967 - ADCR_RETUNE_C12_0 */
+ { 18968, 0x0000 }, /* R18968 - ADCR_RETUNE_C13_1 */
+ { 18969, 0x0000 }, /* R18969 - ADCR_RETUNE_C13_0 */
+ { 18970, 0x0000 }, /* R18970 - ADCR_RETUNE_C14_1 */
+ { 18971, 0x0000 }, /* R18971 - ADCR_RETUNE_C14_0 */
+ { 18972, 0x0000 }, /* R18972 - ADCR_RETUNE_C15_1 */
+ { 18973, 0x0000 }, /* R18973 - ADCR_RETUNE_C15_0 */
+ { 18974, 0x0000 }, /* R18974 - ADCR_RETUNE_C16_1 */
+ { 18975, 0x0000 }, /* R18975 - ADCR_RETUNE_C16_0 */
+ { 18976, 0x0000 }, /* R18976 - ADCR_RETUNE_C17_1 */
+ { 18977, 0x0000 }, /* R18977 - ADCR_RETUNE_C17_0 */
+ { 18978, 0x0000 }, /* R18978 - ADCR_RETUNE_C18_1 */
+ { 18979, 0x0000 }, /* R18979 - ADCR_RETUNE_C18_0 */
+ { 18980, 0x0000 }, /* R18980 - ADCR_RETUNE_C19_1 */
+ { 18981, 0x0000 }, /* R18981 - ADCR_RETUNE_C19_0 */
+ { 18982, 0x0000 }, /* R18982 - ADCR_RETUNE_C20_1 */
+ { 18983, 0x0000 }, /* R18983 - ADCR_RETUNE_C20_0 */
+ { 18984, 0x0000 }, /* R18984 - ADCR_RETUNE_C21_1 */
+ { 18985, 0x0000 }, /* R18985 - ADCR_RETUNE_C21_0 */
+ { 18986, 0x0000 }, /* R18986 - ADCR_RETUNE_C22_1 */
+ { 18987, 0x0000 }, /* R18987 - ADCR_RETUNE_C22_0 */
+ { 18988, 0x0000 }, /* R18988 - ADCR_RETUNE_C23_1 */
+ { 18989, 0x0000 }, /* R18989 - ADCR_RETUNE_C23_0 */
+ { 18990, 0x0000 }, /* R18990 - ADCR_RETUNE_C24_1 */
+ { 18991, 0x0000 }, /* R18991 - ADCR_RETUNE_C24_0 */
+ { 18992, 0x0000 }, /* R18992 - ADCR_RETUNE_C25_1 */
+ { 18993, 0x0000 }, /* R18993 - ADCR_RETUNE_C25_0 */
+ { 18994, 0x0000 }, /* R18994 - ADCR_RETUNE_C26_1 */
+ { 18995, 0x0000 }, /* R18995 - ADCR_RETUNE_C26_0 */
+ { 18996, 0x0000 }, /* R18996 - ADCR_RETUNE_C27_1 */
+ { 18997, 0x0000 }, /* R18997 - ADCR_RETUNE_C27_0 */
+ { 18998, 0x0000 }, /* R18998 - ADCR_RETUNE_C28_1 */
+ { 18999, 0x0000 }, /* R18999 - ADCR_RETUNE_C28_0 */
+ { 19000, 0x0000 }, /* R19000 - ADCR_RETUNE_C29_1 */
+ { 19001, 0x0000 }, /* R19001 - ADCR_RETUNE_C29_0 */
+ { 19002, 0x0000 }, /* R19002 - ADCR_RETUNE_C30_1 */
+ { 19003, 0x0000 }, /* R19003 - ADCR_RETUNE_C30_0 */
+ { 19004, 0x0000 }, /* R19004 - ADCR_RETUNE_C31_1 */
+ { 19005, 0x0000 }, /* R19005 - ADCR_RETUNE_C31_0 */
+ { 19006, 0x0000 }, /* R19006 - ADCR_RETUNE_C32_1 */
+ { 19007, 0x0000 }, /* R19007 - ADCR_RETUNE_C32_0 */
+
+ { 19456, 0x007F }, /* R19456 - DACL_RETUNE_C1_1 */
+ { 19457, 0xFFFF }, /* R19457 - DACL_RETUNE_C1_0 */
+ { 19458, 0x0000 }, /* R19458 - DACL_RETUNE_C2_1 */
+ { 19459, 0x0000 }, /* R19459 - DACL_RETUNE_C2_0 */
+ { 19460, 0x0000 }, /* R19460 - DACL_RETUNE_C3_1 */
+ { 19461, 0x0000 }, /* R19461 - DACL_RETUNE_C3_0 */
+ { 19462, 0x0000 }, /* R19462 - DACL_RETUNE_C4_1 */
+ { 19463, 0x0000 }, /* R19463 - DACL_RETUNE_C4_0 */
+ { 19464, 0x0000 }, /* R19464 - DACL_RETUNE_C5_1 */
+ { 19465, 0x0000 }, /* R19465 - DACL_RETUNE_C5_0 */
+ { 19466, 0x0000 }, /* R19466 - DACL_RETUNE_C6_1 */
+ { 19467, 0x0000 }, /* R19467 - DACL_RETUNE_C6_0 */
+ { 19468, 0x0000 }, /* R19468 - DACL_RETUNE_C7_1 */
+ { 19469, 0x0000 }, /* R19469 - DACL_RETUNE_C7_0 */
+ { 19470, 0x0000 }, /* R19470 - DACL_RETUNE_C8_1 */
+ { 19471, 0x0000 }, /* R19471 - DACL_RETUNE_C8_0 */
+ { 19472, 0x0000 }, /* R19472 - DACL_RETUNE_C9_1 */
+ { 19473, 0x0000 }, /* R19473 - DACL_RETUNE_C9_0 */
+ { 19474, 0x0000 }, /* R19474 - DACL_RETUNE_C10_1 */
+ { 19475, 0x0000 }, /* R19475 - DACL_RETUNE_C10_0 */
+ { 19476, 0x0000 }, /* R19476 - DACL_RETUNE_C11_1 */
+ { 19477, 0x0000 }, /* R19477 - DACL_RETUNE_C11_0 */
+ { 19478, 0x0000 }, /* R19478 - DACL_RETUNE_C12_1 */
+ { 19479, 0x0000 }, /* R19479 - DACL_RETUNE_C12_0 */
+ { 19480, 0x0000 }, /* R19480 - DACL_RETUNE_C13_1 */
+ { 19481, 0x0000 }, /* R19481 - DACL_RETUNE_C13_0 */
+ { 19482, 0x0000 }, /* R19482 - DACL_RETUNE_C14_1 */
+ { 19483, 0x0000 }, /* R19483 - DACL_RETUNE_C14_0 */
+ { 19484, 0x0000 }, /* R19484 - DACL_RETUNE_C15_1 */
+ { 19485, 0x0000 }, /* R19485 - DACL_RETUNE_C15_0 */
+ { 19486, 0x0000 }, /* R19486 - DACL_RETUNE_C16_1 */
+ { 19487, 0x0000 }, /* R19487 - DACL_RETUNE_C16_0 */
+ { 19488, 0x0000 }, /* R19488 - DACL_RETUNE_C17_1 */
+ { 19489, 0x0000 }, /* R19489 - DACL_RETUNE_C17_0 */
+ { 19490, 0x0000 }, /* R19490 - DACL_RETUNE_C18_1 */
+ { 19491, 0x0000 }, /* R19491 - DACL_RETUNE_C18_0 */
+ { 19492, 0x0000 }, /* R19492 - DACL_RETUNE_C19_1 */
+ { 19493, 0x0000 }, /* R19493 - DACL_RETUNE_C19_0 */
+ { 19494, 0x0000 }, /* R19494 - DACL_RETUNE_C20_1 */
+ { 19495, 0x0000 }, /* R19495 - DACL_RETUNE_C20_0 */
+ { 19496, 0x0000 }, /* R19496 - DACL_RETUNE_C21_1 */
+ { 19497, 0x0000 }, /* R19497 - DACL_RETUNE_C21_0 */
+ { 19498, 0x0000 }, /* R19498 - DACL_RETUNE_C22_1 */
+ { 19499, 0x0000 }, /* R19499 - DACL_RETUNE_C22_0 */
+ { 19500, 0x0000 }, /* R19500 - DACL_RETUNE_C23_1 */
+ { 19501, 0x0000 }, /* R19501 - DACL_RETUNE_C23_0 */
+ { 19502, 0x0000 }, /* R19502 - DACL_RETUNE_C24_1 */
+ { 19503, 0x0000 }, /* R19503 - DACL_RETUNE_C24_0 */
+ { 19504, 0x0000 }, /* R19504 - DACL_RETUNE_C25_1 */
+ { 19505, 0x0000 }, /* R19505 - DACL_RETUNE_C25_0 */
+ { 19506, 0x0000 }, /* R19506 - DACL_RETUNE_C26_1 */
+ { 19507, 0x0000 }, /* R19507 - DACL_RETUNE_C26_0 */
+ { 19508, 0x0000 }, /* R19508 - DACL_RETUNE_C27_1 */
+ { 19509, 0x0000 }, /* R19509 - DACL_RETUNE_C27_0 */
+ { 19510, 0x0000 }, /* R19510 - DACL_RETUNE_C28_1 */
+ { 19511, 0x0000 }, /* R19511 - DACL_RETUNE_C28_0 */
+ { 19512, 0x0000 }, /* R19512 - DACL_RETUNE_C29_1 */
+ { 19513, 0x0000 }, /* R19513 - DACL_RETUNE_C29_0 */
+ { 19514, 0x0000 }, /* R19514 - DACL_RETUNE_C30_1 */
+ { 19515, 0x0000 }, /* R19515 - DACL_RETUNE_C30_0 */
+ { 19516, 0x0000 }, /* R19516 - DACL_RETUNE_C31_1 */
+ { 19517, 0x0000 }, /* R19517 - DACL_RETUNE_C31_0 */
+ { 19518, 0x0000 }, /* R19518 - DACL_RETUNE_C32_1 */
+ { 19519, 0x0000 }, /* R19519 - DACL_RETUNE_C32_0 */
+
+ { 19968, 0x0020 }, /* R19968 - RETUNEDAC_PG2_1 */
+ { 19969, 0x0000 }, /* R19969 - RETUNEDAC_PG2_0 */
+ { 19970, 0x0040 }, /* R19970 - RETUNEDAC_PG_1 */
+ { 19971, 0x0000 }, /* R19971 - RETUNEDAC_PG_0 */
+
+ { 20480, 0x007F }, /* R20480 - DACR_RETUNE_C1_1 */
+ { 20481, 0xFFFF }, /* R20481 - DACR_RETUNE_C1_0 */
+ { 20482, 0x0000 }, /* R20482 - DACR_RETUNE_C2_1 */
+ { 20483, 0x0000 }, /* R20483 - DACR_RETUNE_C2_0 */
+ { 20484, 0x0000 }, /* R20484 - DACR_RETUNE_C3_1 */
+ { 20485, 0x0000 }, /* R20485 - DACR_RETUNE_C3_0 */
+ { 20486, 0x0000 }, /* R20486 - DACR_RETUNE_C4_1 */
+ { 20487, 0x0000 }, /* R20487 - DACR_RETUNE_C4_0 */
+ { 20488, 0x0000 }, /* R20488 - DACR_RETUNE_C5_1 */
+ { 20489, 0x0000 }, /* R20489 - DACR_RETUNE_C5_0 */
+ { 20490, 0x0000 }, /* R20490 - DACR_RETUNE_C6_1 */
+ { 20491, 0x0000 }, /* R20491 - DACR_RETUNE_C6_0 */
+ { 20492, 0x0000 }, /* R20492 - DACR_RETUNE_C7_1 */
+ { 20493, 0x0000 }, /* R20493 - DACR_RETUNE_C7_0 */
+ { 20494, 0x0000 }, /* R20494 - DACR_RETUNE_C8_1 */
+ { 20495, 0x0000 }, /* R20495 - DACR_RETUNE_C8_0 */
+ { 20496, 0x0000 }, /* R20496 - DACR_RETUNE_C9_1 */
+ { 20497, 0x0000 }, /* R20497 - DACR_RETUNE_C9_0 */
+ { 20498, 0x0000 }, /* R20498 - DACR_RETUNE_C10_1 */
+ { 20499, 0x0000 }, /* R20499 - DACR_RETUNE_C10_0 */
+ { 20500, 0x0000 }, /* R20500 - DACR_RETUNE_C11_1 */
+ { 20501, 0x0000 }, /* R20501 - DACR_RETUNE_C11_0 */
+ { 20502, 0x0000 }, /* R20502 - DACR_RETUNE_C12_1 */
+ { 20503, 0x0000 }, /* R20503 - DACR_RETUNE_C12_0 */
+ { 20504, 0x0000 }, /* R20504 - DACR_RETUNE_C13_1 */
+ { 20505, 0x0000 }, /* R20505 - DACR_RETUNE_C13_0 */
+ { 20506, 0x0000 }, /* R20506 - DACR_RETUNE_C14_1 */
+ { 20507, 0x0000 }, /* R20507 - DACR_RETUNE_C14_0 */
+ { 20508, 0x0000 }, /* R20508 - DACR_RETUNE_C15_1 */
+ { 20509, 0x0000 }, /* R20509 - DACR_RETUNE_C15_0 */
+ { 20510, 0x0000 }, /* R20510 - DACR_RETUNE_C16_1 */
+ { 20511, 0x0000 }, /* R20511 - DACR_RETUNE_C16_0 */
+ { 20512, 0x0000 }, /* R20512 - DACR_RETUNE_C17_1 */
+ { 20513, 0x0000 }, /* R20513 - DACR_RETUNE_C17_0 */
+ { 20514, 0x0000 }, /* R20514 - DACR_RETUNE_C18_1 */
+ { 20515, 0x0000 }, /* R20515 - DACR_RETUNE_C18_0 */
+ { 20516, 0x0000 }, /* R20516 - DACR_RETUNE_C19_1 */
+ { 20517, 0x0000 }, /* R20517 - DACR_RETUNE_C19_0 */
+ { 20518, 0x0000 }, /* R20518 - DACR_RETUNE_C20_1 */
+ { 20519, 0x0000 }, /* R20519 - DACR_RETUNE_C20_0 */
+ { 20520, 0x0000 }, /* R20520 - DACR_RETUNE_C21_1 */
+ { 20521, 0x0000 }, /* R20521 - DACR_RETUNE_C21_0 */
+ { 20522, 0x0000 }, /* R20522 - DACR_RETUNE_C22_1 */
+ { 20523, 0x0000 }, /* R20523 - DACR_RETUNE_C22_0 */
+ { 20524, 0x0000 }, /* R20524 - DACR_RETUNE_C23_1 */
+ { 20525, 0x0000 }, /* R20525 - DACR_RETUNE_C23_0 */
+ { 20526, 0x0000 }, /* R20526 - DACR_RETUNE_C24_1 */
+ { 20527, 0x0000 }, /* R20527 - DACR_RETUNE_C24_0 */
+ { 20528, 0x0000 }, /* R20528 - DACR_RETUNE_C25_1 */
+ { 20529, 0x0000 }, /* R20529 - DACR_RETUNE_C25_0 */
+ { 20530, 0x0000 }, /* R20530 - DACR_RETUNE_C26_1 */
+ { 20531, 0x0000 }, /* R20531 - DACR_RETUNE_C26_0 */
+ { 20532, 0x0000 }, /* R20532 - DACR_RETUNE_C27_1 */
+ { 20533, 0x0000 }, /* R20533 - DACR_RETUNE_C27_0 */
+ { 20534, 0x0000 }, /* R20534 - DACR_RETUNE_C28_1 */
+ { 20535, 0x0000 }, /* R20535 - DACR_RETUNE_C28_0 */
+ { 20536, 0x0000 }, /* R20536 - DACR_RETUNE_C29_1 */
+ { 20537, 0x0000 }, /* R20537 - DACR_RETUNE_C29_0 */
+ { 20538, 0x0000 }, /* R20538 - DACR_RETUNE_C30_1 */
+ { 20539, 0x0000 }, /* R20539 - DACR_RETUNE_C30_0 */
+ { 20540, 0x0000 }, /* R20540 - DACR_RETUNE_C31_1 */
+ { 20541, 0x0000 }, /* R20541 - DACR_RETUNE_C31_0 */
+ { 20542, 0x0000 }, /* R20542 - DACR_RETUNE_C32_1 */
+ { 20543, 0x0000 }, /* R20543 - DACR_RETUNE_C32_0 */
+
+ { 20992, 0x008C }, /* R20992 - VSS_XHD2_1 */
+ { 20993, 0x0200 }, /* R20993 - VSS_XHD2_0 */
+ { 20994, 0x0035 }, /* R20994 - VSS_XHD3_1 */
+ { 20995, 0x0700 }, /* R20995 - VSS_XHD3_0 */
+ { 20996, 0x003A }, /* R20996 - VSS_XHN1_1 */
+ { 20997, 0x4100 }, /* R20997 - VSS_XHN1_0 */
+ { 20998, 0x008B }, /* R20998 - VSS_XHN2_1 */
+ { 20999, 0x7D00 }, /* R20999 - VSS_XHN2_0 */
+ { 21000, 0x003A }, /* R21000 - VSS_XHN3_1 */
+ { 21001, 0x4100 }, /* R21001 - VSS_XHN3_0 */
+ { 21002, 0x008C }, /* R21002 - VSS_XLA_1 */
+ { 21003, 0xFEE8 }, /* R21003 - VSS_XLA_0 */
+ { 21004, 0x0078 }, /* R21004 - VSS_XLB_1 */
+ { 21005, 0x0000 }, /* R21005 - VSS_XLB_0 */
+ { 21006, 0x003F }, /* R21006 - VSS_XLG_1 */
+ { 21007, 0xB260 }, /* R21007 - VSS_XLG_0 */
+ { 21008, 0x002D }, /* R21008 - VSS_PG2_1 */
+ { 21009, 0x1818 }, /* R21009 - VSS_PG2_0 */
+ { 21010, 0x0020 }, /* R21010 - VSS_PG_1 */
+ { 21011, 0x0000 }, /* R21011 - VSS_PG_0 */
+ { 21012, 0x00F1 }, /* R21012 - VSS_XTD1_1 */
+ { 21013, 0x8340 }, /* R21013 - VSS_XTD1_0 */
+ { 21014, 0x00FB }, /* R21014 - VSS_XTD2_1 */
+ { 21015, 0x8300 }, /* R21015 - VSS_XTD2_0 */
+ { 21016, 0x00EE }, /* R21016 - VSS_XTD3_1 */
+ { 21017, 0xAEC0 }, /* R21017 - VSS_XTD3_0 */
+ { 21018, 0x00FB }, /* R21018 - VSS_XTD4_1 */
+ { 21019, 0xAC40 }, /* R21019 - VSS_XTD4_0 */
+ { 21020, 0x00F1 }, /* R21020 - VSS_XTD5_1 */
+ { 21021, 0x7F80 }, /* R21021 - VSS_XTD5_0 */
+ { 21022, 0x00F4 }, /* R21022 - VSS_XTD6_1 */
+ { 21023, 0x3B40 }, /* R21023 - VSS_XTD6_0 */
+ { 21024, 0x00F5 }, /* R21024 - VSS_XTD7_1 */
+ { 21025, 0xFB00 }, /* R21025 - VSS_XTD7_0 */
+ { 21026, 0x00EA }, /* R21026 - VSS_XTD8_1 */
+ { 21027, 0x10C0 }, /* R21027 - VSS_XTD8_0 */
+ { 21028, 0x00FC }, /* R21028 - VSS_XTD9_1 */
+ { 21029, 0xC580 }, /* R21029 - VSS_XTD9_0 */
+ { 21030, 0x00E2 }, /* R21030 - VSS_XTD10_1 */
+ { 21031, 0x75C0 }, /* R21031 - VSS_XTD10_0 */
+ { 21032, 0x0004 }, /* R21032 - VSS_XTD11_1 */
+ { 21033, 0xB480 }, /* R21033 - VSS_XTD11_0 */
+ { 21034, 0x00D4 }, /* R21034 - VSS_XTD12_1 */
+ { 21035, 0xF980 }, /* R21035 - VSS_XTD12_0 */
+ { 21036, 0x0004 }, /* R21036 - VSS_XTD13_1 */
+ { 21037, 0x9140 }, /* R21037 - VSS_XTD13_0 */
+ { 21038, 0x00D8 }, /* R21038 - VSS_XTD14_1 */
+ { 21039, 0xA480 }, /* R21039 - VSS_XTD14_0 */
+ { 21040, 0x0002 }, /* R21040 - VSS_XTD15_1 */
+ { 21041, 0x3DC0 }, /* R21041 - VSS_XTD15_0 */
+ { 21042, 0x00CF }, /* R21042 - VSS_XTD16_1 */
+ { 21043, 0x7A80 }, /* R21043 - VSS_XTD16_0 */
+ { 21044, 0x00DC }, /* R21044 - VSS_XTD17_1 */
+ { 21045, 0x0600 }, /* R21045 - VSS_XTD17_0 */
+ { 21046, 0x00F2 }, /* R21046 - VSS_XTD18_1 */
+ { 21047, 0xDAC0 }, /* R21047 - VSS_XTD18_0 */
+ { 21048, 0x00BA }, /* R21048 - VSS_XTD19_1 */
+ { 21049, 0xF340 }, /* R21049 - VSS_XTD19_0 */
+ { 21050, 0x000A }, /* R21050 - VSS_XTD20_1 */
+ { 21051, 0x7940 }, /* R21051 - VSS_XTD20_0 */
+ { 21052, 0x001C }, /* R21052 - VSS_XTD21_1 */
+ { 21053, 0x0680 }, /* R21053 - VSS_XTD21_0 */
+ { 21054, 0x00FD }, /* R21054 - VSS_XTD22_1 */
+ { 21055, 0x2D00 }, /* R21055 - VSS_XTD22_0 */
+ { 21056, 0x001C }, /* R21056 - VSS_XTD23_1 */
+ { 21057, 0xE840 }, /* R21057 - VSS_XTD23_0 */
+ { 21058, 0x000D }, /* R21058 - VSS_XTD24_1 */
+ { 21059, 0xDC40 }, /* R21059 - VSS_XTD24_0 */
+ { 21060, 0x00FC }, /* R21060 - VSS_XTD25_1 */
+ { 21061, 0x9D00 }, /* R21061 - VSS_XTD25_0 */
+ { 21062, 0x0009 }, /* R21062 - VSS_XTD26_1 */
+ { 21063, 0x5580 }, /* R21063 - VSS_XTD26_0 */
+ { 21064, 0x00FE }, /* R21064 - VSS_XTD27_1 */
+ { 21065, 0x7E80 }, /* R21065 - VSS_XTD27_0 */
+ { 21066, 0x000E }, /* R21066 - VSS_XTD28_1 */
+ { 21067, 0xAB40 }, /* R21067 - VSS_XTD28_0 */
+ { 21068, 0x00F9 }, /* R21068 - VSS_XTD29_1 */
+ { 21069, 0x9880 }, /* R21069 - VSS_XTD29_0 */
+ { 21070, 0x0009 }, /* R21070 - VSS_XTD30_1 */
+ { 21071, 0x87C0 }, /* R21071 - VSS_XTD30_0 */
+ { 21072, 0x00FD }, /* R21072 - VSS_XTD31_1 */
+ { 21073, 0x2C40 }, /* R21073 - VSS_XTD31_0 */
+ { 21074, 0x0009 }, /* R21074 - VSS_XTD32_1 */
+ { 21075, 0x4800 }, /* R21075 - VSS_XTD32_0 */
+ { 21076, 0x0003 }, /* R21076 - VSS_XTS1_1 */
+ { 21077, 0x5F40 }, /* R21077 - VSS_XTS1_0 */
+ { 21078, 0x0000 }, /* R21078 - VSS_XTS2_1 */
+ { 21079, 0x8700 }, /* R21079 - VSS_XTS2_0 */
+ { 21080, 0x00FA }, /* R21080 - VSS_XTS3_1 */
+ { 21081, 0xE4C0 }, /* R21081 - VSS_XTS3_0 */
+ { 21082, 0x0000 }, /* R21082 - VSS_XTS4_1 */
+ { 21083, 0x0B40 }, /* R21083 - VSS_XTS4_0 */
+ { 21084, 0x0004 }, /* R21084 - VSS_XTS5_1 */
+ { 21085, 0xE180 }, /* R21085 - VSS_XTS5_0 */
+ { 21086, 0x0001 }, /* R21086 - VSS_XTS6_1 */
+ { 21087, 0x1F40 }, /* R21087 - VSS_XTS6_0 */
+ { 21088, 0x00F8 }, /* R21088 - VSS_XTS7_1 */
+ { 21089, 0xB000 }, /* R21089 - VSS_XTS7_0 */
+ { 21090, 0x00FB }, /* R21090 - VSS_XTS8_1 */
+ { 21091, 0xCBC0 }, /* R21091 - VSS_XTS8_0 */
+ { 21092, 0x0004 }, /* R21092 - VSS_XTS9_1 */
+ { 21093, 0xF380 }, /* R21093 - VSS_XTS9_0 */
+ { 21094, 0x0007 }, /* R21094 - VSS_XTS10_1 */
+ { 21095, 0xDF40 }, /* R21095 - VSS_XTS10_0 */
+ { 21096, 0x00FF }, /* R21096 - VSS_XTS11_1 */
+ { 21097, 0x0700 }, /* R21097 - VSS_XTS11_0 */
+ { 21098, 0x00EF }, /* R21098 - VSS_XTS12_1 */
+ { 21099, 0xD700 }, /* R21099 - VSS_XTS12_0 */
+ { 21100, 0x00FB }, /* R21100 - VSS_XTS13_1 */
+ { 21101, 0xAF40 }, /* R21101 - VSS_XTS13_0 */
+ { 21102, 0x0010 }, /* R21102 - VSS_XTS14_1 */
+ { 21103, 0x8A80 }, /* R21103 - VSS_XTS14_0 */
+ { 21104, 0x0011 }, /* R21104 - VSS_XTS15_1 */
+ { 21105, 0x07C0 }, /* R21105 - VSS_XTS15_0 */
+ { 21106, 0x00E0 }, /* R21106 - VSS_XTS16_1 */
+ { 21107, 0x0800 }, /* R21107 - VSS_XTS16_0 */
+ { 21108, 0x00D2 }, /* R21108 - VSS_XTS17_1 */
+ { 21109, 0x7600 }, /* R21109 - VSS_XTS17_0 */
+ { 21110, 0x0020 }, /* R21110 - VSS_XTS18_1 */
+ { 21111, 0xCF40 }, /* R21111 - VSS_XTS18_0 */
+ { 21112, 0x0030 }, /* R21112 - VSS_XTS19_1 */
+ { 21113, 0x2340 }, /* R21113 - VSS_XTS19_0 */
+ { 21114, 0x00FD }, /* R21114 - VSS_XTS20_1 */
+ { 21115, 0x69C0 }, /* R21115 - VSS_XTS20_0 */
+ { 21116, 0x0028 }, /* R21116 - VSS_XTS21_1 */
+ { 21117, 0x3500 }, /* R21117 - VSS_XTS21_0 */
+ { 21118, 0x0006 }, /* R21118 - VSS_XTS22_1 */
+ { 21119, 0x3300 }, /* R21119 - VSS_XTS22_0 */
+ { 21120, 0x00D9 }, /* R21120 - VSS_XTS23_1 */
+ { 21121, 0xF6C0 }, /* R21121 - VSS_XTS23_0 */
+ { 21122, 0x00F3 }, /* R21122 - VSS_XTS24_1 */
+ { 21123, 0x3340 }, /* R21123 - VSS_XTS24_0 */
+ { 21124, 0x000F }, /* R21124 - VSS_XTS25_1 */
+ { 21125, 0x4200 }, /* R21125 - VSS_XTS25_0 */
+ { 21126, 0x0004 }, /* R21126 - VSS_XTS26_1 */
+ { 21127, 0x0C80 }, /* R21127 - VSS_XTS26_0 */
+ { 21128, 0x00FB }, /* R21128 - VSS_XTS27_1 */
+ { 21129, 0x3F80 }, /* R21129 - VSS_XTS27_0 */
+ { 21130, 0x00F7 }, /* R21130 - VSS_XTS28_1 */
+ { 21131, 0x57C0 }, /* R21131 - VSS_XTS28_0 */
+ { 21132, 0x0003 }, /* R21132 - VSS_XTS29_1 */
+ { 21133, 0x5400 }, /* R21133 - VSS_XTS29_0 */
+ { 21134, 0x0000 }, /* R21134 - VSS_XTS30_1 */
+ { 21135, 0xC6C0 }, /* R21135 - VSS_XTS30_0 */
+ { 21136, 0x0003 }, /* R21136 - VSS_XTS31_1 */
+ { 21137, 0x12C0 }, /* R21137 - VSS_XTS31_0 */
+ { 21138, 0x00FD }, /* R21138 - VSS_XTS32_1 */
+ { 21139, 0x8580 }, /* R21139 - VSS_XTS32_0 */
+};
+
+static bool wm8962_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8962_CLOCKING1:
+ case WM8962_CLOCKING2:
+ case WM8962_SOFTWARE_RESET:
+ case WM8962_ALC2:
+ case WM8962_THERMAL_SHUTDOWN_STATUS:
+ case WM8962_ADDITIONAL_CONTROL_4:
+ case WM8962_DC_SERVO_6:
+ case WM8962_INTERRUPT_STATUS_1:
+ case WM8962_INTERRUPT_STATUS_2:
+ case WM8962_DSP2_EXECCONTROL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wm8962_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8962_LEFT_INPUT_VOLUME:
+ case WM8962_RIGHT_INPUT_VOLUME:
+ case WM8962_HPOUTL_VOLUME:
+ case WM8962_HPOUTR_VOLUME:
+ case WM8962_CLOCKING1:
+ case WM8962_ADC_DAC_CONTROL_1:
+ case WM8962_ADC_DAC_CONTROL_2:
+ case WM8962_AUDIO_INTERFACE_0:
+ case WM8962_CLOCKING2:
+ case WM8962_AUDIO_INTERFACE_1:
+ case WM8962_LEFT_DAC_VOLUME:
+ case WM8962_RIGHT_DAC_VOLUME:
+ case WM8962_AUDIO_INTERFACE_2:
+ case WM8962_SOFTWARE_RESET:
+ case WM8962_ALC1:
+ case WM8962_ALC2:
+ case WM8962_ALC3:
+ case WM8962_NOISE_GATE:
+ case WM8962_LEFT_ADC_VOLUME:
+ case WM8962_RIGHT_ADC_VOLUME:
+ case WM8962_ADDITIONAL_CONTROL_1:
+ case WM8962_ADDITIONAL_CONTROL_2:
+ case WM8962_PWR_MGMT_1:
+ case WM8962_PWR_MGMT_2:
+ case WM8962_ADDITIONAL_CONTROL_3:
+ case WM8962_ANTI_POP:
+ case WM8962_CLOCKING_3:
+ case WM8962_INPUT_MIXER_CONTROL_1:
+ case WM8962_LEFT_INPUT_MIXER_VOLUME:
+ case WM8962_RIGHT_INPUT_MIXER_VOLUME:
+ case WM8962_INPUT_MIXER_CONTROL_2:
+ case WM8962_INPUT_BIAS_CONTROL:
+ case WM8962_LEFT_INPUT_PGA_CONTROL:
+ case WM8962_RIGHT_INPUT_PGA_CONTROL:
+ case WM8962_SPKOUTL_VOLUME:
+ case WM8962_SPKOUTR_VOLUME:
+ case WM8962_THERMAL_SHUTDOWN_STATUS:
+ case WM8962_ADDITIONAL_CONTROL_4:
+ case WM8962_CLASS_D_CONTROL_1:
+ case WM8962_CLASS_D_CONTROL_2:
+ case WM8962_CLOCKING_4:
+ case WM8962_DAC_DSP_MIXING_1:
+ case WM8962_DAC_DSP_MIXING_2:
+ case WM8962_DC_SERVO_0:
+ case WM8962_DC_SERVO_1:
+ case WM8962_DC_SERVO_4:
+ case WM8962_DC_SERVO_6:
+ case WM8962_ANALOGUE_PGA_BIAS:
+ case WM8962_ANALOGUE_HP_0:
+ case WM8962_ANALOGUE_HP_2:
+ case WM8962_CHARGE_PUMP_1:
+ case WM8962_CHARGE_PUMP_B:
+ case WM8962_WRITE_SEQUENCER_CONTROL_1:
+ case WM8962_WRITE_SEQUENCER_CONTROL_2:
+ case WM8962_WRITE_SEQUENCER_CONTROL_3:
+ case WM8962_CONTROL_INTERFACE:
+ case WM8962_MIXER_ENABLES:
+ case WM8962_HEADPHONE_MIXER_1:
+ case WM8962_HEADPHONE_MIXER_2:
+ case WM8962_HEADPHONE_MIXER_3:
+ case WM8962_HEADPHONE_MIXER_4:
+ case WM8962_SPEAKER_MIXER_1:
+ case WM8962_SPEAKER_MIXER_2:
+ case WM8962_SPEAKER_MIXER_3:
+ case WM8962_SPEAKER_MIXER_4:
+ case WM8962_SPEAKER_MIXER_5:
+ case WM8962_BEEP_GENERATOR_1:
+ case WM8962_OSCILLATOR_TRIM_3:
+ case WM8962_OSCILLATOR_TRIM_4:
+ case WM8962_OSCILLATOR_TRIM_7:
+ case WM8962_ANALOGUE_CLOCKING1:
+ case WM8962_ANALOGUE_CLOCKING2:
+ case WM8962_ANALOGUE_CLOCKING3:
+ case WM8962_PLL_SOFTWARE_RESET:
+ case WM8962_PLL2:
+ case WM8962_PLL_4:
+ case WM8962_PLL_9:
+ case WM8962_PLL_10:
+ case WM8962_PLL_11:
+ case WM8962_PLL_12:
+ case WM8962_PLL_13:
+ case WM8962_PLL_14:
+ case WM8962_PLL_15:
+ case WM8962_PLL_16:
+ case WM8962_FLL_CONTROL_1:
+ case WM8962_FLL_CONTROL_2:
+ case WM8962_FLL_CONTROL_3:
+ case WM8962_FLL_CONTROL_5:
+ case WM8962_FLL_CONTROL_6:
+ case WM8962_FLL_CONTROL_7:
+ case WM8962_FLL_CONTROL_8:
+ case WM8962_GENERAL_TEST_1:
+ case WM8962_DF1:
+ case WM8962_DF2:
+ case WM8962_DF3:
+ case WM8962_DF4:
+ case WM8962_DF5:
+ case WM8962_DF6:
+ case WM8962_DF7:
+ case WM8962_LHPF1:
+ case WM8962_LHPF2:
+ case WM8962_THREED1:
+ case WM8962_THREED2:
+ case WM8962_THREED3:
+ case WM8962_THREED4:
+ case WM8962_DRC_1:
+ case WM8962_DRC_2:
+ case WM8962_DRC_3:
+ case WM8962_DRC_4:
+ case WM8962_DRC_5:
+ case WM8962_TLOOPBACK:
+ case WM8962_EQ1:
+ case WM8962_EQ2:
+ case WM8962_EQ3:
+ case WM8962_EQ4:
+ case WM8962_EQ5:
+ case WM8962_EQ6:
+ case WM8962_EQ7:
+ case WM8962_EQ8:
+ case WM8962_EQ9:
+ case WM8962_EQ10:
+ case WM8962_EQ11:
+ case WM8962_EQ12:
+ case WM8962_EQ13:
+ case WM8962_EQ14:
+ case WM8962_EQ15:
+ case WM8962_EQ16:
+ case WM8962_EQ17:
+ case WM8962_EQ18:
+ case WM8962_EQ19:
+ case WM8962_EQ20:
+ case WM8962_EQ21:
+ case WM8962_EQ22:
+ case WM8962_EQ23:
+ case WM8962_EQ24:
+ case WM8962_EQ25:
+ case WM8962_EQ26:
+ case WM8962_EQ27:
+ case WM8962_EQ28:
+ case WM8962_EQ29:
+ case WM8962_EQ30:
+ case WM8962_EQ31:
+ case WM8962_EQ32:
+ case WM8962_EQ33:
+ case WM8962_EQ34:
+ case WM8962_EQ35:
+ case WM8962_EQ36:
+ case WM8962_EQ37:
+ case WM8962_EQ38:
+ case WM8962_EQ39:
+ case WM8962_EQ40:
+ case WM8962_EQ41:
+ case WM8962_GPIO_BASE:
+ case WM8962_GPIO_2:
+ case WM8962_GPIO_3:
+ case WM8962_GPIO_5:
+ case WM8962_GPIO_6:
+ case WM8962_INTERRUPT_STATUS_1:
+ case WM8962_INTERRUPT_STATUS_2:
+ case WM8962_INTERRUPT_STATUS_1_MASK:
+ case WM8962_INTERRUPT_STATUS_2_MASK:
+ case WM8962_INTERRUPT_CONTROL:
+ case WM8962_IRQ_DEBOUNCE:
+ case WM8962_MICINT_SOURCE_POL:
+ case WM8962_DSP2_POWER_MANAGEMENT:
+ case WM8962_DSP2_EXECCONTROL:
+ case WM8962_DSP2_INSTRUCTION_RAM_0:
+ case WM8962_DSP2_ADDRESS_RAM_2:
+ case WM8962_DSP2_ADDRESS_RAM_1:
+ case WM8962_DSP2_ADDRESS_RAM_0:
+ case WM8962_DSP2_DATA1_RAM_1:
+ case WM8962_DSP2_DATA1_RAM_0:
+ case WM8962_DSP2_DATA2_RAM_1:
+ case WM8962_DSP2_DATA2_RAM_0:
+ case WM8962_DSP2_DATA3_RAM_1:
+ case WM8962_DSP2_DATA3_RAM_0:
+ case WM8962_DSP2_COEFF_RAM_0:
+ case WM8962_RETUNEADC_SHARED_COEFF_1:
+ case WM8962_RETUNEADC_SHARED_COEFF_0:
+ case WM8962_RETUNEDAC_SHARED_COEFF_1:
+ case WM8962_RETUNEDAC_SHARED_COEFF_0:
+ case WM8962_SOUNDSTAGE_ENABLES_1:
+ case WM8962_SOUNDSTAGE_ENABLES_0:
+ case WM8962_HDBASS_AI_1:
+ case WM8962_HDBASS_AI_0:
+ case WM8962_HDBASS_AR_1:
+ case WM8962_HDBASS_AR_0:
+ case WM8962_HDBASS_B_1:
+ case WM8962_HDBASS_B_0:
+ case WM8962_HDBASS_K_1:
+ case WM8962_HDBASS_K_0:
+ case WM8962_HDBASS_N1_1:
+ case WM8962_HDBASS_N1_0:
+ case WM8962_HDBASS_N2_1:
+ case WM8962_HDBASS_N2_0:
+ case WM8962_HDBASS_N3_1:
+ case WM8962_HDBASS_N3_0:
+ case WM8962_HDBASS_N4_1:
+ case WM8962_HDBASS_N4_0:
+ case WM8962_HDBASS_N5_1:
+ case WM8962_HDBASS_N5_0:
+ case WM8962_HDBASS_X1_1:
+ case WM8962_HDBASS_X1_0:
+ case WM8962_HDBASS_X2_1:
+ case WM8962_HDBASS_X2_0:
+ case WM8962_HDBASS_X3_1:
+ case WM8962_HDBASS_X3_0:
+ case WM8962_HDBASS_ATK_1:
+ case WM8962_HDBASS_ATK_0:
+ case WM8962_HDBASS_DCY_1:
+ case WM8962_HDBASS_DCY_0:
+ case WM8962_HDBASS_PG_1:
+ case WM8962_HDBASS_PG_0:
+ case WM8962_HPF_C_1:
+ case WM8962_HPF_C_0:
+ case WM8962_ADCL_RETUNE_C1_1:
+ case WM8962_ADCL_RETUNE_C1_0:
+ case WM8962_ADCL_RETUNE_C2_1:
+ case WM8962_ADCL_RETUNE_C2_0:
+ case WM8962_ADCL_RETUNE_C3_1:
+ case WM8962_ADCL_RETUNE_C3_0:
+ case WM8962_ADCL_RETUNE_C4_1:
+ case WM8962_ADCL_RETUNE_C4_0:
+ case WM8962_ADCL_RETUNE_C5_1:
+ case WM8962_ADCL_RETUNE_C5_0:
+ case WM8962_ADCL_RETUNE_C6_1:
+ case WM8962_ADCL_RETUNE_C6_0:
+ case WM8962_ADCL_RETUNE_C7_1:
+ case WM8962_ADCL_RETUNE_C7_0:
+ case WM8962_ADCL_RETUNE_C8_1:
+ case WM8962_ADCL_RETUNE_C8_0:
+ case WM8962_ADCL_RETUNE_C9_1:
+ case WM8962_ADCL_RETUNE_C9_0:
+ case WM8962_ADCL_RETUNE_C10_1:
+ case WM8962_ADCL_RETUNE_C10_0:
+ case WM8962_ADCL_RETUNE_C11_1:
+ case WM8962_ADCL_RETUNE_C11_0:
+ case WM8962_ADCL_RETUNE_C12_1:
+ case WM8962_ADCL_RETUNE_C12_0:
+ case WM8962_ADCL_RETUNE_C13_1:
+ case WM8962_ADCL_RETUNE_C13_0:
+ case WM8962_ADCL_RETUNE_C14_1:
+ case WM8962_ADCL_RETUNE_C14_0:
+ case WM8962_ADCL_RETUNE_C15_1:
+ case WM8962_ADCL_RETUNE_C15_0:
+ case WM8962_ADCL_RETUNE_C16_1:
+ case WM8962_ADCL_RETUNE_C16_0:
+ case WM8962_ADCL_RETUNE_C17_1:
+ case WM8962_ADCL_RETUNE_C17_0:
+ case WM8962_ADCL_RETUNE_C18_1:
+ case WM8962_ADCL_RETUNE_C18_0:
+ case WM8962_ADCL_RETUNE_C19_1:
+ case WM8962_ADCL_RETUNE_C19_0:
+ case WM8962_ADCL_RETUNE_C20_1:
+ case WM8962_ADCL_RETUNE_C20_0:
+ case WM8962_ADCL_RETUNE_C21_1:
+ case WM8962_ADCL_RETUNE_C21_0:
+ case WM8962_ADCL_RETUNE_C22_1:
+ case WM8962_ADCL_RETUNE_C22_0:
+ case WM8962_ADCL_RETUNE_C23_1:
+ case WM8962_ADCL_RETUNE_C23_0:
+ case WM8962_ADCL_RETUNE_C24_1:
+ case WM8962_ADCL_RETUNE_C24_0:
+ case WM8962_ADCL_RETUNE_C25_1:
+ case WM8962_ADCL_RETUNE_C25_0:
+ case WM8962_ADCL_RETUNE_C26_1:
+ case WM8962_ADCL_RETUNE_C26_0:
+ case WM8962_ADCL_RETUNE_C27_1:
+ case WM8962_ADCL_RETUNE_C27_0:
+ case WM8962_ADCL_RETUNE_C28_1:
+ case WM8962_ADCL_RETUNE_C28_0:
+ case WM8962_ADCL_RETUNE_C29_1:
+ case WM8962_ADCL_RETUNE_C29_0:
+ case WM8962_ADCL_RETUNE_C30_1:
+ case WM8962_ADCL_RETUNE_C30_0:
+ case WM8962_ADCL_RETUNE_C31_1:
+ case WM8962_ADCL_RETUNE_C31_0:
+ case WM8962_ADCL_RETUNE_C32_1:
+ case WM8962_ADCL_RETUNE_C32_0:
+ case WM8962_RETUNEADC_PG2_1:
+ case WM8962_RETUNEADC_PG2_0:
+ case WM8962_RETUNEADC_PG_1:
+ case WM8962_RETUNEADC_PG_0:
+ case WM8962_ADCR_RETUNE_C1_1:
+ case WM8962_ADCR_RETUNE_C1_0:
+ case WM8962_ADCR_RETUNE_C2_1:
+ case WM8962_ADCR_RETUNE_C2_0:
+ case WM8962_ADCR_RETUNE_C3_1:
+ case WM8962_ADCR_RETUNE_C3_0:
+ case WM8962_ADCR_RETUNE_C4_1:
+ case WM8962_ADCR_RETUNE_C4_0:
+ case WM8962_ADCR_RETUNE_C5_1:
+ case WM8962_ADCR_RETUNE_C5_0:
+ case WM8962_ADCR_RETUNE_C6_1:
+ case WM8962_ADCR_RETUNE_C6_0:
+ case WM8962_ADCR_RETUNE_C7_1:
+ case WM8962_ADCR_RETUNE_C7_0:
+ case WM8962_ADCR_RETUNE_C8_1:
+ case WM8962_ADCR_RETUNE_C8_0:
+ case WM8962_ADCR_RETUNE_C9_1:
+ case WM8962_ADCR_RETUNE_C9_0:
+ case WM8962_ADCR_RETUNE_C10_1:
+ case WM8962_ADCR_RETUNE_C10_0:
+ case WM8962_ADCR_RETUNE_C11_1:
+ case WM8962_ADCR_RETUNE_C11_0:
+ case WM8962_ADCR_RETUNE_C12_1:
+ case WM8962_ADCR_RETUNE_C12_0:
+ case WM8962_ADCR_RETUNE_C13_1:
+ case WM8962_ADCR_RETUNE_C13_0:
+ case WM8962_ADCR_RETUNE_C14_1:
+ case WM8962_ADCR_RETUNE_C14_0:
+ case WM8962_ADCR_RETUNE_C15_1:
+ case WM8962_ADCR_RETUNE_C15_0:
+ case WM8962_ADCR_RETUNE_C16_1:
+ case WM8962_ADCR_RETUNE_C16_0:
+ case WM8962_ADCR_RETUNE_C17_1:
+ case WM8962_ADCR_RETUNE_C17_0:
+ case WM8962_ADCR_RETUNE_C18_1:
+ case WM8962_ADCR_RETUNE_C18_0:
+ case WM8962_ADCR_RETUNE_C19_1:
+ case WM8962_ADCR_RETUNE_C19_0:
+ case WM8962_ADCR_RETUNE_C20_1:
+ case WM8962_ADCR_RETUNE_C20_0:
+ case WM8962_ADCR_RETUNE_C21_1:
+ case WM8962_ADCR_RETUNE_C21_0:
+ case WM8962_ADCR_RETUNE_C22_1:
+ case WM8962_ADCR_RETUNE_C22_0:
+ case WM8962_ADCR_RETUNE_C23_1:
+ case WM8962_ADCR_RETUNE_C23_0:
+ case WM8962_ADCR_RETUNE_C24_1:
+ case WM8962_ADCR_RETUNE_C24_0:
+ case WM8962_ADCR_RETUNE_C25_1:
+ case WM8962_ADCR_RETUNE_C25_0:
+ case WM8962_ADCR_RETUNE_C26_1:
+ case WM8962_ADCR_RETUNE_C26_0:
+ case WM8962_ADCR_RETUNE_C27_1:
+ case WM8962_ADCR_RETUNE_C27_0:
+ case WM8962_ADCR_RETUNE_C28_1:
+ case WM8962_ADCR_RETUNE_C28_0:
+ case WM8962_ADCR_RETUNE_C29_1:
+ case WM8962_ADCR_RETUNE_C29_0:
+ case WM8962_ADCR_RETUNE_C30_1:
+ case WM8962_ADCR_RETUNE_C30_0:
+ case WM8962_ADCR_RETUNE_C31_1:
+ case WM8962_ADCR_RETUNE_C31_0:
+ case WM8962_ADCR_RETUNE_C32_1:
+ case WM8962_ADCR_RETUNE_C32_0:
+ case WM8962_DACL_RETUNE_C1_1:
+ case WM8962_DACL_RETUNE_C1_0:
+ case WM8962_DACL_RETUNE_C2_1:
+ case WM8962_DACL_RETUNE_C2_0:
+ case WM8962_DACL_RETUNE_C3_1:
+ case WM8962_DACL_RETUNE_C3_0:
+ case WM8962_DACL_RETUNE_C4_1:
+ case WM8962_DACL_RETUNE_C4_0:
+ case WM8962_DACL_RETUNE_C5_1:
+ case WM8962_DACL_RETUNE_C5_0:
+ case WM8962_DACL_RETUNE_C6_1:
+ case WM8962_DACL_RETUNE_C6_0:
+ case WM8962_DACL_RETUNE_C7_1:
+ case WM8962_DACL_RETUNE_C7_0:
+ case WM8962_DACL_RETUNE_C8_1:
+ case WM8962_DACL_RETUNE_C8_0:
+ case WM8962_DACL_RETUNE_C9_1:
+ case WM8962_DACL_RETUNE_C9_0:
+ case WM8962_DACL_RETUNE_C10_1:
+ case WM8962_DACL_RETUNE_C10_0:
+ case WM8962_DACL_RETUNE_C11_1:
+ case WM8962_DACL_RETUNE_C11_0:
+ case WM8962_DACL_RETUNE_C12_1:
+ case WM8962_DACL_RETUNE_C12_0:
+ case WM8962_DACL_RETUNE_C13_1:
+ case WM8962_DACL_RETUNE_C13_0:
+ case WM8962_DACL_RETUNE_C14_1:
+ case WM8962_DACL_RETUNE_C14_0:
+ case WM8962_DACL_RETUNE_C15_1:
+ case WM8962_DACL_RETUNE_C15_0:
+ case WM8962_DACL_RETUNE_C16_1:
+ case WM8962_DACL_RETUNE_C16_0:
+ case WM8962_DACL_RETUNE_C17_1:
+ case WM8962_DACL_RETUNE_C17_0:
+ case WM8962_DACL_RETUNE_C18_1:
+ case WM8962_DACL_RETUNE_C18_0:
+ case WM8962_DACL_RETUNE_C19_1:
+ case WM8962_DACL_RETUNE_C19_0:
+ case WM8962_DACL_RETUNE_C20_1:
+ case WM8962_DACL_RETUNE_C20_0:
+ case WM8962_DACL_RETUNE_C21_1:
+ case WM8962_DACL_RETUNE_C21_0:
+ case WM8962_DACL_RETUNE_C22_1:
+ case WM8962_DACL_RETUNE_C22_0:
+ case WM8962_DACL_RETUNE_C23_1:
+ case WM8962_DACL_RETUNE_C23_0:
+ case WM8962_DACL_RETUNE_C24_1:
+ case WM8962_DACL_RETUNE_C24_0:
+ case WM8962_DACL_RETUNE_C25_1:
+ case WM8962_DACL_RETUNE_C25_0:
+ case WM8962_DACL_RETUNE_C26_1:
+ case WM8962_DACL_RETUNE_C26_0:
+ case WM8962_DACL_RETUNE_C27_1:
+ case WM8962_DACL_RETUNE_C27_0:
+ case WM8962_DACL_RETUNE_C28_1:
+ case WM8962_DACL_RETUNE_C28_0:
+ case WM8962_DACL_RETUNE_C29_1:
+ case WM8962_DACL_RETUNE_C29_0:
+ case WM8962_DACL_RETUNE_C30_1:
+ case WM8962_DACL_RETUNE_C30_0:
+ case WM8962_DACL_RETUNE_C31_1:
+ case WM8962_DACL_RETUNE_C31_0:
+ case WM8962_DACL_RETUNE_C32_1:
+ case WM8962_DACL_RETUNE_C32_0:
+ case WM8962_RETUNEDAC_PG2_1:
+ case WM8962_RETUNEDAC_PG2_0:
+ case WM8962_RETUNEDAC_PG_1:
+ case WM8962_RETUNEDAC_PG_0:
+ case WM8962_DACR_RETUNE_C1_1:
+ case WM8962_DACR_RETUNE_C1_0:
+ case WM8962_DACR_RETUNE_C2_1:
+ case WM8962_DACR_RETUNE_C2_0:
+ case WM8962_DACR_RETUNE_C3_1:
+ case WM8962_DACR_RETUNE_C3_0:
+ case WM8962_DACR_RETUNE_C4_1:
+ case WM8962_DACR_RETUNE_C4_0:
+ case WM8962_DACR_RETUNE_C5_1:
+ case WM8962_DACR_RETUNE_C5_0:
+ case WM8962_DACR_RETUNE_C6_1:
+ case WM8962_DACR_RETUNE_C6_0:
+ case WM8962_DACR_RETUNE_C7_1:
+ case WM8962_DACR_RETUNE_C7_0:
+ case WM8962_DACR_RETUNE_C8_1:
+ case WM8962_DACR_RETUNE_C8_0:
+ case WM8962_DACR_RETUNE_C9_1:
+ case WM8962_DACR_RETUNE_C9_0:
+ case WM8962_DACR_RETUNE_C10_1:
+ case WM8962_DACR_RETUNE_C10_0:
+ case WM8962_DACR_RETUNE_C11_1:
+ case WM8962_DACR_RETUNE_C11_0:
+ case WM8962_DACR_RETUNE_C12_1:
+ case WM8962_DACR_RETUNE_C12_0:
+ case WM8962_DACR_RETUNE_C13_1:
+ case WM8962_DACR_RETUNE_C13_0:
+ case WM8962_DACR_RETUNE_C14_1:
+ case WM8962_DACR_RETUNE_C14_0:
+ case WM8962_DACR_RETUNE_C15_1:
+ case WM8962_DACR_RETUNE_C15_0:
+ case WM8962_DACR_RETUNE_C16_1:
+ case WM8962_DACR_RETUNE_C16_0:
+ case WM8962_DACR_RETUNE_C17_1:
+ case WM8962_DACR_RETUNE_C17_0:
+ case WM8962_DACR_RETUNE_C18_1:
+ case WM8962_DACR_RETUNE_C18_0:
+ case WM8962_DACR_RETUNE_C19_1:
+ case WM8962_DACR_RETUNE_C19_0:
+ case WM8962_DACR_RETUNE_C20_1:
+ case WM8962_DACR_RETUNE_C20_0:
+ case WM8962_DACR_RETUNE_C21_1:
+ case WM8962_DACR_RETUNE_C21_0:
+ case WM8962_DACR_RETUNE_C22_1:
+ case WM8962_DACR_RETUNE_C22_0:
+ case WM8962_DACR_RETUNE_C23_1:
+ case WM8962_DACR_RETUNE_C23_0:
+ case WM8962_DACR_RETUNE_C24_1:
+ case WM8962_DACR_RETUNE_C24_0:
+ case WM8962_DACR_RETUNE_C25_1:
+ case WM8962_DACR_RETUNE_C25_0:
+ case WM8962_DACR_RETUNE_C26_1:
+ case WM8962_DACR_RETUNE_C26_0:
+ case WM8962_DACR_RETUNE_C27_1:
+ case WM8962_DACR_RETUNE_C27_0:
+ case WM8962_DACR_RETUNE_C28_1:
+ case WM8962_DACR_RETUNE_C28_0:
+ case WM8962_DACR_RETUNE_C29_1:
+ case WM8962_DACR_RETUNE_C29_0:
+ case WM8962_DACR_RETUNE_C30_1:
+ case WM8962_DACR_RETUNE_C30_0:
+ case WM8962_DACR_RETUNE_C31_1:
+ case WM8962_DACR_RETUNE_C31_0:
+ case WM8962_DACR_RETUNE_C32_1:
+ case WM8962_DACR_RETUNE_C32_0:
+ case WM8962_VSS_XHD2_1:
+ case WM8962_VSS_XHD2_0:
+ case WM8962_VSS_XHD3_1:
+ case WM8962_VSS_XHD3_0:
+ case WM8962_VSS_XHN1_1:
+ case WM8962_VSS_XHN1_0:
+ case WM8962_VSS_XHN2_1:
+ case WM8962_VSS_XHN2_0:
+ case WM8962_VSS_XHN3_1:
+ case WM8962_VSS_XHN3_0:
+ case WM8962_VSS_XLA_1:
+ case WM8962_VSS_XLA_0:
+ case WM8962_VSS_XLB_1:
+ case WM8962_VSS_XLB_0:
+ case WM8962_VSS_XLG_1:
+ case WM8962_VSS_XLG_0:
+ case WM8962_VSS_PG2_1:
+ case WM8962_VSS_PG2_0:
+ case WM8962_VSS_PG_1:
+ case WM8962_VSS_PG_0:
+ case WM8962_VSS_XTD1_1:
+ case WM8962_VSS_XTD1_0:
+ case WM8962_VSS_XTD2_1:
+ case WM8962_VSS_XTD2_0:
+ case WM8962_VSS_XTD3_1:
+ case WM8962_VSS_XTD3_0:
+ case WM8962_VSS_XTD4_1:
+ case WM8962_VSS_XTD4_0:
+ case WM8962_VSS_XTD5_1:
+ case WM8962_VSS_XTD5_0:
+ case WM8962_VSS_XTD6_1:
+ case WM8962_VSS_XTD6_0:
+ case WM8962_VSS_XTD7_1:
+ case WM8962_VSS_XTD7_0:
+ case WM8962_VSS_XTD8_1:
+ case WM8962_VSS_XTD8_0:
+ case WM8962_VSS_XTD9_1:
+ case WM8962_VSS_XTD9_0:
+ case WM8962_VSS_XTD10_1:
+ case WM8962_VSS_XTD10_0:
+ case WM8962_VSS_XTD11_1:
+ case WM8962_VSS_XTD11_0:
+ case WM8962_VSS_XTD12_1:
+ case WM8962_VSS_XTD12_0:
+ case WM8962_VSS_XTD13_1:
+ case WM8962_VSS_XTD13_0:
+ case WM8962_VSS_XTD14_1:
+ case WM8962_VSS_XTD14_0:
+ case WM8962_VSS_XTD15_1:
+ case WM8962_VSS_XTD15_0:
+ case WM8962_VSS_XTD16_1:
+ case WM8962_VSS_XTD16_0:
+ case WM8962_VSS_XTD17_1:
+ case WM8962_VSS_XTD17_0:
+ case WM8962_VSS_XTD18_1:
+ case WM8962_VSS_XTD18_0:
+ case WM8962_VSS_XTD19_1:
+ case WM8962_VSS_XTD19_0:
+ case WM8962_VSS_XTD20_1:
+ case WM8962_VSS_XTD20_0:
+ case WM8962_VSS_XTD21_1:
+ case WM8962_VSS_XTD21_0:
+ case WM8962_VSS_XTD22_1:
+ case WM8962_VSS_XTD22_0:
+ case WM8962_VSS_XTD23_1:
+ case WM8962_VSS_XTD23_0:
+ case WM8962_VSS_XTD24_1:
+ case WM8962_VSS_XTD24_0:
+ case WM8962_VSS_XTD25_1:
+ case WM8962_VSS_XTD25_0:
+ case WM8962_VSS_XTD26_1:
+ case WM8962_VSS_XTD26_0:
+ case WM8962_VSS_XTD27_1:
+ case WM8962_VSS_XTD27_0:
+ case WM8962_VSS_XTD28_1:
+ case WM8962_VSS_XTD28_0:
+ case WM8962_VSS_XTD29_1:
+ case WM8962_VSS_XTD29_0:
+ case WM8962_VSS_XTD30_1:
+ case WM8962_VSS_XTD30_0:
+ case WM8962_VSS_XTD31_1:
+ case WM8962_VSS_XTD31_0:
+ case WM8962_VSS_XTD32_1:
+ case WM8962_VSS_XTD32_0:
+ case WM8962_VSS_XTS1_1:
+ case WM8962_VSS_XTS1_0:
+ case WM8962_VSS_XTS2_1:
+ case WM8962_VSS_XTS2_0:
+ case WM8962_VSS_XTS3_1:
+ case WM8962_VSS_XTS3_0:
+ case WM8962_VSS_XTS4_1:
+ case WM8962_VSS_XTS4_0:
+ case WM8962_VSS_XTS5_1:
+ case WM8962_VSS_XTS5_0:
+ case WM8962_VSS_XTS6_1:
+ case WM8962_VSS_XTS6_0:
+ case WM8962_VSS_XTS7_1:
+ case WM8962_VSS_XTS7_0:
+ case WM8962_VSS_XTS8_1:
+ case WM8962_VSS_XTS8_0:
+ case WM8962_VSS_XTS9_1:
+ case WM8962_VSS_XTS9_0:
+ case WM8962_VSS_XTS10_1:
+ case WM8962_VSS_XTS10_0:
+ case WM8962_VSS_XTS11_1:
+ case WM8962_VSS_XTS11_0:
+ case WM8962_VSS_XTS12_1:
+ case WM8962_VSS_XTS12_0:
+ case WM8962_VSS_XTS13_1:
+ case WM8962_VSS_XTS13_0:
+ case WM8962_VSS_XTS14_1:
+ case WM8962_VSS_XTS14_0:
+ case WM8962_VSS_XTS15_1:
+ case WM8962_VSS_XTS15_0:
+ case WM8962_VSS_XTS16_1:
+ case WM8962_VSS_XTS16_0:
+ case WM8962_VSS_XTS17_1:
+ case WM8962_VSS_XTS17_0:
+ case WM8962_VSS_XTS18_1:
+ case WM8962_VSS_XTS18_0:
+ case WM8962_VSS_XTS19_1:
+ case WM8962_VSS_XTS19_0:
+ case WM8962_VSS_XTS20_1:
+ case WM8962_VSS_XTS20_0:
+ case WM8962_VSS_XTS21_1:
+ case WM8962_VSS_XTS21_0:
+ case WM8962_VSS_XTS22_1:
+ case WM8962_VSS_XTS22_0:
+ case WM8962_VSS_XTS23_1:
+ case WM8962_VSS_XTS23_0:
+ case WM8962_VSS_XTS24_1:
+ case WM8962_VSS_XTS24_0:
+ case WM8962_VSS_XTS25_1:
+ case WM8962_VSS_XTS25_0:
+ case WM8962_VSS_XTS26_1:
+ case WM8962_VSS_XTS26_0:
+ case WM8962_VSS_XTS27_1:
+ case WM8962_VSS_XTS27_0:
+ case WM8962_VSS_XTS28_1:
+ case WM8962_VSS_XTS28_0:
+ case WM8962_VSS_XTS29_1:
+ case WM8962_VSS_XTS29_0:
+ case WM8962_VSS_XTS30_1:
+ case WM8962_VSS_XTS30_0:
+ case WM8962_VSS_XTS31_1:
+ case WM8962_VSS_XTS31_0:
+ case WM8962_VSS_XTS32_1:
+ case WM8962_VSS_XTS32_0:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int wm8962_reset(struct wm8962_priv *wm8962)
+{
+ int ret;
+
+ ret = regmap_write(wm8962->regmap, WM8962_SOFTWARE_RESET, 0x6243);
+ if (ret != 0)
+ return ret;
+
+ return regmap_write(wm8962->regmap, WM8962_PLL_SOFTWARE_RESET, 0);
+}
+
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -2325, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mixin_tlv, -1500, 300, 0);
+static const unsigned int mixinpga_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(1300, 1300, 0),
+ 3, 4, TLV_DB_SCALE_ITEM(1800, 200, 0),
+ 5, 5, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(2700, 300, 0),
+};
+static const DECLARE_TLV_DB_SCALE(beep_tlv, -9600, 600, 1);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(st_tlv, -3600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(inmix_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(hp_tlv, -700, 100, 0);
+static const unsigned int classd_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+
+static int wm8962_dsp2_write_config(struct snd_soc_codec *codec)
+{
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+ return regcache_sync_region(wm8962->regmap,
+ WM8962_HDBASS_AI_1, WM8962_MAX_REGISTER);
+}
+
+static int wm8962_dsp2_set_enable(struct snd_soc_codec *codec, u16 val)
+{
+ u16 adcl = snd_soc_read(codec, WM8962_LEFT_ADC_VOLUME);
+ u16 adcr = snd_soc_read(codec, WM8962_RIGHT_ADC_VOLUME);
+ u16 dac = snd_soc_read(codec, WM8962_ADC_DAC_CONTROL_1);
+
+ /* Mute the ADCs and DACs */
+ snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, 0);
+ snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, WM8962_ADC_VU);
+ snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
+ WM8962_DAC_MUTE, WM8962_DAC_MUTE);
+
+ snd_soc_write(codec, WM8962_SOUNDSTAGE_ENABLES_0, val);
+
+ /* Restore the ADCs and DACs */
+ snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, adcl);
+ snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, adcr);
+ snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
+ WM8962_DAC_MUTE, dac);
+
+ return 0;
+}
+
+static int wm8962_dsp2_start(struct snd_soc_codec *codec)
+{
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+ wm8962_dsp2_write_config(codec);
+
+ snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_RUNR);
+
+ wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
+
+ return 0;
+}
+
+static int wm8962_dsp2_stop(struct snd_soc_codec *codec)
+{
+ wm8962_dsp2_set_enable(codec, 0);
+
+ snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_STOP);
+
+ return 0;
+}
+
+#define WM8962_DSP2_ENABLE(xname, xshift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = wm8962_dsp2_ena_info, \
+ .get = wm8962_dsp2_ena_get, .put = wm8962_dsp2_ena_put, \
+ .private_value = xshift }
+
+static int wm8962_dsp2_ena_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+
+ return 0;
+}
+
+static int wm8962_dsp2_ena_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int shift = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = !!(wm8962->dsp2_ena & 1 << shift);
+
+ return 0;
+}
+
+static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int shift = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ int old = wm8962->dsp2_ena;
+ int ret = 0;
+ int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) &
+ WM8962_DSP2_ENA;
+
+ mutex_lock(&wm8962->dsp2_ena_lock);
+
+ if (ucontrol->value.integer.value[0])
+ wm8962->dsp2_ena |= 1 << shift;
+ else
+ wm8962->dsp2_ena &= ~(1 << shift);
+
+ if (wm8962->dsp2_ena == old)
+ goto out;
+
+ ret = 1;
+
+ if (dsp2_running) {
+ if (wm8962->dsp2_ena)
+ wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
+ else
+ wm8962_dsp2_stop(codec);
+ }
+
+out:
+ mutex_unlock(&wm8962->dsp2_ena_lock);
+
+ return ret;
+}
+
+/* The VU bits for the headphones are in a different register to the mute
+ * bits and only take effect on the PGA if it is actually powered.
+ */
+static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int ret;
+
+ /* Apply the update (if any) */
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret == 0)
+ return 0;
+
+ /* If the left PGA is enabled hit that VU bit... */
+ ret = snd_soc_read(codec, WM8962_PWR_MGMT_2);
+ if (ret & WM8962_HPOUTL_PGA_ENA) {
+ snd_soc_write(codec, WM8962_HPOUTL_VOLUME,
+ snd_soc_read(codec, WM8962_HPOUTL_VOLUME));
+ return 1;
+ }
+
+ /* ...otherwise the right. The VU is stereo. */
+ if (ret & WM8962_HPOUTR_PGA_ENA)
+ snd_soc_write(codec, WM8962_HPOUTR_VOLUME,
+ snd_soc_read(codec, WM8962_HPOUTR_VOLUME));
+
+ return 1;
+}
+
+/* The VU bits for the speakers are in a different register to the mute
+ * bits and only take effect on the PGA if it is actually powered.
+ */
+static int wm8962_put_spk_sw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int ret;
+
+ /* Apply the update (if any) */
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret == 0)
+ return 0;
+
+ /* If the left PGA is enabled hit that VU bit... */
+ ret = snd_soc_read(codec, WM8962_PWR_MGMT_2);
+ if (ret & WM8962_SPKOUTL_PGA_ENA) {
+ snd_soc_write(codec, WM8962_SPKOUTL_VOLUME,
+ snd_soc_read(codec, WM8962_SPKOUTL_VOLUME));
+ return 1;
+ }
+
+ /* ...otherwise the right. The VU is stereo. */
+ if (ret & WM8962_SPKOUTR_PGA_ENA)
+ snd_soc_write(codec, WM8962_SPKOUTR_VOLUME,
+ snd_soc_read(codec, WM8962_SPKOUTR_VOLUME));
+
+ return 1;
+}
+
+static const char *cap_hpf_mode_text[] = {
+ "Hi-fi", "Application"
+};
+
+static SOC_ENUM_SINGLE_DECL(cap_hpf_mode,
+ WM8962_ADC_DAC_CONTROL_2, 10, cap_hpf_mode_text);
+
+
+static const char *cap_lhpf_mode_text[] = {
+ "LPF", "HPF"
+};
+
+static SOC_ENUM_SINGLE_DECL(cap_lhpf_mode,
+ WM8962_LHPF1, 1, cap_lhpf_mode_text);
+
+static const struct snd_kcontrol_new wm8962_snd_controls[] = {
+SOC_DOUBLE("Input Mixer Switch", WM8962_INPUT_MIXER_CONTROL_1, 3, 2, 1, 1),
+
+SOC_SINGLE_TLV("MIXINL IN2L Volume", WM8962_LEFT_INPUT_MIXER_VOLUME, 6, 7, 0,
+ mixin_tlv),
+SOC_SINGLE_TLV("MIXINL PGA Volume", WM8962_LEFT_INPUT_MIXER_VOLUME, 3, 7, 0,
+ mixinpga_tlv),
+SOC_SINGLE_TLV("MIXINL IN3L Volume", WM8962_LEFT_INPUT_MIXER_VOLUME, 0, 7, 0,
+ mixin_tlv),
+
+SOC_SINGLE_TLV("MIXINR IN2R Volume", WM8962_RIGHT_INPUT_MIXER_VOLUME, 6, 7, 0,
+ mixin_tlv),
+SOC_SINGLE_TLV("MIXINR PGA Volume", WM8962_RIGHT_INPUT_MIXER_VOLUME, 3, 7, 0,
+ mixinpga_tlv),
+SOC_SINGLE_TLV("MIXINR IN3R Volume", WM8962_RIGHT_INPUT_MIXER_VOLUME, 0, 7, 0,
+ mixin_tlv),
+
+SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8962_LEFT_ADC_VOLUME,
+ WM8962_RIGHT_ADC_VOLUME, 1, 127, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("Capture Volume", WM8962_LEFT_INPUT_VOLUME,
+ WM8962_RIGHT_INPUT_VOLUME, 0, 63, 0, inpga_tlv),
+SOC_DOUBLE_R("Capture Switch", WM8962_LEFT_INPUT_VOLUME,
+ WM8962_RIGHT_INPUT_VOLUME, 7, 1, 1),
+SOC_DOUBLE_R("Capture ZC Switch", WM8962_LEFT_INPUT_VOLUME,
+ WM8962_RIGHT_INPUT_VOLUME, 6, 1, 1),
+SOC_SINGLE("Capture HPF Switch", WM8962_ADC_DAC_CONTROL_1, 0, 1, 1),
+SOC_ENUM("Capture HPF Mode", cap_hpf_mode),
+SOC_SINGLE("Capture HPF Cutoff", WM8962_ADC_DAC_CONTROL_2, 7, 7, 0),
+SOC_SINGLE("Capture LHPF Switch", WM8962_LHPF1, 0, 1, 0),
+SOC_ENUM("Capture LHPF Mode", cap_lhpf_mode),
+
+SOC_DOUBLE_R_TLV("Sidetone Volume", WM8962_DAC_DSP_MIXING_1,
+ WM8962_DAC_DSP_MIXING_2, 4, 12, 0, st_tlv),
+
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8962_LEFT_DAC_VOLUME,
+ WM8962_RIGHT_DAC_VOLUME, 1, 127, 0, digital_tlv),
+SOC_SINGLE("DAC High Performance Switch", WM8962_ADC_DAC_CONTROL_2, 0, 1, 0),
+SOC_SINGLE("DAC L/R Swap Switch", WM8962_AUDIO_INTERFACE_0, 5, 1, 0),
+SOC_SINGLE("ADC L/R Swap Switch", WM8962_AUDIO_INTERFACE_0, 8, 1, 0),
+
+SOC_SINGLE("ADC High Performance Switch", WM8962_ADDITIONAL_CONTROL_1,
+ 5, 1, 0),
+
+SOC_SINGLE_TLV("Beep Volume", WM8962_BEEP_GENERATOR_1, 4, 15, 0, beep_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Volume", WM8962_HPOUTL_VOLUME,
+ WM8962_HPOUTR_VOLUME, 0, 127, 0, out_tlv),
+SOC_DOUBLE_EXT("Headphone Switch", WM8962_PWR_MGMT_2, 1, 0, 1, 1,
+ snd_soc_get_volsw, wm8962_put_hp_sw),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8962_HPOUTL_VOLUME, WM8962_HPOUTR_VOLUME,
+ 7, 1, 0),
+SOC_DOUBLE_TLV("Headphone Aux Volume", WM8962_ANALOGUE_HP_2, 3, 6, 7, 0,
+ hp_tlv),
+
+SOC_DOUBLE_R("Headphone Mixer Switch", WM8962_HEADPHONE_MIXER_3,
+ WM8962_HEADPHONE_MIXER_4, 8, 1, 1),
+
+SOC_SINGLE_TLV("HPMIXL IN4L Volume", WM8962_HEADPHONE_MIXER_3,
+ 3, 7, 0, bypass_tlv),
+SOC_SINGLE_TLV("HPMIXL IN4R Volume", WM8962_HEADPHONE_MIXER_3,
+ 0, 7, 0, bypass_tlv),
+SOC_SINGLE_TLV("HPMIXL MIXINL Volume", WM8962_HEADPHONE_MIXER_3,
+ 7, 1, 1, inmix_tlv),
+SOC_SINGLE_TLV("HPMIXL MIXINR Volume", WM8962_HEADPHONE_MIXER_3,
+ 6, 1, 1, inmix_tlv),
+
+SOC_SINGLE_TLV("HPMIXR IN4L Volume", WM8962_HEADPHONE_MIXER_4,
+ 3, 7, 0, bypass_tlv),
+SOC_SINGLE_TLV("HPMIXR IN4R Volume", WM8962_HEADPHONE_MIXER_4,
+ 0, 7, 0, bypass_tlv),
+SOC_SINGLE_TLV("HPMIXR MIXINL Volume", WM8962_HEADPHONE_MIXER_4,
+ 7, 1, 1, inmix_tlv),
+SOC_SINGLE_TLV("HPMIXR MIXINR Volume", WM8962_HEADPHONE_MIXER_4,
+ 6, 1, 1, inmix_tlv),
+
+SOC_SINGLE_TLV("Speaker Boost Volume", WM8962_CLASS_D_CONTROL_2, 0, 7, 0,
+ classd_tlv),
+
+SOC_SINGLE("EQ Switch", WM8962_EQ1, WM8962_EQ_ENA_SHIFT, 1, 0),
+SOC_DOUBLE_R_TLV("EQ1 Volume", WM8962_EQ2, WM8962_EQ22,
+ WM8962_EQL_B1_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ2 Volume", WM8962_EQ2, WM8962_EQ22,
+ WM8962_EQL_B2_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ3 Volume", WM8962_EQ2, WM8962_EQ22,
+ WM8962_EQL_B3_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23,
+ WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
+ WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv),
+SND_SOC_BYTES("EQL Coefficients", WM8962_EQ4, 18),
+SND_SOC_BYTES("EQR Coefficients", WM8962_EQ24, 18),
+
+
+SOC_SINGLE("3D Switch", WM8962_THREED1, 0, 1, 0),
+SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA),
+
+SOC_SINGLE("DF1 Switch", WM8962_DF1, 0, 1, 0),
+SND_SOC_BYTES_MASK("DF1 Coefficients", WM8962_DF1, 7, WM8962_DF1_ENA),
+
+SOC_SINGLE("DRC Switch", WM8962_DRC_1, 0, 1, 0),
+SND_SOC_BYTES_MASK("DRC Coefficients", WM8962_DRC_1, 5, WM8962_DRC_ENA),
+
+WM8962_DSP2_ENABLE("VSS Switch", WM8962_VSS_ENA_SHIFT),
+SND_SOC_BYTES("VSS Coefficients", WM8962_VSS_XHD2_1, 148),
+WM8962_DSP2_ENABLE("HPF1 Switch", WM8962_HPF1_ENA_SHIFT),
+WM8962_DSP2_ENABLE("HPF2 Switch", WM8962_HPF2_ENA_SHIFT),
+SND_SOC_BYTES("HPF Coefficients", WM8962_LHPF2, 1),
+WM8962_DSP2_ENABLE("HD Bass Switch", WM8962_HDBASS_ENA_SHIFT),
+SND_SOC_BYTES("HD Bass Coefficients", WM8962_HDBASS_AI_1, 30),
+
+SOC_DOUBLE("ALC Switch", WM8962_ALC1, WM8962_ALCL_ENA_SHIFT,
+ WM8962_ALCR_ENA_SHIFT, 1, 0),
+SND_SOC_BYTES_MASK("ALC Coefficients", WM8962_ALC1, 4,
+ WM8962_ALCL_ENA_MASK | WM8962_ALCR_ENA_MASK),
+};
+
+static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
+SOC_SINGLE_TLV("Speaker Volume", WM8962_SPKOUTL_VOLUME, 0, 127, 0, out_tlv),
+SOC_SINGLE_EXT("Speaker Switch", WM8962_CLASS_D_CONTROL_1, 1, 1, 1,
+ snd_soc_get_volsw, wm8962_put_spk_sw),
+SOC_SINGLE("Speaker ZC Switch", WM8962_SPKOUTL_VOLUME, 7, 1, 0),
+
+SOC_SINGLE("Speaker Mixer Switch", WM8962_SPEAKER_MIXER_3, 8, 1, 1),
+SOC_SINGLE_TLV("Speaker Mixer IN4L Volume", WM8962_SPEAKER_MIXER_3,
+ 3, 7, 0, bypass_tlv),
+SOC_SINGLE_TLV("Speaker Mixer IN4R Volume", WM8962_SPEAKER_MIXER_3,
+ 0, 7, 0, bypass_tlv),
+SOC_SINGLE_TLV("Speaker Mixer MIXINL Volume", WM8962_SPEAKER_MIXER_3,
+ 7, 1, 1, inmix_tlv),
+SOC_SINGLE_TLV("Speaker Mixer MIXINR Volume", WM8962_SPEAKER_MIXER_3,
+ 6, 1, 1, inmix_tlv),
+SOC_SINGLE_TLV("Speaker Mixer DACL Volume", WM8962_SPEAKER_MIXER_5,
+ 7, 1, 0, inmix_tlv),
+SOC_SINGLE_TLV("Speaker Mixer DACR Volume", WM8962_SPEAKER_MIXER_5,
+ 6, 1, 0, inmix_tlv),
+};
+
+static const struct snd_kcontrol_new wm8962_spk_stereo_controls[] = {
+SOC_DOUBLE_R_TLV("Speaker Volume", WM8962_SPKOUTL_VOLUME,
+ WM8962_SPKOUTR_VOLUME, 0, 127, 0, out_tlv),
+SOC_DOUBLE_EXT("Speaker Switch", WM8962_CLASS_D_CONTROL_1, 1, 0, 1, 1,
+ snd_soc_get_volsw, wm8962_put_spk_sw),
+SOC_DOUBLE_R("Speaker ZC Switch", WM8962_SPKOUTL_VOLUME, WM8962_SPKOUTR_VOLUME,
+ 7, 1, 0),
+
+SOC_DOUBLE_R("Speaker Mixer Switch", WM8962_SPEAKER_MIXER_3,
+ WM8962_SPEAKER_MIXER_4, 8, 1, 1),
+
+SOC_SINGLE_TLV("SPKOUTL Mixer IN4L Volume", WM8962_SPEAKER_MIXER_3,
+ 3, 7, 0, bypass_tlv),
+SOC_SINGLE_TLV("SPKOUTL Mixer IN4R Volume", WM8962_SPEAKER_MIXER_3,
+ 0, 7, 0, bypass_tlv),
+SOC_SINGLE_TLV("SPKOUTL Mixer MIXINL Volume", WM8962_SPEAKER_MIXER_3,
+ 7, 1, 1, inmix_tlv),
+SOC_SINGLE_TLV("SPKOUTL Mixer MIXINR Volume", WM8962_SPEAKER_MIXER_3,
+ 6, 1, 1, inmix_tlv),
+SOC_SINGLE_TLV("SPKOUTL Mixer DACL Volume", WM8962_SPEAKER_MIXER_5,
+ 7, 1, 0, inmix_tlv),
+SOC_SINGLE_TLV("SPKOUTL Mixer DACR Volume", WM8962_SPEAKER_MIXER_5,
+ 6, 1, 0, inmix_tlv),
+
+SOC_SINGLE_TLV("SPKOUTR Mixer IN4L Volume", WM8962_SPEAKER_MIXER_4,
+ 3, 7, 0, bypass_tlv),
+SOC_SINGLE_TLV("SPKOUTR Mixer IN4R Volume", WM8962_SPEAKER_MIXER_4,
+ 0, 7, 0, bypass_tlv),
+SOC_SINGLE_TLV("SPKOUTR Mixer MIXINL Volume", WM8962_SPEAKER_MIXER_4,
+ 7, 1, 1, inmix_tlv),
+SOC_SINGLE_TLV("SPKOUTR Mixer MIXINR Volume", WM8962_SPEAKER_MIXER_4,
+ 6, 1, 1, inmix_tlv),
+SOC_SINGLE_TLV("SPKOUTR Mixer DACL Volume", WM8962_SPEAKER_MIXER_5,
+ 5, 1, 0, inmix_tlv),
+SOC_SINGLE_TLV("SPKOUTR Mixer DACR Volume", WM8962_SPEAKER_MIXER_5,
+ 4, 1, 0, inmix_tlv),
+};
+
+static int cp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(5);
+ break;
+
+ default:
+ WARN(1, "Invalid event %d\n", event);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int timeout;
+ int reg;
+ int expected = (WM8962_DCS_STARTUP_DONE_HP1L |
+ WM8962_DCS_STARTUP_DONE_HP1R);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
+ WM8962_HP1L_ENA | WM8962_HP1R_ENA,
+ WM8962_HP1L_ENA | WM8962_HP1R_ENA);
+ udelay(20);
+
+ snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
+ WM8962_HP1L_ENA_DLY | WM8962_HP1R_ENA_DLY,
+ WM8962_HP1L_ENA_DLY | WM8962_HP1R_ENA_DLY);
+
+ /* Start the DC servo */
+ snd_soc_update_bits(codec, WM8962_DC_SERVO_1,
+ WM8962_HP1L_DCS_ENA | WM8962_HP1R_DCS_ENA |
+ WM8962_HP1L_DCS_STARTUP |
+ WM8962_HP1R_DCS_STARTUP,
+ WM8962_HP1L_DCS_ENA | WM8962_HP1R_DCS_ENA |
+ WM8962_HP1L_DCS_STARTUP |
+ WM8962_HP1R_DCS_STARTUP);
+
+ /* Wait for it to complete, should be well under 100ms */
+ timeout = 0;
+ do {
+ msleep(1);
+ reg = snd_soc_read(codec, WM8962_DC_SERVO_6);
+ if (reg < 0) {
+ dev_err(codec->dev,
+ "Failed to read DCS status: %d\n",
+ reg);
+ continue;
+ }
+ dev_dbg(codec->dev, "DCS status: %x\n", reg);
+ } while (++timeout < 200 && (reg & expected) != expected);
+
+ if ((reg & expected) != expected)
+ dev_err(codec->dev, "DC servo timed out\n");
+ else
+ dev_dbg(codec->dev, "DC servo complete after %dms\n",
+ timeout);
+
+ snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
+ WM8962_HP1L_ENA_OUTP |
+ WM8962_HP1R_ENA_OUTP,
+ WM8962_HP1L_ENA_OUTP |
+ WM8962_HP1R_ENA_OUTP);
+ udelay(20);
+
+ snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
+ WM8962_HP1L_RMV_SHORT |
+ WM8962_HP1R_RMV_SHORT,
+ WM8962_HP1L_RMV_SHORT |
+ WM8962_HP1R_RMV_SHORT);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
+ WM8962_HP1L_RMV_SHORT |
+ WM8962_HP1R_RMV_SHORT, 0);
+
+ udelay(20);
+
+ snd_soc_update_bits(codec, WM8962_DC_SERVO_1,
+ WM8962_HP1L_DCS_ENA | WM8962_HP1R_DCS_ENA |
+ WM8962_HP1L_DCS_STARTUP |
+ WM8962_HP1R_DCS_STARTUP,
+ 0);
+
+ snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
+ WM8962_HP1L_ENA | WM8962_HP1R_ENA |
+ WM8962_HP1L_ENA_DLY | WM8962_HP1R_ENA_DLY |
+ WM8962_HP1L_ENA_OUTP |
+ WM8962_HP1R_ENA_OUTP, 0);
+
+ break;
+
+ default:
+ WARN(1, "Invalid event %d\n", event);
+ return -EINVAL;
+
+ }
+
+ return 0;
+}
+
+/* VU bits for the output PGAs only take effect while the PGA is powered */
+static int out_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int reg;
+
+ switch (w->shift) {
+ case WM8962_HPOUTR_PGA_ENA_SHIFT:
+ reg = WM8962_HPOUTR_VOLUME;
+ break;
+ case WM8962_HPOUTL_PGA_ENA_SHIFT:
+ reg = WM8962_HPOUTL_VOLUME;
+ break;
+ case WM8962_SPKOUTR_PGA_ENA_SHIFT:
+ reg = WM8962_SPKOUTR_VOLUME;
+ break;
+ case WM8962_SPKOUTL_PGA_ENA_SHIFT:
+ reg = WM8962_SPKOUTL_VOLUME;
+ break;
+ default:
+ WARN(1, "Invalid shift %d\n", w->shift);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return snd_soc_write(codec, reg, snd_soc_read(codec, reg));
+ default:
+ WARN(1, "Invalid event %d\n", event);
+ return -EINVAL;
+ }
+}
+
+static int dsp2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (wm8962->dsp2_ena)
+ wm8962_dsp2_start(codec);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ if (wm8962->dsp2_ena)
+ wm8962_dsp2_stop(codec);
+ break;
+
+ default:
+ WARN(1, "Invalid event %d\n", event);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char *st_text[] = { "None", "Left", "Right" };
+
+static SOC_ENUM_SINGLE_DECL(str_enum,
+ WM8962_DAC_DSP_MIXING_1, 2, st_text);
+
+static const struct snd_kcontrol_new str_mux =
+ SOC_DAPM_ENUM("Right Sidetone", str_enum);
+
+static SOC_ENUM_SINGLE_DECL(stl_enum,
+ WM8962_DAC_DSP_MIXING_2, 2, st_text);
+
+static const struct snd_kcontrol_new stl_mux =
+ SOC_DAPM_ENUM("Left Sidetone", stl_enum);
+
+static const char *outmux_text[] = { "DAC", "Mixer" };
+
+static SOC_ENUM_SINGLE_DECL(spkoutr_enum,
+ WM8962_SPEAKER_MIXER_2, 7, outmux_text);
+
+static const struct snd_kcontrol_new spkoutr_mux =
+ SOC_DAPM_ENUM("SPKOUTR Mux", spkoutr_enum);
+
+static SOC_ENUM_SINGLE_DECL(spkoutl_enum,
+ WM8962_SPEAKER_MIXER_1, 7, outmux_text);
+
+static const struct snd_kcontrol_new spkoutl_mux =
+ SOC_DAPM_ENUM("SPKOUTL Mux", spkoutl_enum);
+
+static SOC_ENUM_SINGLE_DECL(hpoutr_enum,
+ WM8962_HEADPHONE_MIXER_2, 7, outmux_text);
+
+static const struct snd_kcontrol_new hpoutr_mux =
+ SOC_DAPM_ENUM("HPOUTR Mux", hpoutr_enum);
+
+static SOC_ENUM_SINGLE_DECL(hpoutl_enum,
+ WM8962_HEADPHONE_MIXER_1, 7, outmux_text);
+
+static const struct snd_kcontrol_new hpoutl_mux =
+ SOC_DAPM_ENUM("HPOUTL Mux", hpoutl_enum);
+
+static const struct snd_kcontrol_new inpgal[] = {
+SOC_DAPM_SINGLE("IN1L Switch", WM8962_LEFT_INPUT_PGA_CONTROL, 3, 1, 0),
+SOC_DAPM_SINGLE("IN2L Switch", WM8962_LEFT_INPUT_PGA_CONTROL, 2, 1, 0),
+SOC_DAPM_SINGLE("IN3L Switch", WM8962_LEFT_INPUT_PGA_CONTROL, 1, 1, 0),
+SOC_DAPM_SINGLE("IN4L Switch", WM8962_LEFT_INPUT_PGA_CONTROL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new inpgar[] = {
+SOC_DAPM_SINGLE("IN1R Switch", WM8962_RIGHT_INPUT_PGA_CONTROL, 3, 1, 0),
+SOC_DAPM_SINGLE("IN2R Switch", WM8962_RIGHT_INPUT_PGA_CONTROL, 2, 1, 0),
+SOC_DAPM_SINGLE("IN3R Switch", WM8962_RIGHT_INPUT_PGA_CONTROL, 1, 1, 0),
+SOC_DAPM_SINGLE("IN4R Switch", WM8962_RIGHT_INPUT_PGA_CONTROL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixinl[] = {
+SOC_DAPM_SINGLE("IN2L Switch", WM8962_INPUT_MIXER_CONTROL_2, 5, 1, 0),
+SOC_DAPM_SINGLE("IN3L Switch", WM8962_INPUT_MIXER_CONTROL_2, 4, 1, 0),
+SOC_DAPM_SINGLE("PGA Switch", WM8962_INPUT_MIXER_CONTROL_2, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixinr[] = {
+SOC_DAPM_SINGLE("IN2R Switch", WM8962_INPUT_MIXER_CONTROL_2, 2, 1, 0),
+SOC_DAPM_SINGLE("IN3R Switch", WM8962_INPUT_MIXER_CONTROL_2, 1, 1, 0),
+SOC_DAPM_SINGLE("PGA Switch", WM8962_INPUT_MIXER_CONTROL_2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new hpmixl[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8962_HEADPHONE_MIXER_1, 5, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8962_HEADPHONE_MIXER_1, 4, 1, 0),
+SOC_DAPM_SINGLE("MIXINL Switch", WM8962_HEADPHONE_MIXER_1, 3, 1, 0),
+SOC_DAPM_SINGLE("MIXINR Switch", WM8962_HEADPHONE_MIXER_1, 2, 1, 0),
+SOC_DAPM_SINGLE("IN4L Switch", WM8962_HEADPHONE_MIXER_1, 1, 1, 0),
+SOC_DAPM_SINGLE("IN4R Switch", WM8962_HEADPHONE_MIXER_1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new hpmixr[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8962_HEADPHONE_MIXER_2, 5, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8962_HEADPHONE_MIXER_2, 4, 1, 0),
+SOC_DAPM_SINGLE("MIXINL Switch", WM8962_HEADPHONE_MIXER_2, 3, 1, 0),
+SOC_DAPM_SINGLE("MIXINR Switch", WM8962_HEADPHONE_MIXER_2, 2, 1, 0),
+SOC_DAPM_SINGLE("IN4L Switch", WM8962_HEADPHONE_MIXER_2, 1, 1, 0),
+SOC_DAPM_SINGLE("IN4R Switch", WM8962_HEADPHONE_MIXER_2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new spkmixl[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8962_SPEAKER_MIXER_1, 5, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8962_SPEAKER_MIXER_1, 4, 1, 0),
+SOC_DAPM_SINGLE("MIXINL Switch", WM8962_SPEAKER_MIXER_1, 3, 1, 0),
+SOC_DAPM_SINGLE("MIXINR Switch", WM8962_SPEAKER_MIXER_1, 2, 1, 0),
+SOC_DAPM_SINGLE("IN4L Switch", WM8962_SPEAKER_MIXER_1, 1, 1, 0),
+SOC_DAPM_SINGLE("IN4R Switch", WM8962_SPEAKER_MIXER_1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new spkmixr[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8962_SPEAKER_MIXER_2, 5, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8962_SPEAKER_MIXER_2, 4, 1, 0),
+SOC_DAPM_SINGLE("MIXINL Switch", WM8962_SPEAKER_MIXER_2, 3, 1, 0),
+SOC_DAPM_SINGLE("MIXINR Switch", WM8962_SPEAKER_MIXER_2, 2, 1, 0),
+SOC_DAPM_SINGLE("IN4L Switch", WM8962_SPEAKER_MIXER_2, 1, 1, 0),
+SOC_DAPM_SINGLE("IN4R Switch", WM8962_SPEAKER_MIXER_2, 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8962_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+SND_SOC_DAPM_INPUT("IN3L"),
+SND_SOC_DAPM_INPUT("IN3R"),
+SND_SOC_DAPM_INPUT("IN4L"),
+SND_SOC_DAPM_INPUT("IN4R"),
+SND_SOC_DAPM_SIGGEN("Beep"),
+SND_SOC_DAPM_INPUT("DMICDAT"),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS", WM8962_PWR_MGMT_1, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("Class G", WM8962_CHARGE_PUMP_B, 0, 1, NULL, 0),
+SND_SOC_DAPM_SUPPLY("SYSCLK", WM8962_CLOCKING2, 5, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("Charge Pump", WM8962_CHARGE_PUMP_1, 0, 0, cp_event,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_SUPPLY("TOCLK", WM8962_ADDITIONAL_CONTROL_1, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("DSP2", 1, WM8962_DSP2_POWER_MANAGEMENT,
+ WM8962_DSP2_ENA_SHIFT, 0, dsp2_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_SUPPLY("TEMP_HP", WM8962_ADDITIONAL_CONTROL_4, 2, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("TEMP_SPK", WM8962_ADDITIONAL_CONTROL_4, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("INPGAL", WM8962_LEFT_INPUT_PGA_CONTROL, 4, 0,
+ inpgal, ARRAY_SIZE(inpgal)),
+SND_SOC_DAPM_MIXER("INPGAR", WM8962_RIGHT_INPUT_PGA_CONTROL, 4, 0,
+ inpgar, ARRAY_SIZE(inpgar)),
+SND_SOC_DAPM_MIXER("MIXINL", WM8962_PWR_MGMT_1, 5, 0,
+ mixinl, ARRAY_SIZE(mixinl)),
+SND_SOC_DAPM_MIXER("MIXINR", WM8962_PWR_MGMT_1, 4, 0,
+ mixinr, ARRAY_SIZE(mixinr)),
+
+SND_SOC_DAPM_AIF_IN("DMIC_ENA", NULL, 0, WM8962_PWR_MGMT_1, 10, 0),
+
+SND_SOC_DAPM_ADC("ADCL", "Capture", WM8962_PWR_MGMT_1, 3, 0),
+SND_SOC_DAPM_ADC("ADCR", "Capture", WM8962_PWR_MGMT_1, 2, 0),
+
+SND_SOC_DAPM_MUX("STL", SND_SOC_NOPM, 0, 0, &stl_mux),
+SND_SOC_DAPM_MUX("STR", SND_SOC_NOPM, 0, 0, &str_mux),
+
+SND_SOC_DAPM_DAC("DACL", "Playback", WM8962_PWR_MGMT_2, 8, 0),
+SND_SOC_DAPM_DAC("DACR", "Playback", WM8962_PWR_MGMT_2, 7, 0),
+
+SND_SOC_DAPM_PGA("Left Bypass", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Bypass", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("HPMIXL", WM8962_MIXER_ENABLES, 3, 0,
+ hpmixl, ARRAY_SIZE(hpmixl)),
+SND_SOC_DAPM_MIXER("HPMIXR", WM8962_MIXER_ENABLES, 2, 0,
+ hpmixr, ARRAY_SIZE(hpmixr)),
+
+SND_SOC_DAPM_MUX_E("HPOUTL PGA", WM8962_PWR_MGMT_2, 6, 0, &hpoutl_mux,
+ out_pga_event, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_MUX_E("HPOUTR PGA", WM8962_PWR_MGMT_2, 5, 0, &hpoutr_mux,
+ out_pga_event, SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA_E("HPOUT", SND_SOC_NOPM, 0, 0, NULL, 0, hp_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+};
+
+static const struct snd_soc_dapm_widget wm8962_dapm_spk_mono_widgets[] = {
+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8962_MIXER_ENABLES, 1, 0,
+ spkmixl, ARRAY_SIZE(spkmixl)),
+SND_SOC_DAPM_MUX_E("Speaker PGA", WM8962_PWR_MGMT_2, 4, 0, &spkoutl_mux,
+ out_pga_event, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA("Speaker Output", WM8962_CLASS_D_CONTROL_1, 7, 0, NULL, 0),
+SND_SOC_DAPM_OUTPUT("SPKOUT"),
+};
+
+static const struct snd_soc_dapm_widget wm8962_dapm_spk_stereo_widgets[] = {
+SND_SOC_DAPM_MIXER("SPKOUTL Mixer", WM8962_MIXER_ENABLES, 1, 0,
+ spkmixl, ARRAY_SIZE(spkmixl)),
+SND_SOC_DAPM_MIXER("SPKOUTR Mixer", WM8962_MIXER_ENABLES, 0, 0,
+ spkmixr, ARRAY_SIZE(spkmixr)),
+
+SND_SOC_DAPM_MUX_E("SPKOUTL PGA", WM8962_PWR_MGMT_2, 4, 0, &spkoutl_mux,
+ out_pga_event, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_MUX_E("SPKOUTR PGA", WM8962_PWR_MGMT_2, 3, 0, &spkoutr_mux,
+ out_pga_event, SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPKOUTR Output", WM8962_CLASS_D_CONTROL_1, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPKOUTL Output", WM8962_CLASS_D_CONTROL_1, 6, 0, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("SPKOUTL"),
+SND_SOC_DAPM_OUTPUT("SPKOUTR"),
+};
+
+static const struct snd_soc_dapm_route wm8962_intercon[] = {
+ { "INPGAL", "IN1L Switch", "IN1L" },
+ { "INPGAL", "IN2L Switch", "IN2L" },
+ { "INPGAL", "IN3L Switch", "IN3L" },
+ { "INPGAL", "IN4L Switch", "IN4L" },
+
+ { "INPGAR", "IN1R Switch", "IN1R" },
+ { "INPGAR", "IN2R Switch", "IN2R" },
+ { "INPGAR", "IN3R Switch", "IN3R" },
+ { "INPGAR", "IN4R Switch", "IN4R" },
+
+ { "MIXINL", "IN2L Switch", "IN2L" },
+ { "MIXINL", "IN3L Switch", "IN3L" },
+ { "MIXINL", "PGA Switch", "INPGAL" },
+
+ { "MIXINR", "IN2R Switch", "IN2R" },
+ { "MIXINR", "IN3R Switch", "IN3R" },
+ { "MIXINR", "PGA Switch", "INPGAR" },
+
+ { "MICBIAS", NULL, "SYSCLK" },
+
+ { "DMIC_ENA", NULL, "DMICDAT" },
+
+ { "ADCL", NULL, "SYSCLK" },
+ { "ADCL", NULL, "TOCLK" },
+ { "ADCL", NULL, "MIXINL" },
+ { "ADCL", NULL, "DMIC_ENA" },
+ { "ADCL", NULL, "DSP2" },
+
+ { "ADCR", NULL, "SYSCLK" },
+ { "ADCR", NULL, "TOCLK" },
+ { "ADCR", NULL, "MIXINR" },
+ { "ADCR", NULL, "DMIC_ENA" },
+ { "ADCR", NULL, "DSP2" },
+
+ { "STL", "Left", "ADCL" },
+ { "STL", "Right", "ADCR" },
+ { "STL", NULL, "Class G" },
+
+ { "STR", "Left", "ADCL" },
+ { "STR", "Right", "ADCR" },
+ { "STR", NULL, "Class G" },
+
+ { "DACL", NULL, "SYSCLK" },
+ { "DACL", NULL, "TOCLK" },
+ { "DACL", NULL, "Beep" },
+ { "DACL", NULL, "STL" },
+ { "DACL", NULL, "DSP2" },
+
+ { "DACR", NULL, "SYSCLK" },
+ { "DACR", NULL, "TOCLK" },
+ { "DACR", NULL, "Beep" },
+ { "DACR", NULL, "STR" },
+ { "DACR", NULL, "DSP2" },
+
+ { "HPMIXL", "IN4L Switch", "IN4L" },
+ { "HPMIXL", "IN4R Switch", "IN4R" },
+ { "HPMIXL", "DACL Switch", "DACL" },
+ { "HPMIXL", "DACR Switch", "DACR" },
+ { "HPMIXL", "MIXINL Switch", "MIXINL" },
+ { "HPMIXL", "MIXINR Switch", "MIXINR" },
+
+ { "HPMIXR", "IN4L Switch", "IN4L" },
+ { "HPMIXR", "IN4R Switch", "IN4R" },
+ { "HPMIXR", "DACL Switch", "DACL" },
+ { "HPMIXR", "DACR Switch", "DACR" },
+ { "HPMIXR", "MIXINL Switch", "MIXINL" },
+ { "HPMIXR", "MIXINR Switch", "MIXINR" },
+
+ { "Left Bypass", NULL, "HPMIXL" },
+ { "Left Bypass", NULL, "Class G" },
+
+ { "Right Bypass", NULL, "HPMIXR" },
+ { "Right Bypass", NULL, "Class G" },
+
+ { "HPOUTL PGA", "Mixer", "Left Bypass" },
+ { "HPOUTL PGA", "DAC", "DACL" },
+
+ { "HPOUTR PGA", "Mixer", "Right Bypass" },
+ { "HPOUTR PGA", "DAC", "DACR" },
+
+ { "HPOUT", NULL, "HPOUTL PGA" },
+ { "HPOUT", NULL, "HPOUTR PGA" },
+ { "HPOUT", NULL, "Charge Pump" },
+ { "HPOUT", NULL, "SYSCLK" },
+ { "HPOUT", NULL, "TOCLK" },
+
+ { "HPOUTL", NULL, "HPOUT" },
+ { "HPOUTR", NULL, "HPOUT" },
+
+ { "HPOUTL", NULL, "TEMP_HP" },
+ { "HPOUTR", NULL, "TEMP_HP" },
+};
+
+static const struct snd_soc_dapm_route wm8962_spk_mono_intercon[] = {
+ { "Speaker Mixer", "IN4L Switch", "IN4L" },
+ { "Speaker Mixer", "IN4R Switch", "IN4R" },
+ { "Speaker Mixer", "DACL Switch", "DACL" },
+ { "Speaker Mixer", "DACR Switch", "DACR" },
+ { "Speaker Mixer", "MIXINL Switch", "MIXINL" },
+ { "Speaker Mixer", "MIXINR Switch", "MIXINR" },
+
+ { "Speaker PGA", "Mixer", "Speaker Mixer" },
+ { "Speaker PGA", "DAC", "DACL" },
+
+ { "Speaker Output", NULL, "Speaker PGA" },
+ { "Speaker Output", NULL, "SYSCLK" },
+ { "Speaker Output", NULL, "TOCLK" },
+ { "Speaker Output", NULL, "TEMP_SPK" },
+
+ { "SPKOUT", NULL, "Speaker Output" },
+};
+
+static const struct snd_soc_dapm_route wm8962_spk_stereo_intercon[] = {
+ { "SPKOUTL Mixer", "IN4L Switch", "IN4L" },
+ { "SPKOUTL Mixer", "IN4R Switch", "IN4R" },
+ { "SPKOUTL Mixer", "DACL Switch", "DACL" },
+ { "SPKOUTL Mixer", "DACR Switch", "DACR" },
+ { "SPKOUTL Mixer", "MIXINL Switch", "MIXINL" },
+ { "SPKOUTL Mixer", "MIXINR Switch", "MIXINR" },
+
+ { "SPKOUTR Mixer", "IN4L Switch", "IN4L" },
+ { "SPKOUTR Mixer", "IN4R Switch", "IN4R" },
+ { "SPKOUTR Mixer", "DACL Switch", "DACL" },
+ { "SPKOUTR Mixer", "DACR Switch", "DACR" },
+ { "SPKOUTR Mixer", "MIXINL Switch", "MIXINL" },
+ { "SPKOUTR Mixer", "MIXINR Switch", "MIXINR" },
+
+ { "SPKOUTL PGA", "Mixer", "SPKOUTL Mixer" },
+ { "SPKOUTL PGA", "DAC", "DACL" },
+
+ { "SPKOUTR PGA", "Mixer", "SPKOUTR Mixer" },
+ { "SPKOUTR PGA", "DAC", "DACR" },
+
+ { "SPKOUTL Output", NULL, "SPKOUTL PGA" },
+ { "SPKOUTL Output", NULL, "SYSCLK" },
+ { "SPKOUTL Output", NULL, "TOCLK" },
+ { "SPKOUTL Output", NULL, "TEMP_SPK" },
+
+ { "SPKOUTR Output", NULL, "SPKOUTR PGA" },
+ { "SPKOUTR Output", NULL, "SYSCLK" },
+ { "SPKOUTR Output", NULL, "TOCLK" },
+ { "SPKOUTR Output", NULL, "TEMP_SPK" },
+
+ { "SPKOUTL", NULL, "SPKOUTL Output" },
+ { "SPKOUTR", NULL, "SPKOUTR Output" },
+};
+
+static int wm8962_add_widgets(struct snd_soc_codec *codec)
+{
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct wm8962_pdata *pdata = &wm8962->pdata;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ snd_soc_add_codec_controls(codec, wm8962_snd_controls,
+ ARRAY_SIZE(wm8962_snd_controls));
+ if (pdata->spk_mono)
+ snd_soc_add_codec_controls(codec, wm8962_spk_mono_controls,
+ ARRAY_SIZE(wm8962_spk_mono_controls));
+ else
+ snd_soc_add_codec_controls(codec, wm8962_spk_stereo_controls,
+ ARRAY_SIZE(wm8962_spk_stereo_controls));
+
+
+ snd_soc_dapm_new_controls(dapm, wm8962_dapm_widgets,
+ ARRAY_SIZE(wm8962_dapm_widgets));
+ if (pdata->spk_mono)
+ snd_soc_dapm_new_controls(dapm, wm8962_dapm_spk_mono_widgets,
+ ARRAY_SIZE(wm8962_dapm_spk_mono_widgets));
+ else
+ snd_soc_dapm_new_controls(dapm, wm8962_dapm_spk_stereo_widgets,
+ ARRAY_SIZE(wm8962_dapm_spk_stereo_widgets));
+
+ snd_soc_dapm_add_routes(dapm, wm8962_intercon,
+ ARRAY_SIZE(wm8962_intercon));
+ if (pdata->spk_mono)
+ snd_soc_dapm_add_routes(dapm, wm8962_spk_mono_intercon,
+ ARRAY_SIZE(wm8962_spk_mono_intercon));
+ else
+ snd_soc_dapm_add_routes(dapm, wm8962_spk_stereo_intercon,
+ ARRAY_SIZE(wm8962_spk_stereo_intercon));
+
+
+ snd_soc_dapm_disable_pin(dapm, "Beep");
+
+ return 0;
+}
+
+/* -1 for reserved values */
+static const int bclk_divs[] = {
+ 1, -1, 2, 3, 4, -1, 6, 8, -1, 12, 16, 24, -1, 32, 32, 32
+};
+
+static const int sysclk_rates[] = {
+ 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536, 3072, 6144
+};
+
+static void wm8962_configure_bclk(struct snd_soc_codec *codec)
+{
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ int dspclk, i;
+ int clocking2 = 0;
+ int clocking4 = 0;
+ int aif2 = 0;
+
+ if (!wm8962->sysclk_rate) {
+ dev_dbg(codec->dev, "No SYSCLK configured\n");
+ return;
+ }
+
+ if (!wm8962->bclk || !wm8962->lrclk) {
+ dev_dbg(codec->dev, "No audio clocks configured\n");
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) {
+ if (sysclk_rates[i] == wm8962->sysclk_rate / wm8962->lrclk) {
+ clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(sysclk_rates)) {
+ dev_err(codec->dev, "Unsupported sysclk ratio %d\n",
+ wm8962->sysclk_rate / wm8962->lrclk);
+ return;
+ }
+
+ dev_dbg(codec->dev, "Selected sysclk ratio %d\n", sysclk_rates[i]);
+
+ snd_soc_update_bits(codec, WM8962_CLOCKING_4,
+ WM8962_SYSCLK_RATE_MASK, clocking4);
+
+ /* DSPCLK_DIV can be only generated correctly after enabling SYSCLK.
+ * So we here provisionally enable it and then disable it afterward
+ * if current bias_level hasn't reached SND_SOC_BIAS_ON.
+ */
+ if (codec->dapm.bias_level != SND_SOC_BIAS_ON)
+ snd_soc_update_bits(codec, WM8962_CLOCKING2,
+ WM8962_SYSCLK_ENA_MASK, WM8962_SYSCLK_ENA);
+
+ dspclk = snd_soc_read(codec, WM8962_CLOCKING1);
+
+ if (codec->dapm.bias_level != SND_SOC_BIAS_ON)
+ snd_soc_update_bits(codec, WM8962_CLOCKING2,
+ WM8962_SYSCLK_ENA_MASK, 0);
+
+ if (dspclk < 0) {
+ dev_err(codec->dev, "Failed to read DSPCLK: %d\n", dspclk);
+ return;
+ }
+
+ dspclk = (dspclk & WM8962_DSPCLK_DIV_MASK) >> WM8962_DSPCLK_DIV_SHIFT;
+ switch (dspclk) {
+ case 0:
+ dspclk = wm8962->sysclk_rate;
+ break;
+ case 1:
+ dspclk = wm8962->sysclk_rate / 2;
+ break;
+ case 2:
+ dspclk = wm8962->sysclk_rate / 4;
+ break;
+ default:
+ dev_warn(codec->dev, "Unknown DSPCLK divisor read back\n");
+ dspclk = wm8962->sysclk;
+ }
+
+ dev_dbg(codec->dev, "DSPCLK is %dHz, BCLK %d\n", dspclk, wm8962->bclk);
+
+ /* We're expecting an exact match */
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ if (bclk_divs[i] < 0)
+ continue;
+
+ if (dspclk / bclk_divs[i] == wm8962->bclk) {
+ dev_dbg(codec->dev, "Selected BCLK_DIV %d for %dHz\n",
+ bclk_divs[i], wm8962->bclk);
+ clocking2 |= i;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(bclk_divs)) {
+ dev_err(codec->dev, "Unsupported BCLK ratio %d\n",
+ dspclk / wm8962->bclk);
+ return;
+ }
+
+ aif2 |= wm8962->bclk / wm8962->lrclk;
+ dev_dbg(codec->dev, "Selected LRCLK divisor %d for %dHz\n",
+ wm8962->bclk / wm8962->lrclk, wm8962->lrclk);
+
+ snd_soc_update_bits(codec, WM8962_CLOCKING2,
+ WM8962_BCLK_DIV_MASK, clocking2);
+ snd_soc_update_bits(codec, WM8962_AUDIO_INTERFACE_2,
+ WM8962_AIF_RATE_MASK, aif2);
+}
+
+static int wm8962_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ if (level == codec->dapm.bias_level)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID 2*50k */
+ snd_soc_update_bits(codec, WM8962_PWR_MGMT_1,
+ WM8962_VMID_SEL_MASK, 0x80);
+
+ wm8962_configure_bclk(codec);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ /* VMID 2*250k */
+ snd_soc_update_bits(codec, WM8962_PWR_MGMT_1,
+ WM8962_VMID_SEL_MASK, 0x100);
+
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ msleep(100);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static const struct {
+ int rate;
+ int reg;
+} sr_vals[] = {
+ { 48000, 0 },
+ { 44100, 0 },
+ { 32000, 1 },
+ { 22050, 2 },
+ { 24000, 2 },
+ { 16000, 3 },
+ { 11025, 4 },
+ { 12000, 4 },
+ { 8000, 5 },
+ { 88200, 6 },
+ { 96000, 6 },
+};
+
+static int wm8962_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ int i;
+ int aif0 = 0;
+ int adctl3 = 0;
+
+ wm8962->bclk = snd_soc_params_to_bclk(params);
+ if (params_channels(params) == 1)
+ wm8962->bclk *= 2;
+
+ wm8962->lrclk = params_rate(params);
+
+ for (i = 0; i < ARRAY_SIZE(sr_vals); i++) {
+ if (sr_vals[i].rate == wm8962->lrclk) {
+ adctl3 |= sr_vals[i].reg;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(sr_vals)) {
+ dev_err(codec->dev, "Unsupported rate %dHz\n", wm8962->lrclk);
+ return -EINVAL;
+ }
+
+ if (wm8962->lrclk % 8000 == 0)
+ adctl3 |= WM8962_SAMPLE_RATE_INT_MODE;
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ aif0 |= 0x4;
+ break;
+ case 24:
+ aif0 |= 0x8;
+ break;
+ case 32:
+ aif0 |= 0xc;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8962_AUDIO_INTERFACE_0,
+ WM8962_WL_MASK, aif0);
+ snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_3,
+ WM8962_SAMPLE_RATE_INT_MODE |
+ WM8962_SAMPLE_RATE_MASK, adctl3);
+
+ dev_dbg(codec->dev, "hw_params set BCLK %dHz LRCLK %dHz\n",
+ wm8962->bclk, wm8962->lrclk);
+
+ if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
+ wm8962_configure_bclk(codec);
+
+ return 0;
+}
+
+static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ int src;
+
+ switch (clk_id) {
+ case WM8962_SYSCLK_MCLK:
+ wm8962->sysclk = WM8962_SYSCLK_MCLK;
+ src = 0;
+ break;
+ case WM8962_SYSCLK_FLL:
+ wm8962->sysclk = WM8962_SYSCLK_FLL;
+ src = 1 << WM8962_SYSCLK_SRC_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8962_CLOCKING2, WM8962_SYSCLK_SRC_MASK,
+ src);
+
+ wm8962->sysclk_rate = freq;
+
+ return 0;
+}
+
+static int wm8962_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int aif0 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_B:
+ aif0 |= WM8962_LRCLK_INV | 3;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif0 |= 3;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif0 |= 1;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aif0 |= 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif0 |= WM8962_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif0 |= WM8962_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif0 |= WM8962_BCLK_INV | WM8962_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif0 |= WM8962_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8962_AUDIO_INTERFACE_0,
+ WM8962_FMT_MASK | WM8962_BCLK_INV | WM8962_MSTR |
+ WM8962_LRCLK_INV, aif0);
+
+ return 0;
+}
+
+struct _fll_div {
+ u16 fll_fratio;
+ u16 fll_outdiv;
+ u16 fll_refclk_div;
+ u16 n;
+ u16 theta;
+ u16 lambda;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fll_fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ unsigned int target;
+ unsigned int div;
+ unsigned int fratio, gcd_fll;
+ int i;
+
+ /* Fref must be <=13.5MHz */
+ div = 1;
+ fll_div->fll_refclk_div = 0;
+ while ((Fref / div) > 13500000) {
+ div *= 2;
+ fll_div->fll_refclk_div++;
+
+ if (div > 4) {
+ pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+ }
+
+ pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout);
+
+ /* Apply the division for our remaining calculations */
+ Fref /= div;
+
+ /* Fvco should be 90-100MHz; don't check the upper bound */
+ div = 2;
+ while (Fout * div < 90000000) {
+ div++;
+ if (div > 64) {
+ pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ target = Fout * div;
+ fll_div->fll_outdiv = div - 1;
+
+ pr_debug("FLL Fvco=%dHz\n", target);
+
+ /* Find an appropriate FLL_FRATIO and factor it out of the target */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+ fratio = fll_fratios[i].ratio;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_fratios)) {
+ pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+ return -EINVAL;
+ }
+
+ fll_div->n = target / (fratio * Fref);
+
+ if (target % Fref == 0) {
+ fll_div->theta = 0;
+ fll_div->lambda = 0;
+ } else {
+ gcd_fll = gcd(target, fratio * Fref);
+
+ fll_div->theta = (target - (fll_div->n * fratio * Fref))
+ / gcd_fll;
+ fll_div->lambda = (fratio * Fref) / gcd_fll;
+ }
+
+ pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n",
+ fll_div->n, fll_div->theta, fll_div->lambda);
+ pr_debug("FLL_FRATIO=%x FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n",
+ fll_div->fll_fratio, fll_div->fll_outdiv,
+ fll_div->fll_refclk_div);
+
+ return 0;
+}
+
+static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct _fll_div fll_div;
+ unsigned long timeout;
+ int ret;
+ int fll1 = 0;
+
+ /* Any change? */
+ if (source == wm8962->fll_src && Fref == wm8962->fll_fref &&
+ Fout == wm8962->fll_fout)
+ return 0;
+
+ if (Fout == 0) {
+ dev_dbg(codec->dev, "FLL disabled\n");
+
+ wm8962->fll_fref = 0;
+ wm8962->fll_fout = 0;
+
+ snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
+ WM8962_FLL_ENA, 0);
+
+ pm_runtime_put(codec->dev);
+
+ return 0;
+ }
+
+ ret = fll_factors(&fll_div, Fref, Fout);
+ if (ret != 0)
+ return ret;
+
+ /* Parameters good, disable so we can reprogram */
+ snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1, WM8962_FLL_ENA, 0);
+
+ switch (fll_id) {
+ case WM8962_FLL_MCLK:
+ case WM8962_FLL_BCLK:
+ case WM8962_FLL_OSC:
+ fll1 |= (fll_id - 1) << WM8962_FLL_REFCLK_SRC_SHIFT;
+ break;
+ case WM8962_FLL_INT:
+ snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
+ WM8962_FLL_OSC_ENA, WM8962_FLL_OSC_ENA);
+ snd_soc_update_bits(codec, WM8962_FLL_CONTROL_5,
+ WM8962_FLL_FRC_NCO, WM8962_FLL_FRC_NCO);
+ break;
+ default:
+ dev_err(codec->dev, "Unknown FLL source %d\n", ret);
+ return -EINVAL;
+ }
+
+ if (fll_div.theta || fll_div.lambda)
+ fll1 |= WM8962_FLL_FRAC;
+
+ /* Stop the FLL while we reconfigure */
+ snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1, WM8962_FLL_ENA, 0);
+
+ snd_soc_update_bits(codec, WM8962_FLL_CONTROL_2,
+ WM8962_FLL_OUTDIV_MASK |
+ WM8962_FLL_REFCLK_DIV_MASK,
+ (fll_div.fll_outdiv << WM8962_FLL_OUTDIV_SHIFT) |
+ (fll_div.fll_refclk_div));
+
+ snd_soc_update_bits(codec, WM8962_FLL_CONTROL_3,
+ WM8962_FLL_FRATIO_MASK, fll_div.fll_fratio);
+
+ snd_soc_write(codec, WM8962_FLL_CONTROL_6, fll_div.theta);
+ snd_soc_write(codec, WM8962_FLL_CONTROL_7, fll_div.lambda);
+ snd_soc_write(codec, WM8962_FLL_CONTROL_8, fll_div.n);
+
+ reinit_completion(&wm8962->fll_lock);
+
+ ret = pm_runtime_get_sync(codec->dev);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to resume device: %d\n", ret);
+ return ret;
+ }
+
+ snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
+ WM8962_FLL_FRAC | WM8962_FLL_REFCLK_SRC_MASK |
+ WM8962_FLL_ENA, fll1 | WM8962_FLL_ENA);
+
+ dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+
+ /* This should be a massive overestimate but go even
+ * higher if we'll error out
+ */
+ if (wm8962->irq)
+ timeout = msecs_to_jiffies(5);
+ else
+ timeout = msecs_to_jiffies(1);
+
+ timeout = wait_for_completion_timeout(&wm8962->fll_lock,
+ timeout);
+
+ if (timeout == 0 && wm8962->irq) {
+ dev_err(codec->dev, "FLL lock timed out");
+ snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
+ WM8962_FLL_ENA, 0);
+ pm_runtime_put(codec->dev);
+ return -ETIMEDOUT;
+ }
+
+ wm8962->fll_fref = Fref;
+ wm8962->fll_fout = Fout;
+ wm8962->fll_src = source;
+
+ return 0;
+}
+
+static int wm8962_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int val, ret;
+
+ if (mute)
+ val = WM8962_DAC_MUTE | WM8962_DAC_MUTE_ALT;
+ else
+ val = 0;
+
+ /**
+ * The DAC mute bit is mirrored in two registers, update both to keep
+ * the register cache consistent.
+ */
+ ret = snd_soc_update_bits(codec, WM8962_CLASS_D_CONTROL_1,
+ WM8962_DAC_MUTE_ALT, val);
+ if (ret < 0)
+ return ret;
+
+ return snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
+ WM8962_DAC_MUTE, val);
+}
+
+#define WM8962_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8962_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8962_dai_ops = {
+ .hw_params = wm8962_hw_params,
+ .set_sysclk = wm8962_set_dai_sysclk,
+ .set_fmt = wm8962_set_dai_fmt,
+ .digital_mute = wm8962_mute,
+};
+
+static struct snd_soc_dai_driver wm8962_dai = {
+ .name = "wm8962",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8962_RATES,
+ .formats = WM8962_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8962_RATES,
+ .formats = WM8962_FORMATS,
+ },
+ .ops = &wm8962_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static void wm8962_mic_work(struct work_struct *work)
+{
+ struct wm8962_priv *wm8962 = container_of(work,
+ struct wm8962_priv,
+ mic_work.work);
+ struct snd_soc_codec *codec = wm8962->codec;
+ int status = 0;
+ int irq_pol = 0;
+ int reg;
+
+ reg = snd_soc_read(codec, WM8962_ADDITIONAL_CONTROL_4);
+
+ if (reg & WM8962_MICDET_STS) {
+ status |= SND_JACK_MICROPHONE;
+ irq_pol |= WM8962_MICD_IRQ_POL;
+ }
+
+ if (reg & WM8962_MICSHORT_STS) {
+ status |= SND_JACK_BTN_0;
+ irq_pol |= WM8962_MICSCD_IRQ_POL;
+ }
+
+ snd_soc_jack_report(wm8962->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_BTN_0);
+
+ snd_soc_update_bits(codec, WM8962_MICINT_SOURCE_POL,
+ WM8962_MICSCD_IRQ_POL |
+ WM8962_MICD_IRQ_POL, irq_pol);
+}
+
+static irqreturn_t wm8962_irq(int irq, void *data)
+{
+ struct device *dev = data;
+ struct wm8962_priv *wm8962 = dev_get_drvdata(dev);
+ unsigned int mask;
+ unsigned int active;
+ int reg, ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to resume: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ ret = regmap_read(wm8962->regmap, WM8962_INTERRUPT_STATUS_2_MASK,
+ &mask);
+ if (ret != 0) {
+ pm_runtime_put(dev);
+ dev_err(dev, "Failed to read interrupt mask: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ ret = regmap_read(wm8962->regmap, WM8962_INTERRUPT_STATUS_2, &active);
+ if (ret != 0) {
+ pm_runtime_put(dev);
+ dev_err(dev, "Failed to read interrupt: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ active &= ~mask;
+
+ if (!active) {
+ pm_runtime_put(dev);
+ return IRQ_NONE;
+ }
+
+ /* Acknowledge the interrupts */
+ ret = regmap_write(wm8962->regmap, WM8962_INTERRUPT_STATUS_2, active);
+ if (ret != 0)
+ dev_warn(dev, "Failed to ack interrupt: %d\n", ret);
+
+ if (active & WM8962_FLL_LOCK_EINT) {
+ dev_dbg(dev, "FLL locked\n");
+ complete(&wm8962->fll_lock);
+ }
+
+ if (active & WM8962_FIFOS_ERR_EINT)
+ dev_err(dev, "FIFO error\n");
+
+ if (active & WM8962_TEMP_SHUT_EINT) {
+ dev_crit(dev, "Thermal shutdown\n");
+
+ ret = regmap_read(wm8962->regmap,
+ WM8962_THERMAL_SHUTDOWN_STATUS, &reg);
+ if (ret != 0) {
+ dev_warn(dev, "Failed to read thermal status: %d\n",
+ ret);
+ reg = 0;
+ }
+
+ if (reg & WM8962_TEMP_ERR_HP)
+ dev_crit(dev, "Headphone thermal error\n");
+ if (reg & WM8962_TEMP_WARN_HP)
+ dev_crit(dev, "Headphone thermal warning\n");
+ if (reg & WM8962_TEMP_ERR_SPK)
+ dev_crit(dev, "Speaker thermal error\n");
+ if (reg & WM8962_TEMP_WARN_SPK)
+ dev_crit(dev, "Speaker thermal warning\n");
+ }
+
+ if (active & (WM8962_MICSCD_EINT | WM8962_MICD_EINT)) {
+ dev_dbg(dev, "Microphone event detected\n");
+
+#ifndef CONFIG_SND_SOC_WM8962_MODULE
+ trace_snd_soc_jack_irq(dev_name(dev));
+#endif
+
+ pm_wakeup_event(dev, 300);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8962->mic_work,
+ msecs_to_jiffies(250));
+ }
+
+ pm_runtime_put(dev);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * wm8962_mic_detect - Enable microphone detection via the WM8962 IRQ
+ *
+ * @codec: WM8962 codec
+ * @jack: jack to report detection events on
+ *
+ * Enable microphone detection via IRQ on the WM8962. If GPIOs are
+ * being used to bring out signals to the processor then only platform
+ * data configuration is needed for WM8962 and processor GPIOs should
+ * be configured using snd_soc_jack_add_gpios() instead.
+ *
+ * If no jack is supplied detection will be disabled.
+ */
+int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+{
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int irq_mask, enable;
+
+ wm8962->jack = jack;
+ if (jack) {
+ irq_mask = 0;
+ enable = WM8962_MICDET_ENA;
+ } else {
+ irq_mask = WM8962_MICD_EINT | WM8962_MICSCD_EINT;
+ enable = 0;
+ }
+
+ snd_soc_update_bits(codec, WM8962_INTERRUPT_STATUS_2_MASK,
+ WM8962_MICD_EINT | WM8962_MICSCD_EINT, irq_mask);
+ snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_4,
+ WM8962_MICDET_ENA, enable);
+
+ /* Send an initial empty report */
+ snd_soc_jack_report(wm8962->jack, 0,
+ SND_JACK_MICROPHONE | SND_JACK_BTN_0);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (jack) {
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "SYSCLK");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS");
+ } else {
+ snd_soc_dapm_disable_pin_unlocked(dapm, "SYSCLK");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS");
+ }
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8962_mic_detect);
+
+static int beep_rates[] = {
+ 500, 1000, 2000, 4000,
+};
+
+static void wm8962_beep_work(struct work_struct *work)
+{
+ struct wm8962_priv *wm8962 =
+ container_of(work, struct wm8962_priv, beep_work);
+ struct snd_soc_codec *codec = wm8962->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int i;
+ int reg = 0;
+ int best = 0;
+
+ if (wm8962->beep_rate) {
+ for (i = 0; i < ARRAY_SIZE(beep_rates); i++) {
+ if (abs(wm8962->beep_rate - beep_rates[i]) <
+ abs(wm8962->beep_rate - beep_rates[best]))
+ best = i;
+ }
+
+ dev_dbg(codec->dev, "Set beep rate %dHz for requested %dHz\n",
+ beep_rates[best], wm8962->beep_rate);
+
+ reg = WM8962_BEEP_ENA | (best << WM8962_BEEP_RATE_SHIFT);
+
+ snd_soc_dapm_enable_pin(dapm, "Beep");
+ } else {
+ dev_dbg(codec->dev, "Disabling beep\n");
+ snd_soc_dapm_disable_pin(dapm, "Beep");
+ }
+
+ snd_soc_update_bits(codec, WM8962_BEEP_GENERATOR_1,
+ WM8962_BEEP_ENA | WM8962_BEEP_RATE_MASK, reg);
+
+ snd_soc_dapm_sync(dapm);
+}
+
+/* For usability define a way of injecting beep events for the device -
+ * many systems will not have a keyboard.
+ */
+static int wm8962_beep_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int hz)
+{
+ struct snd_soc_codec *codec = input_get_drvdata(dev);
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "Beep event %x %x\n", code, hz);
+
+ switch (code) {
+ case SND_BELL:
+ if (hz)
+ hz = 1000;
+ case SND_TONE:
+ break;
+ default:
+ return -1;
+ }
+
+ /* Kick the beep from a workqueue */
+ wm8962->beep_rate = hz;
+ schedule_work(&wm8962->beep_work);
+ return 0;
+}
+
+static ssize_t wm8962_beep_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct wm8962_priv *wm8962 = dev_get_drvdata(dev);
+ long int time;
+ int ret;
+
+ ret = kstrtol(buf, 10, &time);
+ if (ret != 0)
+ return ret;
+
+ input_event(wm8962->beep, EV_SND, SND_TONE, time);
+
+ return count;
+}
+
+static DEVICE_ATTR(beep, 0200, NULL, wm8962_beep_set);
+
+static void wm8962_init_beep(struct snd_soc_codec *codec)
+{
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ wm8962->beep = devm_input_allocate_device(codec->dev);
+ if (!wm8962->beep) {
+ dev_err(codec->dev, "Failed to allocate beep device\n");
+ return;
+ }
+
+ INIT_WORK(&wm8962->beep_work, wm8962_beep_work);
+ wm8962->beep_rate = 0;
+
+ wm8962->beep->name = "WM8962 Beep Generator";
+ wm8962->beep->phys = dev_name(codec->dev);
+ wm8962->beep->id.bustype = BUS_I2C;
+
+ wm8962->beep->evbit[0] = BIT_MASK(EV_SND);
+ wm8962->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+ wm8962->beep->event = wm8962_beep_event;
+ wm8962->beep->dev.parent = codec->dev;
+ input_set_drvdata(wm8962->beep, codec);
+
+ ret = input_register_device(wm8962->beep);
+ if (ret != 0) {
+ wm8962->beep = NULL;
+ dev_err(codec->dev, "Failed to register beep device\n");
+ }
+
+ ret = device_create_file(codec->dev, &dev_attr_beep);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to create keyclick file: %d\n",
+ ret);
+ }
+}
+
+static void wm8962_free_beep(struct snd_soc_codec *codec)
+{
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+ device_remove_file(codec->dev, &dev_attr_beep);
+ cancel_work_sync(&wm8962->beep_work);
+ wm8962->beep = NULL;
+
+ snd_soc_update_bits(codec, WM8962_BEEP_GENERATOR_1, WM8962_BEEP_ENA,0);
+}
+
+static void wm8962_set_gpio_mode(struct wm8962_priv *wm8962, int gpio)
+{
+ int mask = 0;
+ int val = 0;
+
+ /* Some of the GPIOs are behind MFP configuration and need to
+ * be put into GPIO mode. */
+ switch (gpio) {
+ case 2:
+ mask = WM8962_CLKOUT2_SEL_MASK;
+ val = 1 << WM8962_CLKOUT2_SEL_SHIFT;
+ break;
+ case 3:
+ mask = WM8962_CLKOUT3_SEL_MASK;
+ val = 1 << WM8962_CLKOUT3_SEL_SHIFT;
+ break;
+ default:
+ break;
+ }
+
+ if (mask)
+ regmap_update_bits(wm8962->regmap, WM8962_ANALOGUE_CLOCKING1,
+ mask, val);
+}
+
+#ifdef CONFIG_GPIOLIB
+static inline struct wm8962_priv *gpio_to_wm8962(struct gpio_chip *chip)
+{
+ return container_of(chip, struct wm8962_priv, gpio_chip);
+}
+
+static int wm8962_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8962_priv *wm8962 = gpio_to_wm8962(chip);
+
+ /* The WM8962 GPIOs aren't linearly numbered. For simplicity
+ * we export linear numbers and error out if the unsupported
+ * ones are requsted.
+ */
+ switch (offset + 1) {
+ case 2:
+ case 3:
+ case 5:
+ case 6:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8962_set_gpio_mode(wm8962, offset + 1);
+
+ return 0;
+}
+
+static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct wm8962_priv *wm8962 = gpio_to_wm8962(chip);
+ struct snd_soc_codec *codec = wm8962->codec;
+
+ snd_soc_update_bits(codec, WM8962_GPIO_BASE + offset,
+ WM8962_GP2_LVL, !!value << WM8962_GP2_LVL_SHIFT);
+}
+
+static int wm8962_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct wm8962_priv *wm8962 = gpio_to_wm8962(chip);
+ struct snd_soc_codec *codec = wm8962->codec;
+ int ret, val;
+
+ /* Force function 1 (logic output) */
+ val = (1 << WM8962_GP2_FN_SHIFT) | (value << WM8962_GP2_LVL_SHIFT);
+
+ ret = snd_soc_update_bits(codec, WM8962_GPIO_BASE + offset,
+ WM8962_GP2_FN_MASK | WM8962_GP2_LVL, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct gpio_chip wm8962_template_chip = {
+ .label = "wm8962",
+ .owner = THIS_MODULE,
+ .request = wm8962_gpio_request,
+ .direction_output = wm8962_gpio_direction_out,
+ .set = wm8962_gpio_set,
+ .can_sleep = 1,
+};
+
+static void wm8962_init_gpio(struct snd_soc_codec *codec)
+{
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct wm8962_pdata *pdata = &wm8962->pdata;
+ int ret;
+
+ wm8962->gpio_chip = wm8962_template_chip;
+ wm8962->gpio_chip.ngpio = WM8962_MAX_GPIO;
+ wm8962->gpio_chip.dev = codec->dev;
+
+ if (pdata->gpio_base)
+ wm8962->gpio_chip.base = pdata->gpio_base;
+ else
+ wm8962->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&wm8962->gpio_chip);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
+static void wm8962_free_gpio(struct snd_soc_codec *codec)
+{
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+ gpiochip_remove(&wm8962->gpio_chip);
+}
+#else
+static void wm8962_init_gpio(struct snd_soc_codec *codec)
+{
+}
+
+static void wm8962_free_gpio(struct snd_soc_codec *codec)
+{
+}
+#endif
+
+static int wm8962_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ int i;
+ bool dmicclk, dmicdat;
+
+ wm8962->codec = codec;
+
+ wm8962->disable_nb[0].notifier_call = wm8962_regulator_event_0;
+ wm8962->disable_nb[1].notifier_call = wm8962_regulator_event_1;
+ wm8962->disable_nb[2].notifier_call = wm8962_regulator_event_2;
+ wm8962->disable_nb[3].notifier_call = wm8962_regulator_event_3;
+ wm8962->disable_nb[4].notifier_call = wm8962_regulator_event_4;
+ wm8962->disable_nb[5].notifier_call = wm8962_regulator_event_5;
+ wm8962->disable_nb[6].notifier_call = wm8962_regulator_event_6;
+ wm8962->disable_nb[7].notifier_call = wm8962_regulator_event_7;
+
+ /* This should really be moved into the regulator core */
+ for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) {
+ ret = regulator_register_notifier(wm8962->supplies[i].consumer,
+ &wm8962->disable_nb[i]);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ }
+ }
+
+ wm8962_add_widgets(codec);
+
+ /* Save boards having to disable DMIC when not in use */
+ dmicclk = false;
+ dmicdat = false;
+ for (i = 0; i < WM8962_MAX_GPIO; i++) {
+ switch (snd_soc_read(codec, WM8962_GPIO_BASE + i)
+ & WM8962_GP2_FN_MASK) {
+ case WM8962_GPIO_FN_DMICCLK:
+ dmicclk = true;
+ break;
+ case WM8962_GPIO_FN_DMICDAT:
+ dmicdat = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (!dmicclk || !dmicdat) {
+ dev_dbg(codec->dev, "DMIC not in use, disabling\n");
+ snd_soc_dapm_nc_pin(&codec->dapm, "DMICDAT");
+ }
+ if (dmicclk != dmicdat)
+ dev_warn(codec->dev, "DMIC GPIOs partially configured\n");
+
+ wm8962_init_beep(codec);
+ wm8962_init_gpio(codec);
+
+ return 0;
+}
+
+static int wm8962_remove(struct snd_soc_codec *codec)
+{
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ cancel_delayed_work_sync(&wm8962->mic_work);
+
+ wm8962_free_gpio(codec);
+ wm8962_free_beep(codec);
+ for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++)
+ regulator_unregister_notifier(wm8962->supplies[i].consumer,
+ &wm8962->disable_nb[i]);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8962 = {
+ .probe = wm8962_probe,
+ .remove = wm8962_remove,
+ .set_bias_level = wm8962_set_bias_level,
+ .set_pll = wm8962_set_fll,
+ .idle_bias_off = true,
+};
+
+/* Improve power consumption for IN4 DC measurement mode */
+static const struct reg_default wm8962_dc_measure[] = {
+ { 0xfd, 0x1 },
+ { 0xcc, 0x40 },
+ { 0xfd, 0 },
+};
+
+static const struct regmap_config wm8962_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+
+ .max_register = WM8962_MAX_REGISTER,
+ .reg_defaults = wm8962_reg,
+ .num_reg_defaults = ARRAY_SIZE(wm8962_reg),
+ .volatile_reg = wm8962_volatile_register,
+ .readable_reg = wm8962_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int wm8962_set_pdata_from_of(struct i2c_client *i2c,
+ struct wm8962_pdata *pdata)
+{
+ const struct device_node *np = i2c->dev.of_node;
+ u32 val32;
+ int i;
+
+ if (of_property_read_bool(np, "spk-mono"))
+ pdata->spk_mono = true;
+
+ if (of_property_read_u32(np, "mic-cfg", &val32) >= 0)
+ pdata->mic_cfg = val32;
+
+ if (of_property_read_u32_array(np, "gpio-cfg", pdata->gpio_init,
+ ARRAY_SIZE(pdata->gpio_init)) >= 0)
+ for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++) {
+ /*
+ * The range of GPIO register value is [0x0, 0xffff]
+ * While the default value of each register is 0x0
+ * Any other value will be regarded as default value
+ */
+ if (pdata->gpio_init[i] > 0xffff)
+ pdata->gpio_init[i] = 0x0;
+ }
+
+ pdata->mclk = devm_clk_get(&i2c->dev, NULL);
+
+ return 0;
+}
+
+static int wm8962_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8962_pdata *pdata = dev_get_platdata(&i2c->dev);
+ struct wm8962_priv *wm8962;
+ unsigned int reg;
+ int ret, i, irq_pol, trigger;
+
+ wm8962 = devm_kzalloc(&i2c->dev, sizeof(*wm8962), GFP_KERNEL);
+ if (wm8962 == NULL)
+ return -ENOMEM;
+
+ mutex_init(&wm8962->dsp2_ena_lock);
+
+ i2c_set_clientdata(i2c, wm8962);
+
+ INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work);
+ init_completion(&wm8962->fll_lock);
+ wm8962->irq = i2c->irq;
+
+ /* If platform data was supplied, update the default data in priv */
+ if (pdata) {
+ memcpy(&wm8962->pdata, pdata, sizeof(struct wm8962_pdata));
+ } else if (i2c->dev.of_node) {
+ ret = wm8962_set_pdata_from_of(i2c, &wm8962->pdata);
+ if (ret != 0)
+ return ret;
+ }
+
+ /* Mark the mclk pointer to NULL if no mclk assigned */
+ if (IS_ERR(wm8962->pdata.mclk)) {
+ /* But do not ignore the request for probe defer */
+ if (PTR_ERR(wm8962->pdata.mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ wm8962->pdata.mclk = NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++)
+ wm8962->supplies[i].supply = wm8962_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8962->supplies),
+ wm8962->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ goto err;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8962->supplies),
+ wm8962->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ wm8962->regmap = devm_regmap_init_i2c(i2c, &wm8962_regmap);
+ if (IS_ERR(wm8962->regmap)) {
+ ret = PTR_ERR(wm8962->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ goto err_enable;
+ }
+
+ /*
+ * We haven't marked the chip revision as volatile due to
+ * sharing a register with the right input volume; explicitly
+ * bypass the cache to read it.
+ */
+ regcache_cache_bypass(wm8962->regmap, true);
+
+ ret = regmap_read(wm8962->regmap, WM8962_SOFTWARE_RESET, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read ID register\n");
+ goto err_enable;
+ }
+ if (reg != 0x6243) {
+ dev_err(&i2c->dev,
+ "Device is not a WM8962, ID %x != 0x6243\n", reg);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ ret = regmap_read(wm8962->regmap, WM8962_RIGHT_INPUT_VOLUME, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_enable;
+ }
+
+ dev_info(&i2c->dev, "customer id %x revision %c\n",
+ (reg & WM8962_CUST_ID_MASK) >> WM8962_CUST_ID_SHIFT,
+ ((reg & WM8962_CHIP_REV_MASK) >> WM8962_CHIP_REV_SHIFT)
+ + 'A');
+
+ regcache_cache_bypass(wm8962->regmap, false);
+
+ ret = wm8962_reset(wm8962);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to issue reset\n");
+ goto err_enable;
+ }
+
+ /* SYSCLK defaults to on; make sure it is off so we can safely
+ * write to registers if the device is declocked.
+ */
+ regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2,
+ WM8962_SYSCLK_ENA, 0);
+
+ /* Ensure we have soft control over all registers */
+ regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2,
+ WM8962_CLKREG_OVD, WM8962_CLKREG_OVD);
+
+ /* Ensure that the oscillator and PLLs are disabled */
+ regmap_update_bits(wm8962->regmap, WM8962_PLL2,
+ WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA,
+ 0);
+
+ /* Apply static configuration for GPIOs */
+ for (i = 0; i < ARRAY_SIZE(wm8962->pdata.gpio_init); i++)
+ if (wm8962->pdata.gpio_init[i]) {
+ wm8962_set_gpio_mode(wm8962, i + 1);
+ regmap_write(wm8962->regmap, 0x200 + i,
+ wm8962->pdata.gpio_init[i] & 0xffff);
+ }
+
+
+ /* Put the speakers into mono mode? */
+ if (wm8962->pdata.spk_mono)
+ regmap_update_bits(wm8962->regmap, WM8962_CLASS_D_CONTROL_2,
+ WM8962_SPK_MONO_MASK, WM8962_SPK_MONO);
+
+ /* Micbias setup, detection enable and detection
+ * threasholds. */
+ if (wm8962->pdata.mic_cfg)
+ regmap_update_bits(wm8962->regmap, WM8962_ADDITIONAL_CONTROL_4,
+ WM8962_MICDET_ENA |
+ WM8962_MICDET_THR_MASK |
+ WM8962_MICSHORT_THR_MASK |
+ WM8962_MICBIAS_LVL,
+ wm8962->pdata.mic_cfg);
+
+ /* Latch volume update bits */
+ regmap_update_bits(wm8962->regmap, WM8962_LEFT_INPUT_VOLUME,
+ WM8962_IN_VU, WM8962_IN_VU);
+ regmap_update_bits(wm8962->regmap, WM8962_RIGHT_INPUT_VOLUME,
+ WM8962_IN_VU, WM8962_IN_VU);
+ regmap_update_bits(wm8962->regmap, WM8962_LEFT_ADC_VOLUME,
+ WM8962_ADC_VU, WM8962_ADC_VU);
+ regmap_update_bits(wm8962->regmap, WM8962_RIGHT_ADC_VOLUME,
+ WM8962_ADC_VU, WM8962_ADC_VU);
+ regmap_update_bits(wm8962->regmap, WM8962_LEFT_DAC_VOLUME,
+ WM8962_DAC_VU, WM8962_DAC_VU);
+ regmap_update_bits(wm8962->regmap, WM8962_RIGHT_DAC_VOLUME,
+ WM8962_DAC_VU, WM8962_DAC_VU);
+ regmap_update_bits(wm8962->regmap, WM8962_SPKOUTL_VOLUME,
+ WM8962_SPKOUT_VU, WM8962_SPKOUT_VU);
+ regmap_update_bits(wm8962->regmap, WM8962_SPKOUTR_VOLUME,
+ WM8962_SPKOUT_VU, WM8962_SPKOUT_VU);
+ regmap_update_bits(wm8962->regmap, WM8962_HPOUTL_VOLUME,
+ WM8962_HPOUT_VU, WM8962_HPOUT_VU);
+ regmap_update_bits(wm8962->regmap, WM8962_HPOUTR_VOLUME,
+ WM8962_HPOUT_VU, WM8962_HPOUT_VU);
+
+ /* Stereo control for EQ */
+ regmap_update_bits(wm8962->regmap, WM8962_EQ1,
+ WM8962_EQ_SHARED_COEFF, 0);
+
+ /* Don't debouce interrupts so we don't need SYSCLK */
+ regmap_update_bits(wm8962->regmap, WM8962_IRQ_DEBOUNCE,
+ WM8962_FLL_LOCK_DB | WM8962_PLL3_LOCK_DB |
+ WM8962_PLL2_LOCK_DB | WM8962_TEMP_SHUT_DB,
+ 0);
+
+ if (wm8962->pdata.in4_dc_measure) {
+ ret = regmap_register_patch(wm8962->regmap,
+ wm8962_dc_measure,
+ ARRAY_SIZE(wm8962_dc_measure));
+ if (ret != 0)
+ dev_err(&i2c->dev,
+ "Failed to configure for DC mesurement: %d\n",
+ ret);
+ }
+
+ if (wm8962->irq) {
+ if (wm8962->pdata.irq_active_low) {
+ trigger = IRQF_TRIGGER_LOW;
+ irq_pol = WM8962_IRQ_POL;
+ } else {
+ trigger = IRQF_TRIGGER_HIGH;
+ irq_pol = 0;
+ }
+
+ regmap_update_bits(wm8962->regmap, WM8962_INTERRUPT_CONTROL,
+ WM8962_IRQ_POL, irq_pol);
+
+ ret = devm_request_threaded_irq(&i2c->dev, wm8962->irq, NULL,
+ wm8962_irq,
+ trigger | IRQF_ONESHOT,
+ "wm8962", &i2c->dev);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n",
+ wm8962->irq, ret);
+ wm8962->irq = 0;
+ /* Non-fatal */
+ } else {
+ /* Enable some IRQs by default */
+ regmap_update_bits(wm8962->regmap,
+ WM8962_INTERRUPT_STATUS_2_MASK,
+ WM8962_FLL_LOCK_EINT |
+ WM8962_TEMP_SHUT_EINT |
+ WM8962_FIFOS_ERR_EINT, 0);
+ }
+ }
+
+ pm_runtime_enable(&i2c->dev);
+ pm_request_idle(&i2c->dev);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8962, &wm8962_dai, 1);
+ if (ret < 0)
+ goto err_enable;
+
+ regcache_cache_only(wm8962->regmap, true);
+
+ /* The drivers should power up as needed */
+ regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies);
+
+ return 0;
+
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies);
+err:
+ return ret;
+}
+
+static int wm8962_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8962_runtime_resume(struct device *dev)
+{
+ struct wm8962_priv *wm8962 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(wm8962->pdata.mclk);
+ if (ret) {
+ dev_err(dev, "Failed to enable MCLK: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8962->supplies),
+ wm8962->supplies);
+ if (ret != 0) {
+ dev_err(dev,
+ "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(wm8962->regmap, false);
+
+ wm8962_reset(wm8962);
+
+ /* SYSCLK defaults to on; make sure it is off so we can safely
+ * write to registers if the device is declocked.
+ */
+ regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2,
+ WM8962_SYSCLK_ENA, 0);
+
+ /* Ensure we have soft control over all registers */
+ regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2,
+ WM8962_CLKREG_OVD, WM8962_CLKREG_OVD);
+
+ /* Ensure that the oscillator and PLLs are disabled */
+ regmap_update_bits(wm8962->regmap, WM8962_PLL2,
+ WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA,
+ 0);
+
+ regcache_sync(wm8962->regmap);
+
+ regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP,
+ WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA,
+ WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA);
+
+ /* Bias enable at 2*5k (fast start-up) */
+ regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1,
+ WM8962_BIAS_ENA | WM8962_VMID_SEL_MASK,
+ WM8962_BIAS_ENA | 0x180);
+
+ msleep(5);
+
+ return 0;
+}
+
+static int wm8962_runtime_suspend(struct device *dev)
+{
+ struct wm8962_priv *wm8962 = dev_get_drvdata(dev);
+
+ regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1,
+ WM8962_VMID_SEL_MASK | WM8962_BIAS_ENA, 0);
+
+ regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP,
+ WM8962_STARTUP_BIAS_ENA |
+ WM8962_VMID_BUF_ENA, 0);
+
+ regcache_cache_only(wm8962->regmap, true);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies),
+ wm8962->supplies);
+
+ clk_disable_unprepare(wm8962->pdata.mclk);
+
+ return 0;
+}
+#endif
+
+static struct dev_pm_ops wm8962_pm = {
+ SET_RUNTIME_PM_OPS(wm8962_runtime_suspend, wm8962_runtime_resume, NULL)
+};
+
+static const struct i2c_device_id wm8962_i2c_id[] = {
+ { "wm8962", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8962_i2c_id);
+
+static const struct of_device_id wm8962_of_match[] = {
+ { .compatible = "wlf,wm8962", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8962_of_match);
+
+static struct i2c_driver wm8962_i2c_driver = {
+ .driver = {
+ .name = "wm8962",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8962_of_match,
+ .pm = &wm8962_pm,
+ },
+ .probe = wm8962_i2c_probe,
+ .remove = wm8962_i2c_remove,
+ .id_table = wm8962_i2c_id,
+};
+
+module_i2c_driver(wm8962_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8962 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8962.h b/kernel/sound/soc/codecs/wm8962.h
new file mode 100644
index 000000000..910aafd09
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8962.h
@@ -0,0 +1,3784 @@
+/*
+ * wm8962.h -- WM8962 ASoC driver
+ *
+ * Copyright 2010 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 _WM8962_H
+#define _WM8962_H
+
+#include <asm/types.h>
+#include <sound/soc.h>
+
+#define WM8962_SYSCLK_MCLK 1
+#define WM8962_SYSCLK_FLL 2
+#define WM8962_SYSCLK_PLL3 3
+
+#define WM8962_FLL 1
+
+#define WM8962_FLL_MCLK 1
+#define WM8962_FLL_BCLK 2
+#define WM8962_FLL_OSC 3
+#define WM8962_FLL_INT 4
+
+/*
+ * Register values.
+ */
+#define WM8962_LEFT_INPUT_VOLUME 0x00
+#define WM8962_RIGHT_INPUT_VOLUME 0x01
+#define WM8962_HPOUTL_VOLUME 0x02
+#define WM8962_HPOUTR_VOLUME 0x03
+#define WM8962_CLOCKING1 0x04
+#define WM8962_ADC_DAC_CONTROL_1 0x05
+#define WM8962_ADC_DAC_CONTROL_2 0x06
+#define WM8962_AUDIO_INTERFACE_0 0x07
+#define WM8962_CLOCKING2 0x08
+#define WM8962_AUDIO_INTERFACE_1 0x09
+#define WM8962_LEFT_DAC_VOLUME 0x0A
+#define WM8962_RIGHT_DAC_VOLUME 0x0B
+#define WM8962_AUDIO_INTERFACE_2 0x0E
+#define WM8962_SOFTWARE_RESET 0x0F
+#define WM8962_ALC1 0x11
+#define WM8962_ALC2 0x12
+#define WM8962_ALC3 0x13
+#define WM8962_NOISE_GATE 0x14
+#define WM8962_LEFT_ADC_VOLUME 0x15
+#define WM8962_RIGHT_ADC_VOLUME 0x16
+#define WM8962_ADDITIONAL_CONTROL_1 0x17
+#define WM8962_ADDITIONAL_CONTROL_2 0x18
+#define WM8962_PWR_MGMT_1 0x19
+#define WM8962_PWR_MGMT_2 0x1A
+#define WM8962_ADDITIONAL_CONTROL_3 0x1B
+#define WM8962_ANTI_POP 0x1C
+#define WM8962_CLOCKING_3 0x1E
+#define WM8962_INPUT_MIXER_CONTROL_1 0x1F
+#define WM8962_LEFT_INPUT_MIXER_VOLUME 0x20
+#define WM8962_RIGHT_INPUT_MIXER_VOLUME 0x21
+#define WM8962_INPUT_MIXER_CONTROL_2 0x22
+#define WM8962_INPUT_BIAS_CONTROL 0x23
+#define WM8962_LEFT_INPUT_PGA_CONTROL 0x25
+#define WM8962_RIGHT_INPUT_PGA_CONTROL 0x26
+#define WM8962_SPKOUTL_VOLUME 0x28
+#define WM8962_SPKOUTR_VOLUME 0x29
+#define WM8962_THERMAL_SHUTDOWN_STATUS 0x2F
+#define WM8962_ADDITIONAL_CONTROL_4 0x30
+#define WM8962_CLASS_D_CONTROL_1 0x31
+#define WM8962_CLASS_D_CONTROL_2 0x33
+#define WM8962_CLOCKING_4 0x38
+#define WM8962_DAC_DSP_MIXING_1 0x39
+#define WM8962_DAC_DSP_MIXING_2 0x3A
+#define WM8962_DC_SERVO_0 0x3C
+#define WM8962_DC_SERVO_1 0x3D
+#define WM8962_DC_SERVO_4 0x40
+#define WM8962_DC_SERVO_6 0x42
+#define WM8962_ANALOGUE_PGA_BIAS 0x44
+#define WM8962_ANALOGUE_HP_0 0x45
+#define WM8962_ANALOGUE_HP_2 0x47
+#define WM8962_CHARGE_PUMP_1 0x48
+#define WM8962_CHARGE_PUMP_B 0x52
+#define WM8962_WRITE_SEQUENCER_CONTROL_1 0x57
+#define WM8962_WRITE_SEQUENCER_CONTROL_2 0x5A
+#define WM8962_WRITE_SEQUENCER_CONTROL_3 0x5D
+#define WM8962_CONTROL_INTERFACE 0x5E
+#define WM8962_MIXER_ENABLES 0x63
+#define WM8962_HEADPHONE_MIXER_1 0x64
+#define WM8962_HEADPHONE_MIXER_2 0x65
+#define WM8962_HEADPHONE_MIXER_3 0x66
+#define WM8962_HEADPHONE_MIXER_4 0x67
+#define WM8962_SPEAKER_MIXER_1 0x69
+#define WM8962_SPEAKER_MIXER_2 0x6A
+#define WM8962_SPEAKER_MIXER_3 0x6B
+#define WM8962_SPEAKER_MIXER_4 0x6C
+#define WM8962_SPEAKER_MIXER_5 0x6D
+#define WM8962_BEEP_GENERATOR_1 0x6E
+#define WM8962_OSCILLATOR_TRIM_3 0x73
+#define WM8962_OSCILLATOR_TRIM_4 0x74
+#define WM8962_OSCILLATOR_TRIM_7 0x77
+#define WM8962_ANALOGUE_CLOCKING1 0x7C
+#define WM8962_ANALOGUE_CLOCKING2 0x7D
+#define WM8962_ANALOGUE_CLOCKING3 0x7E
+#define WM8962_PLL_SOFTWARE_RESET 0x7F
+#define WM8962_PLL2 0x81
+#define WM8962_PLL_4 0x83
+#define WM8962_PLL_9 0x88
+#define WM8962_PLL_10 0x89
+#define WM8962_PLL_11 0x8A
+#define WM8962_PLL_12 0x8B
+#define WM8962_PLL_13 0x8C
+#define WM8962_PLL_14 0x8D
+#define WM8962_PLL_15 0x8E
+#define WM8962_PLL_16 0x8F
+#define WM8962_FLL_CONTROL_1 0x9B
+#define WM8962_FLL_CONTROL_2 0x9C
+#define WM8962_FLL_CONTROL_3 0x9D
+#define WM8962_FLL_CONTROL_5 0x9F
+#define WM8962_FLL_CONTROL_6 0xA0
+#define WM8962_FLL_CONTROL_7 0xA1
+#define WM8962_FLL_CONTROL_8 0xA2
+#define WM8962_GENERAL_TEST_1 0xFC
+#define WM8962_DF1 0x100
+#define WM8962_DF2 0x101
+#define WM8962_DF3 0x102
+#define WM8962_DF4 0x103
+#define WM8962_DF5 0x104
+#define WM8962_DF6 0x105
+#define WM8962_DF7 0x106
+#define WM8962_LHPF1 0x108
+#define WM8962_LHPF2 0x109
+#define WM8962_THREED1 0x10C
+#define WM8962_THREED2 0x10D
+#define WM8962_THREED3 0x10E
+#define WM8962_THREED4 0x10F
+#define WM8962_DRC_1 0x114
+#define WM8962_DRC_2 0x115
+#define WM8962_DRC_3 0x116
+#define WM8962_DRC_4 0x117
+#define WM8962_DRC_5 0x118
+#define WM8962_TLOOPBACK 0x11D
+#define WM8962_EQ1 0x14F
+#define WM8962_EQ2 0x150
+#define WM8962_EQ3 0x151
+#define WM8962_EQ4 0x152
+#define WM8962_EQ5 0x153
+#define WM8962_EQ6 0x154
+#define WM8962_EQ7 0x155
+#define WM8962_EQ8 0x156
+#define WM8962_EQ9 0x157
+#define WM8962_EQ10 0x158
+#define WM8962_EQ11 0x159
+#define WM8962_EQ12 0x15A
+#define WM8962_EQ13 0x15B
+#define WM8962_EQ14 0x15C
+#define WM8962_EQ15 0x15D
+#define WM8962_EQ16 0x15E
+#define WM8962_EQ17 0x15F
+#define WM8962_EQ18 0x160
+#define WM8962_EQ19 0x161
+#define WM8962_EQ20 0x162
+#define WM8962_EQ21 0x163
+#define WM8962_EQ22 0x164
+#define WM8962_EQ23 0x165
+#define WM8962_EQ24 0x166
+#define WM8962_EQ25 0x167
+#define WM8962_EQ26 0x168
+#define WM8962_EQ27 0x169
+#define WM8962_EQ28 0x16A
+#define WM8962_EQ29 0x16B
+#define WM8962_EQ30 0x16C
+#define WM8962_EQ31 0x16D
+#define WM8962_EQ32 0x16E
+#define WM8962_EQ33 0x16F
+#define WM8962_EQ34 0x170
+#define WM8962_EQ35 0x171
+#define WM8962_EQ36 0x172
+#define WM8962_EQ37 0x173
+#define WM8962_EQ38 0x174
+#define WM8962_EQ39 0x175
+#define WM8962_EQ40 0x176
+#define WM8962_EQ41 0x177
+#define WM8962_GPIO_BASE 0x200
+#define WM8962_GPIO_2 0x201
+#define WM8962_GPIO_3 0x202
+#define WM8962_GPIO_5 0x204
+#define WM8962_GPIO_6 0x205
+#define WM8962_INTERRUPT_STATUS_1 0x230
+#define WM8962_INTERRUPT_STATUS_2 0x231
+#define WM8962_INTERRUPT_STATUS_1_MASK 0x238
+#define WM8962_INTERRUPT_STATUS_2_MASK 0x239
+#define WM8962_INTERRUPT_CONTROL 0x240
+#define WM8962_IRQ_DEBOUNCE 0x248
+#define WM8962_MICINT_SOURCE_POL 0x24A
+#define WM8962_DSP2_POWER_MANAGEMENT 0x300
+#define WM8962_DSP2_EXECCONTROL 0x40D
+#define WM8962_WRITE_SEQUENCER_0 0x1000
+#define WM8962_WRITE_SEQUENCER_1 0x1001
+#define WM8962_WRITE_SEQUENCER_2 0x1002
+#define WM8962_WRITE_SEQUENCER_3 0x1003
+#define WM8962_WRITE_SEQUENCER_4 0x1004
+#define WM8962_WRITE_SEQUENCER_5 0x1005
+#define WM8962_WRITE_SEQUENCER_6 0x1006
+#define WM8962_WRITE_SEQUENCER_7 0x1007
+#define WM8962_WRITE_SEQUENCER_8 0x1008
+#define WM8962_WRITE_SEQUENCER_9 0x1009
+#define WM8962_WRITE_SEQUENCER_10 0x100A
+#define WM8962_WRITE_SEQUENCER_11 0x100B
+#define WM8962_WRITE_SEQUENCER_12 0x100C
+#define WM8962_WRITE_SEQUENCER_13 0x100D
+#define WM8962_WRITE_SEQUENCER_14 0x100E
+#define WM8962_WRITE_SEQUENCER_15 0x100F
+#define WM8962_WRITE_SEQUENCER_16 0x1010
+#define WM8962_WRITE_SEQUENCER_17 0x1011
+#define WM8962_WRITE_SEQUENCER_18 0x1012
+#define WM8962_WRITE_SEQUENCER_19 0x1013
+#define WM8962_WRITE_SEQUENCER_20 0x1014
+#define WM8962_WRITE_SEQUENCER_21 0x1015
+#define WM8962_WRITE_SEQUENCER_22 0x1016
+#define WM8962_WRITE_SEQUENCER_23 0x1017
+#define WM8962_WRITE_SEQUENCER_24 0x1018
+#define WM8962_WRITE_SEQUENCER_25 0x1019
+#define WM8962_WRITE_SEQUENCER_26 0x101A
+#define WM8962_WRITE_SEQUENCER_27 0x101B
+#define WM8962_WRITE_SEQUENCER_28 0x101C
+#define WM8962_WRITE_SEQUENCER_29 0x101D
+#define WM8962_WRITE_SEQUENCER_30 0x101E
+#define WM8962_WRITE_SEQUENCER_31 0x101F
+#define WM8962_WRITE_SEQUENCER_32 0x1020
+#define WM8962_WRITE_SEQUENCER_33 0x1021
+#define WM8962_WRITE_SEQUENCER_34 0x1022
+#define WM8962_WRITE_SEQUENCER_35 0x1023
+#define WM8962_WRITE_SEQUENCER_36 0x1024
+#define WM8962_WRITE_SEQUENCER_37 0x1025
+#define WM8962_WRITE_SEQUENCER_38 0x1026
+#define WM8962_WRITE_SEQUENCER_39 0x1027
+#define WM8962_WRITE_SEQUENCER_40 0x1028
+#define WM8962_WRITE_SEQUENCER_41 0x1029
+#define WM8962_WRITE_SEQUENCER_42 0x102A
+#define WM8962_WRITE_SEQUENCER_43 0x102B
+#define WM8962_WRITE_SEQUENCER_44 0x102C
+#define WM8962_WRITE_SEQUENCER_45 0x102D
+#define WM8962_WRITE_SEQUENCER_46 0x102E
+#define WM8962_WRITE_SEQUENCER_47 0x102F
+#define WM8962_WRITE_SEQUENCER_48 0x1030
+#define WM8962_WRITE_SEQUENCER_49 0x1031
+#define WM8962_WRITE_SEQUENCER_50 0x1032
+#define WM8962_WRITE_SEQUENCER_51 0x1033
+#define WM8962_WRITE_SEQUENCER_52 0x1034
+#define WM8962_WRITE_SEQUENCER_53 0x1035
+#define WM8962_WRITE_SEQUENCER_54 0x1036
+#define WM8962_WRITE_SEQUENCER_55 0x1037
+#define WM8962_WRITE_SEQUENCER_56 0x1038
+#define WM8962_WRITE_SEQUENCER_57 0x1039
+#define WM8962_WRITE_SEQUENCER_58 0x103A
+#define WM8962_WRITE_SEQUENCER_59 0x103B
+#define WM8962_WRITE_SEQUENCER_60 0x103C
+#define WM8962_WRITE_SEQUENCER_61 0x103D
+#define WM8962_WRITE_SEQUENCER_62 0x103E
+#define WM8962_WRITE_SEQUENCER_63 0x103F
+#define WM8962_WRITE_SEQUENCER_64 0x1040
+#define WM8962_WRITE_SEQUENCER_65 0x1041
+#define WM8962_WRITE_SEQUENCER_66 0x1042
+#define WM8962_WRITE_SEQUENCER_67 0x1043
+#define WM8962_WRITE_SEQUENCER_68 0x1044
+#define WM8962_WRITE_SEQUENCER_69 0x1045
+#define WM8962_WRITE_SEQUENCER_70 0x1046
+#define WM8962_WRITE_SEQUENCER_71 0x1047
+#define WM8962_WRITE_SEQUENCER_72 0x1048
+#define WM8962_WRITE_SEQUENCER_73 0x1049
+#define WM8962_WRITE_SEQUENCER_74 0x104A
+#define WM8962_WRITE_SEQUENCER_75 0x104B
+#define WM8962_WRITE_SEQUENCER_76 0x104C
+#define WM8962_WRITE_SEQUENCER_77 0x104D
+#define WM8962_WRITE_SEQUENCER_78 0x104E
+#define WM8962_WRITE_SEQUENCER_79 0x104F
+#define WM8962_WRITE_SEQUENCER_80 0x1050
+#define WM8962_WRITE_SEQUENCER_81 0x1051
+#define WM8962_WRITE_SEQUENCER_82 0x1052
+#define WM8962_WRITE_SEQUENCER_83 0x1053
+#define WM8962_WRITE_SEQUENCER_84 0x1054
+#define WM8962_WRITE_SEQUENCER_85 0x1055
+#define WM8962_WRITE_SEQUENCER_86 0x1056
+#define WM8962_WRITE_SEQUENCER_87 0x1057
+#define WM8962_WRITE_SEQUENCER_88 0x1058
+#define WM8962_WRITE_SEQUENCER_89 0x1059
+#define WM8962_WRITE_SEQUENCER_90 0x105A
+#define WM8962_WRITE_SEQUENCER_91 0x105B
+#define WM8962_WRITE_SEQUENCER_92 0x105C
+#define WM8962_WRITE_SEQUENCER_93 0x105D
+#define WM8962_WRITE_SEQUENCER_94 0x105E
+#define WM8962_WRITE_SEQUENCER_95 0x105F
+#define WM8962_WRITE_SEQUENCER_96 0x1060
+#define WM8962_WRITE_SEQUENCER_97 0x1061
+#define WM8962_WRITE_SEQUENCER_98 0x1062
+#define WM8962_WRITE_SEQUENCER_99 0x1063
+#define WM8962_WRITE_SEQUENCER_100 0x1064
+#define WM8962_WRITE_SEQUENCER_101 0x1065
+#define WM8962_WRITE_SEQUENCER_102 0x1066
+#define WM8962_WRITE_SEQUENCER_103 0x1067
+#define WM8962_WRITE_SEQUENCER_104 0x1068
+#define WM8962_WRITE_SEQUENCER_105 0x1069
+#define WM8962_WRITE_SEQUENCER_106 0x106A
+#define WM8962_WRITE_SEQUENCER_107 0x106B
+#define WM8962_WRITE_SEQUENCER_108 0x106C
+#define WM8962_WRITE_SEQUENCER_109 0x106D
+#define WM8962_WRITE_SEQUENCER_110 0x106E
+#define WM8962_WRITE_SEQUENCER_111 0x106F
+#define WM8962_WRITE_SEQUENCER_112 0x1070
+#define WM8962_WRITE_SEQUENCER_113 0x1071
+#define WM8962_WRITE_SEQUENCER_114 0x1072
+#define WM8962_WRITE_SEQUENCER_115 0x1073
+#define WM8962_WRITE_SEQUENCER_116 0x1074
+#define WM8962_WRITE_SEQUENCER_117 0x1075
+#define WM8962_WRITE_SEQUENCER_118 0x1076
+#define WM8962_WRITE_SEQUENCER_119 0x1077
+#define WM8962_WRITE_SEQUENCER_120 0x1078
+#define WM8962_WRITE_SEQUENCER_121 0x1079
+#define WM8962_WRITE_SEQUENCER_122 0x107A
+#define WM8962_WRITE_SEQUENCER_123 0x107B
+#define WM8962_WRITE_SEQUENCER_124 0x107C
+#define WM8962_WRITE_SEQUENCER_125 0x107D
+#define WM8962_WRITE_SEQUENCER_126 0x107E
+#define WM8962_WRITE_SEQUENCER_127 0x107F
+#define WM8962_WRITE_SEQUENCER_128 0x1080
+#define WM8962_WRITE_SEQUENCER_129 0x1081
+#define WM8962_WRITE_SEQUENCER_130 0x1082
+#define WM8962_WRITE_SEQUENCER_131 0x1083
+#define WM8962_WRITE_SEQUENCER_132 0x1084
+#define WM8962_WRITE_SEQUENCER_133 0x1085
+#define WM8962_WRITE_SEQUENCER_134 0x1086
+#define WM8962_WRITE_SEQUENCER_135 0x1087
+#define WM8962_WRITE_SEQUENCER_136 0x1088
+#define WM8962_WRITE_SEQUENCER_137 0x1089
+#define WM8962_WRITE_SEQUENCER_138 0x108A
+#define WM8962_WRITE_SEQUENCER_139 0x108B
+#define WM8962_WRITE_SEQUENCER_140 0x108C
+#define WM8962_WRITE_SEQUENCER_141 0x108D
+#define WM8962_WRITE_SEQUENCER_142 0x108E
+#define WM8962_WRITE_SEQUENCER_143 0x108F
+#define WM8962_WRITE_SEQUENCER_144 0x1090
+#define WM8962_WRITE_SEQUENCER_145 0x1091
+#define WM8962_WRITE_SEQUENCER_146 0x1092
+#define WM8962_WRITE_SEQUENCER_147 0x1093
+#define WM8962_WRITE_SEQUENCER_148 0x1094
+#define WM8962_WRITE_SEQUENCER_149 0x1095
+#define WM8962_WRITE_SEQUENCER_150 0x1096
+#define WM8962_WRITE_SEQUENCER_151 0x1097
+#define WM8962_WRITE_SEQUENCER_152 0x1098
+#define WM8962_WRITE_SEQUENCER_153 0x1099
+#define WM8962_WRITE_SEQUENCER_154 0x109A
+#define WM8962_WRITE_SEQUENCER_155 0x109B
+#define WM8962_WRITE_SEQUENCER_156 0x109C
+#define WM8962_WRITE_SEQUENCER_157 0x109D
+#define WM8962_WRITE_SEQUENCER_158 0x109E
+#define WM8962_WRITE_SEQUENCER_159 0x109F
+#define WM8962_WRITE_SEQUENCER_160 0x10A0
+#define WM8962_WRITE_SEQUENCER_161 0x10A1
+#define WM8962_WRITE_SEQUENCER_162 0x10A2
+#define WM8962_WRITE_SEQUENCER_163 0x10A3
+#define WM8962_WRITE_SEQUENCER_164 0x10A4
+#define WM8962_WRITE_SEQUENCER_165 0x10A5
+#define WM8962_WRITE_SEQUENCER_166 0x10A6
+#define WM8962_WRITE_SEQUENCER_167 0x10A7
+#define WM8962_WRITE_SEQUENCER_168 0x10A8
+#define WM8962_WRITE_SEQUENCER_169 0x10A9
+#define WM8962_WRITE_SEQUENCER_170 0x10AA
+#define WM8962_WRITE_SEQUENCER_171 0x10AB
+#define WM8962_WRITE_SEQUENCER_172 0x10AC
+#define WM8962_WRITE_SEQUENCER_173 0x10AD
+#define WM8962_WRITE_SEQUENCER_174 0x10AE
+#define WM8962_WRITE_SEQUENCER_175 0x10AF
+#define WM8962_WRITE_SEQUENCER_176 0x10B0
+#define WM8962_WRITE_SEQUENCER_177 0x10B1
+#define WM8962_WRITE_SEQUENCER_178 0x10B2
+#define WM8962_WRITE_SEQUENCER_179 0x10B3
+#define WM8962_WRITE_SEQUENCER_180 0x10B4
+#define WM8962_WRITE_SEQUENCER_181 0x10B5
+#define WM8962_WRITE_SEQUENCER_182 0x10B6
+#define WM8962_WRITE_SEQUENCER_183 0x10B7
+#define WM8962_WRITE_SEQUENCER_184 0x10B8
+#define WM8962_WRITE_SEQUENCER_185 0x10B9
+#define WM8962_WRITE_SEQUENCER_186 0x10BA
+#define WM8962_WRITE_SEQUENCER_187 0x10BB
+#define WM8962_WRITE_SEQUENCER_188 0x10BC
+#define WM8962_WRITE_SEQUENCER_189 0x10BD
+#define WM8962_WRITE_SEQUENCER_190 0x10BE
+#define WM8962_WRITE_SEQUENCER_191 0x10BF
+#define WM8962_WRITE_SEQUENCER_192 0x10C0
+#define WM8962_WRITE_SEQUENCER_193 0x10C1
+#define WM8962_WRITE_SEQUENCER_194 0x10C2
+#define WM8962_WRITE_SEQUENCER_195 0x10C3
+#define WM8962_WRITE_SEQUENCER_196 0x10C4
+#define WM8962_WRITE_SEQUENCER_197 0x10C5
+#define WM8962_WRITE_SEQUENCER_198 0x10C6
+#define WM8962_WRITE_SEQUENCER_199 0x10C7
+#define WM8962_WRITE_SEQUENCER_200 0x10C8
+#define WM8962_WRITE_SEQUENCER_201 0x10C9
+#define WM8962_WRITE_SEQUENCER_202 0x10CA
+#define WM8962_WRITE_SEQUENCER_203 0x10CB
+#define WM8962_WRITE_SEQUENCER_204 0x10CC
+#define WM8962_WRITE_SEQUENCER_205 0x10CD
+#define WM8962_WRITE_SEQUENCER_206 0x10CE
+#define WM8962_WRITE_SEQUENCER_207 0x10CF
+#define WM8962_WRITE_SEQUENCER_208 0x10D0
+#define WM8962_WRITE_SEQUENCER_209 0x10D1
+#define WM8962_WRITE_SEQUENCER_210 0x10D2
+#define WM8962_WRITE_SEQUENCER_211 0x10D3
+#define WM8962_WRITE_SEQUENCER_212 0x10D4
+#define WM8962_WRITE_SEQUENCER_213 0x10D5
+#define WM8962_WRITE_SEQUENCER_214 0x10D6
+#define WM8962_WRITE_SEQUENCER_215 0x10D7
+#define WM8962_WRITE_SEQUENCER_216 0x10D8
+#define WM8962_WRITE_SEQUENCER_217 0x10D9
+#define WM8962_WRITE_SEQUENCER_218 0x10DA
+#define WM8962_WRITE_SEQUENCER_219 0x10DB
+#define WM8962_WRITE_SEQUENCER_220 0x10DC
+#define WM8962_WRITE_SEQUENCER_221 0x10DD
+#define WM8962_WRITE_SEQUENCER_222 0x10DE
+#define WM8962_WRITE_SEQUENCER_223 0x10DF
+#define WM8962_WRITE_SEQUENCER_224 0x10E0
+#define WM8962_WRITE_SEQUENCER_225 0x10E1
+#define WM8962_WRITE_SEQUENCER_226 0x10E2
+#define WM8962_WRITE_SEQUENCER_227 0x10E3
+#define WM8962_WRITE_SEQUENCER_228 0x10E4
+#define WM8962_WRITE_SEQUENCER_229 0x10E5
+#define WM8962_WRITE_SEQUENCER_230 0x10E6
+#define WM8962_WRITE_SEQUENCER_231 0x10E7
+#define WM8962_WRITE_SEQUENCER_232 0x10E8
+#define WM8962_WRITE_SEQUENCER_233 0x10E9
+#define WM8962_WRITE_SEQUENCER_234 0x10EA
+#define WM8962_WRITE_SEQUENCER_235 0x10EB
+#define WM8962_WRITE_SEQUENCER_236 0x10EC
+#define WM8962_WRITE_SEQUENCER_237 0x10ED
+#define WM8962_WRITE_SEQUENCER_238 0x10EE
+#define WM8962_WRITE_SEQUENCER_239 0x10EF
+#define WM8962_WRITE_SEQUENCER_240 0x10F0
+#define WM8962_WRITE_SEQUENCER_241 0x10F1
+#define WM8962_WRITE_SEQUENCER_242 0x10F2
+#define WM8962_WRITE_SEQUENCER_243 0x10F3
+#define WM8962_WRITE_SEQUENCER_244 0x10F4
+#define WM8962_WRITE_SEQUENCER_245 0x10F5
+#define WM8962_WRITE_SEQUENCER_246 0x10F6
+#define WM8962_WRITE_SEQUENCER_247 0x10F7
+#define WM8962_WRITE_SEQUENCER_248 0x10F8
+#define WM8962_WRITE_SEQUENCER_249 0x10F9
+#define WM8962_WRITE_SEQUENCER_250 0x10FA
+#define WM8962_WRITE_SEQUENCER_251 0x10FB
+#define WM8962_WRITE_SEQUENCER_252 0x10FC
+#define WM8962_WRITE_SEQUENCER_253 0x10FD
+#define WM8962_WRITE_SEQUENCER_254 0x10FE
+#define WM8962_WRITE_SEQUENCER_255 0x10FF
+#define WM8962_WRITE_SEQUENCER_256 0x1100
+#define WM8962_WRITE_SEQUENCER_257 0x1101
+#define WM8962_WRITE_SEQUENCER_258 0x1102
+#define WM8962_WRITE_SEQUENCER_259 0x1103
+#define WM8962_WRITE_SEQUENCER_260 0x1104
+#define WM8962_WRITE_SEQUENCER_261 0x1105
+#define WM8962_WRITE_SEQUENCER_262 0x1106
+#define WM8962_WRITE_SEQUENCER_263 0x1107
+#define WM8962_WRITE_SEQUENCER_264 0x1108
+#define WM8962_WRITE_SEQUENCER_265 0x1109
+#define WM8962_WRITE_SEQUENCER_266 0x110A
+#define WM8962_WRITE_SEQUENCER_267 0x110B
+#define WM8962_WRITE_SEQUENCER_268 0x110C
+#define WM8962_WRITE_SEQUENCER_269 0x110D
+#define WM8962_WRITE_SEQUENCER_270 0x110E
+#define WM8962_WRITE_SEQUENCER_271 0x110F
+#define WM8962_WRITE_SEQUENCER_272 0x1110
+#define WM8962_WRITE_SEQUENCER_273 0x1111
+#define WM8962_WRITE_SEQUENCER_274 0x1112
+#define WM8962_WRITE_SEQUENCER_275 0x1113
+#define WM8962_WRITE_SEQUENCER_276 0x1114
+#define WM8962_WRITE_SEQUENCER_277 0x1115
+#define WM8962_WRITE_SEQUENCER_278 0x1116
+#define WM8962_WRITE_SEQUENCER_279 0x1117
+#define WM8962_WRITE_SEQUENCER_280 0x1118
+#define WM8962_WRITE_SEQUENCER_281 0x1119
+#define WM8962_WRITE_SEQUENCER_282 0x111A
+#define WM8962_WRITE_SEQUENCER_283 0x111B
+#define WM8962_WRITE_SEQUENCER_284 0x111C
+#define WM8962_WRITE_SEQUENCER_285 0x111D
+#define WM8962_WRITE_SEQUENCER_286 0x111E
+#define WM8962_WRITE_SEQUENCER_287 0x111F
+#define WM8962_WRITE_SEQUENCER_288 0x1120
+#define WM8962_WRITE_SEQUENCER_289 0x1121
+#define WM8962_WRITE_SEQUENCER_290 0x1122
+#define WM8962_WRITE_SEQUENCER_291 0x1123
+#define WM8962_WRITE_SEQUENCER_292 0x1124
+#define WM8962_WRITE_SEQUENCER_293 0x1125
+#define WM8962_WRITE_SEQUENCER_294 0x1126
+#define WM8962_WRITE_SEQUENCER_295 0x1127
+#define WM8962_WRITE_SEQUENCER_296 0x1128
+#define WM8962_WRITE_SEQUENCER_297 0x1129
+#define WM8962_WRITE_SEQUENCER_298 0x112A
+#define WM8962_WRITE_SEQUENCER_299 0x112B
+#define WM8962_WRITE_SEQUENCER_300 0x112C
+#define WM8962_WRITE_SEQUENCER_301 0x112D
+#define WM8962_WRITE_SEQUENCER_302 0x112E
+#define WM8962_WRITE_SEQUENCER_303 0x112F
+#define WM8962_WRITE_SEQUENCER_304 0x1130
+#define WM8962_WRITE_SEQUENCER_305 0x1131
+#define WM8962_WRITE_SEQUENCER_306 0x1132
+#define WM8962_WRITE_SEQUENCER_307 0x1133
+#define WM8962_WRITE_SEQUENCER_308 0x1134
+#define WM8962_WRITE_SEQUENCER_309 0x1135
+#define WM8962_WRITE_SEQUENCER_310 0x1136
+#define WM8962_WRITE_SEQUENCER_311 0x1137
+#define WM8962_WRITE_SEQUENCER_312 0x1138
+#define WM8962_WRITE_SEQUENCER_313 0x1139
+#define WM8962_WRITE_SEQUENCER_314 0x113A
+#define WM8962_WRITE_SEQUENCER_315 0x113B
+#define WM8962_WRITE_SEQUENCER_316 0x113C
+#define WM8962_WRITE_SEQUENCER_317 0x113D
+#define WM8962_WRITE_SEQUENCER_318 0x113E
+#define WM8962_WRITE_SEQUENCER_319 0x113F
+#define WM8962_WRITE_SEQUENCER_320 0x1140
+#define WM8962_WRITE_SEQUENCER_321 0x1141
+#define WM8962_WRITE_SEQUENCER_322 0x1142
+#define WM8962_WRITE_SEQUENCER_323 0x1143
+#define WM8962_WRITE_SEQUENCER_324 0x1144
+#define WM8962_WRITE_SEQUENCER_325 0x1145
+#define WM8962_WRITE_SEQUENCER_326 0x1146
+#define WM8962_WRITE_SEQUENCER_327 0x1147
+#define WM8962_WRITE_SEQUENCER_328 0x1148
+#define WM8962_WRITE_SEQUENCER_329 0x1149
+#define WM8962_WRITE_SEQUENCER_330 0x114A
+#define WM8962_WRITE_SEQUENCER_331 0x114B
+#define WM8962_WRITE_SEQUENCER_332 0x114C
+#define WM8962_WRITE_SEQUENCER_333 0x114D
+#define WM8962_WRITE_SEQUENCER_334 0x114E
+#define WM8962_WRITE_SEQUENCER_335 0x114F
+#define WM8962_WRITE_SEQUENCER_336 0x1150
+#define WM8962_WRITE_SEQUENCER_337 0x1151
+#define WM8962_WRITE_SEQUENCER_338 0x1152
+#define WM8962_WRITE_SEQUENCER_339 0x1153
+#define WM8962_WRITE_SEQUENCER_340 0x1154
+#define WM8962_WRITE_SEQUENCER_341 0x1155
+#define WM8962_WRITE_SEQUENCER_342 0x1156
+#define WM8962_WRITE_SEQUENCER_343 0x1157
+#define WM8962_WRITE_SEQUENCER_344 0x1158
+#define WM8962_WRITE_SEQUENCER_345 0x1159
+#define WM8962_WRITE_SEQUENCER_346 0x115A
+#define WM8962_WRITE_SEQUENCER_347 0x115B
+#define WM8962_WRITE_SEQUENCER_348 0x115C
+#define WM8962_WRITE_SEQUENCER_349 0x115D
+#define WM8962_WRITE_SEQUENCER_350 0x115E
+#define WM8962_WRITE_SEQUENCER_351 0x115F
+#define WM8962_WRITE_SEQUENCER_352 0x1160
+#define WM8962_WRITE_SEQUENCER_353 0x1161
+#define WM8962_WRITE_SEQUENCER_354 0x1162
+#define WM8962_WRITE_SEQUENCER_355 0x1163
+#define WM8962_WRITE_SEQUENCER_356 0x1164
+#define WM8962_WRITE_SEQUENCER_357 0x1165
+#define WM8962_WRITE_SEQUENCER_358 0x1166
+#define WM8962_WRITE_SEQUENCER_359 0x1167
+#define WM8962_WRITE_SEQUENCER_360 0x1168
+#define WM8962_WRITE_SEQUENCER_361 0x1169
+#define WM8962_WRITE_SEQUENCER_362 0x116A
+#define WM8962_WRITE_SEQUENCER_363 0x116B
+#define WM8962_WRITE_SEQUENCER_364 0x116C
+#define WM8962_WRITE_SEQUENCER_365 0x116D
+#define WM8962_WRITE_SEQUENCER_366 0x116E
+#define WM8962_WRITE_SEQUENCER_367 0x116F
+#define WM8962_WRITE_SEQUENCER_368 0x1170
+#define WM8962_WRITE_SEQUENCER_369 0x1171
+#define WM8962_WRITE_SEQUENCER_370 0x1172
+#define WM8962_WRITE_SEQUENCER_371 0x1173
+#define WM8962_WRITE_SEQUENCER_372 0x1174
+#define WM8962_WRITE_SEQUENCER_373 0x1175
+#define WM8962_WRITE_SEQUENCER_374 0x1176
+#define WM8962_WRITE_SEQUENCER_375 0x1177
+#define WM8962_WRITE_SEQUENCER_376 0x1178
+#define WM8962_WRITE_SEQUENCER_377 0x1179
+#define WM8962_WRITE_SEQUENCER_378 0x117A
+#define WM8962_WRITE_SEQUENCER_379 0x117B
+#define WM8962_WRITE_SEQUENCER_380 0x117C
+#define WM8962_WRITE_SEQUENCER_381 0x117D
+#define WM8962_WRITE_SEQUENCER_382 0x117E
+#define WM8962_WRITE_SEQUENCER_383 0x117F
+#define WM8962_WRITE_SEQUENCER_384 0x1180
+#define WM8962_WRITE_SEQUENCER_385 0x1181
+#define WM8962_WRITE_SEQUENCER_386 0x1182
+#define WM8962_WRITE_SEQUENCER_387 0x1183
+#define WM8962_WRITE_SEQUENCER_388 0x1184
+#define WM8962_WRITE_SEQUENCER_389 0x1185
+#define WM8962_WRITE_SEQUENCER_390 0x1186
+#define WM8962_WRITE_SEQUENCER_391 0x1187
+#define WM8962_WRITE_SEQUENCER_392 0x1188
+#define WM8962_WRITE_SEQUENCER_393 0x1189
+#define WM8962_WRITE_SEQUENCER_394 0x118A
+#define WM8962_WRITE_SEQUENCER_395 0x118B
+#define WM8962_WRITE_SEQUENCER_396 0x118C
+#define WM8962_WRITE_SEQUENCER_397 0x118D
+#define WM8962_WRITE_SEQUENCER_398 0x118E
+#define WM8962_WRITE_SEQUENCER_399 0x118F
+#define WM8962_WRITE_SEQUENCER_400 0x1190
+#define WM8962_WRITE_SEQUENCER_401 0x1191
+#define WM8962_WRITE_SEQUENCER_402 0x1192
+#define WM8962_WRITE_SEQUENCER_403 0x1193
+#define WM8962_WRITE_SEQUENCER_404 0x1194
+#define WM8962_WRITE_SEQUENCER_405 0x1195
+#define WM8962_WRITE_SEQUENCER_406 0x1196
+#define WM8962_WRITE_SEQUENCER_407 0x1197
+#define WM8962_WRITE_SEQUENCER_408 0x1198
+#define WM8962_WRITE_SEQUENCER_409 0x1199
+#define WM8962_WRITE_SEQUENCER_410 0x119A
+#define WM8962_WRITE_SEQUENCER_411 0x119B
+#define WM8962_WRITE_SEQUENCER_412 0x119C
+#define WM8962_WRITE_SEQUENCER_413 0x119D
+#define WM8962_WRITE_SEQUENCER_414 0x119E
+#define WM8962_WRITE_SEQUENCER_415 0x119F
+#define WM8962_WRITE_SEQUENCER_416 0x11A0
+#define WM8962_WRITE_SEQUENCER_417 0x11A1
+#define WM8962_WRITE_SEQUENCER_418 0x11A2
+#define WM8962_WRITE_SEQUENCER_419 0x11A3
+#define WM8962_WRITE_SEQUENCER_420 0x11A4
+#define WM8962_WRITE_SEQUENCER_421 0x11A5
+#define WM8962_WRITE_SEQUENCER_422 0x11A6
+#define WM8962_WRITE_SEQUENCER_423 0x11A7
+#define WM8962_WRITE_SEQUENCER_424 0x11A8
+#define WM8962_WRITE_SEQUENCER_425 0x11A9
+#define WM8962_WRITE_SEQUENCER_426 0x11AA
+#define WM8962_WRITE_SEQUENCER_427 0x11AB
+#define WM8962_WRITE_SEQUENCER_428 0x11AC
+#define WM8962_WRITE_SEQUENCER_429 0x11AD
+#define WM8962_WRITE_SEQUENCER_430 0x11AE
+#define WM8962_WRITE_SEQUENCER_431 0x11AF
+#define WM8962_WRITE_SEQUENCER_432 0x11B0
+#define WM8962_WRITE_SEQUENCER_433 0x11B1
+#define WM8962_WRITE_SEQUENCER_434 0x11B2
+#define WM8962_WRITE_SEQUENCER_435 0x11B3
+#define WM8962_WRITE_SEQUENCER_436 0x11B4
+#define WM8962_WRITE_SEQUENCER_437 0x11B5
+#define WM8962_WRITE_SEQUENCER_438 0x11B6
+#define WM8962_WRITE_SEQUENCER_439 0x11B7
+#define WM8962_WRITE_SEQUENCER_440 0x11B8
+#define WM8962_WRITE_SEQUENCER_441 0x11B9
+#define WM8962_WRITE_SEQUENCER_442 0x11BA
+#define WM8962_WRITE_SEQUENCER_443 0x11BB
+#define WM8962_WRITE_SEQUENCER_444 0x11BC
+#define WM8962_WRITE_SEQUENCER_445 0x11BD
+#define WM8962_WRITE_SEQUENCER_446 0x11BE
+#define WM8962_WRITE_SEQUENCER_447 0x11BF
+#define WM8962_WRITE_SEQUENCER_448 0x11C0
+#define WM8962_WRITE_SEQUENCER_449 0x11C1
+#define WM8962_WRITE_SEQUENCER_450 0x11C2
+#define WM8962_WRITE_SEQUENCER_451 0x11C3
+#define WM8962_WRITE_SEQUENCER_452 0x11C4
+#define WM8962_WRITE_SEQUENCER_453 0x11C5
+#define WM8962_WRITE_SEQUENCER_454 0x11C6
+#define WM8962_WRITE_SEQUENCER_455 0x11C7
+#define WM8962_WRITE_SEQUENCER_456 0x11C8
+#define WM8962_WRITE_SEQUENCER_457 0x11C9
+#define WM8962_WRITE_SEQUENCER_458 0x11CA
+#define WM8962_WRITE_SEQUENCER_459 0x11CB
+#define WM8962_WRITE_SEQUENCER_460 0x11CC
+#define WM8962_WRITE_SEQUENCER_461 0x11CD
+#define WM8962_WRITE_SEQUENCER_462 0x11CE
+#define WM8962_WRITE_SEQUENCER_463 0x11CF
+#define WM8962_WRITE_SEQUENCER_464 0x11D0
+#define WM8962_WRITE_SEQUENCER_465 0x11D1
+#define WM8962_WRITE_SEQUENCER_466 0x11D2
+#define WM8962_WRITE_SEQUENCER_467 0x11D3
+#define WM8962_WRITE_SEQUENCER_468 0x11D4
+#define WM8962_WRITE_SEQUENCER_469 0x11D5
+#define WM8962_WRITE_SEQUENCER_470 0x11D6
+#define WM8962_WRITE_SEQUENCER_471 0x11D7
+#define WM8962_WRITE_SEQUENCER_472 0x11D8
+#define WM8962_WRITE_SEQUENCER_473 0x11D9
+#define WM8962_WRITE_SEQUENCER_474 0x11DA
+#define WM8962_WRITE_SEQUENCER_475 0x11DB
+#define WM8962_WRITE_SEQUENCER_476 0x11DC
+#define WM8962_WRITE_SEQUENCER_477 0x11DD
+#define WM8962_WRITE_SEQUENCER_478 0x11DE
+#define WM8962_WRITE_SEQUENCER_479 0x11DF
+#define WM8962_WRITE_SEQUENCER_480 0x11E0
+#define WM8962_WRITE_SEQUENCER_481 0x11E1
+#define WM8962_WRITE_SEQUENCER_482 0x11E2
+#define WM8962_WRITE_SEQUENCER_483 0x11E3
+#define WM8962_WRITE_SEQUENCER_484 0x11E4
+#define WM8962_WRITE_SEQUENCER_485 0x11E5
+#define WM8962_WRITE_SEQUENCER_486 0x11E6
+#define WM8962_WRITE_SEQUENCER_487 0x11E7
+#define WM8962_WRITE_SEQUENCER_488 0x11E8
+#define WM8962_WRITE_SEQUENCER_489 0x11E9
+#define WM8962_WRITE_SEQUENCER_490 0x11EA
+#define WM8962_WRITE_SEQUENCER_491 0x11EB
+#define WM8962_WRITE_SEQUENCER_492 0x11EC
+#define WM8962_WRITE_SEQUENCER_493 0x11ED
+#define WM8962_WRITE_SEQUENCER_494 0x11EE
+#define WM8962_WRITE_SEQUENCER_495 0x11EF
+#define WM8962_WRITE_SEQUENCER_496 0x11F0
+#define WM8962_WRITE_SEQUENCER_497 0x11F1
+#define WM8962_WRITE_SEQUENCER_498 0x11F2
+#define WM8962_WRITE_SEQUENCER_499 0x11F3
+#define WM8962_WRITE_SEQUENCER_500 0x11F4
+#define WM8962_WRITE_SEQUENCER_501 0x11F5
+#define WM8962_WRITE_SEQUENCER_502 0x11F6
+#define WM8962_WRITE_SEQUENCER_503 0x11F7
+#define WM8962_WRITE_SEQUENCER_504 0x11F8
+#define WM8962_WRITE_SEQUENCER_505 0x11F9
+#define WM8962_WRITE_SEQUENCER_506 0x11FA
+#define WM8962_WRITE_SEQUENCER_507 0x11FB
+#define WM8962_WRITE_SEQUENCER_508 0x11FC
+#define WM8962_WRITE_SEQUENCER_509 0x11FD
+#define WM8962_WRITE_SEQUENCER_510 0x11FE
+#define WM8962_WRITE_SEQUENCER_511 0x11FF
+#define WM8962_DSP2_INSTRUCTION_RAM_0 0x2000
+#define WM8962_DSP2_ADDRESS_RAM_2 0x2400
+#define WM8962_DSP2_ADDRESS_RAM_1 0x2401
+#define WM8962_DSP2_ADDRESS_RAM_0 0x2402
+#define WM8962_DSP2_DATA1_RAM_1 0x3000
+#define WM8962_DSP2_DATA1_RAM_0 0x3001
+#define WM8962_DSP2_DATA2_RAM_1 0x3400
+#define WM8962_DSP2_DATA2_RAM_0 0x3401
+#define WM8962_DSP2_DATA3_RAM_1 0x3800
+#define WM8962_DSP2_DATA3_RAM_0 0x3801
+#define WM8962_DSP2_COEFF_RAM_0 0x3C00
+#define WM8962_RETUNEADC_SHARED_COEFF_1 0x4000
+#define WM8962_RETUNEADC_SHARED_COEFF_0 0x4001
+#define WM8962_RETUNEDAC_SHARED_COEFF_1 0x4002
+#define WM8962_RETUNEDAC_SHARED_COEFF_0 0x4003
+#define WM8962_SOUNDSTAGE_ENABLES_1 0x4004
+#define WM8962_SOUNDSTAGE_ENABLES_0 0x4005
+#define WM8962_HDBASS_AI_1 0x4200
+#define WM8962_HDBASS_AI_0 0x4201
+#define WM8962_HDBASS_AR_1 0x4202
+#define WM8962_HDBASS_AR_0 0x4203
+#define WM8962_HDBASS_B_1 0x4204
+#define WM8962_HDBASS_B_0 0x4205
+#define WM8962_HDBASS_K_1 0x4206
+#define WM8962_HDBASS_K_0 0x4207
+#define WM8962_HDBASS_N1_1 0x4208
+#define WM8962_HDBASS_N1_0 0x4209
+#define WM8962_HDBASS_N2_1 0x420A
+#define WM8962_HDBASS_N2_0 0x420B
+#define WM8962_HDBASS_N3_1 0x420C
+#define WM8962_HDBASS_N3_0 0x420D
+#define WM8962_HDBASS_N4_1 0x420E
+#define WM8962_HDBASS_N4_0 0x420F
+#define WM8962_HDBASS_N5_1 0x4210
+#define WM8962_HDBASS_N5_0 0x4211
+#define WM8962_HDBASS_X1_1 0x4212
+#define WM8962_HDBASS_X1_0 0x4213
+#define WM8962_HDBASS_X2_1 0x4214
+#define WM8962_HDBASS_X2_0 0x4215
+#define WM8962_HDBASS_X3_1 0x4216
+#define WM8962_HDBASS_X3_0 0x4217
+#define WM8962_HDBASS_ATK_1 0x4218
+#define WM8962_HDBASS_ATK_0 0x4219
+#define WM8962_HDBASS_DCY_1 0x421A
+#define WM8962_HDBASS_DCY_0 0x421B
+#define WM8962_HDBASS_PG_1 0x421C
+#define WM8962_HDBASS_PG_0 0x421D
+#define WM8962_HPF_C_1 0x4400
+#define WM8962_HPF_C_0 0x4401
+#define WM8962_ADCL_RETUNE_C1_1 0x4600
+#define WM8962_ADCL_RETUNE_C1_0 0x4601
+#define WM8962_ADCL_RETUNE_C2_1 0x4602
+#define WM8962_ADCL_RETUNE_C2_0 0x4603
+#define WM8962_ADCL_RETUNE_C3_1 0x4604
+#define WM8962_ADCL_RETUNE_C3_0 0x4605
+#define WM8962_ADCL_RETUNE_C4_1 0x4606
+#define WM8962_ADCL_RETUNE_C4_0 0x4607
+#define WM8962_ADCL_RETUNE_C5_1 0x4608
+#define WM8962_ADCL_RETUNE_C5_0 0x4609
+#define WM8962_ADCL_RETUNE_C6_1 0x460A
+#define WM8962_ADCL_RETUNE_C6_0 0x460B
+#define WM8962_ADCL_RETUNE_C7_1 0x460C
+#define WM8962_ADCL_RETUNE_C7_0 0x460D
+#define WM8962_ADCL_RETUNE_C8_1 0x460E
+#define WM8962_ADCL_RETUNE_C8_0 0x460F
+#define WM8962_ADCL_RETUNE_C9_1 0x4610
+#define WM8962_ADCL_RETUNE_C9_0 0x4611
+#define WM8962_ADCL_RETUNE_C10_1 0x4612
+#define WM8962_ADCL_RETUNE_C10_0 0x4613
+#define WM8962_ADCL_RETUNE_C11_1 0x4614
+#define WM8962_ADCL_RETUNE_C11_0 0x4615
+#define WM8962_ADCL_RETUNE_C12_1 0x4616
+#define WM8962_ADCL_RETUNE_C12_0 0x4617
+#define WM8962_ADCL_RETUNE_C13_1 0x4618
+#define WM8962_ADCL_RETUNE_C13_0 0x4619
+#define WM8962_ADCL_RETUNE_C14_1 0x461A
+#define WM8962_ADCL_RETUNE_C14_0 0x461B
+#define WM8962_ADCL_RETUNE_C15_1 0x461C
+#define WM8962_ADCL_RETUNE_C15_0 0x461D
+#define WM8962_ADCL_RETUNE_C16_1 0x461E
+#define WM8962_ADCL_RETUNE_C16_0 0x461F
+#define WM8962_ADCL_RETUNE_C17_1 0x4620
+#define WM8962_ADCL_RETUNE_C17_0 0x4621
+#define WM8962_ADCL_RETUNE_C18_1 0x4622
+#define WM8962_ADCL_RETUNE_C18_0 0x4623
+#define WM8962_ADCL_RETUNE_C19_1 0x4624
+#define WM8962_ADCL_RETUNE_C19_0 0x4625
+#define WM8962_ADCL_RETUNE_C20_1 0x4626
+#define WM8962_ADCL_RETUNE_C20_0 0x4627
+#define WM8962_ADCL_RETUNE_C21_1 0x4628
+#define WM8962_ADCL_RETUNE_C21_0 0x4629
+#define WM8962_ADCL_RETUNE_C22_1 0x462A
+#define WM8962_ADCL_RETUNE_C22_0 0x462B
+#define WM8962_ADCL_RETUNE_C23_1 0x462C
+#define WM8962_ADCL_RETUNE_C23_0 0x462D
+#define WM8962_ADCL_RETUNE_C24_1 0x462E
+#define WM8962_ADCL_RETUNE_C24_0 0x462F
+#define WM8962_ADCL_RETUNE_C25_1 0x4630
+#define WM8962_ADCL_RETUNE_C25_0 0x4631
+#define WM8962_ADCL_RETUNE_C26_1 0x4632
+#define WM8962_ADCL_RETUNE_C26_0 0x4633
+#define WM8962_ADCL_RETUNE_C27_1 0x4634
+#define WM8962_ADCL_RETUNE_C27_0 0x4635
+#define WM8962_ADCL_RETUNE_C28_1 0x4636
+#define WM8962_ADCL_RETUNE_C28_0 0x4637
+#define WM8962_ADCL_RETUNE_C29_1 0x4638
+#define WM8962_ADCL_RETUNE_C29_0 0x4639
+#define WM8962_ADCL_RETUNE_C30_1 0x463A
+#define WM8962_ADCL_RETUNE_C30_0 0x463B
+#define WM8962_ADCL_RETUNE_C31_1 0x463C
+#define WM8962_ADCL_RETUNE_C31_0 0x463D
+#define WM8962_ADCL_RETUNE_C32_1 0x463E
+#define WM8962_ADCL_RETUNE_C32_0 0x463F
+#define WM8962_RETUNEADC_PG2_1 0x4800
+#define WM8962_RETUNEADC_PG2_0 0x4801
+#define WM8962_RETUNEADC_PG_1 0x4802
+#define WM8962_RETUNEADC_PG_0 0x4803
+#define WM8962_ADCR_RETUNE_C1_1 0x4A00
+#define WM8962_ADCR_RETUNE_C1_0 0x4A01
+#define WM8962_ADCR_RETUNE_C2_1 0x4A02
+#define WM8962_ADCR_RETUNE_C2_0 0x4A03
+#define WM8962_ADCR_RETUNE_C3_1 0x4A04
+#define WM8962_ADCR_RETUNE_C3_0 0x4A05
+#define WM8962_ADCR_RETUNE_C4_1 0x4A06
+#define WM8962_ADCR_RETUNE_C4_0 0x4A07
+#define WM8962_ADCR_RETUNE_C5_1 0x4A08
+#define WM8962_ADCR_RETUNE_C5_0 0x4A09
+#define WM8962_ADCR_RETUNE_C6_1 0x4A0A
+#define WM8962_ADCR_RETUNE_C6_0 0x4A0B
+#define WM8962_ADCR_RETUNE_C7_1 0x4A0C
+#define WM8962_ADCR_RETUNE_C7_0 0x4A0D
+#define WM8962_ADCR_RETUNE_C8_1 0x4A0E
+#define WM8962_ADCR_RETUNE_C8_0 0x4A0F
+#define WM8962_ADCR_RETUNE_C9_1 0x4A10
+#define WM8962_ADCR_RETUNE_C9_0 0x4A11
+#define WM8962_ADCR_RETUNE_C10_1 0x4A12
+#define WM8962_ADCR_RETUNE_C10_0 0x4A13
+#define WM8962_ADCR_RETUNE_C11_1 0x4A14
+#define WM8962_ADCR_RETUNE_C11_0 0x4A15
+#define WM8962_ADCR_RETUNE_C12_1 0x4A16
+#define WM8962_ADCR_RETUNE_C12_0 0x4A17
+#define WM8962_ADCR_RETUNE_C13_1 0x4A18
+#define WM8962_ADCR_RETUNE_C13_0 0x4A19
+#define WM8962_ADCR_RETUNE_C14_1 0x4A1A
+#define WM8962_ADCR_RETUNE_C14_0 0x4A1B
+#define WM8962_ADCR_RETUNE_C15_1 0x4A1C
+#define WM8962_ADCR_RETUNE_C15_0 0x4A1D
+#define WM8962_ADCR_RETUNE_C16_1 0x4A1E
+#define WM8962_ADCR_RETUNE_C16_0 0x4A1F
+#define WM8962_ADCR_RETUNE_C17_1 0x4A20
+#define WM8962_ADCR_RETUNE_C17_0 0x4A21
+#define WM8962_ADCR_RETUNE_C18_1 0x4A22
+#define WM8962_ADCR_RETUNE_C18_0 0x4A23
+#define WM8962_ADCR_RETUNE_C19_1 0x4A24
+#define WM8962_ADCR_RETUNE_C19_0 0x4A25
+#define WM8962_ADCR_RETUNE_C20_1 0x4A26
+#define WM8962_ADCR_RETUNE_C20_0 0x4A27
+#define WM8962_ADCR_RETUNE_C21_1 0x4A28
+#define WM8962_ADCR_RETUNE_C21_0 0x4A29
+#define WM8962_ADCR_RETUNE_C22_1 0x4A2A
+#define WM8962_ADCR_RETUNE_C22_0 0x4A2B
+#define WM8962_ADCR_RETUNE_C23_1 0x4A2C
+#define WM8962_ADCR_RETUNE_C23_0 0x4A2D
+#define WM8962_ADCR_RETUNE_C24_1 0x4A2E
+#define WM8962_ADCR_RETUNE_C24_0 0x4A2F
+#define WM8962_ADCR_RETUNE_C25_1 0x4A30
+#define WM8962_ADCR_RETUNE_C25_0 0x4A31
+#define WM8962_ADCR_RETUNE_C26_1 0x4A32
+#define WM8962_ADCR_RETUNE_C26_0 0x4A33
+#define WM8962_ADCR_RETUNE_C27_1 0x4A34
+#define WM8962_ADCR_RETUNE_C27_0 0x4A35
+#define WM8962_ADCR_RETUNE_C28_1 0x4A36
+#define WM8962_ADCR_RETUNE_C28_0 0x4A37
+#define WM8962_ADCR_RETUNE_C29_1 0x4A38
+#define WM8962_ADCR_RETUNE_C29_0 0x4A39
+#define WM8962_ADCR_RETUNE_C30_1 0x4A3A
+#define WM8962_ADCR_RETUNE_C30_0 0x4A3B
+#define WM8962_ADCR_RETUNE_C31_1 0x4A3C
+#define WM8962_ADCR_RETUNE_C31_0 0x4A3D
+#define WM8962_ADCR_RETUNE_C32_1 0x4A3E
+#define WM8962_ADCR_RETUNE_C32_0 0x4A3F
+#define WM8962_DACL_RETUNE_C1_1 0x4C00
+#define WM8962_DACL_RETUNE_C1_0 0x4C01
+#define WM8962_DACL_RETUNE_C2_1 0x4C02
+#define WM8962_DACL_RETUNE_C2_0 0x4C03
+#define WM8962_DACL_RETUNE_C3_1 0x4C04
+#define WM8962_DACL_RETUNE_C3_0 0x4C05
+#define WM8962_DACL_RETUNE_C4_1 0x4C06
+#define WM8962_DACL_RETUNE_C4_0 0x4C07
+#define WM8962_DACL_RETUNE_C5_1 0x4C08
+#define WM8962_DACL_RETUNE_C5_0 0x4C09
+#define WM8962_DACL_RETUNE_C6_1 0x4C0A
+#define WM8962_DACL_RETUNE_C6_0 0x4C0B
+#define WM8962_DACL_RETUNE_C7_1 0x4C0C
+#define WM8962_DACL_RETUNE_C7_0 0x4C0D
+#define WM8962_DACL_RETUNE_C8_1 0x4C0E
+#define WM8962_DACL_RETUNE_C8_0 0x4C0F
+#define WM8962_DACL_RETUNE_C9_1 0x4C10
+#define WM8962_DACL_RETUNE_C9_0 0x4C11
+#define WM8962_DACL_RETUNE_C10_1 0x4C12
+#define WM8962_DACL_RETUNE_C10_0 0x4C13
+#define WM8962_DACL_RETUNE_C11_1 0x4C14
+#define WM8962_DACL_RETUNE_C11_0 0x4C15
+#define WM8962_DACL_RETUNE_C12_1 0x4C16
+#define WM8962_DACL_RETUNE_C12_0 0x4C17
+#define WM8962_DACL_RETUNE_C13_1 0x4C18
+#define WM8962_DACL_RETUNE_C13_0 0x4C19
+#define WM8962_DACL_RETUNE_C14_1 0x4C1A
+#define WM8962_DACL_RETUNE_C14_0 0x4C1B
+#define WM8962_DACL_RETUNE_C15_1 0x4C1C
+#define WM8962_DACL_RETUNE_C15_0 0x4C1D
+#define WM8962_DACL_RETUNE_C16_1 0x4C1E
+#define WM8962_DACL_RETUNE_C16_0 0x4C1F
+#define WM8962_DACL_RETUNE_C17_1 0x4C20
+#define WM8962_DACL_RETUNE_C17_0 0x4C21
+#define WM8962_DACL_RETUNE_C18_1 0x4C22
+#define WM8962_DACL_RETUNE_C18_0 0x4C23
+#define WM8962_DACL_RETUNE_C19_1 0x4C24
+#define WM8962_DACL_RETUNE_C19_0 0x4C25
+#define WM8962_DACL_RETUNE_C20_1 0x4C26
+#define WM8962_DACL_RETUNE_C20_0 0x4C27
+#define WM8962_DACL_RETUNE_C21_1 0x4C28
+#define WM8962_DACL_RETUNE_C21_0 0x4C29
+#define WM8962_DACL_RETUNE_C22_1 0x4C2A
+#define WM8962_DACL_RETUNE_C22_0 0x4C2B
+#define WM8962_DACL_RETUNE_C23_1 0x4C2C
+#define WM8962_DACL_RETUNE_C23_0 0x4C2D
+#define WM8962_DACL_RETUNE_C24_1 0x4C2E
+#define WM8962_DACL_RETUNE_C24_0 0x4C2F
+#define WM8962_DACL_RETUNE_C25_1 0x4C30
+#define WM8962_DACL_RETUNE_C25_0 0x4C31
+#define WM8962_DACL_RETUNE_C26_1 0x4C32
+#define WM8962_DACL_RETUNE_C26_0 0x4C33
+#define WM8962_DACL_RETUNE_C27_1 0x4C34
+#define WM8962_DACL_RETUNE_C27_0 0x4C35
+#define WM8962_DACL_RETUNE_C28_1 0x4C36
+#define WM8962_DACL_RETUNE_C28_0 0x4C37
+#define WM8962_DACL_RETUNE_C29_1 0x4C38
+#define WM8962_DACL_RETUNE_C29_0 0x4C39
+#define WM8962_DACL_RETUNE_C30_1 0x4C3A
+#define WM8962_DACL_RETUNE_C30_0 0x4C3B
+#define WM8962_DACL_RETUNE_C31_1 0x4C3C
+#define WM8962_DACL_RETUNE_C31_0 0x4C3D
+#define WM8962_DACL_RETUNE_C32_1 0x4C3E
+#define WM8962_DACL_RETUNE_C32_0 0x4C3F
+#define WM8962_RETUNEDAC_PG2_1 0x4E00
+#define WM8962_RETUNEDAC_PG2_0 0x4E01
+#define WM8962_RETUNEDAC_PG_1 0x4E02
+#define WM8962_RETUNEDAC_PG_0 0x4E03
+#define WM8962_DACR_RETUNE_C1_1 0x5000
+#define WM8962_DACR_RETUNE_C1_0 0x5001
+#define WM8962_DACR_RETUNE_C2_1 0x5002
+#define WM8962_DACR_RETUNE_C2_0 0x5003
+#define WM8962_DACR_RETUNE_C3_1 0x5004
+#define WM8962_DACR_RETUNE_C3_0 0x5005
+#define WM8962_DACR_RETUNE_C4_1 0x5006
+#define WM8962_DACR_RETUNE_C4_0 0x5007
+#define WM8962_DACR_RETUNE_C5_1 0x5008
+#define WM8962_DACR_RETUNE_C5_0 0x5009
+#define WM8962_DACR_RETUNE_C6_1 0x500A
+#define WM8962_DACR_RETUNE_C6_0 0x500B
+#define WM8962_DACR_RETUNE_C7_1 0x500C
+#define WM8962_DACR_RETUNE_C7_0 0x500D
+#define WM8962_DACR_RETUNE_C8_1 0x500E
+#define WM8962_DACR_RETUNE_C8_0 0x500F
+#define WM8962_DACR_RETUNE_C9_1 0x5010
+#define WM8962_DACR_RETUNE_C9_0 0x5011
+#define WM8962_DACR_RETUNE_C10_1 0x5012
+#define WM8962_DACR_RETUNE_C10_0 0x5013
+#define WM8962_DACR_RETUNE_C11_1 0x5014
+#define WM8962_DACR_RETUNE_C11_0 0x5015
+#define WM8962_DACR_RETUNE_C12_1 0x5016
+#define WM8962_DACR_RETUNE_C12_0 0x5017
+#define WM8962_DACR_RETUNE_C13_1 0x5018
+#define WM8962_DACR_RETUNE_C13_0 0x5019
+#define WM8962_DACR_RETUNE_C14_1 0x501A
+#define WM8962_DACR_RETUNE_C14_0 0x501B
+#define WM8962_DACR_RETUNE_C15_1 0x501C
+#define WM8962_DACR_RETUNE_C15_0 0x501D
+#define WM8962_DACR_RETUNE_C16_1 0x501E
+#define WM8962_DACR_RETUNE_C16_0 0x501F
+#define WM8962_DACR_RETUNE_C17_1 0x5020
+#define WM8962_DACR_RETUNE_C17_0 0x5021
+#define WM8962_DACR_RETUNE_C18_1 0x5022
+#define WM8962_DACR_RETUNE_C18_0 0x5023
+#define WM8962_DACR_RETUNE_C19_1 0x5024
+#define WM8962_DACR_RETUNE_C19_0 0x5025
+#define WM8962_DACR_RETUNE_C20_1 0x5026
+#define WM8962_DACR_RETUNE_C20_0 0x5027
+#define WM8962_DACR_RETUNE_C21_1 0x5028
+#define WM8962_DACR_RETUNE_C21_0 0x5029
+#define WM8962_DACR_RETUNE_C22_1 0x502A
+#define WM8962_DACR_RETUNE_C22_0 0x502B
+#define WM8962_DACR_RETUNE_C23_1 0x502C
+#define WM8962_DACR_RETUNE_C23_0 0x502D
+#define WM8962_DACR_RETUNE_C24_1 0x502E
+#define WM8962_DACR_RETUNE_C24_0 0x502F
+#define WM8962_DACR_RETUNE_C25_1 0x5030
+#define WM8962_DACR_RETUNE_C25_0 0x5031
+#define WM8962_DACR_RETUNE_C26_1 0x5032
+#define WM8962_DACR_RETUNE_C26_0 0x5033
+#define WM8962_DACR_RETUNE_C27_1 0x5034
+#define WM8962_DACR_RETUNE_C27_0 0x5035
+#define WM8962_DACR_RETUNE_C28_1 0x5036
+#define WM8962_DACR_RETUNE_C28_0 0x5037
+#define WM8962_DACR_RETUNE_C29_1 0x5038
+#define WM8962_DACR_RETUNE_C29_0 0x5039
+#define WM8962_DACR_RETUNE_C30_1 0x503A
+#define WM8962_DACR_RETUNE_C30_0 0x503B
+#define WM8962_DACR_RETUNE_C31_1 0x503C
+#define WM8962_DACR_RETUNE_C31_0 0x503D
+#define WM8962_DACR_RETUNE_C32_1 0x503E
+#define WM8962_DACR_RETUNE_C32_0 0x503F
+#define WM8962_VSS_XHD2_1 0x5200
+#define WM8962_VSS_XHD2_0 0x5201
+#define WM8962_VSS_XHD3_1 0x5202
+#define WM8962_VSS_XHD3_0 0x5203
+#define WM8962_VSS_XHN1_1 0x5204
+#define WM8962_VSS_XHN1_0 0x5205
+#define WM8962_VSS_XHN2_1 0x5206
+#define WM8962_VSS_XHN2_0 0x5207
+#define WM8962_VSS_XHN3_1 0x5208
+#define WM8962_VSS_XHN3_0 0x5209
+#define WM8962_VSS_XLA_1 0x520A
+#define WM8962_VSS_XLA_0 0x520B
+#define WM8962_VSS_XLB_1 0x520C
+#define WM8962_VSS_XLB_0 0x520D
+#define WM8962_VSS_XLG_1 0x520E
+#define WM8962_VSS_XLG_0 0x520F
+#define WM8962_VSS_PG2_1 0x5210
+#define WM8962_VSS_PG2_0 0x5211
+#define WM8962_VSS_PG_1 0x5212
+#define WM8962_VSS_PG_0 0x5213
+#define WM8962_VSS_XTD1_1 0x5214
+#define WM8962_VSS_XTD1_0 0x5215
+#define WM8962_VSS_XTD2_1 0x5216
+#define WM8962_VSS_XTD2_0 0x5217
+#define WM8962_VSS_XTD3_1 0x5218
+#define WM8962_VSS_XTD3_0 0x5219
+#define WM8962_VSS_XTD4_1 0x521A
+#define WM8962_VSS_XTD4_0 0x521B
+#define WM8962_VSS_XTD5_1 0x521C
+#define WM8962_VSS_XTD5_0 0x521D
+#define WM8962_VSS_XTD6_1 0x521E
+#define WM8962_VSS_XTD6_0 0x521F
+#define WM8962_VSS_XTD7_1 0x5220
+#define WM8962_VSS_XTD7_0 0x5221
+#define WM8962_VSS_XTD8_1 0x5222
+#define WM8962_VSS_XTD8_0 0x5223
+#define WM8962_VSS_XTD9_1 0x5224
+#define WM8962_VSS_XTD9_0 0x5225
+#define WM8962_VSS_XTD10_1 0x5226
+#define WM8962_VSS_XTD10_0 0x5227
+#define WM8962_VSS_XTD11_1 0x5228
+#define WM8962_VSS_XTD11_0 0x5229
+#define WM8962_VSS_XTD12_1 0x522A
+#define WM8962_VSS_XTD12_0 0x522B
+#define WM8962_VSS_XTD13_1 0x522C
+#define WM8962_VSS_XTD13_0 0x522D
+#define WM8962_VSS_XTD14_1 0x522E
+#define WM8962_VSS_XTD14_0 0x522F
+#define WM8962_VSS_XTD15_1 0x5230
+#define WM8962_VSS_XTD15_0 0x5231
+#define WM8962_VSS_XTD16_1 0x5232
+#define WM8962_VSS_XTD16_0 0x5233
+#define WM8962_VSS_XTD17_1 0x5234
+#define WM8962_VSS_XTD17_0 0x5235
+#define WM8962_VSS_XTD18_1 0x5236
+#define WM8962_VSS_XTD18_0 0x5237
+#define WM8962_VSS_XTD19_1 0x5238
+#define WM8962_VSS_XTD19_0 0x5239
+#define WM8962_VSS_XTD20_1 0x523A
+#define WM8962_VSS_XTD20_0 0x523B
+#define WM8962_VSS_XTD21_1 0x523C
+#define WM8962_VSS_XTD21_0 0x523D
+#define WM8962_VSS_XTD22_1 0x523E
+#define WM8962_VSS_XTD22_0 0x523F
+#define WM8962_VSS_XTD23_1 0x5240
+#define WM8962_VSS_XTD23_0 0x5241
+#define WM8962_VSS_XTD24_1 0x5242
+#define WM8962_VSS_XTD24_0 0x5243
+#define WM8962_VSS_XTD25_1 0x5244
+#define WM8962_VSS_XTD25_0 0x5245
+#define WM8962_VSS_XTD26_1 0x5246
+#define WM8962_VSS_XTD26_0 0x5247
+#define WM8962_VSS_XTD27_1 0x5248
+#define WM8962_VSS_XTD27_0 0x5249
+#define WM8962_VSS_XTD28_1 0x524A
+#define WM8962_VSS_XTD28_0 0x524B
+#define WM8962_VSS_XTD29_1 0x524C
+#define WM8962_VSS_XTD29_0 0x524D
+#define WM8962_VSS_XTD30_1 0x524E
+#define WM8962_VSS_XTD30_0 0x524F
+#define WM8962_VSS_XTD31_1 0x5250
+#define WM8962_VSS_XTD31_0 0x5251
+#define WM8962_VSS_XTD32_1 0x5252
+#define WM8962_VSS_XTD32_0 0x5253
+#define WM8962_VSS_XTS1_1 0x5254
+#define WM8962_VSS_XTS1_0 0x5255
+#define WM8962_VSS_XTS2_1 0x5256
+#define WM8962_VSS_XTS2_0 0x5257
+#define WM8962_VSS_XTS3_1 0x5258
+#define WM8962_VSS_XTS3_0 0x5259
+#define WM8962_VSS_XTS4_1 0x525A
+#define WM8962_VSS_XTS4_0 0x525B
+#define WM8962_VSS_XTS5_1 0x525C
+#define WM8962_VSS_XTS5_0 0x525D
+#define WM8962_VSS_XTS6_1 0x525E
+#define WM8962_VSS_XTS6_0 0x525F
+#define WM8962_VSS_XTS7_1 0x5260
+#define WM8962_VSS_XTS7_0 0x5261
+#define WM8962_VSS_XTS8_1 0x5262
+#define WM8962_VSS_XTS8_0 0x5263
+#define WM8962_VSS_XTS9_1 0x5264
+#define WM8962_VSS_XTS9_0 0x5265
+#define WM8962_VSS_XTS10_1 0x5266
+#define WM8962_VSS_XTS10_0 0x5267
+#define WM8962_VSS_XTS11_1 0x5268
+#define WM8962_VSS_XTS11_0 0x5269
+#define WM8962_VSS_XTS12_1 0x526A
+#define WM8962_VSS_XTS12_0 0x526B
+#define WM8962_VSS_XTS13_1 0x526C
+#define WM8962_VSS_XTS13_0 0x526D
+#define WM8962_VSS_XTS14_1 0x526E
+#define WM8962_VSS_XTS14_0 0x526F
+#define WM8962_VSS_XTS15_1 0x5270
+#define WM8962_VSS_XTS15_0 0x5271
+#define WM8962_VSS_XTS16_1 0x5272
+#define WM8962_VSS_XTS16_0 0x5273
+#define WM8962_VSS_XTS17_1 0x5274
+#define WM8962_VSS_XTS17_0 0x5275
+#define WM8962_VSS_XTS18_1 0x5276
+#define WM8962_VSS_XTS18_0 0x5277
+#define WM8962_VSS_XTS19_1 0x5278
+#define WM8962_VSS_XTS19_0 0x5279
+#define WM8962_VSS_XTS20_1 0x527A
+#define WM8962_VSS_XTS20_0 0x527B
+#define WM8962_VSS_XTS21_1 0x527C
+#define WM8962_VSS_XTS21_0 0x527D
+#define WM8962_VSS_XTS22_1 0x527E
+#define WM8962_VSS_XTS22_0 0x527F
+#define WM8962_VSS_XTS23_1 0x5280
+#define WM8962_VSS_XTS23_0 0x5281
+#define WM8962_VSS_XTS24_1 0x5282
+#define WM8962_VSS_XTS24_0 0x5283
+#define WM8962_VSS_XTS25_1 0x5284
+#define WM8962_VSS_XTS25_0 0x5285
+#define WM8962_VSS_XTS26_1 0x5286
+#define WM8962_VSS_XTS26_0 0x5287
+#define WM8962_VSS_XTS27_1 0x5288
+#define WM8962_VSS_XTS27_0 0x5289
+#define WM8962_VSS_XTS28_1 0x528A
+#define WM8962_VSS_XTS28_0 0x528B
+#define WM8962_VSS_XTS29_1 0x528C
+#define WM8962_VSS_XTS29_0 0x528D
+#define WM8962_VSS_XTS30_1 0x528E
+#define WM8962_VSS_XTS30_0 0x528F
+#define WM8962_VSS_XTS31_1 0x5290
+#define WM8962_VSS_XTS31_0 0x5291
+#define WM8962_VSS_XTS32_1 0x5292
+#define WM8962_VSS_XTS32_0 0x5293
+
+#define WM8962_REGISTER_COUNT 1138
+#define WM8962_MAX_REGISTER 0x5293
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Left Input volume
+ */
+#define WM8962_IN_VU 0x0100 /* IN_VU */
+#define WM8962_IN_VU_MASK 0x0100 /* IN_VU */
+#define WM8962_IN_VU_SHIFT 8 /* IN_VU */
+#define WM8962_IN_VU_WIDTH 1 /* IN_VU */
+#define WM8962_INPGAL_MUTE 0x0080 /* INPGAL_MUTE */
+#define WM8962_INPGAL_MUTE_MASK 0x0080 /* INPGAL_MUTE */
+#define WM8962_INPGAL_MUTE_SHIFT 7 /* INPGAL_MUTE */
+#define WM8962_INPGAL_MUTE_WIDTH 1 /* INPGAL_MUTE */
+#define WM8962_INL_ZC 0x0040 /* INL_ZC */
+#define WM8962_INL_ZC_MASK 0x0040 /* INL_ZC */
+#define WM8962_INL_ZC_SHIFT 6 /* INL_ZC */
+#define WM8962_INL_ZC_WIDTH 1 /* INL_ZC */
+#define WM8962_INL_VOL_MASK 0x003F /* INL_VOL - [5:0] */
+#define WM8962_INL_VOL_SHIFT 0 /* INL_VOL - [5:0] */
+#define WM8962_INL_VOL_WIDTH 6 /* INL_VOL - [5:0] */
+
+/*
+ * R1 (0x01) - Right Input volume
+ */
+#define WM8962_CUST_ID_MASK 0xF000 /* CUST_ID - [15:12] */
+#define WM8962_CUST_ID_SHIFT 12 /* CUST_ID - [15:12] */
+#define WM8962_CUST_ID_WIDTH 4 /* CUST_ID - [15:12] */
+#define WM8962_CHIP_REV_MASK 0x0E00 /* CHIP_REV - [11:9] */
+#define WM8962_CHIP_REV_SHIFT 9 /* CHIP_REV - [11:9] */
+#define WM8962_CHIP_REV_WIDTH 3 /* CHIP_REV - [11:9] */
+#define WM8962_IN_VU 0x0100 /* IN_VU */
+#define WM8962_IN_VU_MASK 0x0100 /* IN_VU */
+#define WM8962_IN_VU_SHIFT 8 /* IN_VU */
+#define WM8962_IN_VU_WIDTH 1 /* IN_VU */
+#define WM8962_INPGAR_MUTE 0x0080 /* INPGAR_MUTE */
+#define WM8962_INPGAR_MUTE_MASK 0x0080 /* INPGAR_MUTE */
+#define WM8962_INPGAR_MUTE_SHIFT 7 /* INPGAR_MUTE */
+#define WM8962_INPGAR_MUTE_WIDTH 1 /* INPGAR_MUTE */
+#define WM8962_INR_ZC 0x0040 /* INR_ZC */
+#define WM8962_INR_ZC_MASK 0x0040 /* INR_ZC */
+#define WM8962_INR_ZC_SHIFT 6 /* INR_ZC */
+#define WM8962_INR_ZC_WIDTH 1 /* INR_ZC */
+#define WM8962_INR_VOL_MASK 0x003F /* INR_VOL - [5:0] */
+#define WM8962_INR_VOL_SHIFT 0 /* INR_VOL - [5:0] */
+#define WM8962_INR_VOL_WIDTH 6 /* INR_VOL - [5:0] */
+
+/*
+ * R2 (0x02) - HPOUTL volume
+ */
+#define WM8962_HPOUT_VU 0x0100 /* HPOUT_VU */
+#define WM8962_HPOUT_VU_MASK 0x0100 /* HPOUT_VU */
+#define WM8962_HPOUT_VU_SHIFT 8 /* HPOUT_VU */
+#define WM8962_HPOUT_VU_WIDTH 1 /* HPOUT_VU */
+#define WM8962_HPOUTL_ZC 0x0080 /* HPOUTL_ZC */
+#define WM8962_HPOUTL_ZC_MASK 0x0080 /* HPOUTL_ZC */
+#define WM8962_HPOUTL_ZC_SHIFT 7 /* HPOUTL_ZC */
+#define WM8962_HPOUTL_ZC_WIDTH 1 /* HPOUTL_ZC */
+#define WM8962_HPOUTL_VOL_MASK 0x007F /* HPOUTL_VOL - [6:0] */
+#define WM8962_HPOUTL_VOL_SHIFT 0 /* HPOUTL_VOL - [6:0] */
+#define WM8962_HPOUTL_VOL_WIDTH 7 /* HPOUTL_VOL - [6:0] */
+
+/*
+ * R3 (0x03) - HPOUTR volume
+ */
+#define WM8962_HPOUT_VU 0x0100 /* HPOUT_VU */
+#define WM8962_HPOUT_VU_MASK 0x0100 /* HPOUT_VU */
+#define WM8962_HPOUT_VU_SHIFT 8 /* HPOUT_VU */
+#define WM8962_HPOUT_VU_WIDTH 1 /* HPOUT_VU */
+#define WM8962_HPOUTR_ZC 0x0080 /* HPOUTR_ZC */
+#define WM8962_HPOUTR_ZC_MASK 0x0080 /* HPOUTR_ZC */
+#define WM8962_HPOUTR_ZC_SHIFT 7 /* HPOUTR_ZC */
+#define WM8962_HPOUTR_ZC_WIDTH 1 /* HPOUTR_ZC */
+#define WM8962_HPOUTR_VOL_MASK 0x007F /* HPOUTR_VOL - [6:0] */
+#define WM8962_HPOUTR_VOL_SHIFT 0 /* HPOUTR_VOL - [6:0] */
+#define WM8962_HPOUTR_VOL_WIDTH 7 /* HPOUTR_VOL - [6:0] */
+
+/*
+ * R4 (0x04) - Clocking1
+ */
+#define WM8962_DSPCLK_DIV_MASK 0x0600 /* DSPCLK_DIV - [10:9] */
+#define WM8962_DSPCLK_DIV_SHIFT 9 /* DSPCLK_DIV - [10:9] */
+#define WM8962_DSPCLK_DIV_WIDTH 2 /* DSPCLK_DIV - [10:9] */
+#define WM8962_ADCSYS_CLK_DIV_MASK 0x01C0 /* ADCSYS_CLK_DIV - [8:6] */
+#define WM8962_ADCSYS_CLK_DIV_SHIFT 6 /* ADCSYS_CLK_DIV - [8:6] */
+#define WM8962_ADCSYS_CLK_DIV_WIDTH 3 /* ADCSYS_CLK_DIV - [8:6] */
+#define WM8962_DACSYS_CLK_DIV_MASK 0x0038 /* DACSYS_CLK_DIV - [5:3] */
+#define WM8962_DACSYS_CLK_DIV_SHIFT 3 /* DACSYS_CLK_DIV - [5:3] */
+#define WM8962_DACSYS_CLK_DIV_WIDTH 3 /* DACSYS_CLK_DIV - [5:3] */
+#define WM8962_MCLKDIV_MASK 0x0006 /* MCLKDIV - [2:1] */
+#define WM8962_MCLKDIV_SHIFT 1 /* MCLKDIV - [2:1] */
+#define WM8962_MCLKDIV_WIDTH 2 /* MCLKDIV - [2:1] */
+
+/*
+ * R5 (0x05) - ADC & DAC Control 1
+ */
+#define WM8962_ADCR_DAT_INV 0x0040 /* ADCR_DAT_INV */
+#define WM8962_ADCR_DAT_INV_MASK 0x0040 /* ADCR_DAT_INV */
+#define WM8962_ADCR_DAT_INV_SHIFT 6 /* ADCR_DAT_INV */
+#define WM8962_ADCR_DAT_INV_WIDTH 1 /* ADCR_DAT_INV */
+#define WM8962_ADCL_DAT_INV 0x0020 /* ADCL_DAT_INV */
+#define WM8962_ADCL_DAT_INV_MASK 0x0020 /* ADCL_DAT_INV */
+#define WM8962_ADCL_DAT_INV_SHIFT 5 /* ADCL_DAT_INV */
+#define WM8962_ADCL_DAT_INV_WIDTH 1 /* ADCL_DAT_INV */
+#define WM8962_DAC_MUTE_RAMP 0x0010 /* DAC_MUTE_RAMP */
+#define WM8962_DAC_MUTE_RAMP_MASK 0x0010 /* DAC_MUTE_RAMP */
+#define WM8962_DAC_MUTE_RAMP_SHIFT 4 /* DAC_MUTE_RAMP */
+#define WM8962_DAC_MUTE_RAMP_WIDTH 1 /* DAC_MUTE_RAMP */
+#define WM8962_DAC_MUTE 0x0008 /* DAC_MUTE */
+#define WM8962_DAC_MUTE_MASK 0x0008 /* DAC_MUTE */
+#define WM8962_DAC_MUTE_SHIFT 3 /* DAC_MUTE */
+#define WM8962_DAC_MUTE_WIDTH 1 /* DAC_MUTE */
+#define WM8962_DAC_DEEMP_MASK 0x0006 /* DAC_DEEMP - [2:1] */
+#define WM8962_DAC_DEEMP_SHIFT 1 /* DAC_DEEMP - [2:1] */
+#define WM8962_DAC_DEEMP_WIDTH 2 /* DAC_DEEMP - [2:1] */
+#define WM8962_ADC_HPF_DIS 0x0001 /* ADC_HPF_DIS */
+#define WM8962_ADC_HPF_DIS_MASK 0x0001 /* ADC_HPF_DIS */
+#define WM8962_ADC_HPF_DIS_SHIFT 0 /* ADC_HPF_DIS */
+#define WM8962_ADC_HPF_DIS_WIDTH 1 /* ADC_HPF_DIS */
+
+/*
+ * R6 (0x06) - ADC & DAC Control 2
+ */
+#define WM8962_ADC_HPF_SR_MASK 0x3000 /* ADC_HPF_SR - [13:12] */
+#define WM8962_ADC_HPF_SR_SHIFT 12 /* ADC_HPF_SR - [13:12] */
+#define WM8962_ADC_HPF_SR_WIDTH 2 /* ADC_HPF_SR - [13:12] */
+#define WM8962_ADC_HPF_MODE 0x0400 /* ADC_HPF_MODE */
+#define WM8962_ADC_HPF_MODE_MASK 0x0400 /* ADC_HPF_MODE */
+#define WM8962_ADC_HPF_MODE_SHIFT 10 /* ADC_HPF_MODE */
+#define WM8962_ADC_HPF_MODE_WIDTH 1 /* ADC_HPF_MODE */
+#define WM8962_ADC_HPF_CUT_MASK 0x0380 /* ADC_HPF_CUT - [9:7] */
+#define WM8962_ADC_HPF_CUT_SHIFT 7 /* ADC_HPF_CUT - [9:7] */
+#define WM8962_ADC_HPF_CUT_WIDTH 3 /* ADC_HPF_CUT - [9:7] */
+#define WM8962_DACR_DAT_INV 0x0040 /* DACR_DAT_INV */
+#define WM8962_DACR_DAT_INV_MASK 0x0040 /* DACR_DAT_INV */
+#define WM8962_DACR_DAT_INV_SHIFT 6 /* DACR_DAT_INV */
+#define WM8962_DACR_DAT_INV_WIDTH 1 /* DACR_DAT_INV */
+#define WM8962_DACL_DAT_INV 0x0020 /* DACL_DAT_INV */
+#define WM8962_DACL_DAT_INV_MASK 0x0020 /* DACL_DAT_INV */
+#define WM8962_DACL_DAT_INV_SHIFT 5 /* DACL_DAT_INV */
+#define WM8962_DACL_DAT_INV_WIDTH 1 /* DACL_DAT_INV */
+#define WM8962_DAC_UNMUTE_RAMP 0x0008 /* DAC_UNMUTE_RAMP */
+#define WM8962_DAC_UNMUTE_RAMP_MASK 0x0008 /* DAC_UNMUTE_RAMP */
+#define WM8962_DAC_UNMUTE_RAMP_SHIFT 3 /* DAC_UNMUTE_RAMP */
+#define WM8962_DAC_UNMUTE_RAMP_WIDTH 1 /* DAC_UNMUTE_RAMP */
+#define WM8962_DAC_MUTERATE 0x0004 /* DAC_MUTERATE */
+#define WM8962_DAC_MUTERATE_MASK 0x0004 /* DAC_MUTERATE */
+#define WM8962_DAC_MUTERATE_SHIFT 2 /* DAC_MUTERATE */
+#define WM8962_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
+#define WM8962_DAC_HP 0x0001 /* DAC_HP */
+#define WM8962_DAC_HP_MASK 0x0001 /* DAC_HP */
+#define WM8962_DAC_HP_SHIFT 0 /* DAC_HP */
+#define WM8962_DAC_HP_WIDTH 1 /* DAC_HP */
+
+/*
+ * R7 (0x07) - Audio Interface 0
+ */
+#define WM8962_AIFDAC_TDM_MODE 0x1000 /* AIFDAC_TDM_MODE */
+#define WM8962_AIFDAC_TDM_MODE_MASK 0x1000 /* AIFDAC_TDM_MODE */
+#define WM8962_AIFDAC_TDM_MODE_SHIFT 12 /* AIFDAC_TDM_MODE */
+#define WM8962_AIFDAC_TDM_MODE_WIDTH 1 /* AIFDAC_TDM_MODE */
+#define WM8962_AIFDAC_TDM_SLOT 0x0800 /* AIFDAC_TDM_SLOT */
+#define WM8962_AIFDAC_TDM_SLOT_MASK 0x0800 /* AIFDAC_TDM_SLOT */
+#define WM8962_AIFDAC_TDM_SLOT_SHIFT 11 /* AIFDAC_TDM_SLOT */
+#define WM8962_AIFDAC_TDM_SLOT_WIDTH 1 /* AIFDAC_TDM_SLOT */
+#define WM8962_AIFADC_TDM_MODE 0x0400 /* AIFADC_TDM_MODE */
+#define WM8962_AIFADC_TDM_MODE_MASK 0x0400 /* AIFADC_TDM_MODE */
+#define WM8962_AIFADC_TDM_MODE_SHIFT 10 /* AIFADC_TDM_MODE */
+#define WM8962_AIFADC_TDM_MODE_WIDTH 1 /* AIFADC_TDM_MODE */
+#define WM8962_AIFADC_TDM_SLOT 0x0200 /* AIFADC_TDM_SLOT */
+#define WM8962_AIFADC_TDM_SLOT_MASK 0x0200 /* AIFADC_TDM_SLOT */
+#define WM8962_AIFADC_TDM_SLOT_SHIFT 9 /* AIFADC_TDM_SLOT */
+#define WM8962_AIFADC_TDM_SLOT_WIDTH 1 /* AIFADC_TDM_SLOT */
+#define WM8962_ADC_LRSWAP 0x0100 /* ADC_LRSWAP */
+#define WM8962_ADC_LRSWAP_MASK 0x0100 /* ADC_LRSWAP */
+#define WM8962_ADC_LRSWAP_SHIFT 8 /* ADC_LRSWAP */
+#define WM8962_ADC_LRSWAP_WIDTH 1 /* ADC_LRSWAP */
+#define WM8962_BCLK_INV 0x0080 /* BCLK_INV */
+#define WM8962_BCLK_INV_MASK 0x0080 /* BCLK_INV */
+#define WM8962_BCLK_INV_SHIFT 7 /* BCLK_INV */
+#define WM8962_BCLK_INV_WIDTH 1 /* BCLK_INV */
+#define WM8962_MSTR 0x0040 /* MSTR */
+#define WM8962_MSTR_MASK 0x0040 /* MSTR */
+#define WM8962_MSTR_SHIFT 6 /* MSTR */
+#define WM8962_MSTR_WIDTH 1 /* MSTR */
+#define WM8962_DAC_LRSWAP 0x0020 /* DAC_LRSWAP */
+#define WM8962_DAC_LRSWAP_MASK 0x0020 /* DAC_LRSWAP */
+#define WM8962_DAC_LRSWAP_SHIFT 5 /* DAC_LRSWAP */
+#define WM8962_DAC_LRSWAP_WIDTH 1 /* DAC_LRSWAP */
+#define WM8962_LRCLK_INV 0x0010 /* LRCLK_INV */
+#define WM8962_LRCLK_INV_MASK 0x0010 /* LRCLK_INV */
+#define WM8962_LRCLK_INV_SHIFT 4 /* LRCLK_INV */
+#define WM8962_LRCLK_INV_WIDTH 1 /* LRCLK_INV */
+#define WM8962_WL_MASK 0x000C /* WL - [3:2] */
+#define WM8962_WL_SHIFT 2 /* WL - [3:2] */
+#define WM8962_WL_WIDTH 2 /* WL - [3:2] */
+#define WM8962_FMT_MASK 0x0003 /* FMT - [1:0] */
+#define WM8962_FMT_SHIFT 0 /* FMT - [1:0] */
+#define WM8962_FMT_WIDTH 2 /* FMT - [1:0] */
+
+/*
+ * R8 (0x08) - Clocking2
+ */
+#define WM8962_CLKREG_OVD 0x0800 /* CLKREG_OVD */
+#define WM8962_CLKREG_OVD_MASK 0x0800 /* CLKREG_OVD */
+#define WM8962_CLKREG_OVD_SHIFT 11 /* CLKREG_OVD */
+#define WM8962_CLKREG_OVD_WIDTH 1 /* CLKREG_OVD */
+#define WM8962_SYSCLK_SRC_MASK 0x0600 /* SYSCLK_SRC - [10:9] */
+#define WM8962_SYSCLK_SRC_SHIFT 9 /* SYSCLK_SRC - [10:9] */
+#define WM8962_SYSCLK_SRC_WIDTH 2 /* SYSCLK_SRC - [10:9] */
+#define WM8962_CLASSD_CLK_DIV_MASK 0x01C0 /* CLASSD_CLK_DIV - [8:6] */
+#define WM8962_CLASSD_CLK_DIV_SHIFT 6 /* CLASSD_CLK_DIV - [8:6] */
+#define WM8962_CLASSD_CLK_DIV_WIDTH 3 /* CLASSD_CLK_DIV - [8:6] */
+#define WM8962_SYSCLK_ENA 0x0020 /* SYSCLK_ENA */
+#define WM8962_SYSCLK_ENA_MASK 0x0020 /* SYSCLK_ENA */
+#define WM8962_SYSCLK_ENA_SHIFT 5 /* SYSCLK_ENA */
+#define WM8962_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */
+#define WM8962_BCLK_DIV_MASK 0x000F /* BCLK_DIV - [3:0] */
+#define WM8962_BCLK_DIV_SHIFT 0 /* BCLK_DIV - [3:0] */
+#define WM8962_BCLK_DIV_WIDTH 4 /* BCLK_DIV - [3:0] */
+
+/*
+ * R9 (0x09) - Audio Interface 1
+ */
+#define WM8962_AUTOMUTE_STS 0x0800 /* AUTOMUTE_STS */
+#define WM8962_AUTOMUTE_STS_MASK 0x0800 /* AUTOMUTE_STS */
+#define WM8962_AUTOMUTE_STS_SHIFT 11 /* AUTOMUTE_STS */
+#define WM8962_AUTOMUTE_STS_WIDTH 1 /* AUTOMUTE_STS */
+#define WM8962_DAC_AUTOMUTE_SAMPLES_MASK 0x0300 /* DAC_AUTOMUTE_SAMPLES - [9:8] */
+#define WM8962_DAC_AUTOMUTE_SAMPLES_SHIFT 8 /* DAC_AUTOMUTE_SAMPLES - [9:8] */
+#define WM8962_DAC_AUTOMUTE_SAMPLES_WIDTH 2 /* DAC_AUTOMUTE_SAMPLES - [9:8] */
+#define WM8962_DAC_AUTOMUTE 0x0080 /* DAC_AUTOMUTE */
+#define WM8962_DAC_AUTOMUTE_MASK 0x0080 /* DAC_AUTOMUTE */
+#define WM8962_DAC_AUTOMUTE_SHIFT 7 /* DAC_AUTOMUTE */
+#define WM8962_DAC_AUTOMUTE_WIDTH 1 /* DAC_AUTOMUTE */
+#define WM8962_DAC_COMP 0x0010 /* DAC_COMP */
+#define WM8962_DAC_COMP_MASK 0x0010 /* DAC_COMP */
+#define WM8962_DAC_COMP_SHIFT 4 /* DAC_COMP */
+#define WM8962_DAC_COMP_WIDTH 1 /* DAC_COMP */
+#define WM8962_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */
+#define WM8962_DAC_COMPMODE_MASK 0x0008 /* DAC_COMPMODE */
+#define WM8962_DAC_COMPMODE_SHIFT 3 /* DAC_COMPMODE */
+#define WM8962_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */
+#define WM8962_ADC_COMP 0x0004 /* ADC_COMP */
+#define WM8962_ADC_COMP_MASK 0x0004 /* ADC_COMP */
+#define WM8962_ADC_COMP_SHIFT 2 /* ADC_COMP */
+#define WM8962_ADC_COMP_WIDTH 1 /* ADC_COMP */
+#define WM8962_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */
+#define WM8962_ADC_COMPMODE_MASK 0x0002 /* ADC_COMPMODE */
+#define WM8962_ADC_COMPMODE_SHIFT 1 /* ADC_COMPMODE */
+#define WM8962_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */
+#define WM8962_LOOPBACK 0x0001 /* LOOPBACK */
+#define WM8962_LOOPBACK_MASK 0x0001 /* LOOPBACK */
+#define WM8962_LOOPBACK_SHIFT 0 /* LOOPBACK */
+#define WM8962_LOOPBACK_WIDTH 1 /* LOOPBACK */
+
+/*
+ * R10 (0x0A) - Left DAC volume
+ */
+#define WM8962_DAC_VU 0x0100 /* DAC_VU */
+#define WM8962_DAC_VU_MASK 0x0100 /* DAC_VU */
+#define WM8962_DAC_VU_SHIFT 8 /* DAC_VU */
+#define WM8962_DAC_VU_WIDTH 1 /* DAC_VU */
+#define WM8962_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */
+#define WM8962_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */
+#define WM8962_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */
+
+/*
+ * R11 (0x0B) - Right DAC volume
+ */
+#define WM8962_DAC_VU 0x0100 /* DAC_VU */
+#define WM8962_DAC_VU_MASK 0x0100 /* DAC_VU */
+#define WM8962_DAC_VU_SHIFT 8 /* DAC_VU */
+#define WM8962_DAC_VU_WIDTH 1 /* DAC_VU */
+#define WM8962_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */
+#define WM8962_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */
+#define WM8962_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */
+
+/*
+ * R14 (0x0E) - Audio Interface 2
+ */
+#define WM8962_AIF_RATE_MASK 0x07FF /* AIF_RATE - [10:0] */
+#define WM8962_AIF_RATE_SHIFT 0 /* AIF_RATE - [10:0] */
+#define WM8962_AIF_RATE_WIDTH 11 /* AIF_RATE - [10:0] */
+
+/*
+ * R15 (0x0F) - Software Reset
+ */
+#define WM8962_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */
+#define WM8962_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */
+#define WM8962_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */
+
+/*
+ * R17 (0x11) - ALC1
+ */
+#define WM8962_ALC_INACTIVE_ENA 0x0400 /* ALC_INACTIVE_ENA */
+#define WM8962_ALC_INACTIVE_ENA_MASK 0x0400 /* ALC_INACTIVE_ENA */
+#define WM8962_ALC_INACTIVE_ENA_SHIFT 10 /* ALC_INACTIVE_ENA */
+#define WM8962_ALC_INACTIVE_ENA_WIDTH 1 /* ALC_INACTIVE_ENA */
+#define WM8962_ALC_LVL_MODE 0x0200 /* ALC_LVL_MODE */
+#define WM8962_ALC_LVL_MODE_MASK 0x0200 /* ALC_LVL_MODE */
+#define WM8962_ALC_LVL_MODE_SHIFT 9 /* ALC_LVL_MODE */
+#define WM8962_ALC_LVL_MODE_WIDTH 1 /* ALC_LVL_MODE */
+#define WM8962_ALCL_ENA 0x0100 /* ALCL_ENA */
+#define WM8962_ALCL_ENA_MASK 0x0100 /* ALCL_ENA */
+#define WM8962_ALCL_ENA_SHIFT 8 /* ALCL_ENA */
+#define WM8962_ALCL_ENA_WIDTH 1 /* ALCL_ENA */
+#define WM8962_ALCR_ENA 0x0080 /* ALCR_ENA */
+#define WM8962_ALCR_ENA_MASK 0x0080 /* ALCR_ENA */
+#define WM8962_ALCR_ENA_SHIFT 7 /* ALCR_ENA */
+#define WM8962_ALCR_ENA_WIDTH 1 /* ALCR_ENA */
+#define WM8962_ALC_MAXGAIN_MASK 0x0070 /* ALC_MAXGAIN - [6:4] */
+#define WM8962_ALC_MAXGAIN_SHIFT 4 /* ALC_MAXGAIN - [6:4] */
+#define WM8962_ALC_MAXGAIN_WIDTH 3 /* ALC_MAXGAIN - [6:4] */
+#define WM8962_ALC_LVL_MASK 0x000F /* ALC_LVL - [3:0] */
+#define WM8962_ALC_LVL_SHIFT 0 /* ALC_LVL - [3:0] */
+#define WM8962_ALC_LVL_WIDTH 4 /* ALC_LVL - [3:0] */
+
+/*
+ * R18 (0x12) - ALC2
+ */
+#define WM8962_ALC_LOCK_STS 0x8000 /* ALC_LOCK_STS */
+#define WM8962_ALC_LOCK_STS_MASK 0x8000 /* ALC_LOCK_STS */
+#define WM8962_ALC_LOCK_STS_SHIFT 15 /* ALC_LOCK_STS */
+#define WM8962_ALC_LOCK_STS_WIDTH 1 /* ALC_LOCK_STS */
+#define WM8962_ALC_THRESH_STS 0x4000 /* ALC_THRESH_STS */
+#define WM8962_ALC_THRESH_STS_MASK 0x4000 /* ALC_THRESH_STS */
+#define WM8962_ALC_THRESH_STS_SHIFT 14 /* ALC_THRESH_STS */
+#define WM8962_ALC_THRESH_STS_WIDTH 1 /* ALC_THRESH_STS */
+#define WM8962_ALC_SAT_STS 0x2000 /* ALC_SAT_STS */
+#define WM8962_ALC_SAT_STS_MASK 0x2000 /* ALC_SAT_STS */
+#define WM8962_ALC_SAT_STS_SHIFT 13 /* ALC_SAT_STS */
+#define WM8962_ALC_SAT_STS_WIDTH 1 /* ALC_SAT_STS */
+#define WM8962_ALC_PKOVR_STS 0x1000 /* ALC_PKOVR_STS */
+#define WM8962_ALC_PKOVR_STS_MASK 0x1000 /* ALC_PKOVR_STS */
+#define WM8962_ALC_PKOVR_STS_SHIFT 12 /* ALC_PKOVR_STS */
+#define WM8962_ALC_PKOVR_STS_WIDTH 1 /* ALC_PKOVR_STS */
+#define WM8962_ALC_NGATE_STS 0x0800 /* ALC_NGATE_STS */
+#define WM8962_ALC_NGATE_STS_MASK 0x0800 /* ALC_NGATE_STS */
+#define WM8962_ALC_NGATE_STS_SHIFT 11 /* ALC_NGATE_STS */
+#define WM8962_ALC_NGATE_STS_WIDTH 1 /* ALC_NGATE_STS */
+#define WM8962_ALC_ZC 0x0080 /* ALC_ZC */
+#define WM8962_ALC_ZC_MASK 0x0080 /* ALC_ZC */
+#define WM8962_ALC_ZC_SHIFT 7 /* ALC_ZC */
+#define WM8962_ALC_ZC_WIDTH 1 /* ALC_ZC */
+#define WM8962_ALC_MINGAIN_MASK 0x0070 /* ALC_MINGAIN - [6:4] */
+#define WM8962_ALC_MINGAIN_SHIFT 4 /* ALC_MINGAIN - [6:4] */
+#define WM8962_ALC_MINGAIN_WIDTH 3 /* ALC_MINGAIN - [6:4] */
+#define WM8962_ALC_HLD_MASK 0x000F /* ALC_HLD - [3:0] */
+#define WM8962_ALC_HLD_SHIFT 0 /* ALC_HLD - [3:0] */
+#define WM8962_ALC_HLD_WIDTH 4 /* ALC_HLD - [3:0] */
+
+/*
+ * R19 (0x13) - ALC3
+ */
+#define WM8962_ALC_NGATE_GAIN_MASK 0x1C00 /* ALC_NGATE_GAIN - [12:10] */
+#define WM8962_ALC_NGATE_GAIN_SHIFT 10 /* ALC_NGATE_GAIN - [12:10] */
+#define WM8962_ALC_NGATE_GAIN_WIDTH 3 /* ALC_NGATE_GAIN - [12:10] */
+#define WM8962_ALC_MODE 0x0100 /* ALC_MODE */
+#define WM8962_ALC_MODE_MASK 0x0100 /* ALC_MODE */
+#define WM8962_ALC_MODE_SHIFT 8 /* ALC_MODE */
+#define WM8962_ALC_MODE_WIDTH 1 /* ALC_MODE */
+#define WM8962_ALC_DCY_MASK 0x00F0 /* ALC_DCY - [7:4] */
+#define WM8962_ALC_DCY_SHIFT 4 /* ALC_DCY - [7:4] */
+#define WM8962_ALC_DCY_WIDTH 4 /* ALC_DCY - [7:4] */
+#define WM8962_ALC_ATK_MASK 0x000F /* ALC_ATK - [3:0] */
+#define WM8962_ALC_ATK_SHIFT 0 /* ALC_ATK - [3:0] */
+#define WM8962_ALC_ATK_WIDTH 4 /* ALC_ATK - [3:0] */
+
+/*
+ * R20 (0x14) - Noise Gate
+ */
+#define WM8962_ALC_NGATE_DCY_MASK 0xF000 /* ALC_NGATE_DCY - [15:12] */
+#define WM8962_ALC_NGATE_DCY_SHIFT 12 /* ALC_NGATE_DCY - [15:12] */
+#define WM8962_ALC_NGATE_DCY_WIDTH 4 /* ALC_NGATE_DCY - [15:12] */
+#define WM8962_ALC_NGATE_ATK_MASK 0x0F00 /* ALC_NGATE_ATK - [11:8] */
+#define WM8962_ALC_NGATE_ATK_SHIFT 8 /* ALC_NGATE_ATK - [11:8] */
+#define WM8962_ALC_NGATE_ATK_WIDTH 4 /* ALC_NGATE_ATK - [11:8] */
+#define WM8962_ALC_NGATE_THR_MASK 0x00F8 /* ALC_NGATE_THR - [7:3] */
+#define WM8962_ALC_NGATE_THR_SHIFT 3 /* ALC_NGATE_THR - [7:3] */
+#define WM8962_ALC_NGATE_THR_WIDTH 5 /* ALC_NGATE_THR - [7:3] */
+#define WM8962_ALC_NGATE_MODE_MASK 0x0006 /* ALC_NGATE_MODE - [2:1] */
+#define WM8962_ALC_NGATE_MODE_SHIFT 1 /* ALC_NGATE_MODE - [2:1] */
+#define WM8962_ALC_NGATE_MODE_WIDTH 2 /* ALC_NGATE_MODE - [2:1] */
+#define WM8962_ALC_NGATE_ENA 0x0001 /* ALC_NGATE_ENA */
+#define WM8962_ALC_NGATE_ENA_MASK 0x0001 /* ALC_NGATE_ENA */
+#define WM8962_ALC_NGATE_ENA_SHIFT 0 /* ALC_NGATE_ENA */
+#define WM8962_ALC_NGATE_ENA_WIDTH 1 /* ALC_NGATE_ENA */
+
+/*
+ * R21 (0x15) - Left ADC volume
+ */
+#define WM8962_ADC_VU 0x0100 /* ADC_VU */
+#define WM8962_ADC_VU_MASK 0x0100 /* ADC_VU */
+#define WM8962_ADC_VU_SHIFT 8 /* ADC_VU */
+#define WM8962_ADC_VU_WIDTH 1 /* ADC_VU */
+#define WM8962_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */
+#define WM8962_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */
+#define WM8962_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */
+
+/*
+ * R22 (0x16) - Right ADC volume
+ */
+#define WM8962_ADC_VU 0x0100 /* ADC_VU */
+#define WM8962_ADC_VU_MASK 0x0100 /* ADC_VU */
+#define WM8962_ADC_VU_SHIFT 8 /* ADC_VU */
+#define WM8962_ADC_VU_WIDTH 1 /* ADC_VU */
+#define WM8962_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */
+#define WM8962_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */
+#define WM8962_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */
+
+/*
+ * R23 (0x17) - Additional control(1)
+ */
+#define WM8962_THERR_ACT 0x0100 /* THERR_ACT */
+#define WM8962_THERR_ACT_MASK 0x0100 /* THERR_ACT */
+#define WM8962_THERR_ACT_SHIFT 8 /* THERR_ACT */
+#define WM8962_THERR_ACT_WIDTH 1 /* THERR_ACT */
+#define WM8962_ADC_BIAS 0x0040 /* ADC_BIAS */
+#define WM8962_ADC_BIAS_MASK 0x0040 /* ADC_BIAS */
+#define WM8962_ADC_BIAS_SHIFT 6 /* ADC_BIAS */
+#define WM8962_ADC_BIAS_WIDTH 1 /* ADC_BIAS */
+#define WM8962_ADC_HP 0x0020 /* ADC_HP */
+#define WM8962_ADC_HP_MASK 0x0020 /* ADC_HP */
+#define WM8962_ADC_HP_SHIFT 5 /* ADC_HP */
+#define WM8962_ADC_HP_WIDTH 1 /* ADC_HP */
+#define WM8962_TOCLK_ENA 0x0001 /* TOCLK_ENA */
+#define WM8962_TOCLK_ENA_MASK 0x0001 /* TOCLK_ENA */
+#define WM8962_TOCLK_ENA_SHIFT 0 /* TOCLK_ENA */
+#define WM8962_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */
+
+/*
+ * R24 (0x18) - Additional control(2)
+ */
+#define WM8962_AIF_TRI 0x0008 /* AIF_TRI */
+#define WM8962_AIF_TRI_MASK 0x0008 /* AIF_TRI */
+#define WM8962_AIF_TRI_SHIFT 3 /* AIF_TRI */
+#define WM8962_AIF_TRI_WIDTH 1 /* AIF_TRI */
+
+/*
+ * R25 (0x19) - Pwr Mgmt (1)
+ */
+#define WM8962_DMIC_ENA 0x0400 /* DMIC_ENA */
+#define WM8962_DMIC_ENA_MASK 0x0400 /* DMIC_ENA */
+#define WM8962_DMIC_ENA_SHIFT 10 /* DMIC_ENA */
+#define WM8962_DMIC_ENA_WIDTH 1 /* DMIC_ENA */
+#define WM8962_OPCLK_ENA 0x0200 /* OPCLK_ENA */
+#define WM8962_OPCLK_ENA_MASK 0x0200 /* OPCLK_ENA */
+#define WM8962_OPCLK_ENA_SHIFT 9 /* OPCLK_ENA */
+#define WM8962_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */
+#define WM8962_VMID_SEL_MASK 0x0180 /* VMID_SEL - [8:7] */
+#define WM8962_VMID_SEL_SHIFT 7 /* VMID_SEL - [8:7] */
+#define WM8962_VMID_SEL_WIDTH 2 /* VMID_SEL - [8:7] */
+#define WM8962_BIAS_ENA 0x0040 /* BIAS_ENA */
+#define WM8962_BIAS_ENA_MASK 0x0040 /* BIAS_ENA */
+#define WM8962_BIAS_ENA_SHIFT 6 /* BIAS_ENA */
+#define WM8962_BIAS_ENA_WIDTH 1 /* BIAS_ENA */
+#define WM8962_INL_ENA 0x0020 /* INL_ENA */
+#define WM8962_INL_ENA_MASK 0x0020 /* INL_ENA */
+#define WM8962_INL_ENA_SHIFT 5 /* INL_ENA */
+#define WM8962_INL_ENA_WIDTH 1 /* INL_ENA */
+#define WM8962_INR_ENA 0x0010 /* INR_ENA */
+#define WM8962_INR_ENA_MASK 0x0010 /* INR_ENA */
+#define WM8962_INR_ENA_SHIFT 4 /* INR_ENA */
+#define WM8962_INR_ENA_WIDTH 1 /* INR_ENA */
+#define WM8962_ADCL_ENA 0x0008 /* ADCL_ENA */
+#define WM8962_ADCL_ENA_MASK 0x0008 /* ADCL_ENA */
+#define WM8962_ADCL_ENA_SHIFT 3 /* ADCL_ENA */
+#define WM8962_ADCL_ENA_WIDTH 1 /* ADCL_ENA */
+#define WM8962_ADCR_ENA 0x0004 /* ADCR_ENA */
+#define WM8962_ADCR_ENA_MASK 0x0004 /* ADCR_ENA */
+#define WM8962_ADCR_ENA_SHIFT 2 /* ADCR_ENA */
+#define WM8962_ADCR_ENA_WIDTH 1 /* ADCR_ENA */
+#define WM8962_MICBIAS_ENA 0x0002 /* MICBIAS_ENA */
+#define WM8962_MICBIAS_ENA_MASK 0x0002 /* MICBIAS_ENA */
+#define WM8962_MICBIAS_ENA_SHIFT 1 /* MICBIAS_ENA */
+#define WM8962_MICBIAS_ENA_WIDTH 1 /* MICBIAS_ENA */
+
+/*
+ * R26 (0x1A) - Pwr Mgmt (2)
+ */
+#define WM8962_DACL_ENA 0x0100 /* DACL_ENA */
+#define WM8962_DACL_ENA_MASK 0x0100 /* DACL_ENA */
+#define WM8962_DACL_ENA_SHIFT 8 /* DACL_ENA */
+#define WM8962_DACL_ENA_WIDTH 1 /* DACL_ENA */
+#define WM8962_DACR_ENA 0x0080 /* DACR_ENA */
+#define WM8962_DACR_ENA_MASK 0x0080 /* DACR_ENA */
+#define WM8962_DACR_ENA_SHIFT 7 /* DACR_ENA */
+#define WM8962_DACR_ENA_WIDTH 1 /* DACR_ENA */
+#define WM8962_HPOUTL_PGA_ENA 0x0040 /* HPOUTL_PGA_ENA */
+#define WM8962_HPOUTL_PGA_ENA_MASK 0x0040 /* HPOUTL_PGA_ENA */
+#define WM8962_HPOUTL_PGA_ENA_SHIFT 6 /* HPOUTL_PGA_ENA */
+#define WM8962_HPOUTL_PGA_ENA_WIDTH 1 /* HPOUTL_PGA_ENA */
+#define WM8962_HPOUTR_PGA_ENA 0x0020 /* HPOUTR_PGA_ENA */
+#define WM8962_HPOUTR_PGA_ENA_MASK 0x0020 /* HPOUTR_PGA_ENA */
+#define WM8962_HPOUTR_PGA_ENA_SHIFT 5 /* HPOUTR_PGA_ENA */
+#define WM8962_HPOUTR_PGA_ENA_WIDTH 1 /* HPOUTR_PGA_ENA */
+#define WM8962_SPKOUTL_PGA_ENA 0x0010 /* SPKOUTL_PGA_ENA */
+#define WM8962_SPKOUTL_PGA_ENA_MASK 0x0010 /* SPKOUTL_PGA_ENA */
+#define WM8962_SPKOUTL_PGA_ENA_SHIFT 4 /* SPKOUTL_PGA_ENA */
+#define WM8962_SPKOUTL_PGA_ENA_WIDTH 1 /* SPKOUTL_PGA_ENA */
+#define WM8962_SPKOUTR_PGA_ENA 0x0008 /* SPKOUTR_PGA_ENA */
+#define WM8962_SPKOUTR_PGA_ENA_MASK 0x0008 /* SPKOUTR_PGA_ENA */
+#define WM8962_SPKOUTR_PGA_ENA_SHIFT 3 /* SPKOUTR_PGA_ENA */
+#define WM8962_SPKOUTR_PGA_ENA_WIDTH 1 /* SPKOUTR_PGA_ENA */
+#define WM8962_HPOUTL_PGA_MUTE 0x0002 /* HPOUTL_PGA_MUTE */
+#define WM8962_HPOUTL_PGA_MUTE_MASK 0x0002 /* HPOUTL_PGA_MUTE */
+#define WM8962_HPOUTL_PGA_MUTE_SHIFT 1 /* HPOUTL_PGA_MUTE */
+#define WM8962_HPOUTL_PGA_MUTE_WIDTH 1 /* HPOUTL_PGA_MUTE */
+#define WM8962_HPOUTR_PGA_MUTE 0x0001 /* HPOUTR_PGA_MUTE */
+#define WM8962_HPOUTR_PGA_MUTE_MASK 0x0001 /* HPOUTR_PGA_MUTE */
+#define WM8962_HPOUTR_PGA_MUTE_SHIFT 0 /* HPOUTR_PGA_MUTE */
+#define WM8962_HPOUTR_PGA_MUTE_WIDTH 1 /* HPOUTR_PGA_MUTE */
+
+/*
+ * R27 (0x1B) - Additional Control (3)
+ */
+#define WM8962_SAMPLE_RATE_INT_MODE 0x0010 /* SAMPLE_RATE_INT_MODE */
+#define WM8962_SAMPLE_RATE_INT_MODE_MASK 0x0010 /* SAMPLE_RATE_INT_MODE */
+#define WM8962_SAMPLE_RATE_INT_MODE_SHIFT 4 /* SAMPLE_RATE_INT_MODE */
+#define WM8962_SAMPLE_RATE_INT_MODE_WIDTH 1 /* SAMPLE_RATE_INT_MODE */
+#define WM8962_SAMPLE_RATE_MASK 0x0007 /* SAMPLE_RATE - [2:0] */
+#define WM8962_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [2:0] */
+#define WM8962_SAMPLE_RATE_WIDTH 3 /* SAMPLE_RATE - [2:0] */
+
+/*
+ * R28 (0x1C) - Anti-pop
+ */
+#define WM8962_STARTUP_BIAS_ENA 0x0010 /* STARTUP_BIAS_ENA */
+#define WM8962_STARTUP_BIAS_ENA_MASK 0x0010 /* STARTUP_BIAS_ENA */
+#define WM8962_STARTUP_BIAS_ENA_SHIFT 4 /* STARTUP_BIAS_ENA */
+#define WM8962_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */
+#define WM8962_VMID_BUF_ENA 0x0008 /* VMID_BUF_ENA */
+#define WM8962_VMID_BUF_ENA_MASK 0x0008 /* VMID_BUF_ENA */
+#define WM8962_VMID_BUF_ENA_SHIFT 3 /* VMID_BUF_ENA */
+#define WM8962_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */
+#define WM8962_VMID_RAMP 0x0004 /* VMID_RAMP */
+#define WM8962_VMID_RAMP_MASK 0x0004 /* VMID_RAMP */
+#define WM8962_VMID_RAMP_SHIFT 2 /* VMID_RAMP */
+#define WM8962_VMID_RAMP_WIDTH 1 /* VMID_RAMP */
+
+/*
+ * R30 (0x1E) - Clocking 3
+ */
+#define WM8962_DBCLK_DIV_MASK 0xE000 /* DBCLK_DIV - [15:13] */
+#define WM8962_DBCLK_DIV_SHIFT 13 /* DBCLK_DIV - [15:13] */
+#define WM8962_DBCLK_DIV_WIDTH 3 /* DBCLK_DIV - [15:13] */
+#define WM8962_OPCLK_DIV_MASK 0x1C00 /* OPCLK_DIV - [12:10] */
+#define WM8962_OPCLK_DIV_SHIFT 10 /* OPCLK_DIV - [12:10] */
+#define WM8962_OPCLK_DIV_WIDTH 3 /* OPCLK_DIV - [12:10] */
+#define WM8962_TOCLK_DIV_MASK 0x0380 /* TOCLK_DIV - [9:7] */
+#define WM8962_TOCLK_DIV_SHIFT 7 /* TOCLK_DIV - [9:7] */
+#define WM8962_TOCLK_DIV_WIDTH 3 /* TOCLK_DIV - [9:7] */
+#define WM8962_F256KCLK_DIV_MASK 0x007E /* F256KCLK_DIV - [6:1] */
+#define WM8962_F256KCLK_DIV_SHIFT 1 /* F256KCLK_DIV - [6:1] */
+#define WM8962_F256KCLK_DIV_WIDTH 6 /* F256KCLK_DIV - [6:1] */
+
+/*
+ * R31 (0x1F) - Input mixer control (1)
+ */
+#define WM8962_MIXINL_MUTE 0x0008 /* MIXINL_MUTE */
+#define WM8962_MIXINL_MUTE_MASK 0x0008 /* MIXINL_MUTE */
+#define WM8962_MIXINL_MUTE_SHIFT 3 /* MIXINL_MUTE */
+#define WM8962_MIXINL_MUTE_WIDTH 1 /* MIXINL_MUTE */
+#define WM8962_MIXINR_MUTE 0x0004 /* MIXINR_MUTE */
+#define WM8962_MIXINR_MUTE_MASK 0x0004 /* MIXINR_MUTE */
+#define WM8962_MIXINR_MUTE_SHIFT 2 /* MIXINR_MUTE */
+#define WM8962_MIXINR_MUTE_WIDTH 1 /* MIXINR_MUTE */
+#define WM8962_MIXINL_ENA 0x0002 /* MIXINL_ENA */
+#define WM8962_MIXINL_ENA_MASK 0x0002 /* MIXINL_ENA */
+#define WM8962_MIXINL_ENA_SHIFT 1 /* MIXINL_ENA */
+#define WM8962_MIXINL_ENA_WIDTH 1 /* MIXINL_ENA */
+#define WM8962_MIXINR_ENA 0x0001 /* MIXINR_ENA */
+#define WM8962_MIXINR_ENA_MASK 0x0001 /* MIXINR_ENA */
+#define WM8962_MIXINR_ENA_SHIFT 0 /* MIXINR_ENA */
+#define WM8962_MIXINR_ENA_WIDTH 1 /* MIXINR_ENA */
+
+/*
+ * R32 (0x20) - Left input mixer volume
+ */
+#define WM8962_IN2L_MIXINL_VOL_MASK 0x01C0 /* IN2L_MIXINL_VOL - [8:6] */
+#define WM8962_IN2L_MIXINL_VOL_SHIFT 6 /* IN2L_MIXINL_VOL - [8:6] */
+#define WM8962_IN2L_MIXINL_VOL_WIDTH 3 /* IN2L_MIXINL_VOL - [8:6] */
+#define WM8962_INPGAL_MIXINL_VOL_MASK 0x0038 /* INPGAL_MIXINL_VOL - [5:3] */
+#define WM8962_INPGAL_MIXINL_VOL_SHIFT 3 /* INPGAL_MIXINL_VOL - [5:3] */
+#define WM8962_INPGAL_MIXINL_VOL_WIDTH 3 /* INPGAL_MIXINL_VOL - [5:3] */
+#define WM8962_IN3L_MIXINL_VOL_MASK 0x0007 /* IN3L_MIXINL_VOL - [2:0] */
+#define WM8962_IN3L_MIXINL_VOL_SHIFT 0 /* IN3L_MIXINL_VOL - [2:0] */
+#define WM8962_IN3L_MIXINL_VOL_WIDTH 3 /* IN3L_MIXINL_VOL - [2:0] */
+
+/*
+ * R33 (0x21) - Right input mixer volume
+ */
+#define WM8962_IN2R_MIXINR_VOL_MASK 0x01C0 /* IN2R_MIXINR_VOL - [8:6] */
+#define WM8962_IN2R_MIXINR_VOL_SHIFT 6 /* IN2R_MIXINR_VOL - [8:6] */
+#define WM8962_IN2R_MIXINR_VOL_WIDTH 3 /* IN2R_MIXINR_VOL - [8:6] */
+#define WM8962_INPGAR_MIXINR_VOL_MASK 0x0038 /* INPGAR_MIXINR_VOL - [5:3] */
+#define WM8962_INPGAR_MIXINR_VOL_SHIFT 3 /* INPGAR_MIXINR_VOL - [5:3] */
+#define WM8962_INPGAR_MIXINR_VOL_WIDTH 3 /* INPGAR_MIXINR_VOL - [5:3] */
+#define WM8962_IN3R_MIXINR_VOL_MASK 0x0007 /* IN3R_MIXINR_VOL - [2:0] */
+#define WM8962_IN3R_MIXINR_VOL_SHIFT 0 /* IN3R_MIXINR_VOL - [2:0] */
+#define WM8962_IN3R_MIXINR_VOL_WIDTH 3 /* IN3R_MIXINR_VOL - [2:0] */
+
+/*
+ * R34 (0x22) - Input mixer control (2)
+ */
+#define WM8962_IN2L_TO_MIXINL 0x0020 /* IN2L_TO_MIXINL */
+#define WM8962_IN2L_TO_MIXINL_MASK 0x0020 /* IN2L_TO_MIXINL */
+#define WM8962_IN2L_TO_MIXINL_SHIFT 5 /* IN2L_TO_MIXINL */
+#define WM8962_IN2L_TO_MIXINL_WIDTH 1 /* IN2L_TO_MIXINL */
+#define WM8962_IN3L_TO_MIXINL 0x0010 /* IN3L_TO_MIXINL */
+#define WM8962_IN3L_TO_MIXINL_MASK 0x0010 /* IN3L_TO_MIXINL */
+#define WM8962_IN3L_TO_MIXINL_SHIFT 4 /* IN3L_TO_MIXINL */
+#define WM8962_IN3L_TO_MIXINL_WIDTH 1 /* IN3L_TO_MIXINL */
+#define WM8962_INPGAL_TO_MIXINL 0x0008 /* INPGAL_TO_MIXINL */
+#define WM8962_INPGAL_TO_MIXINL_MASK 0x0008 /* INPGAL_TO_MIXINL */
+#define WM8962_INPGAL_TO_MIXINL_SHIFT 3 /* INPGAL_TO_MIXINL */
+#define WM8962_INPGAL_TO_MIXINL_WIDTH 1 /* INPGAL_TO_MIXINL */
+#define WM8962_IN2R_TO_MIXINR 0x0004 /* IN2R_TO_MIXINR */
+#define WM8962_IN2R_TO_MIXINR_MASK 0x0004 /* IN2R_TO_MIXINR */
+#define WM8962_IN2R_TO_MIXINR_SHIFT 2 /* IN2R_TO_MIXINR */
+#define WM8962_IN2R_TO_MIXINR_WIDTH 1 /* IN2R_TO_MIXINR */
+#define WM8962_IN3R_TO_MIXINR 0x0002 /* IN3R_TO_MIXINR */
+#define WM8962_IN3R_TO_MIXINR_MASK 0x0002 /* IN3R_TO_MIXINR */
+#define WM8962_IN3R_TO_MIXINR_SHIFT 1 /* IN3R_TO_MIXINR */
+#define WM8962_IN3R_TO_MIXINR_WIDTH 1 /* IN3R_TO_MIXINR */
+#define WM8962_INPGAR_TO_MIXINR 0x0001 /* INPGAR_TO_MIXINR */
+#define WM8962_INPGAR_TO_MIXINR_MASK 0x0001 /* INPGAR_TO_MIXINR */
+#define WM8962_INPGAR_TO_MIXINR_SHIFT 0 /* INPGAR_TO_MIXINR */
+#define WM8962_INPGAR_TO_MIXINR_WIDTH 1 /* INPGAR_TO_MIXINR */
+
+/*
+ * R35 (0x23) - Input bias control
+ */
+#define WM8962_MIXIN_BIAS_MASK 0x0038 /* MIXIN_BIAS - [5:3] */
+#define WM8962_MIXIN_BIAS_SHIFT 3 /* MIXIN_BIAS - [5:3] */
+#define WM8962_MIXIN_BIAS_WIDTH 3 /* MIXIN_BIAS - [5:3] */
+#define WM8962_INPGA_BIAS_MASK 0x0007 /* INPGA_BIAS - [2:0] */
+#define WM8962_INPGA_BIAS_SHIFT 0 /* INPGA_BIAS - [2:0] */
+#define WM8962_INPGA_BIAS_WIDTH 3 /* INPGA_BIAS - [2:0] */
+
+/*
+ * R37 (0x25) - Left input PGA control
+ */
+#define WM8962_INPGAL_ENA 0x0010 /* INPGAL_ENA */
+#define WM8962_INPGAL_ENA_MASK 0x0010 /* INPGAL_ENA */
+#define WM8962_INPGAL_ENA_SHIFT 4 /* INPGAL_ENA */
+#define WM8962_INPGAL_ENA_WIDTH 1 /* INPGAL_ENA */
+#define WM8962_IN1L_TO_INPGAL 0x0008 /* IN1L_TO_INPGAL */
+#define WM8962_IN1L_TO_INPGAL_MASK 0x0008 /* IN1L_TO_INPGAL */
+#define WM8962_IN1L_TO_INPGAL_SHIFT 3 /* IN1L_TO_INPGAL */
+#define WM8962_IN1L_TO_INPGAL_WIDTH 1 /* IN1L_TO_INPGAL */
+#define WM8962_IN2L_TO_INPGAL 0x0004 /* IN2L_TO_INPGAL */
+#define WM8962_IN2L_TO_INPGAL_MASK 0x0004 /* IN2L_TO_INPGAL */
+#define WM8962_IN2L_TO_INPGAL_SHIFT 2 /* IN2L_TO_INPGAL */
+#define WM8962_IN2L_TO_INPGAL_WIDTH 1 /* IN2L_TO_INPGAL */
+#define WM8962_IN3L_TO_INPGAL 0x0002 /* IN3L_TO_INPGAL */
+#define WM8962_IN3L_TO_INPGAL_MASK 0x0002 /* IN3L_TO_INPGAL */
+#define WM8962_IN3L_TO_INPGAL_SHIFT 1 /* IN3L_TO_INPGAL */
+#define WM8962_IN3L_TO_INPGAL_WIDTH 1 /* IN3L_TO_INPGAL */
+#define WM8962_IN4L_TO_INPGAL 0x0001 /* IN4L_TO_INPGAL */
+#define WM8962_IN4L_TO_INPGAL_MASK 0x0001 /* IN4L_TO_INPGAL */
+#define WM8962_IN4L_TO_INPGAL_SHIFT 0 /* IN4L_TO_INPGAL */
+#define WM8962_IN4L_TO_INPGAL_WIDTH 1 /* IN4L_TO_INPGAL */
+
+/*
+ * R38 (0x26) - Right input PGA control
+ */
+#define WM8962_INPGAR_ENA 0x0010 /* INPGAR_ENA */
+#define WM8962_INPGAR_ENA_MASK 0x0010 /* INPGAR_ENA */
+#define WM8962_INPGAR_ENA_SHIFT 4 /* INPGAR_ENA */
+#define WM8962_INPGAR_ENA_WIDTH 1 /* INPGAR_ENA */
+#define WM8962_IN1R_TO_INPGAR 0x0008 /* IN1R_TO_INPGAR */
+#define WM8962_IN1R_TO_INPGAR_MASK 0x0008 /* IN1R_TO_INPGAR */
+#define WM8962_IN1R_TO_INPGAR_SHIFT 3 /* IN1R_TO_INPGAR */
+#define WM8962_IN1R_TO_INPGAR_WIDTH 1 /* IN1R_TO_INPGAR */
+#define WM8962_IN2R_TO_INPGAR 0x0004 /* IN2R_TO_INPGAR */
+#define WM8962_IN2R_TO_INPGAR_MASK 0x0004 /* IN2R_TO_INPGAR */
+#define WM8962_IN2R_TO_INPGAR_SHIFT 2 /* IN2R_TO_INPGAR */
+#define WM8962_IN2R_TO_INPGAR_WIDTH 1 /* IN2R_TO_INPGAR */
+#define WM8962_IN3R_TO_INPGAR 0x0002 /* IN3R_TO_INPGAR */
+#define WM8962_IN3R_TO_INPGAR_MASK 0x0002 /* IN3R_TO_INPGAR */
+#define WM8962_IN3R_TO_INPGAR_SHIFT 1 /* IN3R_TO_INPGAR */
+#define WM8962_IN3R_TO_INPGAR_WIDTH 1 /* IN3R_TO_INPGAR */
+#define WM8962_IN4R_TO_INPGAR 0x0001 /* IN4R_TO_INPGAR */
+#define WM8962_IN4R_TO_INPGAR_MASK 0x0001 /* IN4R_TO_INPGAR */
+#define WM8962_IN4R_TO_INPGAR_SHIFT 0 /* IN4R_TO_INPGAR */
+#define WM8962_IN4R_TO_INPGAR_WIDTH 1 /* IN4R_TO_INPGAR */
+
+/*
+ * R40 (0x28) - SPKOUTL volume
+ */
+#define WM8962_SPKOUT_VU 0x0100 /* SPKOUT_VU */
+#define WM8962_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */
+#define WM8962_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */
+#define WM8962_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */
+#define WM8962_SPKOUTL_ZC 0x0080 /* SPKOUTL_ZC */
+#define WM8962_SPKOUTL_ZC_MASK 0x0080 /* SPKOUTL_ZC */
+#define WM8962_SPKOUTL_ZC_SHIFT 7 /* SPKOUTL_ZC */
+#define WM8962_SPKOUTL_ZC_WIDTH 1 /* SPKOUTL_ZC */
+#define WM8962_SPKOUTL_VOL_MASK 0x007F /* SPKOUTL_VOL - [6:0] */
+#define WM8962_SPKOUTL_VOL_SHIFT 0 /* SPKOUTL_VOL - [6:0] */
+#define WM8962_SPKOUTL_VOL_WIDTH 7 /* SPKOUTL_VOL - [6:0] */
+
+/*
+ * R41 (0x29) - SPKOUTR volume
+ */
+#define WM8962_SPKOUTR_ZC 0x0080 /* SPKOUTR_ZC */
+#define WM8962_SPKOUTR_ZC_MASK 0x0080 /* SPKOUTR_ZC */
+#define WM8962_SPKOUTR_ZC_SHIFT 7 /* SPKOUTR_ZC */
+#define WM8962_SPKOUTR_ZC_WIDTH 1 /* SPKOUTR_ZC */
+#define WM8962_SPKOUTR_VOL_MASK 0x007F /* SPKOUTR_VOL - [6:0] */
+#define WM8962_SPKOUTR_VOL_SHIFT 0 /* SPKOUTR_VOL - [6:0] */
+#define WM8962_SPKOUTR_VOL_WIDTH 7 /* SPKOUTR_VOL - [6:0] */
+
+/*
+ * R47 (0x2F) - Thermal Shutdown Status
+ */
+#define WM8962_TEMP_ERR_HP 0x0008 /* TEMP_ERR_HP */
+#define WM8962_TEMP_ERR_HP_MASK 0x0008 /* TEMP_ERR_HP */
+#define WM8962_TEMP_ERR_HP_SHIFT 3 /* TEMP_ERR_HP */
+#define WM8962_TEMP_ERR_HP_WIDTH 1 /* TEMP_ERR_HP */
+#define WM8962_TEMP_WARN_HP 0x0004 /* TEMP_WARN_HP */
+#define WM8962_TEMP_WARN_HP_MASK 0x0004 /* TEMP_WARN_HP */
+#define WM8962_TEMP_WARN_HP_SHIFT 2 /* TEMP_WARN_HP */
+#define WM8962_TEMP_WARN_HP_WIDTH 1 /* TEMP_WARN_HP */
+#define WM8962_TEMP_ERR_SPK 0x0002 /* TEMP_ERR_SPK */
+#define WM8962_TEMP_ERR_SPK_MASK 0x0002 /* TEMP_ERR_SPK */
+#define WM8962_TEMP_ERR_SPK_SHIFT 1 /* TEMP_ERR_SPK */
+#define WM8962_TEMP_ERR_SPK_WIDTH 1 /* TEMP_ERR_SPK */
+#define WM8962_TEMP_WARN_SPK 0x0001 /* TEMP_WARN_SPK */
+#define WM8962_TEMP_WARN_SPK_MASK 0x0001 /* TEMP_WARN_SPK */
+#define WM8962_TEMP_WARN_SPK_SHIFT 0 /* TEMP_WARN_SPK */
+#define WM8962_TEMP_WARN_SPK_WIDTH 1 /* TEMP_WARN_SPK */
+
+/*
+ * R48 (0x30) - Additional Control (4)
+ */
+#define WM8962_MICDET_THR_MASK 0x7000 /* MICDET_THR - [14:12] */
+#define WM8962_MICDET_THR_SHIFT 12 /* MICDET_THR - [14:12] */
+#define WM8962_MICDET_THR_WIDTH 3 /* MICDET_THR - [14:12] */
+#define WM8962_MICSHORT_THR_MASK 0x0C00 /* MICSHORT_THR - [11:10] */
+#define WM8962_MICSHORT_THR_SHIFT 10 /* MICSHORT_THR - [11:10] */
+#define WM8962_MICSHORT_THR_WIDTH 2 /* MICSHORT_THR - [11:10] */
+#define WM8962_MICDET_ENA 0x0200 /* MICDET_ENA */
+#define WM8962_MICDET_ENA_MASK 0x0200 /* MICDET_ENA */
+#define WM8962_MICDET_ENA_SHIFT 9 /* MICDET_ENA */
+#define WM8962_MICDET_ENA_WIDTH 1 /* MICDET_ENA */
+#define WM8962_MICDET_STS 0x0080 /* MICDET_STS */
+#define WM8962_MICDET_STS_MASK 0x0080 /* MICDET_STS */
+#define WM8962_MICDET_STS_SHIFT 7 /* MICDET_STS */
+#define WM8962_MICDET_STS_WIDTH 1 /* MICDET_STS */
+#define WM8962_MICSHORT_STS 0x0040 /* MICSHORT_STS */
+#define WM8962_MICSHORT_STS_MASK 0x0040 /* MICSHORT_STS */
+#define WM8962_MICSHORT_STS_SHIFT 6 /* MICSHORT_STS */
+#define WM8962_MICSHORT_STS_WIDTH 1 /* MICSHORT_STS */
+#define WM8962_TEMP_ENA_HP 0x0004 /* TEMP_ENA_HP */
+#define WM8962_TEMP_ENA_HP_MASK 0x0004 /* TEMP_ENA_HP */
+#define WM8962_TEMP_ENA_HP_SHIFT 2 /* TEMP_ENA_HP */
+#define WM8962_TEMP_ENA_HP_WIDTH 1 /* TEMP_ENA_HP */
+#define WM8962_TEMP_ENA_SPK 0x0002 /* TEMP_ENA_SPK */
+#define WM8962_TEMP_ENA_SPK_MASK 0x0002 /* TEMP_ENA_SPK */
+#define WM8962_TEMP_ENA_SPK_SHIFT 1 /* TEMP_ENA_SPK */
+#define WM8962_TEMP_ENA_SPK_WIDTH 1 /* TEMP_ENA_SPK */
+#define WM8962_MICBIAS_LVL 0x0001 /* MICBIAS_LVL */
+#define WM8962_MICBIAS_LVL_MASK 0x0001 /* MICBIAS_LVL */
+#define WM8962_MICBIAS_LVL_SHIFT 0 /* MICBIAS_LVL */
+#define WM8962_MICBIAS_LVL_WIDTH 1 /* MICBIAS_LVL */
+
+/*
+ * R49 (0x31) - Class D Control 1
+ */
+#define WM8962_SPKOUTR_ENA 0x0080 /* SPKOUTR_ENA */
+#define WM8962_SPKOUTR_ENA_MASK 0x0080 /* SPKOUTR_ENA */
+#define WM8962_SPKOUTR_ENA_SHIFT 7 /* SPKOUTR_ENA */
+#define WM8962_SPKOUTR_ENA_WIDTH 1 /* SPKOUTR_ENA */
+#define WM8962_SPKOUTL_ENA 0x0040 /* SPKOUTL_ENA */
+#define WM8962_SPKOUTL_ENA_MASK 0x0040 /* SPKOUTL_ENA */
+#define WM8962_SPKOUTL_ENA_SHIFT 6 /* SPKOUTL_ENA */
+#define WM8962_SPKOUTL_ENA_WIDTH 1 /* SPKOUTL_ENA */
+#define WM8962_DAC_MUTE_ALT 0x0010 /* DAC_MUTE */
+#define WM8962_DAC_MUTE_ALT_MASK 0x0010 /* DAC_MUTE */
+#define WM8962_DAC_MUTE_ALT_SHIFT 4 /* DAC_MUTE */
+#define WM8962_DAC_MUTE_ALT_WIDTH 1 /* DAC_MUTE */
+#define WM8962_SPKOUTL_PGA_MUTE 0x0002 /* SPKOUTL_PGA_MUTE */
+#define WM8962_SPKOUTL_PGA_MUTE_MASK 0x0002 /* SPKOUTL_PGA_MUTE */
+#define WM8962_SPKOUTL_PGA_MUTE_SHIFT 1 /* SPKOUTL_PGA_MUTE */
+#define WM8962_SPKOUTL_PGA_MUTE_WIDTH 1 /* SPKOUTL_PGA_MUTE */
+#define WM8962_SPKOUTR_PGA_MUTE 0x0001 /* SPKOUTR_PGA_MUTE */
+#define WM8962_SPKOUTR_PGA_MUTE_MASK 0x0001 /* SPKOUTR_PGA_MUTE */
+#define WM8962_SPKOUTR_PGA_MUTE_SHIFT 0 /* SPKOUTR_PGA_MUTE */
+#define WM8962_SPKOUTR_PGA_MUTE_WIDTH 1 /* SPKOUTR_PGA_MUTE */
+
+/*
+ * R51 (0x33) - Class D Control 2
+ */
+#define WM8962_SPK_MONO 0x0040 /* SPK_MONO */
+#define WM8962_SPK_MONO_MASK 0x0040 /* SPK_MONO */
+#define WM8962_SPK_MONO_SHIFT 6 /* SPK_MONO */
+#define WM8962_SPK_MONO_WIDTH 1 /* SPK_MONO */
+#define WM8962_CLASSD_VOL_MASK 0x0007 /* CLASSD_VOL - [2:0] */
+#define WM8962_CLASSD_VOL_SHIFT 0 /* CLASSD_VOL - [2:0] */
+#define WM8962_CLASSD_VOL_WIDTH 3 /* CLASSD_VOL - [2:0] */
+
+/*
+ * R56 (0x38) - Clocking 4
+ */
+#define WM8962_SYSCLK_RATE_MASK 0x001E /* SYSCLK_RATE - [4:1] */
+#define WM8962_SYSCLK_RATE_SHIFT 1 /* SYSCLK_RATE - [4:1] */
+#define WM8962_SYSCLK_RATE_WIDTH 4 /* SYSCLK_RATE - [4:1] */
+
+/*
+ * R57 (0x39) - DAC DSP Mixing (1)
+ */
+#define WM8962_DAC_MONOMIX 0x0200 /* DAC_MONOMIX */
+#define WM8962_DAC_MONOMIX_MASK 0x0200 /* DAC_MONOMIX */
+#define WM8962_DAC_MONOMIX_SHIFT 9 /* DAC_MONOMIX */
+#define WM8962_DAC_MONOMIX_WIDTH 1 /* DAC_MONOMIX */
+#define WM8962_ADCR_DAC_SVOL_MASK 0x00F0 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8962_ADCR_DAC_SVOL_SHIFT 4 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8962_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8962_ADC_TO_DACR_MASK 0x000C /* ADC_TO_DACR - [3:2] */
+#define WM8962_ADC_TO_DACR_SHIFT 2 /* ADC_TO_DACR - [3:2] */
+#define WM8962_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [3:2] */
+
+/*
+ * R58 (0x3A) - DAC DSP Mixing (2)
+ */
+#define WM8962_ADCL_DAC_SVOL_MASK 0x00F0 /* ADCL_DAC_SVOL - [7:4] */
+#define WM8962_ADCL_DAC_SVOL_SHIFT 4 /* ADCL_DAC_SVOL - [7:4] */
+#define WM8962_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [7:4] */
+#define WM8962_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */
+#define WM8962_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */
+#define WM8962_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */
+
+/*
+ * R60 (0x3C) - DC Servo 0
+ */
+#define WM8962_INL_DCS_ENA 0x0080 /* INL_DCS_ENA */
+#define WM8962_INL_DCS_ENA_MASK 0x0080 /* INL_DCS_ENA */
+#define WM8962_INL_DCS_ENA_SHIFT 7 /* INL_DCS_ENA */
+#define WM8962_INL_DCS_ENA_WIDTH 1 /* INL_DCS_ENA */
+#define WM8962_INL_DCS_STARTUP 0x0040 /* INL_DCS_STARTUP */
+#define WM8962_INL_DCS_STARTUP_MASK 0x0040 /* INL_DCS_STARTUP */
+#define WM8962_INL_DCS_STARTUP_SHIFT 6 /* INL_DCS_STARTUP */
+#define WM8962_INL_DCS_STARTUP_WIDTH 1 /* INL_DCS_STARTUP */
+#define WM8962_INR_DCS_ENA 0x0008 /* INR_DCS_ENA */
+#define WM8962_INR_DCS_ENA_MASK 0x0008 /* INR_DCS_ENA */
+#define WM8962_INR_DCS_ENA_SHIFT 3 /* INR_DCS_ENA */
+#define WM8962_INR_DCS_ENA_WIDTH 1 /* INR_DCS_ENA */
+#define WM8962_INR_DCS_STARTUP 0x0004 /* INR_DCS_STARTUP */
+#define WM8962_INR_DCS_STARTUP_MASK 0x0004 /* INR_DCS_STARTUP */
+#define WM8962_INR_DCS_STARTUP_SHIFT 2 /* INR_DCS_STARTUP */
+#define WM8962_INR_DCS_STARTUP_WIDTH 1 /* INR_DCS_STARTUP */
+
+/*
+ * R61 (0x3D) - DC Servo 1
+ */
+#define WM8962_HP1L_DCS_ENA 0x0080 /* HP1L_DCS_ENA */
+#define WM8962_HP1L_DCS_ENA_MASK 0x0080 /* HP1L_DCS_ENA */
+#define WM8962_HP1L_DCS_ENA_SHIFT 7 /* HP1L_DCS_ENA */
+#define WM8962_HP1L_DCS_ENA_WIDTH 1 /* HP1L_DCS_ENA */
+#define WM8962_HP1L_DCS_STARTUP 0x0040 /* HP1L_DCS_STARTUP */
+#define WM8962_HP1L_DCS_STARTUP_MASK 0x0040 /* HP1L_DCS_STARTUP */
+#define WM8962_HP1L_DCS_STARTUP_SHIFT 6 /* HP1L_DCS_STARTUP */
+#define WM8962_HP1L_DCS_STARTUP_WIDTH 1 /* HP1L_DCS_STARTUP */
+#define WM8962_HP1L_DCS_SYNC 0x0010 /* HP1L_DCS_SYNC */
+#define WM8962_HP1L_DCS_SYNC_MASK 0x0010 /* HP1L_DCS_SYNC */
+#define WM8962_HP1L_DCS_SYNC_SHIFT 4 /* HP1L_DCS_SYNC */
+#define WM8962_HP1L_DCS_SYNC_WIDTH 1 /* HP1L_DCS_SYNC */
+#define WM8962_HP1R_DCS_ENA 0x0008 /* HP1R_DCS_ENA */
+#define WM8962_HP1R_DCS_ENA_MASK 0x0008 /* HP1R_DCS_ENA */
+#define WM8962_HP1R_DCS_ENA_SHIFT 3 /* HP1R_DCS_ENA */
+#define WM8962_HP1R_DCS_ENA_WIDTH 1 /* HP1R_DCS_ENA */
+#define WM8962_HP1R_DCS_STARTUP 0x0004 /* HP1R_DCS_STARTUP */
+#define WM8962_HP1R_DCS_STARTUP_MASK 0x0004 /* HP1R_DCS_STARTUP */
+#define WM8962_HP1R_DCS_STARTUP_SHIFT 2 /* HP1R_DCS_STARTUP */
+#define WM8962_HP1R_DCS_STARTUP_WIDTH 1 /* HP1R_DCS_STARTUP */
+#define WM8962_HP1R_DCS_SYNC 0x0001 /* HP1R_DCS_SYNC */
+#define WM8962_HP1R_DCS_SYNC_MASK 0x0001 /* HP1R_DCS_SYNC */
+#define WM8962_HP1R_DCS_SYNC_SHIFT 0 /* HP1R_DCS_SYNC */
+#define WM8962_HP1R_DCS_SYNC_WIDTH 1 /* HP1R_DCS_SYNC */
+
+/*
+ * R64 (0x40) - DC Servo 4
+ */
+#define WM8962_HP1_DCS_SYNC_STEPS_MASK 0x3F80 /* HP1_DCS_SYNC_STEPS - [13:7] */
+#define WM8962_HP1_DCS_SYNC_STEPS_SHIFT 7 /* HP1_DCS_SYNC_STEPS - [13:7] */
+#define WM8962_HP1_DCS_SYNC_STEPS_WIDTH 7 /* HP1_DCS_SYNC_STEPS - [13:7] */
+
+/*
+ * R66 (0x42) - DC Servo 6
+ */
+#define WM8962_DCS_STARTUP_DONE_INL 0x0400 /* DCS_STARTUP_DONE_INL */
+#define WM8962_DCS_STARTUP_DONE_INL_MASK 0x0400 /* DCS_STARTUP_DONE_INL */
+#define WM8962_DCS_STARTUP_DONE_INL_SHIFT 10 /* DCS_STARTUP_DONE_INL */
+#define WM8962_DCS_STARTUP_DONE_INL_WIDTH 1 /* DCS_STARTUP_DONE_INL */
+#define WM8962_DCS_STARTUP_DONE_INR 0x0200 /* DCS_STARTUP_DONE_INR */
+#define WM8962_DCS_STARTUP_DONE_INR_MASK 0x0200 /* DCS_STARTUP_DONE_INR */
+#define WM8962_DCS_STARTUP_DONE_INR_SHIFT 9 /* DCS_STARTUP_DONE_INR */
+#define WM8962_DCS_STARTUP_DONE_INR_WIDTH 1 /* DCS_STARTUP_DONE_INR */
+#define WM8962_DCS_STARTUP_DONE_HP1L 0x0100 /* DCS_STARTUP_DONE_HP1L */
+#define WM8962_DCS_STARTUP_DONE_HP1L_MASK 0x0100 /* DCS_STARTUP_DONE_HP1L */
+#define WM8962_DCS_STARTUP_DONE_HP1L_SHIFT 8 /* DCS_STARTUP_DONE_HP1L */
+#define WM8962_DCS_STARTUP_DONE_HP1L_WIDTH 1 /* DCS_STARTUP_DONE_HP1L */
+#define WM8962_DCS_STARTUP_DONE_HP1R 0x0080 /* DCS_STARTUP_DONE_HP1R */
+#define WM8962_DCS_STARTUP_DONE_HP1R_MASK 0x0080 /* DCS_STARTUP_DONE_HP1R */
+#define WM8962_DCS_STARTUP_DONE_HP1R_SHIFT 7 /* DCS_STARTUP_DONE_HP1R */
+#define WM8962_DCS_STARTUP_DONE_HP1R_WIDTH 1 /* DCS_STARTUP_DONE_HP1R */
+
+/*
+ * R68 (0x44) - Analogue PGA Bias
+ */
+#define WM8962_HP_PGAS_BIAS_MASK 0x0007 /* HP_PGAS_BIAS - [2:0] */
+#define WM8962_HP_PGAS_BIAS_SHIFT 0 /* HP_PGAS_BIAS - [2:0] */
+#define WM8962_HP_PGAS_BIAS_WIDTH 3 /* HP_PGAS_BIAS - [2:0] */
+
+/*
+ * R69 (0x45) - Analogue HP 0
+ */
+#define WM8962_HP1L_RMV_SHORT 0x0080 /* HP1L_RMV_SHORT */
+#define WM8962_HP1L_RMV_SHORT_MASK 0x0080 /* HP1L_RMV_SHORT */
+#define WM8962_HP1L_RMV_SHORT_SHIFT 7 /* HP1L_RMV_SHORT */
+#define WM8962_HP1L_RMV_SHORT_WIDTH 1 /* HP1L_RMV_SHORT */
+#define WM8962_HP1L_ENA_OUTP 0x0040 /* HP1L_ENA_OUTP */
+#define WM8962_HP1L_ENA_OUTP_MASK 0x0040 /* HP1L_ENA_OUTP */
+#define WM8962_HP1L_ENA_OUTP_SHIFT 6 /* HP1L_ENA_OUTP */
+#define WM8962_HP1L_ENA_OUTP_WIDTH 1 /* HP1L_ENA_OUTP */
+#define WM8962_HP1L_ENA_DLY 0x0020 /* HP1L_ENA_DLY */
+#define WM8962_HP1L_ENA_DLY_MASK 0x0020 /* HP1L_ENA_DLY */
+#define WM8962_HP1L_ENA_DLY_SHIFT 5 /* HP1L_ENA_DLY */
+#define WM8962_HP1L_ENA_DLY_WIDTH 1 /* HP1L_ENA_DLY */
+#define WM8962_HP1L_ENA 0x0010 /* HP1L_ENA */
+#define WM8962_HP1L_ENA_MASK 0x0010 /* HP1L_ENA */
+#define WM8962_HP1L_ENA_SHIFT 4 /* HP1L_ENA */
+#define WM8962_HP1L_ENA_WIDTH 1 /* HP1L_ENA */
+#define WM8962_HP1R_RMV_SHORT 0x0008 /* HP1R_RMV_SHORT */
+#define WM8962_HP1R_RMV_SHORT_MASK 0x0008 /* HP1R_RMV_SHORT */
+#define WM8962_HP1R_RMV_SHORT_SHIFT 3 /* HP1R_RMV_SHORT */
+#define WM8962_HP1R_RMV_SHORT_WIDTH 1 /* HP1R_RMV_SHORT */
+#define WM8962_HP1R_ENA_OUTP 0x0004 /* HP1R_ENA_OUTP */
+#define WM8962_HP1R_ENA_OUTP_MASK 0x0004 /* HP1R_ENA_OUTP */
+#define WM8962_HP1R_ENA_OUTP_SHIFT 2 /* HP1R_ENA_OUTP */
+#define WM8962_HP1R_ENA_OUTP_WIDTH 1 /* HP1R_ENA_OUTP */
+#define WM8962_HP1R_ENA_DLY 0x0002 /* HP1R_ENA_DLY */
+#define WM8962_HP1R_ENA_DLY_MASK 0x0002 /* HP1R_ENA_DLY */
+#define WM8962_HP1R_ENA_DLY_SHIFT 1 /* HP1R_ENA_DLY */
+#define WM8962_HP1R_ENA_DLY_WIDTH 1 /* HP1R_ENA_DLY */
+#define WM8962_HP1R_ENA 0x0001 /* HP1R_ENA */
+#define WM8962_HP1R_ENA_MASK 0x0001 /* HP1R_ENA */
+#define WM8962_HP1R_ENA_SHIFT 0 /* HP1R_ENA */
+#define WM8962_HP1R_ENA_WIDTH 1 /* HP1R_ENA */
+
+/*
+ * R71 (0x47) - Analogue HP 2
+ */
+#define WM8962_HP1L_VOL_MASK 0x01C0 /* HP1L_VOL - [8:6] */
+#define WM8962_HP1L_VOL_SHIFT 6 /* HP1L_VOL - [8:6] */
+#define WM8962_HP1L_VOL_WIDTH 3 /* HP1L_VOL - [8:6] */
+#define WM8962_HP1R_VOL_MASK 0x0038 /* HP1R_VOL - [5:3] */
+#define WM8962_HP1R_VOL_SHIFT 3 /* HP1R_VOL - [5:3] */
+#define WM8962_HP1R_VOL_WIDTH 3 /* HP1R_VOL - [5:3] */
+#define WM8962_HP_BIAS_BOOST_MASK 0x0007 /* HP_BIAS_BOOST - [2:0] */
+#define WM8962_HP_BIAS_BOOST_SHIFT 0 /* HP_BIAS_BOOST - [2:0] */
+#define WM8962_HP_BIAS_BOOST_WIDTH 3 /* HP_BIAS_BOOST - [2:0] */
+
+/*
+ * R72 (0x48) - Charge Pump 1
+ */
+#define WM8962_CP_ENA 0x0001 /* CP_ENA */
+#define WM8962_CP_ENA_MASK 0x0001 /* CP_ENA */
+#define WM8962_CP_ENA_SHIFT 0 /* CP_ENA */
+#define WM8962_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R82 (0x52) - Charge Pump B
+ */
+#define WM8962_CP_DYN_PWR 0x0001 /* CP_DYN_PWR */
+#define WM8962_CP_DYN_PWR_MASK 0x0001 /* CP_DYN_PWR */
+#define WM8962_CP_DYN_PWR_SHIFT 0 /* CP_DYN_PWR */
+#define WM8962_CP_DYN_PWR_WIDTH 1 /* CP_DYN_PWR */
+
+/*
+ * R87 (0x57) - Write Sequencer Control 1
+ */
+#define WM8962_WSEQ_AUTOSEQ_ENA 0x0080 /* WSEQ_AUTOSEQ_ENA */
+#define WM8962_WSEQ_AUTOSEQ_ENA_MASK 0x0080 /* WSEQ_AUTOSEQ_ENA */
+#define WM8962_WSEQ_AUTOSEQ_ENA_SHIFT 7 /* WSEQ_AUTOSEQ_ENA */
+#define WM8962_WSEQ_AUTOSEQ_ENA_WIDTH 1 /* WSEQ_AUTOSEQ_ENA */
+#define WM8962_WSEQ_ENA 0x0020 /* WSEQ_ENA */
+#define WM8962_WSEQ_ENA_MASK 0x0020 /* WSEQ_ENA */
+#define WM8962_WSEQ_ENA_SHIFT 5 /* WSEQ_ENA */
+#define WM8962_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+
+/*
+ * R90 (0x5A) - Write Sequencer Control 2
+ */
+#define WM8962_WSEQ_ABORT 0x0100 /* WSEQ_ABORT */
+#define WM8962_WSEQ_ABORT_MASK 0x0100 /* WSEQ_ABORT */
+#define WM8962_WSEQ_ABORT_SHIFT 8 /* WSEQ_ABORT */
+#define WM8962_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8962_WSEQ_START 0x0080 /* WSEQ_START */
+#define WM8962_WSEQ_START_MASK 0x0080 /* WSEQ_START */
+#define WM8962_WSEQ_START_SHIFT 7 /* WSEQ_START */
+#define WM8962_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8962_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */
+#define WM8962_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */
+#define WM8962_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */
+
+/*
+ * R93 (0x5D) - Write Sequencer Control 3
+ */
+#define WM8962_WSEQ_CURRENT_INDEX_MASK 0x03F8 /* WSEQ_CURRENT_INDEX - [9:3] */
+#define WM8962_WSEQ_CURRENT_INDEX_SHIFT 3 /* WSEQ_CURRENT_INDEX - [9:3] */
+#define WM8962_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [9:3] */
+#define WM8962_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */
+#define WM8962_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */
+#define WM8962_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */
+#define WM8962_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+
+/*
+ * R94 (0x5E) - Control Interface
+ */
+#define WM8962_SPI_CONTRD 0x0040 /* SPI_CONTRD */
+#define WM8962_SPI_CONTRD_MASK 0x0040 /* SPI_CONTRD */
+#define WM8962_SPI_CONTRD_SHIFT 6 /* SPI_CONTRD */
+#define WM8962_SPI_CONTRD_WIDTH 1 /* SPI_CONTRD */
+#define WM8962_SPI_4WIRE 0x0020 /* SPI_4WIRE */
+#define WM8962_SPI_4WIRE_MASK 0x0020 /* SPI_4WIRE */
+#define WM8962_SPI_4WIRE_SHIFT 5 /* SPI_4WIRE */
+#define WM8962_SPI_4WIRE_WIDTH 1 /* SPI_4WIRE */
+#define WM8962_SPI_CFG 0x0010 /* SPI_CFG */
+#define WM8962_SPI_CFG_MASK 0x0010 /* SPI_CFG */
+#define WM8962_SPI_CFG_SHIFT 4 /* SPI_CFG */
+#define WM8962_SPI_CFG_WIDTH 1 /* SPI_CFG */
+
+/*
+ * R99 (0x63) - Mixer Enables
+ */
+#define WM8962_HPMIXL_ENA 0x0008 /* HPMIXL_ENA */
+#define WM8962_HPMIXL_ENA_MASK 0x0008 /* HPMIXL_ENA */
+#define WM8962_HPMIXL_ENA_SHIFT 3 /* HPMIXL_ENA */
+#define WM8962_HPMIXL_ENA_WIDTH 1 /* HPMIXL_ENA */
+#define WM8962_HPMIXR_ENA 0x0004 /* HPMIXR_ENA */
+#define WM8962_HPMIXR_ENA_MASK 0x0004 /* HPMIXR_ENA */
+#define WM8962_HPMIXR_ENA_SHIFT 2 /* HPMIXR_ENA */
+#define WM8962_HPMIXR_ENA_WIDTH 1 /* HPMIXR_ENA */
+#define WM8962_SPKMIXL_ENA 0x0002 /* SPKMIXL_ENA */
+#define WM8962_SPKMIXL_ENA_MASK 0x0002 /* SPKMIXL_ENA */
+#define WM8962_SPKMIXL_ENA_SHIFT 1 /* SPKMIXL_ENA */
+#define WM8962_SPKMIXL_ENA_WIDTH 1 /* SPKMIXL_ENA */
+#define WM8962_SPKMIXR_ENA 0x0001 /* SPKMIXR_ENA */
+#define WM8962_SPKMIXR_ENA_MASK 0x0001 /* SPKMIXR_ENA */
+#define WM8962_SPKMIXR_ENA_SHIFT 0 /* SPKMIXR_ENA */
+#define WM8962_SPKMIXR_ENA_WIDTH 1 /* SPKMIXR_ENA */
+
+/*
+ * R100 (0x64) - Headphone Mixer (1)
+ */
+#define WM8962_HPMIXL_TO_HPOUTL_PGA 0x0080 /* HPMIXL_TO_HPOUTL_PGA */
+#define WM8962_HPMIXL_TO_HPOUTL_PGA_MASK 0x0080 /* HPMIXL_TO_HPOUTL_PGA */
+#define WM8962_HPMIXL_TO_HPOUTL_PGA_SHIFT 7 /* HPMIXL_TO_HPOUTL_PGA */
+#define WM8962_HPMIXL_TO_HPOUTL_PGA_WIDTH 1 /* HPMIXL_TO_HPOUTL_PGA */
+#define WM8962_DACL_TO_HPMIXL 0x0020 /* DACL_TO_HPMIXL */
+#define WM8962_DACL_TO_HPMIXL_MASK 0x0020 /* DACL_TO_HPMIXL */
+#define WM8962_DACL_TO_HPMIXL_SHIFT 5 /* DACL_TO_HPMIXL */
+#define WM8962_DACL_TO_HPMIXL_WIDTH 1 /* DACL_TO_HPMIXL */
+#define WM8962_DACR_TO_HPMIXL 0x0010 /* DACR_TO_HPMIXL */
+#define WM8962_DACR_TO_HPMIXL_MASK 0x0010 /* DACR_TO_HPMIXL */
+#define WM8962_DACR_TO_HPMIXL_SHIFT 4 /* DACR_TO_HPMIXL */
+#define WM8962_DACR_TO_HPMIXL_WIDTH 1 /* DACR_TO_HPMIXL */
+#define WM8962_MIXINL_TO_HPMIXL 0x0008 /* MIXINL_TO_HPMIXL */
+#define WM8962_MIXINL_TO_HPMIXL_MASK 0x0008 /* MIXINL_TO_HPMIXL */
+#define WM8962_MIXINL_TO_HPMIXL_SHIFT 3 /* MIXINL_TO_HPMIXL */
+#define WM8962_MIXINL_TO_HPMIXL_WIDTH 1 /* MIXINL_TO_HPMIXL */
+#define WM8962_MIXINR_TO_HPMIXL 0x0004 /* MIXINR_TO_HPMIXL */
+#define WM8962_MIXINR_TO_HPMIXL_MASK 0x0004 /* MIXINR_TO_HPMIXL */
+#define WM8962_MIXINR_TO_HPMIXL_SHIFT 2 /* MIXINR_TO_HPMIXL */
+#define WM8962_MIXINR_TO_HPMIXL_WIDTH 1 /* MIXINR_TO_HPMIXL */
+#define WM8962_IN4L_TO_HPMIXL 0x0002 /* IN4L_TO_HPMIXL */
+#define WM8962_IN4L_TO_HPMIXL_MASK 0x0002 /* IN4L_TO_HPMIXL */
+#define WM8962_IN4L_TO_HPMIXL_SHIFT 1 /* IN4L_TO_HPMIXL */
+#define WM8962_IN4L_TO_HPMIXL_WIDTH 1 /* IN4L_TO_HPMIXL */
+#define WM8962_IN4R_TO_HPMIXL 0x0001 /* IN4R_TO_HPMIXL */
+#define WM8962_IN4R_TO_HPMIXL_MASK 0x0001 /* IN4R_TO_HPMIXL */
+#define WM8962_IN4R_TO_HPMIXL_SHIFT 0 /* IN4R_TO_HPMIXL */
+#define WM8962_IN4R_TO_HPMIXL_WIDTH 1 /* IN4R_TO_HPMIXL */
+
+/*
+ * R101 (0x65) - Headphone Mixer (2)
+ */
+#define WM8962_HPMIXR_TO_HPOUTR_PGA 0x0080 /* HPMIXR_TO_HPOUTR_PGA */
+#define WM8962_HPMIXR_TO_HPOUTR_PGA_MASK 0x0080 /* HPMIXR_TO_HPOUTR_PGA */
+#define WM8962_HPMIXR_TO_HPOUTR_PGA_SHIFT 7 /* HPMIXR_TO_HPOUTR_PGA */
+#define WM8962_HPMIXR_TO_HPOUTR_PGA_WIDTH 1 /* HPMIXR_TO_HPOUTR_PGA */
+#define WM8962_DACL_TO_HPMIXR 0x0020 /* DACL_TO_HPMIXR */
+#define WM8962_DACL_TO_HPMIXR_MASK 0x0020 /* DACL_TO_HPMIXR */
+#define WM8962_DACL_TO_HPMIXR_SHIFT 5 /* DACL_TO_HPMIXR */
+#define WM8962_DACL_TO_HPMIXR_WIDTH 1 /* DACL_TO_HPMIXR */
+#define WM8962_DACR_TO_HPMIXR 0x0010 /* DACR_TO_HPMIXR */
+#define WM8962_DACR_TO_HPMIXR_MASK 0x0010 /* DACR_TO_HPMIXR */
+#define WM8962_DACR_TO_HPMIXR_SHIFT 4 /* DACR_TO_HPMIXR */
+#define WM8962_DACR_TO_HPMIXR_WIDTH 1 /* DACR_TO_HPMIXR */
+#define WM8962_MIXINL_TO_HPMIXR 0x0008 /* MIXINL_TO_HPMIXR */
+#define WM8962_MIXINL_TO_HPMIXR_MASK 0x0008 /* MIXINL_TO_HPMIXR */
+#define WM8962_MIXINL_TO_HPMIXR_SHIFT 3 /* MIXINL_TO_HPMIXR */
+#define WM8962_MIXINL_TO_HPMIXR_WIDTH 1 /* MIXINL_TO_HPMIXR */
+#define WM8962_MIXINR_TO_HPMIXR 0x0004 /* MIXINR_TO_HPMIXR */
+#define WM8962_MIXINR_TO_HPMIXR_MASK 0x0004 /* MIXINR_TO_HPMIXR */
+#define WM8962_MIXINR_TO_HPMIXR_SHIFT 2 /* MIXINR_TO_HPMIXR */
+#define WM8962_MIXINR_TO_HPMIXR_WIDTH 1 /* MIXINR_TO_HPMIXR */
+#define WM8962_IN4L_TO_HPMIXR 0x0002 /* IN4L_TO_HPMIXR */
+#define WM8962_IN4L_TO_HPMIXR_MASK 0x0002 /* IN4L_TO_HPMIXR */
+#define WM8962_IN4L_TO_HPMIXR_SHIFT 1 /* IN4L_TO_HPMIXR */
+#define WM8962_IN4L_TO_HPMIXR_WIDTH 1 /* IN4L_TO_HPMIXR */
+#define WM8962_IN4R_TO_HPMIXR 0x0001 /* IN4R_TO_HPMIXR */
+#define WM8962_IN4R_TO_HPMIXR_MASK 0x0001 /* IN4R_TO_HPMIXR */
+#define WM8962_IN4R_TO_HPMIXR_SHIFT 0 /* IN4R_TO_HPMIXR */
+#define WM8962_IN4R_TO_HPMIXR_WIDTH 1 /* IN4R_TO_HPMIXR */
+
+/*
+ * R102 (0x66) - Headphone Mixer (3)
+ */
+#define WM8962_HPMIXL_MUTE 0x0100 /* HPMIXL_MUTE */
+#define WM8962_HPMIXL_MUTE_MASK 0x0100 /* HPMIXL_MUTE */
+#define WM8962_HPMIXL_MUTE_SHIFT 8 /* HPMIXL_MUTE */
+#define WM8962_HPMIXL_MUTE_WIDTH 1 /* HPMIXL_MUTE */
+#define WM8962_MIXINL_HPMIXL_VOL 0x0080 /* MIXINL_HPMIXL_VOL */
+#define WM8962_MIXINL_HPMIXL_VOL_MASK 0x0080 /* MIXINL_HPMIXL_VOL */
+#define WM8962_MIXINL_HPMIXL_VOL_SHIFT 7 /* MIXINL_HPMIXL_VOL */
+#define WM8962_MIXINL_HPMIXL_VOL_WIDTH 1 /* MIXINL_HPMIXL_VOL */
+#define WM8962_MIXINR_HPMIXL_VOL 0x0040 /* MIXINR_HPMIXL_VOL */
+#define WM8962_MIXINR_HPMIXL_VOL_MASK 0x0040 /* MIXINR_HPMIXL_VOL */
+#define WM8962_MIXINR_HPMIXL_VOL_SHIFT 6 /* MIXINR_HPMIXL_VOL */
+#define WM8962_MIXINR_HPMIXL_VOL_WIDTH 1 /* MIXINR_HPMIXL_VOL */
+#define WM8962_IN4L_HPMIXL_VOL_MASK 0x0038 /* IN4L_HPMIXL_VOL - [5:3] */
+#define WM8962_IN4L_HPMIXL_VOL_SHIFT 3 /* IN4L_HPMIXL_VOL - [5:3] */
+#define WM8962_IN4L_HPMIXL_VOL_WIDTH 3 /* IN4L_HPMIXL_VOL - [5:3] */
+#define WM8962_IN4R_HPMIXL_VOL_MASK 0x0007 /* IN4R_HPMIXL_VOL - [2:0] */
+#define WM8962_IN4R_HPMIXL_VOL_SHIFT 0 /* IN4R_HPMIXL_VOL - [2:0] */
+#define WM8962_IN4R_HPMIXL_VOL_WIDTH 3 /* IN4R_HPMIXL_VOL - [2:0] */
+
+/*
+ * R103 (0x67) - Headphone Mixer (4)
+ */
+#define WM8962_HPMIXR_MUTE 0x0100 /* HPMIXR_MUTE */
+#define WM8962_HPMIXR_MUTE_MASK 0x0100 /* HPMIXR_MUTE */
+#define WM8962_HPMIXR_MUTE_SHIFT 8 /* HPMIXR_MUTE */
+#define WM8962_HPMIXR_MUTE_WIDTH 1 /* HPMIXR_MUTE */
+#define WM8962_MIXINL_HPMIXR_VOL 0x0080 /* MIXINL_HPMIXR_VOL */
+#define WM8962_MIXINL_HPMIXR_VOL_MASK 0x0080 /* MIXINL_HPMIXR_VOL */
+#define WM8962_MIXINL_HPMIXR_VOL_SHIFT 7 /* MIXINL_HPMIXR_VOL */
+#define WM8962_MIXINL_HPMIXR_VOL_WIDTH 1 /* MIXINL_HPMIXR_VOL */
+#define WM8962_MIXINR_HPMIXR_VOL 0x0040 /* MIXINR_HPMIXR_VOL */
+#define WM8962_MIXINR_HPMIXR_VOL_MASK 0x0040 /* MIXINR_HPMIXR_VOL */
+#define WM8962_MIXINR_HPMIXR_VOL_SHIFT 6 /* MIXINR_HPMIXR_VOL */
+#define WM8962_MIXINR_HPMIXR_VOL_WIDTH 1 /* MIXINR_HPMIXR_VOL */
+#define WM8962_IN4L_HPMIXR_VOL_MASK 0x0038 /* IN4L_HPMIXR_VOL - [5:3] */
+#define WM8962_IN4L_HPMIXR_VOL_SHIFT 3 /* IN4L_HPMIXR_VOL - [5:3] */
+#define WM8962_IN4L_HPMIXR_VOL_WIDTH 3 /* IN4L_HPMIXR_VOL - [5:3] */
+#define WM8962_IN4R_HPMIXR_VOL_MASK 0x0007 /* IN4R_HPMIXR_VOL - [2:0] */
+#define WM8962_IN4R_HPMIXR_VOL_SHIFT 0 /* IN4R_HPMIXR_VOL - [2:0] */
+#define WM8962_IN4R_HPMIXR_VOL_WIDTH 3 /* IN4R_HPMIXR_VOL - [2:0] */
+
+/*
+ * R105 (0x69) - Speaker Mixer (1)
+ */
+#define WM8962_SPKMIXL_TO_SPKOUTL_PGA 0x0080 /* SPKMIXL_TO_SPKOUTL_PGA */
+#define WM8962_SPKMIXL_TO_SPKOUTL_PGA_MASK 0x0080 /* SPKMIXL_TO_SPKOUTL_PGA */
+#define WM8962_SPKMIXL_TO_SPKOUTL_PGA_SHIFT 7 /* SPKMIXL_TO_SPKOUTL_PGA */
+#define WM8962_SPKMIXL_TO_SPKOUTL_PGA_WIDTH 1 /* SPKMIXL_TO_SPKOUTL_PGA */
+#define WM8962_DACL_TO_SPKMIXL 0x0020 /* DACL_TO_SPKMIXL */
+#define WM8962_DACL_TO_SPKMIXL_MASK 0x0020 /* DACL_TO_SPKMIXL */
+#define WM8962_DACL_TO_SPKMIXL_SHIFT 5 /* DACL_TO_SPKMIXL */
+#define WM8962_DACL_TO_SPKMIXL_WIDTH 1 /* DACL_TO_SPKMIXL */
+#define WM8962_DACR_TO_SPKMIXL 0x0010 /* DACR_TO_SPKMIXL */
+#define WM8962_DACR_TO_SPKMIXL_MASK 0x0010 /* DACR_TO_SPKMIXL */
+#define WM8962_DACR_TO_SPKMIXL_SHIFT 4 /* DACR_TO_SPKMIXL */
+#define WM8962_DACR_TO_SPKMIXL_WIDTH 1 /* DACR_TO_SPKMIXL */
+#define WM8962_MIXINL_TO_SPKMIXL 0x0008 /* MIXINL_TO_SPKMIXL */
+#define WM8962_MIXINL_TO_SPKMIXL_MASK 0x0008 /* MIXINL_TO_SPKMIXL */
+#define WM8962_MIXINL_TO_SPKMIXL_SHIFT 3 /* MIXINL_TO_SPKMIXL */
+#define WM8962_MIXINL_TO_SPKMIXL_WIDTH 1 /* MIXINL_TO_SPKMIXL */
+#define WM8962_MIXINR_TO_SPKMIXL 0x0004 /* MIXINR_TO_SPKMIXL */
+#define WM8962_MIXINR_TO_SPKMIXL_MASK 0x0004 /* MIXINR_TO_SPKMIXL */
+#define WM8962_MIXINR_TO_SPKMIXL_SHIFT 2 /* MIXINR_TO_SPKMIXL */
+#define WM8962_MIXINR_TO_SPKMIXL_WIDTH 1 /* MIXINR_TO_SPKMIXL */
+#define WM8962_IN4L_TO_SPKMIXL 0x0002 /* IN4L_TO_SPKMIXL */
+#define WM8962_IN4L_TO_SPKMIXL_MASK 0x0002 /* IN4L_TO_SPKMIXL */
+#define WM8962_IN4L_TO_SPKMIXL_SHIFT 1 /* IN4L_TO_SPKMIXL */
+#define WM8962_IN4L_TO_SPKMIXL_WIDTH 1 /* IN4L_TO_SPKMIXL */
+#define WM8962_IN4R_TO_SPKMIXL 0x0001 /* IN4R_TO_SPKMIXL */
+#define WM8962_IN4R_TO_SPKMIXL_MASK 0x0001 /* IN4R_TO_SPKMIXL */
+#define WM8962_IN4R_TO_SPKMIXL_SHIFT 0 /* IN4R_TO_SPKMIXL */
+#define WM8962_IN4R_TO_SPKMIXL_WIDTH 1 /* IN4R_TO_SPKMIXL */
+
+/*
+ * R106 (0x6A) - Speaker Mixer (2)
+ */
+#define WM8962_SPKMIXR_TO_SPKOUTR_PGA 0x0080 /* SPKMIXR_TO_SPKOUTR_PGA */
+#define WM8962_SPKMIXR_TO_SPKOUTR_PGA_MASK 0x0080 /* SPKMIXR_TO_SPKOUTR_PGA */
+#define WM8962_SPKMIXR_TO_SPKOUTR_PGA_SHIFT 7 /* SPKMIXR_TO_SPKOUTR_PGA */
+#define WM8962_SPKMIXR_TO_SPKOUTR_PGA_WIDTH 1 /* SPKMIXR_TO_SPKOUTR_PGA */
+#define WM8962_DACL_TO_SPKMIXR 0x0020 /* DACL_TO_SPKMIXR */
+#define WM8962_DACL_TO_SPKMIXR_MASK 0x0020 /* DACL_TO_SPKMIXR */
+#define WM8962_DACL_TO_SPKMIXR_SHIFT 5 /* DACL_TO_SPKMIXR */
+#define WM8962_DACL_TO_SPKMIXR_WIDTH 1 /* DACL_TO_SPKMIXR */
+#define WM8962_DACR_TO_SPKMIXR 0x0010 /* DACR_TO_SPKMIXR */
+#define WM8962_DACR_TO_SPKMIXR_MASK 0x0010 /* DACR_TO_SPKMIXR */
+#define WM8962_DACR_TO_SPKMIXR_SHIFT 4 /* DACR_TO_SPKMIXR */
+#define WM8962_DACR_TO_SPKMIXR_WIDTH 1 /* DACR_TO_SPKMIXR */
+#define WM8962_MIXINL_TO_SPKMIXR 0x0008 /* MIXINL_TO_SPKMIXR */
+#define WM8962_MIXINL_TO_SPKMIXR_MASK 0x0008 /* MIXINL_TO_SPKMIXR */
+#define WM8962_MIXINL_TO_SPKMIXR_SHIFT 3 /* MIXINL_TO_SPKMIXR */
+#define WM8962_MIXINL_TO_SPKMIXR_WIDTH 1 /* MIXINL_TO_SPKMIXR */
+#define WM8962_MIXINR_TO_SPKMIXR 0x0004 /* MIXINR_TO_SPKMIXR */
+#define WM8962_MIXINR_TO_SPKMIXR_MASK 0x0004 /* MIXINR_TO_SPKMIXR */
+#define WM8962_MIXINR_TO_SPKMIXR_SHIFT 2 /* MIXINR_TO_SPKMIXR */
+#define WM8962_MIXINR_TO_SPKMIXR_WIDTH 1 /* MIXINR_TO_SPKMIXR */
+#define WM8962_IN4L_TO_SPKMIXR 0x0002 /* IN4L_TO_SPKMIXR */
+#define WM8962_IN4L_TO_SPKMIXR_MASK 0x0002 /* IN4L_TO_SPKMIXR */
+#define WM8962_IN4L_TO_SPKMIXR_SHIFT 1 /* IN4L_TO_SPKMIXR */
+#define WM8962_IN4L_TO_SPKMIXR_WIDTH 1 /* IN4L_TO_SPKMIXR */
+#define WM8962_IN4R_TO_SPKMIXR 0x0001 /* IN4R_TO_SPKMIXR */
+#define WM8962_IN4R_TO_SPKMIXR_MASK 0x0001 /* IN4R_TO_SPKMIXR */
+#define WM8962_IN4R_TO_SPKMIXR_SHIFT 0 /* IN4R_TO_SPKMIXR */
+#define WM8962_IN4R_TO_SPKMIXR_WIDTH 1 /* IN4R_TO_SPKMIXR */
+
+/*
+ * R107 (0x6B) - Speaker Mixer (3)
+ */
+#define WM8962_SPKMIXL_MUTE 0x0100 /* SPKMIXL_MUTE */
+#define WM8962_SPKMIXL_MUTE_MASK 0x0100 /* SPKMIXL_MUTE */
+#define WM8962_SPKMIXL_MUTE_SHIFT 8 /* SPKMIXL_MUTE */
+#define WM8962_SPKMIXL_MUTE_WIDTH 1 /* SPKMIXL_MUTE */
+#define WM8962_MIXINL_SPKMIXL_VOL 0x0080 /* MIXINL_SPKMIXL_VOL */
+#define WM8962_MIXINL_SPKMIXL_VOL_MASK 0x0080 /* MIXINL_SPKMIXL_VOL */
+#define WM8962_MIXINL_SPKMIXL_VOL_SHIFT 7 /* MIXINL_SPKMIXL_VOL */
+#define WM8962_MIXINL_SPKMIXL_VOL_WIDTH 1 /* MIXINL_SPKMIXL_VOL */
+#define WM8962_MIXINR_SPKMIXL_VOL 0x0040 /* MIXINR_SPKMIXL_VOL */
+#define WM8962_MIXINR_SPKMIXL_VOL_MASK 0x0040 /* MIXINR_SPKMIXL_VOL */
+#define WM8962_MIXINR_SPKMIXL_VOL_SHIFT 6 /* MIXINR_SPKMIXL_VOL */
+#define WM8962_MIXINR_SPKMIXL_VOL_WIDTH 1 /* MIXINR_SPKMIXL_VOL */
+#define WM8962_IN4L_SPKMIXL_VOL_MASK 0x0038 /* IN4L_SPKMIXL_VOL - [5:3] */
+#define WM8962_IN4L_SPKMIXL_VOL_SHIFT 3 /* IN4L_SPKMIXL_VOL - [5:3] */
+#define WM8962_IN4L_SPKMIXL_VOL_WIDTH 3 /* IN4L_SPKMIXL_VOL - [5:3] */
+#define WM8962_IN4R_SPKMIXL_VOL_MASK 0x0007 /* IN4R_SPKMIXL_VOL - [2:0] */
+#define WM8962_IN4R_SPKMIXL_VOL_SHIFT 0 /* IN4R_SPKMIXL_VOL - [2:0] */
+#define WM8962_IN4R_SPKMIXL_VOL_WIDTH 3 /* IN4R_SPKMIXL_VOL - [2:0] */
+
+/*
+ * R108 (0x6C) - Speaker Mixer (4)
+ */
+#define WM8962_SPKMIXR_MUTE 0x0100 /* SPKMIXR_MUTE */
+#define WM8962_SPKMIXR_MUTE_MASK 0x0100 /* SPKMIXR_MUTE */
+#define WM8962_SPKMIXR_MUTE_SHIFT 8 /* SPKMIXR_MUTE */
+#define WM8962_SPKMIXR_MUTE_WIDTH 1 /* SPKMIXR_MUTE */
+#define WM8962_MIXINL_SPKMIXR_VOL 0x0080 /* MIXINL_SPKMIXR_VOL */
+#define WM8962_MIXINL_SPKMIXR_VOL_MASK 0x0080 /* MIXINL_SPKMIXR_VOL */
+#define WM8962_MIXINL_SPKMIXR_VOL_SHIFT 7 /* MIXINL_SPKMIXR_VOL */
+#define WM8962_MIXINL_SPKMIXR_VOL_WIDTH 1 /* MIXINL_SPKMIXR_VOL */
+#define WM8962_MIXINR_SPKMIXR_VOL 0x0040 /* MIXINR_SPKMIXR_VOL */
+#define WM8962_MIXINR_SPKMIXR_VOL_MASK 0x0040 /* MIXINR_SPKMIXR_VOL */
+#define WM8962_MIXINR_SPKMIXR_VOL_SHIFT 6 /* MIXINR_SPKMIXR_VOL */
+#define WM8962_MIXINR_SPKMIXR_VOL_WIDTH 1 /* MIXINR_SPKMIXR_VOL */
+#define WM8962_IN4L_SPKMIXR_VOL_MASK 0x0038 /* IN4L_SPKMIXR_VOL - [5:3] */
+#define WM8962_IN4L_SPKMIXR_VOL_SHIFT 3 /* IN4L_SPKMIXR_VOL - [5:3] */
+#define WM8962_IN4L_SPKMIXR_VOL_WIDTH 3 /* IN4L_SPKMIXR_VOL - [5:3] */
+#define WM8962_IN4R_SPKMIXR_VOL_MASK 0x0007 /* IN4R_SPKMIXR_VOL - [2:0] */
+#define WM8962_IN4R_SPKMIXR_VOL_SHIFT 0 /* IN4R_SPKMIXR_VOL - [2:0] */
+#define WM8962_IN4R_SPKMIXR_VOL_WIDTH 3 /* IN4R_SPKMIXR_VOL - [2:0] */
+
+/*
+ * R109 (0x6D) - Speaker Mixer (5)
+ */
+#define WM8962_DACL_SPKMIXL_VOL 0x0080 /* DACL_SPKMIXL_VOL */
+#define WM8962_DACL_SPKMIXL_VOL_MASK 0x0080 /* DACL_SPKMIXL_VOL */
+#define WM8962_DACL_SPKMIXL_VOL_SHIFT 7 /* DACL_SPKMIXL_VOL */
+#define WM8962_DACL_SPKMIXL_VOL_WIDTH 1 /* DACL_SPKMIXL_VOL */
+#define WM8962_DACR_SPKMIXL_VOL 0x0040 /* DACR_SPKMIXL_VOL */
+#define WM8962_DACR_SPKMIXL_VOL_MASK 0x0040 /* DACR_SPKMIXL_VOL */
+#define WM8962_DACR_SPKMIXL_VOL_SHIFT 6 /* DACR_SPKMIXL_VOL */
+#define WM8962_DACR_SPKMIXL_VOL_WIDTH 1 /* DACR_SPKMIXL_VOL */
+#define WM8962_DACL_SPKMIXR_VOL 0x0020 /* DACL_SPKMIXR_VOL */
+#define WM8962_DACL_SPKMIXR_VOL_MASK 0x0020 /* DACL_SPKMIXR_VOL */
+#define WM8962_DACL_SPKMIXR_VOL_SHIFT 5 /* DACL_SPKMIXR_VOL */
+#define WM8962_DACL_SPKMIXR_VOL_WIDTH 1 /* DACL_SPKMIXR_VOL */
+#define WM8962_DACR_SPKMIXR_VOL 0x0010 /* DACR_SPKMIXR_VOL */
+#define WM8962_DACR_SPKMIXR_VOL_MASK 0x0010 /* DACR_SPKMIXR_VOL */
+#define WM8962_DACR_SPKMIXR_VOL_SHIFT 4 /* DACR_SPKMIXR_VOL */
+#define WM8962_DACR_SPKMIXR_VOL_WIDTH 1 /* DACR_SPKMIXR_VOL */
+
+/*
+ * R110 (0x6E) - Beep Generator (1)
+ */
+#define WM8962_BEEP_GAIN_MASK 0x00F0 /* BEEP_GAIN - [7:4] */
+#define WM8962_BEEP_GAIN_SHIFT 4 /* BEEP_GAIN - [7:4] */
+#define WM8962_BEEP_GAIN_WIDTH 4 /* BEEP_GAIN - [7:4] */
+#define WM8962_BEEP_RATE_MASK 0x0006 /* BEEP_RATE - [2:1] */
+#define WM8962_BEEP_RATE_SHIFT 1 /* BEEP_RATE - [2:1] */
+#define WM8962_BEEP_RATE_WIDTH 2 /* BEEP_RATE - [2:1] */
+#define WM8962_BEEP_ENA 0x0001 /* BEEP_ENA */
+#define WM8962_BEEP_ENA_MASK 0x0001 /* BEEP_ENA */
+#define WM8962_BEEP_ENA_SHIFT 0 /* BEEP_ENA */
+#define WM8962_BEEP_ENA_WIDTH 1 /* BEEP_ENA */
+
+/*
+ * R115 (0x73) - Oscillator Trim (3)
+ */
+#define WM8962_OSC_TRIM_XTI_MASK 0x001F /* OSC_TRIM_XTI - [4:0] */
+#define WM8962_OSC_TRIM_XTI_SHIFT 0 /* OSC_TRIM_XTI - [4:0] */
+#define WM8962_OSC_TRIM_XTI_WIDTH 5 /* OSC_TRIM_XTI - [4:0] */
+
+/*
+ * R116 (0x74) - Oscillator Trim (4)
+ */
+#define WM8962_OSC_TRIM_XTO_MASK 0x001F /* OSC_TRIM_XTO - [4:0] */
+#define WM8962_OSC_TRIM_XTO_SHIFT 0 /* OSC_TRIM_XTO - [4:0] */
+#define WM8962_OSC_TRIM_XTO_WIDTH 5 /* OSC_TRIM_XTO - [4:0] */
+
+/*
+ * R119 (0x77) - Oscillator Trim (7)
+ */
+#define WM8962_XTO_CAP_SEL_MASK 0x00F0 /* XTO_CAP_SEL - [7:4] */
+#define WM8962_XTO_CAP_SEL_SHIFT 4 /* XTO_CAP_SEL - [7:4] */
+#define WM8962_XTO_CAP_SEL_WIDTH 4 /* XTO_CAP_SEL - [7:4] */
+#define WM8962_XTI_CAP_SEL_MASK 0x000F /* XTI_CAP_SEL - [3:0] */
+#define WM8962_XTI_CAP_SEL_SHIFT 0 /* XTI_CAP_SEL - [3:0] */
+#define WM8962_XTI_CAP_SEL_WIDTH 4 /* XTI_CAP_SEL - [3:0] */
+
+/*
+ * R124 (0x7C) - Analogue Clocking1
+ */
+#define WM8962_CLKOUT2_SEL_MASK 0x0060 /* CLKOUT2_SEL - [6:5] */
+#define WM8962_CLKOUT2_SEL_SHIFT 5 /* CLKOUT2_SEL - [6:5] */
+#define WM8962_CLKOUT2_SEL_WIDTH 2 /* CLKOUT2_SEL - [6:5] */
+#define WM8962_CLKOUT3_SEL_MASK 0x0018 /* CLKOUT3_SEL - [4:3] */
+#define WM8962_CLKOUT3_SEL_SHIFT 3 /* CLKOUT3_SEL - [4:3] */
+#define WM8962_CLKOUT3_SEL_WIDTH 2 /* CLKOUT3_SEL - [4:3] */
+#define WM8962_CLKOUT5_SEL 0x0001 /* CLKOUT5_SEL */
+#define WM8962_CLKOUT5_SEL_MASK 0x0001 /* CLKOUT5_SEL */
+#define WM8962_CLKOUT5_SEL_SHIFT 0 /* CLKOUT5_SEL */
+#define WM8962_CLKOUT5_SEL_WIDTH 1 /* CLKOUT5_SEL */
+
+/*
+ * R125 (0x7D) - Analogue Clocking2
+ */
+#define WM8962_PLL2_OUTDIV 0x0080 /* PLL2_OUTDIV */
+#define WM8962_PLL2_OUTDIV_MASK 0x0080 /* PLL2_OUTDIV */
+#define WM8962_PLL2_OUTDIV_SHIFT 7 /* PLL2_OUTDIV */
+#define WM8962_PLL2_OUTDIV_WIDTH 1 /* PLL2_OUTDIV */
+#define WM8962_PLL3_OUTDIV 0x0040 /* PLL3_OUTDIV */
+#define WM8962_PLL3_OUTDIV_MASK 0x0040 /* PLL3_OUTDIV */
+#define WM8962_PLL3_OUTDIV_SHIFT 6 /* PLL3_OUTDIV */
+#define WM8962_PLL3_OUTDIV_WIDTH 1 /* PLL3_OUTDIV */
+#define WM8962_PLL_SYSCLK_DIV_MASK 0x0018 /* PLL_SYSCLK_DIV - [4:3] */
+#define WM8962_PLL_SYSCLK_DIV_SHIFT 3 /* PLL_SYSCLK_DIV - [4:3] */
+#define WM8962_PLL_SYSCLK_DIV_WIDTH 2 /* PLL_SYSCLK_DIV - [4:3] */
+#define WM8962_CLKOUT3_DIV 0x0004 /* CLKOUT3_DIV */
+#define WM8962_CLKOUT3_DIV_MASK 0x0004 /* CLKOUT3_DIV */
+#define WM8962_CLKOUT3_DIV_SHIFT 2 /* CLKOUT3_DIV */
+#define WM8962_CLKOUT3_DIV_WIDTH 1 /* CLKOUT3_DIV */
+#define WM8962_CLKOUT2_DIV 0x0002 /* CLKOUT2_DIV */
+#define WM8962_CLKOUT2_DIV_MASK 0x0002 /* CLKOUT2_DIV */
+#define WM8962_CLKOUT2_DIV_SHIFT 1 /* CLKOUT2_DIV */
+#define WM8962_CLKOUT2_DIV_WIDTH 1 /* CLKOUT2_DIV */
+#define WM8962_CLKOUT5_DIV 0x0001 /* CLKOUT5_DIV */
+#define WM8962_CLKOUT5_DIV_MASK 0x0001 /* CLKOUT5_DIV */
+#define WM8962_CLKOUT5_DIV_SHIFT 0 /* CLKOUT5_DIV */
+#define WM8962_CLKOUT5_DIV_WIDTH 1 /* CLKOUT5_DIV */
+
+/*
+ * R126 (0x7E) - Analogue Clocking3
+ */
+#define WM8962_CLKOUT2_OE 0x0008 /* CLKOUT2_OE */
+#define WM8962_CLKOUT2_OE_MASK 0x0008 /* CLKOUT2_OE */
+#define WM8962_CLKOUT2_OE_SHIFT 3 /* CLKOUT2_OE */
+#define WM8962_CLKOUT2_OE_WIDTH 1 /* CLKOUT2_OE */
+#define WM8962_CLKOUT3_OE 0x0004 /* CLKOUT3_OE */
+#define WM8962_CLKOUT3_OE_MASK 0x0004 /* CLKOUT3_OE */
+#define WM8962_CLKOUT3_OE_SHIFT 2 /* CLKOUT3_OE */
+#define WM8962_CLKOUT3_OE_WIDTH 1 /* CLKOUT3_OE */
+#define WM8962_CLKOUT5_OE 0x0001 /* CLKOUT5_OE */
+#define WM8962_CLKOUT5_OE_MASK 0x0001 /* CLKOUT5_OE */
+#define WM8962_CLKOUT5_OE_SHIFT 0 /* CLKOUT5_OE */
+#define WM8962_CLKOUT5_OE_WIDTH 1 /* CLKOUT5_OE */
+
+/*
+ * R127 (0x7F) - PLL Software Reset
+ */
+#define WM8962_SW_RESET_PLL_MASK 0xFFFF /* SW_RESET_PLL - [15:0] */
+#define WM8962_SW_RESET_PLL_SHIFT 0 /* SW_RESET_PLL - [15:0] */
+#define WM8962_SW_RESET_PLL_WIDTH 16 /* SW_RESET_PLL - [15:0] */
+
+/*
+ * R129 (0x81) - PLL2
+ */
+#define WM8962_OSC_ENA 0x0080 /* OSC_ENA */
+#define WM8962_OSC_ENA_MASK 0x0080 /* OSC_ENA */
+#define WM8962_OSC_ENA_SHIFT 7 /* OSC_ENA */
+#define WM8962_OSC_ENA_WIDTH 1 /* OSC_ENA */
+#define WM8962_PLL2_ENA 0x0020 /* PLL2_ENA */
+#define WM8962_PLL2_ENA_MASK 0x0020 /* PLL2_ENA */
+#define WM8962_PLL2_ENA_SHIFT 5 /* PLL2_ENA */
+#define WM8962_PLL2_ENA_WIDTH 1 /* PLL2_ENA */
+#define WM8962_PLL3_ENA 0x0010 /* PLL3_ENA */
+#define WM8962_PLL3_ENA_MASK 0x0010 /* PLL3_ENA */
+#define WM8962_PLL3_ENA_SHIFT 4 /* PLL3_ENA */
+#define WM8962_PLL3_ENA_WIDTH 1 /* PLL3_ENA */
+
+/*
+ * R131 (0x83) - PLL 4
+ */
+#define WM8962_PLL_CLK_SRC 0x0002 /* PLL_CLK_SRC */
+#define WM8962_PLL_CLK_SRC_MASK 0x0002 /* PLL_CLK_SRC */
+#define WM8962_PLL_CLK_SRC_SHIFT 1 /* PLL_CLK_SRC */
+#define WM8962_PLL_CLK_SRC_WIDTH 1 /* PLL_CLK_SRC */
+#define WM8962_FLL_TO_PLL3 0x0001 /* FLL_TO_PLL3 */
+#define WM8962_FLL_TO_PLL3_MASK 0x0001 /* FLL_TO_PLL3 */
+#define WM8962_FLL_TO_PLL3_SHIFT 0 /* FLL_TO_PLL3 */
+#define WM8962_FLL_TO_PLL3_WIDTH 1 /* FLL_TO_PLL3 */
+
+/*
+ * R136 (0x88) - PLL 9
+ */
+#define WM8962_PLL2_FRAC 0x0040 /* PLL2_FRAC */
+#define WM8962_PLL2_FRAC_MASK 0x0040 /* PLL2_FRAC */
+#define WM8962_PLL2_FRAC_SHIFT 6 /* PLL2_FRAC */
+#define WM8962_PLL2_FRAC_WIDTH 1 /* PLL2_FRAC */
+#define WM8962_PLL2_N_MASK 0x001F /* PLL2_N - [4:0] */
+#define WM8962_PLL2_N_SHIFT 0 /* PLL2_N - [4:0] */
+#define WM8962_PLL2_N_WIDTH 5 /* PLL2_N - [4:0] */
+
+/*
+ * R137 (0x89) - PLL 10
+ */
+#define WM8962_PLL2_K_MASK 0x00FF /* PLL2_K - [7:0] */
+#define WM8962_PLL2_K_SHIFT 0 /* PLL2_K - [7:0] */
+#define WM8962_PLL2_K_WIDTH 8 /* PLL2_K - [7:0] */
+
+/*
+ * R138 (0x8A) - PLL 11
+ */
+#define WM8962_PLL2_K_MASK 0x00FF /* PLL2_K - [7:0] */
+#define WM8962_PLL2_K_SHIFT 0 /* PLL2_K - [7:0] */
+#define WM8962_PLL2_K_WIDTH 8 /* PLL2_K - [7:0] */
+
+/*
+ * R139 (0x8B) - PLL 12
+ */
+#define WM8962_PLL2_K_MASK 0x00FF /* PLL2_K - [7:0] */
+#define WM8962_PLL2_K_SHIFT 0 /* PLL2_K - [7:0] */
+#define WM8962_PLL2_K_WIDTH 8 /* PLL2_K - [7:0] */
+
+/*
+ * R140 (0x8C) - PLL 13
+ */
+#define WM8962_PLL3_FRAC 0x0040 /* PLL3_FRAC */
+#define WM8962_PLL3_FRAC_MASK 0x0040 /* PLL3_FRAC */
+#define WM8962_PLL3_FRAC_SHIFT 6 /* PLL3_FRAC */
+#define WM8962_PLL3_FRAC_WIDTH 1 /* PLL3_FRAC */
+#define WM8962_PLL3_N_MASK 0x001F /* PLL3_N - [4:0] */
+#define WM8962_PLL3_N_SHIFT 0 /* PLL3_N - [4:0] */
+#define WM8962_PLL3_N_WIDTH 5 /* PLL3_N - [4:0] */
+
+/*
+ * R141 (0x8D) - PLL 14
+ */
+#define WM8962_PLL3_K_MASK 0x00FF /* PLL3_K - [7:0] */
+#define WM8962_PLL3_K_SHIFT 0 /* PLL3_K - [7:0] */
+#define WM8962_PLL3_K_WIDTH 8 /* PLL3_K - [7:0] */
+
+/*
+ * R142 (0x8E) - PLL 15
+ */
+#define WM8962_PLL3_K_MASK 0x00FF /* PLL3_K - [7:0] */
+#define WM8962_PLL3_K_SHIFT 0 /* PLL3_K - [7:0] */
+#define WM8962_PLL3_K_WIDTH 8 /* PLL3_K - [7:0] */
+
+/*
+ * R143 (0x8F) - PLL 16
+ */
+#define WM8962_PLL3_K_MASK 0x00FF /* PLL3_K - [7:0] */
+#define WM8962_PLL3_K_SHIFT 0 /* PLL3_K - [7:0] */
+#define WM8962_PLL3_K_WIDTH 8 /* PLL3_K - [7:0] */
+
+/*
+ * R155 (0x9B) - FLL Control (1)
+ */
+#define WM8962_FLL_REFCLK_SRC_MASK 0x0060 /* FLL_REFCLK_SRC - [6:5] */
+#define WM8962_FLL_REFCLK_SRC_SHIFT 5 /* FLL_REFCLK_SRC - [6:5] */
+#define WM8962_FLL_REFCLK_SRC_WIDTH 2 /* FLL_REFCLK_SRC - [6:5] */
+#define WM8962_FLL_FRAC 0x0004 /* FLL_FRAC */
+#define WM8962_FLL_FRAC_MASK 0x0004 /* FLL_FRAC */
+#define WM8962_FLL_FRAC_SHIFT 2 /* FLL_FRAC */
+#define WM8962_FLL_FRAC_WIDTH 1 /* FLL_FRAC */
+#define WM8962_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */
+#define WM8962_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */
+#define WM8962_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */
+#define WM8962_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */
+#define WM8962_FLL_ENA 0x0001 /* FLL_ENA */
+#define WM8962_FLL_ENA_MASK 0x0001 /* FLL_ENA */
+#define WM8962_FLL_ENA_SHIFT 0 /* FLL_ENA */
+#define WM8962_FLL_ENA_WIDTH 1 /* FLL_ENA */
+
+/*
+ * R156 (0x9C) - FLL Control (2)
+ */
+#define WM8962_FLL_OUTDIV_MASK 0x01F8 /* FLL_OUTDIV - [8:3] */
+#define WM8962_FLL_OUTDIV_SHIFT 3 /* FLL_OUTDIV - [8:3] */
+#define WM8962_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [8:3] */
+#define WM8962_FLL_REFCLK_DIV_MASK 0x0003 /* FLL_REFCLK_DIV - [1:0] */
+#define WM8962_FLL_REFCLK_DIV_SHIFT 0 /* FLL_REFCLK_DIV - [1:0] */
+#define WM8962_FLL_REFCLK_DIV_WIDTH 2 /* FLL_REFCLK_DIV - [1:0] */
+
+/*
+ * R157 (0x9D) - FLL Control (3)
+ */
+#define WM8962_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
+#define WM8962_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
+#define WM8962_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
+
+/*
+ * R159 (0x9F) - FLL Control (5)
+ */
+#define WM8962_FLL_FRC_NCO_VAL_MASK 0x007E /* FLL_FRC_NCO_VAL - [6:1] */
+#define WM8962_FLL_FRC_NCO_VAL_SHIFT 1 /* FLL_FRC_NCO_VAL - [6:1] */
+#define WM8962_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [6:1] */
+#define WM8962_FLL_FRC_NCO 0x0001 /* FLL_FRC_NCO */
+#define WM8962_FLL_FRC_NCO_MASK 0x0001 /* FLL_FRC_NCO */
+#define WM8962_FLL_FRC_NCO_SHIFT 0 /* FLL_FRC_NCO */
+#define WM8962_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */
+
+/*
+ * R160 (0xA0) - FLL Control (6)
+ */
+#define WM8962_FLL_THETA_MASK 0xFFFF /* FLL_THETA - [15:0] */
+#define WM8962_FLL_THETA_SHIFT 0 /* FLL_THETA - [15:0] */
+#define WM8962_FLL_THETA_WIDTH 16 /* FLL_THETA - [15:0] */
+
+/*
+ * R161 (0xA1) - FLL Control (7)
+ */
+#define WM8962_FLL_LAMBDA_MASK 0xFFFF /* FLL_LAMBDA - [15:0] */
+#define WM8962_FLL_LAMBDA_SHIFT 0 /* FLL_LAMBDA - [15:0] */
+#define WM8962_FLL_LAMBDA_WIDTH 16 /* FLL_LAMBDA - [15:0] */
+
+/*
+ * R162 (0xA2) - FLL Control (8)
+ */
+#define WM8962_FLL_N_MASK 0x03FF /* FLL_N - [9:0] */
+#define WM8962_FLL_N_SHIFT 0 /* FLL_N - [9:0] */
+#define WM8962_FLL_N_WIDTH 10 /* FLL_N - [9:0] */
+
+/*
+ * R252 (0xFC) - General test 1
+ */
+#define WM8962_REG_SYNC 0x0004 /* REG_SYNC */
+#define WM8962_REG_SYNC_MASK 0x0004 /* REG_SYNC */
+#define WM8962_REG_SYNC_SHIFT 2 /* REG_SYNC */
+#define WM8962_REG_SYNC_WIDTH 1 /* REG_SYNC */
+#define WM8962_AUTO_INC 0x0001 /* AUTO_INC */
+#define WM8962_AUTO_INC_MASK 0x0001 /* AUTO_INC */
+#define WM8962_AUTO_INC_SHIFT 0 /* AUTO_INC */
+#define WM8962_AUTO_INC_WIDTH 1 /* AUTO_INC */
+
+/*
+ * R256 (0x100) - DF1
+ */
+#define WM8962_DRC_DF1_ENA 0x0008 /* DRC_DF1_ENA */
+#define WM8962_DRC_DF1_ENA_MASK 0x0008 /* DRC_DF1_ENA */
+#define WM8962_DRC_DF1_ENA_SHIFT 3 /* DRC_DF1_ENA */
+#define WM8962_DRC_DF1_ENA_WIDTH 1 /* DRC_DF1_ENA */
+#define WM8962_DF1_SHARED_COEFF 0x0004 /* DF1_SHARED_COEFF */
+#define WM8962_DF1_SHARED_COEFF_MASK 0x0004 /* DF1_SHARED_COEFF */
+#define WM8962_DF1_SHARED_COEFF_SHIFT 2 /* DF1_SHARED_COEFF */
+#define WM8962_DF1_SHARED_COEFF_WIDTH 1 /* DF1_SHARED_COEFF */
+#define WM8962_DF1_SHARED_COEFF_SEL 0x0002 /* DF1_SHARED_COEFF_SEL */
+#define WM8962_DF1_SHARED_COEFF_SEL_MASK 0x0002 /* DF1_SHARED_COEFF_SEL */
+#define WM8962_DF1_SHARED_COEFF_SEL_SHIFT 1 /* DF1_SHARED_COEFF_SEL */
+#define WM8962_DF1_SHARED_COEFF_SEL_WIDTH 1 /* DF1_SHARED_COEFF_SEL */
+#define WM8962_DF1_ENA 0x0001 /* DF1_ENA */
+#define WM8962_DF1_ENA_MASK 0x0001 /* DF1_ENA */
+#define WM8962_DF1_ENA_SHIFT 0 /* DF1_ENA */
+#define WM8962_DF1_ENA_WIDTH 1 /* DF1_ENA */
+
+/*
+ * R257 (0x101) - DF2
+ */
+#define WM8962_DF1_COEFF_L0_MASK 0xFFFF /* DF1_COEFF_L0 - [15:0] */
+#define WM8962_DF1_COEFF_L0_SHIFT 0 /* DF1_COEFF_L0 - [15:0] */
+#define WM8962_DF1_COEFF_L0_WIDTH 16 /* DF1_COEFF_L0 - [15:0] */
+
+/*
+ * R258 (0x102) - DF3
+ */
+#define WM8962_DF1_COEFF_L1_MASK 0xFFFF /* DF1_COEFF_L1 - [15:0] */
+#define WM8962_DF1_COEFF_L1_SHIFT 0 /* DF1_COEFF_L1 - [15:0] */
+#define WM8962_DF1_COEFF_L1_WIDTH 16 /* DF1_COEFF_L1 - [15:0] */
+
+/*
+ * R259 (0x103) - DF4
+ */
+#define WM8962_DF1_COEFF_L2_MASK 0xFFFF /* DF1_COEFF_L2 - [15:0] */
+#define WM8962_DF1_COEFF_L2_SHIFT 0 /* DF1_COEFF_L2 - [15:0] */
+#define WM8962_DF1_COEFF_L2_WIDTH 16 /* DF1_COEFF_L2 - [15:0] */
+
+/*
+ * R260 (0x104) - DF5
+ */
+#define WM8962_DF1_COEFF_R0_MASK 0xFFFF /* DF1_COEFF_R0 - [15:0] */
+#define WM8962_DF1_COEFF_R0_SHIFT 0 /* DF1_COEFF_R0 - [15:0] */
+#define WM8962_DF1_COEFF_R0_WIDTH 16 /* DF1_COEFF_R0 - [15:0] */
+
+/*
+ * R261 (0x105) - DF6
+ */
+#define WM8962_DF1_COEFF_R1_MASK 0xFFFF /* DF1_COEFF_R1 - [15:0] */
+#define WM8962_DF1_COEFF_R1_SHIFT 0 /* DF1_COEFF_R1 - [15:0] */
+#define WM8962_DF1_COEFF_R1_WIDTH 16 /* DF1_COEFF_R1 - [15:0] */
+
+/*
+ * R262 (0x106) - DF7
+ */
+#define WM8962_DF1_COEFF_R2_MASK 0xFFFF /* DF1_COEFF_R2 - [15:0] */
+#define WM8962_DF1_COEFF_R2_SHIFT 0 /* DF1_COEFF_R2 - [15:0] */
+#define WM8962_DF1_COEFF_R2_WIDTH 16 /* DF1_COEFF_R2 - [15:0] */
+
+/*
+ * R264 (0x108) - LHPF1
+ */
+#define WM8962_LHPF_MODE 0x0002 /* LHPF_MODE */
+#define WM8962_LHPF_MODE_MASK 0x0002 /* LHPF_MODE */
+#define WM8962_LHPF_MODE_SHIFT 1 /* LHPF_MODE */
+#define WM8962_LHPF_MODE_WIDTH 1 /* LHPF_MODE */
+#define WM8962_LHPF_ENA 0x0001 /* LHPF_ENA */
+#define WM8962_LHPF_ENA_MASK 0x0001 /* LHPF_ENA */
+#define WM8962_LHPF_ENA_SHIFT 0 /* LHPF_ENA */
+#define WM8962_LHPF_ENA_WIDTH 1 /* LHPF_ENA */
+
+/*
+ * R265 (0x109) - LHPF2
+ */
+#define WM8962_LHPF_COEFF_MASK 0xFFFF /* LHPF_COEFF - [15:0] */
+#define WM8962_LHPF_COEFF_SHIFT 0 /* LHPF_COEFF - [15:0] */
+#define WM8962_LHPF_COEFF_WIDTH 16 /* LHPF_COEFF - [15:0] */
+
+/*
+ * R268 (0x10C) - THREED1
+ */
+#define WM8962_ADC_MONOMIX 0x0040 /* ADC_MONOMIX */
+#define WM8962_ADC_MONOMIX_MASK 0x0040 /* ADC_MONOMIX */
+#define WM8962_ADC_MONOMIX_SHIFT 6 /* ADC_MONOMIX */
+#define WM8962_ADC_MONOMIX_WIDTH 1 /* ADC_MONOMIX */
+#define WM8962_THREED_SIGN_L 0x0020 /* THREED_SIGN_L */
+#define WM8962_THREED_SIGN_L_MASK 0x0020 /* THREED_SIGN_L */
+#define WM8962_THREED_SIGN_L_SHIFT 5 /* THREED_SIGN_L */
+#define WM8962_THREED_SIGN_L_WIDTH 1 /* THREED_SIGN_L */
+#define WM8962_THREED_SIGN_R 0x0010 /* THREED_SIGN_R */
+#define WM8962_THREED_SIGN_R_MASK 0x0010 /* THREED_SIGN_R */
+#define WM8962_THREED_SIGN_R_SHIFT 4 /* THREED_SIGN_R */
+#define WM8962_THREED_SIGN_R_WIDTH 1 /* THREED_SIGN_R */
+#define WM8962_THREED_LHPF_MODE 0x0004 /* THREED_LHPF_MODE */
+#define WM8962_THREED_LHPF_MODE_MASK 0x0004 /* THREED_LHPF_MODE */
+#define WM8962_THREED_LHPF_MODE_SHIFT 2 /* THREED_LHPF_MODE */
+#define WM8962_THREED_LHPF_MODE_WIDTH 1 /* THREED_LHPF_MODE */
+#define WM8962_THREED_LHPF_ENA 0x0002 /* THREED_LHPF_ENA */
+#define WM8962_THREED_LHPF_ENA_MASK 0x0002 /* THREED_LHPF_ENA */
+#define WM8962_THREED_LHPF_ENA_SHIFT 1 /* THREED_LHPF_ENA */
+#define WM8962_THREED_LHPF_ENA_WIDTH 1 /* THREED_LHPF_ENA */
+#define WM8962_THREED_ENA 0x0001 /* THREED_ENA */
+#define WM8962_THREED_ENA_MASK 0x0001 /* THREED_ENA */
+#define WM8962_THREED_ENA_SHIFT 0 /* THREED_ENA */
+#define WM8962_THREED_ENA_WIDTH 1 /* THREED_ENA */
+
+/*
+ * R269 (0x10D) - THREED2
+ */
+#define WM8962_THREED_FGAINL_MASK 0xF800 /* THREED_FGAINL - [15:11] */
+#define WM8962_THREED_FGAINL_SHIFT 11 /* THREED_FGAINL - [15:11] */
+#define WM8962_THREED_FGAINL_WIDTH 5 /* THREED_FGAINL - [15:11] */
+#define WM8962_THREED_CGAINL_MASK 0x07C0 /* THREED_CGAINL - [10:6] */
+#define WM8962_THREED_CGAINL_SHIFT 6 /* THREED_CGAINL - [10:6] */
+#define WM8962_THREED_CGAINL_WIDTH 5 /* THREED_CGAINL - [10:6] */
+#define WM8962_THREED_DELAYL_MASK 0x003C /* THREED_DELAYL - [5:2] */
+#define WM8962_THREED_DELAYL_SHIFT 2 /* THREED_DELAYL - [5:2] */
+#define WM8962_THREED_DELAYL_WIDTH 4 /* THREED_DELAYL - [5:2] */
+
+/*
+ * R270 (0x10E) - THREED3
+ */
+#define WM8962_THREED_LHPF_COEFF_MASK 0xFFFF /* THREED_LHPF_COEFF - [15:0] */
+#define WM8962_THREED_LHPF_COEFF_SHIFT 0 /* THREED_LHPF_COEFF - [15:0] */
+#define WM8962_THREED_LHPF_COEFF_WIDTH 16 /* THREED_LHPF_COEFF - [15:0] */
+
+/*
+ * R271 (0x10F) - THREED4
+ */
+#define WM8962_THREED_FGAINR_MASK 0xF800 /* THREED_FGAINR - [15:11] */
+#define WM8962_THREED_FGAINR_SHIFT 11 /* THREED_FGAINR - [15:11] */
+#define WM8962_THREED_FGAINR_WIDTH 5 /* THREED_FGAINR - [15:11] */
+#define WM8962_THREED_CGAINR_MASK 0x07C0 /* THREED_CGAINR - [10:6] */
+#define WM8962_THREED_CGAINR_SHIFT 6 /* THREED_CGAINR - [10:6] */
+#define WM8962_THREED_CGAINR_WIDTH 5 /* THREED_CGAINR - [10:6] */
+#define WM8962_THREED_DELAYR_MASK 0x003C /* THREED_DELAYR - [5:2] */
+#define WM8962_THREED_DELAYR_SHIFT 2 /* THREED_DELAYR - [5:2] */
+#define WM8962_THREED_DELAYR_WIDTH 4 /* THREED_DELAYR - [5:2] */
+
+/*
+ * R276 (0x114) - DRC 1
+ */
+#define WM8962_DRC_SIG_DET_RMS_MASK 0x7C00 /* DRC_SIG_DET_RMS - [14:10] */
+#define WM8962_DRC_SIG_DET_RMS_SHIFT 10 /* DRC_SIG_DET_RMS - [14:10] */
+#define WM8962_DRC_SIG_DET_RMS_WIDTH 5 /* DRC_SIG_DET_RMS - [14:10] */
+#define WM8962_DRC_SIG_DET_PK_MASK 0x0300 /* DRC_SIG_DET_PK - [9:8] */
+#define WM8962_DRC_SIG_DET_PK_SHIFT 8 /* DRC_SIG_DET_PK - [9:8] */
+#define WM8962_DRC_SIG_DET_PK_WIDTH 2 /* DRC_SIG_DET_PK - [9:8] */
+#define WM8962_DRC_NG_ENA 0x0080 /* DRC_NG_ENA */
+#define WM8962_DRC_NG_ENA_MASK 0x0080 /* DRC_NG_ENA */
+#define WM8962_DRC_NG_ENA_SHIFT 7 /* DRC_NG_ENA */
+#define WM8962_DRC_NG_ENA_WIDTH 1 /* DRC_NG_ENA */
+#define WM8962_DRC_SIG_DET_MODE 0x0040 /* DRC_SIG_DET_MODE */
+#define WM8962_DRC_SIG_DET_MODE_MASK 0x0040 /* DRC_SIG_DET_MODE */
+#define WM8962_DRC_SIG_DET_MODE_SHIFT 6 /* DRC_SIG_DET_MODE */
+#define WM8962_DRC_SIG_DET_MODE_WIDTH 1 /* DRC_SIG_DET_MODE */
+#define WM8962_DRC_SIG_DET 0x0020 /* DRC_SIG_DET */
+#define WM8962_DRC_SIG_DET_MASK 0x0020 /* DRC_SIG_DET */
+#define WM8962_DRC_SIG_DET_SHIFT 5 /* DRC_SIG_DET */
+#define WM8962_DRC_SIG_DET_WIDTH 1 /* DRC_SIG_DET */
+#define WM8962_DRC_KNEE2_OP_ENA 0x0010 /* DRC_KNEE2_OP_ENA */
+#define WM8962_DRC_KNEE2_OP_ENA_MASK 0x0010 /* DRC_KNEE2_OP_ENA */
+#define WM8962_DRC_KNEE2_OP_ENA_SHIFT 4 /* DRC_KNEE2_OP_ENA */
+#define WM8962_DRC_KNEE2_OP_ENA_WIDTH 1 /* DRC_KNEE2_OP_ENA */
+#define WM8962_DRC_QR 0x0008 /* DRC_QR */
+#define WM8962_DRC_QR_MASK 0x0008 /* DRC_QR */
+#define WM8962_DRC_QR_SHIFT 3 /* DRC_QR */
+#define WM8962_DRC_QR_WIDTH 1 /* DRC_QR */
+#define WM8962_DRC_ANTICLIP 0x0004 /* DRC_ANTICLIP */
+#define WM8962_DRC_ANTICLIP_MASK 0x0004 /* DRC_ANTICLIP */
+#define WM8962_DRC_ANTICLIP_SHIFT 2 /* DRC_ANTICLIP */
+#define WM8962_DRC_ANTICLIP_WIDTH 1 /* DRC_ANTICLIP */
+#define WM8962_DRC_MODE 0x0002 /* DRC_MODE */
+#define WM8962_DRC_MODE_MASK 0x0002 /* DRC_MODE */
+#define WM8962_DRC_MODE_SHIFT 1 /* DRC_MODE */
+#define WM8962_DRC_MODE_WIDTH 1 /* DRC_MODE */
+#define WM8962_DRC_ENA 0x0001 /* DRC_ENA */
+#define WM8962_DRC_ENA_MASK 0x0001 /* DRC_ENA */
+#define WM8962_DRC_ENA_SHIFT 0 /* DRC_ENA */
+#define WM8962_DRC_ENA_WIDTH 1 /* DRC_ENA */
+
+/*
+ * R277 (0x115) - DRC 2
+ */
+#define WM8962_DRC_ATK_MASK 0x1E00 /* DRC_ATK - [12:9] */
+#define WM8962_DRC_ATK_SHIFT 9 /* DRC_ATK - [12:9] */
+#define WM8962_DRC_ATK_WIDTH 4 /* DRC_ATK - [12:9] */
+#define WM8962_DRC_DCY_MASK 0x01E0 /* DRC_DCY - [8:5] */
+#define WM8962_DRC_DCY_SHIFT 5 /* DRC_DCY - [8:5] */
+#define WM8962_DRC_DCY_WIDTH 4 /* DRC_DCY - [8:5] */
+#define WM8962_DRC_MINGAIN_MASK 0x001C /* DRC_MINGAIN - [4:2] */
+#define WM8962_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [4:2] */
+#define WM8962_DRC_MINGAIN_WIDTH 3 /* DRC_MINGAIN - [4:2] */
+#define WM8962_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */
+#define WM8962_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */
+#define WM8962_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R278 (0x116) - DRC 3
+ */
+#define WM8962_DRC_NG_MINGAIN_MASK 0xF000 /* DRC_NG_MINGAIN - [15:12] */
+#define WM8962_DRC_NG_MINGAIN_SHIFT 12 /* DRC_NG_MINGAIN - [15:12] */
+#define WM8962_DRC_NG_MINGAIN_WIDTH 4 /* DRC_NG_MINGAIN - [15:12] */
+#define WM8962_DRC_QR_THR_MASK 0x0C00 /* DRC_QR_THR - [11:10] */
+#define WM8962_DRC_QR_THR_SHIFT 10 /* DRC_QR_THR - [11:10] */
+#define WM8962_DRC_QR_THR_WIDTH 2 /* DRC_QR_THR - [11:10] */
+#define WM8962_DRC_QR_DCY_MASK 0x0300 /* DRC_QR_DCY - [9:8] */
+#define WM8962_DRC_QR_DCY_SHIFT 8 /* DRC_QR_DCY - [9:8] */
+#define WM8962_DRC_QR_DCY_WIDTH 2 /* DRC_QR_DCY - [9:8] */
+#define WM8962_DRC_NG_EXP_MASK 0x00C0 /* DRC_NG_EXP - [7:6] */
+#define WM8962_DRC_NG_EXP_SHIFT 6 /* DRC_NG_EXP - [7:6] */
+#define WM8962_DRC_NG_EXP_WIDTH 2 /* DRC_NG_EXP - [7:6] */
+#define WM8962_DRC_HI_COMP_MASK 0x0038 /* DRC_HI_COMP - [5:3] */
+#define WM8962_DRC_HI_COMP_SHIFT 3 /* DRC_HI_COMP - [5:3] */
+#define WM8962_DRC_HI_COMP_WIDTH 3 /* DRC_HI_COMP - [5:3] */
+#define WM8962_DRC_LO_COMP_MASK 0x0007 /* DRC_LO_COMP - [2:0] */
+#define WM8962_DRC_LO_COMP_SHIFT 0 /* DRC_LO_COMP - [2:0] */
+#define WM8962_DRC_LO_COMP_WIDTH 3 /* DRC_LO_COMP - [2:0] */
+
+/*
+ * R279 (0x117) - DRC 4
+ */
+#define WM8962_DRC_KNEE_IP_MASK 0x07E0 /* DRC_KNEE_IP - [10:5] */
+#define WM8962_DRC_KNEE_IP_SHIFT 5 /* DRC_KNEE_IP - [10:5] */
+#define WM8962_DRC_KNEE_IP_WIDTH 6 /* DRC_KNEE_IP - [10:5] */
+#define WM8962_DRC_KNEE_OP_MASK 0x001F /* DRC_KNEE_OP - [4:0] */
+#define WM8962_DRC_KNEE_OP_SHIFT 0 /* DRC_KNEE_OP - [4:0] */
+#define WM8962_DRC_KNEE_OP_WIDTH 5 /* DRC_KNEE_OP - [4:0] */
+
+/*
+ * R280 (0x118) - DRC 5
+ */
+#define WM8962_DRC_KNEE2_IP_MASK 0x03E0 /* DRC_KNEE2_IP - [9:5] */
+#define WM8962_DRC_KNEE2_IP_SHIFT 5 /* DRC_KNEE2_IP - [9:5] */
+#define WM8962_DRC_KNEE2_IP_WIDTH 5 /* DRC_KNEE2_IP - [9:5] */
+#define WM8962_DRC_KNEE2_OP_MASK 0x001F /* DRC_KNEE2_OP - [4:0] */
+#define WM8962_DRC_KNEE2_OP_SHIFT 0 /* DRC_KNEE2_OP - [4:0] */
+#define WM8962_DRC_KNEE2_OP_WIDTH 5 /* DRC_KNEE2_OP - [4:0] */
+
+/*
+ * R285 (0x11D) - Tloopback
+ */
+#define WM8962_TLB_ENA 0x0002 /* TLB_ENA */
+#define WM8962_TLB_ENA_MASK 0x0002 /* TLB_ENA */
+#define WM8962_TLB_ENA_SHIFT 1 /* TLB_ENA */
+#define WM8962_TLB_ENA_WIDTH 1 /* TLB_ENA */
+#define WM8962_TLB_MODE 0x0001 /* TLB_MODE */
+#define WM8962_TLB_MODE_MASK 0x0001 /* TLB_MODE */
+#define WM8962_TLB_MODE_SHIFT 0 /* TLB_MODE */
+#define WM8962_TLB_MODE_WIDTH 1 /* TLB_MODE */
+
+/*
+ * R335 (0x14F) - EQ1
+ */
+#define WM8962_EQ_SHARED_COEFF 0x0004 /* EQ_SHARED_COEFF */
+#define WM8962_EQ_SHARED_COEFF_MASK 0x0004 /* EQ_SHARED_COEFF */
+#define WM8962_EQ_SHARED_COEFF_SHIFT 2 /* EQ_SHARED_COEFF */
+#define WM8962_EQ_SHARED_COEFF_WIDTH 1 /* EQ_SHARED_COEFF */
+#define WM8962_EQ_SHARED_COEFF_SEL 0x0002 /* EQ_SHARED_COEFF_SEL */
+#define WM8962_EQ_SHARED_COEFF_SEL_MASK 0x0002 /* EQ_SHARED_COEFF_SEL */
+#define WM8962_EQ_SHARED_COEFF_SEL_SHIFT 1 /* EQ_SHARED_COEFF_SEL */
+#define WM8962_EQ_SHARED_COEFF_SEL_WIDTH 1 /* EQ_SHARED_COEFF_SEL */
+#define WM8962_EQ_ENA 0x0001 /* EQ_ENA */
+#define WM8962_EQ_ENA_MASK 0x0001 /* EQ_ENA */
+#define WM8962_EQ_ENA_SHIFT 0 /* EQ_ENA */
+#define WM8962_EQ_ENA_WIDTH 1 /* EQ_ENA */
+
+/*
+ * R336 (0x150) - EQ2
+ */
+#define WM8962_EQL_B1_GAIN_MASK 0xF800 /* EQL_B1_GAIN - [15:11] */
+#define WM8962_EQL_B1_GAIN_SHIFT 11 /* EQL_B1_GAIN - [15:11] */
+#define WM8962_EQL_B1_GAIN_WIDTH 5 /* EQL_B1_GAIN - [15:11] */
+#define WM8962_EQL_B2_GAIN_MASK 0x07C0 /* EQL_B2_GAIN - [10:6] */
+#define WM8962_EQL_B2_GAIN_SHIFT 6 /* EQL_B2_GAIN - [10:6] */
+#define WM8962_EQL_B2_GAIN_WIDTH 5 /* EQL_B2_GAIN - [10:6] */
+#define WM8962_EQL_B3_GAIN_MASK 0x003E /* EQL_B3_GAIN - [5:1] */
+#define WM8962_EQL_B3_GAIN_SHIFT 1 /* EQL_B3_GAIN - [5:1] */
+#define WM8962_EQL_B3_GAIN_WIDTH 5 /* EQL_B3_GAIN - [5:1] */
+
+/*
+ * R337 (0x151) - EQ3
+ */
+#define WM8962_EQL_B4_GAIN_MASK 0xF800 /* EQL_B4_GAIN - [15:11] */
+#define WM8962_EQL_B4_GAIN_SHIFT 11 /* EQL_B4_GAIN - [15:11] */
+#define WM8962_EQL_B4_GAIN_WIDTH 5 /* EQL_B4_GAIN - [15:11] */
+#define WM8962_EQL_B5_GAIN_MASK 0x07C0 /* EQL_B5_GAIN - [10:6] */
+#define WM8962_EQL_B5_GAIN_SHIFT 6 /* EQL_B5_GAIN - [10:6] */
+#define WM8962_EQL_B5_GAIN_WIDTH 5 /* EQL_B5_GAIN - [10:6] */
+
+/*
+ * R338 (0x152) - EQ4
+ */
+#define WM8962_EQL_B1_A_MASK 0xFFFF /* EQL_B1_A - [15:0] */
+#define WM8962_EQL_B1_A_SHIFT 0 /* EQL_B1_A - [15:0] */
+#define WM8962_EQL_B1_A_WIDTH 16 /* EQL_B1_A - [15:0] */
+
+/*
+ * R339 (0x153) - EQ5
+ */
+#define WM8962_EQL_B1_B_MASK 0xFFFF /* EQL_B1_B - [15:0] */
+#define WM8962_EQL_B1_B_SHIFT 0 /* EQL_B1_B - [15:0] */
+#define WM8962_EQL_B1_B_WIDTH 16 /* EQL_B1_B - [15:0] */
+
+/*
+ * R340 (0x154) - EQ6
+ */
+#define WM8962_EQL_B1_PG_MASK 0xFFFF /* EQL_B1_PG - [15:0] */
+#define WM8962_EQL_B1_PG_SHIFT 0 /* EQL_B1_PG - [15:0] */
+#define WM8962_EQL_B1_PG_WIDTH 16 /* EQL_B1_PG - [15:0] */
+
+/*
+ * R341 (0x155) - EQ7
+ */
+#define WM8962_EQL_B2_A_MASK 0xFFFF /* EQL_B2_A - [15:0] */
+#define WM8962_EQL_B2_A_SHIFT 0 /* EQL_B2_A - [15:0] */
+#define WM8962_EQL_B2_A_WIDTH 16 /* EQL_B2_A - [15:0] */
+
+/*
+ * R342 (0x156) - EQ8
+ */
+#define WM8962_EQL_B2_B_MASK 0xFFFF /* EQL_B2_B - [15:0] */
+#define WM8962_EQL_B2_B_SHIFT 0 /* EQL_B2_B - [15:0] */
+#define WM8962_EQL_B2_B_WIDTH 16 /* EQL_B2_B - [15:0] */
+
+/*
+ * R343 (0x157) - EQ9
+ */
+#define WM8962_EQL_B2_C_MASK 0xFFFF /* EQL_B2_C - [15:0] */
+#define WM8962_EQL_B2_C_SHIFT 0 /* EQL_B2_C - [15:0] */
+#define WM8962_EQL_B2_C_WIDTH 16 /* EQL_B2_C - [15:0] */
+
+/*
+ * R344 (0x158) - EQ10
+ */
+#define WM8962_EQL_B2_PG_MASK 0xFFFF /* EQL_B2_PG - [15:0] */
+#define WM8962_EQL_B2_PG_SHIFT 0 /* EQL_B2_PG - [15:0] */
+#define WM8962_EQL_B2_PG_WIDTH 16 /* EQL_B2_PG - [15:0] */
+
+/*
+ * R345 (0x159) - EQ11
+ */
+#define WM8962_EQL_B3_A_MASK 0xFFFF /* EQL_B3_A - [15:0] */
+#define WM8962_EQL_B3_A_SHIFT 0 /* EQL_B3_A - [15:0] */
+#define WM8962_EQL_B3_A_WIDTH 16 /* EQL_B3_A - [15:0] */
+
+/*
+ * R346 (0x15A) - EQ12
+ */
+#define WM8962_EQL_B3_B_MASK 0xFFFF /* EQL_B3_B - [15:0] */
+#define WM8962_EQL_B3_B_SHIFT 0 /* EQL_B3_B - [15:0] */
+#define WM8962_EQL_B3_B_WIDTH 16 /* EQL_B3_B - [15:0] */
+
+/*
+ * R347 (0x15B) - EQ13
+ */
+#define WM8962_EQL_B3_C_MASK 0xFFFF /* EQL_B3_C - [15:0] */
+#define WM8962_EQL_B3_C_SHIFT 0 /* EQL_B3_C - [15:0] */
+#define WM8962_EQL_B3_C_WIDTH 16 /* EQL_B3_C - [15:0] */
+
+/*
+ * R348 (0x15C) - EQ14
+ */
+#define WM8962_EQL_B3_PG_MASK 0xFFFF /* EQL_B3_PG - [15:0] */
+#define WM8962_EQL_B3_PG_SHIFT 0 /* EQL_B3_PG - [15:0] */
+#define WM8962_EQL_B3_PG_WIDTH 16 /* EQL_B3_PG - [15:0] */
+
+/*
+ * R349 (0x15D) - EQ15
+ */
+#define WM8962_EQL_B4_A_MASK 0xFFFF /* EQL_B4_A - [15:0] */
+#define WM8962_EQL_B4_A_SHIFT 0 /* EQL_B4_A - [15:0] */
+#define WM8962_EQL_B4_A_WIDTH 16 /* EQL_B4_A - [15:0] */
+
+/*
+ * R350 (0x15E) - EQ16
+ */
+#define WM8962_EQL_B4_B_MASK 0xFFFF /* EQL_B4_B - [15:0] */
+#define WM8962_EQL_B4_B_SHIFT 0 /* EQL_B4_B - [15:0] */
+#define WM8962_EQL_B4_B_WIDTH 16 /* EQL_B4_B - [15:0] */
+
+/*
+ * R351 (0x15F) - EQ17
+ */
+#define WM8962_EQL_B4_C_MASK 0xFFFF /* EQL_B4_C - [15:0] */
+#define WM8962_EQL_B4_C_SHIFT 0 /* EQL_B4_C - [15:0] */
+#define WM8962_EQL_B4_C_WIDTH 16 /* EQL_B4_C - [15:0] */
+
+/*
+ * R352 (0x160) - EQ18
+ */
+#define WM8962_EQL_B4_PG_MASK 0xFFFF /* EQL_B4_PG - [15:0] */
+#define WM8962_EQL_B4_PG_SHIFT 0 /* EQL_B4_PG - [15:0] */
+#define WM8962_EQL_B4_PG_WIDTH 16 /* EQL_B4_PG - [15:0] */
+
+/*
+ * R353 (0x161) - EQ19
+ */
+#define WM8962_EQL_B5_A_MASK 0xFFFF /* EQL_B5_A - [15:0] */
+#define WM8962_EQL_B5_A_SHIFT 0 /* EQL_B5_A - [15:0] */
+#define WM8962_EQL_B5_A_WIDTH 16 /* EQL_B5_A - [15:0] */
+
+/*
+ * R354 (0x162) - EQ20
+ */
+#define WM8962_EQL_B5_B_MASK 0xFFFF /* EQL_B5_B - [15:0] */
+#define WM8962_EQL_B5_B_SHIFT 0 /* EQL_B5_B - [15:0] */
+#define WM8962_EQL_B5_B_WIDTH 16 /* EQL_B5_B - [15:0] */
+
+/*
+ * R355 (0x163) - EQ21
+ */
+#define WM8962_EQL_B5_PG_MASK 0xFFFF /* EQL_B5_PG - [15:0] */
+#define WM8962_EQL_B5_PG_SHIFT 0 /* EQL_B5_PG - [15:0] */
+#define WM8962_EQL_B5_PG_WIDTH 16 /* EQL_B5_PG - [15:0] */
+
+/*
+ * R356 (0x164) - EQ22
+ */
+#define WM8962_EQR_B1_GAIN_MASK 0xF800 /* EQR_B1_GAIN - [15:11] */
+#define WM8962_EQR_B1_GAIN_SHIFT 11 /* EQR_B1_GAIN - [15:11] */
+#define WM8962_EQR_B1_GAIN_WIDTH 5 /* EQR_B1_GAIN - [15:11] */
+#define WM8962_EQR_B2_GAIN_MASK 0x07C0 /* EQR_B2_GAIN - [10:6] */
+#define WM8962_EQR_B2_GAIN_SHIFT 6 /* EQR_B2_GAIN - [10:6] */
+#define WM8962_EQR_B2_GAIN_WIDTH 5 /* EQR_B2_GAIN - [10:6] */
+#define WM8962_EQR_B3_GAIN_MASK 0x003E /* EQR_B3_GAIN - [5:1] */
+#define WM8962_EQR_B3_GAIN_SHIFT 1 /* EQR_B3_GAIN - [5:1] */
+#define WM8962_EQR_B3_GAIN_WIDTH 5 /* EQR_B3_GAIN - [5:1] */
+
+/*
+ * R357 (0x165) - EQ23
+ */
+#define WM8962_EQR_B4_GAIN_MASK 0xF800 /* EQR_B4_GAIN - [15:11] */
+#define WM8962_EQR_B4_GAIN_SHIFT 11 /* EQR_B4_GAIN - [15:11] */
+#define WM8962_EQR_B4_GAIN_WIDTH 5 /* EQR_B4_GAIN - [15:11] */
+#define WM8962_EQR_B5_GAIN_MASK 0x07C0 /* EQR_B5_GAIN - [10:6] */
+#define WM8962_EQR_B5_GAIN_SHIFT 6 /* EQR_B5_GAIN - [10:6] */
+#define WM8962_EQR_B5_GAIN_WIDTH 5 /* EQR_B5_GAIN - [10:6] */
+
+/*
+ * R358 (0x166) - EQ24
+ */
+#define WM8962_EQR_B1_A_MASK 0xFFFF /* EQR_B1_A - [15:0] */
+#define WM8962_EQR_B1_A_SHIFT 0 /* EQR_B1_A - [15:0] */
+#define WM8962_EQR_B1_A_WIDTH 16 /* EQR_B1_A - [15:0] */
+
+/*
+ * R359 (0x167) - EQ25
+ */
+#define WM8962_EQR_B1_B_MASK 0xFFFF /* EQR_B1_B - [15:0] */
+#define WM8962_EQR_B1_B_SHIFT 0 /* EQR_B1_B - [15:0] */
+#define WM8962_EQR_B1_B_WIDTH 16 /* EQR_B1_B - [15:0] */
+
+/*
+ * R360 (0x168) - EQ26
+ */
+#define WM8962_EQR_B1_PG_MASK 0xFFFF /* EQR_B1_PG - [15:0] */
+#define WM8962_EQR_B1_PG_SHIFT 0 /* EQR_B1_PG - [15:0] */
+#define WM8962_EQR_B1_PG_WIDTH 16 /* EQR_B1_PG - [15:0] */
+
+/*
+ * R361 (0x169) - EQ27
+ */
+#define WM8962_EQR_B2_A_MASK 0xFFFF /* EQR_B2_A - [15:0] */
+#define WM8962_EQR_B2_A_SHIFT 0 /* EQR_B2_A - [15:0] */
+#define WM8962_EQR_B2_A_WIDTH 16 /* EQR_B2_A - [15:0] */
+
+/*
+ * R362 (0x16A) - EQ28
+ */
+#define WM8962_EQR_B2_B_MASK 0xFFFF /* EQR_B2_B - [15:0] */
+#define WM8962_EQR_B2_B_SHIFT 0 /* EQR_B2_B - [15:0] */
+#define WM8962_EQR_B2_B_WIDTH 16 /* EQR_B2_B - [15:0] */
+
+/*
+ * R363 (0x16B) - EQ29
+ */
+#define WM8962_EQR_B2_C_MASK 0xFFFF /* EQR_B2_C - [15:0] */
+#define WM8962_EQR_B2_C_SHIFT 0 /* EQR_B2_C - [15:0] */
+#define WM8962_EQR_B2_C_WIDTH 16 /* EQR_B2_C - [15:0] */
+
+/*
+ * R364 (0x16C) - EQ30
+ */
+#define WM8962_EQR_B2_PG_MASK 0xFFFF /* EQR_B2_PG - [15:0] */
+#define WM8962_EQR_B2_PG_SHIFT 0 /* EQR_B2_PG - [15:0] */
+#define WM8962_EQR_B2_PG_WIDTH 16 /* EQR_B2_PG - [15:0] */
+
+/*
+ * R365 (0x16D) - EQ31
+ */
+#define WM8962_EQR_B3_A_MASK 0xFFFF /* EQR_B3_A - [15:0] */
+#define WM8962_EQR_B3_A_SHIFT 0 /* EQR_B3_A - [15:0] */
+#define WM8962_EQR_B3_A_WIDTH 16 /* EQR_B3_A - [15:0] */
+
+/*
+ * R366 (0x16E) - EQ32
+ */
+#define WM8962_EQR_B3_B_MASK 0xFFFF /* EQR_B3_B - [15:0] */
+#define WM8962_EQR_B3_B_SHIFT 0 /* EQR_B3_B - [15:0] */
+#define WM8962_EQR_B3_B_WIDTH 16 /* EQR_B3_B - [15:0] */
+
+/*
+ * R367 (0x16F) - EQ33
+ */
+#define WM8962_EQR_B3_C_MASK 0xFFFF /* EQR_B3_C - [15:0] */
+#define WM8962_EQR_B3_C_SHIFT 0 /* EQR_B3_C - [15:0] */
+#define WM8962_EQR_B3_C_WIDTH 16 /* EQR_B3_C - [15:0] */
+
+/*
+ * R368 (0x170) - EQ34
+ */
+#define WM8962_EQR_B3_PG_MASK 0xFFFF /* EQR_B3_PG - [15:0] */
+#define WM8962_EQR_B3_PG_SHIFT 0 /* EQR_B3_PG - [15:0] */
+#define WM8962_EQR_B3_PG_WIDTH 16 /* EQR_B3_PG - [15:0] */
+
+/*
+ * R369 (0x171) - EQ35
+ */
+#define WM8962_EQR_B4_A_MASK 0xFFFF /* EQR_B4_A - [15:0] */
+#define WM8962_EQR_B4_A_SHIFT 0 /* EQR_B4_A - [15:0] */
+#define WM8962_EQR_B4_A_WIDTH 16 /* EQR_B4_A - [15:0] */
+
+/*
+ * R370 (0x172) - EQ36
+ */
+#define WM8962_EQR_B4_B_MASK 0xFFFF /* EQR_B4_B - [15:0] */
+#define WM8962_EQR_B4_B_SHIFT 0 /* EQR_B4_B - [15:0] */
+#define WM8962_EQR_B4_B_WIDTH 16 /* EQR_B4_B - [15:0] */
+
+/*
+ * R371 (0x173) - EQ37
+ */
+#define WM8962_EQR_B4_C_MASK 0xFFFF /* EQR_B4_C - [15:0] */
+#define WM8962_EQR_B4_C_SHIFT 0 /* EQR_B4_C - [15:0] */
+#define WM8962_EQR_B4_C_WIDTH 16 /* EQR_B4_C - [15:0] */
+
+/*
+ * R372 (0x174) - EQ38
+ */
+#define WM8962_EQR_B4_PG_MASK 0xFFFF /* EQR_B4_PG - [15:0] */
+#define WM8962_EQR_B4_PG_SHIFT 0 /* EQR_B4_PG - [15:0] */
+#define WM8962_EQR_B4_PG_WIDTH 16 /* EQR_B4_PG - [15:0] */
+
+/*
+ * R373 (0x175) - EQ39
+ */
+#define WM8962_EQR_B5_A_MASK 0xFFFF /* EQR_B5_A - [15:0] */
+#define WM8962_EQR_B5_A_SHIFT 0 /* EQR_B5_A - [15:0] */
+#define WM8962_EQR_B5_A_WIDTH 16 /* EQR_B5_A - [15:0] */
+
+/*
+ * R374 (0x176) - EQ40
+ */
+#define WM8962_EQR_B5_B_MASK 0xFFFF /* EQR_B5_B - [15:0] */
+#define WM8962_EQR_B5_B_SHIFT 0 /* EQR_B5_B - [15:0] */
+#define WM8962_EQR_B5_B_WIDTH 16 /* EQR_B5_B - [15:0] */
+
+/*
+ * R375 (0x177) - EQ41
+ */
+#define WM8962_EQR_B5_PG_MASK 0xFFFF /* EQR_B5_PG - [15:0] */
+#define WM8962_EQR_B5_PG_SHIFT 0 /* EQR_B5_PG - [15:0] */
+#define WM8962_EQR_B5_PG_WIDTH 16 /* EQR_B5_PG - [15:0] */
+
+/*
+ * R513 (0x201) - GPIO 2
+ */
+#define WM8962_GP2_POL 0x0400 /* GP2_POL */
+#define WM8962_GP2_POL_MASK 0x0400 /* GP2_POL */
+#define WM8962_GP2_POL_SHIFT 10 /* GP2_POL */
+#define WM8962_GP2_POL_WIDTH 1 /* GP2_POL */
+#define WM8962_GP2_LVL 0x0040 /* GP2_LVL */
+#define WM8962_GP2_LVL_MASK 0x0040 /* GP2_LVL */
+#define WM8962_GP2_LVL_SHIFT 6 /* GP2_LVL */
+#define WM8962_GP2_LVL_WIDTH 1 /* GP2_LVL */
+#define WM8962_GP2_FN_MASK 0x001F /* GP2_FN - [4:0] */
+#define WM8962_GP2_FN_SHIFT 0 /* GP2_FN - [4:0] */
+#define WM8962_GP2_FN_WIDTH 5 /* GP2_FN - [4:0] */
+
+/*
+ * R514 (0x202) - GPIO 3
+ */
+#define WM8962_GP3_POL 0x0400 /* GP3_POL */
+#define WM8962_GP3_POL_MASK 0x0400 /* GP3_POL */
+#define WM8962_GP3_POL_SHIFT 10 /* GP3_POL */
+#define WM8962_GP3_POL_WIDTH 1 /* GP3_POL */
+#define WM8962_GP3_LVL 0x0040 /* GP3_LVL */
+#define WM8962_GP3_LVL_MASK 0x0040 /* GP3_LVL */
+#define WM8962_GP3_LVL_SHIFT 6 /* GP3_LVL */
+#define WM8962_GP3_LVL_WIDTH 1 /* GP3_LVL */
+#define WM8962_GP3_FN_MASK 0x001F /* GP3_FN - [4:0] */
+#define WM8962_GP3_FN_SHIFT 0 /* GP3_FN - [4:0] */
+#define WM8962_GP3_FN_WIDTH 5 /* GP3_FN - [4:0] */
+
+/*
+ * R516 (0x204) - GPIO 5
+ */
+#define WM8962_GP5_DIR 0x8000 /* GP5_DIR */
+#define WM8962_GP5_DIR_MASK 0x8000 /* GP5_DIR */
+#define WM8962_GP5_DIR_SHIFT 15 /* GP5_DIR */
+#define WM8962_GP5_DIR_WIDTH 1 /* GP5_DIR */
+#define WM8962_GP5_PU 0x4000 /* GP5_PU */
+#define WM8962_GP5_PU_MASK 0x4000 /* GP5_PU */
+#define WM8962_GP5_PU_SHIFT 14 /* GP5_PU */
+#define WM8962_GP5_PU_WIDTH 1 /* GP5_PU */
+#define WM8962_GP5_PD 0x2000 /* GP5_PD */
+#define WM8962_GP5_PD_MASK 0x2000 /* GP5_PD */
+#define WM8962_GP5_PD_SHIFT 13 /* GP5_PD */
+#define WM8962_GP5_PD_WIDTH 1 /* GP5_PD */
+#define WM8962_GP5_POL 0x0400 /* GP5_POL */
+#define WM8962_GP5_POL_MASK 0x0400 /* GP5_POL */
+#define WM8962_GP5_POL_SHIFT 10 /* GP5_POL */
+#define WM8962_GP5_POL_WIDTH 1 /* GP5_POL */
+#define WM8962_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */
+#define WM8962_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */
+#define WM8962_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */
+#define WM8962_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */
+#define WM8962_GP5_DB 0x0100 /* GP5_DB */
+#define WM8962_GP5_DB_MASK 0x0100 /* GP5_DB */
+#define WM8962_GP5_DB_SHIFT 8 /* GP5_DB */
+#define WM8962_GP5_DB_WIDTH 1 /* GP5_DB */
+#define WM8962_GP5_LVL 0x0040 /* GP5_LVL */
+#define WM8962_GP5_LVL_MASK 0x0040 /* GP5_LVL */
+#define WM8962_GP5_LVL_SHIFT 6 /* GP5_LVL */
+#define WM8962_GP5_LVL_WIDTH 1 /* GP5_LVL */
+#define WM8962_GP5_FN_MASK 0x001F /* GP5_FN - [4:0] */
+#define WM8962_GP5_FN_SHIFT 0 /* GP5_FN - [4:0] */
+#define WM8962_GP5_FN_WIDTH 5 /* GP5_FN - [4:0] */
+
+/*
+ * R517 (0x205) - GPIO 6
+ */
+#define WM8962_GP6_DIR 0x8000 /* GP6_DIR */
+#define WM8962_GP6_DIR_MASK 0x8000 /* GP6_DIR */
+#define WM8962_GP6_DIR_SHIFT 15 /* GP6_DIR */
+#define WM8962_GP6_DIR_WIDTH 1 /* GP6_DIR */
+#define WM8962_GP6_PU 0x4000 /* GP6_PU */
+#define WM8962_GP6_PU_MASK 0x4000 /* GP6_PU */
+#define WM8962_GP6_PU_SHIFT 14 /* GP6_PU */
+#define WM8962_GP6_PU_WIDTH 1 /* GP6_PU */
+#define WM8962_GP6_PD 0x2000 /* GP6_PD */
+#define WM8962_GP6_PD_MASK 0x2000 /* GP6_PD */
+#define WM8962_GP6_PD_SHIFT 13 /* GP6_PD */
+#define WM8962_GP6_PD_WIDTH 1 /* GP6_PD */
+#define WM8962_GP6_POL 0x0400 /* GP6_POL */
+#define WM8962_GP6_POL_MASK 0x0400 /* GP6_POL */
+#define WM8962_GP6_POL_SHIFT 10 /* GP6_POL */
+#define WM8962_GP6_POL_WIDTH 1 /* GP6_POL */
+#define WM8962_GP6_OP_CFG 0x0200 /* GP6_OP_CFG */
+#define WM8962_GP6_OP_CFG_MASK 0x0200 /* GP6_OP_CFG */
+#define WM8962_GP6_OP_CFG_SHIFT 9 /* GP6_OP_CFG */
+#define WM8962_GP6_OP_CFG_WIDTH 1 /* GP6_OP_CFG */
+#define WM8962_GP6_DB 0x0100 /* GP6_DB */
+#define WM8962_GP6_DB_MASK 0x0100 /* GP6_DB */
+#define WM8962_GP6_DB_SHIFT 8 /* GP6_DB */
+#define WM8962_GP6_DB_WIDTH 1 /* GP6_DB */
+#define WM8962_GP6_LVL 0x0040 /* GP6_LVL */
+#define WM8962_GP6_LVL_MASK 0x0040 /* GP6_LVL */
+#define WM8962_GP6_LVL_SHIFT 6 /* GP6_LVL */
+#define WM8962_GP6_LVL_WIDTH 1 /* GP6_LVL */
+#define WM8962_GP6_FN_MASK 0x001F /* GP6_FN - [4:0] */
+#define WM8962_GP6_FN_SHIFT 0 /* GP6_FN - [4:0] */
+#define WM8962_GP6_FN_WIDTH 5 /* GP6_FN - [4:0] */
+
+/*
+ * R560 (0x230) - Interrupt Status 1
+ */
+#define WM8962_GP6_EINT 0x0020 /* GP6_EINT */
+#define WM8962_GP6_EINT_MASK 0x0020 /* GP6_EINT */
+#define WM8962_GP6_EINT_SHIFT 5 /* GP6_EINT */
+#define WM8962_GP6_EINT_WIDTH 1 /* GP6_EINT */
+#define WM8962_GP5_EINT 0x0010 /* GP5_EINT */
+#define WM8962_GP5_EINT_MASK 0x0010 /* GP5_EINT */
+#define WM8962_GP5_EINT_SHIFT 4 /* GP5_EINT */
+#define WM8962_GP5_EINT_WIDTH 1 /* GP5_EINT */
+
+/*
+ * R561 (0x231) - Interrupt Status 2
+ */
+#define WM8962_MICSCD_EINT 0x8000 /* MICSCD_EINT */
+#define WM8962_MICSCD_EINT_MASK 0x8000 /* MICSCD_EINT */
+#define WM8962_MICSCD_EINT_SHIFT 15 /* MICSCD_EINT */
+#define WM8962_MICSCD_EINT_WIDTH 1 /* MICSCD_EINT */
+#define WM8962_MICD_EINT 0x4000 /* MICD_EINT */
+#define WM8962_MICD_EINT_MASK 0x4000 /* MICD_EINT */
+#define WM8962_MICD_EINT_SHIFT 14 /* MICD_EINT */
+#define WM8962_MICD_EINT_WIDTH 1 /* MICD_EINT */
+#define WM8962_FIFOS_ERR_EINT 0x2000 /* FIFOS_ERR_EINT */
+#define WM8962_FIFOS_ERR_EINT_MASK 0x2000 /* FIFOS_ERR_EINT */
+#define WM8962_FIFOS_ERR_EINT_SHIFT 13 /* FIFOS_ERR_EINT */
+#define WM8962_FIFOS_ERR_EINT_WIDTH 1 /* FIFOS_ERR_EINT */
+#define WM8962_ALC_LOCK_EINT 0x1000 /* ALC_LOCK_EINT */
+#define WM8962_ALC_LOCK_EINT_MASK 0x1000 /* ALC_LOCK_EINT */
+#define WM8962_ALC_LOCK_EINT_SHIFT 12 /* ALC_LOCK_EINT */
+#define WM8962_ALC_LOCK_EINT_WIDTH 1 /* ALC_LOCK_EINT */
+#define WM8962_ALC_THRESH_EINT 0x0800 /* ALC_THRESH_EINT */
+#define WM8962_ALC_THRESH_EINT_MASK 0x0800 /* ALC_THRESH_EINT */
+#define WM8962_ALC_THRESH_EINT_SHIFT 11 /* ALC_THRESH_EINT */
+#define WM8962_ALC_THRESH_EINT_WIDTH 1 /* ALC_THRESH_EINT */
+#define WM8962_ALC_SAT_EINT 0x0400 /* ALC_SAT_EINT */
+#define WM8962_ALC_SAT_EINT_MASK 0x0400 /* ALC_SAT_EINT */
+#define WM8962_ALC_SAT_EINT_SHIFT 10 /* ALC_SAT_EINT */
+#define WM8962_ALC_SAT_EINT_WIDTH 1 /* ALC_SAT_EINT */
+#define WM8962_ALC_PKOVR_EINT 0x0200 /* ALC_PKOVR_EINT */
+#define WM8962_ALC_PKOVR_EINT_MASK 0x0200 /* ALC_PKOVR_EINT */
+#define WM8962_ALC_PKOVR_EINT_SHIFT 9 /* ALC_PKOVR_EINT */
+#define WM8962_ALC_PKOVR_EINT_WIDTH 1 /* ALC_PKOVR_EINT */
+#define WM8962_ALC_NGATE_EINT 0x0100 /* ALC_NGATE_EINT */
+#define WM8962_ALC_NGATE_EINT_MASK 0x0100 /* ALC_NGATE_EINT */
+#define WM8962_ALC_NGATE_EINT_SHIFT 8 /* ALC_NGATE_EINT */
+#define WM8962_ALC_NGATE_EINT_WIDTH 1 /* ALC_NGATE_EINT */
+#define WM8962_WSEQ_DONE_EINT 0x0080 /* WSEQ_DONE_EINT */
+#define WM8962_WSEQ_DONE_EINT_MASK 0x0080 /* WSEQ_DONE_EINT */
+#define WM8962_WSEQ_DONE_EINT_SHIFT 7 /* WSEQ_DONE_EINT */
+#define WM8962_WSEQ_DONE_EINT_WIDTH 1 /* WSEQ_DONE_EINT */
+#define WM8962_DRC_ACTDET_EINT 0x0040 /* DRC_ACTDET_EINT */
+#define WM8962_DRC_ACTDET_EINT_MASK 0x0040 /* DRC_ACTDET_EINT */
+#define WM8962_DRC_ACTDET_EINT_SHIFT 6 /* DRC_ACTDET_EINT */
+#define WM8962_DRC_ACTDET_EINT_WIDTH 1 /* DRC_ACTDET_EINT */
+#define WM8962_FLL_LOCK_EINT 0x0020 /* FLL_LOCK_EINT */
+#define WM8962_FLL_LOCK_EINT_MASK 0x0020 /* FLL_LOCK_EINT */
+#define WM8962_FLL_LOCK_EINT_SHIFT 5 /* FLL_LOCK_EINT */
+#define WM8962_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */
+#define WM8962_PLL3_LOCK_EINT 0x0008 /* PLL3_LOCK_EINT */
+#define WM8962_PLL3_LOCK_EINT_MASK 0x0008 /* PLL3_LOCK_EINT */
+#define WM8962_PLL3_LOCK_EINT_SHIFT 3 /* PLL3_LOCK_EINT */
+#define WM8962_PLL3_LOCK_EINT_WIDTH 1 /* PLL3_LOCK_EINT */
+#define WM8962_PLL2_LOCK_EINT 0x0004 /* PLL2_LOCK_EINT */
+#define WM8962_PLL2_LOCK_EINT_MASK 0x0004 /* PLL2_LOCK_EINT */
+#define WM8962_PLL2_LOCK_EINT_SHIFT 2 /* PLL2_LOCK_EINT */
+#define WM8962_PLL2_LOCK_EINT_WIDTH 1 /* PLL2_LOCK_EINT */
+#define WM8962_TEMP_SHUT_EINT 0x0001 /* TEMP_SHUT_EINT */
+#define WM8962_TEMP_SHUT_EINT_MASK 0x0001 /* TEMP_SHUT_EINT */
+#define WM8962_TEMP_SHUT_EINT_SHIFT 0 /* TEMP_SHUT_EINT */
+#define WM8962_TEMP_SHUT_EINT_WIDTH 1 /* TEMP_SHUT_EINT */
+
+/*
+ * R568 (0x238) - Interrupt Status 1 Mask
+ */
+#define WM8962_IM_GP6_EINT 0x0020 /* IM_GP6_EINT */
+#define WM8962_IM_GP6_EINT_MASK 0x0020 /* IM_GP6_EINT */
+#define WM8962_IM_GP6_EINT_SHIFT 5 /* IM_GP6_EINT */
+#define WM8962_IM_GP6_EINT_WIDTH 1 /* IM_GP6_EINT */
+#define WM8962_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */
+#define WM8962_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */
+#define WM8962_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */
+#define WM8962_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */
+
+/*
+ * R569 (0x239) - Interrupt Status 2 Mask
+ */
+#define WM8962_IM_MICSCD_EINT 0x8000 /* IM_MICSCD_EINT */
+#define WM8962_IM_MICSCD_EINT_MASK 0x8000 /* IM_MICSCD_EINT */
+#define WM8962_IM_MICSCD_EINT_SHIFT 15 /* IM_MICSCD_EINT */
+#define WM8962_IM_MICSCD_EINT_WIDTH 1 /* IM_MICSCD_EINT */
+#define WM8962_IM_MICD_EINT 0x4000 /* IM_MICD_EINT */
+#define WM8962_IM_MICD_EINT_MASK 0x4000 /* IM_MICD_EINT */
+#define WM8962_IM_MICD_EINT_SHIFT 14 /* IM_MICD_EINT */
+#define WM8962_IM_MICD_EINT_WIDTH 1 /* IM_MICD_EINT */
+#define WM8962_IM_FIFOS_ERR_EINT 0x2000 /* IM_FIFOS_ERR_EINT */
+#define WM8962_IM_FIFOS_ERR_EINT_MASK 0x2000 /* IM_FIFOS_ERR_EINT */
+#define WM8962_IM_FIFOS_ERR_EINT_SHIFT 13 /* IM_FIFOS_ERR_EINT */
+#define WM8962_IM_FIFOS_ERR_EINT_WIDTH 1 /* IM_FIFOS_ERR_EINT */
+#define WM8962_IM_ALC_LOCK_EINT 0x1000 /* IM_ALC_LOCK_EINT */
+#define WM8962_IM_ALC_LOCK_EINT_MASK 0x1000 /* IM_ALC_LOCK_EINT */
+#define WM8962_IM_ALC_LOCK_EINT_SHIFT 12 /* IM_ALC_LOCK_EINT */
+#define WM8962_IM_ALC_LOCK_EINT_WIDTH 1 /* IM_ALC_LOCK_EINT */
+#define WM8962_IM_ALC_THRESH_EINT 0x0800 /* IM_ALC_THRESH_EINT */
+#define WM8962_IM_ALC_THRESH_EINT_MASK 0x0800 /* IM_ALC_THRESH_EINT */
+#define WM8962_IM_ALC_THRESH_EINT_SHIFT 11 /* IM_ALC_THRESH_EINT */
+#define WM8962_IM_ALC_THRESH_EINT_WIDTH 1 /* IM_ALC_THRESH_EINT */
+#define WM8962_IM_ALC_SAT_EINT 0x0400 /* IM_ALC_SAT_EINT */
+#define WM8962_IM_ALC_SAT_EINT_MASK 0x0400 /* IM_ALC_SAT_EINT */
+#define WM8962_IM_ALC_SAT_EINT_SHIFT 10 /* IM_ALC_SAT_EINT */
+#define WM8962_IM_ALC_SAT_EINT_WIDTH 1 /* IM_ALC_SAT_EINT */
+#define WM8962_IM_ALC_PKOVR_EINT 0x0200 /* IM_ALC_PKOVR_EINT */
+#define WM8962_IM_ALC_PKOVR_EINT_MASK 0x0200 /* IM_ALC_PKOVR_EINT */
+#define WM8962_IM_ALC_PKOVR_EINT_SHIFT 9 /* IM_ALC_PKOVR_EINT */
+#define WM8962_IM_ALC_PKOVR_EINT_WIDTH 1 /* IM_ALC_PKOVR_EINT */
+#define WM8962_IM_ALC_NGATE_EINT 0x0100 /* IM_ALC_NGATE_EINT */
+#define WM8962_IM_ALC_NGATE_EINT_MASK 0x0100 /* IM_ALC_NGATE_EINT */
+#define WM8962_IM_ALC_NGATE_EINT_SHIFT 8 /* IM_ALC_NGATE_EINT */
+#define WM8962_IM_ALC_NGATE_EINT_WIDTH 1 /* IM_ALC_NGATE_EINT */
+#define WM8962_IM_WSEQ_DONE_EINT 0x0080 /* IM_WSEQ_DONE_EINT */
+#define WM8962_IM_WSEQ_DONE_EINT_MASK 0x0080 /* IM_WSEQ_DONE_EINT */
+#define WM8962_IM_WSEQ_DONE_EINT_SHIFT 7 /* IM_WSEQ_DONE_EINT */
+#define WM8962_IM_WSEQ_DONE_EINT_WIDTH 1 /* IM_WSEQ_DONE_EINT */
+#define WM8962_IM_DRC_ACTDET_EINT 0x0040 /* IM_DRC_ACTDET_EINT */
+#define WM8962_IM_DRC_ACTDET_EINT_MASK 0x0040 /* IM_DRC_ACTDET_EINT */
+#define WM8962_IM_DRC_ACTDET_EINT_SHIFT 6 /* IM_DRC_ACTDET_EINT */
+#define WM8962_IM_DRC_ACTDET_EINT_WIDTH 1 /* IM_DRC_ACTDET_EINT */
+#define WM8962_IM_FLL_LOCK_EINT 0x0020 /* IM_FLL_LOCK_EINT */
+#define WM8962_IM_FLL_LOCK_EINT_MASK 0x0020 /* IM_FLL_LOCK_EINT */
+#define WM8962_IM_FLL_LOCK_EINT_SHIFT 5 /* IM_FLL_LOCK_EINT */
+#define WM8962_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */
+#define WM8962_IM_PLL3_LOCK_EINT 0x0008 /* IM_PLL3_LOCK_EINT */
+#define WM8962_IM_PLL3_LOCK_EINT_MASK 0x0008 /* IM_PLL3_LOCK_EINT */
+#define WM8962_IM_PLL3_LOCK_EINT_SHIFT 3 /* IM_PLL3_LOCK_EINT */
+#define WM8962_IM_PLL3_LOCK_EINT_WIDTH 1 /* IM_PLL3_LOCK_EINT */
+#define WM8962_IM_PLL2_LOCK_EINT 0x0004 /* IM_PLL2_LOCK_EINT */
+#define WM8962_IM_PLL2_LOCK_EINT_MASK 0x0004 /* IM_PLL2_LOCK_EINT */
+#define WM8962_IM_PLL2_LOCK_EINT_SHIFT 2 /* IM_PLL2_LOCK_EINT */
+#define WM8962_IM_PLL2_LOCK_EINT_WIDTH 1 /* IM_PLL2_LOCK_EINT */
+#define WM8962_IM_TEMP_SHUT_EINT 0x0001 /* IM_TEMP_SHUT_EINT */
+#define WM8962_IM_TEMP_SHUT_EINT_MASK 0x0001 /* IM_TEMP_SHUT_EINT */
+#define WM8962_IM_TEMP_SHUT_EINT_SHIFT 0 /* IM_TEMP_SHUT_EINT */
+#define WM8962_IM_TEMP_SHUT_EINT_WIDTH 1 /* IM_TEMP_SHUT_EINT */
+
+/*
+ * R576 (0x240) - Interrupt Control
+ */
+#define WM8962_IRQ_POL 0x0001 /* IRQ_POL */
+#define WM8962_IRQ_POL_MASK 0x0001 /* IRQ_POL */
+#define WM8962_IRQ_POL_SHIFT 0 /* IRQ_POL */
+#define WM8962_IRQ_POL_WIDTH 1 /* IRQ_POL */
+
+/*
+ * R584 (0x248) - IRQ Debounce
+ */
+#define WM8962_FLL_LOCK_DB 0x0020 /* FLL_LOCK_DB */
+#define WM8962_FLL_LOCK_DB_MASK 0x0020 /* FLL_LOCK_DB */
+#define WM8962_FLL_LOCK_DB_SHIFT 5 /* FLL_LOCK_DB */
+#define WM8962_FLL_LOCK_DB_WIDTH 1 /* FLL_LOCK_DB */
+#define WM8962_PLL3_LOCK_DB 0x0008 /* PLL3_LOCK_DB */
+#define WM8962_PLL3_LOCK_DB_MASK 0x0008 /* PLL3_LOCK_DB */
+#define WM8962_PLL3_LOCK_DB_SHIFT 3 /* PLL3_LOCK_DB */
+#define WM8962_PLL3_LOCK_DB_WIDTH 1 /* PLL3_LOCK_DB */
+#define WM8962_PLL2_LOCK_DB 0x0004 /* PLL2_LOCK_DB */
+#define WM8962_PLL2_LOCK_DB_MASK 0x0004 /* PLL2_LOCK_DB */
+#define WM8962_PLL2_LOCK_DB_SHIFT 2 /* PLL2_LOCK_DB */
+#define WM8962_PLL2_LOCK_DB_WIDTH 1 /* PLL2_LOCK_DB */
+#define WM8962_TEMP_SHUT_DB 0x0001 /* TEMP_SHUT_DB */
+#define WM8962_TEMP_SHUT_DB_MASK 0x0001 /* TEMP_SHUT_DB */
+#define WM8962_TEMP_SHUT_DB_SHIFT 0 /* TEMP_SHUT_DB */
+#define WM8962_TEMP_SHUT_DB_WIDTH 1 /* TEMP_SHUT_DB */
+
+/*
+ * R586 (0x24A) - MICINT Source Pol
+ */
+#define WM8962_MICSCD_IRQ_POL 0x8000 /* MICSCD_IRQ_POL */
+#define WM8962_MICSCD_IRQ_POL_MASK 0x8000 /* MICSCD_IRQ_POL */
+#define WM8962_MICSCD_IRQ_POL_SHIFT 15 /* MICSCD_IRQ_POL */
+#define WM8962_MICSCD_IRQ_POL_WIDTH 1 /* MICSCD_IRQ_POL */
+#define WM8962_MICD_IRQ_POL 0x4000 /* MICD_IRQ_POL */
+#define WM8962_MICD_IRQ_POL_MASK 0x4000 /* MICD_IRQ_POL */
+#define WM8962_MICD_IRQ_POL_SHIFT 14 /* MICD_IRQ_POL */
+#define WM8962_MICD_IRQ_POL_WIDTH 1 /* MICD_IRQ_POL */
+
+/*
+ * R768 (0x300) - DSP2 Power Management
+ */
+#define WM8962_DSP2_ENA 0x0001 /* DSP2_ENA */
+#define WM8962_DSP2_ENA_MASK 0x0001 /* DSP2_ENA */
+#define WM8962_DSP2_ENA_SHIFT 0 /* DSP2_ENA */
+#define WM8962_DSP2_ENA_WIDTH 1 /* DSP2_ENA */
+
+/*
+ * R1037 (0x40D) - DSP2_ExecControl
+ */
+#define WM8962_DSP2_STOPC 0x0020 /* DSP2_STOPC */
+#define WM8962_DSP2_STOPC_MASK 0x0020 /* DSP2_STOPC */
+#define WM8962_DSP2_STOPC_SHIFT 5 /* DSP2_STOPC */
+#define WM8962_DSP2_STOPC_WIDTH 1 /* DSP2_STOPC */
+#define WM8962_DSP2_STOPS 0x0010 /* DSP2_STOPS */
+#define WM8962_DSP2_STOPS_MASK 0x0010 /* DSP2_STOPS */
+#define WM8962_DSP2_STOPS_SHIFT 4 /* DSP2_STOPS */
+#define WM8962_DSP2_STOPS_WIDTH 1 /* DSP2_STOPS */
+#define WM8962_DSP2_STOPI 0x0008 /* DSP2_STOPI */
+#define WM8962_DSP2_STOPI_MASK 0x0008 /* DSP2_STOPI */
+#define WM8962_DSP2_STOPI_SHIFT 3 /* DSP2_STOPI */
+#define WM8962_DSP2_STOPI_WIDTH 1 /* DSP2_STOPI */
+#define WM8962_DSP2_STOP 0x0004 /* DSP2_STOP */
+#define WM8962_DSP2_STOP_MASK 0x0004 /* DSP2_STOP */
+#define WM8962_DSP2_STOP_SHIFT 2 /* DSP2_STOP */
+#define WM8962_DSP2_STOP_WIDTH 1 /* DSP2_STOP */
+#define WM8962_DSP2_RUNR 0x0002 /* DSP2_RUNR */
+#define WM8962_DSP2_RUNR_MASK 0x0002 /* DSP2_RUNR */
+#define WM8962_DSP2_RUNR_SHIFT 1 /* DSP2_RUNR */
+#define WM8962_DSP2_RUNR_WIDTH 1 /* DSP2_RUNR */
+#define WM8962_DSP2_RUN 0x0001 /* DSP2_RUN */
+#define WM8962_DSP2_RUN_MASK 0x0001 /* DSP2_RUN */
+#define WM8962_DSP2_RUN_SHIFT 0 /* DSP2_RUN */
+#define WM8962_DSP2_RUN_WIDTH 1 /* DSP2_RUN */
+
+/*
+ * R8192 (0x2000) - DSP2 Instruction RAM 0
+ */
+#define WM8962_DSP2_INSTR_RAM_1024_10_9_0_MASK 0x03FF /* DSP2_INSTR_RAM_1024_10_9_0 - [9:0] */
+#define WM8962_DSP2_INSTR_RAM_1024_10_9_0_SHIFT 0 /* DSP2_INSTR_RAM_1024_10_9_0 - [9:0] */
+#define WM8962_DSP2_INSTR_RAM_1024_10_9_0_WIDTH 10 /* DSP2_INSTR_RAM_1024_10_9_0 - [9:0] */
+
+/*
+ * R9216 (0x2400) - DSP2 Address RAM 2
+ */
+#define WM8962_DSP2_ADDR_RAM_1024_38_37_32_MASK 0x003F /* DSP2_ADDR_RAM_1024_38_37_32 - [5:0] */
+#define WM8962_DSP2_ADDR_RAM_1024_38_37_32_SHIFT 0 /* DSP2_ADDR_RAM_1024_38_37_32 - [5:0] */
+#define WM8962_DSP2_ADDR_RAM_1024_38_37_32_WIDTH 6 /* DSP2_ADDR_RAM_1024_38_37_32 - [5:0] */
+
+/*
+ * R9217 (0x2401) - DSP2 Address RAM 1
+ */
+#define WM8962_DSP2_ADDR_RAM_1024_38_31_16_MASK 0xFFFF /* DSP2_ADDR_RAM_1024_38_31_16 - [15:0] */
+#define WM8962_DSP2_ADDR_RAM_1024_38_31_16_SHIFT 0 /* DSP2_ADDR_RAM_1024_38_31_16 - [15:0] */
+#define WM8962_DSP2_ADDR_RAM_1024_38_31_16_WIDTH 16 /* DSP2_ADDR_RAM_1024_38_31_16 - [15:0] */
+
+/*
+ * R9218 (0x2402) - DSP2 Address RAM 0
+ */
+#define WM8962_DSP2_ADDR_RAM_1024_38_15_0_MASK 0xFFFF /* DSP2_ADDR_RAM_1024_38_15_0 - [15:0] */
+#define WM8962_DSP2_ADDR_RAM_1024_38_15_0_SHIFT 0 /* DSP2_ADDR_RAM_1024_38_15_0 - [15:0] */
+#define WM8962_DSP2_ADDR_RAM_1024_38_15_0_WIDTH 16 /* DSP2_ADDR_RAM_1024_38_15_0 - [15:0] */
+
+/*
+ * R12288 (0x3000) - DSP2 Data1 RAM 1
+ */
+#define WM8962_DSP2_DATA1_RAM_384_24_23_16_MASK 0x00FF /* DSP2_DATA1_RAM_384_24_23_16 - [7:0] */
+#define WM8962_DSP2_DATA1_RAM_384_24_23_16_SHIFT 0 /* DSP2_DATA1_RAM_384_24_23_16 - [7:0] */
+#define WM8962_DSP2_DATA1_RAM_384_24_23_16_WIDTH 8 /* DSP2_DATA1_RAM_384_24_23_16 - [7:0] */
+
+/*
+ * R12289 (0x3001) - DSP2 Data1 RAM 0
+ */
+#define WM8962_DSP2_DATA1_RAM_384_24_15_0_MASK 0xFFFF /* DSP2_DATA1_RAM_384_24_15_0 - [15:0] */
+#define WM8962_DSP2_DATA1_RAM_384_24_15_0_SHIFT 0 /* DSP2_DATA1_RAM_384_24_15_0 - [15:0] */
+#define WM8962_DSP2_DATA1_RAM_384_24_15_0_WIDTH 16 /* DSP2_DATA1_RAM_384_24_15_0 - [15:0] */
+
+/*
+ * R13312 (0x3400) - DSP2 Data2 RAM 1
+ */
+#define WM8962_DSP2_DATA2_RAM_384_24_23_16_MASK 0x00FF /* DSP2_DATA2_RAM_384_24_23_16 - [7:0] */
+#define WM8962_DSP2_DATA2_RAM_384_24_23_16_SHIFT 0 /* DSP2_DATA2_RAM_384_24_23_16 - [7:0] */
+#define WM8962_DSP2_DATA2_RAM_384_24_23_16_WIDTH 8 /* DSP2_DATA2_RAM_384_24_23_16 - [7:0] */
+
+/*
+ * R13313 (0x3401) - DSP2 Data2 RAM 0
+ */
+#define WM8962_DSP2_DATA2_RAM_384_24_15_0_MASK 0xFFFF /* DSP2_DATA2_RAM_384_24_15_0 - [15:0] */
+#define WM8962_DSP2_DATA2_RAM_384_24_15_0_SHIFT 0 /* DSP2_DATA2_RAM_384_24_15_0 - [15:0] */
+#define WM8962_DSP2_DATA2_RAM_384_24_15_0_WIDTH 16 /* DSP2_DATA2_RAM_384_24_15_0 - [15:0] */
+
+/*
+ * R14336 (0x3800) - DSP2 Data3 RAM 1
+ */
+#define WM8962_DSP2_DATA3_RAM_384_24_23_16_MASK 0x00FF /* DSP2_DATA3_RAM_384_24_23_16 - [7:0] */
+#define WM8962_DSP2_DATA3_RAM_384_24_23_16_SHIFT 0 /* DSP2_DATA3_RAM_384_24_23_16 - [7:0] */
+#define WM8962_DSP2_DATA3_RAM_384_24_23_16_WIDTH 8 /* DSP2_DATA3_RAM_384_24_23_16 - [7:0] */
+
+/*
+ * R14337 (0x3801) - DSP2 Data3 RAM 0
+ */
+#define WM8962_DSP2_DATA3_RAM_384_24_15_0_MASK 0xFFFF /* DSP2_DATA3_RAM_384_24_15_0 - [15:0] */
+#define WM8962_DSP2_DATA3_RAM_384_24_15_0_SHIFT 0 /* DSP2_DATA3_RAM_384_24_15_0 - [15:0] */
+#define WM8962_DSP2_DATA3_RAM_384_24_15_0_WIDTH 16 /* DSP2_DATA3_RAM_384_24_15_0 - [15:0] */
+
+/*
+ * R15360 (0x3C00) - DSP2 Coeff RAM 0
+ */
+#define WM8962_DSP2_CMAP_RAM_384_11_10_0_MASK 0x07FF /* DSP2_CMAP_RAM_384_11_10_0 - [10:0] */
+#define WM8962_DSP2_CMAP_RAM_384_11_10_0_SHIFT 0 /* DSP2_CMAP_RAM_384_11_10_0 - [10:0] */
+#define WM8962_DSP2_CMAP_RAM_384_11_10_0_WIDTH 11 /* DSP2_CMAP_RAM_384_11_10_0 - [10:0] */
+
+/*
+ * R16384 (0x4000) - RETUNEADC_SHARED_COEFF_1
+ */
+#define WM8962_ADC_RETUNE_SCV 0x0080 /* ADC_RETUNE_SCV */
+#define WM8962_ADC_RETUNE_SCV_MASK 0x0080 /* ADC_RETUNE_SCV */
+#define WM8962_ADC_RETUNE_SCV_SHIFT 7 /* ADC_RETUNE_SCV */
+#define WM8962_ADC_RETUNE_SCV_WIDTH 1 /* ADC_RETUNE_SCV */
+#define WM8962_RETUNEADC_SHARED_COEFF_22_16_MASK 0x007F /* RETUNEADC_SHARED_COEFF_22_16 - [6:0] */
+#define WM8962_RETUNEADC_SHARED_COEFF_22_16_SHIFT 0 /* RETUNEADC_SHARED_COEFF_22_16 - [6:0] */
+#define WM8962_RETUNEADC_SHARED_COEFF_22_16_WIDTH 7 /* RETUNEADC_SHARED_COEFF_22_16 - [6:0] */
+
+/*
+ * R16385 (0x4001) - RETUNEADC_SHARED_COEFF_0
+ */
+#define WM8962_RETUNEADC_SHARED_COEFF_15_00_MASK 0xFFFF /* RETUNEADC_SHARED_COEFF_15_00 - [15:0] */
+#define WM8962_RETUNEADC_SHARED_COEFF_15_00_SHIFT 0 /* RETUNEADC_SHARED_COEFF_15_00 - [15:0] */
+#define WM8962_RETUNEADC_SHARED_COEFF_15_00_WIDTH 16 /* RETUNEADC_SHARED_COEFF_15_00 - [15:0] */
+
+/*
+ * R16386 (0x4002) - RETUNEDAC_SHARED_COEFF_1
+ */
+#define WM8962_DAC_RETUNE_SCV 0x0080 /* DAC_RETUNE_SCV */
+#define WM8962_DAC_RETUNE_SCV_MASK 0x0080 /* DAC_RETUNE_SCV */
+#define WM8962_DAC_RETUNE_SCV_SHIFT 7 /* DAC_RETUNE_SCV */
+#define WM8962_DAC_RETUNE_SCV_WIDTH 1 /* DAC_RETUNE_SCV */
+#define WM8962_RETUNEDAC_SHARED_COEFF_23_16_MASK 0x007F /* RETUNEDAC_SHARED_COEFF_23_16 - [6:0] */
+#define WM8962_RETUNEDAC_SHARED_COEFF_23_16_SHIFT 0 /* RETUNEDAC_SHARED_COEFF_23_16 - [6:0] */
+#define WM8962_RETUNEDAC_SHARED_COEFF_23_16_WIDTH 7 /* RETUNEDAC_SHARED_COEFF_23_16 - [6:0] */
+
+/*
+ * R16387 (0x4003) - RETUNEDAC_SHARED_COEFF_0
+ */
+#define WM8962_RETUNEDAC_SHARED_COEFF_15_00_MASK 0xFFFF /* RETUNEDAC_SHARED_COEFF_15_00 - [15:0] */
+#define WM8962_RETUNEDAC_SHARED_COEFF_15_00_SHIFT 0 /* RETUNEDAC_SHARED_COEFF_15_00 - [15:0] */
+#define WM8962_RETUNEDAC_SHARED_COEFF_15_00_WIDTH 16 /* RETUNEDAC_SHARED_COEFF_15_00 - [15:0] */
+
+/*
+ * R16388 (0x4004) - SOUNDSTAGE_ENABLES_1
+ */
+#define WM8962_SOUNDSTAGE_ENABLES_23_16_MASK 0x00FF /* SOUNDSTAGE_ENABLES_23_16 - [7:0] */
+#define WM8962_SOUNDSTAGE_ENABLES_23_16_SHIFT 0 /* SOUNDSTAGE_ENABLES_23_16 - [7:0] */
+#define WM8962_SOUNDSTAGE_ENABLES_23_16_WIDTH 8 /* SOUNDSTAGE_ENABLES_23_16 - [7:0] */
+
+/*
+ * R16389 (0x4005) - SOUNDSTAGE_ENABLES_0
+ */
+#define WM8962_SOUNDSTAGE_ENABLES_15_06_MASK 0xFFC0 /* SOUNDSTAGE_ENABLES_15_06 - [15:6] */
+#define WM8962_SOUNDSTAGE_ENABLES_15_06_SHIFT 6 /* SOUNDSTAGE_ENABLES_15_06 - [15:6] */
+#define WM8962_SOUNDSTAGE_ENABLES_15_06_WIDTH 10 /* SOUNDSTAGE_ENABLES_15_06 - [15:6] */
+#define WM8962_RTN_ADC_ENA 0x0020 /* RTN_ADC_ENA */
+#define WM8962_RTN_ADC_ENA_MASK 0x0020 /* RTN_ADC_ENA */
+#define WM8962_RTN_ADC_ENA_SHIFT 5 /* RTN_ADC_ENA */
+#define WM8962_RTN_ADC_ENA_WIDTH 1 /* RTN_ADC_ENA */
+#define WM8962_RTN_DAC_ENA 0x0010 /* RTN_DAC_ENA */
+#define WM8962_RTN_DAC_ENA_MASK 0x0010 /* RTN_DAC_ENA */
+#define WM8962_RTN_DAC_ENA_SHIFT 4 /* RTN_DAC_ENA */
+#define WM8962_RTN_DAC_ENA_WIDTH 1 /* RTN_DAC_ENA */
+#define WM8962_HDBASS_ENA 0x0008 /* HDBASS_ENA */
+#define WM8962_HDBASS_ENA_MASK 0x0008 /* HDBASS_ENA */
+#define WM8962_HDBASS_ENA_SHIFT 3 /* HDBASS_ENA */
+#define WM8962_HDBASS_ENA_WIDTH 1 /* HDBASS_ENA */
+#define WM8962_HPF2_ENA 0x0004 /* HPF2_ENA */
+#define WM8962_HPF2_ENA_MASK 0x0004 /* HPF2_ENA */
+#define WM8962_HPF2_ENA_SHIFT 2 /* HPF2_ENA */
+#define WM8962_HPF2_ENA_WIDTH 1 /* HPF2_ENA */
+#define WM8962_HPF1_ENA 0x0002 /* HPF1_ENA */
+#define WM8962_HPF1_ENA_MASK 0x0002 /* HPF1_ENA */
+#define WM8962_HPF1_ENA_SHIFT 1 /* HPF1_ENA */
+#define WM8962_HPF1_ENA_WIDTH 1 /* HPF1_ENA */
+#define WM8962_VSS_ENA 0x0001 /* VSS_ENA */
+#define WM8962_VSS_ENA_MASK 0x0001 /* VSS_ENA */
+#define WM8962_VSS_ENA_SHIFT 0 /* VSS_ENA */
+#define WM8962_VSS_ENA_WIDTH 1 /* VSS_ENA */
+
+int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8971.c b/kernel/sound/soc/codecs/wm8971.c
new file mode 100644
index 000000000..f9cbabdc6
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8971.c
@@ -0,0 +1,725 @@
+/*
+ * wm8971.c -- WM8971 ALSA SoC Audio driver
+ *
+ * Copyright 2005 Lab126, Inc.
+ *
+ * Author: Kenneth Kiraly <kiraly@lab126.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 as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#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/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#include "wm8971.h"
+
+#define WM8971_REG_COUNT 43
+
+/* codec private data */
+struct wm8971_priv {
+ unsigned int sysclk;
+ struct delayed_work charge_work;
+ struct regmap *regmap;
+};
+
+/*
+ * wm8971 register cache
+ * We can't read the WM8971 register space when we
+ * are using 2 wire for device control, so we cache them instead.
+ */
+static const struct reg_default wm8971_reg_defaults[] = {
+ { 0, 0x0097 },
+ { 1, 0x0097 },
+ { 2, 0x0079 },
+ { 3, 0x0079 },
+ { 4, 0x0000 },
+ { 5, 0x0008 },
+ { 6, 0x0000 },
+ { 7, 0x000a },
+ { 8, 0x0000 },
+ { 9, 0x0000 },
+ { 10, 0x00ff },
+ { 11, 0x00ff },
+ { 12, 0x000f },
+ { 13, 0x000f },
+ { 14, 0x0000 },
+ { 15, 0x0000 },
+ { 16, 0x0000 },
+ { 17, 0x007b },
+ { 18, 0x0000 },
+ { 19, 0x0032 },
+ { 20, 0x0000 },
+ { 21, 0x00c3 },
+ { 22, 0x00c3 },
+ { 23, 0x00c0 },
+ { 24, 0x0000 },
+ { 25, 0x0000 },
+ { 26, 0x0000 },
+ { 27, 0x0000 },
+ { 28, 0x0000 },
+ { 29, 0x0000 },
+ { 30, 0x0000 },
+ { 31, 0x0000 },
+ { 32, 0x0000 },
+ { 33, 0x0000 },
+ { 34, 0x0050 },
+ { 35, 0x0050 },
+ { 36, 0x0050 },
+ { 37, 0x0050 },
+ { 38, 0x0050 },
+ { 39, 0x0050 },
+ { 40, 0x0079 },
+ { 41, 0x0079 },
+ { 42, 0x0079 },
+};
+
+#define wm8971_reset(c) snd_soc_write(c, WM8971_RESET, 0)
+
+/* WM8971 Controls */
+static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" };
+static const char *wm8971_bass_filter[] = { "130Hz @ 48kHz",
+ "200Hz @ 48kHz" };
+static const char *wm8971_treble[] = { "8kHz", "4kHz" };
+static const char *wm8971_alc_func[] = { "Off", "Right", "Left", "Stereo" };
+static const char *wm8971_ng_type[] = { "Constant PGA Gain",
+ "Mute ADC Output" };
+static const char *wm8971_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8971_mono_mux[] = {"Stereo", "Mono (Left)",
+ "Mono (Right)", "Digital Mono"};
+static const char *wm8971_dac_phase[] = { "Non Inverted", "Inverted" };
+static const char *wm8971_lline_mux[] = {"Line", "NC", "NC", "PGA",
+ "Differential"};
+static const char *wm8971_rline_mux[] = {"Line", "Mic", "NC", "PGA",
+ "Differential"};
+static const char *wm8971_lpga_sel[] = {"Line", "NC", "NC", "Differential"};
+static const char *wm8971_rpga_sel[] = {"Line", "Mic", "NC", "Differential"};
+static const char *wm8971_adcpol[] = {"Normal", "L Invert", "R Invert",
+ "L + R Invert"};
+
+static const struct soc_enum wm8971_enum[] = {
+ SOC_ENUM_SINGLE(WM8971_BASS, 7, 2, wm8971_bass), /* 0 */
+ SOC_ENUM_SINGLE(WM8971_BASS, 6, 2, wm8971_bass_filter),
+ SOC_ENUM_SINGLE(WM8971_TREBLE, 6, 2, wm8971_treble),
+ SOC_ENUM_SINGLE(WM8971_ALC1, 7, 4, wm8971_alc_func),
+ SOC_ENUM_SINGLE(WM8971_NGATE, 1, 2, wm8971_ng_type), /* 4 */
+ SOC_ENUM_SINGLE(WM8971_ADCDAC, 1, 4, wm8971_deemp),
+ SOC_ENUM_SINGLE(WM8971_ADCTL1, 4, 4, wm8971_mono_mux),
+ SOC_ENUM_SINGLE(WM8971_ADCTL1, 1, 2, wm8971_dac_phase),
+ SOC_ENUM_SINGLE(WM8971_LOUTM1, 0, 5, wm8971_lline_mux), /* 8 */
+ SOC_ENUM_SINGLE(WM8971_ROUTM1, 0, 5, wm8971_rline_mux),
+ SOC_ENUM_SINGLE(WM8971_LADCIN, 6, 4, wm8971_lpga_sel),
+ SOC_ENUM_SINGLE(WM8971_RADCIN, 6, 4, wm8971_rpga_sel),
+ SOC_ENUM_SINGLE(WM8971_ADCDAC, 5, 4, wm8971_adcpol), /* 12 */
+ SOC_ENUM_SINGLE(WM8971_ADCIN, 6, 4, wm8971_mono_mux),
+};
+
+static const struct snd_kcontrol_new wm8971_snd_controls[] = {
+ SOC_DOUBLE_R("Capture Volume", WM8971_LINVOL, WM8971_RINVOL, 0, 63, 0),
+ SOC_DOUBLE_R("Capture ZC Switch", WM8971_LINVOL, WM8971_RINVOL,
+ 6, 1, 0),
+ SOC_DOUBLE_R("Capture Switch", WM8971_LINVOL, WM8971_RINVOL, 7, 1, 1),
+
+ SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8971_LOUT1V,
+ WM8971_ROUT1V, 7, 1, 0),
+ SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8971_LOUT2V,
+ WM8971_ROUT2V, 7, 1, 0),
+ SOC_SINGLE("Mono Playback ZC Switch", WM8971_MOUTV, 7, 1, 0),
+
+ SOC_DOUBLE_R("PCM Volume", WM8971_LDAC, WM8971_RDAC, 0, 255, 0),
+
+ SOC_DOUBLE_R("Bypass Left Playback Volume", WM8971_LOUTM1,
+ WM8971_LOUTM2, 4, 7, 1),
+ SOC_DOUBLE_R("Bypass Right Playback Volume", WM8971_ROUTM1,
+ WM8971_ROUTM2, 4, 7, 1),
+ SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8971_MOUTM1,
+ WM8971_MOUTM2, 4, 7, 1),
+
+ SOC_DOUBLE_R("Headphone Playback Volume", WM8971_LOUT1V,
+ WM8971_ROUT1V, 0, 127, 0),
+ SOC_DOUBLE_R("Speaker Playback Volume", WM8971_LOUT2V,
+ WM8971_ROUT2V, 0, 127, 0),
+
+ SOC_ENUM("Bass Boost", wm8971_enum[0]),
+ SOC_ENUM("Bass Filter", wm8971_enum[1]),
+ SOC_SINGLE("Bass Volume", WM8971_BASS, 0, 7, 1),
+
+ SOC_SINGLE("Treble Volume", WM8971_TREBLE, 0, 7, 0),
+ SOC_ENUM("Treble Cut-off", wm8971_enum[2]),
+
+ SOC_SINGLE("Capture Filter Switch", WM8971_ADCDAC, 0, 1, 1),
+
+ SOC_SINGLE("ALC Target Volume", WM8971_ALC1, 0, 7, 0),
+ SOC_SINGLE("ALC Max Volume", WM8971_ALC1, 4, 7, 0),
+
+ SOC_SINGLE("ALC Capture Target Volume", WM8971_ALC1, 0, 7, 0),
+ SOC_SINGLE("ALC Capture Max Volume", WM8971_ALC1, 4, 7, 0),
+ SOC_ENUM("ALC Capture Function", wm8971_enum[3]),
+ SOC_SINGLE("ALC Capture ZC Switch", WM8971_ALC2, 7, 1, 0),
+ SOC_SINGLE("ALC Capture Hold Time", WM8971_ALC2, 0, 15, 0),
+ SOC_SINGLE("ALC Capture Decay Time", WM8971_ALC3, 4, 15, 0),
+ SOC_SINGLE("ALC Capture Attack Time", WM8971_ALC3, 0, 15, 0),
+ SOC_SINGLE("ALC Capture NG Threshold", WM8971_NGATE, 3, 31, 0),
+ SOC_ENUM("ALC Capture NG Type", wm8971_enum[4]),
+ SOC_SINGLE("ALC Capture NG Switch", WM8971_NGATE, 0, 1, 0),
+
+ SOC_SINGLE("Capture 6dB Attenuate", WM8971_ADCDAC, 8, 1, 0),
+ SOC_SINGLE("Playback 6dB Attenuate", WM8971_ADCDAC, 7, 1, 0),
+
+ SOC_ENUM("Playback De-emphasis", wm8971_enum[5]),
+ SOC_ENUM("Playback Function", wm8971_enum[6]),
+ SOC_ENUM("Playback Phase", wm8971_enum[7]),
+
+ SOC_DOUBLE_R("Mic Boost", WM8971_LADCIN, WM8971_RADCIN, 4, 3, 0),
+};
+
+/*
+ * DAPM Controls
+ */
+
+/* Left Mixer */
+static const struct snd_kcontrol_new wm8971_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("Playback Switch", WM8971_LOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_LOUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8971_LOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_LOUTM2, 7, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new wm8971_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8971_ROUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_ROUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Playback Switch", WM8971_ROUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_ROUTM2, 7, 1, 0),
+};
+
+/* Mono Mixer */
+static const struct snd_kcontrol_new wm8971_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8971_MOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_MOUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8971_MOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_MOUTM2, 7, 1, 0),
+};
+
+/* Left Line Mux */
+static const struct snd_kcontrol_new wm8971_left_line_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[8]);
+
+/* Right Line Mux */
+static const struct snd_kcontrol_new wm8971_right_line_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[9]);
+
+/* Left PGA Mux */
+static const struct snd_kcontrol_new wm8971_left_pga_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[10]);
+
+/* Right PGA Mux */
+static const struct snd_kcontrol_new wm8971_right_pga_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[11]);
+
+/* Mono ADC Mux */
+static const struct snd_kcontrol_new wm8971_monomux_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[13]);
+
+static const struct snd_soc_dapm_widget wm8971_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ &wm8971_left_mixer_controls[0],
+ ARRAY_SIZE(wm8971_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ &wm8971_right_mixer_controls[0],
+ ARRAY_SIZE(wm8971_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono Mixer", WM8971_PWR2, 2, 0,
+ &wm8971_mono_mixer_controls[0],
+ ARRAY_SIZE(wm8971_mono_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("Right Out 2", WM8971_PWR2, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", WM8971_PWR2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", WM8971_PWR2, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", WM8971_PWR2, 6, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8971_PWR2, 7, 0),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8971_PWR2, 8, 0),
+ SND_SOC_DAPM_PGA("Mono Out 1", WM8971_PWR2, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", WM8971_PWR1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8971_PWR1, 2, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8971_PWR1, 3, 0),
+
+ SND_SOC_DAPM_MUX("Left PGA Mux", WM8971_PWR1, 5, 0,
+ &wm8971_left_pga_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", WM8971_PWR1, 4, 0,
+ &wm8971_right_pga_controls),
+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
+ &wm8971_left_line_controls),
+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
+ &wm8971_right_line_controls),
+
+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+ &wm8971_monomux_controls),
+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+ &wm8971_monomux_controls),
+
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("MONO"),
+
+ SND_SOC_DAPM_INPUT("LINPUT1"),
+ SND_SOC_DAPM_INPUT("RINPUT1"),
+ SND_SOC_DAPM_INPUT("MIC"),
+};
+
+static const struct snd_soc_dapm_route wm8971_dapm_routes[] = {
+ /* left mixer */
+ {"Left Mixer", "Playback Switch", "Left DAC"},
+ {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Left Mixer", "Right Playback Switch", "Right DAC"},
+ {"Left Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* right mixer */
+ {"Right Mixer", "Left Playback Switch", "Left DAC"},
+ {"Right Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Right Mixer", "Playback Switch", "Right DAC"},
+ {"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* left out 1 */
+ {"Left Out 1", NULL, "Left Mixer"},
+ {"LOUT1", NULL, "Left Out 1"},
+
+ /* left out 2 */
+ {"Left Out 2", NULL, "Left Mixer"},
+ {"LOUT2", NULL, "Left Out 2"},
+
+ /* right out 1 */
+ {"Right Out 1", NULL, "Right Mixer"},
+ {"ROUT1", NULL, "Right Out 1"},
+
+ /* right out 2 */
+ {"Right Out 2", NULL, "Right Mixer"},
+ {"ROUT2", NULL, "Right Out 2"},
+
+ /* mono mixer */
+ {"Mono Mixer", "Left Playback Switch", "Left DAC"},
+ {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Mono Mixer", "Right Playback Switch", "Right DAC"},
+ {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* mono out */
+ {"Mono Out", NULL, "Mono Mixer"},
+ {"MONO1", NULL, "Mono Out"},
+
+ /* Left Line Mux */
+ {"Left Line Mux", "Line", "LINPUT1"},
+ {"Left Line Mux", "PGA", "Left PGA Mux"},
+ {"Left Line Mux", "Differential", "Differential Mux"},
+
+ /* Right Line Mux */
+ {"Right Line Mux", "Line", "RINPUT1"},
+ {"Right Line Mux", "Mic", "MIC"},
+ {"Right Line Mux", "PGA", "Right PGA Mux"},
+ {"Right Line Mux", "Differential", "Differential Mux"},
+
+ /* Left PGA Mux */
+ {"Left PGA Mux", "Line", "LINPUT1"},
+ {"Left PGA Mux", "Differential", "Differential Mux"},
+
+ /* Right PGA Mux */
+ {"Right PGA Mux", "Line", "RINPUT1"},
+ {"Right PGA Mux", "Differential", "Differential Mux"},
+
+ /* Differential Mux */
+ {"Differential Mux", "Line", "LINPUT1"},
+ {"Differential Mux", "Line", "RINPUT1"},
+
+ /* Left ADC Mux */
+ {"Left ADC Mux", "Stereo", "Left PGA Mux"},
+ {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
+ {"Left ADC Mux", "Digital Mono", "Left PGA Mux"},
+
+ /* Right ADC Mux */
+ {"Right ADC Mux", "Stereo", "Right PGA Mux"},
+ {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
+ {"Right ADC Mux", "Digital Mono", "Right PGA Mux"},
+
+ /* ADC */
+ {"Left ADC", NULL, "Left ADC Mux"},
+ {"Right ADC", NULL, "Right ADC Mux"},
+};
+
+struct _coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr:5;
+ u8 usb:1;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ /* 8k */
+ {12288000, 8000, 1536, 0x6, 0x0},
+ {11289600, 8000, 1408, 0x16, 0x0},
+ {18432000, 8000, 2304, 0x7, 0x0},
+ {16934400, 8000, 2112, 0x17, 0x0},
+ {12000000, 8000, 1500, 0x6, 0x1},
+
+ /* 11.025k */
+ {11289600, 11025, 1024, 0x18, 0x0},
+ {16934400, 11025, 1536, 0x19, 0x0},
+ {12000000, 11025, 1088, 0x19, 0x1},
+
+ /* 16k */
+ {12288000, 16000, 768, 0xa, 0x0},
+ {18432000, 16000, 1152, 0xb, 0x0},
+ {12000000, 16000, 750, 0xa, 0x1},
+
+ /* 22.05k */
+ {11289600, 22050, 512, 0x1a, 0x0},
+ {16934400, 22050, 768, 0x1b, 0x0},
+ {12000000, 22050, 544, 0x1b, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 384, 0xc, 0x0},
+ {18432000, 32000, 576, 0xd, 0x0},
+ {12000000, 32000, 375, 0xa, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x10, 0x0},
+ {16934400, 44100, 384, 0x11, 0x0},
+ {12000000, 44100, 272, 0x11, 0x1},
+
+ /* 48k */
+ {12288000, 48000, 256, 0x0, 0x0},
+ {18432000, 48000, 384, 0x1, 0x0},
+ {12000000, 48000, 250, 0x0, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0x1e, 0x0},
+ {16934400, 88200, 192, 0x1f, 0x0},
+ {12000000, 88200, 136, 0x1f, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 128, 0xe, 0x0},
+ {18432000, 96000, 192, 0xf, 0x0},
+ {12000000, 96000, 125, 0xe, 0x1},
+};
+
+static int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int wm8971_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
+
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ wm8971->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface = 0x0040;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8971_IFACE, iface);
+ return 0;
+}
+
+static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
+ u16 iface = snd_soc_read(codec, WM8971_IFACE) & 0x1f3;
+ u16 srate = snd_soc_read(codec, WM8971_SRATE) & 0x1c0;
+ int coeff = get_coeff(wm8971->sysclk, params_rate(params));
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface |= 0x0004;
+ break;
+ case 24:
+ iface |= 0x0008;
+ break;
+ case 32:
+ iface |= 0x000c;
+ break;
+ }
+
+ /* set iface & srate */
+ snd_soc_write(codec, WM8971_IFACE, iface);
+ if (coeff >= 0)
+ snd_soc_write(codec, WM8971_SRATE, srate |
+ (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
+
+ return 0;
+}
+
+static int wm8971_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8971_ADCDAC) & 0xfff7;
+
+ if (mute)
+ snd_soc_write(codec, WM8971_ADCDAC, mute_reg | 0x8);
+ else
+ snd_soc_write(codec, WM8971_ADCDAC, mute_reg);
+ return 0;
+}
+
+static void wm8971_charge_work(struct work_struct *work)
+{
+ struct wm8971_priv *wm8971 =
+ container_of(work, struct wm8971_priv, charge_work.work);
+
+ /* Set to 500k */
+ regmap_update_bits(wm8971->regmap, WM8971_PWR1, 0x0180, 0x0100);
+}
+
+static int wm8971_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
+ u16 pwr_reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* set vmid to 50k and unmute dac */
+ snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* Wait until fully charged */
+ flush_delayed_work(&wm8971->charge_work);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ snd_soc_cache_sync(codec);
+ /* charge output caps - set vmid to 5k for quick power up */
+ snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x01c0);
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8971->charge_work, msecs_to_jiffies(1000));
+ } else {
+ /* mute dac and set vmid to 500k, enable VREF */
+ snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
+ }
+
+ break;
+ case SND_SOC_BIAS_OFF:
+ cancel_delayed_work_sync(&wm8971->charge_work);
+ snd_soc_write(codec, WM8971_PWR1, 0x0001);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8971_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define WM8971_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8971_dai_ops = {
+ .hw_params = wm8971_pcm_hw_params,
+ .digital_mute = wm8971_mute,
+ .set_fmt = wm8971_set_dai_fmt,
+ .set_sysclk = wm8971_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver wm8971_dai = {
+ .name = "wm8971-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8971_RATES,
+ .formats = WM8971_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8971_RATES,
+ .formats = WM8971_FORMATS,},
+ .ops = &wm8971_dai_ops,
+};
+
+static int wm8971_probe(struct snd_soc_codec *codec)
+{
+ struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
+
+ INIT_DELAYED_WORK(&wm8971->charge_work, wm8971_charge_work);
+
+ wm8971_reset(codec);
+
+ /* set the update bits */
+ snd_soc_update_bits(codec, WM8971_LDAC, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8971_RDAC, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8971_LOUT1V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8971_ROUT1V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8971_LOUT2V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8971_ROUT2V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8971_LINVOL, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8971_RINVOL, 0x0100, 0x0100);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8971 = {
+ .probe = wm8971_probe,
+ .set_bias_level = wm8971_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8971_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8971_snd_controls),
+ .dapm_widgets = wm8971_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8971_dapm_widgets),
+ .dapm_routes = wm8971_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8971_dapm_routes),
+};
+
+static const struct regmap_config wm8971_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8971_MOUTV,
+
+ .reg_defaults = wm8971_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8971_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int wm8971_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8971_priv *wm8971;
+ int ret;
+
+ wm8971 = devm_kzalloc(&i2c->dev, sizeof(struct wm8971_priv),
+ GFP_KERNEL);
+ if (wm8971 == NULL)
+ return -ENOMEM;
+
+ wm8971->regmap = devm_regmap_init_i2c(i2c, &wm8971_regmap);
+ if (IS_ERR(wm8971->regmap))
+ return PTR_ERR(wm8971->regmap);
+
+ i2c_set_clientdata(i2c, wm8971);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8971, &wm8971_dai, 1);
+
+ return ret;
+}
+
+static int wm8971_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8971_i2c_id[] = {
+ { "wm8971", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8971_i2c_id);
+
+static struct i2c_driver wm8971_i2c_driver = {
+ .driver = {
+ .name = "wm8971",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8971_i2c_probe,
+ .remove = wm8971_i2c_remove,
+ .id_table = wm8971_i2c_id,
+};
+
+module_i2c_driver(wm8971_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8971 driver");
+MODULE_AUTHOR("Lab126");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8971.h b/kernel/sound/soc/codecs/wm8971.h
new file mode 100644
index 000000000..f31c38fdd
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8971.h
@@ -0,0 +1,56 @@
+/*
+ * wm8971.h -- audio driver for WM8971
+ *
+ * Copyright 2005 Lab126, Inc.
+ *
+ * Author: Kenneth Kiraly <kiraly@lab126.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 _WM8971_H
+#define _WM8971_H
+
+#define WM8971_LINVOL 0x00
+#define WM8971_RINVOL 0x01
+#define WM8971_LOUT1V 0x02
+#define WM8971_ROUT1V 0x03
+#define WM8971_ADCDAC 0x05
+#define WM8971_IFACE 0x07
+#define WM8971_SRATE 0x08
+#define WM8971_LDAC 0x0a
+#define WM8971_RDAC 0x0b
+#define WM8971_BASS 0x0c
+#define WM8971_TREBLE 0x0d
+#define WM8971_RESET 0x0f
+#define WM8971_ALC1 0x11
+#define WM8971_ALC2 0x12
+#define WM8971_ALC3 0x13
+#define WM8971_NGATE 0x14
+#define WM8971_LADC 0x15
+#define WM8971_RADC 0x16
+#define WM8971_ADCTL1 0x17
+#define WM8971_ADCTL2 0x18
+#define WM8971_PWR1 0x19
+#define WM8971_PWR2 0x1a
+#define WM8971_ADCTL3 0x1b
+#define WM8971_ADCIN 0x1f
+#define WM8971_LADCIN 0x20
+#define WM8971_RADCIN 0x21
+#define WM8971_LOUTM1 0x22
+#define WM8971_LOUTM2 0x23
+#define WM8971_ROUTM1 0x24
+#define WM8971_ROUTM2 0x25
+#define WM8971_MOUTM1 0x26
+#define WM8971_MOUTM2 0x27
+#define WM8971_LOUT2V 0x28
+#define WM8971_ROUT2V 0x29
+#define WM8971_MOUTV 0x2A
+
+#define WM8971_SYSCLK 0
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8974.c b/kernel/sound/soc/codecs/wm8974.c
new file mode 100644
index 000000000..ff0e4646b
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8974.c
@@ -0,0 +1,649 @@
+/*
+ * wm8974.c -- WM8974 ALSA Soc Audio driver
+ *
+ * Copyright 2006-2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <Liam.Girdwood@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/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 "wm8974.h"
+
+static const struct reg_default wm8974_reg_defaults[] = {
+ { 0, 0x0000 }, { 1, 0x0000 }, { 2, 0x0000 }, { 3, 0x0000 },
+ { 4, 0x0050 }, { 5, 0x0000 }, { 6, 0x0140 }, { 7, 0x0000 },
+ { 8, 0x0000 }, { 9, 0x0000 }, { 10, 0x0000 }, { 11, 0x00ff },
+ { 12, 0x0000 }, { 13, 0x0000 }, { 14, 0x0100 }, { 15, 0x00ff },
+ { 16, 0x0000 }, { 17, 0x0000 }, { 18, 0x012c }, { 19, 0x002c },
+ { 20, 0x002c }, { 21, 0x002c }, { 22, 0x002c }, { 23, 0x0000 },
+ { 24, 0x0032 }, { 25, 0x0000 }, { 26, 0x0000 }, { 27, 0x0000 },
+ { 28, 0x0000 }, { 29, 0x0000 }, { 30, 0x0000 }, { 31, 0x0000 },
+ { 32, 0x0038 }, { 33, 0x000b }, { 34, 0x0032 }, { 35, 0x0000 },
+ { 36, 0x0008 }, { 37, 0x000c }, { 38, 0x0093 }, { 39, 0x00e9 },
+ { 40, 0x0000 }, { 41, 0x0000 }, { 42, 0x0000 }, { 43, 0x0000 },
+ { 44, 0x0003 }, { 45, 0x0010 }, { 46, 0x0000 }, { 47, 0x0000 },
+ { 48, 0x0000 }, { 49, 0x0002 }, { 50, 0x0000 }, { 51, 0x0000 },
+ { 52, 0x0000 }, { 53, 0x0000 }, { 54, 0x0039 }, { 55, 0x0000 },
+ { 56, 0x0000 },
+};
+
+#define WM8974_POWER1_BIASEN 0x08
+#define WM8974_POWER1_BUFIOEN 0x04
+
+#define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0)
+
+static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
+static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8974_eqmode[] = {"Capture", "Playback" };
+static const char *wm8974_bw[] = {"Narrow", "Wide" };
+static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
+static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
+static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
+static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
+static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
+static const char *wm8974_alc[] = {"ALC", "Limiter" };
+
+static const struct soc_enum wm8974_enum[] = {
+ SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
+ SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
+ SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
+ SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
+
+ SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
+ SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
+ SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
+ SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
+
+ SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
+ SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
+ SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
+ SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
+
+ SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
+ SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
+};
+
+static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
+
+static SOC_ENUM_SINGLE_DECL(wm8974_auxmode,
+ WM8974_INPUT, 3, wm8974_auxmode_text);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
+
+static const struct snd_kcontrol_new wm8974_snd_controls[] = {
+
+SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
+
+SOC_ENUM("DAC Companding", wm8974_enum[1]),
+SOC_ENUM("ADC Companding", wm8974_enum[0]),
+
+SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
+SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
+
+SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
+
+SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
+SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
+SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0),
+
+SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv),
+
+SOC_ENUM("Equaliser Function", wm8974_enum[3]),
+SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
+SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv),
+
+SOC_ENUM("Equaliser EQ2 Bandwidth", wm8974_enum[5]),
+SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
+SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv),
+
+SOC_ENUM("Equaliser EQ3 Bandwidth", wm8974_enum[7]),
+SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
+SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv),
+
+SOC_ENUM("Equaliser EQ4 Bandwidth", wm8974_enum[9]),
+SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
+SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv),
+
+SOC_ENUM("Equaliser EQ5 Bandwidth", wm8974_enum[11]),
+SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
+SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv),
+
+SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
+SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
+SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
+
+SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
+SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
+
+SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
+SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
+SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
+
+SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
+SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
+SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
+
+SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
+SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
+
+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
+
+SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
+SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv),
+
+SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
+SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
+SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv),
+
+SOC_ENUM("Aux Mode", wm8974_auxmode),
+
+SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
+SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
+
+/* DAC / ADC oversampling */
+SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0),
+SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0),
+};
+
+/* Speaker Output Mixer */
+static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0),
+};
+
+/* Mono Output Mixer */
+static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0),
+};
+
+/* Boost mixer */
+static const struct snd_kcontrol_new wm8974_boost_mixer[] = {
+SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0),
+};
+
+/* Input PGA */
+static const struct snd_kcontrol_new wm8974_inpga[] = {
+SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0),
+SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0),
+SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0),
+};
+
+/* AUX Input boost vol */
+static const struct snd_kcontrol_new wm8974_aux_boost_controls =
+SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
+
+/* Mic Input boost vol */
+static const struct snd_kcontrol_new wm8974_mic_boost_controls =
+SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
+
+static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
+ &wm8974_speaker_mixer_controls[0],
+ ARRAY_SIZE(wm8974_speaker_mixer_controls)),
+SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
+ &wm8974_mono_mixer_controls[0],
+ ARRAY_SIZE(wm8974_mono_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
+SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga,
+ ARRAY_SIZE(wm8974_inpga)),
+SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0,
+ wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)),
+
+SND_SOC_DAPM_SUPPLY("Mic Bias", WM8974_POWER1, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_INPUT("MICN"),
+SND_SOC_DAPM_INPUT("MICP"),
+SND_SOC_DAPM_INPUT("AUX"),
+SND_SOC_DAPM_OUTPUT("MONOOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+};
+
+static const struct snd_soc_dapm_route wm8974_dapm_routes[] = {
+ /* Mono output mixer */
+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+ /* Speaker output mixer */
+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+ /* Outputs */
+ {"Mono Out", NULL, "Mono Mixer"},
+ {"MONOOUT", NULL, "Mono Out"},
+ {"SpkN Out", NULL, "Speaker Mixer"},
+ {"SpkP Out", NULL, "Speaker Mixer"},
+ {"SPKOUTN", NULL, "SpkN Out"},
+ {"SPKOUTP", NULL, "SpkP Out"},
+
+ /* Boost Mixer */
+ {"ADC", NULL, "Boost Mixer"},
+ {"Boost Mixer", "Aux Switch", "Aux Input"},
+ {"Boost Mixer", NULL, "Input PGA"},
+ {"Boost Mixer", NULL, "MICP"},
+
+ /* Input PGA */
+ {"Input PGA", "Aux Switch", "Aux Input"},
+ {"Input PGA", "MicN Switch", "MICN"},
+ {"Input PGA", "MicP Switch", "MICP"},
+
+ /* Inputs */
+ {"Aux Input", NULL, "AUX"},
+};
+
+struct pll_ {
+ unsigned int pre_div:1;
+ unsigned int n:4;
+ unsigned int k;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+
+static void pll_factors(struct pll_ *pll_div,
+ unsigned int target, unsigned int source)
+{
+ unsigned long long Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+ /* There is a fixed divide by 4 in the output path */
+ target *= 4;
+
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source /= 2;
+ pll_div->pre_div = 1;
+ Ndiv = target / source;
+ } else
+ pll_div->pre_div = 0;
+
+ if ((Ndiv < 6) || (Ndiv > 12))
+ printk(KERN_WARNING
+ "WM8974 N value %u outwith recommended range!\n",
+ Ndiv);
+
+ pll_div->n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div->k = K;
+}
+
+static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct pll_ pll_div;
+ u16 reg;
+
+ if (freq_in == 0 || freq_out == 0) {
+ /* Clock CODEC directly from MCLK */
+ reg = snd_soc_read(codec, WM8974_CLOCK);
+ snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
+
+ /* Turn off PLL */
+ reg = snd_soc_read(codec, WM8974_POWER1);
+ snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
+ return 0;
+ }
+
+ pll_factors(&pll_div, freq_out, freq_in);
+
+ snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
+ snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
+ snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
+ reg = snd_soc_read(codec, WM8974_POWER1);
+ snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
+
+ /* Run CODEC from PLL instead of MCLK */
+ reg = snd_soc_read(codec, WM8974_CLOCK);
+ snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
+
+ return 0;
+}
+
+/*
+ * Configure WM8974 clock dividers.
+ */
+static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8974_OPCLKDIV:
+ reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
+ snd_soc_write(codec, WM8974_GPIO, reg | div);
+ break;
+ case WM8974_MCLKDIV:
+ reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
+ snd_soc_write(codec, WM8974_CLOCK, reg | div);
+ break;
+ case WM8974_BCLKDIV:
+ reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
+ snd_soc_write(codec, WM8974_CLOCK, reg | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+ u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ clk |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0010;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0008;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x00018;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0180;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0100;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0080;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8974_IFACE, iface);
+ snd_soc_write(codec, WM8974_CLOCK, clk);
+ return 0;
+}
+
+static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
+ u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface |= 0x0020;
+ break;
+ case 24:
+ iface |= 0x0040;
+ break;
+ case 32:
+ iface |= 0x0060;
+ break;
+ }
+
+ /* filter coefficient */
+ switch (params_rate(params)) {
+ case 8000:
+ adn |= 0x5 << 1;
+ break;
+ case 11025:
+ adn |= 0x4 << 1;
+ break;
+ case 16000:
+ adn |= 0x3 << 1;
+ break;
+ case 22050:
+ adn |= 0x2 << 1;
+ break;
+ case 32000:
+ adn |= 0x1 << 1;
+ break;
+ case 44100:
+ case 48000:
+ break;
+ }
+
+ snd_soc_write(codec, WM8974_IFACE, iface);
+ snd_soc_write(codec, WM8974_ADD, adn);
+ return 0;
+}
+
+static int wm8974_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
+
+ if (mute)
+ snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
+ else
+ snd_soc_write(codec, WM8974_DAC, mute_reg);
+ return 0;
+}
+
+/* liam need to make this lower power with dapm */
+static int wm8974_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ power1 |= 0x1; /* VMID 50k */
+ snd_soc_write(codec, WM8974_POWER1, power1);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
+
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ regcache_sync(dev_get_regmap(codec->dev, NULL));
+
+ /* Initial cap charge at VMID 5k */
+ snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
+ mdelay(100);
+ }
+
+ power1 |= 0x2; /* VMID 500k */
+ snd_soc_write(codec, WM8974_POWER1, power1);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, WM8974_POWER1, 0);
+ snd_soc_write(codec, WM8974_POWER2, 0);
+ snd_soc_write(codec, WM8974_POWER3, 0);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8974_ops = {
+ .hw_params = wm8974_pcm_hw_params,
+ .digital_mute = wm8974_mute,
+ .set_fmt = wm8974_set_dai_fmt,
+ .set_clkdiv = wm8974_set_dai_clkdiv,
+ .set_pll = wm8974_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver wm8974_dai = {
+ .name = "wm8974-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2, /* Only 1 channel of data */
+ .rates = WM8974_RATES,
+ .formats = WM8974_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2, /* Only 1 channel of data */
+ .rates = WM8974_RATES,
+ .formats = WM8974_FORMATS,},
+ .ops = &wm8974_ops,
+ .symmetric_rates = 1,
+};
+
+static const struct regmap_config wm8974_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = WM8974_MONOMIX,
+ .reg_defaults = wm8974_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8974_reg_defaults),
+};
+
+static int wm8974_probe(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+
+ ret = wm8974_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
+ .probe = wm8974_probe,
+ .set_bias_level = wm8974_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8974_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8974_snd_controls),
+ .dapm_widgets = wm8974_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8974_dapm_widgets),
+ .dapm_routes = wm8974_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8974_dapm_routes),
+};
+
+static int wm8974_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8974, &wm8974_dai, 1);
+
+ return ret;
+}
+
+static int wm8974_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm8974_i2c_id[] = {
+ { "wm8974", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
+
+static struct i2c_driver wm8974_i2c_driver = {
+ .driver = {
+ .name = "wm8974",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8974_i2c_probe,
+ .remove = wm8974_i2c_remove,
+ .id_table = wm8974_i2c_id,
+};
+
+module_i2c_driver(wm8974_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8974 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8974.h b/kernel/sound/soc/codecs/wm8974.h
new file mode 100644
index 000000000..3c94e7bb5
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8974.h
@@ -0,0 +1,86 @@
+/*
+ * wm8974.h -- WM8974 Soc Audio driver
+ *
+ * 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 _WM8974_H
+#define _WM8974_H
+
+/* WM8974 register space */
+
+#define WM8974_RESET 0x0
+#define WM8974_POWER1 0x1
+#define WM8974_POWER2 0x2
+#define WM8974_POWER3 0x3
+#define WM8974_IFACE 0x4
+#define WM8974_COMP 0x5
+#define WM8974_CLOCK 0x6
+#define WM8974_ADD 0x7
+#define WM8974_GPIO 0x8
+#define WM8974_DAC 0xa
+#define WM8974_DACVOL 0xb
+#define WM8974_ADC 0xe
+#define WM8974_ADCVOL 0xf
+#define WM8974_EQ1 0x12
+#define WM8974_EQ2 0x13
+#define WM8974_EQ3 0x14
+#define WM8974_EQ4 0x15
+#define WM8974_EQ5 0x16
+#define WM8974_DACLIM1 0x18
+#define WM8974_DACLIM2 0x19
+#define WM8974_NOTCH1 0x1b
+#define WM8974_NOTCH2 0x1c
+#define WM8974_NOTCH3 0x1d
+#define WM8974_NOTCH4 0x1e
+#define WM8974_ALC1 0x20
+#define WM8974_ALC2 0x21
+#define WM8974_ALC3 0x22
+#define WM8974_NGATE 0x23
+#define WM8974_PLLN 0x24
+#define WM8974_PLLK1 0x25
+#define WM8974_PLLK2 0x26
+#define WM8974_PLLK3 0x27
+#define WM8974_ATTEN 0x28
+#define WM8974_INPUT 0x2c
+#define WM8974_INPPGA 0x2d
+#define WM8974_ADCBOOST 0x2f
+#define WM8974_OUTPUT 0x31
+#define WM8974_SPKMIX 0x32
+#define WM8974_SPKVOL 0x36
+#define WM8974_MONOMIX 0x38
+
+#define WM8974_CACHEREGNUM 57
+
+/* Clock divider Id's */
+#define WM8974_OPCLKDIV 0
+#define WM8974_MCLKDIV 1
+#define WM8974_BCLKDIV 2
+
+/* PLL Out dividers */
+#define WM8974_OPCLKDIV_1 (0 << 4)
+#define WM8974_OPCLKDIV_2 (1 << 4)
+#define WM8974_OPCLKDIV_3 (2 << 4)
+#define WM8974_OPCLKDIV_4 (3 << 4)
+
+/* BCLK clock dividers */
+#define WM8974_BCLKDIV_1 (0 << 2)
+#define WM8974_BCLKDIV_2 (1 << 2)
+#define WM8974_BCLKDIV_4 (2 << 2)
+#define WM8974_BCLKDIV_8 (3 << 2)
+#define WM8974_BCLKDIV_16 (4 << 2)
+#define WM8974_BCLKDIV_32 (5 << 2)
+
+/* MCLK clock dividers */
+#define WM8974_MCLKDIV_1 (0 << 5)
+#define WM8974_MCLKDIV_1_5 (1 << 5)
+#define WM8974_MCLKDIV_2 (2 << 5)
+#define WM8974_MCLKDIV_3 (3 << 5)
+#define WM8974_MCLKDIV_4 (4 << 5)
+#define WM8974_MCLKDIV_6 (5 << 5)
+#define WM8974_MCLKDIV_8 (6 << 5)
+#define WM8974_MCLKDIV_12 (7 << 5)
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8978.c b/kernel/sound/soc/codecs/wm8978.c
new file mode 100644
index 000000000..cf7032911
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8978.c
@@ -0,0 +1,1087 @@
+/*
+ * wm8978.c -- WM8978 ALSA SoC Audio Codec driver
+ *
+ * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2007 Carlos Munoz <carlos@kenati.com>
+ * Copyright 2006-2009 Wolfson Microelectronics PLC.
+ * Based on wm8974 and wm8990 by 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.
+ */
+
+#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 <asm/div64.h>
+
+#include "wm8978.h"
+
+static const struct reg_default wm8978_reg_defaults[] = {
+ { 1, 0x0000 },
+ { 2, 0x0000 },
+ { 3, 0x0000 },
+ { 4, 0x0050 },
+ { 5, 0x0000 },
+ { 6, 0x0140 },
+ { 7, 0x0000 },
+ { 8, 0x0000 },
+ { 9, 0x0000 },
+ { 10, 0x0000 },
+ { 11, 0x00ff },
+ { 12, 0x00ff },
+ { 13, 0x0000 },
+ { 14, 0x0100 },
+ { 15, 0x00ff },
+ { 16, 0x00ff },
+ { 17, 0x0000 },
+ { 18, 0x012c },
+ { 19, 0x002c },
+ { 20, 0x002c },
+ { 21, 0x002c },
+ { 22, 0x002c },
+ { 23, 0x0000 },
+ { 24, 0x0032 },
+ { 25, 0x0000 },
+ { 26, 0x0000 },
+ { 27, 0x0000 },
+ { 28, 0x0000 },
+ { 29, 0x0000 },
+ { 30, 0x0000 },
+ { 31, 0x0000 },
+ { 32, 0x0038 },
+ { 33, 0x000b },
+ { 34, 0x0032 },
+ { 35, 0x0000 },
+ { 36, 0x0008 },
+ { 37, 0x000c },
+ { 38, 0x0093 },
+ { 39, 0x00e9 },
+ { 40, 0x0000 },
+ { 41, 0x0000 },
+ { 42, 0x0000 },
+ { 43, 0x0000 },
+ { 44, 0x0033 },
+ { 45, 0x0010 },
+ { 46, 0x0010 },
+ { 47, 0x0100 },
+ { 48, 0x0100 },
+ { 49, 0x0002 },
+ { 50, 0x0001 },
+ { 51, 0x0001 },
+ { 52, 0x0039 },
+ { 53, 0x0039 },
+ { 54, 0x0039 },
+ { 55, 0x0039 },
+ { 56, 0x0001 },
+ { 57, 0x0001 },
+};
+
+static bool wm8978_volatile(struct device *dev, unsigned int reg)
+{
+ return reg == WM8978_RESET;
+}
+
+/* codec private data */
+struct wm8978_priv {
+ struct regmap *regmap;
+ unsigned int f_pllout;
+ unsigned int f_mclk;
+ unsigned int f_256fs;
+ unsigned int f_opclk;
+ int mclk_idx;
+ enum wm8978_sysclk_src sysclk;
+};
+
+static const char *wm8978_companding[] = {"Off", "NC", "u-law", "A-law"};
+static const char *wm8978_eqmode[] = {"Capture", "Playback"};
+static const char *wm8978_bw[] = {"Narrow", "Wide"};
+static const char *wm8978_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz"};
+static const char *wm8978_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz"};
+static const char *wm8978_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz"};
+static const char *wm8978_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz"};
+static const char *wm8978_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz"};
+static const char *wm8978_alc3[] = {"ALC", "Limiter"};
+static const char *wm8978_alc1[] = {"Off", "Right", "Left", "Both"};
+
+static SOC_ENUM_SINGLE_DECL(adc_compand, WM8978_COMPANDING_CONTROL, 1,
+ wm8978_companding);
+static SOC_ENUM_SINGLE_DECL(dac_compand, WM8978_COMPANDING_CONTROL, 3,
+ wm8978_companding);
+static SOC_ENUM_SINGLE_DECL(eqmode, WM8978_EQ1, 8, wm8978_eqmode);
+static SOC_ENUM_SINGLE_DECL(eq1, WM8978_EQ1, 5, wm8978_eq1);
+static SOC_ENUM_SINGLE_DECL(eq2bw, WM8978_EQ2, 8, wm8978_bw);
+static SOC_ENUM_SINGLE_DECL(eq2, WM8978_EQ2, 5, wm8978_eq2);
+static SOC_ENUM_SINGLE_DECL(eq3bw, WM8978_EQ3, 8, wm8978_bw);
+static SOC_ENUM_SINGLE_DECL(eq3, WM8978_EQ3, 5, wm8978_eq3);
+static SOC_ENUM_SINGLE_DECL(eq4bw, WM8978_EQ4, 8, wm8978_bw);
+static SOC_ENUM_SINGLE_DECL(eq4, WM8978_EQ4, 5, wm8978_eq4);
+static SOC_ENUM_SINGLE_DECL(eq5, WM8978_EQ5, 5, wm8978_eq5);
+static SOC_ENUM_SINGLE_DECL(alc3, WM8978_ALC_CONTROL_3, 8, wm8978_alc3);
+static SOC_ENUM_SINGLE_DECL(alc1, WM8978_ALC_CONTROL_1, 7, wm8978_alc1);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(limiter_tlv, 0, 100, 0);
+
+static const struct snd_kcontrol_new wm8978_snd_controls[] = {
+
+ SOC_SINGLE("Digital Loopback Switch",
+ WM8978_COMPANDING_CONTROL, 0, 1, 0),
+
+ SOC_ENUM("ADC Companding", adc_compand),
+ SOC_ENUM("DAC Companding", dac_compand),
+
+ SOC_DOUBLE("DAC Inversion Switch", WM8978_DAC_CONTROL, 0, 1, 1, 0),
+
+ SOC_DOUBLE_R_TLV("PCM Volume",
+ WM8978_LEFT_DAC_DIGITAL_VOLUME, WM8978_RIGHT_DAC_DIGITAL_VOLUME,
+ 0, 255, 0, digital_tlv),
+
+ SOC_SINGLE("High Pass Filter Switch", WM8978_ADC_CONTROL, 8, 1, 0),
+ SOC_SINGLE("High Pass Cut Off", WM8978_ADC_CONTROL, 4, 7, 0),
+ SOC_DOUBLE("ADC Inversion Switch", WM8978_ADC_CONTROL, 0, 1, 1, 0),
+
+ SOC_DOUBLE_R_TLV("ADC Volume",
+ WM8978_LEFT_ADC_DIGITAL_VOLUME, WM8978_RIGHT_ADC_DIGITAL_VOLUME,
+ 0, 255, 0, digital_tlv),
+
+ SOC_ENUM("Equaliser Function", eqmode),
+ SOC_ENUM("EQ1 Cut Off", eq1),
+ SOC_SINGLE_TLV("EQ1 Volume", WM8978_EQ1, 0, 24, 1, eq_tlv),
+
+ SOC_ENUM("Equaliser EQ2 Bandwidth", eq2bw),
+ SOC_ENUM("EQ2 Cut Off", eq2),
+ SOC_SINGLE_TLV("EQ2 Volume", WM8978_EQ2, 0, 24, 1, eq_tlv),
+
+ SOC_ENUM("Equaliser EQ3 Bandwidth", eq3bw),
+ SOC_ENUM("EQ3 Cut Off", eq3),
+ SOC_SINGLE_TLV("EQ3 Volume", WM8978_EQ3, 0, 24, 1, eq_tlv),
+
+ SOC_ENUM("Equaliser EQ4 Bandwidth", eq4bw),
+ SOC_ENUM("EQ4 Cut Off", eq4),
+ SOC_SINGLE_TLV("EQ4 Volume", WM8978_EQ4, 0, 24, 1, eq_tlv),
+
+ SOC_ENUM("EQ5 Cut Off", eq5),
+ SOC_SINGLE_TLV("EQ5 Volume", WM8978_EQ5, 0, 24, 1, eq_tlv),
+
+ SOC_SINGLE("DAC Playback Limiter Switch",
+ WM8978_DAC_LIMITER_1, 8, 1, 0),
+ SOC_SINGLE("DAC Playback Limiter Decay",
+ WM8978_DAC_LIMITER_1, 4, 15, 0),
+ SOC_SINGLE("DAC Playback Limiter Attack",
+ WM8978_DAC_LIMITER_1, 0, 15, 0),
+
+ SOC_SINGLE("DAC Playback Limiter Threshold",
+ WM8978_DAC_LIMITER_2, 4, 7, 0),
+ SOC_SINGLE_TLV("DAC Playback Limiter Volume",
+ WM8978_DAC_LIMITER_2, 0, 12, 0, limiter_tlv),
+
+ SOC_ENUM("ALC Enable Switch", alc1),
+ SOC_SINGLE("ALC Capture Min Gain", WM8978_ALC_CONTROL_1, 0, 7, 0),
+ SOC_SINGLE("ALC Capture Max Gain", WM8978_ALC_CONTROL_1, 3, 7, 0),
+
+ SOC_SINGLE("ALC Capture Hold", WM8978_ALC_CONTROL_2, 4, 10, 0),
+ SOC_SINGLE("ALC Capture Target", WM8978_ALC_CONTROL_2, 0, 15, 0),
+
+ SOC_ENUM("ALC Capture Mode", alc3),
+ SOC_SINGLE("ALC Capture Decay", WM8978_ALC_CONTROL_3, 4, 10, 0),
+ SOC_SINGLE("ALC Capture Attack", WM8978_ALC_CONTROL_3, 0, 10, 0),
+
+ SOC_SINGLE("ALC Capture Noise Gate Switch", WM8978_NOISE_GATE, 3, 1, 0),
+ SOC_SINGLE("ALC Capture Noise Gate Threshold",
+ WM8978_NOISE_GATE, 0, 7, 0),
+
+ SOC_DOUBLE_R("Capture PGA ZC Switch",
+ WM8978_LEFT_INP_PGA_CONTROL, WM8978_RIGHT_INP_PGA_CONTROL,
+ 7, 1, 0),
+
+ /* OUT1 - Headphones */
+ SOC_DOUBLE_R("Headphone Playback ZC Switch",
+ WM8978_LOUT1_HP_CONTROL, WM8978_ROUT1_HP_CONTROL, 7, 1, 0),
+
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume",
+ WM8978_LOUT1_HP_CONTROL, WM8978_ROUT1_HP_CONTROL,
+ 0, 63, 0, spk_tlv),
+
+ /* OUT2 - Speakers */
+ SOC_DOUBLE_R("Speaker Playback ZC Switch",
+ WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, 7, 1, 0),
+
+ SOC_DOUBLE_R_TLV("Speaker Playback Volume",
+ WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL,
+ 0, 63, 0, spk_tlv),
+
+ /* OUT3/4 - Line Output */
+ SOC_DOUBLE_R("Line Playback Switch",
+ WM8978_OUT3_MIXER_CONTROL, WM8978_OUT4_MIXER_CONTROL, 6, 1, 1),
+
+ /* Mixer #3: Boost (Input) mixer */
+ SOC_DOUBLE_R("PGA Boost (+20dB)",
+ WM8978_LEFT_ADC_BOOST_CONTROL, WM8978_RIGHT_ADC_BOOST_CONTROL,
+ 8, 1, 0),
+ SOC_DOUBLE_R_TLV("L2/R2 Boost Volume",
+ WM8978_LEFT_ADC_BOOST_CONTROL, WM8978_RIGHT_ADC_BOOST_CONTROL,
+ 4, 7, 0, boost_tlv),
+ SOC_DOUBLE_R_TLV("Aux Boost Volume",
+ WM8978_LEFT_ADC_BOOST_CONTROL, WM8978_RIGHT_ADC_BOOST_CONTROL,
+ 0, 7, 0, boost_tlv),
+
+ /* Input PGA volume */
+ SOC_DOUBLE_R_TLV("Input PGA Volume",
+ WM8978_LEFT_INP_PGA_CONTROL, WM8978_RIGHT_INP_PGA_CONTROL,
+ 0, 63, 0, inpga_tlv),
+
+ /* Headphone */
+ SOC_DOUBLE_R("Headphone Switch",
+ WM8978_LOUT1_HP_CONTROL, WM8978_ROUT1_HP_CONTROL, 6, 1, 1),
+
+ /* Speaker */
+ SOC_DOUBLE_R("Speaker Switch",
+ WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, 6, 1, 1),
+
+ /* DAC / ADC oversampling */
+ SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL,
+ 5, 1, 0),
+ SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL,
+ 5, 1, 0),
+};
+
+/* Mixer #1: Output (OUT1, OUT2) Mixer: mix AUX, Input mixer output and DAC */
+static const struct snd_kcontrol_new wm8978_left_out_mixer[] = {
+ SOC_DAPM_SINGLE("Line Bypass Switch", WM8978_LEFT_MIXER_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux Playback Switch", WM8978_LEFT_MIXER_CONTROL, 5, 1, 0),
+ SOC_DAPM_SINGLE("PCM Playback Switch", WM8978_LEFT_MIXER_CONTROL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8978_right_out_mixer[] = {
+ SOC_DAPM_SINGLE("Line Bypass Switch", WM8978_RIGHT_MIXER_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux Playback Switch", WM8978_RIGHT_MIXER_CONTROL, 5, 1, 0),
+ SOC_DAPM_SINGLE("PCM Playback Switch", WM8978_RIGHT_MIXER_CONTROL, 0, 1, 0),
+};
+
+/* OUT3/OUT4 Mixer not implemented */
+
+/* Mixer #2: Input PGA Mute */
+static const struct snd_kcontrol_new wm8978_left_input_mixer[] = {
+ SOC_DAPM_SINGLE("L2 Switch", WM8978_INPUT_CONTROL, 2, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", WM8978_INPUT_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", WM8978_INPUT_CONTROL, 0, 1, 0),
+};
+static const struct snd_kcontrol_new wm8978_right_input_mixer[] = {
+ SOC_DAPM_SINGLE("R2 Switch", WM8978_INPUT_CONTROL, 6, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", WM8978_INPUT_CONTROL, 5, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", WM8978_INPUT_CONTROL, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8978_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback",
+ WM8978_POWER_MANAGEMENT_3, 0, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback",
+ WM8978_POWER_MANAGEMENT_3, 1, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture",
+ WM8978_POWER_MANAGEMENT_2, 0, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture",
+ WM8978_POWER_MANAGEMENT_2, 1, 0),
+
+ /* Mixer #1: OUT1,2 */
+ SOC_MIXER_ARRAY("Left Output Mixer", WM8978_POWER_MANAGEMENT_3,
+ 2, 0, wm8978_left_out_mixer),
+ SOC_MIXER_ARRAY("Right Output Mixer", WM8978_POWER_MANAGEMENT_3,
+ 3, 0, wm8978_right_out_mixer),
+
+ SOC_MIXER_ARRAY("Left Input Mixer", WM8978_POWER_MANAGEMENT_2,
+ 2, 0, wm8978_left_input_mixer),
+ SOC_MIXER_ARRAY("Right Input Mixer", WM8978_POWER_MANAGEMENT_2,
+ 3, 0, wm8978_right_input_mixer),
+
+ SND_SOC_DAPM_PGA("Left Boost Mixer", WM8978_POWER_MANAGEMENT_2,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Boost Mixer", WM8978_POWER_MANAGEMENT_2,
+ 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Capture PGA", WM8978_LEFT_INP_PGA_CONTROL,
+ 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Capture PGA", WM8978_RIGHT_INP_PGA_CONTROL,
+ 6, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Headphone Out", WM8978_POWER_MANAGEMENT_2,
+ 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Headphone Out", WM8978_POWER_MANAGEMENT_2,
+ 8, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Speaker Out", WM8978_POWER_MANAGEMENT_3,
+ 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Speaker Out", WM8978_POWER_MANAGEMENT_3,
+ 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("OUT4 VMID", WM8978_POWER_MANAGEMENT_3,
+ 8, 0, NULL, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8978_POWER_MANAGEMENT_1, 4, 0),
+
+ SND_SOC_DAPM_INPUT("LMICN"),
+ SND_SOC_DAPM_INPUT("LMICP"),
+ SND_SOC_DAPM_INPUT("RMICN"),
+ SND_SOC_DAPM_INPUT("RMICP"),
+ SND_SOC_DAPM_INPUT("LAUX"),
+ SND_SOC_DAPM_INPUT("RAUX"),
+ SND_SOC_DAPM_INPUT("L2"),
+ SND_SOC_DAPM_INPUT("R2"),
+ SND_SOC_DAPM_OUTPUT("LHP"),
+ SND_SOC_DAPM_OUTPUT("RHP"),
+ SND_SOC_DAPM_OUTPUT("LSPK"),
+ SND_SOC_DAPM_OUTPUT("RSPK"),
+};
+
+static const struct snd_soc_dapm_route wm8978_dapm_routes[] = {
+ /* Output mixer */
+ {"Right Output Mixer", "PCM Playback Switch", "Right DAC"},
+ {"Right Output Mixer", "Aux Playback Switch", "RAUX"},
+ {"Right Output Mixer", "Line Bypass Switch", "Right Boost Mixer"},
+
+ {"Left Output Mixer", "PCM Playback Switch", "Left DAC"},
+ {"Left Output Mixer", "Aux Playback Switch", "LAUX"},
+ {"Left Output Mixer", "Line Bypass Switch", "Left Boost Mixer"},
+
+ /* Outputs */
+ {"Right Headphone Out", NULL, "Right Output Mixer"},
+ {"RHP", NULL, "Right Headphone Out"},
+
+ {"Left Headphone Out", NULL, "Left Output Mixer"},
+ {"LHP", NULL, "Left Headphone Out"},
+
+ {"Right Speaker Out", NULL, "Right Output Mixer"},
+ {"RSPK", NULL, "Right Speaker Out"},
+
+ {"Left Speaker Out", NULL, "Left Output Mixer"},
+ {"LSPK", NULL, "Left Speaker Out"},
+
+ /* Boost Mixer */
+ {"Right ADC", NULL, "Right Boost Mixer"},
+
+ {"Right Boost Mixer", NULL, "RAUX"},
+ {"Right Boost Mixer", NULL, "Right Capture PGA"},
+ {"Right Boost Mixer", NULL, "R2"},
+
+ {"Left ADC", NULL, "Left Boost Mixer"},
+
+ {"Left Boost Mixer", NULL, "LAUX"},
+ {"Left Boost Mixer", NULL, "Left Capture PGA"},
+ {"Left Boost Mixer", NULL, "L2"},
+
+ /* Input PGA */
+ {"Right Capture PGA", NULL, "Right Input Mixer"},
+ {"Left Capture PGA", NULL, "Left Input Mixer"},
+
+ {"Right Input Mixer", "R2 Switch", "R2"},
+ {"Right Input Mixer", "MicN Switch", "RMICN"},
+ {"Right Input Mixer", "MicP Switch", "RMICP"},
+
+ {"Left Input Mixer", "L2 Switch", "L2"},
+ {"Left Input Mixer", "MicN Switch", "LMICN"},
+ {"Left Input Mixer", "MicP Switch", "LMICP"},
+};
+
+/* PLL divisors */
+struct wm8978_pll_div {
+ u32 k;
+ u8 n;
+ u8 div2;
+};
+
+#define FIXED_PLL_SIZE (1 << 24)
+
+static void pll_factors(struct snd_soc_codec *codec,
+ struct wm8978_pll_div *pll_div, unsigned int target, unsigned int source)
+{
+ u64 k_part;
+ unsigned int k, n_div, n_mod;
+
+ n_div = target / source;
+ if (n_div < 6) {
+ source >>= 1;
+ pll_div->div2 = 1;
+ n_div = target / source;
+ } else {
+ pll_div->div2 = 0;
+ }
+
+ if (n_div < 6 || n_div > 12)
+ dev_warn(codec->dev,
+ "WM8978 N value exceeds recommended range! N = %u\n",
+ n_div);
+
+ pll_div->n = n_div;
+ n_mod = target - source * n_div;
+ k_part = FIXED_PLL_SIZE * (long long)n_mod + source / 2;
+
+ do_div(k_part, source);
+
+ k = k_part & 0xFFFFFFFF;
+
+ pll_div->k = k;
+}
+
+/* MCLK dividers */
+static const int mclk_numerator[] = {1, 3, 2, 3, 4, 6, 8, 12};
+static const int mclk_denominator[] = {1, 2, 1, 1, 1, 1, 1, 1};
+
+/*
+ * find index >= idx, such that, for a given f_out,
+ * 3 * f_mclk / 4 <= f_PLLOUT < 13 * f_mclk / 4
+ * f_out can be f_256fs or f_opclk, currently only used for f_256fs. Can be
+ * generalised for f_opclk with suitable coefficient arrays, but currently
+ * the OPCLK divisor is calculated directly, not iteratively.
+ */
+static int wm8978_enum_mclk(unsigned int f_out, unsigned int f_mclk,
+ unsigned int *f_pllout)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) {
+ unsigned int f_pllout_x4 = 4 * f_out * mclk_numerator[i] /
+ mclk_denominator[i];
+ if (3 * f_mclk <= f_pllout_x4 && f_pllout_x4 < 13 * f_mclk) {
+ *f_pllout = f_pllout_x4 / 4;
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Calculate internal frequencies and dividers, according to Figure 40
+ * "PLL and Clock Select Circuit" in WM8978 datasheet Rev. 2.6
+ */
+static int wm8978_configure_pll(struct snd_soc_codec *codec)
+{
+ struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ struct wm8978_pll_div pll_div;
+ unsigned int f_opclk = wm8978->f_opclk, f_mclk = wm8978->f_mclk,
+ f_256fs = wm8978->f_256fs;
+ unsigned int f2;
+
+ if (!f_mclk)
+ return -EINVAL;
+
+ if (f_opclk) {
+ unsigned int opclk_div;
+ /* Cannot set up MCLK divider now, do later */
+ wm8978->mclk_idx = -1;
+
+ /*
+ * The user needs OPCLK. Choose OPCLKDIV to put
+ * 6 <= R = f2 / f1 < 13, 1 <= OPCLKDIV <= 4.
+ * f_opclk = f_mclk * prescale * R / 4 / OPCLKDIV, where
+ * prescale = 1, or prescale = 2. Prescale is calculated inside
+ * pll_factors(). We have to select f_PLLOUT, such that
+ * f_mclk * 3 / 4 <= f_PLLOUT < f_mclk * 13 / 4. Must be
+ * f_mclk * 3 / 16 <= f_opclk < f_mclk * 13 / 4.
+ */
+ if (16 * f_opclk < 3 * f_mclk || 4 * f_opclk >= 13 * f_mclk)
+ return -EINVAL;
+
+ if (4 * f_opclk < 3 * f_mclk)
+ /* Have to use OPCLKDIV */
+ opclk_div = (3 * f_mclk / 4 + f_opclk - 1) / f_opclk;
+ else
+ opclk_div = 1;
+
+ dev_dbg(codec->dev, "%s: OPCLKDIV=%d\n", __func__, opclk_div);
+
+ snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 0x30,
+ (opclk_div - 1) << 4);
+
+ wm8978->f_pllout = f_opclk * opclk_div;
+ } else if (f_256fs) {
+ /*
+ * Not using OPCLK, but PLL is used for the codec, choose R:
+ * 6 <= R = f2 / f1 < 13, to put 1 <= MCLKDIV <= 12.
+ * f_256fs = f_mclk * prescale * R / 4 / MCLKDIV, where
+ * prescale = 1, or prescale = 2. Prescale is calculated inside
+ * pll_factors(). We have to select f_PLLOUT, such that
+ * f_mclk * 3 / 4 <= f_PLLOUT < f_mclk * 13 / 4. Must be
+ * f_mclk * 3 / 48 <= f_256fs < f_mclk * 13 / 4. This means MCLK
+ * must be 3.781MHz <= f_MCLK <= 32.768MHz
+ */
+ int idx = wm8978_enum_mclk(f_256fs, f_mclk, &wm8978->f_pllout);
+ if (idx < 0)
+ return idx;
+
+ wm8978->mclk_idx = idx;
+ } else {
+ return -EINVAL;
+ }
+
+ f2 = wm8978->f_pllout * 4;
+
+ dev_dbg(codec->dev, "%s: f_MCLK=%uHz, f_PLLOUT=%uHz\n", __func__,
+ wm8978->f_mclk, wm8978->f_pllout);
+
+ pll_factors(codec, &pll_div, f2, wm8978->f_mclk);
+
+ dev_dbg(codec->dev, "%s: calculated PLL N=0x%x, K=0x%x, div2=%d\n",
+ __func__, pll_div.n, pll_div.k, pll_div.div2);
+
+ /* Turn PLL off for configuration... */
+ snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0);
+
+ snd_soc_write(codec, WM8978_PLL_N, (pll_div.div2 << 4) | pll_div.n);
+ snd_soc_write(codec, WM8978_PLL_K1, pll_div.k >> 18);
+ snd_soc_write(codec, WM8978_PLL_K2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8978_PLL_K3, pll_div.k & 0x1ff);
+
+ /* ...and on again */
+ snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0x20);
+
+ if (f_opclk)
+ /* Output PLL (OPCLK) to GPIO1 */
+ snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 7, 4);
+
+ return 0;
+}
+
+/*
+ * Configure WM8978 clock dividers.
+ */
+static int wm8978_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ switch (div_id) {
+ case WM8978_OPCLKRATE:
+ wm8978->f_opclk = div;
+
+ if (wm8978->f_mclk)
+ /*
+ * We know the MCLK frequency, the user has requested
+ * OPCLK, configure the PLL based on that and start it
+ * and OPCLK immediately. We will configure PLL to match
+ * user-requested OPCLK frquency as good as possible.
+ * In fact, it is likely, that matching the sampling
+ * rate, when it becomes known, is more important, and
+ * we will not be reconfiguring PLL then, because we
+ * must not interrupt OPCLK. But it should be fine,
+ * because typically the user will request OPCLK to run
+ * at 256fs or 512fs, and for these cases we will also
+ * find an exact MCLK divider configuration - it will
+ * be equal to or double the OPCLK divisor.
+ */
+ ret = wm8978_configure_pll(codec);
+ break;
+ case WM8978_BCLKDIV:
+ if (div & ~0x1c)
+ return -EINVAL;
+ snd_soc_update_bits(codec, WM8978_CLOCKING, 0x1c, div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "%s: ID %d, value %u\n", __func__, div_id, div);
+
+ return ret;
+}
+
+/*
+ * @freq: when .set_pll() us not used, freq is codec MCLK input frequency
+ */
+static int wm8978_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s: ID %d, freq %u\n", __func__, clk_id, freq);
+
+ if (freq) {
+ wm8978->f_mclk = freq;
+
+ /* Even if MCLK is used for system clock, might have to drive OPCLK */
+ if (wm8978->f_opclk)
+ ret = wm8978_configure_pll(codec);
+
+ /* Our sysclk is fixed to 256 * fs, will configure in .hw_params() */
+
+ if (!ret)
+ wm8978->sysclk = clk_id;
+ }
+
+ if (wm8978->sysclk == WM8978_PLL && (!freq || clk_id == WM8978_MCLK)) {
+ /* Clock CODEC directly from MCLK */
+ snd_soc_update_bits(codec, WM8978_CLOCKING, 0x100, 0);
+
+ /* GPIO1 into default mode as input - before configuring PLL */
+ snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 7, 0);
+
+ /* Turn off PLL */
+ snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0);
+ wm8978->sysclk = WM8978_MCLK;
+ wm8978->f_pllout = 0;
+ wm8978->f_opclk = 0;
+ }
+
+ return ret;
+}
+
+/*
+ * Set ADC and Voice DAC format.
+ */
+static int wm8978_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ /*
+ * BCLK polarity mask = 0x100, LRC clock polarity mask = 0x80,
+ * Data Format mask = 0x18: all will be calculated anew
+ */
+ u16 iface = snd_soc_read(codec, WM8978_AUDIO_INTERFACE) & ~0x198;
+ u16 clk = snd_soc_read(codec, WM8978_CLOCKING);
+
+ dev_dbg(codec->dev, "%s\n", __func__);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ clk |= 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ clk &= ~1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x10;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x8;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x18;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x180;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x100;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8978_AUDIO_INTERFACE, iface);
+ snd_soc_write(codec, WM8978_CLOCKING, clk);
+
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8978_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ /* Word length mask = 0x60 */
+ u16 iface_ctl = snd_soc_read(codec, WM8978_AUDIO_INTERFACE) & ~0x60;
+ /* Sampling rate mask = 0xe (for filters) */
+ u16 add_ctl = snd_soc_read(codec, WM8978_ADDITIONAL_CONTROL) & ~0xe;
+ u16 clking = snd_soc_read(codec, WM8978_CLOCKING);
+ enum wm8978_sysclk_src current_clk_id = clking & 0x100 ?
+ WM8978_PLL : WM8978_MCLK;
+ unsigned int f_sel, diff, diff_best = INT_MAX;
+ int i, best = 0;
+
+ if (!wm8978->f_mclk)
+ return -EINVAL;
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface_ctl |= 0x20;
+ break;
+ case 24:
+ iface_ctl |= 0x40;
+ break;
+ case 32:
+ iface_ctl |= 0x60;
+ break;
+ }
+
+ /* filter coefficient */
+ switch (params_rate(params)) {
+ case 8000:
+ add_ctl |= 0x5 << 1;
+ break;
+ case 11025:
+ add_ctl |= 0x4 << 1;
+ break;
+ case 16000:
+ add_ctl |= 0x3 << 1;
+ break;
+ case 22050:
+ add_ctl |= 0x2 << 1;
+ break;
+ case 32000:
+ add_ctl |= 0x1 << 1;
+ break;
+ case 44100:
+ case 48000:
+ break;
+ }
+
+ /* Sampling rate is known now, can configure the MCLK divider */
+ wm8978->f_256fs = params_rate(params) * 256;
+
+ if (wm8978->sysclk == WM8978_MCLK) {
+ wm8978->mclk_idx = -1;
+ f_sel = wm8978->f_mclk;
+ } else {
+ if (!wm8978->f_opclk) {
+ /* We only enter here, if OPCLK is not used */
+ int ret = wm8978_configure_pll(codec);
+ if (ret < 0)
+ return ret;
+ }
+ f_sel = wm8978->f_pllout;
+ }
+
+ if (wm8978->mclk_idx < 0) {
+ /* Either MCLK is used directly, or OPCLK is used */
+ if (f_sel < wm8978->f_256fs || f_sel > 12 * wm8978->f_256fs)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) {
+ diff = abs(wm8978->f_256fs * 3 -
+ f_sel * 3 * mclk_denominator[i] / mclk_numerator[i]);
+
+ if (diff < diff_best) {
+ diff_best = diff;
+ best = i;
+ }
+
+ if (!diff)
+ break;
+ }
+ } else {
+ /* OPCLK not used, codec driven by PLL */
+ best = wm8978->mclk_idx;
+ diff = 0;
+ }
+
+ if (diff)
+ dev_warn(codec->dev, "Imprecise sampling rate: %uHz%s\n",
+ f_sel * mclk_denominator[best] / mclk_numerator[best] / 256,
+ wm8978->sysclk == WM8978_MCLK ?
+ ", consider using PLL" : "");
+
+ dev_dbg(codec->dev, "%s: width %d, rate %u, MCLK divisor #%d\n", __func__,
+ params_width(params), params_rate(params), best);
+
+ /* MCLK divisor mask = 0xe0 */
+ snd_soc_update_bits(codec, WM8978_CLOCKING, 0xe0, best << 5);
+
+ snd_soc_write(codec, WM8978_AUDIO_INTERFACE, iface_ctl);
+ snd_soc_write(codec, WM8978_ADDITIONAL_CONTROL, add_ctl);
+
+ if (wm8978->sysclk != current_clk_id) {
+ if (wm8978->sysclk == WM8978_PLL)
+ /* Run CODEC from PLL instead of MCLK */
+ snd_soc_update_bits(codec, WM8978_CLOCKING,
+ 0x100, 0x100);
+ else
+ /* Clock CODEC directly from MCLK */
+ snd_soc_update_bits(codec, WM8978_CLOCKING, 0x100, 0);
+ }
+
+ return 0;
+}
+
+static int wm8978_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ dev_dbg(codec->dev, "%s: %d\n", __func__, mute);
+
+ if (mute)
+ snd_soc_update_bits(codec, WM8978_DAC_CONTROL, 0x40, 0x40);
+ else
+ snd_soc_update_bits(codec, WM8978_DAC_CONTROL, 0x40, 0);
+
+ return 0;
+}
+
+static int wm8978_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 power1 = snd_soc_read(codec, WM8978_POWER_MANAGEMENT_1) & ~3;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ power1 |= 1; /* VMID 75k */
+ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* bit 3: enable bias, bit 2: enable I/O tie off buffer */
+ power1 |= 0xc;
+
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ /* Initial cap charge at VMID 5k */
+ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1,
+ power1 | 0x3);
+ mdelay(100);
+ }
+
+ power1 |= 0x2; /* VMID 500k */
+ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Preserve PLL - OPCLK may be used by someone */
+ snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, ~0x20, 0);
+ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_2, 0);
+ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_3, 0);
+ break;
+ }
+
+ dev_dbg(codec->dev, "%s: %d, %x\n", __func__, level, power1);
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8978_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8978_dai_ops = {
+ .hw_params = wm8978_hw_params,
+ .digital_mute = wm8978_mute,
+ .set_fmt = wm8978_set_dai_fmt,
+ .set_clkdiv = wm8978_set_dai_clkdiv,
+ .set_sysclk = wm8978_set_dai_sysclk,
+};
+
+/* Also supports 12kHz */
+static struct snd_soc_dai_driver wm8978_dai = {
+ .name = "wm8978-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = WM8978_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = WM8978_FORMATS,
+ },
+ .ops = &wm8978_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int wm8978_suspend(struct snd_soc_codec *codec)
+{
+ struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+
+ wm8978_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ /* Also switch PLL off */
+ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, 0);
+
+ regcache_mark_dirty(wm8978->regmap);
+
+ return 0;
+}
+
+static int wm8978_resume(struct snd_soc_codec *codec)
+{
+ struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+
+ /* Sync reg_cache with the hardware */
+ regcache_sync(wm8978->regmap);
+
+ wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ if (wm8978->f_pllout)
+ /* Switch PLL on */
+ snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0x20);
+
+ return 0;
+}
+
+/*
+ * These registers contain an "update" bit - bit 8. This means, for example,
+ * that one can write new DAC digital volume for both channels, but only when
+ * the update bit is set, will also the volume be updated - simultaneously for
+ * both channels.
+ */
+static const int update_reg[] = {
+ WM8978_LEFT_DAC_DIGITAL_VOLUME,
+ WM8978_RIGHT_DAC_DIGITAL_VOLUME,
+ WM8978_LEFT_ADC_DIGITAL_VOLUME,
+ WM8978_RIGHT_ADC_DIGITAL_VOLUME,
+ WM8978_LEFT_INP_PGA_CONTROL,
+ WM8978_RIGHT_INP_PGA_CONTROL,
+ WM8978_LOUT1_HP_CONTROL,
+ WM8978_ROUT1_HP_CONTROL,
+ WM8978_LOUT2_SPK_CONTROL,
+ WM8978_ROUT2_SPK_CONTROL,
+};
+
+static int wm8978_probe(struct snd_soc_codec *codec)
+{
+ struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ /*
+ * Set default system clock to PLL, it is more precise, this is also the
+ * default hardware setting
+ */
+ wm8978->sysclk = WM8978_PLL;
+
+ /*
+ * Set the update bit in all registers, that have one. This way all
+ * writes to those registers will also cause the update bit to be
+ * written.
+ */
+ for (i = 0; i < ARRAY_SIZE(update_reg); i++)
+ snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8978 = {
+ .probe = wm8978_probe,
+ .suspend = wm8978_suspend,
+ .resume = wm8978_resume,
+ .set_bias_level = wm8978_set_bias_level,
+
+ .controls = wm8978_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8978_snd_controls),
+ .dapm_widgets = wm8978_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8978_dapm_widgets),
+ .dapm_routes = wm8978_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8978_dapm_routes),
+};
+
+static const struct regmap_config wm8978_regmap_config = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = WM8978_MAX_REGISTER,
+ .volatile_reg = wm8978_volatile,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm8978_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8978_reg_defaults),
+};
+
+static int wm8978_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8978_priv *wm8978;
+ int ret;
+
+ wm8978 = devm_kzalloc(&i2c->dev, sizeof(struct wm8978_priv),
+ GFP_KERNEL);
+ if (wm8978 == NULL)
+ return -ENOMEM;
+
+ wm8978->regmap = devm_regmap_init_i2c(i2c, &wm8978_regmap_config);
+ if (IS_ERR(wm8978->regmap)) {
+ ret = PTR_ERR(wm8978->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, wm8978);
+
+ /* Reset the codec */
+ ret = regmap_write(wm8978->regmap, WM8978_RESET, 0);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8978, &wm8978_dai, 1);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wm8978_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm8978_i2c_id[] = {
+ { "wm8978", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8978_i2c_id);
+
+static struct i2c_driver wm8978_i2c_driver = {
+ .driver = {
+ .name = "wm8978",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8978_i2c_probe,
+ .remove = wm8978_i2c_remove,
+ .id_table = wm8978_i2c_id,
+};
+
+module_i2c_driver(wm8978_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8978 codec driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8978.h b/kernel/sound/soc/codecs/wm8978.h
new file mode 100644
index 000000000..6ae43495b
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8978.h
@@ -0,0 +1,85 @@
+/*
+ * wm8978.h -- codec driver for WM8978
+ *
+ * Copyright 2009 Guennadi Liakhovetski <g.liakhovetski@gmx.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.
+ */
+
+#ifndef __WM8978_H__
+#define __WM8978_H__
+
+/*
+ * Register values.
+ */
+#define WM8978_RESET 0x00
+#define WM8978_POWER_MANAGEMENT_1 0x01
+#define WM8978_POWER_MANAGEMENT_2 0x02
+#define WM8978_POWER_MANAGEMENT_3 0x03
+#define WM8978_AUDIO_INTERFACE 0x04
+#define WM8978_COMPANDING_CONTROL 0x05
+#define WM8978_CLOCKING 0x06
+#define WM8978_ADDITIONAL_CONTROL 0x07
+#define WM8978_GPIO_CONTROL 0x08
+#define WM8978_JACK_DETECT_CONTROL_1 0x09
+#define WM8978_DAC_CONTROL 0x0A
+#define WM8978_LEFT_DAC_DIGITAL_VOLUME 0x0B
+#define WM8978_RIGHT_DAC_DIGITAL_VOLUME 0x0C
+#define WM8978_JACK_DETECT_CONTROL_2 0x0D
+#define WM8978_ADC_CONTROL 0x0E
+#define WM8978_LEFT_ADC_DIGITAL_VOLUME 0x0F
+#define WM8978_RIGHT_ADC_DIGITAL_VOLUME 0x10
+#define WM8978_EQ1 0x12
+#define WM8978_EQ2 0x13
+#define WM8978_EQ3 0x14
+#define WM8978_EQ4 0x15
+#define WM8978_EQ5 0x16
+#define WM8978_DAC_LIMITER_1 0x18
+#define WM8978_DAC_LIMITER_2 0x19
+#define WM8978_NOTCH_FILTER_1 0x1b
+#define WM8978_NOTCH_FILTER_2 0x1c
+#define WM8978_NOTCH_FILTER_3 0x1d
+#define WM8978_NOTCH_FILTER_4 0x1e
+#define WM8978_ALC_CONTROL_1 0x20
+#define WM8978_ALC_CONTROL_2 0x21
+#define WM8978_ALC_CONTROL_3 0x22
+#define WM8978_NOISE_GATE 0x23
+#define WM8978_PLL_N 0x24
+#define WM8978_PLL_K1 0x25
+#define WM8978_PLL_K2 0x26
+#define WM8978_PLL_K3 0x27
+#define WM8978_3D_CONTROL 0x29
+#define WM8978_BEEP_CONTROL 0x2b
+#define WM8978_INPUT_CONTROL 0x2c
+#define WM8978_LEFT_INP_PGA_CONTROL 0x2d
+#define WM8978_RIGHT_INP_PGA_CONTROL 0x2e
+#define WM8978_LEFT_ADC_BOOST_CONTROL 0x2f
+#define WM8978_RIGHT_ADC_BOOST_CONTROL 0x30
+#define WM8978_OUTPUT_CONTROL 0x31
+#define WM8978_LEFT_MIXER_CONTROL 0x32
+#define WM8978_RIGHT_MIXER_CONTROL 0x33
+#define WM8978_LOUT1_HP_CONTROL 0x34
+#define WM8978_ROUT1_HP_CONTROL 0x35
+#define WM8978_LOUT2_SPK_CONTROL 0x36
+#define WM8978_ROUT2_SPK_CONTROL 0x37
+#define WM8978_OUT3_MIXER_CONTROL 0x38
+#define WM8978_OUT4_MIXER_CONTROL 0x39
+
+#define WM8978_MAX_REGISTER 0x39
+
+#define WM8978_CACHEREGNUM 58
+
+/* Clock divider Id's */
+enum wm8978_clk_id {
+ WM8978_OPCLKRATE,
+ WM8978_BCLKDIV,
+};
+
+enum wm8978_sysclk_src {
+ WM8978_PLL,
+ WM8978_MCLK
+};
+
+#endif /* __WM8978_H__ */
diff --git a/kernel/sound/soc/codecs/wm8983.c b/kernel/sound/soc/codecs/wm8983.c
new file mode 100644
index 000000000..5d1cf08a7
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8983.c
@@ -0,0 +1,1180 @@
+/*
+ * wm8983.c -- WM8983 ALSA SoC Audio driver
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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/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/spi/spi.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 "wm8983.h"
+
+static const struct reg_default wm8983_defaults[] = {
+ { 0x01, 0x0000 }, /* R1 - Power management 1 */
+ { 0x02, 0x0000 }, /* R2 - Power management 2 */
+ { 0x03, 0x0000 }, /* R3 - Power management 3 */
+ { 0x04, 0x0050 }, /* R4 - Audio Interface */
+ { 0x05, 0x0000 }, /* R5 - Companding control */
+ { 0x06, 0x0140 }, /* R6 - Clock Gen control */
+ { 0x07, 0x0000 }, /* R7 - Additional control */
+ { 0x08, 0x0000 }, /* R8 - GPIO Control */
+ { 0x09, 0x0000 }, /* R9 - Jack Detect Control 1 */
+ { 0x0A, 0x0000 }, /* R10 - DAC Control */
+ { 0x0B, 0x00FF }, /* R11 - Left DAC digital Vol */
+ { 0x0C, 0x00FF }, /* R12 - Right DAC digital vol */
+ { 0x0D, 0x0000 }, /* R13 - Jack Detect Control 2 */
+ { 0x0E, 0x0100 }, /* R14 - ADC Control */
+ { 0x0F, 0x00FF }, /* R15 - Left ADC Digital Vol */
+ { 0x10, 0x00FF }, /* R16 - Right ADC Digital Vol */
+ { 0x12, 0x012C }, /* R18 - EQ1 - low shelf */
+ { 0x13, 0x002C }, /* R19 - EQ2 - peak 1 */
+ { 0x14, 0x002C }, /* R20 - EQ3 - peak 2 */
+ { 0x15, 0x002C }, /* R21 - EQ4 - peak 3 */
+ { 0x16, 0x002C }, /* R22 - EQ5 - high shelf */
+ { 0x18, 0x0032 }, /* R24 - DAC Limiter 1 */
+ { 0x19, 0x0000 }, /* R25 - DAC Limiter 2 */
+ { 0x1B, 0x0000 }, /* R27 - Notch Filter 1 */
+ { 0x1C, 0x0000 }, /* R28 - Notch Filter 2 */
+ { 0x1D, 0x0000 }, /* R29 - Notch Filter 3 */
+ { 0x1E, 0x0000 }, /* R30 - Notch Filter 4 */
+ { 0x20, 0x0038 }, /* R32 - ALC control 1 */
+ { 0x21, 0x000B }, /* R33 - ALC control 2 */
+ { 0x22, 0x0032 }, /* R34 - ALC control 3 */
+ { 0x23, 0x0000 }, /* R35 - Noise Gate */
+ { 0x24, 0x0008 }, /* R36 - PLL N */
+ { 0x25, 0x000C }, /* R37 - PLL K 1 */
+ { 0x26, 0x0093 }, /* R38 - PLL K 2 */
+ { 0x27, 0x00E9 }, /* R39 - PLL K 3 */
+ { 0x29, 0x0000 }, /* R41 - 3D control */
+ { 0x2A, 0x0000 }, /* R42 - OUT4 to ADC */
+ { 0x2B, 0x0000 }, /* R43 - Beep control */
+ { 0x2C, 0x0033 }, /* R44 - Input ctrl */
+ { 0x2D, 0x0010 }, /* R45 - Left INP PGA gain ctrl */
+ { 0x2E, 0x0010 }, /* R46 - Right INP PGA gain ctrl */
+ { 0x2F, 0x0100 }, /* R47 - Left ADC BOOST ctrl */
+ { 0x30, 0x0100 }, /* R48 - Right ADC BOOST ctrl */
+ { 0x31, 0x0002 }, /* R49 - Output ctrl */
+ { 0x32, 0x0001 }, /* R50 - Left mixer ctrl */
+ { 0x33, 0x0001 }, /* R51 - Right mixer ctrl */
+ { 0x34, 0x0039 }, /* R52 - LOUT1 (HP) volume ctrl */
+ { 0x35, 0x0039 }, /* R53 - ROUT1 (HP) volume ctrl */
+ { 0x36, 0x0039 }, /* R54 - LOUT2 (SPK) volume ctrl */
+ { 0x37, 0x0039 }, /* R55 - ROUT2 (SPK) volume ctrl */
+ { 0x38, 0x0001 }, /* R56 - OUT3 mixer ctrl */
+ { 0x39, 0x0001 }, /* R57 - OUT4 (MONO) mix ctrl */
+ { 0x3D, 0x0000 }, /* R61 - BIAS CTRL */
+};
+
+static const struct wm8983_reg_access {
+ u16 read; /* Mask of readable bits */
+ u16 write; /* Mask of writable bits */
+} wm8983_access_masks[WM8983_MAX_REGISTER + 1] = {
+ [0x00] = { 0x0000, 0x01FF }, /* R0 - Software Reset */
+ [0x01] = { 0x0000, 0x01FF }, /* R1 - Power management 1 */
+ [0x02] = { 0x0000, 0x01FF }, /* R2 - Power management 2 */
+ [0x03] = { 0x0000, 0x01EF }, /* R3 - Power management 3 */
+ [0x04] = { 0x0000, 0x01FF }, /* R4 - Audio Interface */
+ [0x05] = { 0x0000, 0x003F }, /* R5 - Companding control */
+ [0x06] = { 0x0000, 0x01FD }, /* R6 - Clock Gen control */
+ [0x07] = { 0x0000, 0x000F }, /* R7 - Additional control */
+ [0x08] = { 0x0000, 0x003F }, /* R8 - GPIO Control */
+ [0x09] = { 0x0000, 0x0070 }, /* R9 - Jack Detect Control 1 */
+ [0x0A] = { 0x0000, 0x004F }, /* R10 - DAC Control */
+ [0x0B] = { 0x0000, 0x01FF }, /* R11 - Left DAC digital Vol */
+ [0x0C] = { 0x0000, 0x01FF }, /* R12 - Right DAC digital vol */
+ [0x0D] = { 0x0000, 0x00FF }, /* R13 - Jack Detect Control 2 */
+ [0x0E] = { 0x0000, 0x01FB }, /* R14 - ADC Control */
+ [0x0F] = { 0x0000, 0x01FF }, /* R15 - Left ADC Digital Vol */
+ [0x10] = { 0x0000, 0x01FF }, /* R16 - Right ADC Digital Vol */
+ [0x12] = { 0x0000, 0x017F }, /* R18 - EQ1 - low shelf */
+ [0x13] = { 0x0000, 0x017F }, /* R19 - EQ2 - peak 1 */
+ [0x14] = { 0x0000, 0x017F }, /* R20 - EQ3 - peak 2 */
+ [0x15] = { 0x0000, 0x017F }, /* R21 - EQ4 - peak 3 */
+ [0x16] = { 0x0000, 0x007F }, /* R22 - EQ5 - high shelf */
+ [0x18] = { 0x0000, 0x01FF }, /* R24 - DAC Limiter 1 */
+ [0x19] = { 0x0000, 0x007F }, /* R25 - DAC Limiter 2 */
+ [0x1B] = { 0x0000, 0x01FF }, /* R27 - Notch Filter 1 */
+ [0x1C] = { 0x0000, 0x017F }, /* R28 - Notch Filter 2 */
+ [0x1D] = { 0x0000, 0x017F }, /* R29 - Notch Filter 3 */
+ [0x1E] = { 0x0000, 0x017F }, /* R30 - Notch Filter 4 */
+ [0x20] = { 0x0000, 0x01BF }, /* R32 - ALC control 1 */
+ [0x21] = { 0x0000, 0x00FF }, /* R33 - ALC control 2 */
+ [0x22] = { 0x0000, 0x01FF }, /* R34 - ALC control 3 */
+ [0x23] = { 0x0000, 0x000F }, /* R35 - Noise Gate */
+ [0x24] = { 0x0000, 0x001F }, /* R36 - PLL N */
+ [0x25] = { 0x0000, 0x003F }, /* R37 - PLL K 1 */
+ [0x26] = { 0x0000, 0x01FF }, /* R38 - PLL K 2 */
+ [0x27] = { 0x0000, 0x01FF }, /* R39 - PLL K 3 */
+ [0x29] = { 0x0000, 0x000F }, /* R41 - 3D control */
+ [0x2A] = { 0x0000, 0x01E7 }, /* R42 - OUT4 to ADC */
+ [0x2B] = { 0x0000, 0x01BF }, /* R43 - Beep control */
+ [0x2C] = { 0x0000, 0x0177 }, /* R44 - Input ctrl */
+ [0x2D] = { 0x0000, 0x01FF }, /* R45 - Left INP PGA gain ctrl */
+ [0x2E] = { 0x0000, 0x01FF }, /* R46 - Right INP PGA gain ctrl */
+ [0x2F] = { 0x0000, 0x0177 }, /* R47 - Left ADC BOOST ctrl */
+ [0x30] = { 0x0000, 0x0177 }, /* R48 - Right ADC BOOST ctrl */
+ [0x31] = { 0x0000, 0x007F }, /* R49 - Output ctrl */
+ [0x32] = { 0x0000, 0x01FF }, /* R50 - Left mixer ctrl */
+ [0x33] = { 0x0000, 0x01FF }, /* R51 - Right mixer ctrl */
+ [0x34] = { 0x0000, 0x01FF }, /* R52 - LOUT1 (HP) volume ctrl */
+ [0x35] = { 0x0000, 0x01FF }, /* R53 - ROUT1 (HP) volume ctrl */
+ [0x36] = { 0x0000, 0x01FF }, /* R54 - LOUT2 (SPK) volume ctrl */
+ [0x37] = { 0x0000, 0x01FF }, /* R55 - ROUT2 (SPK) volume ctrl */
+ [0x38] = { 0x0000, 0x004F }, /* R56 - OUT3 mixer ctrl */
+ [0x39] = { 0x0000, 0x00FF }, /* R57 - OUT4 (MONO) mix ctrl */
+ [0x3D] = { 0x0000, 0x0100 } /* R61 - BIAS CTRL */
+};
+
+/* vol/gain update regs */
+static const int vol_update_regs[] = {
+ WM8983_LEFT_DAC_DIGITAL_VOL,
+ WM8983_RIGHT_DAC_DIGITAL_VOL,
+ WM8983_LEFT_ADC_DIGITAL_VOL,
+ WM8983_RIGHT_ADC_DIGITAL_VOL,
+ WM8983_LOUT1_HP_VOLUME_CTRL,
+ WM8983_ROUT1_HP_VOLUME_CTRL,
+ WM8983_LOUT2_SPK_VOLUME_CTRL,
+ WM8983_ROUT2_SPK_VOLUME_CTRL,
+ WM8983_LEFT_INP_PGA_GAIN_CTRL,
+ WM8983_RIGHT_INP_PGA_GAIN_CTRL
+};
+
+struct wm8983_priv {
+ struct regmap *regmap;
+ u32 sysclk;
+ u32 bclk;
+};
+
+static const struct {
+ int div;
+ int ratio;
+} fs_ratios[] = {
+ { 10, 128 },
+ { 15, 192 },
+ { 20, 256 },
+ { 30, 384 },
+ { 40, 512 },
+ { 60, 768 },
+ { 80, 1024 },
+ { 120, 1536 }
+};
+
+static const int srates[] = { 48000, 32000, 24000, 16000, 12000, 8000 };
+
+static const int bclk_divs[] = {
+ 1, 2, 4, 8, 16, 32
+};
+
+static int eqmode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int eqmode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(lim_thresh_tlv, -600, 100, 0);
+static const DECLARE_TLV_DB_SCALE(lim_boost_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(alc_min_tlv, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(alc_max_tlv, -675, 600, 0);
+static const DECLARE_TLV_DB_SCALE(alc_tar_tlv, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(pga_vol_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(aux_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0);
+
+static const char *alc_sel_text[] = { "Off", "Right", "Left", "Stereo" };
+static SOC_ENUM_SINGLE_DECL(alc_sel, WM8983_ALC_CONTROL_1, 7, alc_sel_text);
+
+static const char *alc_mode_text[] = { "ALC", "Limiter" };
+static SOC_ENUM_SINGLE_DECL(alc_mode, WM8983_ALC_CONTROL_3, 8, alc_mode_text);
+
+static const char *filter_mode_text[] = { "Audio", "Application" };
+static SOC_ENUM_SINGLE_DECL(filter_mode, WM8983_ADC_CONTROL, 7,
+ filter_mode_text);
+
+static const char *eq_bw_text[] = { "Narrow", "Wide" };
+static const char *eqmode_text[] = { "Capture", "Playback" };
+static SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text);
+
+static const char *eq1_cutoff_text[] = {
+ "80Hz", "105Hz", "135Hz", "175Hz"
+};
+static SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8983_EQ1_LOW_SHELF, 5,
+ eq1_cutoff_text);
+static const char *eq2_cutoff_text[] = {
+ "230Hz", "300Hz", "385Hz", "500Hz"
+};
+static SOC_ENUM_SINGLE_DECL(eq2_bw, WM8983_EQ2_PEAK_1, 8, eq_bw_text);
+static SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8983_EQ2_PEAK_1, 5, eq2_cutoff_text);
+static const char *eq3_cutoff_text[] = {
+ "650Hz", "850Hz", "1.1kHz", "1.4kHz"
+};
+static SOC_ENUM_SINGLE_DECL(eq3_bw, WM8983_EQ3_PEAK_2, 8, eq_bw_text);
+static SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8983_EQ3_PEAK_2, 5, eq3_cutoff_text);
+static const char *eq4_cutoff_text[] = {
+ "1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz"
+};
+static SOC_ENUM_SINGLE_DECL(eq4_bw, WM8983_EQ4_PEAK_3, 8, eq_bw_text);
+static SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8983_EQ4_PEAK_3, 5, eq4_cutoff_text);
+static const char *eq5_cutoff_text[] = {
+ "5.3kHz", "6.9kHz", "9kHz", "11.7kHz"
+};
+static SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8983_EQ5_HIGH_SHELF, 5,
+ eq5_cutoff_text);
+
+static const char *depth_3d_text[] = {
+ "Off",
+ "6.67%",
+ "13.3%",
+ "20%",
+ "26.7%",
+ "33.3%",
+ "40%",
+ "46.6%",
+ "53.3%",
+ "60%",
+ "66.7%",
+ "73.3%",
+ "80%",
+ "86.7%",
+ "93.3%",
+ "100%"
+};
+static SOC_ENUM_SINGLE_DECL(depth_3d, WM8983_3D_CONTROL, 0,
+ depth_3d_text);
+
+static const struct snd_kcontrol_new wm8983_snd_controls[] = {
+ SOC_SINGLE("Digital Loopback Switch", WM8983_COMPANDING_CONTROL,
+ 0, 1, 0),
+
+ SOC_ENUM("ALC Capture Function", alc_sel),
+ SOC_SINGLE_TLV("ALC Capture Max Volume", WM8983_ALC_CONTROL_1,
+ 3, 7, 0, alc_max_tlv),
+ SOC_SINGLE_TLV("ALC Capture Min Volume", WM8983_ALC_CONTROL_1,
+ 0, 7, 0, alc_min_tlv),
+ SOC_SINGLE_TLV("ALC Capture Target Volume", WM8983_ALC_CONTROL_2,
+ 0, 15, 0, alc_tar_tlv),
+ SOC_SINGLE("ALC Capture Attack", WM8983_ALC_CONTROL_3, 0, 10, 0),
+ SOC_SINGLE("ALC Capture Hold", WM8983_ALC_CONTROL_2, 4, 10, 0),
+ SOC_SINGLE("ALC Capture Decay", WM8983_ALC_CONTROL_3, 4, 10, 0),
+ SOC_ENUM("ALC Mode", alc_mode),
+ SOC_SINGLE("ALC Capture NG Switch", WM8983_NOISE_GATE,
+ 3, 1, 0),
+ SOC_SINGLE("ALC Capture NG Threshold", WM8983_NOISE_GATE,
+ 0, 7, 1),
+
+ SOC_DOUBLE_R_TLV("Capture Volume", WM8983_LEFT_ADC_DIGITAL_VOL,
+ WM8983_RIGHT_ADC_DIGITAL_VOL, 0, 255, 0, adc_tlv),
+ SOC_DOUBLE_R("Capture PGA ZC Switch", WM8983_LEFT_INP_PGA_GAIN_CTRL,
+ WM8983_RIGHT_INP_PGA_GAIN_CTRL, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("Capture PGA Volume", WM8983_LEFT_INP_PGA_GAIN_CTRL,
+ WM8983_RIGHT_INP_PGA_GAIN_CTRL, 0, 63, 0, pga_vol_tlv),
+
+ SOC_DOUBLE_R_TLV("Capture PGA Boost Volume",
+ WM8983_LEFT_ADC_BOOST_CTRL, WM8983_RIGHT_ADC_BOOST_CTRL,
+ 8, 1, 0, pga_boost_tlv),
+
+ SOC_DOUBLE("ADC Inversion Switch", WM8983_ADC_CONTROL, 0, 1, 1, 0),
+ SOC_SINGLE("ADC 128x Oversampling Switch", WM8983_ADC_CONTROL, 8, 1, 0),
+
+ SOC_DOUBLE_R_TLV("Playback Volume", WM8983_LEFT_DAC_DIGITAL_VOL,
+ WM8983_RIGHT_DAC_DIGITAL_VOL, 0, 255, 0, dac_tlv),
+
+ SOC_SINGLE("DAC Playback Limiter Switch", WM8983_DAC_LIMITER_1, 8, 1, 0),
+ SOC_SINGLE("DAC Playback Limiter Decay", WM8983_DAC_LIMITER_1, 4, 10, 0),
+ SOC_SINGLE("DAC Playback Limiter Attack", WM8983_DAC_LIMITER_1, 0, 11, 0),
+ SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8983_DAC_LIMITER_2,
+ 4, 7, 1, lim_thresh_tlv),
+ SOC_SINGLE_TLV("DAC Playback Limiter Boost Volume", WM8983_DAC_LIMITER_2,
+ 0, 12, 0, lim_boost_tlv),
+ SOC_DOUBLE("DAC Inversion Switch", WM8983_DAC_CONTROL, 0, 1, 1, 0),
+ SOC_SINGLE("DAC Auto Mute Switch", WM8983_DAC_CONTROL, 2, 1, 0),
+ SOC_SINGLE("DAC 128x Oversampling Switch", WM8983_DAC_CONTROL, 3, 1, 0),
+
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8983_LOUT1_HP_VOLUME_CTRL,
+ WM8983_ROUT1_HP_VOLUME_CTRL, 0, 63, 0, out_tlv),
+ SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8983_LOUT1_HP_VOLUME_CTRL,
+ WM8983_ROUT1_HP_VOLUME_CTRL, 7, 1, 0),
+ SOC_DOUBLE_R("Headphone Switch", WM8983_LOUT1_HP_VOLUME_CTRL,
+ WM8983_ROUT1_HP_VOLUME_CTRL, 6, 1, 1),
+
+ SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8983_LOUT2_SPK_VOLUME_CTRL,
+ WM8983_ROUT2_SPK_VOLUME_CTRL, 0, 63, 0, out_tlv),
+ SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8983_LOUT2_SPK_VOLUME_CTRL,
+ WM8983_ROUT2_SPK_VOLUME_CTRL, 7, 1, 0),
+ SOC_DOUBLE_R("Speaker Switch", WM8983_LOUT2_SPK_VOLUME_CTRL,
+ WM8983_ROUT2_SPK_VOLUME_CTRL, 6, 1, 1),
+
+ SOC_SINGLE("OUT3 Switch", WM8983_OUT3_MIXER_CTRL,
+ 6, 1, 1),
+
+ SOC_SINGLE("OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL,
+ 6, 1, 1),
+
+ SOC_SINGLE("High Pass Filter Switch", WM8983_ADC_CONTROL, 8, 1, 0),
+ SOC_ENUM("High Pass Filter Mode", filter_mode),
+ SOC_SINGLE("High Pass Filter Cutoff", WM8983_ADC_CONTROL, 4, 7, 0),
+
+ SOC_DOUBLE_R_TLV("Aux Bypass Volume",
+ WM8983_LEFT_MIXER_CTRL, WM8983_RIGHT_MIXER_CTRL, 6, 7, 0,
+ aux_tlv),
+
+ SOC_DOUBLE_R_TLV("Input PGA Bypass Volume",
+ WM8983_LEFT_MIXER_CTRL, WM8983_RIGHT_MIXER_CTRL, 2, 7, 0,
+ bypass_tlv),
+
+ SOC_ENUM_EXT("Equalizer Function", eqmode, eqmode_get, eqmode_put),
+ SOC_ENUM("EQ1 Cutoff", eq1_cutoff),
+ SOC_SINGLE_TLV("EQ1 Volume", WM8983_EQ1_LOW_SHELF, 0, 24, 1, eq_tlv),
+ SOC_ENUM("EQ2 Bandwidth", eq2_bw),
+ SOC_ENUM("EQ2 Cutoff", eq2_cutoff),
+ SOC_SINGLE_TLV("EQ2 Volume", WM8983_EQ2_PEAK_1, 0, 24, 1, eq_tlv),
+ SOC_ENUM("EQ3 Bandwidth", eq3_bw),
+ SOC_ENUM("EQ3 Cutoff", eq3_cutoff),
+ SOC_SINGLE_TLV("EQ3 Volume", WM8983_EQ3_PEAK_2, 0, 24, 1, eq_tlv),
+ SOC_ENUM("EQ4 Bandwidth", eq4_bw),
+ SOC_ENUM("EQ4 Cutoff", eq4_cutoff),
+ SOC_SINGLE_TLV("EQ4 Volume", WM8983_EQ4_PEAK_3, 0, 24, 1, eq_tlv),
+ SOC_ENUM("EQ5 Cutoff", eq5_cutoff),
+ SOC_SINGLE_TLV("EQ5 Volume", WM8983_EQ5_HIGH_SHELF, 0, 24, 1, eq_tlv),
+
+ SOC_ENUM("3D Depth", depth_3d),
+};
+
+static const struct snd_kcontrol_new left_out_mixer[] = {
+ SOC_DAPM_SINGLE("Line Switch", WM8983_LEFT_MIXER_CTRL, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux Switch", WM8983_LEFT_MIXER_CTRL, 5, 1, 0),
+ SOC_DAPM_SINGLE("PCM Switch", WM8983_LEFT_MIXER_CTRL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_out_mixer[] = {
+ SOC_DAPM_SINGLE("Line Switch", WM8983_RIGHT_MIXER_CTRL, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux Switch", WM8983_RIGHT_MIXER_CTRL, 5, 1, 0),
+ SOC_DAPM_SINGLE("PCM Switch", WM8983_RIGHT_MIXER_CTRL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_input_mixer[] = {
+ SOC_DAPM_SINGLE("L2 Switch", WM8983_INPUT_CTRL, 2, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", WM8983_INPUT_CTRL, 1, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", WM8983_INPUT_CTRL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_input_mixer[] = {
+ SOC_DAPM_SINGLE("R2 Switch", WM8983_INPUT_CTRL, 6, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", WM8983_INPUT_CTRL, 5, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", WM8983_INPUT_CTRL, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_boost_mixer[] = {
+ SOC_DAPM_SINGLE_TLV("L2 Volume", WM8983_LEFT_ADC_BOOST_CTRL,
+ 4, 7, 0, boost_tlv),
+ SOC_DAPM_SINGLE_TLV("AUXL Volume", WM8983_LEFT_ADC_BOOST_CTRL,
+ 0, 7, 0, boost_tlv)
+};
+
+static const struct snd_kcontrol_new out3_mixer[] = {
+ SOC_DAPM_SINGLE("LMIX2OUT3 Switch", WM8983_OUT3_MIXER_CTRL,
+ 1, 1, 0),
+ SOC_DAPM_SINGLE("LDAC2OUT3 Switch", WM8983_OUT3_MIXER_CTRL,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new out4_mixer[] = {
+ SOC_DAPM_SINGLE("LMIX2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL,
+ 4, 1, 0),
+ SOC_DAPM_SINGLE("RMIX2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL,
+ 1, 1, 0),
+ SOC_DAPM_SINGLE("LDAC2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL,
+ 3, 1, 0),
+ SOC_DAPM_SINGLE("RDAC2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_boost_mixer[] = {
+ SOC_DAPM_SINGLE_TLV("R2 Volume", WM8983_RIGHT_ADC_BOOST_CTRL,
+ 4, 7, 0, boost_tlv),
+ SOC_DAPM_SINGLE_TLV("AUXR Volume", WM8983_RIGHT_ADC_BOOST_CTRL,
+ 0, 7, 0, boost_tlv)
+};
+
+static const struct snd_soc_dapm_widget wm8983_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8983_POWER_MANAGEMENT_3,
+ 0, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8983_POWER_MANAGEMENT_3,
+ 1, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8983_POWER_MANAGEMENT_2,
+ 0, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8983_POWER_MANAGEMENT_2,
+ 1, 0),
+
+ SND_SOC_DAPM_MIXER("Left Output Mixer", WM8983_POWER_MANAGEMENT_3,
+ 2, 0, left_out_mixer, ARRAY_SIZE(left_out_mixer)),
+ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8983_POWER_MANAGEMENT_3,
+ 3, 0, right_out_mixer, ARRAY_SIZE(right_out_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left Input Mixer", WM8983_POWER_MANAGEMENT_2,
+ 2, 0, left_input_mixer, ARRAY_SIZE(left_input_mixer)),
+ SND_SOC_DAPM_MIXER("Right Input Mixer", WM8983_POWER_MANAGEMENT_2,
+ 3, 0, right_input_mixer, ARRAY_SIZE(right_input_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8983_POWER_MANAGEMENT_2,
+ 4, 0, left_boost_mixer, ARRAY_SIZE(left_boost_mixer)),
+ SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8983_POWER_MANAGEMENT_2,
+ 5, 0, right_boost_mixer, ARRAY_SIZE(right_boost_mixer)),
+
+ SND_SOC_DAPM_MIXER("OUT3 Mixer", WM8983_POWER_MANAGEMENT_1,
+ 6, 0, out3_mixer, ARRAY_SIZE(out3_mixer)),
+
+ SND_SOC_DAPM_MIXER("OUT4 Mixer", WM8983_POWER_MANAGEMENT_1,
+ 7, 0, out4_mixer, ARRAY_SIZE(out4_mixer)),
+
+ SND_SOC_DAPM_PGA("Left Capture PGA", WM8983_LEFT_INP_PGA_GAIN_CTRL,
+ 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Capture PGA", WM8983_RIGHT_INP_PGA_GAIN_CTRL,
+ 6, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Headphone Out", WM8983_POWER_MANAGEMENT_2,
+ 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Headphone Out", WM8983_POWER_MANAGEMENT_2,
+ 8, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Speaker Out", WM8983_POWER_MANAGEMENT_3,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Speaker Out", WM8983_POWER_MANAGEMENT_3,
+ 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("OUT3 Out", WM8983_POWER_MANAGEMENT_3,
+ 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("OUT4 Out", WM8983_POWER_MANAGEMENT_3,
+ 8, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", WM8983_POWER_MANAGEMENT_1, 4, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_INPUT("LIN"),
+ SND_SOC_DAPM_INPUT("LIP"),
+ SND_SOC_DAPM_INPUT("RIN"),
+ SND_SOC_DAPM_INPUT("RIP"),
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+ SND_SOC_DAPM_INPUT("L2"),
+ SND_SOC_DAPM_INPUT("R2"),
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("OUT4")
+};
+
+static const struct snd_soc_dapm_route wm8983_audio_map[] = {
+ { "OUT3 Mixer", "LMIX2OUT3 Switch", "Left Output Mixer" },
+ { "OUT3 Mixer", "LDAC2OUT3 Switch", "Left DAC" },
+
+ { "OUT3 Out", NULL, "OUT3 Mixer" },
+ { "OUT3", NULL, "OUT3 Out" },
+
+ { "OUT4 Mixer", "LMIX2OUT4 Switch", "Left Output Mixer" },
+ { "OUT4 Mixer", "RMIX2OUT4 Switch", "Right Output Mixer" },
+ { "OUT4 Mixer", "LDAC2OUT4 Switch", "Left DAC" },
+ { "OUT4 Mixer", "RDAC2OUT4 Switch", "Right DAC" },
+
+ { "OUT4 Out", NULL, "OUT4 Mixer" },
+ { "OUT4", NULL, "OUT4 Out" },
+
+ { "Right Output Mixer", "PCM Switch", "Right DAC" },
+ { "Right Output Mixer", "Aux Switch", "AUXR" },
+ { "Right Output Mixer", "Line Switch", "Right Boost Mixer" },
+
+ { "Left Output Mixer", "PCM Switch", "Left DAC" },
+ { "Left Output Mixer", "Aux Switch", "AUXL" },
+ { "Left Output Mixer", "Line Switch", "Left Boost Mixer" },
+
+ { "Right Headphone Out", NULL, "Right Output Mixer" },
+ { "HPR", NULL, "Right Headphone Out" },
+
+ { "Left Headphone Out", NULL, "Left Output Mixer" },
+ { "HPL", NULL, "Left Headphone Out" },
+
+ { "Right Speaker Out", NULL, "Right Output Mixer" },
+ { "SPKR", NULL, "Right Speaker Out" },
+
+ { "Left Speaker Out", NULL, "Left Output Mixer" },
+ { "SPKL", NULL, "Left Speaker Out" },
+
+ { "Right ADC", NULL, "Right Boost Mixer" },
+
+ { "Right Boost Mixer", "AUXR Volume", "AUXR" },
+ { "Right Boost Mixer", NULL, "Right Capture PGA" },
+ { "Right Boost Mixer", "R2 Volume", "R2" },
+
+ { "Left ADC", NULL, "Left Boost Mixer" },
+
+ { "Left Boost Mixer", "AUXL Volume", "AUXL" },
+ { "Left Boost Mixer", NULL, "Left Capture PGA" },
+ { "Left Boost Mixer", "L2 Volume", "L2" },
+
+ { "Right Capture PGA", NULL, "Right Input Mixer" },
+ { "Left Capture PGA", NULL, "Left Input Mixer" },
+
+ { "Right Input Mixer", "R2 Switch", "R2" },
+ { "Right Input Mixer", "MicN Switch", "RIN" },
+ { "Right Input Mixer", "MicP Switch", "RIP" },
+
+ { "Left Input Mixer", "L2 Switch", "L2" },
+ { "Left Input Mixer", "MicN Switch", "LIN" },
+ { "Left Input Mixer", "MicP Switch", "LIP" },
+};
+
+static int eqmode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, WM8983_EQ1_LOW_SHELF);
+ if (reg & WM8983_EQ3DMODE)
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int eqmode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int regpwr2, regpwr3;
+ unsigned int reg_eq;
+
+ if (ucontrol->value.integer.value[0] != 0
+ && ucontrol->value.integer.value[0] != 1)
+ return -EINVAL;
+
+ reg_eq = snd_soc_read(codec, WM8983_EQ1_LOW_SHELF);
+ switch ((reg_eq & WM8983_EQ3DMODE) >> WM8983_EQ3DMODE_SHIFT) {
+ case 0:
+ if (!ucontrol->value.integer.value[0])
+ return 0;
+ break;
+ case 1:
+ if (ucontrol->value.integer.value[0])
+ return 0;
+ break;
+ }
+
+ regpwr2 = snd_soc_read(codec, WM8983_POWER_MANAGEMENT_2);
+ regpwr3 = snd_soc_read(codec, WM8983_POWER_MANAGEMENT_3);
+ /* disable the DACs and ADCs */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_2,
+ WM8983_ADCENR_MASK | WM8983_ADCENL_MASK, 0);
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_3,
+ WM8983_DACENR_MASK | WM8983_DACENL_MASK, 0);
+ /* set the desired eqmode */
+ snd_soc_update_bits(codec, WM8983_EQ1_LOW_SHELF,
+ WM8983_EQ3DMODE_MASK,
+ ucontrol->value.integer.value[0]
+ << WM8983_EQ3DMODE_SHIFT);
+ /* restore DAC/ADC configuration */
+ snd_soc_write(codec, WM8983_POWER_MANAGEMENT_2, regpwr2);
+ snd_soc_write(codec, WM8983_POWER_MANAGEMENT_3, regpwr3);
+ return 0;
+}
+
+static bool wm8983_readable(struct device *dev, unsigned int reg)
+{
+ if (reg > WM8983_MAX_REGISTER)
+ return 0;
+
+ return wm8983_access_masks[reg].read != 0;
+}
+
+static int wm8983_dac_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ return snd_soc_update_bits(codec, WM8983_DAC_CONTROL,
+ WM8983_SOFTMUTE_MASK,
+ !!mute << WM8983_SOFTMUTE_SHIFT);
+}
+
+static int wm8983_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 format, master, bcp, lrp;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = 0x2;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ format = 0x0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = 0x1;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ format = 0x3;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown dai format\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE,
+ WM8983_FMT_MASK, format << WM8983_FMT_SHIFT);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ master = 0;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown master/slave configuration\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ WM8983_MS_MASK, master << WM8983_MS_SHIFT);
+
+ /* FIXME: We don't currently support DSP A/B modes */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ dev_err(dai->dev, "DSP A/B modes are not supported\n");
+ return -EINVAL;
+ default:
+ break;
+ }
+
+ bcp = lrp = 0;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bcp = lrp = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bcp = 1;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrp = 1;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown polarity configuration\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE,
+ WM8983_LRCP_MASK, lrp << WM8983_LRCP_SHIFT);
+ snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE,
+ WM8983_BCP_MASK, bcp << WM8983_BCP_SHIFT);
+ return 0;
+}
+
+static int wm8983_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int i;
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8983_priv *wm8983 = snd_soc_codec_get_drvdata(codec);
+ u16 blen, srate_idx;
+ u32 tmp;
+ int srate_best;
+ int ret;
+
+ ret = snd_soc_params_to_bclk(params);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to convert params to bclk: %d\n", ret);
+ return ret;
+ }
+
+ wm8983->bclk = ret;
+
+ switch (params_width(params)) {
+ case 16:
+ blen = 0x0;
+ break;
+ case 20:
+ blen = 0x1;
+ break;
+ case 24:
+ blen = 0x2;
+ break;
+ case 32:
+ blen = 0x3;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported word length %u\n",
+ params_width(params));
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8983_AUDIO_INTERFACE,
+ WM8983_WL_MASK, blen << WM8983_WL_SHIFT);
+
+ /*
+ * match to the nearest possible sample rate and rely
+ * on the array index to configure the SR register
+ */
+ srate_idx = 0;
+ srate_best = abs(srates[0] - params_rate(params));
+ for (i = 1; i < ARRAY_SIZE(srates); ++i) {
+ if (abs(srates[i] - params_rate(params)) >= srate_best)
+ continue;
+ srate_idx = i;
+ srate_best = abs(srates[i] - params_rate(params));
+ }
+
+ dev_dbg(dai->dev, "Selected SRATE = %d\n", srates[srate_idx]);
+ snd_soc_update_bits(codec, WM8983_ADDITIONAL_CONTROL,
+ WM8983_SR_MASK, srate_idx << WM8983_SR_SHIFT);
+
+ dev_dbg(dai->dev, "Target BCLK = %uHz\n", wm8983->bclk);
+ dev_dbg(dai->dev, "SYSCLK = %uHz\n", wm8983->sysclk);
+
+ for (i = 0; i < ARRAY_SIZE(fs_ratios); ++i) {
+ if (wm8983->sysclk / params_rate(params)
+ == fs_ratios[i].ratio)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(fs_ratios)) {
+ dev_err(dai->dev, "Unable to configure MCLK ratio %u/%u\n",
+ wm8983->sysclk, params_rate(params));
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "MCLK ratio = %dfs\n", fs_ratios[i].ratio);
+ snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ WM8983_MCLKDIV_MASK, i << WM8983_MCLKDIV_SHIFT);
+
+ /* select the appropriate bclk divider */
+ tmp = (wm8983->sysclk / fs_ratios[i].div) * 10;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); ++i) {
+ if (wm8983->bclk == tmp / bclk_divs[i])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(bclk_divs)) {
+ dev_err(dai->dev, "No matching BCLK divider found\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "BCLK div = %d\n", i);
+ snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ WM8983_BCLKDIV_MASK, i << WM8983_BCLKDIV_SHIFT);
+
+ return 0;
+}
+
+struct pll_div {
+ u32 div2:1;
+ u32 n:4;
+ u32 k:24;
+};
+
+#define FIXED_PLL_SIZE ((1ULL << 24) * 10)
+static int pll_factors(struct pll_div *pll_div, unsigned int target,
+ unsigned int source)
+{
+ u64 Kpart;
+ unsigned long int K, Ndiv, Nmod;
+
+ pll_div->div2 = 0;
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div->div2 = 1;
+ Ndiv = target / source;
+ }
+
+ if (Ndiv < 6 || Ndiv > 12) {
+ printk(KERN_ERR "%s: WM8983 N value is not within"
+ " the recommended range: %lu\n", __func__, Ndiv);
+ return -EINVAL;
+ }
+ pll_div->n = Ndiv;
+
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (u64)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xffffffff;
+ if ((K % 10) >= 5)
+ K += 5;
+ K /= 10;
+ pll_div->k = K;
+ return 0;
+}
+
+static int wm8983_set_pll(struct snd_soc_dai *dai, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ int ret;
+ struct snd_soc_codec *codec;
+ struct pll_div pll_div;
+
+ codec = dai->codec;
+ if (!freq_in || !freq_out) {
+ /* disable the PLL */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_PLLEN_MASK, 0);
+ return 0;
+ } else {
+ ret = pll_factors(&pll_div, freq_out * 4 * 2, freq_in);
+ if (ret)
+ return ret;
+
+ /* disable the PLL before re-programming it */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_PLLEN_MASK, 0);
+
+ /* set PLLN and PRESCALE */
+ snd_soc_write(codec, WM8983_PLL_N,
+ (pll_div.div2 << WM8983_PLL_PRESCALE_SHIFT)
+ | pll_div.n);
+ /* set PLLK */
+ snd_soc_write(codec, WM8983_PLL_K_3, pll_div.k & 0x1ff);
+ snd_soc_write(codec, WM8983_PLL_K_2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8983_PLL_K_1, (pll_div.k >> 18));
+ /* enable the PLL */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_PLLEN_MASK, WM8983_PLLEN);
+ }
+
+ return 0;
+}
+
+static int wm8983_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8983_priv *wm8983 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case WM8983_CLKSRC_MCLK:
+ snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ WM8983_CLKSEL_MASK, 0);
+ break;
+ case WM8983_CLKSRC_PLL:
+ snd_soc_update_bits(codec, WM8983_CLOCK_GEN_CONTROL,
+ WM8983_CLKSEL_MASK, WM8983_CLKSEL);
+ break;
+ default:
+ dev_err(dai->dev, "Unknown clock source: %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ wm8983->sysclk = freq;
+ return 0;
+}
+
+static int wm8983_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8983_priv *wm8983 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID at 100k */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_VMIDSEL_MASK,
+ 1 << WM8983_VMIDSEL_SHIFT);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regcache_sync(wm8983->regmap);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ /* enable anti-pop features */
+ snd_soc_update_bits(codec, WM8983_OUT4_TO_ADC,
+ WM8983_POBCTRL_MASK | WM8983_DELEN_MASK,
+ WM8983_POBCTRL | WM8983_DELEN);
+ /* enable thermal shutdown */
+ snd_soc_update_bits(codec, WM8983_OUTPUT_CTRL,
+ WM8983_TSDEN_MASK, WM8983_TSDEN);
+ /* enable BIASEN */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_BIASEN_MASK, WM8983_BIASEN);
+ /* VMID at 100k */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_VMIDSEL_MASK,
+ 1 << WM8983_VMIDSEL_SHIFT);
+ msleep(250);
+ /* disable anti-pop features */
+ snd_soc_update_bits(codec, WM8983_OUT4_TO_ADC,
+ WM8983_POBCTRL_MASK |
+ WM8983_DELEN_MASK, 0);
+ }
+
+ /* VMID at 500k */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_VMIDSEL_MASK,
+ 2 << WM8983_VMIDSEL_SHIFT);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* disable thermal shutdown */
+ snd_soc_update_bits(codec, WM8983_OUTPUT_CTRL,
+ WM8983_TSDEN_MASK, 0);
+ /* disable VMIDSEL and BIASEN */
+ snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+ WM8983_VMIDSEL_MASK | WM8983_BIASEN_MASK,
+ 0);
+ /* wait for VMID to discharge */
+ msleep(100);
+ snd_soc_write(codec, WM8983_POWER_MANAGEMENT_1, 0);
+ snd_soc_write(codec, WM8983_POWER_MANAGEMENT_2, 0);
+ snd_soc_write(codec, WM8983_POWER_MANAGEMENT_3, 0);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int wm8983_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+ int i;
+
+ ret = snd_soc_write(codec, WM8983_SOFTWARE_RESET, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
+
+ /* set the vol/gain update bits */
+ for (i = 0; i < ARRAY_SIZE(vol_update_regs); ++i)
+ snd_soc_update_bits(codec, vol_update_regs[i],
+ 0x100, 0x100);
+
+ /* mute all outputs and set PGAs to minimum gain */
+ for (i = WM8983_LOUT1_HP_VOLUME_CTRL;
+ i <= WM8983_OUT4_MONO_MIX_CTRL; ++i)
+ snd_soc_update_bits(codec, i, 0x40, 0x40);
+
+ /* enable soft mute */
+ snd_soc_update_bits(codec, WM8983_DAC_CONTROL,
+ WM8983_SOFTMUTE_MASK,
+ WM8983_SOFTMUTE);
+
+ /* enable BIASCUT */
+ snd_soc_update_bits(codec, WM8983_BIAS_CTRL,
+ WM8983_BIASCUT, WM8983_BIASCUT);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops wm8983_dai_ops = {
+ .digital_mute = wm8983_dac_mute,
+ .hw_params = wm8983_hw_params,
+ .set_fmt = wm8983_set_fmt,
+ .set_sysclk = wm8983_set_sysclk,
+ .set_pll = wm8983_set_pll
+};
+
+#define WM8983_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver wm8983_dai = {
+ .name = "wm8983-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = WM8983_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = WM8983_FORMATS,
+ },
+ .ops = &wm8983_dai_ops,
+ .symmetric_rates = 1
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8983 = {
+ .probe = wm8983_probe,
+ .set_bias_level = wm8983_set_bias_level,
+ .suspend_bias_off = true,
+ .controls = wm8983_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8983_snd_controls),
+ .dapm_widgets = wm8983_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8983_dapm_widgets),
+ .dapm_routes = wm8983_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm8983_audio_map),
+};
+
+static const struct regmap_config wm8983_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .reg_defaults = wm8983_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8983_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .readable_reg = wm8983_readable,
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8983_spi_probe(struct spi_device *spi)
+{
+ struct wm8983_priv *wm8983;
+ int ret;
+
+ wm8983 = devm_kzalloc(&spi->dev, sizeof *wm8983, GFP_KERNEL);
+ if (!wm8983)
+ return -ENOMEM;
+
+ wm8983->regmap = devm_regmap_init_spi(spi, &wm8983_regmap);
+ if (IS_ERR(wm8983->regmap)) {
+ ret = PTR_ERR(wm8983->regmap);
+ dev_err(&spi->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ spi_set_drvdata(spi, wm8983);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8983, &wm8983_dai, 1);
+ return ret;
+}
+
+static int wm8983_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver wm8983_spi_driver = {
+ .driver = {
+ .name = "wm8983",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8983_spi_probe,
+ .remove = wm8983_spi_remove
+};
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8983_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8983_priv *wm8983;
+ int ret;
+
+ wm8983 = devm_kzalloc(&i2c->dev, sizeof *wm8983, GFP_KERNEL);
+ if (!wm8983)
+ return -ENOMEM;
+
+ wm8983->regmap = devm_regmap_init_i2c(i2c, &wm8983_regmap);
+ if (IS_ERR(wm8983->regmap)) {
+ ret = PTR_ERR(wm8983->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, wm8983);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8983, &wm8983_dai, 1);
+
+ return ret;
+}
+
+static int wm8983_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8983_i2c_id[] = {
+ { "wm8983", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8983_i2c_id);
+
+static struct i2c_driver wm8983_i2c_driver = {
+ .driver = {
+ .name = "wm8983",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8983_i2c_probe,
+ .remove = wm8983_i2c_remove,
+ .id_table = wm8983_i2c_id
+};
+#endif
+
+static int __init wm8983_modinit(void)
+{
+ int ret = 0;
+
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8983_i2c_driver);
+ if (ret) {
+ printk(KERN_ERR "Failed to register wm8983 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8983_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8983 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(wm8983_modinit);
+
+static void __exit wm8983_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8983_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8983_spi_driver);
+#endif
+}
+module_exit(wm8983_exit);
+
+MODULE_DESCRIPTION("ASoC WM8983 driver");
+MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8983.h b/kernel/sound/soc/codecs/wm8983.h
new file mode 100644
index 000000000..71ee619c2
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8983.h
@@ -0,0 +1,1029 @@
+/*
+ * wm8983.h -- WM8983 ALSA SoC Audio driver
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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 _WM8983_H
+#define _WM8983_H
+
+/*
+ * Register values.
+ */
+#define WM8983_SOFTWARE_RESET 0x00
+#define WM8983_POWER_MANAGEMENT_1 0x01
+#define WM8983_POWER_MANAGEMENT_2 0x02
+#define WM8983_POWER_MANAGEMENT_3 0x03
+#define WM8983_AUDIO_INTERFACE 0x04
+#define WM8983_COMPANDING_CONTROL 0x05
+#define WM8983_CLOCK_GEN_CONTROL 0x06
+#define WM8983_ADDITIONAL_CONTROL 0x07
+#define WM8983_GPIO_CONTROL 0x08
+#define WM8983_JACK_DETECT_CONTROL_1 0x09
+#define WM8983_DAC_CONTROL 0x0A
+#define WM8983_LEFT_DAC_DIGITAL_VOL 0x0B
+#define WM8983_RIGHT_DAC_DIGITAL_VOL 0x0C
+#define WM8983_JACK_DETECT_CONTROL_2 0x0D
+#define WM8983_ADC_CONTROL 0x0E
+#define WM8983_LEFT_ADC_DIGITAL_VOL 0x0F
+#define WM8983_RIGHT_ADC_DIGITAL_VOL 0x10
+#define WM8983_EQ1_LOW_SHELF 0x12
+#define WM8983_EQ2_PEAK_1 0x13
+#define WM8983_EQ3_PEAK_2 0x14
+#define WM8983_EQ4_PEAK_3 0x15
+#define WM8983_EQ5_HIGH_SHELF 0x16
+#define WM8983_DAC_LIMITER_1 0x18
+#define WM8983_DAC_LIMITER_2 0x19
+#define WM8983_NOTCH_FILTER_1 0x1B
+#define WM8983_NOTCH_FILTER_2 0x1C
+#define WM8983_NOTCH_FILTER_3 0x1D
+#define WM8983_NOTCH_FILTER_4 0x1E
+#define WM8983_ALC_CONTROL_1 0x20
+#define WM8983_ALC_CONTROL_2 0x21
+#define WM8983_ALC_CONTROL_3 0x22
+#define WM8983_NOISE_GATE 0x23
+#define WM8983_PLL_N 0x24
+#define WM8983_PLL_K_1 0x25
+#define WM8983_PLL_K_2 0x26
+#define WM8983_PLL_K_3 0x27
+#define WM8983_3D_CONTROL 0x29
+#define WM8983_OUT4_TO_ADC 0x2A
+#define WM8983_BEEP_CONTROL 0x2B
+#define WM8983_INPUT_CTRL 0x2C
+#define WM8983_LEFT_INP_PGA_GAIN_CTRL 0x2D
+#define WM8983_RIGHT_INP_PGA_GAIN_CTRL 0x2E
+#define WM8983_LEFT_ADC_BOOST_CTRL 0x2F
+#define WM8983_RIGHT_ADC_BOOST_CTRL 0x30
+#define WM8983_OUTPUT_CTRL 0x31
+#define WM8983_LEFT_MIXER_CTRL 0x32
+#define WM8983_RIGHT_MIXER_CTRL 0x33
+#define WM8983_LOUT1_HP_VOLUME_CTRL 0x34
+#define WM8983_ROUT1_HP_VOLUME_CTRL 0x35
+#define WM8983_LOUT2_SPK_VOLUME_CTRL 0x36
+#define WM8983_ROUT2_SPK_VOLUME_CTRL 0x37
+#define WM8983_OUT3_MIXER_CTRL 0x38
+#define WM8983_OUT4_MONO_MIX_CTRL 0x39
+#define WM8983_BIAS_CTRL 0x3D
+
+#define WM8983_REGISTER_COUNT 59
+#define WM8983_MAX_REGISTER 0x3F
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM8983_SOFTWARE_RESET_MASK 0x01FF /* SOFTWARE_RESET - [8:0] */
+#define WM8983_SOFTWARE_RESET_SHIFT 0 /* SOFTWARE_RESET - [8:0] */
+#define WM8983_SOFTWARE_RESET_WIDTH 9 /* SOFTWARE_RESET - [8:0] */
+
+/*
+ * R1 (0x01) - Power management 1
+ */
+#define WM8983_BUFDCOPEN 0x0100 /* BUFDCOPEN */
+#define WM8983_BUFDCOPEN_MASK 0x0100 /* BUFDCOPEN */
+#define WM8983_BUFDCOPEN_SHIFT 8 /* BUFDCOPEN */
+#define WM8983_BUFDCOPEN_WIDTH 1 /* BUFDCOPEN */
+#define WM8983_OUT4MIXEN 0x0080 /* OUT4MIXEN */
+#define WM8983_OUT4MIXEN_MASK 0x0080 /* OUT4MIXEN */
+#define WM8983_OUT4MIXEN_SHIFT 7 /* OUT4MIXEN */
+#define WM8983_OUT4MIXEN_WIDTH 1 /* OUT4MIXEN */
+#define WM8983_OUT3MIXEN 0x0040 /* OUT3MIXEN */
+#define WM8983_OUT3MIXEN_MASK 0x0040 /* OUT3MIXEN */
+#define WM8983_OUT3MIXEN_SHIFT 6 /* OUT3MIXEN */
+#define WM8983_OUT3MIXEN_WIDTH 1 /* OUT3MIXEN */
+#define WM8983_PLLEN 0x0020 /* PLLEN */
+#define WM8983_PLLEN_MASK 0x0020 /* PLLEN */
+#define WM8983_PLLEN_SHIFT 5 /* PLLEN */
+#define WM8983_PLLEN_WIDTH 1 /* PLLEN */
+#define WM8983_MICBEN 0x0010 /* MICBEN */
+#define WM8983_MICBEN_MASK 0x0010 /* MICBEN */
+#define WM8983_MICBEN_SHIFT 4 /* MICBEN */
+#define WM8983_MICBEN_WIDTH 1 /* MICBEN */
+#define WM8983_BIASEN 0x0008 /* BIASEN */
+#define WM8983_BIASEN_MASK 0x0008 /* BIASEN */
+#define WM8983_BIASEN_SHIFT 3 /* BIASEN */
+#define WM8983_BIASEN_WIDTH 1 /* BIASEN */
+#define WM8983_BUFIOEN 0x0004 /* BUFIOEN */
+#define WM8983_BUFIOEN_MASK 0x0004 /* BUFIOEN */
+#define WM8983_BUFIOEN_SHIFT 2 /* BUFIOEN */
+#define WM8983_BUFIOEN_WIDTH 1 /* BUFIOEN */
+#define WM8983_VMIDSEL_MASK 0x0003 /* VMIDSEL - [1:0] */
+#define WM8983_VMIDSEL_SHIFT 0 /* VMIDSEL - [1:0] */
+#define WM8983_VMIDSEL_WIDTH 2 /* VMIDSEL - [1:0] */
+
+/*
+ * R2 (0x02) - Power management 2
+ */
+#define WM8983_ROUT1EN 0x0100 /* ROUT1EN */
+#define WM8983_ROUT1EN_MASK 0x0100 /* ROUT1EN */
+#define WM8983_ROUT1EN_SHIFT 8 /* ROUT1EN */
+#define WM8983_ROUT1EN_WIDTH 1 /* ROUT1EN */
+#define WM8983_LOUT1EN 0x0080 /* LOUT1EN */
+#define WM8983_LOUT1EN_MASK 0x0080 /* LOUT1EN */
+#define WM8983_LOUT1EN_SHIFT 7 /* LOUT1EN */
+#define WM8983_LOUT1EN_WIDTH 1 /* LOUT1EN */
+#define WM8983_SLEEP 0x0040 /* SLEEP */
+#define WM8983_SLEEP_MASK 0x0040 /* SLEEP */
+#define WM8983_SLEEP_SHIFT 6 /* SLEEP */
+#define WM8983_SLEEP_WIDTH 1 /* SLEEP */
+#define WM8983_BOOSTENR 0x0020 /* BOOSTENR */
+#define WM8983_BOOSTENR_MASK 0x0020 /* BOOSTENR */
+#define WM8983_BOOSTENR_SHIFT 5 /* BOOSTENR */
+#define WM8983_BOOSTENR_WIDTH 1 /* BOOSTENR */
+#define WM8983_BOOSTENL 0x0010 /* BOOSTENL */
+#define WM8983_BOOSTENL_MASK 0x0010 /* BOOSTENL */
+#define WM8983_BOOSTENL_SHIFT 4 /* BOOSTENL */
+#define WM8983_BOOSTENL_WIDTH 1 /* BOOSTENL */
+#define WM8983_INPGAENR 0x0008 /* INPGAENR */
+#define WM8983_INPGAENR_MASK 0x0008 /* INPGAENR */
+#define WM8983_INPGAENR_SHIFT 3 /* INPGAENR */
+#define WM8983_INPGAENR_WIDTH 1 /* INPGAENR */
+#define WM8983_INPPGAENL 0x0004 /* INPPGAENL */
+#define WM8983_INPPGAENL_MASK 0x0004 /* INPPGAENL */
+#define WM8983_INPPGAENL_SHIFT 2 /* INPPGAENL */
+#define WM8983_INPPGAENL_WIDTH 1 /* INPPGAENL */
+#define WM8983_ADCENR 0x0002 /* ADCENR */
+#define WM8983_ADCENR_MASK 0x0002 /* ADCENR */
+#define WM8983_ADCENR_SHIFT 1 /* ADCENR */
+#define WM8983_ADCENR_WIDTH 1 /* ADCENR */
+#define WM8983_ADCENL 0x0001 /* ADCENL */
+#define WM8983_ADCENL_MASK 0x0001 /* ADCENL */
+#define WM8983_ADCENL_SHIFT 0 /* ADCENL */
+#define WM8983_ADCENL_WIDTH 1 /* ADCENL */
+
+/*
+ * R3 (0x03) - Power management 3
+ */
+#define WM8983_OUT4EN 0x0100 /* OUT4EN */
+#define WM8983_OUT4EN_MASK 0x0100 /* OUT4EN */
+#define WM8983_OUT4EN_SHIFT 8 /* OUT4EN */
+#define WM8983_OUT4EN_WIDTH 1 /* OUT4EN */
+#define WM8983_OUT3EN 0x0080 /* OUT3EN */
+#define WM8983_OUT3EN_MASK 0x0080 /* OUT3EN */
+#define WM8983_OUT3EN_SHIFT 7 /* OUT3EN */
+#define WM8983_OUT3EN_WIDTH 1 /* OUT3EN */
+#define WM8983_LOUT2EN 0x0040 /* LOUT2EN */
+#define WM8983_LOUT2EN_MASK 0x0040 /* LOUT2EN */
+#define WM8983_LOUT2EN_SHIFT 6 /* LOUT2EN */
+#define WM8983_LOUT2EN_WIDTH 1 /* LOUT2EN */
+#define WM8983_ROUT2EN 0x0020 /* ROUT2EN */
+#define WM8983_ROUT2EN_MASK 0x0020 /* ROUT2EN */
+#define WM8983_ROUT2EN_SHIFT 5 /* ROUT2EN */
+#define WM8983_ROUT2EN_WIDTH 1 /* ROUT2EN */
+#define WM8983_RMIXEN 0x0008 /* RMIXEN */
+#define WM8983_RMIXEN_MASK 0x0008 /* RMIXEN */
+#define WM8983_RMIXEN_SHIFT 3 /* RMIXEN */
+#define WM8983_RMIXEN_WIDTH 1 /* RMIXEN */
+#define WM8983_LMIXEN 0x0004 /* LMIXEN */
+#define WM8983_LMIXEN_MASK 0x0004 /* LMIXEN */
+#define WM8983_LMIXEN_SHIFT 2 /* LMIXEN */
+#define WM8983_LMIXEN_WIDTH 1 /* LMIXEN */
+#define WM8983_DACENR 0x0002 /* DACENR */
+#define WM8983_DACENR_MASK 0x0002 /* DACENR */
+#define WM8983_DACENR_SHIFT 1 /* DACENR */
+#define WM8983_DACENR_WIDTH 1 /* DACENR */
+#define WM8983_DACENL 0x0001 /* DACENL */
+#define WM8983_DACENL_MASK 0x0001 /* DACENL */
+#define WM8983_DACENL_SHIFT 0 /* DACENL */
+#define WM8983_DACENL_WIDTH 1 /* DACENL */
+
+/*
+ * R4 (0x04) - Audio Interface
+ */
+#define WM8983_BCP 0x0100 /* BCP */
+#define WM8983_BCP_MASK 0x0100 /* BCP */
+#define WM8983_BCP_SHIFT 8 /* BCP */
+#define WM8983_BCP_WIDTH 1 /* BCP */
+#define WM8983_LRCP 0x0080 /* LRCP */
+#define WM8983_LRCP_MASK 0x0080 /* LRCP */
+#define WM8983_LRCP_SHIFT 7 /* LRCP */
+#define WM8983_LRCP_WIDTH 1 /* LRCP */
+#define WM8983_WL_MASK 0x0060 /* WL - [6:5] */
+#define WM8983_WL_SHIFT 5 /* WL - [6:5] */
+#define WM8983_WL_WIDTH 2 /* WL - [6:5] */
+#define WM8983_FMT_MASK 0x0018 /* FMT - [4:3] */
+#define WM8983_FMT_SHIFT 3 /* FMT - [4:3] */
+#define WM8983_FMT_WIDTH 2 /* FMT - [4:3] */
+#define WM8983_DLRSWAP 0x0004 /* DLRSWAP */
+#define WM8983_DLRSWAP_MASK 0x0004 /* DLRSWAP */
+#define WM8983_DLRSWAP_SHIFT 2 /* DLRSWAP */
+#define WM8983_DLRSWAP_WIDTH 1 /* DLRSWAP */
+#define WM8983_ALRSWAP 0x0002 /* ALRSWAP */
+#define WM8983_ALRSWAP_MASK 0x0002 /* ALRSWAP */
+#define WM8983_ALRSWAP_SHIFT 1 /* ALRSWAP */
+#define WM8983_ALRSWAP_WIDTH 1 /* ALRSWAP */
+#define WM8983_MONO 0x0001 /* MONO */
+#define WM8983_MONO_MASK 0x0001 /* MONO */
+#define WM8983_MONO_SHIFT 0 /* MONO */
+#define WM8983_MONO_WIDTH 1 /* MONO */
+
+/*
+ * R5 (0x05) - Companding control
+ */
+#define WM8983_WL8 0x0020 /* WL8 */
+#define WM8983_WL8_MASK 0x0020 /* WL8 */
+#define WM8983_WL8_SHIFT 5 /* WL8 */
+#define WM8983_WL8_WIDTH 1 /* WL8 */
+#define WM8983_DAC_COMP_MASK 0x0018 /* DAC_COMP - [4:3] */
+#define WM8983_DAC_COMP_SHIFT 3 /* DAC_COMP - [4:3] */
+#define WM8983_DAC_COMP_WIDTH 2 /* DAC_COMP - [4:3] */
+#define WM8983_ADC_COMP_MASK 0x0006 /* ADC_COMP - [2:1] */
+#define WM8983_ADC_COMP_SHIFT 1 /* ADC_COMP - [2:1] */
+#define WM8983_ADC_COMP_WIDTH 2 /* ADC_COMP - [2:1] */
+#define WM8983_LOOPBACK 0x0001 /* LOOPBACK */
+#define WM8983_LOOPBACK_MASK 0x0001 /* LOOPBACK */
+#define WM8983_LOOPBACK_SHIFT 0 /* LOOPBACK */
+#define WM8983_LOOPBACK_WIDTH 1 /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clock Gen control
+ */
+#define WM8983_CLKSEL 0x0100 /* CLKSEL */
+#define WM8983_CLKSEL_MASK 0x0100 /* CLKSEL */
+#define WM8983_CLKSEL_SHIFT 8 /* CLKSEL */
+#define WM8983_CLKSEL_WIDTH 1 /* CLKSEL */
+#define WM8983_MCLKDIV_MASK 0x00E0 /* MCLKDIV - [7:5] */
+#define WM8983_MCLKDIV_SHIFT 5 /* MCLKDIV - [7:5] */
+#define WM8983_MCLKDIV_WIDTH 3 /* MCLKDIV - [7:5] */
+#define WM8983_BCLKDIV_MASK 0x001C /* BCLKDIV - [4:2] */
+#define WM8983_BCLKDIV_SHIFT 2 /* BCLKDIV - [4:2] */
+#define WM8983_BCLKDIV_WIDTH 3 /* BCLKDIV - [4:2] */
+#define WM8983_MS 0x0001 /* MS */
+#define WM8983_MS_MASK 0x0001 /* MS */
+#define WM8983_MS_SHIFT 0 /* MS */
+#define WM8983_MS_WIDTH 1 /* MS */
+
+/*
+ * R7 (0x07) - Additional control
+ */
+#define WM8983_SR_MASK 0x000E /* SR - [3:1] */
+#define WM8983_SR_SHIFT 1 /* SR - [3:1] */
+#define WM8983_SR_WIDTH 3 /* SR - [3:1] */
+#define WM8983_SLOWCLKEN 0x0001 /* SLOWCLKEN */
+#define WM8983_SLOWCLKEN_MASK 0x0001 /* SLOWCLKEN */
+#define WM8983_SLOWCLKEN_SHIFT 0 /* SLOWCLKEN */
+#define WM8983_SLOWCLKEN_WIDTH 1 /* SLOWCLKEN */
+
+/*
+ * R8 (0x08) - GPIO Control
+ */
+#define WM8983_OPCLKDIV_MASK 0x0030 /* OPCLKDIV - [5:4] */
+#define WM8983_OPCLKDIV_SHIFT 4 /* OPCLKDIV - [5:4] */
+#define WM8983_OPCLKDIV_WIDTH 2 /* OPCLKDIV - [5:4] */
+#define WM8983_GPIO1POL 0x0008 /* GPIO1POL */
+#define WM8983_GPIO1POL_MASK 0x0008 /* GPIO1POL */
+#define WM8983_GPIO1POL_SHIFT 3 /* GPIO1POL */
+#define WM8983_GPIO1POL_WIDTH 1 /* GPIO1POL */
+#define WM8983_GPIO1SEL_MASK 0x0007 /* GPIO1SEL - [2:0] */
+#define WM8983_GPIO1SEL_SHIFT 0 /* GPIO1SEL - [2:0] */
+#define WM8983_GPIO1SEL_WIDTH 3 /* GPIO1SEL - [2:0] */
+
+/*
+ * R9 (0x09) - Jack Detect Control 1
+ */
+#define WM8983_JD_VMID1 0x0100 /* JD_VMID1 */
+#define WM8983_JD_VMID1_MASK 0x0100 /* JD_VMID1 */
+#define WM8983_JD_VMID1_SHIFT 8 /* JD_VMID1 */
+#define WM8983_JD_VMID1_WIDTH 1 /* JD_VMID1 */
+#define WM8983_JD_VMID0 0x0080 /* JD_VMID0 */
+#define WM8983_JD_VMID0_MASK 0x0080 /* JD_VMID0 */
+#define WM8983_JD_VMID0_SHIFT 7 /* JD_VMID0 */
+#define WM8983_JD_VMID0_WIDTH 1 /* JD_VMID0 */
+#define WM8983_JD_EN 0x0040 /* JD_EN */
+#define WM8983_JD_EN_MASK 0x0040 /* JD_EN */
+#define WM8983_JD_EN_SHIFT 6 /* JD_EN */
+#define WM8983_JD_EN_WIDTH 1 /* JD_EN */
+#define WM8983_JD_SEL_MASK 0x0030 /* JD_SEL - [5:4] */
+#define WM8983_JD_SEL_SHIFT 4 /* JD_SEL - [5:4] */
+#define WM8983_JD_SEL_WIDTH 2 /* JD_SEL - [5:4] */
+
+/*
+ * R10 (0x0A) - DAC Control
+ */
+#define WM8983_SOFTMUTE 0x0040 /* SOFTMUTE */
+#define WM8983_SOFTMUTE_MASK 0x0040 /* SOFTMUTE */
+#define WM8983_SOFTMUTE_SHIFT 6 /* SOFTMUTE */
+#define WM8983_SOFTMUTE_WIDTH 1 /* SOFTMUTE */
+#define WM8983_DACOSR128 0x0008 /* DACOSR128 */
+#define WM8983_DACOSR128_MASK 0x0008 /* DACOSR128 */
+#define WM8983_DACOSR128_SHIFT 3 /* DACOSR128 */
+#define WM8983_DACOSR128_WIDTH 1 /* DACOSR128 */
+#define WM8983_AMUTE 0x0004 /* AMUTE */
+#define WM8983_AMUTE_MASK 0x0004 /* AMUTE */
+#define WM8983_AMUTE_SHIFT 2 /* AMUTE */
+#define WM8983_AMUTE_WIDTH 1 /* AMUTE */
+#define WM8983_DACRPOL 0x0002 /* DACRPOL */
+#define WM8983_DACRPOL_MASK 0x0002 /* DACRPOL */
+#define WM8983_DACRPOL_SHIFT 1 /* DACRPOL */
+#define WM8983_DACRPOL_WIDTH 1 /* DACRPOL */
+#define WM8983_DACLPOL 0x0001 /* DACLPOL */
+#define WM8983_DACLPOL_MASK 0x0001 /* DACLPOL */
+#define WM8983_DACLPOL_SHIFT 0 /* DACLPOL */
+#define WM8983_DACLPOL_WIDTH 1 /* DACLPOL */
+
+/*
+ * R11 (0x0B) - Left DAC digital Vol
+ */
+#define WM8983_DACVU 0x0100 /* DACVU */
+#define WM8983_DACVU_MASK 0x0100 /* DACVU */
+#define WM8983_DACVU_SHIFT 8 /* DACVU */
+#define WM8983_DACVU_WIDTH 1 /* DACVU */
+#define WM8983_DACLVOL_MASK 0x00FF /* DACLVOL - [7:0] */
+#define WM8983_DACLVOL_SHIFT 0 /* DACLVOL - [7:0] */
+#define WM8983_DACLVOL_WIDTH 8 /* DACLVOL - [7:0] */
+
+/*
+ * R12 (0x0C) - Right DAC digital vol
+ */
+#define WM8983_DACVU 0x0100 /* DACVU */
+#define WM8983_DACVU_MASK 0x0100 /* DACVU */
+#define WM8983_DACVU_SHIFT 8 /* DACVU */
+#define WM8983_DACVU_WIDTH 1 /* DACVU */
+#define WM8983_DACRVOL_MASK 0x00FF /* DACRVOL - [7:0] */
+#define WM8983_DACRVOL_SHIFT 0 /* DACRVOL - [7:0] */
+#define WM8983_DACRVOL_WIDTH 8 /* DACRVOL - [7:0] */
+
+/*
+ * R13 (0x0D) - Jack Detect Control 2
+ */
+#define WM8983_JD_EN1_MASK 0x00F0 /* JD_EN1 - [7:4] */
+#define WM8983_JD_EN1_SHIFT 4 /* JD_EN1 - [7:4] */
+#define WM8983_JD_EN1_WIDTH 4 /* JD_EN1 - [7:4] */
+#define WM8983_JD_EN0_MASK 0x000F /* JD_EN0 - [3:0] */
+#define WM8983_JD_EN0_SHIFT 0 /* JD_EN0 - [3:0] */
+#define WM8983_JD_EN0_WIDTH 4 /* JD_EN0 - [3:0] */
+
+/*
+ * R14 (0x0E) - ADC Control
+ */
+#define WM8983_HPFEN 0x0100 /* HPFEN */
+#define WM8983_HPFEN_MASK 0x0100 /* HPFEN */
+#define WM8983_HPFEN_SHIFT 8 /* HPFEN */
+#define WM8983_HPFEN_WIDTH 1 /* HPFEN */
+#define WM8983_HPFAPP 0x0080 /* HPFAPP */
+#define WM8983_HPFAPP_MASK 0x0080 /* HPFAPP */
+#define WM8983_HPFAPP_SHIFT 7 /* HPFAPP */
+#define WM8983_HPFAPP_WIDTH 1 /* HPFAPP */
+#define WM8983_HPFCUT_MASK 0x0070 /* HPFCUT - [6:4] */
+#define WM8983_HPFCUT_SHIFT 4 /* HPFCUT - [6:4] */
+#define WM8983_HPFCUT_WIDTH 3 /* HPFCUT - [6:4] */
+#define WM8983_ADCOSR128 0x0008 /* ADCOSR128 */
+#define WM8983_ADCOSR128_MASK 0x0008 /* ADCOSR128 */
+#define WM8983_ADCOSR128_SHIFT 3 /* ADCOSR128 */
+#define WM8983_ADCOSR128_WIDTH 1 /* ADCOSR128 */
+#define WM8983_ADCRPOL 0x0002 /* ADCRPOL */
+#define WM8983_ADCRPOL_MASK 0x0002 /* ADCRPOL */
+#define WM8983_ADCRPOL_SHIFT 1 /* ADCRPOL */
+#define WM8983_ADCRPOL_WIDTH 1 /* ADCRPOL */
+#define WM8983_ADCLPOL 0x0001 /* ADCLPOL */
+#define WM8983_ADCLPOL_MASK 0x0001 /* ADCLPOL */
+#define WM8983_ADCLPOL_SHIFT 0 /* ADCLPOL */
+#define WM8983_ADCLPOL_WIDTH 1 /* ADCLPOL */
+
+/*
+ * R15 (0x0F) - Left ADC Digital Vol
+ */
+#define WM8983_ADCVU 0x0100 /* ADCVU */
+#define WM8983_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8983_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8983_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8983_ADCLVOL_MASK 0x00FF /* ADCLVOL - [7:0] */
+#define WM8983_ADCLVOL_SHIFT 0 /* ADCLVOL - [7:0] */
+#define WM8983_ADCLVOL_WIDTH 8 /* ADCLVOL - [7:0] */
+
+/*
+ * R16 (0x10) - Right ADC Digital Vol
+ */
+#define WM8983_ADCVU 0x0100 /* ADCVU */
+#define WM8983_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8983_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8983_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8983_ADCRVOL_MASK 0x00FF /* ADCRVOL - [7:0] */
+#define WM8983_ADCRVOL_SHIFT 0 /* ADCRVOL - [7:0] */
+#define WM8983_ADCRVOL_WIDTH 8 /* ADCRVOL - [7:0] */
+
+/*
+ * R18 (0x12) - EQ1 - low shelf
+ */
+#define WM8983_EQ3DMODE 0x0100 /* EQ3DMODE */
+#define WM8983_EQ3DMODE_MASK 0x0100 /* EQ3DMODE */
+#define WM8983_EQ3DMODE_SHIFT 8 /* EQ3DMODE */
+#define WM8983_EQ3DMODE_WIDTH 1 /* EQ3DMODE */
+#define WM8983_EQ1C_MASK 0x0060 /* EQ1C - [6:5] */
+#define WM8983_EQ1C_SHIFT 5 /* EQ1C - [6:5] */
+#define WM8983_EQ1C_WIDTH 2 /* EQ1C - [6:5] */
+#define WM8983_EQ1G_MASK 0x001F /* EQ1G - [4:0] */
+#define WM8983_EQ1G_SHIFT 0 /* EQ1G - [4:0] */
+#define WM8983_EQ1G_WIDTH 5 /* EQ1G - [4:0] */
+
+/*
+ * R19 (0x13) - EQ2 - peak 1
+ */
+#define WM8983_EQ2BW 0x0100 /* EQ2BW */
+#define WM8983_EQ2BW_MASK 0x0100 /* EQ2BW */
+#define WM8983_EQ2BW_SHIFT 8 /* EQ2BW */
+#define WM8983_EQ2BW_WIDTH 1 /* EQ2BW */
+#define WM8983_EQ2C_MASK 0x0060 /* EQ2C - [6:5] */
+#define WM8983_EQ2C_SHIFT 5 /* EQ2C - [6:5] */
+#define WM8983_EQ2C_WIDTH 2 /* EQ2C - [6:5] */
+#define WM8983_EQ2G_MASK 0x001F /* EQ2G - [4:0] */
+#define WM8983_EQ2G_SHIFT 0 /* EQ2G - [4:0] */
+#define WM8983_EQ2G_WIDTH 5 /* EQ2G - [4:0] */
+
+/*
+ * R20 (0x14) - EQ3 - peak 2
+ */
+#define WM8983_EQ3BW 0x0100 /* EQ3BW */
+#define WM8983_EQ3BW_MASK 0x0100 /* EQ3BW */
+#define WM8983_EQ3BW_SHIFT 8 /* EQ3BW */
+#define WM8983_EQ3BW_WIDTH 1 /* EQ3BW */
+#define WM8983_EQ3C_MASK 0x0060 /* EQ3C - [6:5] */
+#define WM8983_EQ3C_SHIFT 5 /* EQ3C - [6:5] */
+#define WM8983_EQ3C_WIDTH 2 /* EQ3C - [6:5] */
+#define WM8983_EQ3G_MASK 0x001F /* EQ3G - [4:0] */
+#define WM8983_EQ3G_SHIFT 0 /* EQ3G - [4:0] */
+#define WM8983_EQ3G_WIDTH 5 /* EQ3G - [4:0] */
+
+/*
+ * R21 (0x15) - EQ4 - peak 3
+ */
+#define WM8983_EQ4BW 0x0100 /* EQ4BW */
+#define WM8983_EQ4BW_MASK 0x0100 /* EQ4BW */
+#define WM8983_EQ4BW_SHIFT 8 /* EQ4BW */
+#define WM8983_EQ4BW_WIDTH 1 /* EQ4BW */
+#define WM8983_EQ4C_MASK 0x0060 /* EQ4C - [6:5] */
+#define WM8983_EQ4C_SHIFT 5 /* EQ4C - [6:5] */
+#define WM8983_EQ4C_WIDTH 2 /* EQ4C - [6:5] */
+#define WM8983_EQ4G_MASK 0x001F /* EQ4G - [4:0] */
+#define WM8983_EQ4G_SHIFT 0 /* EQ4G - [4:0] */
+#define WM8983_EQ4G_WIDTH 5 /* EQ4G - [4:0] */
+
+/*
+ * R22 (0x16) - EQ5 - high shelf
+ */
+#define WM8983_EQ5C_MASK 0x0060 /* EQ5C - [6:5] */
+#define WM8983_EQ5C_SHIFT 5 /* EQ5C - [6:5] */
+#define WM8983_EQ5C_WIDTH 2 /* EQ5C - [6:5] */
+#define WM8983_EQ5G_MASK 0x001F /* EQ5G - [4:0] */
+#define WM8983_EQ5G_SHIFT 0 /* EQ5G - [4:0] */
+#define WM8983_EQ5G_WIDTH 5 /* EQ5G - [4:0] */
+
+/*
+ * R24 (0x18) - DAC Limiter 1
+ */
+#define WM8983_LIMEN 0x0100 /* LIMEN */
+#define WM8983_LIMEN_MASK 0x0100 /* LIMEN */
+#define WM8983_LIMEN_SHIFT 8 /* LIMEN */
+#define WM8983_LIMEN_WIDTH 1 /* LIMEN */
+#define WM8983_LIMDCY_MASK 0x00F0 /* LIMDCY - [7:4] */
+#define WM8983_LIMDCY_SHIFT 4 /* LIMDCY - [7:4] */
+#define WM8983_LIMDCY_WIDTH 4 /* LIMDCY - [7:4] */
+#define WM8983_LIMATK_MASK 0x000F /* LIMATK - [3:0] */
+#define WM8983_LIMATK_SHIFT 0 /* LIMATK - [3:0] */
+#define WM8983_LIMATK_WIDTH 4 /* LIMATK - [3:0] */
+
+/*
+ * R25 (0x19) - DAC Limiter 2
+ */
+#define WM8983_LIMLVL_MASK 0x0070 /* LIMLVL - [6:4] */
+#define WM8983_LIMLVL_SHIFT 4 /* LIMLVL - [6:4] */
+#define WM8983_LIMLVL_WIDTH 3 /* LIMLVL - [6:4] */
+#define WM8983_LIMBOOST_MASK 0x000F /* LIMBOOST - [3:0] */
+#define WM8983_LIMBOOST_SHIFT 0 /* LIMBOOST - [3:0] */
+#define WM8983_LIMBOOST_WIDTH 4 /* LIMBOOST - [3:0] */
+
+/*
+ * R27 (0x1B) - Notch Filter 1
+ */
+#define WM8983_NFU 0x0100 /* NFU */
+#define WM8983_NFU_MASK 0x0100 /* NFU */
+#define WM8983_NFU_SHIFT 8 /* NFU */
+#define WM8983_NFU_WIDTH 1 /* NFU */
+#define WM8983_NFEN 0x0080 /* NFEN */
+#define WM8983_NFEN_MASK 0x0080 /* NFEN */
+#define WM8983_NFEN_SHIFT 7 /* NFEN */
+#define WM8983_NFEN_WIDTH 1 /* NFEN */
+#define WM8983_NFA0_13_7_MASK 0x007F /* NFA0(13:7) - [6:0] */
+#define WM8983_NFA0_13_7_SHIFT 0 /* NFA0(13:7) - [6:0] */
+#define WM8983_NFA0_13_7_WIDTH 7 /* NFA0(13:7) - [6:0] */
+
+/*
+ * R28 (0x1C) - Notch Filter 2
+ */
+#define WM8983_NFU 0x0100 /* NFU */
+#define WM8983_NFU_MASK 0x0100 /* NFU */
+#define WM8983_NFU_SHIFT 8 /* NFU */
+#define WM8983_NFU_WIDTH 1 /* NFU */
+#define WM8983_NFA0_6_0_MASK 0x007F /* NFA0(6:0) - [6:0] */
+#define WM8983_NFA0_6_0_SHIFT 0 /* NFA0(6:0) - [6:0] */
+#define WM8983_NFA0_6_0_WIDTH 7 /* NFA0(6:0) - [6:0] */
+
+/*
+ * R29 (0x1D) - Notch Filter 3
+ */
+#define WM8983_NFU 0x0100 /* NFU */
+#define WM8983_NFU_MASK 0x0100 /* NFU */
+#define WM8983_NFU_SHIFT 8 /* NFU */
+#define WM8983_NFU_WIDTH 1 /* NFU */
+#define WM8983_NFA1_13_7_MASK 0x007F /* NFA1(13:7) - [6:0] */
+#define WM8983_NFA1_13_7_SHIFT 0 /* NFA1(13:7) - [6:0] */
+#define WM8983_NFA1_13_7_WIDTH 7 /* NFA1(13:7) - [6:0] */
+
+/*
+ * R30 (0x1E) - Notch Filter 4
+ */
+#define WM8983_NFU 0x0100 /* NFU */
+#define WM8983_NFU_MASK 0x0100 /* NFU */
+#define WM8983_NFU_SHIFT 8 /* NFU */
+#define WM8983_NFU_WIDTH 1 /* NFU */
+#define WM8983_NFA1_6_0_MASK 0x007F /* NFA1(6:0) - [6:0] */
+#define WM8983_NFA1_6_0_SHIFT 0 /* NFA1(6:0) - [6:0] */
+#define WM8983_NFA1_6_0_WIDTH 7 /* NFA1(6:0) - [6:0] */
+
+/*
+ * R32 (0x20) - ALC control 1
+ */
+#define WM8983_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */
+#define WM8983_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */
+#define WM8983_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */
+#define WM8983_ALCMAX_MASK 0x0038 /* ALCMAX - [5:3] */
+#define WM8983_ALCMAX_SHIFT 3 /* ALCMAX - [5:3] */
+#define WM8983_ALCMAX_WIDTH 3 /* ALCMAX - [5:3] */
+#define WM8983_ALCMIN_MASK 0x0007 /* ALCMIN - [2:0] */
+#define WM8983_ALCMIN_SHIFT 0 /* ALCMIN - [2:0] */
+#define WM8983_ALCMIN_WIDTH 3 /* ALCMIN - [2:0] */
+
+/*
+ * R33 (0x21) - ALC control 2
+ */
+#define WM8983_ALCHLD_MASK 0x00F0 /* ALCHLD - [7:4] */
+#define WM8983_ALCHLD_SHIFT 4 /* ALCHLD - [7:4] */
+#define WM8983_ALCHLD_WIDTH 4 /* ALCHLD - [7:4] */
+#define WM8983_ALCLVL_MASK 0x000F /* ALCLVL - [3:0] */
+#define WM8983_ALCLVL_SHIFT 0 /* ALCLVL - [3:0] */
+#define WM8983_ALCLVL_WIDTH 4 /* ALCLVL - [3:0] */
+
+/*
+ * R34 (0x22) - ALC control 3
+ */
+#define WM8983_ALCMODE 0x0100 /* ALCMODE */
+#define WM8983_ALCMODE_MASK 0x0100 /* ALCMODE */
+#define WM8983_ALCMODE_SHIFT 8 /* ALCMODE */
+#define WM8983_ALCMODE_WIDTH 1 /* ALCMODE */
+#define WM8983_ALCDCY_MASK 0x00F0 /* ALCDCY - [7:4] */
+#define WM8983_ALCDCY_SHIFT 4 /* ALCDCY - [7:4] */
+#define WM8983_ALCDCY_WIDTH 4 /* ALCDCY - [7:4] */
+#define WM8983_ALCATK_MASK 0x000F /* ALCATK - [3:0] */
+#define WM8983_ALCATK_SHIFT 0 /* ALCATK - [3:0] */
+#define WM8983_ALCATK_WIDTH 4 /* ALCATK - [3:0] */
+
+/*
+ * R35 (0x23) - Noise Gate
+ */
+#define WM8983_NGEN 0x0008 /* NGEN */
+#define WM8983_NGEN_MASK 0x0008 /* NGEN */
+#define WM8983_NGEN_SHIFT 3 /* NGEN */
+#define WM8983_NGEN_WIDTH 1 /* NGEN */
+#define WM8983_NGTH_MASK 0x0007 /* NGTH - [2:0] */
+#define WM8983_NGTH_SHIFT 0 /* NGTH - [2:0] */
+#define WM8983_NGTH_WIDTH 3 /* NGTH - [2:0] */
+
+/*
+ * R36 (0x24) - PLL N
+ */
+#define WM8983_PLL_PRESCALE 0x0010 /* PLL_PRESCALE */
+#define WM8983_PLL_PRESCALE_MASK 0x0010 /* PLL_PRESCALE */
+#define WM8983_PLL_PRESCALE_SHIFT 4 /* PLL_PRESCALE */
+#define WM8983_PLL_PRESCALE_WIDTH 1 /* PLL_PRESCALE */
+#define WM8983_PLLN_MASK 0x000F /* PLLN - [3:0] */
+#define WM8983_PLLN_SHIFT 0 /* PLLN - [3:0] */
+#define WM8983_PLLN_WIDTH 4 /* PLLN - [3:0] */
+
+/*
+ * R37 (0x25) - PLL K 1
+ */
+#define WM8983_PLLK_23_18_MASK 0x003F /* PLLK(23:18) - [5:0] */
+#define WM8983_PLLK_23_18_SHIFT 0 /* PLLK(23:18) - [5:0] */
+#define WM8983_PLLK_23_18_WIDTH 6 /* PLLK(23:18) - [5:0] */
+
+/*
+ * R38 (0x26) - PLL K 2
+ */
+#define WM8983_PLLK_17_9_MASK 0x01FF /* PLLK(17:9) - [8:0] */
+#define WM8983_PLLK_17_9_SHIFT 0 /* PLLK(17:9) - [8:0] */
+#define WM8983_PLLK_17_9_WIDTH 9 /* PLLK(17:9) - [8:0] */
+
+/*
+ * R39 (0x27) - PLL K 3
+ */
+#define WM8983_PLLK_8_0_MASK 0x01FF /* PLLK(8:0) - [8:0] */
+#define WM8983_PLLK_8_0_SHIFT 0 /* PLLK(8:0) - [8:0] */
+#define WM8983_PLLK_8_0_WIDTH 9 /* PLLK(8:0) - [8:0] */
+
+/*
+ * R41 (0x29) - 3D control
+ */
+#define WM8983_DEPTH3D_MASK 0x000F /* DEPTH3D - [3:0] */
+#define WM8983_DEPTH3D_SHIFT 0 /* DEPTH3D - [3:0] */
+#define WM8983_DEPTH3D_WIDTH 4 /* DEPTH3D - [3:0] */
+
+/*
+ * R42 (0x2A) - OUT4 to ADC
+ */
+#define WM8983_OUT4_2ADCVOL_MASK 0x01C0 /* OUT4_2ADCVOL - [8:6] */
+#define WM8983_OUT4_2ADCVOL_SHIFT 6 /* OUT4_2ADCVOL - [8:6] */
+#define WM8983_OUT4_2ADCVOL_WIDTH 3 /* OUT4_2ADCVOL - [8:6] */
+#define WM8983_OUT4_2LNR 0x0020 /* OUT4_2LNR */
+#define WM8983_OUT4_2LNR_MASK 0x0020 /* OUT4_2LNR */
+#define WM8983_OUT4_2LNR_SHIFT 5 /* OUT4_2LNR */
+#define WM8983_OUT4_2LNR_WIDTH 1 /* OUT4_2LNR */
+#define WM8983_POBCTRL 0x0004 /* POBCTRL */
+#define WM8983_POBCTRL_MASK 0x0004 /* POBCTRL */
+#define WM8983_POBCTRL_SHIFT 2 /* POBCTRL */
+#define WM8983_POBCTRL_WIDTH 1 /* POBCTRL */
+#define WM8983_DELEN 0x0002 /* DELEN */
+#define WM8983_DELEN_MASK 0x0002 /* DELEN */
+#define WM8983_DELEN_SHIFT 1 /* DELEN */
+#define WM8983_DELEN_WIDTH 1 /* DELEN */
+#define WM8983_OUT1DEL 0x0001 /* OUT1DEL */
+#define WM8983_OUT1DEL_MASK 0x0001 /* OUT1DEL */
+#define WM8983_OUT1DEL_SHIFT 0 /* OUT1DEL */
+#define WM8983_OUT1DEL_WIDTH 1 /* OUT1DEL */
+
+/*
+ * R43 (0x2B) - Beep control
+ */
+#define WM8983_BYPL2RMIX 0x0100 /* BYPL2RMIX */
+#define WM8983_BYPL2RMIX_MASK 0x0100 /* BYPL2RMIX */
+#define WM8983_BYPL2RMIX_SHIFT 8 /* BYPL2RMIX */
+#define WM8983_BYPL2RMIX_WIDTH 1 /* BYPL2RMIX */
+#define WM8983_BYPR2LMIX 0x0080 /* BYPR2LMIX */
+#define WM8983_BYPR2LMIX_MASK 0x0080 /* BYPR2LMIX */
+#define WM8983_BYPR2LMIX_SHIFT 7 /* BYPR2LMIX */
+#define WM8983_BYPR2LMIX_WIDTH 1 /* BYPR2LMIX */
+#define WM8983_MUTERPGA2INV 0x0020 /* MUTERPGA2INV */
+#define WM8983_MUTERPGA2INV_MASK 0x0020 /* MUTERPGA2INV */
+#define WM8983_MUTERPGA2INV_SHIFT 5 /* MUTERPGA2INV */
+#define WM8983_MUTERPGA2INV_WIDTH 1 /* MUTERPGA2INV */
+#define WM8983_INVROUT2 0x0010 /* INVROUT2 */
+#define WM8983_INVROUT2_MASK 0x0010 /* INVROUT2 */
+#define WM8983_INVROUT2_SHIFT 4 /* INVROUT2 */
+#define WM8983_INVROUT2_WIDTH 1 /* INVROUT2 */
+#define WM8983_BEEPVOL_MASK 0x000E /* BEEPVOL - [3:1] */
+#define WM8983_BEEPVOL_SHIFT 1 /* BEEPVOL - [3:1] */
+#define WM8983_BEEPVOL_WIDTH 3 /* BEEPVOL - [3:1] */
+#define WM8983_BEEPEN 0x0001 /* BEEPEN */
+#define WM8983_BEEPEN_MASK 0x0001 /* BEEPEN */
+#define WM8983_BEEPEN_SHIFT 0 /* BEEPEN */
+#define WM8983_BEEPEN_WIDTH 1 /* BEEPEN */
+
+/*
+ * R44 (0x2C) - Input ctrl
+ */
+#define WM8983_MBVSEL 0x0100 /* MBVSEL */
+#define WM8983_MBVSEL_MASK 0x0100 /* MBVSEL */
+#define WM8983_MBVSEL_SHIFT 8 /* MBVSEL */
+#define WM8983_MBVSEL_WIDTH 1 /* MBVSEL */
+#define WM8983_R2_2INPPGA 0x0040 /* R2_2INPPGA */
+#define WM8983_R2_2INPPGA_MASK 0x0040 /* R2_2INPPGA */
+#define WM8983_R2_2INPPGA_SHIFT 6 /* R2_2INPPGA */
+#define WM8983_R2_2INPPGA_WIDTH 1 /* R2_2INPPGA */
+#define WM8983_RIN2INPPGA 0x0020 /* RIN2INPPGA */
+#define WM8983_RIN2INPPGA_MASK 0x0020 /* RIN2INPPGA */
+#define WM8983_RIN2INPPGA_SHIFT 5 /* RIN2INPPGA */
+#define WM8983_RIN2INPPGA_WIDTH 1 /* RIN2INPPGA */
+#define WM8983_RIP2INPPGA 0x0010 /* RIP2INPPGA */
+#define WM8983_RIP2INPPGA_MASK 0x0010 /* RIP2INPPGA */
+#define WM8983_RIP2INPPGA_SHIFT 4 /* RIP2INPPGA */
+#define WM8983_RIP2INPPGA_WIDTH 1 /* RIP2INPPGA */
+#define WM8983_L2_2INPPGA 0x0004 /* L2_2INPPGA */
+#define WM8983_L2_2INPPGA_MASK 0x0004 /* L2_2INPPGA */
+#define WM8983_L2_2INPPGA_SHIFT 2 /* L2_2INPPGA */
+#define WM8983_L2_2INPPGA_WIDTH 1 /* L2_2INPPGA */
+#define WM8983_LIN2INPPGA 0x0002 /* LIN2INPPGA */
+#define WM8983_LIN2INPPGA_MASK 0x0002 /* LIN2INPPGA */
+#define WM8983_LIN2INPPGA_SHIFT 1 /* LIN2INPPGA */
+#define WM8983_LIN2INPPGA_WIDTH 1 /* LIN2INPPGA */
+#define WM8983_LIP2INPPGA 0x0001 /* LIP2INPPGA */
+#define WM8983_LIP2INPPGA_MASK 0x0001 /* LIP2INPPGA */
+#define WM8983_LIP2INPPGA_SHIFT 0 /* LIP2INPPGA */
+#define WM8983_LIP2INPPGA_WIDTH 1 /* LIP2INPPGA */
+
+/*
+ * R45 (0x2D) - Left INP PGA gain ctrl
+ */
+#define WM8983_INPGAVU 0x0100 /* INPGAVU */
+#define WM8983_INPGAVU_MASK 0x0100 /* INPGAVU */
+#define WM8983_INPGAVU_SHIFT 8 /* INPGAVU */
+#define WM8983_INPGAVU_WIDTH 1 /* INPGAVU */
+#define WM8983_INPPGAZCL 0x0080 /* INPPGAZCL */
+#define WM8983_INPPGAZCL_MASK 0x0080 /* INPPGAZCL */
+#define WM8983_INPPGAZCL_SHIFT 7 /* INPPGAZCL */
+#define WM8983_INPPGAZCL_WIDTH 1 /* INPPGAZCL */
+#define WM8983_INPPGAMUTEL 0x0040 /* INPPGAMUTEL */
+#define WM8983_INPPGAMUTEL_MASK 0x0040 /* INPPGAMUTEL */
+#define WM8983_INPPGAMUTEL_SHIFT 6 /* INPPGAMUTEL */
+#define WM8983_INPPGAMUTEL_WIDTH 1 /* INPPGAMUTEL */
+#define WM8983_INPPGAVOLL_MASK 0x003F /* INPPGAVOLL - [5:0] */
+#define WM8983_INPPGAVOLL_SHIFT 0 /* INPPGAVOLL - [5:0] */
+#define WM8983_INPPGAVOLL_WIDTH 6 /* INPPGAVOLL - [5:0] */
+
+/*
+ * R46 (0x2E) - Right INP PGA gain ctrl
+ */
+#define WM8983_INPGAVU 0x0100 /* INPGAVU */
+#define WM8983_INPGAVU_MASK 0x0100 /* INPGAVU */
+#define WM8983_INPGAVU_SHIFT 8 /* INPGAVU */
+#define WM8983_INPGAVU_WIDTH 1 /* INPGAVU */
+#define WM8983_INPPGAZCR 0x0080 /* INPPGAZCR */
+#define WM8983_INPPGAZCR_MASK 0x0080 /* INPPGAZCR */
+#define WM8983_INPPGAZCR_SHIFT 7 /* INPPGAZCR */
+#define WM8983_INPPGAZCR_WIDTH 1 /* INPPGAZCR */
+#define WM8983_INPPGAMUTER 0x0040 /* INPPGAMUTER */
+#define WM8983_INPPGAMUTER_MASK 0x0040 /* INPPGAMUTER */
+#define WM8983_INPPGAMUTER_SHIFT 6 /* INPPGAMUTER */
+#define WM8983_INPPGAMUTER_WIDTH 1 /* INPPGAMUTER */
+#define WM8983_INPPGAVOLR_MASK 0x003F /* INPPGAVOLR - [5:0] */
+#define WM8983_INPPGAVOLR_SHIFT 0 /* INPPGAVOLR - [5:0] */
+#define WM8983_INPPGAVOLR_WIDTH 6 /* INPPGAVOLR - [5:0] */
+
+/*
+ * R47 (0x2F) - Left ADC BOOST ctrl
+ */
+#define WM8983_PGABOOSTL 0x0100 /* PGABOOSTL */
+#define WM8983_PGABOOSTL_MASK 0x0100 /* PGABOOSTL */
+#define WM8983_PGABOOSTL_SHIFT 8 /* PGABOOSTL */
+#define WM8983_PGABOOSTL_WIDTH 1 /* PGABOOSTL */
+#define WM8983_L2_2BOOSTVOL_MASK 0x0070 /* L2_2BOOSTVOL - [6:4] */
+#define WM8983_L2_2BOOSTVOL_SHIFT 4 /* L2_2BOOSTVOL - [6:4] */
+#define WM8983_L2_2BOOSTVOL_WIDTH 3 /* L2_2BOOSTVOL - [6:4] */
+#define WM8983_AUXL2BOOSTVOL_MASK 0x0007 /* AUXL2BOOSTVOL - [2:0] */
+#define WM8983_AUXL2BOOSTVOL_SHIFT 0 /* AUXL2BOOSTVOL - [2:0] */
+#define WM8983_AUXL2BOOSTVOL_WIDTH 3 /* AUXL2BOOSTVOL - [2:0] */
+
+/*
+ * R48 (0x30) - Right ADC BOOST ctrl
+ */
+#define WM8983_PGABOOSTR 0x0100 /* PGABOOSTR */
+#define WM8983_PGABOOSTR_MASK 0x0100 /* PGABOOSTR */
+#define WM8983_PGABOOSTR_SHIFT 8 /* PGABOOSTR */
+#define WM8983_PGABOOSTR_WIDTH 1 /* PGABOOSTR */
+#define WM8983_R2_2BOOSTVOL_MASK 0x0070 /* R2_2BOOSTVOL - [6:4] */
+#define WM8983_R2_2BOOSTVOL_SHIFT 4 /* R2_2BOOSTVOL - [6:4] */
+#define WM8983_R2_2BOOSTVOL_WIDTH 3 /* R2_2BOOSTVOL - [6:4] */
+#define WM8983_AUXR2BOOSTVOL_MASK 0x0007 /* AUXR2BOOSTVOL - [2:0] */
+#define WM8983_AUXR2BOOSTVOL_SHIFT 0 /* AUXR2BOOSTVOL - [2:0] */
+#define WM8983_AUXR2BOOSTVOL_WIDTH 3 /* AUXR2BOOSTVOL - [2:0] */
+
+/*
+ * R49 (0x31) - Output ctrl
+ */
+#define WM8983_DACL2RMIX 0x0040 /* DACL2RMIX */
+#define WM8983_DACL2RMIX_MASK 0x0040 /* DACL2RMIX */
+#define WM8983_DACL2RMIX_SHIFT 6 /* DACL2RMIX */
+#define WM8983_DACL2RMIX_WIDTH 1 /* DACL2RMIX */
+#define WM8983_DACR2LMIX 0x0020 /* DACR2LMIX */
+#define WM8983_DACR2LMIX_MASK 0x0020 /* DACR2LMIX */
+#define WM8983_DACR2LMIX_SHIFT 5 /* DACR2LMIX */
+#define WM8983_DACR2LMIX_WIDTH 1 /* DACR2LMIX */
+#define WM8983_OUT4BOOST 0x0010 /* OUT4BOOST */
+#define WM8983_OUT4BOOST_MASK 0x0010 /* OUT4BOOST */
+#define WM8983_OUT4BOOST_SHIFT 4 /* OUT4BOOST */
+#define WM8983_OUT4BOOST_WIDTH 1 /* OUT4BOOST */
+#define WM8983_OUT3BOOST 0x0008 /* OUT3BOOST */
+#define WM8983_OUT3BOOST_MASK 0x0008 /* OUT3BOOST */
+#define WM8983_OUT3BOOST_SHIFT 3 /* OUT3BOOST */
+#define WM8983_OUT3BOOST_WIDTH 1 /* OUT3BOOST */
+#define WM8983_SPKBOOST 0x0004 /* SPKBOOST */
+#define WM8983_SPKBOOST_MASK 0x0004 /* SPKBOOST */
+#define WM8983_SPKBOOST_SHIFT 2 /* SPKBOOST */
+#define WM8983_SPKBOOST_WIDTH 1 /* SPKBOOST */
+#define WM8983_TSDEN 0x0002 /* TSDEN */
+#define WM8983_TSDEN_MASK 0x0002 /* TSDEN */
+#define WM8983_TSDEN_SHIFT 1 /* TSDEN */
+#define WM8983_TSDEN_WIDTH 1 /* TSDEN */
+#define WM8983_VROI 0x0001 /* VROI */
+#define WM8983_VROI_MASK 0x0001 /* VROI */
+#define WM8983_VROI_SHIFT 0 /* VROI */
+#define WM8983_VROI_WIDTH 1 /* VROI */
+
+/*
+ * R50 (0x32) - Left mixer ctrl
+ */
+#define WM8983_AUXLMIXVOL_MASK 0x01C0 /* AUXLMIXVOL - [8:6] */
+#define WM8983_AUXLMIXVOL_SHIFT 6 /* AUXLMIXVOL - [8:6] */
+#define WM8983_AUXLMIXVOL_WIDTH 3 /* AUXLMIXVOL - [8:6] */
+#define WM8983_AUXL2LMIX 0x0020 /* AUXL2LMIX */
+#define WM8983_AUXL2LMIX_MASK 0x0020 /* AUXL2LMIX */
+#define WM8983_AUXL2LMIX_SHIFT 5 /* AUXL2LMIX */
+#define WM8983_AUXL2LMIX_WIDTH 1 /* AUXL2LMIX */
+#define WM8983_BYPLMIXVOL_MASK 0x001C /* BYPLMIXVOL - [4:2] */
+#define WM8983_BYPLMIXVOL_SHIFT 2 /* BYPLMIXVOL - [4:2] */
+#define WM8983_BYPLMIXVOL_WIDTH 3 /* BYPLMIXVOL - [4:2] */
+#define WM8983_BYPL2LMIX 0x0002 /* BYPL2LMIX */
+#define WM8983_BYPL2LMIX_MASK 0x0002 /* BYPL2LMIX */
+#define WM8983_BYPL2LMIX_SHIFT 1 /* BYPL2LMIX */
+#define WM8983_BYPL2LMIX_WIDTH 1 /* BYPL2LMIX */
+#define WM8983_DACL2LMIX 0x0001 /* DACL2LMIX */
+#define WM8983_DACL2LMIX_MASK 0x0001 /* DACL2LMIX */
+#define WM8983_DACL2LMIX_SHIFT 0 /* DACL2LMIX */
+#define WM8983_DACL2LMIX_WIDTH 1 /* DACL2LMIX */
+
+/*
+ * R51 (0x33) - Right mixer ctrl
+ */
+#define WM8983_AUXRMIXVOL_MASK 0x01C0 /* AUXRMIXVOL - [8:6] */
+#define WM8983_AUXRMIXVOL_SHIFT 6 /* AUXRMIXVOL - [8:6] */
+#define WM8983_AUXRMIXVOL_WIDTH 3 /* AUXRMIXVOL - [8:6] */
+#define WM8983_AUXR2RMIX 0x0020 /* AUXR2RMIX */
+#define WM8983_AUXR2RMIX_MASK 0x0020 /* AUXR2RMIX */
+#define WM8983_AUXR2RMIX_SHIFT 5 /* AUXR2RMIX */
+#define WM8983_AUXR2RMIX_WIDTH 1 /* AUXR2RMIX */
+#define WM8983_BYPRMIXVOL_MASK 0x001C /* BYPRMIXVOL - [4:2] */
+#define WM8983_BYPRMIXVOL_SHIFT 2 /* BYPRMIXVOL - [4:2] */
+#define WM8983_BYPRMIXVOL_WIDTH 3 /* BYPRMIXVOL - [4:2] */
+#define WM8983_BYPR2RMIX 0x0002 /* BYPR2RMIX */
+#define WM8983_BYPR2RMIX_MASK 0x0002 /* BYPR2RMIX */
+#define WM8983_BYPR2RMIX_SHIFT 1 /* BYPR2RMIX */
+#define WM8983_BYPR2RMIX_WIDTH 1 /* BYPR2RMIX */
+#define WM8983_DACR2RMIX 0x0001 /* DACR2RMIX */
+#define WM8983_DACR2RMIX_MASK 0x0001 /* DACR2RMIX */
+#define WM8983_DACR2RMIX_SHIFT 0 /* DACR2RMIX */
+#define WM8983_DACR2RMIX_WIDTH 1 /* DACR2RMIX */
+
+/*
+ * R52 (0x34) - LOUT1 (HP) volume ctrl
+ */
+#define WM8983_OUT1VU 0x0100 /* OUT1VU */
+#define WM8983_OUT1VU_MASK 0x0100 /* OUT1VU */
+#define WM8983_OUT1VU_SHIFT 8 /* OUT1VU */
+#define WM8983_OUT1VU_WIDTH 1 /* OUT1VU */
+#define WM8983_LOUT1ZC 0x0080 /* LOUT1ZC */
+#define WM8983_LOUT1ZC_MASK 0x0080 /* LOUT1ZC */
+#define WM8983_LOUT1ZC_SHIFT 7 /* LOUT1ZC */
+#define WM8983_LOUT1ZC_WIDTH 1 /* LOUT1ZC */
+#define WM8983_LOUT1MUTE 0x0040 /* LOUT1MUTE */
+#define WM8983_LOUT1MUTE_MASK 0x0040 /* LOUT1MUTE */
+#define WM8983_LOUT1MUTE_SHIFT 6 /* LOUT1MUTE */
+#define WM8983_LOUT1MUTE_WIDTH 1 /* LOUT1MUTE */
+#define WM8983_LOUT1VOL_MASK 0x003F /* LOUT1VOL - [5:0] */
+#define WM8983_LOUT1VOL_SHIFT 0 /* LOUT1VOL - [5:0] */
+#define WM8983_LOUT1VOL_WIDTH 6 /* LOUT1VOL - [5:0] */
+
+/*
+ * R53 (0x35) - ROUT1 (HP) volume ctrl
+ */
+#define WM8983_OUT1VU 0x0100 /* OUT1VU */
+#define WM8983_OUT1VU_MASK 0x0100 /* OUT1VU */
+#define WM8983_OUT1VU_SHIFT 8 /* OUT1VU */
+#define WM8983_OUT1VU_WIDTH 1 /* OUT1VU */
+#define WM8983_ROUT1ZC 0x0080 /* ROUT1ZC */
+#define WM8983_ROUT1ZC_MASK 0x0080 /* ROUT1ZC */
+#define WM8983_ROUT1ZC_SHIFT 7 /* ROUT1ZC */
+#define WM8983_ROUT1ZC_WIDTH 1 /* ROUT1ZC */
+#define WM8983_ROUT1MUTE 0x0040 /* ROUT1MUTE */
+#define WM8983_ROUT1MUTE_MASK 0x0040 /* ROUT1MUTE */
+#define WM8983_ROUT1MUTE_SHIFT 6 /* ROUT1MUTE */
+#define WM8983_ROUT1MUTE_WIDTH 1 /* ROUT1MUTE */
+#define WM8983_ROUT1VOL_MASK 0x003F /* ROUT1VOL - [5:0] */
+#define WM8983_ROUT1VOL_SHIFT 0 /* ROUT1VOL - [5:0] */
+#define WM8983_ROUT1VOL_WIDTH 6 /* ROUT1VOL - [5:0] */
+
+/*
+ * R54 (0x36) - LOUT2 (SPK) volume ctrl
+ */
+#define WM8983_OUT2VU 0x0100 /* OUT2VU */
+#define WM8983_OUT2VU_MASK 0x0100 /* OUT2VU */
+#define WM8983_OUT2VU_SHIFT 8 /* OUT2VU */
+#define WM8983_OUT2VU_WIDTH 1 /* OUT2VU */
+#define WM8983_LOUT2ZC 0x0080 /* LOUT2ZC */
+#define WM8983_LOUT2ZC_MASK 0x0080 /* LOUT2ZC */
+#define WM8983_LOUT2ZC_SHIFT 7 /* LOUT2ZC */
+#define WM8983_LOUT2ZC_WIDTH 1 /* LOUT2ZC */
+#define WM8983_LOUT2MUTE 0x0040 /* LOUT2MUTE */
+#define WM8983_LOUT2MUTE_MASK 0x0040 /* LOUT2MUTE */
+#define WM8983_LOUT2MUTE_SHIFT 6 /* LOUT2MUTE */
+#define WM8983_LOUT2MUTE_WIDTH 1 /* LOUT2MUTE */
+#define WM8983_LOUT2VOL_MASK 0x003F /* LOUT2VOL - [5:0] */
+#define WM8983_LOUT2VOL_SHIFT 0 /* LOUT2VOL - [5:0] */
+#define WM8983_LOUT2VOL_WIDTH 6 /* LOUT2VOL - [5:0] */
+
+/*
+ * R55 (0x37) - ROUT2 (SPK) volume ctrl
+ */
+#define WM8983_OUT2VU 0x0100 /* OUT2VU */
+#define WM8983_OUT2VU_MASK 0x0100 /* OUT2VU */
+#define WM8983_OUT2VU_SHIFT 8 /* OUT2VU */
+#define WM8983_OUT2VU_WIDTH 1 /* OUT2VU */
+#define WM8983_ROUT2ZC 0x0080 /* ROUT2ZC */
+#define WM8983_ROUT2ZC_MASK 0x0080 /* ROUT2ZC */
+#define WM8983_ROUT2ZC_SHIFT 7 /* ROUT2ZC */
+#define WM8983_ROUT2ZC_WIDTH 1 /* ROUT2ZC */
+#define WM8983_ROUT2MUTE 0x0040 /* ROUT2MUTE */
+#define WM8983_ROUT2MUTE_MASK 0x0040 /* ROUT2MUTE */
+#define WM8983_ROUT2MUTE_SHIFT 6 /* ROUT2MUTE */
+#define WM8983_ROUT2MUTE_WIDTH 1 /* ROUT2MUTE */
+#define WM8983_ROUT2VOL_MASK 0x003F /* ROUT2VOL - [5:0] */
+#define WM8983_ROUT2VOL_SHIFT 0 /* ROUT2VOL - [5:0] */
+#define WM8983_ROUT2VOL_WIDTH 6 /* ROUT2VOL - [5:0] */
+
+/*
+ * R56 (0x38) - OUT3 mixer ctrl
+ */
+#define WM8983_OUT3MUTE 0x0040 /* OUT3MUTE */
+#define WM8983_OUT3MUTE_MASK 0x0040 /* OUT3MUTE */
+#define WM8983_OUT3MUTE_SHIFT 6 /* OUT3MUTE */
+#define WM8983_OUT3MUTE_WIDTH 1 /* OUT3MUTE */
+#define WM8983_OUT4_2OUT3 0x0008 /* OUT4_2OUT3 */
+#define WM8983_OUT4_2OUT3_MASK 0x0008 /* OUT4_2OUT3 */
+#define WM8983_OUT4_2OUT3_SHIFT 3 /* OUT4_2OUT3 */
+#define WM8983_OUT4_2OUT3_WIDTH 1 /* OUT4_2OUT3 */
+#define WM8983_BYPL2OUT3 0x0004 /* BYPL2OUT3 */
+#define WM8983_BYPL2OUT3_MASK 0x0004 /* BYPL2OUT3 */
+#define WM8983_BYPL2OUT3_SHIFT 2 /* BYPL2OUT3 */
+#define WM8983_BYPL2OUT3_WIDTH 1 /* BYPL2OUT3 */
+#define WM8983_LMIX2OUT3 0x0002 /* LMIX2OUT3 */
+#define WM8983_LMIX2OUT3_MASK 0x0002 /* LMIX2OUT3 */
+#define WM8983_LMIX2OUT3_SHIFT 1 /* LMIX2OUT3 */
+#define WM8983_LMIX2OUT3_WIDTH 1 /* LMIX2OUT3 */
+#define WM8983_LDAC2OUT3 0x0001 /* LDAC2OUT3 */
+#define WM8983_LDAC2OUT3_MASK 0x0001 /* LDAC2OUT3 */
+#define WM8983_LDAC2OUT3_SHIFT 0 /* LDAC2OUT3 */
+#define WM8983_LDAC2OUT3_WIDTH 1 /* LDAC2OUT3 */
+
+/*
+ * R57 (0x39) - OUT4 (MONO) mix ctrl
+ */
+#define WM8983_OUT3_2OUT4 0x0080 /* OUT3_2OUT4 */
+#define WM8983_OUT3_2OUT4_MASK 0x0080 /* OUT3_2OUT4 */
+#define WM8983_OUT3_2OUT4_SHIFT 7 /* OUT3_2OUT4 */
+#define WM8983_OUT3_2OUT4_WIDTH 1 /* OUT3_2OUT4 */
+#define WM8983_OUT4MUTE 0x0040 /* OUT4MUTE */
+#define WM8983_OUT4MUTE_MASK 0x0040 /* OUT4MUTE */
+#define WM8983_OUT4MUTE_SHIFT 6 /* OUT4MUTE */
+#define WM8983_OUT4MUTE_WIDTH 1 /* OUT4MUTE */
+#define WM8983_OUT4ATTN 0x0020 /* OUT4ATTN */
+#define WM8983_OUT4ATTN_MASK 0x0020 /* OUT4ATTN */
+#define WM8983_OUT4ATTN_SHIFT 5 /* OUT4ATTN */
+#define WM8983_OUT4ATTN_WIDTH 1 /* OUT4ATTN */
+#define WM8983_LMIX2OUT4 0x0010 /* LMIX2OUT4 */
+#define WM8983_LMIX2OUT4_MASK 0x0010 /* LMIX2OUT4 */
+#define WM8983_LMIX2OUT4_SHIFT 4 /* LMIX2OUT4 */
+#define WM8983_LMIX2OUT4_WIDTH 1 /* LMIX2OUT4 */
+#define WM8983_LDAC2OUT4 0x0008 /* LDAC2OUT4 */
+#define WM8983_LDAC2OUT4_MASK 0x0008 /* LDAC2OUT4 */
+#define WM8983_LDAC2OUT4_SHIFT 3 /* LDAC2OUT4 */
+#define WM8983_LDAC2OUT4_WIDTH 1 /* LDAC2OUT4 */
+#define WM8983_BYPR2OUT4 0x0004 /* BYPR2OUT4 */
+#define WM8983_BYPR2OUT4_MASK 0x0004 /* BYPR2OUT4 */
+#define WM8983_BYPR2OUT4_SHIFT 2 /* BYPR2OUT4 */
+#define WM8983_BYPR2OUT4_WIDTH 1 /* BYPR2OUT4 */
+#define WM8983_RMIX2OUT4 0x0002 /* RMIX2OUT4 */
+#define WM8983_RMIX2OUT4_MASK 0x0002 /* RMIX2OUT4 */
+#define WM8983_RMIX2OUT4_SHIFT 1 /* RMIX2OUT4 */
+#define WM8983_RMIX2OUT4_WIDTH 1 /* RMIX2OUT4 */
+#define WM8983_RDAC2OUT4 0x0001 /* RDAC2OUT4 */
+#define WM8983_RDAC2OUT4_MASK 0x0001 /* RDAC2OUT4 */
+#define WM8983_RDAC2OUT4_SHIFT 0 /* RDAC2OUT4 */
+#define WM8983_RDAC2OUT4_WIDTH 1 /* RDAC2OUT4 */
+
+/*
+ * R61 (0x3D) - BIAS CTRL
+ */
+#define WM8983_BIASCUT 0x0100 /* BIASCUT */
+#define WM8983_BIASCUT_MASK 0x0100 /* BIASCUT */
+#define WM8983_BIASCUT_SHIFT 8 /* BIASCUT */
+#define WM8983_BIASCUT_WIDTH 1 /* BIASCUT */
+#define WM8983_HALFIPBIAS 0x0080 /* HALFIPBIAS */
+#define WM8983_HALFIPBIAS_MASK 0x0080 /* HALFIPBIAS */
+#define WM8983_HALFIPBIAS_SHIFT 7 /* HALFIPBIAS */
+#define WM8983_HALFIPBIAS_WIDTH 1 /* HALFIPBIAS */
+#define WM8983_VBBIASTST_MASK 0x0060 /* VBBIASTST - [6:5] */
+#define WM8983_VBBIASTST_SHIFT 5 /* VBBIASTST - [6:5] */
+#define WM8983_VBBIASTST_WIDTH 2 /* VBBIASTST - [6:5] */
+#define WM8983_BUFBIAS_MASK 0x0018 /* BUFBIAS - [4:3] */
+#define WM8983_BUFBIAS_SHIFT 3 /* BUFBIAS - [4:3] */
+#define WM8983_BUFBIAS_WIDTH 2 /* BUFBIAS - [4:3] */
+#define WM8983_ADCBIAS_MASK 0x0006 /* ADCBIAS - [2:1] */
+#define WM8983_ADCBIAS_SHIFT 1 /* ADCBIAS - [2:1] */
+#define WM8983_ADCBIAS_WIDTH 2 /* ADCBIAS - [2:1] */
+#define WM8983_HALFOPBIAS 0x0001 /* HALFOPBIAS */
+#define WM8983_HALFOPBIAS_MASK 0x0001 /* HALFOPBIAS */
+#define WM8983_HALFOPBIAS_SHIFT 0 /* HALFOPBIAS */
+#define WM8983_HALFOPBIAS_WIDTH 1 /* HALFOPBIAS */
+
+enum clk_src {
+ WM8983_CLKSRC_MCLK,
+ WM8983_CLKSRC_PLL
+};
+
+#endif /* _WM8983_H */
diff --git a/kernel/sound/soc/codecs/wm8985.c b/kernel/sound/soc/codecs/wm8985.c
new file mode 100644
index 000000000..0b3b54c99
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8985.c
@@ -0,0 +1,1191 @@
+/*
+ * wm8985.c -- WM8985 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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.
+ *
+ * TODO:
+ * o Add OUT3/OUT4 mixer controls.
+ */
+
+#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 <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 "wm8985.h"
+
+#define WM8985_NUM_SUPPLIES 4
+static const char *wm8985_supply_names[WM8985_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD",
+ "AVDD1",
+ "AVDD2"
+};
+
+static const struct reg_default wm8985_reg_defaults[] = {
+ { 1, 0x0000 }, /* R1 - Power management 1 */
+ { 2, 0x0000 }, /* R2 - Power management 2 */
+ { 3, 0x0000 }, /* R3 - Power management 3 */
+ { 4, 0x0050 }, /* R4 - Audio Interface */
+ { 5, 0x0000 }, /* R5 - Companding control */
+ { 6, 0x0140 }, /* R6 - Clock Gen control */
+ { 7, 0x0000 }, /* R7 - Additional control */
+ { 8, 0x0000 }, /* R8 - GPIO Control */
+ { 9, 0x0000 }, /* R9 - Jack Detect Control 1 */
+ { 10, 0x0000 }, /* R10 - DAC Control */
+ { 11, 0x00FF }, /* R11 - Left DAC digital Vol */
+ { 12, 0x00FF }, /* R12 - Right DAC digital vol */
+ { 13, 0x0000 }, /* R13 - Jack Detect Control 2 */
+ { 14, 0x0100 }, /* R14 - ADC Control */
+ { 15, 0x00FF }, /* R15 - Left ADC Digital Vol */
+ { 16, 0x00FF }, /* R16 - Right ADC Digital Vol */
+ { 18, 0x012C }, /* R18 - EQ1 - low shelf */
+ { 19, 0x002C }, /* R19 - EQ2 - peak 1 */
+ { 20, 0x002C }, /* R20 - EQ3 - peak 2 */
+ { 21, 0x002C }, /* R21 - EQ4 - peak 3 */
+ { 22, 0x002C }, /* R22 - EQ5 - high shelf */
+ { 24, 0x0032 }, /* R24 - DAC Limiter 1 */
+ { 25, 0x0000 }, /* R25 - DAC Limiter 2 */
+ { 27, 0x0000 }, /* R27 - Notch Filter 1 */
+ { 28, 0x0000 }, /* R28 - Notch Filter 2 */
+ { 29, 0x0000 }, /* R29 - Notch Filter 3 */
+ { 30, 0x0000 }, /* R30 - Notch Filter 4 */
+ { 32, 0x0038 }, /* R32 - ALC control 1 */
+ { 33, 0x000B }, /* R33 - ALC control 2 */
+ { 34, 0x0032 }, /* R34 - ALC control 3 */
+ { 35, 0x0000 }, /* R35 - Noise Gate */
+ { 36, 0x0008 }, /* R36 - PLL N */
+ { 37, 0x000C }, /* R37 - PLL K 1 */
+ { 38, 0x0093 }, /* R38 - PLL K 2 */
+ { 39, 0x00E9 }, /* R39 - PLL K 3 */
+ { 41, 0x0000 }, /* R41 - 3D control */
+ { 42, 0x0000 }, /* R42 - OUT4 to ADC */
+ { 43, 0x0000 }, /* R43 - Beep control */
+ { 44, 0x0033 }, /* R44 - Input ctrl */
+ { 45, 0x0010 }, /* R45 - Left INP PGA gain ctrl */
+ { 46, 0x0010 }, /* R46 - Right INP PGA gain ctrl */
+ { 47, 0x0100 }, /* R47 - Left ADC BOOST ctrl */
+ { 48, 0x0100 }, /* R48 - Right ADC BOOST ctrl */
+ { 49, 0x0002 }, /* R49 - Output ctrl */
+ { 50, 0x0001 }, /* R50 - Left mixer ctrl */
+ { 51, 0x0001 }, /* R51 - Right mixer ctrl */
+ { 52, 0x0039 }, /* R52 - LOUT1 (HP) volume ctrl */
+ { 53, 0x0039 }, /* R53 - ROUT1 (HP) volume ctrl */
+ { 54, 0x0039 }, /* R54 - LOUT2 (SPK) volume ctrl */
+ { 55, 0x0039 }, /* R55 - ROUT2 (SPK) volume ctrl */
+ { 56, 0x0001 }, /* R56 - OUT3 mixer ctrl */
+ { 57, 0x0001 }, /* R57 - OUT4 (MONO) mix ctrl */
+ { 60, 0x0004 }, /* R60 - OUTPUT ctrl */
+ { 61, 0x0000 }, /* R61 - BIAS CTRL */
+};
+
+static bool wm8985_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8985_SOFTWARE_RESET:
+ case WM8985_POWER_MANAGEMENT_1:
+ case WM8985_POWER_MANAGEMENT_2:
+ case WM8985_POWER_MANAGEMENT_3:
+ case WM8985_AUDIO_INTERFACE:
+ case WM8985_COMPANDING_CONTROL:
+ case WM8985_CLOCK_GEN_CONTROL:
+ case WM8985_ADDITIONAL_CONTROL:
+ case WM8985_GPIO_CONTROL:
+ case WM8985_JACK_DETECT_CONTROL_1:
+ case WM8985_DAC_CONTROL:
+ case WM8985_LEFT_DAC_DIGITAL_VOL:
+ case WM8985_RIGHT_DAC_DIGITAL_VOL:
+ case WM8985_JACK_DETECT_CONTROL_2:
+ case WM8985_ADC_CONTROL:
+ case WM8985_LEFT_ADC_DIGITAL_VOL:
+ case WM8985_RIGHT_ADC_DIGITAL_VOL:
+ case WM8985_EQ1_LOW_SHELF:
+ case WM8985_EQ2_PEAK_1:
+ case WM8985_EQ3_PEAK_2:
+ case WM8985_EQ4_PEAK_3:
+ case WM8985_EQ5_HIGH_SHELF:
+ case WM8985_DAC_LIMITER_1:
+ case WM8985_DAC_LIMITER_2:
+ case WM8985_NOTCH_FILTER_1:
+ case WM8985_NOTCH_FILTER_2:
+ case WM8985_NOTCH_FILTER_3:
+ case WM8985_NOTCH_FILTER_4:
+ case WM8985_ALC_CONTROL_1:
+ case WM8985_ALC_CONTROL_2:
+ case WM8985_ALC_CONTROL_3:
+ case WM8985_NOISE_GATE:
+ case WM8985_PLL_N:
+ case WM8985_PLL_K_1:
+ case WM8985_PLL_K_2:
+ case WM8985_PLL_K_3:
+ case WM8985_3D_CONTROL:
+ case WM8985_OUT4_TO_ADC:
+ case WM8985_BEEP_CONTROL:
+ case WM8985_INPUT_CTRL:
+ case WM8985_LEFT_INP_PGA_GAIN_CTRL:
+ case WM8985_RIGHT_INP_PGA_GAIN_CTRL:
+ case WM8985_LEFT_ADC_BOOST_CTRL:
+ case WM8985_RIGHT_ADC_BOOST_CTRL:
+ case WM8985_OUTPUT_CTRL0:
+ case WM8985_LEFT_MIXER_CTRL:
+ case WM8985_RIGHT_MIXER_CTRL:
+ case WM8985_LOUT1_HP_VOLUME_CTRL:
+ case WM8985_ROUT1_HP_VOLUME_CTRL:
+ case WM8985_LOUT2_SPK_VOLUME_CTRL:
+ case WM8985_ROUT2_SPK_VOLUME_CTRL:
+ case WM8985_OUT3_MIXER_CTRL:
+ case WM8985_OUT4_MONO_MIX_CTRL:
+ case WM8985_OUTPUT_CTRL1:
+ case WM8985_BIAS_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * latch bit 8 of these registers to ensure instant
+ * volume updates
+ */
+static const int volume_update_regs[] = {
+ WM8985_LEFT_DAC_DIGITAL_VOL,
+ WM8985_RIGHT_DAC_DIGITAL_VOL,
+ WM8985_LEFT_ADC_DIGITAL_VOL,
+ WM8985_RIGHT_ADC_DIGITAL_VOL,
+ WM8985_LOUT2_SPK_VOLUME_CTRL,
+ WM8985_ROUT2_SPK_VOLUME_CTRL,
+ WM8985_LOUT1_HP_VOLUME_CTRL,
+ WM8985_ROUT1_HP_VOLUME_CTRL,
+ WM8985_LEFT_INP_PGA_GAIN_CTRL,
+ WM8985_RIGHT_INP_PGA_GAIN_CTRL
+};
+
+struct wm8985_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[WM8985_NUM_SUPPLIES];
+ unsigned int sysclk;
+ unsigned int bclk;
+};
+
+static const struct {
+ int div;
+ int ratio;
+} fs_ratios[] = {
+ { 10, 128 },
+ { 15, 192 },
+ { 20, 256 },
+ { 30, 384 },
+ { 40, 512 },
+ { 60, 768 },
+ { 80, 1024 },
+ { 120, 1536 }
+};
+
+static const int srates[] = { 48000, 32000, 24000, 16000, 12000, 8000 };
+
+static const int bclk_divs[] = {
+ 1, 2, 4, 8, 16, 32
+};
+
+static int eqmode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int eqmode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(lim_thresh_tlv, -600, 100, 0);
+static const DECLARE_TLV_DB_SCALE(lim_boost_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(alc_min_tlv, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(alc_max_tlv, -675, 600, 0);
+static const DECLARE_TLV_DB_SCALE(alc_tar_tlv, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(pga_vol_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(aux_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0);
+
+static const char *alc_sel_text[] = { "Off", "Right", "Left", "Stereo" };
+static SOC_ENUM_SINGLE_DECL(alc_sel, WM8985_ALC_CONTROL_1, 7, alc_sel_text);
+
+static const char *alc_mode_text[] = { "ALC", "Limiter" };
+static SOC_ENUM_SINGLE_DECL(alc_mode, WM8985_ALC_CONTROL_3, 8, alc_mode_text);
+
+static const char *filter_mode_text[] = { "Audio", "Application" };
+static SOC_ENUM_SINGLE_DECL(filter_mode, WM8985_ADC_CONTROL, 7,
+ filter_mode_text);
+
+static const char *eq_bw_text[] = { "Narrow", "Wide" };
+static const char *eqmode_text[] = { "Capture", "Playback" };
+static SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text);
+
+static const char *eq1_cutoff_text[] = {
+ "80Hz", "105Hz", "135Hz", "175Hz"
+};
+static SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8985_EQ1_LOW_SHELF, 5,
+ eq1_cutoff_text);
+static const char *eq2_cutoff_text[] = {
+ "230Hz", "300Hz", "385Hz", "500Hz"
+};
+static SOC_ENUM_SINGLE_DECL(eq2_bw, WM8985_EQ2_PEAK_1, 8, eq_bw_text);
+static SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8985_EQ2_PEAK_1, 5, eq2_cutoff_text);
+static const char *eq3_cutoff_text[] = {
+ "650Hz", "850Hz", "1.1kHz", "1.4kHz"
+};
+static SOC_ENUM_SINGLE_DECL(eq3_bw, WM8985_EQ3_PEAK_2, 8, eq_bw_text);
+static SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8985_EQ3_PEAK_2, 5,
+ eq3_cutoff_text);
+static const char *eq4_cutoff_text[] = {
+ "1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz"
+};
+static SOC_ENUM_SINGLE_DECL(eq4_bw, WM8985_EQ4_PEAK_3, 8, eq_bw_text);
+static SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8985_EQ4_PEAK_3, 5, eq4_cutoff_text);
+static const char *eq5_cutoff_text[] = {
+ "5.3kHz", "6.9kHz", "9kHz", "11.7kHz"
+};
+static SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8985_EQ5_HIGH_SHELF, 5,
+ eq5_cutoff_text);
+
+static const char *speaker_mode_text[] = { "Class A/B", "Class D" };
+static SOC_ENUM_SINGLE_DECL(speaker_mode, 0x17, 8, speaker_mode_text);
+
+static const char *depth_3d_text[] = {
+ "Off",
+ "6.67%",
+ "13.3%",
+ "20%",
+ "26.7%",
+ "33.3%",
+ "40%",
+ "46.6%",
+ "53.3%",
+ "60%",
+ "66.7%",
+ "73.3%",
+ "80%",
+ "86.7%",
+ "93.3%",
+ "100%"
+};
+static SOC_ENUM_SINGLE_DECL(depth_3d, WM8985_3D_CONTROL, 0, depth_3d_text);
+
+static const struct snd_kcontrol_new wm8985_snd_controls[] = {
+ SOC_SINGLE("Digital Loopback Switch", WM8985_COMPANDING_CONTROL,
+ 0, 1, 0),
+
+ SOC_ENUM("ALC Capture Function", alc_sel),
+ SOC_SINGLE_TLV("ALC Capture Max Volume", WM8985_ALC_CONTROL_1,
+ 3, 7, 0, alc_max_tlv),
+ SOC_SINGLE_TLV("ALC Capture Min Volume", WM8985_ALC_CONTROL_1,
+ 0, 7, 0, alc_min_tlv),
+ SOC_SINGLE_TLV("ALC Capture Target Volume", WM8985_ALC_CONTROL_2,
+ 0, 15, 0, alc_tar_tlv),
+ SOC_SINGLE("ALC Capture Attack", WM8985_ALC_CONTROL_3, 0, 10, 0),
+ SOC_SINGLE("ALC Capture Hold", WM8985_ALC_CONTROL_2, 4, 10, 0),
+ SOC_SINGLE("ALC Capture Decay", WM8985_ALC_CONTROL_3, 4, 10, 0),
+ SOC_ENUM("ALC Mode", alc_mode),
+ SOC_SINGLE("ALC Capture NG Switch", WM8985_NOISE_GATE,
+ 3, 1, 0),
+ SOC_SINGLE("ALC Capture NG Threshold", WM8985_NOISE_GATE,
+ 0, 7, 1),
+
+ SOC_DOUBLE_R_TLV("Capture Volume", WM8985_LEFT_ADC_DIGITAL_VOL,
+ WM8985_RIGHT_ADC_DIGITAL_VOL, 0, 255, 0, adc_tlv),
+ SOC_DOUBLE_R("Capture PGA ZC Switch", WM8985_LEFT_INP_PGA_GAIN_CTRL,
+ WM8985_RIGHT_INP_PGA_GAIN_CTRL, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("Capture PGA Volume", WM8985_LEFT_INP_PGA_GAIN_CTRL,
+ WM8985_RIGHT_INP_PGA_GAIN_CTRL, 0, 63, 0, pga_vol_tlv),
+
+ SOC_DOUBLE_R_TLV("Capture PGA Boost Volume",
+ WM8985_LEFT_ADC_BOOST_CTRL, WM8985_RIGHT_ADC_BOOST_CTRL,
+ 8, 1, 0, pga_boost_tlv),
+
+ SOC_DOUBLE("ADC Inversion Switch", WM8985_ADC_CONTROL, 0, 1, 1, 0),
+ SOC_SINGLE("ADC 128x Oversampling Switch", WM8985_ADC_CONTROL, 8, 1, 0),
+
+ SOC_DOUBLE_R_TLV("Playback Volume", WM8985_LEFT_DAC_DIGITAL_VOL,
+ WM8985_RIGHT_DAC_DIGITAL_VOL, 0, 255, 0, dac_tlv),
+
+ SOC_SINGLE("DAC Playback Limiter Switch", WM8985_DAC_LIMITER_1, 8, 1, 0),
+ SOC_SINGLE("DAC Playback Limiter Decay", WM8985_DAC_LIMITER_1, 4, 10, 0),
+ SOC_SINGLE("DAC Playback Limiter Attack", WM8985_DAC_LIMITER_1, 0, 11, 0),
+ SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8985_DAC_LIMITER_2,
+ 4, 7, 1, lim_thresh_tlv),
+ SOC_SINGLE_TLV("DAC Playback Limiter Boost Volume", WM8985_DAC_LIMITER_2,
+ 0, 12, 0, lim_boost_tlv),
+ SOC_DOUBLE("DAC Inversion Switch", WM8985_DAC_CONTROL, 0, 1, 1, 0),
+ SOC_SINGLE("DAC Auto Mute Switch", WM8985_DAC_CONTROL, 2, 1, 0),
+ SOC_SINGLE("DAC 128x Oversampling Switch", WM8985_DAC_CONTROL, 3, 1, 0),
+
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8985_LOUT1_HP_VOLUME_CTRL,
+ WM8985_ROUT1_HP_VOLUME_CTRL, 0, 63, 0, out_tlv),
+ SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8985_LOUT1_HP_VOLUME_CTRL,
+ WM8985_ROUT1_HP_VOLUME_CTRL, 7, 1, 0),
+ SOC_DOUBLE_R("Headphone Switch", WM8985_LOUT1_HP_VOLUME_CTRL,
+ WM8985_ROUT1_HP_VOLUME_CTRL, 6, 1, 1),
+
+ SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8985_LOUT2_SPK_VOLUME_CTRL,
+ WM8985_ROUT2_SPK_VOLUME_CTRL, 0, 63, 0, out_tlv),
+ SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8985_LOUT2_SPK_VOLUME_CTRL,
+ WM8985_ROUT2_SPK_VOLUME_CTRL, 7, 1, 0),
+ SOC_DOUBLE_R("Speaker Switch", WM8985_LOUT2_SPK_VOLUME_CTRL,
+ WM8985_ROUT2_SPK_VOLUME_CTRL, 6, 1, 1),
+
+ SOC_SINGLE("High Pass Filter Switch", WM8985_ADC_CONTROL, 8, 1, 0),
+ SOC_ENUM("High Pass Filter Mode", filter_mode),
+ SOC_SINGLE("High Pass Filter Cutoff", WM8985_ADC_CONTROL, 4, 7, 0),
+
+ SOC_DOUBLE_R_TLV("Aux Bypass Volume",
+ WM8985_LEFT_MIXER_CTRL, WM8985_RIGHT_MIXER_CTRL, 6, 7, 0,
+ aux_tlv),
+
+ SOC_DOUBLE_R_TLV("Input PGA Bypass Volume",
+ WM8985_LEFT_MIXER_CTRL, WM8985_RIGHT_MIXER_CTRL, 2, 7, 0,
+ bypass_tlv),
+
+ SOC_ENUM_EXT("Equalizer Function", eqmode, eqmode_get, eqmode_put),
+ SOC_ENUM("EQ1 Cutoff", eq1_cutoff),
+ SOC_SINGLE_TLV("EQ1 Volume", WM8985_EQ1_LOW_SHELF, 0, 24, 1, eq_tlv),
+ SOC_ENUM("EQ2 Bandwidth", eq2_bw),
+ SOC_ENUM("EQ2 Cutoff", eq2_cutoff),
+ SOC_SINGLE_TLV("EQ2 Volume", WM8985_EQ2_PEAK_1, 0, 24, 1, eq_tlv),
+ SOC_ENUM("EQ3 Bandwidth", eq3_bw),
+ SOC_ENUM("EQ3 Cutoff", eq3_cutoff),
+ SOC_SINGLE_TLV("EQ3 Volume", WM8985_EQ3_PEAK_2, 0, 24, 1, eq_tlv),
+ SOC_ENUM("EQ4 Bandwidth", eq4_bw),
+ SOC_ENUM("EQ4 Cutoff", eq4_cutoff),
+ SOC_SINGLE_TLV("EQ4 Volume", WM8985_EQ4_PEAK_3, 0, 24, 1, eq_tlv),
+ SOC_ENUM("EQ5 Cutoff", eq5_cutoff),
+ SOC_SINGLE_TLV("EQ5 Volume", WM8985_EQ5_HIGH_SHELF, 0, 24, 1, eq_tlv),
+
+ SOC_ENUM("3D Depth", depth_3d),
+
+ SOC_ENUM("Speaker Mode", speaker_mode)
+};
+
+static const struct snd_kcontrol_new left_out_mixer[] = {
+ SOC_DAPM_SINGLE("Line Switch", WM8985_LEFT_MIXER_CTRL, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux Switch", WM8985_LEFT_MIXER_CTRL, 5, 1, 0),
+ SOC_DAPM_SINGLE("PCM Switch", WM8985_LEFT_MIXER_CTRL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_out_mixer[] = {
+ SOC_DAPM_SINGLE("Line Switch", WM8985_RIGHT_MIXER_CTRL, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux Switch", WM8985_RIGHT_MIXER_CTRL, 5, 1, 0),
+ SOC_DAPM_SINGLE("PCM Switch", WM8985_RIGHT_MIXER_CTRL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_input_mixer[] = {
+ SOC_DAPM_SINGLE("L2 Switch", WM8985_INPUT_CTRL, 2, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", WM8985_INPUT_CTRL, 1, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", WM8985_INPUT_CTRL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_input_mixer[] = {
+ SOC_DAPM_SINGLE("R2 Switch", WM8985_INPUT_CTRL, 6, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", WM8985_INPUT_CTRL, 5, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", WM8985_INPUT_CTRL, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_boost_mixer[] = {
+ SOC_DAPM_SINGLE_TLV("L2 Volume", WM8985_LEFT_ADC_BOOST_CTRL,
+ 4, 7, 0, boost_tlv),
+ SOC_DAPM_SINGLE_TLV("AUXL Volume", WM8985_LEFT_ADC_BOOST_CTRL,
+ 0, 7, 0, boost_tlv)
+};
+
+static const struct snd_kcontrol_new right_boost_mixer[] = {
+ SOC_DAPM_SINGLE_TLV("R2 Volume", WM8985_RIGHT_ADC_BOOST_CTRL,
+ 4, 7, 0, boost_tlv),
+ SOC_DAPM_SINGLE_TLV("AUXR Volume", WM8985_RIGHT_ADC_BOOST_CTRL,
+ 0, 7, 0, boost_tlv)
+};
+
+static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8985_POWER_MANAGEMENT_3,
+ 0, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8985_POWER_MANAGEMENT_3,
+ 1, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8985_POWER_MANAGEMENT_2,
+ 0, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8985_POWER_MANAGEMENT_2,
+ 1, 0),
+
+ SND_SOC_DAPM_MIXER("Left Output Mixer", WM8985_POWER_MANAGEMENT_3,
+ 2, 0, left_out_mixer, ARRAY_SIZE(left_out_mixer)),
+ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8985_POWER_MANAGEMENT_3,
+ 3, 0, right_out_mixer, ARRAY_SIZE(right_out_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left Input Mixer", WM8985_POWER_MANAGEMENT_2,
+ 2, 0, left_input_mixer, ARRAY_SIZE(left_input_mixer)),
+ SND_SOC_DAPM_MIXER("Right Input Mixer", WM8985_POWER_MANAGEMENT_2,
+ 3, 0, right_input_mixer, ARRAY_SIZE(right_input_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+ 4, 0, left_boost_mixer, ARRAY_SIZE(left_boost_mixer)),
+ SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+ 5, 0, right_boost_mixer, ARRAY_SIZE(right_boost_mixer)),
+
+ SND_SOC_DAPM_PGA("Left Capture PGA", WM8985_LEFT_INP_PGA_GAIN_CTRL,
+ 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Capture PGA", WM8985_RIGHT_INP_PGA_GAIN_CTRL,
+ 6, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Headphone Out", WM8985_POWER_MANAGEMENT_2,
+ 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Headphone Out", WM8985_POWER_MANAGEMENT_2,
+ 8, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Speaker Out", WM8985_POWER_MANAGEMENT_3,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Speaker Out", WM8985_POWER_MANAGEMENT_3,
+ 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", WM8985_POWER_MANAGEMENT_1, 4, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_INPUT("LIN"),
+ SND_SOC_DAPM_INPUT("LIP"),
+ SND_SOC_DAPM_INPUT("RIN"),
+ SND_SOC_DAPM_INPUT("RIP"),
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+ SND_SOC_DAPM_INPUT("L2"),
+ SND_SOC_DAPM_INPUT("R2"),
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR")
+};
+
+static const struct snd_soc_dapm_route wm8985_dapm_routes[] = {
+ { "Right Output Mixer", "PCM Switch", "Right DAC" },
+ { "Right Output Mixer", "Aux Switch", "AUXR" },
+ { "Right Output Mixer", "Line Switch", "Right Boost Mixer" },
+
+ { "Left Output Mixer", "PCM Switch", "Left DAC" },
+ { "Left Output Mixer", "Aux Switch", "AUXL" },
+ { "Left Output Mixer", "Line Switch", "Left Boost Mixer" },
+
+ { "Right Headphone Out", NULL, "Right Output Mixer" },
+ { "HPR", NULL, "Right Headphone Out" },
+
+ { "Left Headphone Out", NULL, "Left Output Mixer" },
+ { "HPL", NULL, "Left Headphone Out" },
+
+ { "Right Speaker Out", NULL, "Right Output Mixer" },
+ { "SPKR", NULL, "Right Speaker Out" },
+
+ { "Left Speaker Out", NULL, "Left Output Mixer" },
+ { "SPKL", NULL, "Left Speaker Out" },
+
+ { "Right ADC", NULL, "Right Boost Mixer" },
+
+ { "Right Boost Mixer", "AUXR Volume", "AUXR" },
+ { "Right Boost Mixer", NULL, "Right Capture PGA" },
+ { "Right Boost Mixer", "R2 Volume", "R2" },
+
+ { "Left ADC", NULL, "Left Boost Mixer" },
+
+ { "Left Boost Mixer", "AUXL Volume", "AUXL" },
+ { "Left Boost Mixer", NULL, "Left Capture PGA" },
+ { "Left Boost Mixer", "L2 Volume", "L2" },
+
+ { "Right Capture PGA", NULL, "Right Input Mixer" },
+ { "Left Capture PGA", NULL, "Left Input Mixer" },
+
+ { "Right Input Mixer", "R2 Switch", "R2" },
+ { "Right Input Mixer", "MicN Switch", "RIN" },
+ { "Right Input Mixer", "MicP Switch", "RIP" },
+
+ { "Left Input Mixer", "L2 Switch", "L2" },
+ { "Left Input Mixer", "MicN Switch", "LIN" },
+ { "Left Input Mixer", "MicP Switch", "LIP" },
+};
+
+static int eqmode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, WM8985_EQ1_LOW_SHELF);
+ if (reg & WM8985_EQ3DMODE)
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int eqmode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int regpwr2, regpwr3;
+ unsigned int reg_eq;
+
+ if (ucontrol->value.integer.value[0] != 0
+ && ucontrol->value.integer.value[0] != 1)
+ return -EINVAL;
+
+ reg_eq = snd_soc_read(codec, WM8985_EQ1_LOW_SHELF);
+ switch ((reg_eq & WM8985_EQ3DMODE) >> WM8985_EQ3DMODE_SHIFT) {
+ case 0:
+ if (!ucontrol->value.integer.value[0])
+ return 0;
+ break;
+ case 1:
+ if (ucontrol->value.integer.value[0])
+ return 0;
+ break;
+ }
+
+ regpwr2 = snd_soc_read(codec, WM8985_POWER_MANAGEMENT_2);
+ regpwr3 = snd_soc_read(codec, WM8985_POWER_MANAGEMENT_3);
+ /* disable the DACs and ADCs */
+ snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_2,
+ WM8985_ADCENR_MASK | WM8985_ADCENL_MASK, 0);
+ snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_3,
+ WM8985_DACENR_MASK | WM8985_DACENL_MASK, 0);
+ snd_soc_update_bits(codec, WM8985_ADDITIONAL_CONTROL,
+ WM8985_M128ENB_MASK, WM8985_M128ENB);
+ /* set the desired eqmode */
+ snd_soc_update_bits(codec, WM8985_EQ1_LOW_SHELF,
+ WM8985_EQ3DMODE_MASK,
+ ucontrol->value.integer.value[0]
+ << WM8985_EQ3DMODE_SHIFT);
+ /* restore DAC/ADC configuration */
+ snd_soc_write(codec, WM8985_POWER_MANAGEMENT_2, regpwr2);
+ snd_soc_write(codec, WM8985_POWER_MANAGEMENT_3, regpwr3);
+ return 0;
+}
+
+static int wm8985_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, WM8985_SOFTWARE_RESET, 0x0);
+}
+
+static int wm8985_dac_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ return snd_soc_update_bits(codec, WM8985_DAC_CONTROL,
+ WM8985_SOFTMUTE_MASK,
+ !!mute << WM8985_SOFTMUTE_SHIFT);
+}
+
+static int wm8985_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec;
+ u16 format, master, bcp, lrp;
+
+ codec = dai->codec;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = 0x2;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ format = 0x0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = 0x1;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ format = 0x3;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown dai format\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8985_AUDIO_INTERFACE,
+ WM8985_FMT_MASK, format << WM8985_FMT_SHIFT);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ master = 0;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown master/slave configuration\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+ WM8985_MS_MASK, master << WM8985_MS_SHIFT);
+
+ /* frame inversion is not valid for dsp modes */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ case SND_SOC_DAIFMT_NB_IF:
+ return -EINVAL;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ bcp = lrp = 0;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bcp = lrp = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bcp = 1;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrp = 1;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown polarity configuration\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8985_AUDIO_INTERFACE,
+ WM8985_LRP_MASK, lrp << WM8985_LRP_SHIFT);
+ snd_soc_update_bits(codec, WM8985_AUDIO_INTERFACE,
+ WM8985_BCP_MASK, bcp << WM8985_BCP_SHIFT);
+ return 0;
+}
+
+static int wm8985_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int i;
+ struct snd_soc_codec *codec;
+ struct wm8985_priv *wm8985;
+ u16 blen, srate_idx;
+ unsigned int tmp;
+ int srate_best;
+
+ codec = dai->codec;
+ wm8985 = snd_soc_codec_get_drvdata(codec);
+
+ wm8985->bclk = snd_soc_params_to_bclk(params);
+ if ((int)wm8985->bclk < 0)
+ return wm8985->bclk;
+
+ switch (params_width(params)) {
+ case 16:
+ blen = 0x0;
+ break;
+ case 20:
+ blen = 0x1;
+ break;
+ case 24:
+ blen = 0x2;
+ break;
+ case 32:
+ blen = 0x3;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported word length %u\n",
+ params_width(params));
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8985_AUDIO_INTERFACE,
+ WM8985_WL_MASK, blen << WM8985_WL_SHIFT);
+
+ /*
+ * match to the nearest possible sample rate and rely
+ * on the array index to configure the SR register
+ */
+ srate_idx = 0;
+ srate_best = abs(srates[0] - params_rate(params));
+ for (i = 1; i < ARRAY_SIZE(srates); ++i) {
+ if (abs(srates[i] - params_rate(params)) >= srate_best)
+ continue;
+ srate_idx = i;
+ srate_best = abs(srates[i] - params_rate(params));
+ }
+
+ dev_dbg(dai->dev, "Selected SRATE = %d\n", srates[srate_idx]);
+ snd_soc_update_bits(codec, WM8985_ADDITIONAL_CONTROL,
+ WM8985_SR_MASK, srate_idx << WM8985_SR_SHIFT);
+
+ dev_dbg(dai->dev, "Target BCLK = %uHz\n", wm8985->bclk);
+ dev_dbg(dai->dev, "SYSCLK = %uHz\n", wm8985->sysclk);
+
+ for (i = 0; i < ARRAY_SIZE(fs_ratios); ++i) {
+ if (wm8985->sysclk / params_rate(params)
+ == fs_ratios[i].ratio)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(fs_ratios)) {
+ dev_err(dai->dev, "Unable to configure MCLK ratio %u/%u\n",
+ wm8985->sysclk, params_rate(params));
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "MCLK ratio = %dfs\n", fs_ratios[i].ratio);
+ snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+ WM8985_MCLKDIV_MASK, i << WM8985_MCLKDIV_SHIFT);
+
+ /* select the appropriate bclk divider */
+ tmp = (wm8985->sysclk / fs_ratios[i].div) * 10;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); ++i) {
+ if (wm8985->bclk == tmp / bclk_divs[i])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(bclk_divs)) {
+ dev_err(dai->dev, "No matching BCLK divider found\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "BCLK div = %d\n", i);
+ snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+ WM8985_BCLKDIV_MASK, i << WM8985_BCLKDIV_SHIFT);
+ return 0;
+}
+
+struct pll_div {
+ u32 div2:1;
+ u32 n:4;
+ u32 k:24;
+};
+
+#define FIXED_PLL_SIZE ((1ULL << 24) * 10)
+static int pll_factors(struct pll_div *pll_div, unsigned int target,
+ unsigned int source)
+{
+ u64 Kpart;
+ unsigned long int K, Ndiv, Nmod;
+
+ pll_div->div2 = 0;
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div->div2 = 1;
+ Ndiv = target / source;
+ }
+
+ if (Ndiv < 6 || Ndiv > 12) {
+ printk(KERN_ERR "%s: WM8985 N value is not within"
+ " the recommended range: %lu\n", __func__, Ndiv);
+ return -EINVAL;
+ }
+ pll_div->n = Ndiv;
+
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (u64)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xffffffff;
+ if ((K % 10) >= 5)
+ K += 5;
+ K /= 10;
+ pll_div->k = K;
+
+ return 0;
+}
+
+static int wm8985_set_pll(struct snd_soc_dai *dai, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ int ret;
+ struct snd_soc_codec *codec;
+ struct pll_div pll_div;
+
+ codec = dai->codec;
+ if (!freq_in || !freq_out) {
+ /* disable the PLL */
+ snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ WM8985_PLLEN_MASK, 0);
+ } else {
+ ret = pll_factors(&pll_div, freq_out * 4 * 2, freq_in);
+ if (ret)
+ return ret;
+
+ /* set PLLN and PRESCALE */
+ snd_soc_write(codec, WM8985_PLL_N,
+ (pll_div.div2 << WM8985_PLL_PRESCALE_SHIFT)
+ | pll_div.n);
+ /* set PLLK */
+ snd_soc_write(codec, WM8985_PLL_K_3, pll_div.k & 0x1ff);
+ snd_soc_write(codec, WM8985_PLL_K_2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8985_PLL_K_1, (pll_div.k >> 18));
+ /* set the source of the clock to be the PLL */
+ snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+ WM8985_CLKSEL_MASK, WM8985_CLKSEL);
+ /* enable the PLL */
+ snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ WM8985_PLLEN_MASK, WM8985_PLLEN);
+ }
+ return 0;
+}
+
+static int wm8985_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec;
+ struct wm8985_priv *wm8985;
+
+ codec = dai->codec;
+ wm8985 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case WM8985_CLKSRC_MCLK:
+ snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+ WM8985_CLKSEL_MASK, 0);
+ snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ WM8985_PLLEN_MASK, 0);
+ break;
+ case WM8985_CLKSRC_PLL:
+ snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+ WM8985_CLKSEL_MASK, WM8985_CLKSEL);
+ break;
+ default:
+ dev_err(dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ wm8985->sysclk = freq;
+ return 0;
+}
+
+static int wm8985_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+ struct wm8985_priv *wm8985;
+
+ wm8985 = snd_soc_codec_get_drvdata(codec);
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID at 75k */
+ snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ WM8985_VMIDSEL_MASK,
+ 1 << WM8985_VMIDSEL_SHIFT);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8985->supplies),
+ wm8985->supplies);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ regcache_sync(wm8985->regmap);
+
+ /* enable anti-pop features */
+ snd_soc_update_bits(codec, WM8985_OUT4_TO_ADC,
+ WM8985_POBCTRL_MASK,
+ WM8985_POBCTRL);
+ /* enable thermal shutdown */
+ snd_soc_update_bits(codec, WM8985_OUTPUT_CTRL0,
+ WM8985_TSDEN_MASK, WM8985_TSDEN);
+ snd_soc_update_bits(codec, WM8985_OUTPUT_CTRL0,
+ WM8985_TSOPCTRL_MASK,
+ WM8985_TSOPCTRL);
+ /* enable BIASEN */
+ snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ WM8985_BIASEN_MASK, WM8985_BIASEN);
+ /* VMID at 75k */
+ snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ WM8985_VMIDSEL_MASK,
+ 1 << WM8985_VMIDSEL_SHIFT);
+ msleep(500);
+ /* disable anti-pop features */
+ snd_soc_update_bits(codec, WM8985_OUT4_TO_ADC,
+ WM8985_POBCTRL_MASK, 0);
+ }
+ /* VMID at 300k */
+ snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ WM8985_VMIDSEL_MASK,
+ 2 << WM8985_VMIDSEL_SHIFT);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* disable thermal shutdown */
+ snd_soc_update_bits(codec, WM8985_OUTPUT_CTRL0,
+ WM8985_TSOPCTRL_MASK, 0);
+ snd_soc_update_bits(codec, WM8985_OUTPUT_CTRL0,
+ WM8985_TSDEN_MASK, 0);
+ /* disable VMIDSEL and BIASEN */
+ snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+ WM8985_VMIDSEL_MASK | WM8985_BIASEN_MASK,
+ 0);
+ snd_soc_write(codec, WM8985_POWER_MANAGEMENT_1, 0);
+ snd_soc_write(codec, WM8985_POWER_MANAGEMENT_2, 0);
+ snd_soc_write(codec, WM8985_POWER_MANAGEMENT_3, 0);
+
+ regcache_mark_dirty(wm8985->regmap);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8985->supplies),
+ wm8985->supplies);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int wm8985_probe(struct snd_soc_codec *codec)
+{
+ size_t i;
+ struct wm8985_priv *wm8985;
+ int ret;
+
+ wm8985 = snd_soc_codec_get_drvdata(codec);
+
+ for (i = 0; i < ARRAY_SIZE(wm8985->supplies); i++)
+ wm8985->supplies[i].supply = wm8985_supply_names[i];
+
+ ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8985->supplies),
+ wm8985->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8985->supplies),
+ wm8985->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm8985_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ goto err_reg_enable;
+ }
+
+ /* latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(volume_update_regs); ++i)
+ snd_soc_update_bits(codec, volume_update_regs[i],
+ 0x100, 0x100);
+ /* enable BIASCUT */
+ snd_soc_update_bits(codec, WM8985_BIAS_CTRL, WM8985_BIASCUT,
+ WM8985_BIASCUT);
+
+ return 0;
+
+err_reg_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8985->supplies), wm8985->supplies);
+ return ret;
+}
+
+static const struct snd_soc_dai_ops wm8985_dai_ops = {
+ .digital_mute = wm8985_dac_mute,
+ .hw_params = wm8985_hw_params,
+ .set_fmt = wm8985_set_fmt,
+ .set_sysclk = wm8985_set_sysclk,
+ .set_pll = wm8985_set_pll
+};
+
+#define WM8985_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver wm8985_dai = {
+ .name = "wm8985-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = WM8985_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = WM8985_FORMATS,
+ },
+ .ops = &wm8985_dai_ops,
+ .symmetric_rates = 1
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8985 = {
+ .probe = wm8985_probe,
+ .set_bias_level = wm8985_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8985_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8985_snd_controls),
+ .dapm_widgets = wm8985_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8985_dapm_widgets),
+ .dapm_routes = wm8985_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8985_dapm_routes),
+};
+
+static const struct regmap_config wm8985_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = WM8985_MAX_REGISTER,
+ .writeable_reg = wm8985_writeable,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm8985_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8985_reg_defaults),
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8985_spi_probe(struct spi_device *spi)
+{
+ struct wm8985_priv *wm8985;
+ int ret;
+
+ wm8985 = devm_kzalloc(&spi->dev, sizeof *wm8985, GFP_KERNEL);
+ if (!wm8985)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, wm8985);
+
+ wm8985->regmap = devm_regmap_init_spi(spi, &wm8985_regmap);
+ if (IS_ERR(wm8985->regmap)) {
+ ret = PTR_ERR(wm8985->regmap);
+ dev_err(&spi->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8985, &wm8985_dai, 1);
+ return ret;
+}
+
+static int wm8985_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver wm8985_spi_driver = {
+ .driver = {
+ .name = "wm8985",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8985_spi_probe,
+ .remove = wm8985_spi_remove
+};
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8985_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8985_priv *wm8985;
+ int ret;
+
+ wm8985 = devm_kzalloc(&i2c->dev, sizeof *wm8985, GFP_KERNEL);
+ if (!wm8985)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm8985);
+
+ wm8985->regmap = devm_regmap_init_i2c(i2c, &wm8985_regmap);
+ if (IS_ERR(wm8985->regmap)) {
+ ret = PTR_ERR(wm8985->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8985, &wm8985_dai, 1);
+ return ret;
+}
+
+static int wm8985_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8985_i2c_id[] = {
+ { "wm8985", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8985_i2c_id);
+
+static struct i2c_driver wm8985_i2c_driver = {
+ .driver = {
+ .name = "wm8985",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8985_i2c_probe,
+ .remove = wm8985_i2c_remove,
+ .id_table = wm8985_i2c_id
+};
+#endif
+
+static int __init wm8985_modinit(void)
+{
+ int ret = 0;
+
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8985_i2c_driver);
+ if (ret) {
+ printk(KERN_ERR "Failed to register wm8985 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8985_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register wm8985 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(wm8985_modinit);
+
+static void __exit wm8985_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8985_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8985_spi_driver);
+#endif
+}
+module_exit(wm8985_exit);
+
+MODULE_DESCRIPTION("ASoC WM8985 driver");
+MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8985.h b/kernel/sound/soc/codecs/wm8985.h
new file mode 100644
index 000000000..2e71ff507
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8985.h
@@ -0,0 +1,1045 @@
+/*
+ * wm8985.h -- WM8985 ASoC driver
+ *
+ * Copyright 2010 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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 _WM8985_H
+#define _WM8985_H
+
+#define WM8985_SOFTWARE_RESET 0x00
+#define WM8985_POWER_MANAGEMENT_1 0x01
+#define WM8985_POWER_MANAGEMENT_2 0x02
+#define WM8985_POWER_MANAGEMENT_3 0x03
+#define WM8985_AUDIO_INTERFACE 0x04
+#define WM8985_COMPANDING_CONTROL 0x05
+#define WM8985_CLOCK_GEN_CONTROL 0x06
+#define WM8985_ADDITIONAL_CONTROL 0x07
+#define WM8985_GPIO_CONTROL 0x08
+#define WM8985_JACK_DETECT_CONTROL_1 0x09
+#define WM8985_DAC_CONTROL 0x0A
+#define WM8985_LEFT_DAC_DIGITAL_VOL 0x0B
+#define WM8985_RIGHT_DAC_DIGITAL_VOL 0x0C
+#define WM8985_JACK_DETECT_CONTROL_2 0x0D
+#define WM8985_ADC_CONTROL 0x0E
+#define WM8985_LEFT_ADC_DIGITAL_VOL 0x0F
+#define WM8985_RIGHT_ADC_DIGITAL_VOL 0x10
+#define WM8985_EQ1_LOW_SHELF 0x12
+#define WM8985_EQ2_PEAK_1 0x13
+#define WM8985_EQ3_PEAK_2 0x14
+#define WM8985_EQ4_PEAK_3 0x15
+#define WM8985_EQ5_HIGH_SHELF 0x16
+#define WM8985_DAC_LIMITER_1 0x18
+#define WM8985_DAC_LIMITER_2 0x19
+#define WM8985_NOTCH_FILTER_1 0x1B
+#define WM8985_NOTCH_FILTER_2 0x1C
+#define WM8985_NOTCH_FILTER_3 0x1D
+#define WM8985_NOTCH_FILTER_4 0x1E
+#define WM8985_ALC_CONTROL_1 0x20
+#define WM8985_ALC_CONTROL_2 0x21
+#define WM8985_ALC_CONTROL_3 0x22
+#define WM8985_NOISE_GATE 0x23
+#define WM8985_PLL_N 0x24
+#define WM8985_PLL_K_1 0x25
+#define WM8985_PLL_K_2 0x26
+#define WM8985_PLL_K_3 0x27
+#define WM8985_3D_CONTROL 0x29
+#define WM8985_OUT4_TO_ADC 0x2A
+#define WM8985_BEEP_CONTROL 0x2B
+#define WM8985_INPUT_CTRL 0x2C
+#define WM8985_LEFT_INP_PGA_GAIN_CTRL 0x2D
+#define WM8985_RIGHT_INP_PGA_GAIN_CTRL 0x2E
+#define WM8985_LEFT_ADC_BOOST_CTRL 0x2F
+#define WM8985_RIGHT_ADC_BOOST_CTRL 0x30
+#define WM8985_OUTPUT_CTRL0 0x31
+#define WM8985_LEFT_MIXER_CTRL 0x32
+#define WM8985_RIGHT_MIXER_CTRL 0x33
+#define WM8985_LOUT1_HP_VOLUME_CTRL 0x34
+#define WM8985_ROUT1_HP_VOLUME_CTRL 0x35
+#define WM8985_LOUT2_SPK_VOLUME_CTRL 0x36
+#define WM8985_ROUT2_SPK_VOLUME_CTRL 0x37
+#define WM8985_OUT3_MIXER_CTRL 0x38
+#define WM8985_OUT4_MONO_MIX_CTRL 0x39
+#define WM8985_OUTPUT_CTRL1 0x3C
+#define WM8985_BIAS_CTRL 0x3D
+
+#define WM8985_REGISTER_COUNT 59
+#define WM8985_MAX_REGISTER 0x3F
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM8985_SOFTWARE_RESET_MASK 0x01FF /* SOFTWARE_RESET - [8:0] */
+#define WM8985_SOFTWARE_RESET_SHIFT 0 /* SOFTWARE_RESET - [8:0] */
+#define WM8985_SOFTWARE_RESET_WIDTH 9 /* SOFTWARE_RESET - [8:0] */
+
+/*
+ * R1 (0x01) - Power management 1
+ */
+#define WM8985_OUT4MIXEN 0x0080 /* OUT4MIXEN */
+#define WM8985_OUT4MIXEN_MASK 0x0080 /* OUT4MIXEN */
+#define WM8985_OUT4MIXEN_SHIFT 7 /* OUT4MIXEN */
+#define WM8985_OUT4MIXEN_WIDTH 1 /* OUT4MIXEN */
+#define WM8985_OUT3MIXEN 0x0040 /* OUT3MIXEN */
+#define WM8985_OUT3MIXEN_MASK 0x0040 /* OUT3MIXEN */
+#define WM8985_OUT3MIXEN_SHIFT 6 /* OUT3MIXEN */
+#define WM8985_OUT3MIXEN_WIDTH 1 /* OUT3MIXEN */
+#define WM8985_PLLEN 0x0020 /* PLLEN */
+#define WM8985_PLLEN_MASK 0x0020 /* PLLEN */
+#define WM8985_PLLEN_SHIFT 5 /* PLLEN */
+#define WM8985_PLLEN_WIDTH 1 /* PLLEN */
+#define WM8985_MICBEN 0x0010 /* MICBEN */
+#define WM8985_MICBEN_MASK 0x0010 /* MICBEN */
+#define WM8985_MICBEN_SHIFT 4 /* MICBEN */
+#define WM8985_MICBEN_WIDTH 1 /* MICBEN */
+#define WM8985_BIASEN 0x0008 /* BIASEN */
+#define WM8985_BIASEN_MASK 0x0008 /* BIASEN */
+#define WM8985_BIASEN_SHIFT 3 /* BIASEN */
+#define WM8985_BIASEN_WIDTH 1 /* BIASEN */
+#define WM8985_BUFIOEN 0x0004 /* BUFIOEN */
+#define WM8985_BUFIOEN_MASK 0x0004 /* BUFIOEN */
+#define WM8985_BUFIOEN_SHIFT 2 /* BUFIOEN */
+#define WM8985_BUFIOEN_WIDTH 1 /* BUFIOEN */
+#define WM8985_VMIDSEL 0x0003 /* VMIDSEL */
+#define WM8985_VMIDSEL_MASK 0x0003 /* VMIDSEL - [1:0] */
+#define WM8985_VMIDSEL_SHIFT 0 /* VMIDSEL - [1:0] */
+#define WM8985_VMIDSEL_WIDTH 2 /* VMIDSEL - [1:0] */
+
+/*
+ * R2 (0x02) - Power management 2
+ */
+#define WM8985_ROUT1EN 0x0100 /* ROUT1EN */
+#define WM8985_ROUT1EN_MASK 0x0100 /* ROUT1EN */
+#define WM8985_ROUT1EN_SHIFT 8 /* ROUT1EN */
+#define WM8985_ROUT1EN_WIDTH 1 /* ROUT1EN */
+#define WM8985_LOUT1EN 0x0080 /* LOUT1EN */
+#define WM8985_LOUT1EN_MASK 0x0080 /* LOUT1EN */
+#define WM8985_LOUT1EN_SHIFT 7 /* LOUT1EN */
+#define WM8985_LOUT1EN_WIDTH 1 /* LOUT1EN */
+#define WM8985_SLEEP 0x0040 /* SLEEP */
+#define WM8985_SLEEP_MASK 0x0040 /* SLEEP */
+#define WM8985_SLEEP_SHIFT 6 /* SLEEP */
+#define WM8985_SLEEP_WIDTH 1 /* SLEEP */
+#define WM8985_BOOSTENR 0x0020 /* BOOSTENR */
+#define WM8985_BOOSTENR_MASK 0x0020 /* BOOSTENR */
+#define WM8985_BOOSTENR_SHIFT 5 /* BOOSTENR */
+#define WM8985_BOOSTENR_WIDTH 1 /* BOOSTENR */
+#define WM8985_BOOSTENL 0x0010 /* BOOSTENL */
+#define WM8985_BOOSTENL_MASK 0x0010 /* BOOSTENL */
+#define WM8985_BOOSTENL_SHIFT 4 /* BOOSTENL */
+#define WM8985_BOOSTENL_WIDTH 1 /* BOOSTENL */
+#define WM8985_INPGAENR 0x0008 /* INPGAENR */
+#define WM8985_INPGAENR_MASK 0x0008 /* INPGAENR */
+#define WM8985_INPGAENR_SHIFT 3 /* INPGAENR */
+#define WM8985_INPGAENR_WIDTH 1 /* INPGAENR */
+#define WM8985_INPPGAENL 0x0004 /* INPPGAENL */
+#define WM8985_INPPGAENL_MASK 0x0004 /* INPPGAENL */
+#define WM8985_INPPGAENL_SHIFT 2 /* INPPGAENL */
+#define WM8985_INPPGAENL_WIDTH 1 /* INPPGAENL */
+#define WM8985_ADCENR 0x0002 /* ADCENR */
+#define WM8985_ADCENR_MASK 0x0002 /* ADCENR */
+#define WM8985_ADCENR_SHIFT 1 /* ADCENR */
+#define WM8985_ADCENR_WIDTH 1 /* ADCENR */
+#define WM8985_ADCENL 0x0001 /* ADCENL */
+#define WM8985_ADCENL_MASK 0x0001 /* ADCENL */
+#define WM8985_ADCENL_SHIFT 0 /* ADCENL */
+#define WM8985_ADCENL_WIDTH 1 /* ADCENL */
+
+/*
+ * R3 (0x03) - Power management 3
+ */
+#define WM8985_OUT4EN 0x0100 /* OUT4EN */
+#define WM8985_OUT4EN_MASK 0x0100 /* OUT4EN */
+#define WM8985_OUT4EN_SHIFT 8 /* OUT4EN */
+#define WM8985_OUT4EN_WIDTH 1 /* OUT4EN */
+#define WM8985_OUT3EN 0x0080 /* OUT3EN */
+#define WM8985_OUT3EN_MASK 0x0080 /* OUT3EN */
+#define WM8985_OUT3EN_SHIFT 7 /* OUT3EN */
+#define WM8985_OUT3EN_WIDTH 1 /* OUT3EN */
+#define WM8985_ROUT2EN 0x0040 /* ROUT2EN */
+#define WM8985_ROUT2EN_MASK 0x0040 /* ROUT2EN */
+#define WM8985_ROUT2EN_SHIFT 6 /* ROUT2EN */
+#define WM8985_ROUT2EN_WIDTH 1 /* ROUT2EN */
+#define WM8985_LOUT2EN 0x0020 /* LOUT2EN */
+#define WM8985_LOUT2EN_MASK 0x0020 /* LOUT2EN */
+#define WM8985_LOUT2EN_SHIFT 5 /* LOUT2EN */
+#define WM8985_LOUT2EN_WIDTH 1 /* LOUT2EN */
+#define WM8985_RMIXEN 0x0008 /* RMIXEN */
+#define WM8985_RMIXEN_MASK 0x0008 /* RMIXEN */
+#define WM8985_RMIXEN_SHIFT 3 /* RMIXEN */
+#define WM8985_RMIXEN_WIDTH 1 /* RMIXEN */
+#define WM8985_LMIXEN 0x0004 /* LMIXEN */
+#define WM8985_LMIXEN_MASK 0x0004 /* LMIXEN */
+#define WM8985_LMIXEN_SHIFT 2 /* LMIXEN */
+#define WM8985_LMIXEN_WIDTH 1 /* LMIXEN */
+#define WM8985_DACENR 0x0002 /* DACENR */
+#define WM8985_DACENR_MASK 0x0002 /* DACENR */
+#define WM8985_DACENR_SHIFT 1 /* DACENR */
+#define WM8985_DACENR_WIDTH 1 /* DACENR */
+#define WM8985_DACENL 0x0001 /* DACENL */
+#define WM8985_DACENL_MASK 0x0001 /* DACENL */
+#define WM8985_DACENL_SHIFT 0 /* DACENL */
+#define WM8985_DACENL_WIDTH 1 /* DACENL */
+
+/*
+ * R4 (0x04) - Audio Interface
+ */
+#define WM8985_BCP 0x0100 /* BCP */
+#define WM8985_BCP_MASK 0x0100 /* BCP */
+#define WM8985_BCP_SHIFT 8 /* BCP */
+#define WM8985_BCP_WIDTH 1 /* BCP */
+#define WM8985_LRP 0x0080 /* LRP */
+#define WM8985_LRP_MASK 0x0080 /* LRP */
+#define WM8985_LRP_SHIFT 7 /* LRP */
+#define WM8985_LRP_WIDTH 1 /* LRP */
+#define WM8985_WL_MASK 0x0060 /* WL - [6:5] */
+#define WM8985_WL_SHIFT 5 /* WL - [6:5] */
+#define WM8985_WL_WIDTH 2 /* WL - [6:5] */
+#define WM8985_FMT_MASK 0x0018 /* FMT - [4:3] */
+#define WM8985_FMT_SHIFT 3 /* FMT - [4:3] */
+#define WM8985_FMT_WIDTH 2 /* FMT - [4:3] */
+#define WM8985_DLRSWAP 0x0004 /* DLRSWAP */
+#define WM8985_DLRSWAP_MASK 0x0004 /* DLRSWAP */
+#define WM8985_DLRSWAP_SHIFT 2 /* DLRSWAP */
+#define WM8985_DLRSWAP_WIDTH 1 /* DLRSWAP */
+#define WM8985_ALRSWAP 0x0002 /* ALRSWAP */
+#define WM8985_ALRSWAP_MASK 0x0002 /* ALRSWAP */
+#define WM8985_ALRSWAP_SHIFT 1 /* ALRSWAP */
+#define WM8985_ALRSWAP_WIDTH 1 /* ALRSWAP */
+#define WM8985_MONO 0x0001 /* MONO */
+#define WM8985_MONO_MASK 0x0001 /* MONO */
+#define WM8985_MONO_SHIFT 0 /* MONO */
+#define WM8985_MONO_WIDTH 1 /* MONO */
+
+/*
+ * R5 (0x05) - Companding control
+ */
+#define WM8985_WL8 0x0020 /* WL8 */
+#define WM8985_WL8_MASK 0x0020 /* WL8 */
+#define WM8985_WL8_SHIFT 5 /* WL8 */
+#define WM8985_WL8_WIDTH 1 /* WL8 */
+#define WM8985_DAC_COMP_MASK 0x0018 /* DAC_COMP - [4:3] */
+#define WM8985_DAC_COMP_SHIFT 3 /* DAC_COMP - [4:3] */
+#define WM8985_DAC_COMP_WIDTH 2 /* DAC_COMP - [4:3] */
+#define WM8985_ADC_COMP_MASK 0x0006 /* ADC_COMP - [2:1] */
+#define WM8985_ADC_COMP_SHIFT 1 /* ADC_COMP - [2:1] */
+#define WM8985_ADC_COMP_WIDTH 2 /* ADC_COMP - [2:1] */
+#define WM8985_LOOPBACK 0x0001 /* LOOPBACK */
+#define WM8985_LOOPBACK_MASK 0x0001 /* LOOPBACK */
+#define WM8985_LOOPBACK_SHIFT 0 /* LOOPBACK */
+#define WM8985_LOOPBACK_WIDTH 1 /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clock Gen control
+ */
+#define WM8985_CLKSEL 0x0100 /* CLKSEL */
+#define WM8985_CLKSEL_MASK 0x0100 /* CLKSEL */
+#define WM8985_CLKSEL_SHIFT 8 /* CLKSEL */
+#define WM8985_CLKSEL_WIDTH 1 /* CLKSEL */
+#define WM8985_MCLKDIV_MASK 0x00E0 /* MCLKDIV - [7:5] */
+#define WM8985_MCLKDIV_SHIFT 5 /* MCLKDIV - [7:5] */
+#define WM8985_MCLKDIV_WIDTH 3 /* MCLKDIV - [7:5] */
+#define WM8985_BCLKDIV_MASK 0x001C /* BCLKDIV - [4:2] */
+#define WM8985_BCLKDIV_SHIFT 2 /* BCLKDIV - [4:2] */
+#define WM8985_BCLKDIV_WIDTH 3 /* BCLKDIV - [4:2] */
+#define WM8985_MS 0x0001 /* MS */
+#define WM8985_MS_MASK 0x0001 /* MS */
+#define WM8985_MS_SHIFT 0 /* MS */
+#define WM8985_MS_WIDTH 1 /* MS */
+
+/*
+ * R7 (0x07) - Additional control
+ */
+#define WM8985_M128ENB 0x0100 /* M128ENB */
+#define WM8985_M128ENB_MASK 0x0100 /* M128ENB */
+#define WM8985_M128ENB_SHIFT 8 /* M128ENB */
+#define WM8985_M128ENB_WIDTH 1 /* M128ENB */
+#define WM8985_DCLKDIV_MASK 0x00F0 /* DCLKDIV - [7:4] */
+#define WM8985_DCLKDIV_SHIFT 4 /* DCLKDIV - [7:4] */
+#define WM8985_DCLKDIV_WIDTH 4 /* DCLKDIV - [7:4] */
+#define WM8985_SR_MASK 0x000E /* SR - [3:1] */
+#define WM8985_SR_SHIFT 1 /* SR - [3:1] */
+#define WM8985_SR_WIDTH 3 /* SR - [3:1] */
+#define WM8985_SLOWCLKEN 0x0001 /* SLOWCLKEN */
+#define WM8985_SLOWCLKEN_MASK 0x0001 /* SLOWCLKEN */
+#define WM8985_SLOWCLKEN_SHIFT 0 /* SLOWCLKEN */
+#define WM8985_SLOWCLKEN_WIDTH 1 /* SLOWCLKEN */
+
+/*
+ * R8 (0x08) - GPIO Control
+ */
+#define WM8985_GPIO1GP 0x0100 /* GPIO1GP */
+#define WM8985_GPIO1GP_MASK 0x0100 /* GPIO1GP */
+#define WM8985_GPIO1GP_SHIFT 8 /* GPIO1GP */
+#define WM8985_GPIO1GP_WIDTH 1 /* GPIO1GP */
+#define WM8985_GPIO1GPU 0x0080 /* GPIO1GPU */
+#define WM8985_GPIO1GPU_MASK 0x0080 /* GPIO1GPU */
+#define WM8985_GPIO1GPU_SHIFT 7 /* GPIO1GPU */
+#define WM8985_GPIO1GPU_WIDTH 1 /* GPIO1GPU */
+#define WM8985_GPIO1GPD 0x0040 /* GPIO1GPD */
+#define WM8985_GPIO1GPD_MASK 0x0040 /* GPIO1GPD */
+#define WM8985_GPIO1GPD_SHIFT 6 /* GPIO1GPD */
+#define WM8985_GPIO1GPD_WIDTH 1 /* GPIO1GPD */
+#define WM8985_GPIO1POL 0x0008 /* GPIO1POL */
+#define WM8985_GPIO1POL_MASK 0x0008 /* GPIO1POL */
+#define WM8985_GPIO1POL_SHIFT 3 /* GPIO1POL */
+#define WM8985_GPIO1POL_WIDTH 1 /* GPIO1POL */
+#define WM8985_GPIO1SEL_MASK 0x0007 /* GPIO1SEL - [2:0] */
+#define WM8985_GPIO1SEL_SHIFT 0 /* GPIO1SEL - [2:0] */
+#define WM8985_GPIO1SEL_WIDTH 3 /* GPIO1SEL - [2:0] */
+
+/*
+ * R9 (0x09) - Jack Detect Control 1
+ */
+#define WM8985_JD_EN 0x0040 /* JD_EN */
+#define WM8985_JD_EN_MASK 0x0040 /* JD_EN */
+#define WM8985_JD_EN_SHIFT 6 /* JD_EN */
+#define WM8985_JD_EN_WIDTH 1 /* JD_EN */
+#define WM8985_JD_SEL_MASK 0x0030 /* JD_SEL - [5:4] */
+#define WM8985_JD_SEL_SHIFT 4 /* JD_SEL - [5:4] */
+#define WM8985_JD_SEL_WIDTH 2 /* JD_SEL - [5:4] */
+
+/*
+ * R10 (0x0A) - DAC Control
+ */
+#define WM8985_SOFTMUTE 0x0040 /* SOFTMUTE */
+#define WM8985_SOFTMUTE_MASK 0x0040 /* SOFTMUTE */
+#define WM8985_SOFTMUTE_SHIFT 6 /* SOFTMUTE */
+#define WM8985_SOFTMUTE_WIDTH 1 /* SOFTMUTE */
+#define WM8985_DACOSR128 0x0008 /* DACOSR128 */
+#define WM8985_DACOSR128_MASK 0x0008 /* DACOSR128 */
+#define WM8985_DACOSR128_SHIFT 3 /* DACOSR128 */
+#define WM8985_DACOSR128_WIDTH 1 /* DACOSR128 */
+#define WM8985_AMUTE 0x0004 /* AMUTE */
+#define WM8985_AMUTE_MASK 0x0004 /* AMUTE */
+#define WM8985_AMUTE_SHIFT 2 /* AMUTE */
+#define WM8985_AMUTE_WIDTH 1 /* AMUTE */
+#define WM8985_DACPOLR 0x0002 /* DACPOLR */
+#define WM8985_DACPOLR_MASK 0x0002 /* DACPOLR */
+#define WM8985_DACPOLR_SHIFT 1 /* DACPOLR */
+#define WM8985_DACPOLR_WIDTH 1 /* DACPOLR */
+#define WM8985_DACPOLL 0x0001 /* DACPOLL */
+#define WM8985_DACPOLL_MASK 0x0001 /* DACPOLL */
+#define WM8985_DACPOLL_SHIFT 0 /* DACPOLL */
+#define WM8985_DACPOLL_WIDTH 1 /* DACPOLL */
+
+/*
+ * R11 (0x0B) - Left DAC digital Vol
+ */
+#define WM8985_DACVU 0x0100 /* DACVU */
+#define WM8985_DACVU_MASK 0x0100 /* DACVU */
+#define WM8985_DACVU_SHIFT 8 /* DACVU */
+#define WM8985_DACVU_WIDTH 1 /* DACVU */
+#define WM8985_DACVOLL_MASK 0x00FF /* DACVOLL - [7:0] */
+#define WM8985_DACVOLL_SHIFT 0 /* DACVOLL - [7:0] */
+#define WM8985_DACVOLL_WIDTH 8 /* DACVOLL - [7:0] */
+
+/*
+ * R12 (0x0C) - Right DAC digital vol
+ */
+#define WM8985_DACVU 0x0100 /* DACVU */
+#define WM8985_DACVU_MASK 0x0100 /* DACVU */
+#define WM8985_DACVU_SHIFT 8 /* DACVU */
+#define WM8985_DACVU_WIDTH 1 /* DACVU */
+#define WM8985_DACVOLR_MASK 0x00FF /* DACVOLR - [7:0] */
+#define WM8985_DACVOLR_SHIFT 0 /* DACVOLR - [7:0] */
+#define WM8985_DACVOLR_WIDTH 8 /* DACVOLR - [7:0] */
+
+/*
+ * R13 (0x0D) - Jack Detect Control 2
+ */
+#define WM8985_JD_EN1_MASK 0x00F0 /* JD_EN1 - [7:4] */
+#define WM8985_JD_EN1_SHIFT 4 /* JD_EN1 - [7:4] */
+#define WM8985_JD_EN1_WIDTH 4 /* JD_EN1 - [7:4] */
+#define WM8985_JD_EN0_MASK 0x000F /* JD_EN0 - [3:0] */
+#define WM8985_JD_EN0_SHIFT 0 /* JD_EN0 - [3:0] */
+#define WM8985_JD_EN0_WIDTH 4 /* JD_EN0 - [3:0] */
+
+/*
+ * R14 (0x0E) - ADC Control
+ */
+#define WM8985_HPFEN 0x0100 /* HPFEN */
+#define WM8985_HPFEN_MASK 0x0100 /* HPFEN */
+#define WM8985_HPFEN_SHIFT 8 /* HPFEN */
+#define WM8985_HPFEN_WIDTH 1 /* HPFEN */
+#define WM8985_HPFAPP 0x0080 /* HPFAPP */
+#define WM8985_HPFAPP_MASK 0x0080 /* HPFAPP */
+#define WM8985_HPFAPP_SHIFT 7 /* HPFAPP */
+#define WM8985_HPFAPP_WIDTH 1 /* HPFAPP */
+#define WM8985_HPFCUT_MASK 0x0070 /* HPFCUT - [6:4] */
+#define WM8985_HPFCUT_SHIFT 4 /* HPFCUT - [6:4] */
+#define WM8985_HPFCUT_WIDTH 3 /* HPFCUT - [6:4] */
+#define WM8985_ADCOSR128 0x0008 /* ADCOSR128 */
+#define WM8985_ADCOSR128_MASK 0x0008 /* ADCOSR128 */
+#define WM8985_ADCOSR128_SHIFT 3 /* ADCOSR128 */
+#define WM8985_ADCOSR128_WIDTH 1 /* ADCOSR128 */
+#define WM8985_ADCRPOL 0x0002 /* ADCRPOL */
+#define WM8985_ADCRPOL_MASK 0x0002 /* ADCRPOL */
+#define WM8985_ADCRPOL_SHIFT 1 /* ADCRPOL */
+#define WM8985_ADCRPOL_WIDTH 1 /* ADCRPOL */
+#define WM8985_ADCLPOL 0x0001 /* ADCLPOL */
+#define WM8985_ADCLPOL_MASK 0x0001 /* ADCLPOL */
+#define WM8985_ADCLPOL_SHIFT 0 /* ADCLPOL */
+#define WM8985_ADCLPOL_WIDTH 1 /* ADCLPOL */
+
+/*
+ * R15 (0x0F) - Left ADC Digital Vol
+ */
+#define WM8985_ADCVU 0x0100 /* ADCVU */
+#define WM8985_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8985_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8985_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8985_ADCVOLL_MASK 0x00FF /* ADCVOLL - [7:0] */
+#define WM8985_ADCVOLL_SHIFT 0 /* ADCVOLL - [7:0] */
+#define WM8985_ADCVOLL_WIDTH 8 /* ADCVOLL - [7:0] */
+
+/*
+ * R16 (0x10) - Right ADC Digital Vol
+ */
+#define WM8985_ADCVU 0x0100 /* ADCVU */
+#define WM8985_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8985_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8985_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8985_ADCVOLR_MASK 0x00FF /* ADCVOLR - [7:0] */
+#define WM8985_ADCVOLR_SHIFT 0 /* ADCVOLR - [7:0] */
+#define WM8985_ADCVOLR_WIDTH 8 /* ADCVOLR - [7:0] */
+
+/*
+ * R18 (0x12) - EQ1 - low shelf
+ */
+#define WM8985_EQ3DMODE 0x0100 /* EQ3DMODE */
+#define WM8985_EQ3DMODE_MASK 0x0100 /* EQ3DMODE */
+#define WM8985_EQ3DMODE_SHIFT 8 /* EQ3DMODE */
+#define WM8985_EQ3DMODE_WIDTH 1 /* EQ3DMODE */
+#define WM8985_EQ1C_MASK 0x0060 /* EQ1C - [6:5] */
+#define WM8985_EQ1C_SHIFT 5 /* EQ1C - [6:5] */
+#define WM8985_EQ1C_WIDTH 2 /* EQ1C - [6:5] */
+#define WM8985_EQ1G_MASK 0x001F /* EQ1G - [4:0] */
+#define WM8985_EQ1G_SHIFT 0 /* EQ1G - [4:0] */
+#define WM8985_EQ1G_WIDTH 5 /* EQ1G - [4:0] */
+
+/*
+ * R19 (0x13) - EQ2 - peak 1
+ */
+#define WM8985_EQ2BW 0x0100 /* EQ2BW */
+#define WM8985_EQ2BW_MASK 0x0100 /* EQ2BW */
+#define WM8985_EQ2BW_SHIFT 8 /* EQ2BW */
+#define WM8985_EQ2BW_WIDTH 1 /* EQ2BW */
+#define WM8985_EQ2C_MASK 0x0060 /* EQ2C - [6:5] */
+#define WM8985_EQ2C_SHIFT 5 /* EQ2C - [6:5] */
+#define WM8985_EQ2C_WIDTH 2 /* EQ2C - [6:5] */
+#define WM8985_EQ2G_MASK 0x001F /* EQ2G - [4:0] */
+#define WM8985_EQ2G_SHIFT 0 /* EQ2G - [4:0] */
+#define WM8985_EQ2G_WIDTH 5 /* EQ2G - [4:0] */
+
+/*
+ * R20 (0x14) - EQ3 - peak 2
+ */
+#define WM8985_EQ3BW 0x0100 /* EQ3BW */
+#define WM8985_EQ3BW_MASK 0x0100 /* EQ3BW */
+#define WM8985_EQ3BW_SHIFT 8 /* EQ3BW */
+#define WM8985_EQ3BW_WIDTH 1 /* EQ3BW */
+#define WM8985_EQ3C_MASK 0x0060 /* EQ3C - [6:5] */
+#define WM8985_EQ3C_SHIFT 5 /* EQ3C - [6:5] */
+#define WM8985_EQ3C_WIDTH 2 /* EQ3C - [6:5] */
+#define WM8985_EQ3G_MASK 0x001F /* EQ3G - [4:0] */
+#define WM8985_EQ3G_SHIFT 0 /* EQ3G - [4:0] */
+#define WM8985_EQ3G_WIDTH 5 /* EQ3G - [4:0] */
+
+/*
+ * R21 (0x15) - EQ4 - peak 3
+ */
+#define WM8985_EQ4BW 0x0100 /* EQ4BW */
+#define WM8985_EQ4BW_MASK 0x0100 /* EQ4BW */
+#define WM8985_EQ4BW_SHIFT 8 /* EQ4BW */
+#define WM8985_EQ4BW_WIDTH 1 /* EQ4BW */
+#define WM8985_EQ4C_MASK 0x0060 /* EQ4C - [6:5] */
+#define WM8985_EQ4C_SHIFT 5 /* EQ4C - [6:5] */
+#define WM8985_EQ4C_WIDTH 2 /* EQ4C - [6:5] */
+#define WM8985_EQ4G_MASK 0x001F /* EQ4G - [4:0] */
+#define WM8985_EQ4G_SHIFT 0 /* EQ4G - [4:0] */
+#define WM8985_EQ4G_WIDTH 5 /* EQ4G - [4:0] */
+
+/*
+ * R22 (0x16) - EQ5 - high shelf
+ */
+#define WM8985_EQ5C_MASK 0x0060 /* EQ5C - [6:5] */
+#define WM8985_EQ5C_SHIFT 5 /* EQ5C - [6:5] */
+#define WM8985_EQ5C_WIDTH 2 /* EQ5C - [6:5] */
+#define WM8985_EQ5G_MASK 0x001F /* EQ5G - [4:0] */
+#define WM8985_EQ5G_SHIFT 0 /* EQ5G - [4:0] */
+#define WM8985_EQ5G_WIDTH 5 /* EQ5G - [4:0] */
+
+/*
+ * R24 (0x18) - DAC Limiter 1
+ */
+#define WM8985_LIMEN 0x0100 /* LIMEN */
+#define WM8985_LIMEN_MASK 0x0100 /* LIMEN */
+#define WM8985_LIMEN_SHIFT 8 /* LIMEN */
+#define WM8985_LIMEN_WIDTH 1 /* LIMEN */
+#define WM8985_LIMDCY_MASK 0x00F0 /* LIMDCY - [7:4] */
+#define WM8985_LIMDCY_SHIFT 4 /* LIMDCY - [7:4] */
+#define WM8985_LIMDCY_WIDTH 4 /* LIMDCY - [7:4] */
+#define WM8985_LIMATK_MASK 0x000F /* LIMATK - [3:0] */
+#define WM8985_LIMATK_SHIFT 0 /* LIMATK - [3:0] */
+#define WM8985_LIMATK_WIDTH 4 /* LIMATK - [3:0] */
+
+/*
+ * R25 (0x19) - DAC Limiter 2
+ */
+#define WM8985_LIMLVL_MASK 0x0070 /* LIMLVL - [6:4] */
+#define WM8985_LIMLVL_SHIFT 4 /* LIMLVL - [6:4] */
+#define WM8985_LIMLVL_WIDTH 3 /* LIMLVL - [6:4] */
+#define WM8985_LIMBOOST_MASK 0x000F /* LIMBOOST - [3:0] */
+#define WM8985_LIMBOOST_SHIFT 0 /* LIMBOOST - [3:0] */
+#define WM8985_LIMBOOST_WIDTH 4 /* LIMBOOST - [3:0] */
+
+/*
+ * R27 (0x1B) - Notch Filter 1
+ */
+#define WM8985_NFU 0x0100 /* NFU */
+#define WM8985_NFU_MASK 0x0100 /* NFU */
+#define WM8985_NFU_SHIFT 8 /* NFU */
+#define WM8985_NFU_WIDTH 1 /* NFU */
+#define WM8985_NFEN 0x0080 /* NFEN */
+#define WM8985_NFEN_MASK 0x0080 /* NFEN */
+#define WM8985_NFEN_SHIFT 7 /* NFEN */
+#define WM8985_NFEN_WIDTH 1 /* NFEN */
+#define WM8985_NFA0_13_7_MASK 0x007F /* NFA0(13:7) - [6:0] */
+#define WM8985_NFA0_13_7_SHIFT 0 /* NFA0(13:7) - [6:0] */
+#define WM8985_NFA0_13_7_WIDTH 7 /* NFA0(13:7) - [6:0] */
+
+/*
+ * R28 (0x1C) - Notch Filter 2
+ */
+#define WM8985_NFU 0x0100 /* NFU */
+#define WM8985_NFU_MASK 0x0100 /* NFU */
+#define WM8985_NFU_SHIFT 8 /* NFU */
+#define WM8985_NFU_WIDTH 1 /* NFU */
+#define WM8985_NFA0_6_0_MASK 0x007F /* NFA0(6:0) - [6:0] */
+#define WM8985_NFA0_6_0_SHIFT 0 /* NFA0(6:0) - [6:0] */
+#define WM8985_NFA0_6_0_WIDTH 7 /* NFA0(6:0) - [6:0] */
+
+/*
+ * R29 (0x1D) - Notch Filter 3
+ */
+#define WM8985_NFU 0x0100 /* NFU */
+#define WM8985_NFU_MASK 0x0100 /* NFU */
+#define WM8985_NFU_SHIFT 8 /* NFU */
+#define WM8985_NFU_WIDTH 1 /* NFU */
+#define WM8985_NFA1_13_7_MASK 0x007F /* NFA1(13:7) - [6:0] */
+#define WM8985_NFA1_13_7_SHIFT 0 /* NFA1(13:7) - [6:0] */
+#define WM8985_NFA1_13_7_WIDTH 7 /* NFA1(13:7) - [6:0] */
+
+/*
+ * R30 (0x1E) - Notch Filter 4
+ */
+#define WM8985_NFU 0x0100 /* NFU */
+#define WM8985_NFU_MASK 0x0100 /* NFU */
+#define WM8985_NFU_SHIFT 8 /* NFU */
+#define WM8985_NFU_WIDTH 1 /* NFU */
+#define WM8985_NFA1_6_0_MASK 0x007F /* NFA1(6:0) - [6:0] */
+#define WM8985_NFA1_6_0_SHIFT 0 /* NFA1(6:0) - [6:0] */
+#define WM8985_NFA1_6_0_WIDTH 7 /* NFA1(6:0) - [6:0] */
+
+/*
+ * R32 (0x20) - ALC control 1
+ */
+#define WM8985_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */
+#define WM8985_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */
+#define WM8985_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */
+#define WM8985_ALCMAX_MASK 0x0038 /* ALCMAX - [5:3] */
+#define WM8985_ALCMAX_SHIFT 3 /* ALCMAX - [5:3] */
+#define WM8985_ALCMAX_WIDTH 3 /* ALCMAX - [5:3] */
+#define WM8985_ALCMIN_MASK 0x0007 /* ALCMIN - [2:0] */
+#define WM8985_ALCMIN_SHIFT 0 /* ALCMIN - [2:0] */
+#define WM8985_ALCMIN_WIDTH 3 /* ALCMIN - [2:0] */
+
+/*
+ * R33 (0x21) - ALC control 2
+ */
+#define WM8985_ALCHLD_MASK 0x00F0 /* ALCHLD - [7:4] */
+#define WM8985_ALCHLD_SHIFT 4 /* ALCHLD - [7:4] */
+#define WM8985_ALCHLD_WIDTH 4 /* ALCHLD - [7:4] */
+#define WM8985_ALCLVL_MASK 0x000F /* ALCLVL - [3:0] */
+#define WM8985_ALCLVL_SHIFT 0 /* ALCLVL - [3:0] */
+#define WM8985_ALCLVL_WIDTH 4 /* ALCLVL - [3:0] */
+
+/*
+ * R34 (0x22) - ALC control 3
+ */
+#define WM8985_ALCMODE 0x0100 /* ALCMODE */
+#define WM8985_ALCMODE_MASK 0x0100 /* ALCMODE */
+#define WM8985_ALCMODE_SHIFT 8 /* ALCMODE */
+#define WM8985_ALCMODE_WIDTH 1 /* ALCMODE */
+#define WM8985_ALCDCY_MASK 0x00F0 /* ALCDCY - [7:4] */
+#define WM8985_ALCDCY_SHIFT 4 /* ALCDCY - [7:4] */
+#define WM8985_ALCDCY_WIDTH 4 /* ALCDCY - [7:4] */
+#define WM8985_ALCATK_MASK 0x000F /* ALCATK - [3:0] */
+#define WM8985_ALCATK_SHIFT 0 /* ALCATK - [3:0] */
+#define WM8985_ALCATK_WIDTH 4 /* ALCATK - [3:0] */
+
+/*
+ * R35 (0x23) - Noise Gate
+ */
+#define WM8985_NGEN 0x0008 /* NGEN */
+#define WM8985_NGEN_MASK 0x0008 /* NGEN */
+#define WM8985_NGEN_SHIFT 3 /* NGEN */
+#define WM8985_NGEN_WIDTH 1 /* NGEN */
+#define WM8985_NGTH_MASK 0x0007 /* NGTH - [2:0] */
+#define WM8985_NGTH_SHIFT 0 /* NGTH - [2:0] */
+#define WM8985_NGTH_WIDTH 3 /* NGTH - [2:0] */
+
+/*
+ * R36 (0x24) - PLL N
+ */
+#define WM8985_PLL_PRESCALE 0x0010 /* PLL_PRESCALE */
+#define WM8985_PLL_PRESCALE_MASK 0x0010 /* PLL_PRESCALE */
+#define WM8985_PLL_PRESCALE_SHIFT 4 /* PLL_PRESCALE */
+#define WM8985_PLL_PRESCALE_WIDTH 1 /* PLL_PRESCALE */
+#define WM8985_PLLN_MASK 0x000F /* PLLN - [3:0] */
+#define WM8985_PLLN_SHIFT 0 /* PLLN - [3:0] */
+#define WM8985_PLLN_WIDTH 4 /* PLLN - [3:0] */
+
+/*
+ * R37 (0x25) - PLL K 1
+ */
+#define WM8985_PLLK_23_18_MASK 0x003F /* PLLK(23:18) - [5:0] */
+#define WM8985_PLLK_23_18_SHIFT 0 /* PLLK(23:18) - [5:0] */
+#define WM8985_PLLK_23_18_WIDTH 6 /* PLLK(23:18) - [5:0] */
+
+/*
+ * R38 (0x26) - PLL K 2
+ */
+#define WM8985_PLLK_17_9_MASK 0x01FF /* PLLK(17:9) - [8:0] */
+#define WM8985_PLLK_17_9_SHIFT 0 /* PLLK(17:9) - [8:0] */
+#define WM8985_PLLK_17_9_WIDTH 9 /* PLLK(17:9) - [8:0] */
+
+/*
+ * R39 (0x27) - PLL K 3
+ */
+#define WM8985_PLLK_8_0_MASK 0x01FF /* PLLK(8:0) - [8:0] */
+#define WM8985_PLLK_8_0_SHIFT 0 /* PLLK(8:0) - [8:0] */
+#define WM8985_PLLK_8_0_WIDTH 9 /* PLLK(8:0) - [8:0] */
+
+/*
+ * R41 (0x29) - 3D control
+ */
+#define WM8985_DEPTH3D_MASK 0x000F /* DEPTH3D - [3:0] */
+#define WM8985_DEPTH3D_SHIFT 0 /* DEPTH3D - [3:0] */
+#define WM8985_DEPTH3D_WIDTH 4 /* DEPTH3D - [3:0] */
+
+/*
+ * R42 (0x2A) - OUT4 to ADC
+ */
+#define WM8985_OUT4_2ADCVOL_MASK 0x01C0 /* OUT4_2ADCVOL - [8:6] */
+#define WM8985_OUT4_2ADCVOL_SHIFT 6 /* OUT4_2ADCVOL - [8:6] */
+#define WM8985_OUT4_2ADCVOL_WIDTH 3 /* OUT4_2ADCVOL - [8:6] */
+#define WM8985_OUT4_2LNR 0x0020 /* OUT4_2LNR */
+#define WM8985_OUT4_2LNR_MASK 0x0020 /* OUT4_2LNR */
+#define WM8985_OUT4_2LNR_SHIFT 5 /* OUT4_2LNR */
+#define WM8985_OUT4_2LNR_WIDTH 1 /* OUT4_2LNR */
+#define WM8985_POBCTRL 0x0004 /* POBCTRL */
+#define WM8985_POBCTRL_MASK 0x0004 /* POBCTRL */
+#define WM8985_POBCTRL_SHIFT 2 /* POBCTRL */
+#define WM8985_POBCTRL_WIDTH 1 /* POBCTRL */
+#define WM8985_DELEN 0x0002 /* DELEN */
+#define WM8985_DELEN_MASK 0x0002 /* DELEN */
+#define WM8985_DELEN_SHIFT 1 /* DELEN */
+#define WM8985_DELEN_WIDTH 1 /* DELEN */
+#define WM8985_OUT1DEL 0x0001 /* OUT1DEL */
+#define WM8985_OUT1DEL_MASK 0x0001 /* OUT1DEL */
+#define WM8985_OUT1DEL_SHIFT 0 /* OUT1DEL */
+#define WM8985_OUT1DEL_WIDTH 1 /* OUT1DEL */
+
+/*
+ * R43 (0x2B) - Beep control
+ */
+#define WM8985_BYPL2RMIX 0x0100 /* BYPL2RMIX */
+#define WM8985_BYPL2RMIX_MASK 0x0100 /* BYPL2RMIX */
+#define WM8985_BYPL2RMIX_SHIFT 8 /* BYPL2RMIX */
+#define WM8985_BYPL2RMIX_WIDTH 1 /* BYPL2RMIX */
+#define WM8985_BYPR2LMIX 0x0080 /* BYPR2LMIX */
+#define WM8985_BYPR2LMIX_MASK 0x0080 /* BYPR2LMIX */
+#define WM8985_BYPR2LMIX_SHIFT 7 /* BYPR2LMIX */
+#define WM8985_BYPR2LMIX_WIDTH 1 /* BYPR2LMIX */
+#define WM8985_MUTERPGA2INV 0x0020 /* MUTERPGA2INV */
+#define WM8985_MUTERPGA2INV_MASK 0x0020 /* MUTERPGA2INV */
+#define WM8985_MUTERPGA2INV_SHIFT 5 /* MUTERPGA2INV */
+#define WM8985_MUTERPGA2INV_WIDTH 1 /* MUTERPGA2INV */
+#define WM8985_INVROUT2 0x0010 /* INVROUT2 */
+#define WM8985_INVROUT2_MASK 0x0010 /* INVROUT2 */
+#define WM8985_INVROUT2_SHIFT 4 /* INVROUT2 */
+#define WM8985_INVROUT2_WIDTH 1 /* INVROUT2 */
+#define WM8985_BEEPVOL_MASK 0x000E /* BEEPVOL - [3:1] */
+#define WM8985_BEEPVOL_SHIFT 1 /* BEEPVOL - [3:1] */
+#define WM8985_BEEPVOL_WIDTH 3 /* BEEPVOL - [3:1] */
+#define WM8985_BEEPEN 0x0001 /* BEEPEN */
+#define WM8985_BEEPEN_MASK 0x0001 /* BEEPEN */
+#define WM8985_BEEPEN_SHIFT 0 /* BEEPEN */
+#define WM8985_BEEPEN_WIDTH 1 /* BEEPEN */
+
+/*
+ * R44 (0x2C) - Input ctrl
+ */
+#define WM8985_MBVSEL 0x0100 /* MBVSEL */
+#define WM8985_MBVSEL_MASK 0x0100 /* MBVSEL */
+#define WM8985_MBVSEL_SHIFT 8 /* MBVSEL */
+#define WM8985_MBVSEL_WIDTH 1 /* MBVSEL */
+#define WM8985_R2_2INPPGA 0x0040 /* R2_2INPPGA */
+#define WM8985_R2_2INPPGA_MASK 0x0040 /* R2_2INPPGA */
+#define WM8985_R2_2INPPGA_SHIFT 6 /* R2_2INPPGA */
+#define WM8985_R2_2INPPGA_WIDTH 1 /* R2_2INPPGA */
+#define WM8985_RIN2INPPGA 0x0020 /* RIN2INPPGA */
+#define WM8985_RIN2INPPGA_MASK 0x0020 /* RIN2INPPGA */
+#define WM8985_RIN2INPPGA_SHIFT 5 /* RIN2INPPGA */
+#define WM8985_RIN2INPPGA_WIDTH 1 /* RIN2INPPGA */
+#define WM8985_RIP2INPPGA 0x0010 /* RIP2INPPGA */
+#define WM8985_RIP2INPPGA_MASK 0x0010 /* RIP2INPPGA */
+#define WM8985_RIP2INPPGA_SHIFT 4 /* RIP2INPPGA */
+#define WM8985_RIP2INPPGA_WIDTH 1 /* RIP2INPPGA */
+#define WM8985_L2_2INPPGA 0x0004 /* L2_2INPPGA */
+#define WM8985_L2_2INPPGA_MASK 0x0004 /* L2_2INPPGA */
+#define WM8985_L2_2INPPGA_SHIFT 2 /* L2_2INPPGA */
+#define WM8985_L2_2INPPGA_WIDTH 1 /* L2_2INPPGA */
+#define WM8985_LIN2INPPGA 0x0002 /* LIN2INPPGA */
+#define WM8985_LIN2INPPGA_MASK 0x0002 /* LIN2INPPGA */
+#define WM8985_LIN2INPPGA_SHIFT 1 /* LIN2INPPGA */
+#define WM8985_LIN2INPPGA_WIDTH 1 /* LIN2INPPGA */
+#define WM8985_LIP2INPPGA 0x0001 /* LIP2INPPGA */
+#define WM8985_LIP2INPPGA_MASK 0x0001 /* LIP2INPPGA */
+#define WM8985_LIP2INPPGA_SHIFT 0 /* LIP2INPPGA */
+#define WM8985_LIP2INPPGA_WIDTH 1 /* LIP2INPPGA */
+
+/*
+ * R45 (0x2D) - Left INP PGA gain ctrl
+ */
+#define WM8985_INPGAVU 0x0100 /* INPGAVU */
+#define WM8985_INPGAVU_MASK 0x0100 /* INPGAVU */
+#define WM8985_INPGAVU_SHIFT 8 /* INPGAVU */
+#define WM8985_INPGAVU_WIDTH 1 /* INPGAVU */
+#define WM8985_INPPGAZCL 0x0080 /* INPPGAZCL */
+#define WM8985_INPPGAZCL_MASK 0x0080 /* INPPGAZCL */
+#define WM8985_INPPGAZCL_SHIFT 7 /* INPPGAZCL */
+#define WM8985_INPPGAZCL_WIDTH 1 /* INPPGAZCL */
+#define WM8985_INPPGAMUTEL 0x0040 /* INPPGAMUTEL */
+#define WM8985_INPPGAMUTEL_MASK 0x0040 /* INPPGAMUTEL */
+#define WM8985_INPPGAMUTEL_SHIFT 6 /* INPPGAMUTEL */
+#define WM8985_INPPGAMUTEL_WIDTH 1 /* INPPGAMUTEL */
+#define WM8985_INPPGAVOLL_MASK 0x003F /* INPPGAVOLL - [5:0] */
+#define WM8985_INPPGAVOLL_SHIFT 0 /* INPPGAVOLL - [5:0] */
+#define WM8985_INPPGAVOLL_WIDTH 6 /* INPPGAVOLL - [5:0] */
+
+/*
+ * R46 (0x2E) - Right INP PGA gain ctrl
+ */
+#define WM8985_INPGAVU 0x0100 /* INPGAVU */
+#define WM8985_INPGAVU_MASK 0x0100 /* INPGAVU */
+#define WM8985_INPGAVU_SHIFT 8 /* INPGAVU */
+#define WM8985_INPGAVU_WIDTH 1 /* INPGAVU */
+#define WM8985_INPPGAZCR 0x0080 /* INPPGAZCR */
+#define WM8985_INPPGAZCR_MASK 0x0080 /* INPPGAZCR */
+#define WM8985_INPPGAZCR_SHIFT 7 /* INPPGAZCR */
+#define WM8985_INPPGAZCR_WIDTH 1 /* INPPGAZCR */
+#define WM8985_INPPGAMUTER 0x0040 /* INPPGAMUTER */
+#define WM8985_INPPGAMUTER_MASK 0x0040 /* INPPGAMUTER */
+#define WM8985_INPPGAMUTER_SHIFT 6 /* INPPGAMUTER */
+#define WM8985_INPPGAMUTER_WIDTH 1 /* INPPGAMUTER */
+#define WM8985_INPPGAVOLR_MASK 0x003F /* INPPGAVOLR - [5:0] */
+#define WM8985_INPPGAVOLR_SHIFT 0 /* INPPGAVOLR - [5:0] */
+#define WM8985_INPPGAVOLR_WIDTH 6 /* INPPGAVOLR - [5:0] */
+
+/*
+ * R47 (0x2F) - Left ADC BOOST ctrl
+ */
+#define WM8985_PGABOOSTL 0x0100 /* PGABOOSTL */
+#define WM8985_PGABOOSTL_MASK 0x0100 /* PGABOOSTL */
+#define WM8985_PGABOOSTL_SHIFT 8 /* PGABOOSTL */
+#define WM8985_PGABOOSTL_WIDTH 1 /* PGABOOSTL */
+#define WM8985_L2_2BOOSTVOL_MASK 0x0070 /* L2_2BOOSTVOL - [6:4] */
+#define WM8985_L2_2BOOSTVOL_SHIFT 4 /* L2_2BOOSTVOL - [6:4] */
+#define WM8985_L2_2BOOSTVOL_WIDTH 3 /* L2_2BOOSTVOL - [6:4] */
+#define WM8985_AUXL2BOOSTVOL_MASK 0x0007 /* AUXL2BOOSTVOL - [2:0] */
+#define WM8985_AUXL2BOOSTVOL_SHIFT 0 /* AUXL2BOOSTVOL - [2:0] */
+#define WM8985_AUXL2BOOSTVOL_WIDTH 3 /* AUXL2BOOSTVOL - [2:0] */
+
+/*
+ * R48 (0x30) - Right ADC BOOST ctrl
+ */
+#define WM8985_PGABOOSTR 0x0100 /* PGABOOSTR */
+#define WM8985_PGABOOSTR_MASK 0x0100 /* PGABOOSTR */
+#define WM8985_PGABOOSTR_SHIFT 8 /* PGABOOSTR */
+#define WM8985_PGABOOSTR_WIDTH 1 /* PGABOOSTR */
+#define WM8985_R2_2BOOSTVOL_MASK 0x0070 /* R2_2BOOSTVOL - [6:4] */
+#define WM8985_R2_2BOOSTVOL_SHIFT 4 /* R2_2BOOSTVOL - [6:4] */
+#define WM8985_R2_2BOOSTVOL_WIDTH 3 /* R2_2BOOSTVOL - [6:4] */
+#define WM8985_AUXR2BOOSTVOL_MASK 0x0007 /* AUXR2BOOSTVOL - [2:0] */
+#define WM8985_AUXR2BOOSTVOL_SHIFT 0 /* AUXR2BOOSTVOL - [2:0] */
+#define WM8985_AUXR2BOOSTVOL_WIDTH 3 /* AUXR2BOOSTVOL - [2:0] */
+
+/*
+ * R49 (0x31) - Output ctrl
+ */
+#define WM8985_DACL2RMIX 0x0040 /* DACL2RMIX */
+#define WM8985_DACL2RMIX_MASK 0x0040 /* DACL2RMIX */
+#define WM8985_DACL2RMIX_SHIFT 6 /* DACL2RMIX */
+#define WM8985_DACL2RMIX_WIDTH 1 /* DACL2RMIX */
+#define WM8985_DACR2LMIX 0x0020 /* DACR2LMIX */
+#define WM8985_DACR2LMIX_MASK 0x0020 /* DACR2LMIX */
+#define WM8985_DACR2LMIX_SHIFT 5 /* DACR2LMIX */
+#define WM8985_DACR2LMIX_WIDTH 1 /* DACR2LMIX */
+#define WM8985_OUT4BOOST 0x0010 /* OUT4BOOST */
+#define WM8985_OUT4BOOST_MASK 0x0010 /* OUT4BOOST */
+#define WM8985_OUT4BOOST_SHIFT 4 /* OUT4BOOST */
+#define WM8985_OUT4BOOST_WIDTH 1 /* OUT4BOOST */
+#define WM8985_OUT3BOOST 0x0008 /* OUT3BOOST */
+#define WM8985_OUT3BOOST_MASK 0x0008 /* OUT3BOOST */
+#define WM8985_OUT3BOOST_SHIFT 3 /* OUT3BOOST */
+#define WM8985_OUT3BOOST_WIDTH 1 /* OUT3BOOST */
+#define WM8985_TSOPCTRL 0x0004 /* TSOPCTRL */
+#define WM8985_TSOPCTRL_MASK 0x0004 /* TSOPCTRL */
+#define WM8985_TSOPCTRL_SHIFT 2 /* TSOPCTRL */
+#define WM8985_TSOPCTRL_WIDTH 1 /* TSOPCTRL */
+#define WM8985_TSDEN 0x0002 /* TSDEN */
+#define WM8985_TSDEN_MASK 0x0002 /* TSDEN */
+#define WM8985_TSDEN_SHIFT 1 /* TSDEN */
+#define WM8985_TSDEN_WIDTH 1 /* TSDEN */
+#define WM8985_VROI 0x0001 /* VROI */
+#define WM8985_VROI_MASK 0x0001 /* VROI */
+#define WM8985_VROI_SHIFT 0 /* VROI */
+#define WM8985_VROI_WIDTH 1 /* VROI */
+
+/*
+ * R50 (0x32) - Left mixer ctrl
+ */
+#define WM8985_AUXLMIXVOL_MASK 0x01C0 /* AUXLMIXVOL - [8:6] */
+#define WM8985_AUXLMIXVOL_SHIFT 6 /* AUXLMIXVOL - [8:6] */
+#define WM8985_AUXLMIXVOL_WIDTH 3 /* AUXLMIXVOL - [8:6] */
+#define WM8985_AUXL2LMIX 0x0020 /* AUXL2LMIX */
+#define WM8985_AUXL2LMIX_MASK 0x0020 /* AUXL2LMIX */
+#define WM8985_AUXL2LMIX_SHIFT 5 /* AUXL2LMIX */
+#define WM8985_AUXL2LMIX_WIDTH 1 /* AUXL2LMIX */
+#define WM8985_BYPLMIXVOL_MASK 0x001C /* BYPLMIXVOL - [4:2] */
+#define WM8985_BYPLMIXVOL_SHIFT 2 /* BYPLMIXVOL - [4:2] */
+#define WM8985_BYPLMIXVOL_WIDTH 3 /* BYPLMIXVOL - [4:2] */
+#define WM8985_BYPL2LMIX 0x0002 /* BYPL2LMIX */
+#define WM8985_BYPL2LMIX_MASK 0x0002 /* BYPL2LMIX */
+#define WM8985_BYPL2LMIX_SHIFT 1 /* BYPL2LMIX */
+#define WM8985_BYPL2LMIX_WIDTH 1 /* BYPL2LMIX */
+#define WM8985_DACL2LMIX 0x0001 /* DACL2LMIX */
+#define WM8985_DACL2LMIX_MASK 0x0001 /* DACL2LMIX */
+#define WM8985_DACL2LMIX_SHIFT 0 /* DACL2LMIX */
+#define WM8985_DACL2LMIX_WIDTH 1 /* DACL2LMIX */
+
+/*
+ * R51 (0x33) - Right mixer ctrl
+ */
+#define WM8985_AUXRMIXVOL_MASK 0x01C0 /* AUXRMIXVOL - [8:6] */
+#define WM8985_AUXRMIXVOL_SHIFT 6 /* AUXRMIXVOL - [8:6] */
+#define WM8985_AUXRMIXVOL_WIDTH 3 /* AUXRMIXVOL - [8:6] */
+#define WM8985_AUXR2RMIX 0x0020 /* AUXR2RMIX */
+#define WM8985_AUXR2RMIX_MASK 0x0020 /* AUXR2RMIX */
+#define WM8985_AUXR2RMIX_SHIFT 5 /* AUXR2RMIX */
+#define WM8985_AUXR2RMIX_WIDTH 1 /* AUXR2RMIX */
+#define WM8985_BYPRMIXVOL_MASK 0x001C /* BYPRMIXVOL - [4:2] */
+#define WM8985_BYPRMIXVOL_SHIFT 2 /* BYPRMIXVOL - [4:2] */
+#define WM8985_BYPRMIXVOL_WIDTH 3 /* BYPRMIXVOL - [4:2] */
+#define WM8985_BYPR2RMIX 0x0002 /* BYPR2RMIX */
+#define WM8985_BYPR2RMIX_MASK 0x0002 /* BYPR2RMIX */
+#define WM8985_BYPR2RMIX_SHIFT 1 /* BYPR2RMIX */
+#define WM8985_BYPR2RMIX_WIDTH 1 /* BYPR2RMIX */
+#define WM8985_DACR2RMIX 0x0001 /* DACR2RMIX */
+#define WM8985_DACR2RMIX_MASK 0x0001 /* DACR2RMIX */
+#define WM8985_DACR2RMIX_SHIFT 0 /* DACR2RMIX */
+#define WM8985_DACR2RMIX_WIDTH 1 /* DACR2RMIX */
+
+/*
+ * R52 (0x34) - LOUT1 (HP) volume ctrl
+ */
+#define WM8985_OUT1VU 0x0100 /* OUT1VU */
+#define WM8985_OUT1VU_MASK 0x0100 /* OUT1VU */
+#define WM8985_OUT1VU_SHIFT 8 /* OUT1VU */
+#define WM8985_OUT1VU_WIDTH 1 /* OUT1VU */
+#define WM8985_LOUT1ZC 0x0080 /* LOUT1ZC */
+#define WM8985_LOUT1ZC_MASK 0x0080 /* LOUT1ZC */
+#define WM8985_LOUT1ZC_SHIFT 7 /* LOUT1ZC */
+#define WM8985_LOUT1ZC_WIDTH 1 /* LOUT1ZC */
+#define WM8985_LOUT1MUTE 0x0040 /* LOUT1MUTE */
+#define WM8985_LOUT1MUTE_MASK 0x0040 /* LOUT1MUTE */
+#define WM8985_LOUT1MUTE_SHIFT 6 /* LOUT1MUTE */
+#define WM8985_LOUT1MUTE_WIDTH 1 /* LOUT1MUTE */
+#define WM8985_LOUT1VOL_MASK 0x003F /* LOUT1VOL - [5:0] */
+#define WM8985_LOUT1VOL_SHIFT 0 /* LOUT1VOL - [5:0] */
+#define WM8985_LOUT1VOL_WIDTH 6 /* LOUT1VOL - [5:0] */
+
+/*
+ * R53 (0x35) - ROUT1 (HP) volume ctrl
+ */
+#define WM8985_OUT1VU 0x0100 /* OUT1VU */
+#define WM8985_OUT1VU_MASK 0x0100 /* OUT1VU */
+#define WM8985_OUT1VU_SHIFT 8 /* OUT1VU */
+#define WM8985_OUT1VU_WIDTH 1 /* OUT1VU */
+#define WM8985_ROUT1ZC 0x0080 /* ROUT1ZC */
+#define WM8985_ROUT1ZC_MASK 0x0080 /* ROUT1ZC */
+#define WM8985_ROUT1ZC_SHIFT 7 /* ROUT1ZC */
+#define WM8985_ROUT1ZC_WIDTH 1 /* ROUT1ZC */
+#define WM8985_ROUT1MUTE 0x0040 /* ROUT1MUTE */
+#define WM8985_ROUT1MUTE_MASK 0x0040 /* ROUT1MUTE */
+#define WM8985_ROUT1MUTE_SHIFT 6 /* ROUT1MUTE */
+#define WM8985_ROUT1MUTE_WIDTH 1 /* ROUT1MUTE */
+#define WM8985_ROUT1VOL_MASK 0x003F /* ROUT1VOL - [5:0] */
+#define WM8985_ROUT1VOL_SHIFT 0 /* ROUT1VOL - [5:0] */
+#define WM8985_ROUT1VOL_WIDTH 6 /* ROUT1VOL - [5:0] */
+
+/*
+ * R54 (0x36) - LOUT2 (SPK) volume ctrl
+ */
+#define WM8985_OUT2VU 0x0100 /* OUT2VU */
+#define WM8985_OUT2VU_MASK 0x0100 /* OUT2VU */
+#define WM8985_OUT2VU_SHIFT 8 /* OUT2VU */
+#define WM8985_OUT2VU_WIDTH 1 /* OUT2VU */
+#define WM8985_LOUT2ZC 0x0080 /* LOUT2ZC */
+#define WM8985_LOUT2ZC_MASK 0x0080 /* LOUT2ZC */
+#define WM8985_LOUT2ZC_SHIFT 7 /* LOUT2ZC */
+#define WM8985_LOUT2ZC_WIDTH 1 /* LOUT2ZC */
+#define WM8985_LOUT2MUTE 0x0040 /* LOUT2MUTE */
+#define WM8985_LOUT2MUTE_MASK 0x0040 /* LOUT2MUTE */
+#define WM8985_LOUT2MUTE_SHIFT 6 /* LOUT2MUTE */
+#define WM8985_LOUT2MUTE_WIDTH 1 /* LOUT2MUTE */
+#define WM8985_LOUT2VOL_MASK 0x003F /* LOUT2VOL - [5:0] */
+#define WM8985_LOUT2VOL_SHIFT 0 /* LOUT2VOL - [5:0] */
+#define WM8985_LOUT2VOL_WIDTH 6 /* LOUT2VOL - [5:0] */
+
+/*
+ * R55 (0x37) - ROUT2 (SPK) volume ctrl
+ */
+#define WM8985_OUT2VU 0x0100 /* OUT2VU */
+#define WM8985_OUT2VU_MASK 0x0100 /* OUT2VU */
+#define WM8985_OUT2VU_SHIFT 8 /* OUT2VU */
+#define WM8985_OUT2VU_WIDTH 1 /* OUT2VU */
+#define WM8985_ROUT2ZC 0x0080 /* ROUT2ZC */
+#define WM8985_ROUT2ZC_MASK 0x0080 /* ROUT2ZC */
+#define WM8985_ROUT2ZC_SHIFT 7 /* ROUT2ZC */
+#define WM8985_ROUT2ZC_WIDTH 1 /* ROUT2ZC */
+#define WM8985_ROUT2MUTE 0x0040 /* ROUT2MUTE */
+#define WM8985_ROUT2MUTE_MASK 0x0040 /* ROUT2MUTE */
+#define WM8985_ROUT2MUTE_SHIFT 6 /* ROUT2MUTE */
+#define WM8985_ROUT2MUTE_WIDTH 1 /* ROUT2MUTE */
+#define WM8985_ROUT2VOL_MASK 0x003F /* ROUT2VOL - [5:0] */
+#define WM8985_ROUT2VOL_SHIFT 0 /* ROUT2VOL - [5:0] */
+#define WM8985_ROUT2VOL_WIDTH 6 /* ROUT2VOL - [5:0] */
+
+/*
+ * R56 (0x38) - OUT3 mixer ctrl
+ */
+#define WM8985_OUT3MUTE 0x0040 /* OUT3MUTE */
+#define WM8985_OUT3MUTE_MASK 0x0040 /* OUT3MUTE */
+#define WM8985_OUT3MUTE_SHIFT 6 /* OUT3MUTE */
+#define WM8985_OUT3MUTE_WIDTH 1 /* OUT3MUTE */
+#define WM8985_OUT4_2OUT3 0x0008 /* OUT4_2OUT3 */
+#define WM8985_OUT4_2OUT3_MASK 0x0008 /* OUT4_2OUT3 */
+#define WM8985_OUT4_2OUT3_SHIFT 3 /* OUT4_2OUT3 */
+#define WM8985_OUT4_2OUT3_WIDTH 1 /* OUT4_2OUT3 */
+#define WM8985_BYPL2OUT3 0x0004 /* BYPL2OUT3 */
+#define WM8985_BYPL2OUT3_MASK 0x0004 /* BYPL2OUT3 */
+#define WM8985_BYPL2OUT3_SHIFT 2 /* BYPL2OUT3 */
+#define WM8985_BYPL2OUT3_WIDTH 1 /* BYPL2OUT3 */
+#define WM8985_LMIX2OUT3 0x0002 /* LMIX2OUT3 */
+#define WM8985_LMIX2OUT3_MASK 0x0002 /* LMIX2OUT3 */
+#define WM8985_LMIX2OUT3_SHIFT 1 /* LMIX2OUT3 */
+#define WM8985_LMIX2OUT3_WIDTH 1 /* LMIX2OUT3 */
+#define WM8985_LDAC2OUT3 0x0001 /* LDAC2OUT3 */
+#define WM8985_LDAC2OUT3_MASK 0x0001 /* LDAC2OUT3 */
+#define WM8985_LDAC2OUT3_SHIFT 0 /* LDAC2OUT3 */
+#define WM8985_LDAC2OUT3_WIDTH 1 /* LDAC2OUT3 */
+
+/*
+ * R57 (0x39) - OUT4 (MONO) mix ctrl
+ */
+#define WM8985_OUT3_2OUT4 0x0080 /* OUT3_2OUT4 */
+#define WM8985_OUT3_2OUT4_MASK 0x0080 /* OUT3_2OUT4 */
+#define WM8985_OUT3_2OUT4_SHIFT 7 /* OUT3_2OUT4 */
+#define WM8985_OUT3_2OUT4_WIDTH 1 /* OUT3_2OUT4 */
+#define WM8985_OUT4MUTE 0x0040 /* OUT4MUTE */
+#define WM8985_OUT4MUTE_MASK 0x0040 /* OUT4MUTE */
+#define WM8985_OUT4MUTE_SHIFT 6 /* OUT4MUTE */
+#define WM8985_OUT4MUTE_WIDTH 1 /* OUT4MUTE */
+#define WM8985_OUT4ATTN 0x0020 /* OUT4ATTN */
+#define WM8985_OUT4ATTN_MASK 0x0020 /* OUT4ATTN */
+#define WM8985_OUT4ATTN_SHIFT 5 /* OUT4ATTN */
+#define WM8985_OUT4ATTN_WIDTH 1 /* OUT4ATTN */
+#define WM8985_LMIX2OUT4 0x0010 /* LMIX2OUT4 */
+#define WM8985_LMIX2OUT4_MASK 0x0010 /* LMIX2OUT4 */
+#define WM8985_LMIX2OUT4_SHIFT 4 /* LMIX2OUT4 */
+#define WM8985_LMIX2OUT4_WIDTH 1 /* LMIX2OUT4 */
+#define WM8985_LDAC2OUT4 0x0008 /* LDAC2OUT4 */
+#define WM8985_LDAC2OUT4_MASK 0x0008 /* LDAC2OUT4 */
+#define WM8985_LDAC2OUT4_SHIFT 3 /* LDAC2OUT4 */
+#define WM8985_LDAC2OUT4_WIDTH 1 /* LDAC2OUT4 */
+#define WM8985_BYPR2OUT4 0x0004 /* BYPR2OUT4 */
+#define WM8985_BYPR2OUT4_MASK 0x0004 /* BYPR2OUT4 */
+#define WM8985_BYPR2OUT4_SHIFT 2 /* BYPR2OUT4 */
+#define WM8985_BYPR2OUT4_WIDTH 1 /* BYPR2OUT4 */
+#define WM8985_RMIX2OUT4 0x0002 /* RMIX2OUT4 */
+#define WM8985_RMIX2OUT4_MASK 0x0002 /* RMIX2OUT4 */
+#define WM8985_RMIX2OUT4_SHIFT 1 /* RMIX2OUT4 */
+#define WM8985_RMIX2OUT4_WIDTH 1 /* RMIX2OUT4 */
+#define WM8985_RDAC2OUT4 0x0001 /* RDAC2OUT4 */
+#define WM8985_RDAC2OUT4_MASK 0x0001 /* RDAC2OUT4 */
+#define WM8985_RDAC2OUT4_SHIFT 0 /* RDAC2OUT4 */
+#define WM8985_RDAC2OUT4_WIDTH 1 /* RDAC2OUT4 */
+
+/*
+ * R60 (0x3C) - OUTPUT ctrl
+ */
+#define WM8985_VIDBUFFTST_MASK 0x01E0 /* VIDBUFFTST - [8:5] */
+#define WM8985_VIDBUFFTST_SHIFT 5 /* VIDBUFFTST - [8:5] */
+#define WM8985_VIDBUFFTST_WIDTH 4 /* VIDBUFFTST - [8:5] */
+#define WM8985_HPTOG 0x0008 /* HPTOG */
+#define WM8985_HPTOG_MASK 0x0008 /* HPTOG */
+#define WM8985_HPTOG_SHIFT 3 /* HPTOG */
+#define WM8985_HPTOG_WIDTH 1 /* HPTOG */
+
+/*
+ * R61 (0x3D) - BIAS CTRL
+ */
+#define WM8985_BIASCUT 0x0100 /* BIASCUT */
+#define WM8985_BIASCUT_MASK 0x0100 /* BIASCUT */
+#define WM8985_BIASCUT_SHIFT 8 /* BIASCUT */
+#define WM8985_BIASCUT_WIDTH 1 /* BIASCUT */
+#define WM8985_HALFIPBIAS 0x0080 /* HALFIPBIAS */
+#define WM8985_HALFIPBIAS_MASK 0x0080 /* HALFIPBIAS */
+#define WM8985_HALFIPBIAS_SHIFT 7 /* HALFIPBIAS */
+#define WM8985_HALFIPBIAS_WIDTH 1 /* HALFIPBIAS */
+#define WM8985_VBBIASTST_MASK 0x0060 /* VBBIASTST - [6:5] */
+#define WM8985_VBBIASTST_SHIFT 5 /* VBBIASTST - [6:5] */
+#define WM8985_VBBIASTST_WIDTH 2 /* VBBIASTST - [6:5] */
+#define WM8985_BUFBIAS_MASK 0x0018 /* BUFBIAS - [4:3] */
+#define WM8985_BUFBIAS_SHIFT 3 /* BUFBIAS - [4:3] */
+#define WM8985_BUFBIAS_WIDTH 2 /* BUFBIAS - [4:3] */
+#define WM8985_ADCBIAS_MASK 0x0006 /* ADCBIAS - [2:1] */
+#define WM8985_ADCBIAS_SHIFT 1 /* ADCBIAS - [2:1] */
+#define WM8985_ADCBIAS_WIDTH 2 /* ADCBIAS - [2:1] */
+#define WM8985_HALFOPBIAS 0x0001 /* HALFOPBIAS */
+#define WM8985_HALFOPBIAS_MASK 0x0001 /* HALFOPBIAS */
+#define WM8985_HALFOPBIAS_SHIFT 0 /* HALFOPBIAS */
+#define WM8985_HALFOPBIAS_WIDTH 1 /* HALFOPBIAS */
+
+enum clk_src {
+ WM8985_CLKSRC_MCLK,
+ WM8985_CLKSRC_PLL
+};
+
+#define WM8985_PLL 0
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8988.c b/kernel/sound/soc/codecs/wm8988.c
new file mode 100644
index 000000000..24968aa86
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8988.c
@@ -0,0 +1,966 @@
+/*
+ * wm8988.c -- WM8988 ALSA SoC audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.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 <sound/initval.h>
+
+#include "wm8988.h"
+
+/*
+ * wm8988 register cache
+ * We can't read the WM8988 register space when we
+ * are using 2 wire for device control, so we cache them instead.
+ */
+static const struct reg_default wm8988_reg_defaults[] = {
+ { 0, 0x0097 },
+ { 1, 0x0097 },
+ { 2, 0x0079 },
+ { 3, 0x0079 },
+ { 5, 0x0008 },
+ { 7, 0x000a },
+ { 8, 0x0000 },
+ { 10, 0x00ff },
+ { 11, 0x00ff },
+ { 12, 0x000f },
+ { 13, 0x000f },
+ { 16, 0x0000 },
+ { 17, 0x007b },
+ { 18, 0x0000 },
+ { 19, 0x0032 },
+ { 20, 0x0000 },
+ { 21, 0x00c3 },
+ { 22, 0x00c3 },
+ { 23, 0x00c0 },
+ { 24, 0x0000 },
+ { 25, 0x0000 },
+ { 26, 0x0000 },
+ { 27, 0x0000 },
+ { 31, 0x0000 },
+ { 32, 0x0000 },
+ { 33, 0x0000 },
+ { 34, 0x0050 },
+ { 35, 0x0050 },
+ { 36, 0x0050 },
+ { 37, 0x0050 },
+ { 40, 0x0079 },
+ { 41, 0x0079 },
+ { 42, 0x0079 },
+};
+
+static bool wm8988_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8988_LINVOL:
+ case WM8988_RINVOL:
+ case WM8988_LOUT1V:
+ case WM8988_ROUT1V:
+ case WM8988_ADCDAC:
+ case WM8988_IFACE:
+ case WM8988_SRATE:
+ case WM8988_LDAC:
+ case WM8988_RDAC:
+ case WM8988_BASS:
+ case WM8988_TREBLE:
+ case WM8988_RESET:
+ case WM8988_3D:
+ case WM8988_ALC1:
+ case WM8988_ALC2:
+ case WM8988_ALC3:
+ case WM8988_NGATE:
+ case WM8988_LADC:
+ case WM8988_RADC:
+ case WM8988_ADCTL1:
+ case WM8988_ADCTL2:
+ case WM8988_PWR1:
+ case WM8988_PWR2:
+ case WM8988_ADCTL3:
+ case WM8988_ADCIN:
+ case WM8988_LADCIN:
+ case WM8988_RADCIN:
+ case WM8988_LOUTM1:
+ case WM8988_LOUTM2:
+ case WM8988_ROUTM1:
+ case WM8988_ROUTM2:
+ case WM8988_LOUT2V:
+ case WM8988_ROUT2V:
+ case WM8988_LPPB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* codec private data */
+struct wm8988_priv {
+ struct regmap *regmap;
+ unsigned int sysclk;
+ const struct snd_pcm_hw_constraint_list *sysclk_constraints;
+};
+
+#define wm8988_reset(c) snd_soc_write(c, WM8988_RESET, 0)
+
+/*
+ * WM8988 Controls
+ */
+
+static const char *bass_boost_txt[] = {"Linear Control", "Adaptive Boost"};
+static SOC_ENUM_SINGLE_DECL(bass_boost,
+ WM8988_BASS, 7, bass_boost_txt);
+
+static const char *bass_filter_txt[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" };
+static SOC_ENUM_SINGLE_DECL(bass_filter,
+ WM8988_BASS, 6, bass_filter_txt);
+
+static const char *treble_txt[] = {"8kHz", "4kHz"};
+static SOC_ENUM_SINGLE_DECL(treble,
+ WM8988_TREBLE, 6, treble_txt);
+
+static const char *stereo_3d_lc_txt[] = {"200Hz", "500Hz"};
+static SOC_ENUM_SINGLE_DECL(stereo_3d_lc,
+ WM8988_3D, 5, stereo_3d_lc_txt);
+
+static const char *stereo_3d_uc_txt[] = {"2.2kHz", "1.5kHz"};
+static SOC_ENUM_SINGLE_DECL(stereo_3d_uc,
+ WM8988_3D, 6, stereo_3d_uc_txt);
+
+static const char *stereo_3d_func_txt[] = {"Capture", "Playback"};
+static SOC_ENUM_SINGLE_DECL(stereo_3d_func,
+ WM8988_3D, 7, stereo_3d_func_txt);
+
+static const char *alc_func_txt[] = {"Off", "Right", "Left", "Stereo"};
+static SOC_ENUM_SINGLE_DECL(alc_func,
+ WM8988_ALC1, 7, alc_func_txt);
+
+static const char *ng_type_txt[] = {"Constant PGA Gain",
+ "Mute ADC Output"};
+static SOC_ENUM_SINGLE_DECL(ng_type,
+ WM8988_NGATE, 1, ng_type_txt);
+
+static const char *deemph_txt[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static SOC_ENUM_SINGLE_DECL(deemph,
+ WM8988_ADCDAC, 1, deemph_txt);
+
+static const char *adcpol_txt[] = {"Normal", "L Invert", "R Invert",
+ "L + R Invert"};
+static SOC_ENUM_SINGLE_DECL(adcpol,
+ WM8988_ADCDAC, 5, adcpol_txt);
+
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
+
+static const struct snd_kcontrol_new wm8988_snd_controls[] = {
+
+SOC_ENUM("Bass Boost", bass_boost),
+SOC_ENUM("Bass Filter", bass_filter),
+SOC_SINGLE("Bass Volume", WM8988_BASS, 0, 15, 1),
+
+SOC_SINGLE("Treble Volume", WM8988_TREBLE, 0, 15, 0),
+SOC_ENUM("Treble Cut-off", treble),
+
+SOC_SINGLE("3D Switch", WM8988_3D, 0, 1, 0),
+SOC_SINGLE("3D Volume", WM8988_3D, 1, 15, 0),
+SOC_ENUM("3D Lower Cut-off", stereo_3d_lc),
+SOC_ENUM("3D Upper Cut-off", stereo_3d_uc),
+SOC_ENUM("3D Mode", stereo_3d_func),
+
+SOC_SINGLE("ALC Capture Target Volume", WM8988_ALC1, 0, 7, 0),
+SOC_SINGLE("ALC Capture Max Volume", WM8988_ALC1, 4, 7, 0),
+SOC_ENUM("ALC Capture Function", alc_func),
+SOC_SINGLE("ALC Capture ZC Switch", WM8988_ALC2, 7, 1, 0),
+SOC_SINGLE("ALC Capture Hold Time", WM8988_ALC2, 0, 15, 0),
+SOC_SINGLE("ALC Capture Decay Time", WM8988_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Capture Attack Time", WM8988_ALC3, 0, 15, 0),
+SOC_SINGLE("ALC Capture NG Threshold", WM8988_NGATE, 3, 31, 0),
+SOC_ENUM("ALC Capture NG Type", ng_type),
+SOC_SINGLE("ALC Capture NG Switch", WM8988_NGATE, 0, 1, 0),
+
+SOC_SINGLE("ZC Timeout Switch", WM8988_ADCTL1, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("Capture Digital Volume", WM8988_LADC, WM8988_RADC,
+ 0, 255, 0, adc_tlv),
+SOC_DOUBLE_R_TLV("Capture Volume", WM8988_LINVOL, WM8988_RINVOL,
+ 0, 63, 0, pga_tlv),
+SOC_DOUBLE_R("Capture ZC Switch", WM8988_LINVOL, WM8988_RINVOL, 6, 1, 0),
+SOC_DOUBLE_R("Capture Switch", WM8988_LINVOL, WM8988_RINVOL, 7, 1, 1),
+
+SOC_ENUM("Playback De-emphasis", deemph),
+
+SOC_ENUM("Capture Polarity", adcpol),
+SOC_SINGLE("Playback 6dB Attenuate", WM8988_ADCDAC, 7, 1, 0),
+SOC_SINGLE("Capture 6dB Attenuate", WM8988_ADCDAC, 8, 1, 0),
+
+SOC_DOUBLE_R_TLV("PCM Volume", WM8988_LDAC, WM8988_RDAC, 0, 255, 0, dac_tlv),
+
+SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", WM8988_LOUTM1, 4, 7, 1,
+ bypass_tlv),
+SOC_SINGLE_TLV("Left Mixer Right Bypass Volume", WM8988_LOUTM2, 4, 7, 1,
+ bypass_tlv),
+SOC_SINGLE_TLV("Right Mixer Left Bypass Volume", WM8988_ROUTM1, 4, 7, 1,
+ bypass_tlv),
+SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", WM8988_ROUTM2, 4, 7, 1,
+ bypass_tlv),
+
+SOC_DOUBLE_R("Output 1 Playback ZC Switch", WM8988_LOUT1V,
+ WM8988_ROUT1V, 7, 1, 0),
+SOC_DOUBLE_R_TLV("Output 1 Playback Volume", WM8988_LOUT1V, WM8988_ROUT1V,
+ 0, 127, 0, out_tlv),
+
+SOC_DOUBLE_R("Output 2 Playback ZC Switch", WM8988_LOUT2V,
+ WM8988_ROUT2V, 7, 1, 0),
+SOC_DOUBLE_R_TLV("Output 2 Playback Volume", WM8988_LOUT2V, WM8988_ROUT2V,
+ 0, 127, 0, out_tlv),
+
+};
+
+/*
+ * DAPM Controls
+ */
+
+static int wm8988_lrc_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u16 adctl2 = snd_soc_read(codec, WM8988_ADCTL2);
+
+ /* Use the DAC to gate LRC if active, otherwise use ADC */
+ if (snd_soc_read(codec, WM8988_PWR2) & 0x180)
+ adctl2 &= ~0x4;
+ else
+ adctl2 |= 0x4;
+
+ return snd_soc_write(codec, WM8988_ADCTL2, adctl2);
+}
+
+static const char *wm8988_line_texts[] = {
+ "Line 1", "Line 2", "PGA", "Differential"};
+
+static const unsigned int wm8988_line_values[] = {
+ 0, 1, 3, 4};
+
+static const struct soc_enum wm8988_lline_enum =
+ SOC_VALUE_ENUM_SINGLE(WM8988_LOUTM1, 0, 7,
+ ARRAY_SIZE(wm8988_line_texts),
+ wm8988_line_texts,
+ wm8988_line_values);
+static const struct snd_kcontrol_new wm8988_left_line_controls =
+ SOC_DAPM_ENUM("Route", wm8988_lline_enum);
+
+static const struct soc_enum wm8988_rline_enum =
+ SOC_VALUE_ENUM_SINGLE(WM8988_ROUTM1, 0, 7,
+ ARRAY_SIZE(wm8988_line_texts),
+ wm8988_line_texts,
+ wm8988_line_values);
+static const struct snd_kcontrol_new wm8988_right_line_controls =
+ SOC_DAPM_ENUM("Route", wm8988_lline_enum);
+
+/* Left Mixer */
+static const struct snd_kcontrol_new wm8988_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Playback Switch", WM8988_LOUTM1, 8, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_LOUTM1, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Playback Switch", WM8988_LOUTM2, 8, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_LOUTM2, 7, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new wm8988_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left Playback Switch", WM8988_ROUTM1, 8, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_ROUTM1, 7, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", WM8988_ROUTM2, 8, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_ROUTM2, 7, 1, 0),
+};
+
+static const char *wm8988_pga_sel[] = {"Line 1", "Line 2", "Differential"};
+static const unsigned int wm8988_pga_val[] = { 0, 1, 3 };
+
+/* Left PGA Mux */
+static const struct soc_enum wm8988_lpga_enum =
+ SOC_VALUE_ENUM_SINGLE(WM8988_LADCIN, 6, 3,
+ ARRAY_SIZE(wm8988_pga_sel),
+ wm8988_pga_sel,
+ wm8988_pga_val);
+static const struct snd_kcontrol_new wm8988_left_pga_controls =
+ SOC_DAPM_ENUM("Route", wm8988_lpga_enum);
+
+/* Right PGA Mux */
+static const struct soc_enum wm8988_rpga_enum =
+ SOC_VALUE_ENUM_SINGLE(WM8988_RADCIN, 6, 3,
+ ARRAY_SIZE(wm8988_pga_sel),
+ wm8988_pga_sel,
+ wm8988_pga_val);
+static const struct snd_kcontrol_new wm8988_right_pga_controls =
+ SOC_DAPM_ENUM("Route", wm8988_rpga_enum);
+
+/* Differential Mux */
+static const char *wm8988_diff_sel[] = {"Line 1", "Line 2"};
+static SOC_ENUM_SINGLE_DECL(diffmux,
+ WM8988_ADCIN, 8, wm8988_diff_sel);
+static const struct snd_kcontrol_new wm8988_diffmux_controls =
+ SOC_DAPM_ENUM("Route", diffmux);
+
+/* Mono ADC Mux */
+static const char *wm8988_mono_mux[] = {"Stereo", "Mono (Left)",
+ "Mono (Right)", "Digital Mono"};
+static SOC_ENUM_SINGLE_DECL(monomux,
+ WM8988_ADCIN, 6, wm8988_mono_mux);
+static const struct snd_kcontrol_new wm8988_monomux_controls =
+ SOC_DAPM_ENUM("Route", monomux);
+
+static const struct snd_soc_dapm_widget wm8988_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Mic Bias", WM8988_PWR1, 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+ &wm8988_diffmux_controls),
+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+ &wm8988_monomux_controls),
+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+ &wm8988_monomux_controls),
+
+ SND_SOC_DAPM_MUX("Left PGA Mux", WM8988_PWR1, 5, 0,
+ &wm8988_left_pga_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", WM8988_PWR1, 4, 0,
+ &wm8988_right_pga_controls),
+
+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
+ &wm8988_left_line_controls),
+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
+ &wm8988_right_line_controls),
+
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8988_PWR1, 2, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8988_PWR1, 3, 0),
+
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8988_PWR2, 7, 0),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8988_PWR2, 8, 0),
+
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ &wm8988_left_mixer_controls[0],
+ ARRAY_SIZE(wm8988_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ &wm8988_right_mixer_controls[0],
+ ARRAY_SIZE(wm8988_right_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("Right Out 2", WM8988_PWR2, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", WM8988_PWR2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", WM8988_PWR2, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", WM8988_PWR2, 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_POST("LRC control", wm8988_lrc_control),
+
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("VREF"),
+
+ SND_SOC_DAPM_INPUT("LINPUT1"),
+ SND_SOC_DAPM_INPUT("LINPUT2"),
+ SND_SOC_DAPM_INPUT("RINPUT1"),
+ SND_SOC_DAPM_INPUT("RINPUT2"),
+};
+
+static const struct snd_soc_dapm_route wm8988_dapm_routes[] = {
+
+ { "Left Line Mux", "Line 1", "LINPUT1" },
+ { "Left Line Mux", "Line 2", "LINPUT2" },
+ { "Left Line Mux", "PGA", "Left PGA Mux" },
+ { "Left Line Mux", "Differential", "Differential Mux" },
+
+ { "Right Line Mux", "Line 1", "RINPUT1" },
+ { "Right Line Mux", "Line 2", "RINPUT2" },
+ { "Right Line Mux", "PGA", "Right PGA Mux" },
+ { "Right Line Mux", "Differential", "Differential Mux" },
+
+ { "Left PGA Mux", "Line 1", "LINPUT1" },
+ { "Left PGA Mux", "Line 2", "LINPUT2" },
+ { "Left PGA Mux", "Differential", "Differential Mux" },
+
+ { "Right PGA Mux", "Line 1", "RINPUT1" },
+ { "Right PGA Mux", "Line 2", "RINPUT2" },
+ { "Right PGA Mux", "Differential", "Differential Mux" },
+
+ { "Differential Mux", "Line 1", "LINPUT1" },
+ { "Differential Mux", "Line 1", "RINPUT1" },
+ { "Differential Mux", "Line 2", "LINPUT2" },
+ { "Differential Mux", "Line 2", "RINPUT2" },
+
+ { "Left ADC Mux", "Stereo", "Left PGA Mux" },
+ { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" },
+ { "Left ADC Mux", "Digital Mono", "Left PGA Mux" },
+
+ { "Right ADC Mux", "Stereo", "Right PGA Mux" },
+ { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" },
+ { "Right ADC Mux", "Digital Mono", "Right PGA Mux" },
+
+ { "Left ADC", NULL, "Left ADC Mux" },
+ { "Right ADC", NULL, "Right ADC Mux" },
+
+ { "Left Line Mux", "Line 1", "LINPUT1" },
+ { "Left Line Mux", "Line 2", "LINPUT2" },
+ { "Left Line Mux", "PGA", "Left PGA Mux" },
+ { "Left Line Mux", "Differential", "Differential Mux" },
+
+ { "Right Line Mux", "Line 1", "RINPUT1" },
+ { "Right Line Mux", "Line 2", "RINPUT2" },
+ { "Right Line Mux", "PGA", "Right PGA Mux" },
+ { "Right Line Mux", "Differential", "Differential Mux" },
+
+ { "Left Mixer", "Playback Switch", "Left DAC" },
+ { "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
+ { "Left Mixer", "Right Playback Switch", "Right DAC" },
+ { "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
+
+ { "Right Mixer", "Left Playback Switch", "Left DAC" },
+ { "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
+ { "Right Mixer", "Playback Switch", "Right DAC" },
+ { "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
+
+ { "Left Out 1", NULL, "Left Mixer" },
+ { "LOUT1", NULL, "Left Out 1" },
+ { "Right Out 1", NULL, "Right Mixer" },
+ { "ROUT1", NULL, "Right Out 1" },
+
+ { "Left Out 2", NULL, "Left Mixer" },
+ { "LOUT2", NULL, "Left Out 2" },
+ { "Right Out 2", NULL, "Right Mixer" },
+ { "ROUT2", NULL, "Right Out 2" },
+};
+
+struct _coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr:5;
+ u8 usb:1;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ /* 8k */
+ {12288000, 8000, 1536, 0x6, 0x0},
+ {11289600, 8000, 1408, 0x16, 0x0},
+ {18432000, 8000, 2304, 0x7, 0x0},
+ {16934400, 8000, 2112, 0x17, 0x0},
+ {12000000, 8000, 1500, 0x6, 0x1},
+
+ /* 11.025k */
+ {11289600, 11025, 1024, 0x18, 0x0},
+ {16934400, 11025, 1536, 0x19, 0x0},
+ {12000000, 11025, 1088, 0x19, 0x1},
+
+ /* 16k */
+ {12288000, 16000, 768, 0xa, 0x0},
+ {18432000, 16000, 1152, 0xb, 0x0},
+ {12000000, 16000, 750, 0xa, 0x1},
+
+ /* 22.05k */
+ {11289600, 22050, 512, 0x1a, 0x0},
+ {16934400, 22050, 768, 0x1b, 0x0},
+ {12000000, 22050, 544, 0x1b, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 384, 0xc, 0x0},
+ {18432000, 32000, 576, 0xd, 0x0},
+ {12000000, 32000, 375, 0xa, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x10, 0x0},
+ {16934400, 44100, 384, 0x11, 0x0},
+ {12000000, 44100, 272, 0x11, 0x1},
+
+ /* 48k */
+ {12288000, 48000, 256, 0x0, 0x0},
+ {18432000, 48000, 384, 0x1, 0x0},
+ {12000000, 48000, 250, 0x0, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0x1e, 0x0},
+ {16934400, 88200, 192, 0x1f, 0x0},
+ {12000000, 88200, 136, 0x1f, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 128, 0xe, 0x0},
+ {18432000, 96000, 192, 0xf, 0x0},
+ {12000000, 96000, 125, 0xe, 0x1},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+/* The set of rates we can generate from the above for each SYSCLK */
+
+static const unsigned int rates_12288[] = {
+ 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_12288 = {
+ .count = ARRAY_SIZE(rates_12288),
+ .list = rates_12288,
+};
+
+static const unsigned int rates_112896[] = {
+ 8000, 11025, 22050, 44100,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_112896 = {
+ .count = ARRAY_SIZE(rates_112896),
+ .list = rates_112896,
+};
+
+static const unsigned int rates_12[] = {
+ 8000, 11025, 12000, 16000, 22050, 2400, 32000, 41100, 48000,
+ 48000, 88235, 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_12 = {
+ .count = ARRAY_SIZE(rates_12),
+ .list = rates_12,
+};
+
+/*
+ * Note that this should be called from init rather than from hw_params.
+ */
+static int wm8988_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
+
+ switch (freq) {
+ case 11289600:
+ case 18432000:
+ case 22579200:
+ case 36864000:
+ wm8988->sysclk_constraints = &constraints_112896;
+ wm8988->sysclk = freq;
+ return 0;
+
+ case 12288000:
+ case 16934400:
+ case 24576000:
+ case 33868800:
+ wm8988->sysclk_constraints = &constraints_12288;
+ wm8988->sysclk = freq;
+ return 0;
+
+ case 12000000:
+ case 24000000:
+ wm8988->sysclk_constraints = &constraints_12;
+ wm8988->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int wm8988_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface = 0x0040;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8988_IFACE, iface);
+ return 0;
+}
+
+static int wm8988_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
+
+ /* The set of sample rates that can be supported depends on the
+ * MCLK supplied to the CODEC - enforce this.
+ */
+ if (!wm8988->sysclk) {
+ dev_err(codec->dev,
+ "No MCLK configured, call set_sysclk() on init\n");
+ return -EINVAL;
+ }
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ wm8988->sysclk_constraints);
+
+ return 0;
+}
+
+static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
+ u16 iface = snd_soc_read(codec, WM8988_IFACE) & 0x1f3;
+ u16 srate = snd_soc_read(codec, WM8988_SRATE) & 0x180;
+ int coeff;
+
+ coeff = get_coeff(wm8988->sysclk, params_rate(params));
+ if (coeff < 0) {
+ coeff = get_coeff(wm8988->sysclk / 2, params_rate(params));
+ srate |= 0x40;
+ }
+ if (coeff < 0) {
+ dev_err(codec->dev,
+ "Unable to configure sample rate %dHz with %dHz MCLK\n",
+ params_rate(params), wm8988->sysclk);
+ return coeff;
+ }
+
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ iface |= 0x0004;
+ break;
+ case 24:
+ iface |= 0x0008;
+ break;
+ case 32:
+ iface |= 0x000c;
+ break;
+ }
+
+ /* set iface & srate */
+ snd_soc_write(codec, WM8988_IFACE, iface);
+ if (coeff >= 0)
+ snd_soc_write(codec, WM8988_SRATE, srate |
+ (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
+
+ return 0;
+}
+
+static int wm8988_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8988_ADCDAC) & 0xfff7;
+
+ if (mute)
+ snd_soc_write(codec, WM8988_ADCDAC, mute_reg | 0x8);
+ else
+ snd_soc_write(codec, WM8988_ADCDAC, mute_reg);
+ return 0;
+}
+
+static int wm8988_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
+ u16 pwr_reg = snd_soc_read(codec, WM8988_PWR1) & ~0x1c1;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VREF, VMID=2x50k, digital enabled */
+ snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x00c0);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ regcache_sync(wm8988->regmap);
+
+ /* VREF, VMID=2x5k */
+ snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x1c1);
+
+ /* Charge caps */
+ msleep(100);
+ }
+
+ /* VREF, VMID=2*500k, digital stopped */
+ snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x0141);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, WM8988_PWR1, 0x0000);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8988_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8988_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8988_ops = {
+ .startup = wm8988_pcm_startup,
+ .hw_params = wm8988_pcm_hw_params,
+ .set_fmt = wm8988_set_dai_fmt,
+ .set_sysclk = wm8988_set_dai_sysclk,
+ .digital_mute = wm8988_mute,
+};
+
+static struct snd_soc_dai_driver wm8988_dai = {
+ .name = "wm8988-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8988_RATES,
+ .formats = WM8988_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8988_RATES,
+ .formats = WM8988_FORMATS,
+ },
+ .ops = &wm8988_ops,
+ .symmetric_rates = 1,
+};
+
+static int wm8988_probe(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+
+ ret = wm8988_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ return ret;
+ }
+
+ /* set the update bits (we always update left then right) */
+ snd_soc_update_bits(codec, WM8988_RADC, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8988_RDAC, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8988_ROUT1V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8988_ROUT2V, 0x0100, 0x0100);
+ snd_soc_update_bits(codec, WM8988_RINVOL, 0x0100, 0x0100);
+
+ return 0;
+}
+
+static const struct snd_soc_codec_driver soc_codec_dev_wm8988 = {
+ .probe = wm8988_probe,
+ .set_bias_level = wm8988_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8988_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8988_snd_controls),
+ .dapm_widgets = wm8988_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8988_dapm_widgets),
+ .dapm_routes = wm8988_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8988_dapm_routes),
+};
+
+static const struct regmap_config wm8988_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = WM8988_LPPB,
+ .writeable_reg = wm8988_writeable,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm8988_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8988_reg_defaults),
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8988_spi_probe(struct spi_device *spi)
+{
+ struct wm8988_priv *wm8988;
+ int ret;
+
+ wm8988 = devm_kzalloc(&spi->dev, sizeof(struct wm8988_priv),
+ GFP_KERNEL);
+ if (wm8988 == NULL)
+ return -ENOMEM;
+
+ wm8988->regmap = devm_regmap_init_spi(spi, &wm8988_regmap);
+ if (IS_ERR(wm8988->regmap)) {
+ ret = PTR_ERR(wm8988->regmap);
+ dev_err(&spi->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ spi_set_drvdata(spi, wm8988);
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8988, &wm8988_dai, 1);
+ return ret;
+}
+
+static int wm8988_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver wm8988_spi_driver = {
+ .driver = {
+ .name = "wm8988",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8988_spi_probe,
+ .remove = wm8988_spi_remove,
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8988_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8988_priv *wm8988;
+ int ret;
+
+ wm8988 = devm_kzalloc(&i2c->dev, sizeof(struct wm8988_priv),
+ GFP_KERNEL);
+ if (wm8988 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm8988);
+
+ wm8988->regmap = devm_regmap_init_i2c(i2c, &wm8988_regmap);
+ if (IS_ERR(wm8988->regmap)) {
+ ret = PTR_ERR(wm8988->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8988, &wm8988_dai, 1);
+ return ret;
+}
+
+static int wm8988_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8988_i2c_id[] = {
+ { "wm8988", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id);
+
+static struct i2c_driver wm8988_i2c_driver = {
+ .driver = {
+ .name = "wm8988",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8988_i2c_probe,
+ .remove = wm8988_i2c_remove,
+ .id_table = wm8988_i2c_id,
+};
+#endif
+
+static int __init wm8988_modinit(void)
+{
+ int ret = 0;
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8988_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8988 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8988_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8988 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(wm8988_modinit);
+
+static void __exit wm8988_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8988_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8988_spi_driver);
+#endif
+}
+module_exit(wm8988_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM8988 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8988.h b/kernel/sound/soc/codecs/wm8988.h
new file mode 100644
index 000000000..5c04024e5
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8988.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * 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 _WM8988_H
+#define _WM8988_H
+
+/* WM8988 register space */
+
+#define WM8988_LINVOL 0x00
+#define WM8988_RINVOL 0x01
+#define WM8988_LOUT1V 0x02
+#define WM8988_ROUT1V 0x03
+#define WM8988_ADCDAC 0x05
+#define WM8988_IFACE 0x07
+#define WM8988_SRATE 0x08
+#define WM8988_LDAC 0x0a
+#define WM8988_RDAC 0x0b
+#define WM8988_BASS 0x0c
+#define WM8988_TREBLE 0x0d
+#define WM8988_RESET 0x0f
+#define WM8988_3D 0x10
+#define WM8988_ALC1 0x11
+#define WM8988_ALC2 0x12
+#define WM8988_ALC3 0x13
+#define WM8988_NGATE 0x14
+#define WM8988_LADC 0x15
+#define WM8988_RADC 0x16
+#define WM8988_ADCTL1 0x17
+#define WM8988_ADCTL2 0x18
+#define WM8988_PWR1 0x19
+#define WM8988_PWR2 0x1a
+#define WM8988_ADCTL3 0x1b
+#define WM8988_ADCIN 0x1f
+#define WM8988_LADCIN 0x20
+#define WM8988_RADCIN 0x21
+#define WM8988_LOUTM1 0x22
+#define WM8988_LOUTM2 0x23
+#define WM8988_ROUTM1 0x24
+#define WM8988_ROUTM2 0x25
+#define WM8988_LOUT2V 0x28
+#define WM8988_ROUT2V 0x29
+#define WM8988_LPPB 0x43
+#define WM8988_NUM_REG 0x44
+
+#define WM8988_SYSCLK 0
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8990.c b/kernel/sound/soc/codecs/wm8990.c
new file mode 100644
index 000000000..c93bffcb3
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8990.c
@@ -0,0 +1,1371 @@
+/*
+ * wm8990.c -- WM8990 ALSA Soc Audio driver
+ *
+ * Copyright 2008 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.
+ */
+
+#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 <asm/div64.h>
+
+#include "wm8990.h"
+
+/* codec private data */
+struct wm8990_priv {
+ struct regmap *regmap;
+ unsigned int sysclk;
+ unsigned int pcmclk;
+};
+
+static bool wm8990_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8990_RESET:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static const struct reg_default wm8990_reg_defaults[] = {
+ { 1, 0x0000 }, /* R1 - Power Management (1) */
+ { 2, 0x6000 }, /* R2 - Power Management (2) */
+ { 3, 0x0000 }, /* R3 - Power Management (3) */
+ { 4, 0x4050 }, /* R4 - Audio Interface (1) */
+ { 5, 0x4000 }, /* R5 - Audio Interface (2) */
+ { 6, 0x01C8 }, /* R6 - Clocking (1) */
+ { 7, 0x0000 }, /* R7 - Clocking (2) */
+ { 8, 0x0040 }, /* R8 - Audio Interface (3) */
+ { 9, 0x0040 }, /* R9 - Audio Interface (4) */
+ { 10, 0x0004 }, /* R10 - DAC CTRL */
+ { 11, 0x00C0 }, /* R11 - Left DAC Digital Volume */
+ { 12, 0x00C0 }, /* R12 - Right DAC Digital Volume */
+ { 13, 0x0000 }, /* R13 - Digital Side Tone */
+ { 14, 0x0100 }, /* R14 - ADC CTRL */
+ { 15, 0x00C0 }, /* R15 - Left ADC Digital Volume */
+ { 16, 0x00C0 }, /* R16 - Right ADC Digital Volume */
+
+ { 18, 0x0000 }, /* R18 - GPIO CTRL 1 */
+ { 19, 0x1000 }, /* R19 - GPIO1 & GPIO2 */
+ { 20, 0x1010 }, /* R20 - GPIO3 & GPIO4 */
+ { 21, 0x1010 }, /* R21 - GPIO5 & GPIO6 */
+ { 22, 0x8000 }, /* R22 - GPIOCTRL 2 */
+ { 23, 0x0800 }, /* R23 - GPIO_POL */
+ { 24, 0x008B }, /* R24 - Left Line Input 1&2 Volume */
+ { 25, 0x008B }, /* R25 - Left Line Input 3&4 Volume */
+ { 26, 0x008B }, /* R26 - Right Line Input 1&2 Volume */
+ { 27, 0x008B }, /* R27 - Right Line Input 3&4 Volume */
+ { 28, 0x0000 }, /* R28 - Left Output Volume */
+ { 29, 0x0000 }, /* R29 - Right Output Volume */
+ { 30, 0x0066 }, /* R30 - Line Outputs Volume */
+ { 31, 0x0022 }, /* R31 - Out3/4 Volume */
+ { 32, 0x0079 }, /* R32 - Left OPGA Volume */
+ { 33, 0x0079 }, /* R33 - Right OPGA Volume */
+ { 34, 0x0003 }, /* R34 - Speaker Volume */
+ { 35, 0x0003 }, /* R35 - ClassD1 */
+
+ { 37, 0x0100 }, /* R37 - ClassD3 */
+ { 38, 0x0079 }, /* R38 - ClassD4 */
+ { 39, 0x0000 }, /* R39 - Input Mixer1 */
+ { 40, 0x0000 }, /* R40 - Input Mixer2 */
+ { 41, 0x0000 }, /* R41 - Input Mixer3 */
+ { 42, 0x0000 }, /* R42 - Input Mixer4 */
+ { 43, 0x0000 }, /* R43 - Input Mixer5 */
+ { 44, 0x0000 }, /* R44 - Input Mixer6 */
+ { 45, 0x0000 }, /* R45 - Output Mixer1 */
+ { 46, 0x0000 }, /* R46 - Output Mixer2 */
+ { 47, 0x0000 }, /* R47 - Output Mixer3 */
+ { 48, 0x0000 }, /* R48 - Output Mixer4 */
+ { 49, 0x0000 }, /* R49 - Output Mixer5 */
+ { 50, 0x0000 }, /* R50 - Output Mixer6 */
+ { 51, 0x0180 }, /* R51 - Out3/4 Mixer */
+ { 52, 0x0000 }, /* R52 - Line Mixer1 */
+ { 53, 0x0000 }, /* R53 - Line Mixer2 */
+ { 54, 0x0000 }, /* R54 - Speaker Mixer */
+ { 55, 0x0000 }, /* R55 - Additional Control */
+ { 56, 0x0000 }, /* R56 - AntiPOP1 */
+ { 57, 0x0000 }, /* R57 - AntiPOP2 */
+ { 58, 0x0000 }, /* R58 - MICBIAS */
+
+ { 60, 0x0008 }, /* R60 - PLL1 */
+ { 61, 0x0031 }, /* R61 - PLL2 */
+ { 62, 0x0026 }, /* R62 - PLL3 */
+};
+
+#define wm8990_reset(c) snd_soc_write(c, WM8990_RESET, 0)
+
+static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 600, 0);
+
+static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1650, 3000, 0);
+
+static const DECLARE_TLV_DB_SCALE(out_mix_tlv, 0, -2100, 0);
+
+static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -7300, 600, 0);
+
+static const DECLARE_TLV_DB_SCALE(out_omix_tlv, -600, 0, 0);
+
+static const DECLARE_TLV_DB_SCALE(out_dac_tlv, -7163, 0, 0);
+
+static const DECLARE_TLV_DB_SCALE(in_adc_tlv, -7163, 1763, 0);
+
+static const DECLARE_TLV_DB_SCALE(out_sidetone_tlv, -3600, 0, 0);
+
+static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int reg = mc->reg;
+ int ret;
+ u16 val;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /* now hit the volume update bits (always bit 8) */
+ val = snd_soc_read(codec, reg);
+ return snd_soc_write(codec, reg, val | 0x0100);
+}
+
+#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
+ tlv_array) \
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm899x_outpga_put_volsw_vu, tlv_array)
+
+
+static const char *wm8990_digital_sidetone[] =
+ {"None", "Left ADC", "Right ADC", "Reserved"};
+
+static SOC_ENUM_SINGLE_DECL(wm8990_left_digital_sidetone_enum,
+ WM8990_DIGITAL_SIDE_TONE,
+ WM8990_ADC_TO_DACL_SHIFT,
+ wm8990_digital_sidetone);
+
+static SOC_ENUM_SINGLE_DECL(wm8990_right_digital_sidetone_enum,
+ WM8990_DIGITAL_SIDE_TONE,
+ WM8990_ADC_TO_DACR_SHIFT,
+ wm8990_digital_sidetone);
+
+static const char *wm8990_adcmode[] =
+ {"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
+
+static SOC_ENUM_SINGLE_DECL(wm8990_right_adcmode_enum,
+ WM8990_ADC_CTRL,
+ WM8990_ADC_HPF_CUT_SHIFT,
+ wm8990_adcmode);
+
+static const struct snd_kcontrol_new wm8990_snd_controls[] = {
+/* INMIXL */
+SOC_SINGLE("LIN12 PGA Boost", WM8990_INPUT_MIXER3, WM8990_L12MNBST_BIT, 1, 0),
+SOC_SINGLE("LIN34 PGA Boost", WM8990_INPUT_MIXER3, WM8990_L34MNBST_BIT, 1, 0),
+/* INMIXR */
+SOC_SINGLE("RIN12 PGA Boost", WM8990_INPUT_MIXER3, WM8990_R12MNBST_BIT, 1, 0),
+SOC_SINGLE("RIN34 PGA Boost", WM8990_INPUT_MIXER3, WM8990_R34MNBST_BIT, 1, 0),
+
+/* LOMIX */
+SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8990_OUTPUT_MIXER3,
+ WM8990_LLI3LOVOL_SHIFT, WM8990_LLI3LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER3,
+ WM8990_LR12LOVOL_SHIFT, WM8990_LR12LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER3,
+ WM8990_LL12LOVOL_SHIFT, WM8990_LL12LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8990_OUTPUT_MIXER5,
+ WM8990_LRI3LOVOL_SHIFT, WM8990_LRI3LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8990_OUTPUT_MIXER5,
+ WM8990_LRBLOVOL_SHIFT, WM8990_LRBLOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8990_OUTPUT_MIXER5,
+ WM8990_LRBLOVOL_SHIFT, WM8990_LRBLOVOL_MASK, 1, out_mix_tlv),
+
+/* ROMIX */
+SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8990_OUTPUT_MIXER4,
+ WM8990_RRI3ROVOL_SHIFT, WM8990_RRI3ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER4,
+ WM8990_RL12ROVOL_SHIFT, WM8990_RL12ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER4,
+ WM8990_RR12ROVOL_SHIFT, WM8990_RR12ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8990_OUTPUT_MIXER6,
+ WM8990_RLI3ROVOL_SHIFT, WM8990_RLI3ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8990_OUTPUT_MIXER6,
+ WM8990_RLBROVOL_SHIFT, WM8990_RLBROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8990_OUTPUT_MIXER6,
+ WM8990_RRBROVOL_SHIFT, WM8990_RRBROVOL_MASK, 1, out_mix_tlv),
+
+/* LOUT */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8990_LEFT_OUTPUT_VOLUME,
+ WM8990_LOUTVOL_SHIFT, WM8990_LOUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOUT ZC", WM8990_LEFT_OUTPUT_VOLUME, WM8990_LOZC_BIT, 1, 0),
+
+/* ROUT */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8990_RIGHT_OUTPUT_VOLUME,
+ WM8990_ROUTVOL_SHIFT, WM8990_ROUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROUT ZC", WM8990_RIGHT_OUTPUT_VOLUME, WM8990_ROZC_BIT, 1, 0),
+
+/* LOPGA */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8990_LEFT_OPGA_VOLUME,
+ WM8990_LOPGAVOL_SHIFT, WM8990_LOPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOPGA ZC Switch", WM8990_LEFT_OPGA_VOLUME,
+ WM8990_LOPGAZC_BIT, 1, 0),
+
+/* ROPGA */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8990_RIGHT_OPGA_VOLUME,
+ WM8990_ROPGAVOL_SHIFT, WM8990_ROPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROPGA ZC Switch", WM8990_RIGHT_OPGA_VOLUME,
+ WM8990_ROPGAZC_BIT, 1, 0),
+
+SOC_SINGLE("LON Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_LONMUTE_BIT, 1, 0),
+SOC_SINGLE("LOP Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_LOPMUTE_BIT, 1, 0),
+SOC_SINGLE("LOP Attenuation Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_LOATTN_BIT, 1, 0),
+SOC_SINGLE("RON Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_RONMUTE_BIT, 1, 0),
+SOC_SINGLE("ROP Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_ROPMUTE_BIT, 1, 0),
+SOC_SINGLE("ROP Attenuation Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_ROATTN_BIT, 1, 0),
+
+SOC_SINGLE("OUT3 Mute Switch", WM8990_OUT3_4_VOLUME,
+ WM8990_OUT3MUTE_BIT, 1, 0),
+SOC_SINGLE("OUT3 Attenuation Switch", WM8990_OUT3_4_VOLUME,
+ WM8990_OUT3ATTN_BIT, 1, 0),
+
+SOC_SINGLE("OUT4 Mute Switch", WM8990_OUT3_4_VOLUME,
+ WM8990_OUT4MUTE_BIT, 1, 0),
+SOC_SINGLE("OUT4 Attenuation Switch", WM8990_OUT3_4_VOLUME,
+ WM8990_OUT4ATTN_BIT, 1, 0),
+
+SOC_SINGLE("Speaker Mode Switch", WM8990_CLASSD1,
+ WM8990_CDMODE_BIT, 1, 0),
+
+SOC_SINGLE("Speaker Output Attenuation Volume", WM8990_SPEAKER_VOLUME,
+ WM8990_SPKATTN_SHIFT, WM8990_SPKATTN_MASK, 0),
+SOC_SINGLE("Speaker DC Boost Volume", WM8990_CLASSD3,
+ WM8990_DCGAIN_SHIFT, WM8990_DCGAIN_MASK, 0),
+SOC_SINGLE("Speaker AC Boost Volume", WM8990_CLASSD3,
+ WM8990_ACGAIN_SHIFT, WM8990_ACGAIN_MASK, 0),
+SOC_SINGLE_TLV("Speaker Volume", WM8990_CLASSD4,
+ WM8990_SPKVOL_SHIFT, WM8990_SPKVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("Speaker ZC Switch", WM8990_CLASSD4,
+ WM8990_SPKZC_SHIFT, WM8990_SPKZC_MASK, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
+ WM8990_LEFT_DAC_DIGITAL_VOLUME,
+ WM8990_DACL_VOL_SHIFT,
+ WM8990_DACL_VOL_MASK,
+ 0,
+ out_dac_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
+ WM8990_RIGHT_DAC_DIGITAL_VOLUME,
+ WM8990_DACR_VOL_SHIFT,
+ WM8990_DACR_VOL_MASK,
+ 0,
+ out_dac_tlv),
+
+SOC_ENUM("Left Digital Sidetone", wm8990_left_digital_sidetone_enum),
+SOC_ENUM("Right Digital Sidetone", wm8990_right_digital_sidetone_enum),
+
+SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8990_DIGITAL_SIDE_TONE,
+ WM8990_ADCL_DAC_SVOL_SHIFT, WM8990_ADCL_DAC_SVOL_MASK, 0,
+ out_sidetone_tlv),
+SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8990_DIGITAL_SIDE_TONE,
+ WM8990_ADCR_DAC_SVOL_SHIFT, WM8990_ADCR_DAC_SVOL_MASK, 0,
+ out_sidetone_tlv),
+
+SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8990_ADC_CTRL,
+ WM8990_ADC_HPF_ENA_BIT, 1, 0),
+
+SOC_ENUM("ADC HPF Mode", wm8990_right_adcmode_enum),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
+ WM8990_LEFT_ADC_DIGITAL_VOLUME,
+ WM8990_ADCL_VOL_SHIFT,
+ WM8990_ADCL_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
+ WM8990_RIGHT_ADC_DIGITAL_VOLUME,
+ WM8990_ADCR_VOL_SHIFT,
+ WM8990_ADCR_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
+ WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8990_LIN12VOL_SHIFT,
+ WM8990_LIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("LIN12 ZC Switch", WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8990_LI12ZC_BIT, 1, 0),
+
+SOC_SINGLE("LIN12 Mute Switch", WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8990_LI12MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
+ WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8990_LIN34VOL_SHIFT,
+ WM8990_LIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("LIN34 ZC Switch", WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8990_LI34ZC_BIT, 1, 0),
+
+SOC_SINGLE("LIN34 Mute Switch", WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8990_LI34MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
+ WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8990_RIN12VOL_SHIFT,
+ WM8990_RIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("RIN12 ZC Switch", WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8990_RI12ZC_BIT, 1, 0),
+
+SOC_SINGLE("RIN12 Mute Switch", WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8990_RI12MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
+ WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8990_RIN34VOL_SHIFT,
+ WM8990_RIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("RIN34 ZC Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8990_RI34ZC_BIT, 1, 0),
+
+SOC_SINGLE("RIN34 Mute Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8990_RI34MUTE_BIT, 1, 0),
+
+};
+
+/*
+ * _DAPM_ Controls
+ */
+
+static int outmixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u32 reg_shift = kcontrol->private_value & 0xfff;
+ int ret = 0;
+ u16 reg;
+
+ switch (reg_shift) {
+ case WM8990_SPEAKER_MIXER | (WM8990_LDSPK_BIT << 8) :
+ reg = snd_soc_read(codec, WM8990_OUTPUT_MIXER1);
+ if (reg & WM8990_LDLO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 1 LDLO Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8990_SPEAKER_MIXER | (WM8990_RDSPK_BIT << 8):
+ reg = snd_soc_read(codec, WM8990_OUTPUT_MIXER2);
+ if (reg & WM8990_RDRO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 2 RDRO Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8990_OUTPUT_MIXER1 | (WM8990_LDLO_BIT << 8):
+ reg = snd_soc_read(codec, WM8990_SPEAKER_MIXER);
+ if (reg & WM8990_LDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer LDSPK Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8990_OUTPUT_MIXER2 | (WM8990_RDRO_BIT << 8):
+ reg = snd_soc_read(codec, WM8990_SPEAKER_MIXER);
+ if (reg & WM8990_RDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer RDSPK Set\n");
+ ret = -1;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+/* INMIX dB values */
+static const unsigned int in_mix_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 7, TLV_DB_SCALE_ITEM(-1200, 600, 0),
+};
+
+/* Left In PGA Connections */
+static const struct snd_kcontrol_new wm8990_dapm_lin12_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN1 Switch", WM8990_INPUT_MIXER2, WM8990_LMN1_BIT, 1, 0),
+SOC_DAPM_SINGLE("LIN2 Switch", WM8990_INPUT_MIXER2, WM8990_LMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8990_dapm_lin34_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN3 Switch", WM8990_INPUT_MIXER2, WM8990_LMN3_BIT, 1, 0),
+SOC_DAPM_SINGLE("LIN4 Switch", WM8990_INPUT_MIXER2, WM8990_LMP4_BIT, 1, 0),
+};
+
+/* Right In PGA Connections */
+static const struct snd_kcontrol_new wm8990_dapm_rin12_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN1 Switch", WM8990_INPUT_MIXER2, WM8990_RMN1_BIT, 1, 0),
+SOC_DAPM_SINGLE("RIN2 Switch", WM8990_INPUT_MIXER2, WM8990_RMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8990_dapm_rin34_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN3 Switch", WM8990_INPUT_MIXER2, WM8990_RMN3_BIT, 1, 0),
+SOC_DAPM_SINGLE("RIN4 Switch", WM8990_INPUT_MIXER2, WM8990_RMP4_BIT, 1, 0),
+};
+
+/* INMIXL */
+static const struct snd_kcontrol_new wm8990_dapm_inmixl_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8990_INPUT_MIXER3,
+ WM8990_LDBVOL_SHIFT, WM8990_LDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8990_INPUT_MIXER5, WM8990_LI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("LINPGA12 Switch", WM8990_INPUT_MIXER3, WM8990_L12MNB_BIT,
+ 1, 0),
+SOC_DAPM_SINGLE("LINPGA34 Switch", WM8990_INPUT_MIXER3, WM8990_L34MNB_BIT,
+ 1, 0),
+};
+
+/* INMIXR */
+static const struct snd_kcontrol_new wm8990_dapm_inmixr_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8990_INPUT_MIXER4,
+ WM8990_RDBVOL_SHIFT, WM8990_RDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8990_INPUT_MIXER6, WM8990_RI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("RINPGA12 Switch", WM8990_INPUT_MIXER3, WM8990_L12MNB_BIT,
+ 1, 0),
+SOC_DAPM_SINGLE("RINPGA34 Switch", WM8990_INPUT_MIXER3, WM8990_L34MNB_BIT,
+ 1, 0),
+};
+
+/* AINLMUX */
+static const char *wm8990_ainlmux[] =
+ {"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
+
+static SOC_ENUM_SINGLE_DECL(wm8990_ainlmux_enum,
+ WM8990_INPUT_MIXER1, WM8990_AINLMODE_SHIFT,
+ wm8990_ainlmux);
+
+static const struct snd_kcontrol_new wm8990_dapm_ainlmux_controls =
+SOC_DAPM_ENUM("Route", wm8990_ainlmux_enum);
+
+/* DIFFINL */
+
+/* AINRMUX */
+static const char *wm8990_ainrmux[] =
+ {"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
+
+static SOC_ENUM_SINGLE_DECL(wm8990_ainrmux_enum,
+ WM8990_INPUT_MIXER1, WM8990_AINRMODE_SHIFT,
+ wm8990_ainrmux);
+
+static const struct snd_kcontrol_new wm8990_dapm_ainrmux_controls =
+SOC_DAPM_ENUM("Route", wm8990_ainrmux_enum);
+
+/* RXVOICE */
+static const struct snd_kcontrol_new wm8990_dapm_rxvoice_controls[] = {
+SOC_DAPM_SINGLE_TLV("LIN4/RXN", WM8990_INPUT_MIXER5, WM8990_LR4BVOL_SHIFT,
+ WM8990_LR4BVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN4/RXP", WM8990_INPUT_MIXER6, WM8990_RL4BVOL_SHIFT,
+ WM8990_RL4BVOL_MASK, 0, in_mix_tlv),
+};
+
+/* LOMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lomix_controls[] = {
+SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LRBLO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LLBLO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LRI3LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LLI3LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LR12LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LL12LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LDLO_BIT, 1, 0),
+};
+
+/* ROMIX */
+static const struct snd_kcontrol_new wm8990_dapm_romix_controls[] = {
+SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RLBRO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RRBRO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RLI3RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RRI3RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RL12RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RR12RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RDRO_BIT, 1, 0),
+};
+
+/* LONMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lonmix_controls[] = {
+SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8990_LINE_MIXER1,
+ WM8990_LLOPGALON_BIT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8990_LINE_MIXER1,
+ WM8990_LROPGALON_BIT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8990_LINE_MIXER1,
+ WM8990_LOPLON_BIT, 1, 0),
+};
+
+/* LOPMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lopmix_controls[] = {
+SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8990_LINE_MIXER1,
+ WM8990_LR12LOP_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8990_LINE_MIXER1,
+ WM8990_LL12LOP_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8990_LINE_MIXER1,
+ WM8990_LLOPGALOP_BIT, 1, 0),
+};
+
+/* RONMIX */
+static const struct snd_kcontrol_new wm8990_dapm_ronmix_controls[] = {
+SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8990_LINE_MIXER2,
+ WM8990_RROPGARON_BIT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8990_LINE_MIXER2,
+ WM8990_RLOPGARON_BIT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8990_LINE_MIXER2,
+ WM8990_ROPRON_BIT, 1, 0),
+};
+
+/* ROPMIX */
+static const struct snd_kcontrol_new wm8990_dapm_ropmix_controls[] = {
+SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8990_LINE_MIXER2,
+ WM8990_RL12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8990_LINE_MIXER2,
+ WM8990_RR12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8990_LINE_MIXER2,
+ WM8990_RROPGAROP_BIT, 1, 0),
+};
+
+/* OUT3MIX */
+static const struct snd_kcontrol_new wm8990_dapm_out3mix_controls[] = {
+SOC_DAPM_SINGLE("OUT3MIX LIN4/RXP Bypass Switch", WM8990_OUT3_4_MIXER,
+ WM8990_LI4O3_BIT, 1, 0),
+SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8990_OUT3_4_MIXER,
+ WM8990_LPGAO3_BIT, 1, 0),
+};
+
+/* OUT4MIX */
+static const struct snd_kcontrol_new wm8990_dapm_out4mix_controls[] = {
+SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8990_OUT3_4_MIXER,
+ WM8990_RPGAO4_BIT, 1, 0),
+SOC_DAPM_SINGLE("OUT4MIX RIN4/RXP Bypass Switch", WM8990_OUT3_4_MIXER,
+ WM8990_RI4O4_BIT, 1, 0),
+};
+
+/* SPKMIX */
+static const struct snd_kcontrol_new wm8990_dapm_spkmix_controls[] = {
+SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8990_SPEAKER_MIXER,
+ WM8990_LI2SPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8990_SPEAKER_MIXER,
+ WM8990_LB2SPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8990_SPEAKER_MIXER,
+ WM8990_LOPGASPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8990_SPEAKER_MIXER,
+ WM8990_LDSPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8990_SPEAKER_MIXER,
+ WM8990_RDSPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8990_SPEAKER_MIXER,
+ WM8990_ROPGASPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8990_SPEAKER_MIXER,
+ WM8990_RL12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8990_SPEAKER_MIXER,
+ WM8990_RI2SPK_BIT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8990_dapm_widgets[] = {
+/* Input Side */
+/* Input Lines */
+SND_SOC_DAPM_INPUT("LIN1"),
+SND_SOC_DAPM_INPUT("LIN2"),
+SND_SOC_DAPM_INPUT("LIN3"),
+SND_SOC_DAPM_INPUT("LIN4/RXN"),
+SND_SOC_DAPM_INPUT("RIN3"),
+SND_SOC_DAPM_INPUT("RIN4/RXP"),
+SND_SOC_DAPM_INPUT("RIN1"),
+SND_SOC_DAPM_INPUT("RIN2"),
+SND_SOC_DAPM_INPUT("Internal ADC Source"),
+
+SND_SOC_DAPM_SUPPLY("INL", WM8990_POWER_MANAGEMENT_2, WM8990_AINL_ENA_BIT, 0,
+ NULL, 0),
+SND_SOC_DAPM_SUPPLY("INR", WM8990_POWER_MANAGEMENT_2, WM8990_AINR_ENA_BIT, 0,
+ NULL, 0),
+
+/* DACs */
+SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8990_POWER_MANAGEMENT_2,
+ WM8990_ADCL_ENA_BIT, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8990_POWER_MANAGEMENT_2,
+ WM8990_ADCR_ENA_BIT, 0),
+
+/* Input PGAs */
+SND_SOC_DAPM_MIXER("LIN12 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_LIN12_ENA_BIT,
+ 0, &wm8990_dapm_lin12_pga_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lin12_pga_controls)),
+SND_SOC_DAPM_MIXER("LIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_LIN34_ENA_BIT,
+ 0, &wm8990_dapm_lin34_pga_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lin34_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN12 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN12_ENA_BIT,
+ 0, &wm8990_dapm_rin12_pga_controls[0],
+ ARRAY_SIZE(wm8990_dapm_rin12_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN34_ENA_BIT,
+ 0, &wm8990_dapm_rin34_pga_controls[0],
+ ARRAY_SIZE(wm8990_dapm_rin34_pga_controls)),
+
+/* INMIXL */
+SND_SOC_DAPM_MIXER("INMIXL", SND_SOC_NOPM, 0, 0,
+ &wm8990_dapm_inmixl_controls[0],
+ ARRAY_SIZE(wm8990_dapm_inmixl_controls)),
+
+/* AINLMUX */
+SND_SOC_DAPM_MUX("AINLMUX", SND_SOC_NOPM, 0, 0, &wm8990_dapm_ainlmux_controls),
+
+/* INMIXR */
+SND_SOC_DAPM_MIXER("INMIXR", SND_SOC_NOPM, 0, 0,
+ &wm8990_dapm_inmixr_controls[0],
+ ARRAY_SIZE(wm8990_dapm_inmixr_controls)),
+
+/* AINRMUX */
+SND_SOC_DAPM_MUX("AINRMUX", SND_SOC_NOPM, 0, 0, &wm8990_dapm_ainrmux_controls),
+
+/* Output Side */
+/* DACs */
+SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8990_POWER_MANAGEMENT_3,
+ WM8990_DACL_ENA_BIT, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8990_POWER_MANAGEMENT_3,
+ WM8990_DACR_ENA_BIT, 0),
+
+/* LOMIX */
+SND_SOC_DAPM_MIXER_E("LOMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LOMIX_ENA_BIT,
+ 0, &wm8990_dapm_lomix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lomix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LONMIX */
+SND_SOC_DAPM_MIXER("LONMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LON_ENA_BIT, 0,
+ &wm8990_dapm_lonmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lonmix_controls)),
+
+/* LOPMIX */
+SND_SOC_DAPM_MIXER("LOPMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LOP_ENA_BIT, 0,
+ &wm8990_dapm_lopmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lopmix_controls)),
+
+/* OUT3MIX */
+SND_SOC_DAPM_MIXER("OUT3MIX", WM8990_POWER_MANAGEMENT_1, WM8990_OUT3_ENA_BIT, 0,
+ &wm8990_dapm_out3mix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_out3mix_controls)),
+
+/* SPKMIX */
+SND_SOC_DAPM_MIXER_E("SPKMIX", WM8990_POWER_MANAGEMENT_1, WM8990_SPK_ENA_BIT, 0,
+ &wm8990_dapm_spkmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_spkmix_controls), outmixer_event,
+ SND_SOC_DAPM_PRE_REG),
+
+/* OUT4MIX */
+SND_SOC_DAPM_MIXER("OUT4MIX", WM8990_POWER_MANAGEMENT_1, WM8990_OUT4_ENA_BIT, 0,
+ &wm8990_dapm_out4mix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_out4mix_controls)),
+
+/* ROPMIX */
+SND_SOC_DAPM_MIXER("ROPMIX", WM8990_POWER_MANAGEMENT_3, WM8990_ROP_ENA_BIT, 0,
+ &wm8990_dapm_ropmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_ropmix_controls)),
+
+/* RONMIX */
+SND_SOC_DAPM_MIXER("RONMIX", WM8990_POWER_MANAGEMENT_3, WM8990_RON_ENA_BIT, 0,
+ &wm8990_dapm_ronmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_ronmix_controls)),
+
+/* ROMIX */
+SND_SOC_DAPM_MIXER_E("ROMIX", WM8990_POWER_MANAGEMENT_3, WM8990_ROMIX_ENA_BIT,
+ 0, &wm8990_dapm_romix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_romix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LOUT PGA */
+SND_SOC_DAPM_PGA("LOUT PGA", WM8990_POWER_MANAGEMENT_1, WM8990_LOUT_ENA_BIT, 0,
+ NULL, 0),
+
+/* ROUT PGA */
+SND_SOC_DAPM_PGA("ROUT PGA", WM8990_POWER_MANAGEMENT_1, WM8990_ROUT_ENA_BIT, 0,
+ NULL, 0),
+
+/* LOPGA */
+SND_SOC_DAPM_PGA("LOPGA", WM8990_POWER_MANAGEMENT_3, WM8990_LOPGA_ENA_BIT, 0,
+ NULL, 0),
+
+/* ROPGA */
+SND_SOC_DAPM_PGA("ROPGA", WM8990_POWER_MANAGEMENT_3, WM8990_ROPGA_ENA_BIT, 0,
+ NULL, 0),
+
+/* MICBIAS */
+SND_SOC_DAPM_SUPPLY("MICBIAS", WM8990_POWER_MANAGEMENT_1,
+ WM8990_MICBIAS_ENA_BIT, 0, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("LON"),
+SND_SOC_DAPM_OUTPUT("LOP"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("SPKN"),
+SND_SOC_DAPM_OUTPUT("SPKP"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("OUT4"),
+SND_SOC_DAPM_OUTPUT("ROP"),
+SND_SOC_DAPM_OUTPUT("RON"),
+
+SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
+};
+
+static const struct snd_soc_dapm_route wm8990_dapm_routes[] = {
+ /* Make DACs turn on when playing even if not mixed into any outputs */
+ {"Internal DAC Sink", NULL, "Left DAC"},
+ {"Internal DAC Sink", NULL, "Right DAC"},
+
+ /* Make ADCs turn on when recording even if not mixed from any inputs */
+ {"Left ADC", NULL, "Internal ADC Source"},
+ {"Right ADC", NULL, "Internal ADC Source"},
+
+ {"AINLMUX", NULL, "INL"},
+ {"INMIXL", NULL, "INL"},
+ {"AINRMUX", NULL, "INR"},
+ {"INMIXR", NULL, "INR"},
+
+ /* Input Side */
+ /* LIN12 PGA */
+ {"LIN12 PGA", "LIN1 Switch", "LIN1"},
+ {"LIN12 PGA", "LIN2 Switch", "LIN2"},
+ /* LIN34 PGA */
+ {"LIN34 PGA", "LIN3 Switch", "LIN3"},
+ {"LIN34 PGA", "LIN4 Switch", "LIN4/RXN"},
+ /* INMIXL */
+ {"INMIXL", "Record Left Volume", "LOMIX"},
+ {"INMIXL", "LIN2 Volume", "LIN2"},
+ {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"},
+ {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"},
+ /* AINLMUX */
+ {"AINLMUX", "INMIXL Mix", "INMIXL"},
+ {"AINLMUX", "DIFFINL Mix", "LIN12 PGA"},
+ {"AINLMUX", "DIFFINL Mix", "LIN34 PGA"},
+ {"AINLMUX", "RXVOICE Mix", "LIN4/RXN"},
+ {"AINLMUX", "RXVOICE Mix", "RIN4/RXP"},
+ /* ADC */
+ {"Left ADC", NULL, "AINLMUX"},
+
+ /* RIN12 PGA */
+ {"RIN12 PGA", "RIN1 Switch", "RIN1"},
+ {"RIN12 PGA", "RIN2 Switch", "RIN2"},
+ /* RIN34 PGA */
+ {"RIN34 PGA", "RIN3 Switch", "RIN3"},
+ {"RIN34 PGA", "RIN4 Switch", "RIN4/RXP"},
+ /* INMIXL */
+ {"INMIXR", "Record Right Volume", "ROMIX"},
+ {"INMIXR", "RIN2 Volume", "RIN2"},
+ {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"},
+ {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"},
+ /* AINRMUX */
+ {"AINRMUX", "INMIXR Mix", "INMIXR"},
+ {"AINRMUX", "DIFFINR Mix", "RIN12 PGA"},
+ {"AINRMUX", "DIFFINR Mix", "RIN34 PGA"},
+ {"AINRMUX", "RXVOICE Mix", "LIN4/RXN"},
+ {"AINRMUX", "RXVOICE Mix", "RIN4/RXP"},
+ /* ADC */
+ {"Right ADC", NULL, "AINRMUX"},
+
+ /* LOMIX */
+ {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"},
+ {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"},
+ {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"},
+ {"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"},
+ {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
+
+ /* ROMIX */
+ {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"},
+ {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"},
+ {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"},
+ {"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"},
+ {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"},
+
+ /* SPKMIX */
+ {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"},
+ {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"},
+ {"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"},
+ {"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"},
+ {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"},
+ {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"},
+ {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"},
+ {"SPKMIX", "SPKMIX Left DAC Switch", "Left DAC"},
+
+ /* LONMIX */
+ {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"},
+
+ /* LOPMIX */
+ {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"},
+
+ /* OUT3MIX */
+ {"OUT3MIX", "OUT3MIX LIN4/RXP Bypass Switch", "LIN4/RXN"},
+ {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"},
+
+ /* OUT4MIX */
+ {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"},
+ {"OUT4MIX", "OUT4MIX RIN4/RXP Bypass Switch", "RIN4/RXP"},
+
+ /* RONMIX */
+ {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"},
+
+ /* ROPMIX */
+ {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"},
+
+ /* Out Mixer PGAs */
+ {"LOPGA", NULL, "LOMIX"},
+ {"ROPGA", NULL, "ROMIX"},
+
+ {"LOUT PGA", NULL, "LOMIX"},
+ {"ROUT PGA", NULL, "ROMIX"},
+
+ /* Output Pins */
+ {"LON", NULL, "LONMIX"},
+ {"LOP", NULL, "LOPMIX"},
+ {"OUT3", NULL, "OUT3MIX"},
+ {"LOUT", NULL, "LOUT PGA"},
+ {"SPKN", NULL, "SPKMIX"},
+ {"ROUT", NULL, "ROUT PGA"},
+ {"OUT4", NULL, "OUT4MIX"},
+ {"ROP", NULL, "ROPMIX"},
+ {"RON", NULL, "RONMIX"},
+};
+
+/* PLL divisors */
+struct _pll_div {
+ u32 div2;
+ u32 n;
+ u32 k;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 16) * 10)
+
+static void pll_factors(struct _pll_div *pll_div, unsigned int target,
+ unsigned int source)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div->div2 = 1;
+ Ndiv = target / source;
+ } else
+ pll_div->div2 = 0;
+
+ if ((Ndiv < 6) || (Ndiv > 12))
+ printk(KERN_WARNING
+ "WM8990 N value outwith recommended range! N = %u\n", Ndiv);
+
+ pll_div->n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div->k = K;
+}
+
+static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct _pll_div pll_div;
+
+ if (freq_in && freq_out) {
+ pll_factors(&pll_div, freq_out * 4, freq_in);
+
+ /* Turn on PLL */
+ snd_soc_update_bits(codec, WM8990_POWER_MANAGEMENT_2,
+ WM8990_PLL_ENA, WM8990_PLL_ENA);
+
+ /* sysclk comes from PLL */
+ snd_soc_update_bits(codec, WM8990_CLOCKING_2,
+ WM8990_SYSCLK_SRC, WM8990_SYSCLK_SRC);
+
+ /* set up N , fractional mode and pre-divisor if necessary */
+ snd_soc_write(codec, WM8990_PLL1, pll_div.n | WM8990_SDM |
+ (pll_div.div2?WM8990_PRESCALE:0));
+ snd_soc_write(codec, WM8990_PLL2, (u8)(pll_div.k>>8));
+ snd_soc_write(codec, WM8990_PLL3, (u8)(pll_div.k & 0xFF));
+ } else {
+ /* Turn off PLL */
+ snd_soc_update_bits(codec, WM8990_POWER_MANAGEMENT_2,
+ WM8990_PLL_ENA, 0);
+ }
+ return 0;
+}
+
+/*
+ * Clock after PLL and dividers
+ */
+static int wm8990_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8990_priv *wm8990 = snd_soc_codec_get_drvdata(codec);
+
+ wm8990->sysclk = freq;
+ return 0;
+}
+
+/*
+ * Set's ADC and Voice DAC format.
+ */
+static int wm8990_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 audio1, audio3;
+
+ audio1 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_1);
+ audio3 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_3);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ audio3 &= ~WM8990_AIF_MSTR1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ audio3 |= WM8990_AIF_MSTR1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ audio1 &= ~WM8990_AIF_FMT_MASK;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ audio1 |= WM8990_AIF_TMF_I2S;
+ audio1 &= ~WM8990_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audio1 |= WM8990_AIF_TMF_RIGHTJ;
+ audio1 &= ~WM8990_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audio1 |= WM8990_AIF_TMF_LEFTJ;
+ audio1 &= ~WM8990_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audio1 |= WM8990_AIF_TMF_DSP;
+ audio1 &= ~WM8990_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audio1 |= WM8990_AIF_TMF_DSP | WM8990_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+ snd_soc_write(codec, WM8990_AUDIO_INTERFACE_3, audio3);
+ return 0;
+}
+
+static int wm8990_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ switch (div_id) {
+ case WM8990_MCLK_DIV:
+ snd_soc_update_bits(codec, WM8990_CLOCKING_2,
+ WM8990_MCLK_DIV_MASK, div);
+ break;
+ case WM8990_DACCLK_DIV:
+ snd_soc_update_bits(codec, WM8990_CLOCKING_2,
+ WM8990_DAC_CLKDIV_MASK, div);
+ break;
+ case WM8990_ADCCLK_DIV:
+ snd_soc_update_bits(codec, WM8990_CLOCKING_2,
+ WM8990_ADC_CLKDIV_MASK, div);
+ break;
+ case WM8990_BCLK_DIV:
+ snd_soc_update_bits(codec, WM8990_CLOCKING_1,
+ WM8990_BCLK_DIV_MASK, div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8990_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 audio1 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_1);
+
+ audio1 &= ~WM8990_AIF_WL_MASK;
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ audio1 |= WM8990_AIF_WL_20BITS;
+ break;
+ case 24:
+ audio1 |= WM8990_AIF_WL_24BITS;
+ break;
+ case 32:
+ audio1 |= WM8990_AIF_WL_32BITS;
+ break;
+ }
+
+ snd_soc_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+ return 0;
+}
+
+static int wm8990_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 val;
+
+ val = snd_soc_read(codec, WM8990_DAC_CTRL) & ~WM8990_DAC_MUTE;
+
+ if (mute)
+ snd_soc_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+ else
+ snd_soc_write(codec, WM8990_DAC_CTRL, val);
+
+ return 0;
+}
+
+static int wm8990_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8990_priv *wm8990 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID=2*50k */
+ snd_soc_update_bits(codec, WM8990_POWER_MANAGEMENT_1,
+ WM8990_VMID_MODE_MASK, 0x2);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regcache_sync(wm8990->regmap);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+
+ /* Enable all output discharge bits */
+ snd_soc_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+ WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
+ WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
+ WM8990_DIS_ROUT);
+
+ /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
+ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_BUFDCOPEN | WM8990_POBCTRL |
+ WM8990_VMIDTOG);
+
+ /* Delay to allow output caps to discharge */
+ msleep(300);
+
+ /* Disable VMIDTOG */
+ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_BUFDCOPEN | WM8990_POBCTRL);
+
+ /* disable all output discharge bits */
+ snd_soc_write(codec, WM8990_ANTIPOP1, 0);
+
+ /* Enable outputs */
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1b00);
+
+ msleep(50);
+
+ /* Enable VMID at 2x50k */
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f02);
+
+ msleep(100);
+
+ /* Enable VREF */
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+
+ msleep(600);
+
+ /* Enable BUFIOEN */
+ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_BUFDCOPEN | WM8990_POBCTRL |
+ WM8990_BUFIOEN);
+
+ /* Disable outputs */
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x3);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
+
+ /* Enable workaround for ADC clocking issue. */
+ snd_soc_write(codec, WM8990_EXT_ACCESS_ENA, 0x2);
+ snd_soc_write(codec, WM8990_EXT_CTL1, 0xa003);
+ snd_soc_write(codec, WM8990_EXT_ACCESS_ENA, 0);
+ }
+
+ /* VMID=2*250k */
+ snd_soc_update_bits(codec, WM8990_POWER_MANAGEMENT_1,
+ WM8990_VMID_MODE_MASK, 0x4);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Enable POBCTRL and SOFT_ST */
+ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_POBCTRL | WM8990_BUFIOEN);
+
+ /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
+ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_BUFDCOPEN | WM8990_POBCTRL |
+ WM8990_BUFIOEN);
+
+ /* mute DAC */
+ snd_soc_update_bits(codec, WM8990_DAC_CTRL,
+ WM8990_DAC_MUTE, WM8990_DAC_MUTE);
+
+ /* Enable any disabled outputs */
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+
+ /* Disable VMID */
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f01);
+
+ msleep(300);
+
+ /* Enable all output discharge bits */
+ snd_soc_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+ WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
+ WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
+ WM8990_DIS_ROUT);
+
+ /* Disable VREF */
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x0);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ snd_soc_write(codec, WM8990_ANTIPOP2, 0x0);
+
+ regcache_mark_dirty(wm8990->regmap);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8990_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define WM8990_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/*
+ * The WM8990 supports 2 different and mutually exclusive DAI
+ * configurations.
+ *
+ * 1. ADC/DAC on Primary Interface
+ * 2. ADC on Primary Interface/DAC on secondary
+ */
+static const struct snd_soc_dai_ops wm8990_dai_ops = {
+ .hw_params = wm8990_hw_params,
+ .digital_mute = wm8990_mute,
+ .set_fmt = wm8990_set_dai_fmt,
+ .set_clkdiv = wm8990_set_dai_clkdiv,
+ .set_pll = wm8990_set_dai_pll,
+ .set_sysclk = wm8990_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver wm8990_dai = {
+/* ADC/DAC on primary */
+ .name = "wm8990-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8990_RATES,
+ .formats = WM8990_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8990_RATES,
+ .formats = WM8990_FORMATS,},
+ .ops = &wm8990_dai_ops,
+};
+
+/*
+ * initialise the WM8990 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8990_probe(struct snd_soc_codec *codec)
+{
+ wm8990_reset(codec);
+
+ /* charge output caps */
+ wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ snd_soc_update_bits(codec, WM8990_AUDIO_INTERFACE_4,
+ WM8990_ALRCGPIO1, WM8990_ALRCGPIO1);
+
+ snd_soc_update_bits(codec, WM8990_GPIO1_GPIO2,
+ WM8990_GPIO1_SEL_MASK, 1);
+
+ snd_soc_update_bits(codec, WM8990_POWER_MANAGEMENT_2,
+ WM8990_OPCLK_ENA, WM8990_OPCLK_ENA);
+
+ snd_soc_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+ snd_soc_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8990 = {
+ .probe = wm8990_probe,
+ .set_bias_level = wm8990_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8990_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8990_snd_controls),
+ .dapm_widgets = wm8990_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8990_dapm_widgets),
+ .dapm_routes = wm8990_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8990_dapm_routes),
+};
+
+static const struct regmap_config wm8990_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = WM8990_PLL3,
+ .volatile_reg = wm8990_volatile_register,
+ .reg_defaults = wm8990_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8990_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int wm8990_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8990_priv *wm8990;
+ int ret;
+
+ wm8990 = devm_kzalloc(&i2c->dev, sizeof(struct wm8990_priv),
+ GFP_KERNEL);
+ if (wm8990 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm8990);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8990, &wm8990_dai, 1);
+
+ return ret;
+}
+
+static int wm8990_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm8990_i2c_id[] = {
+ { "wm8990", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8990_i2c_id);
+
+static struct i2c_driver wm8990_i2c_driver = {
+ .driver = {
+ .name = "wm8990",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8990_i2c_probe,
+ .remove = wm8990_i2c_remove,
+ .id_table = wm8990_i2c_id,
+};
+
+module_i2c_driver(wm8990_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8990 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8990.h b/kernel/sound/soc/codecs/wm8990.h
new file mode 100644
index 000000000..0e9c78040
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8990.h
@@ -0,0 +1,826 @@
+/*
+ * wm8990.h -- audio driver for WM8990
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * graeme.gregory@wolfsonmicro.com or linux@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 __WM8990REGISTERDEFS_H__
+#define __WM8990REGISTERDEFS_H__
+
+/*
+ * Register values.
+ */
+#define WM8990_RESET 0x00
+#define WM8990_POWER_MANAGEMENT_1 0x01
+#define WM8990_POWER_MANAGEMENT_2 0x02
+#define WM8990_POWER_MANAGEMENT_3 0x03
+#define WM8990_AUDIO_INTERFACE_1 0x04
+#define WM8990_AUDIO_INTERFACE_2 0x05
+#define WM8990_CLOCKING_1 0x06
+#define WM8990_CLOCKING_2 0x07
+#define WM8990_AUDIO_INTERFACE_3 0x08
+#define WM8990_AUDIO_INTERFACE_4 0x09
+#define WM8990_DAC_CTRL 0x0A
+#define WM8990_LEFT_DAC_DIGITAL_VOLUME 0x0B
+#define WM8990_RIGHT_DAC_DIGITAL_VOLUME 0x0C
+#define WM8990_DIGITAL_SIDE_TONE 0x0D
+#define WM8990_ADC_CTRL 0x0E
+#define WM8990_LEFT_ADC_DIGITAL_VOLUME 0x0F
+#define WM8990_RIGHT_ADC_DIGITAL_VOLUME 0x10
+#define WM8990_GPIO_CTRL_1 0x12
+#define WM8990_GPIO1_GPIO2 0x13
+#define WM8990_GPIO3_GPIO4 0x14
+#define WM8990_GPIO5_GPIO6 0x15
+#define WM8990_GPIOCTRL_2 0x16
+#define WM8990_GPIO_POL 0x17
+#define WM8990_LEFT_LINE_INPUT_1_2_VOLUME 0x18
+#define WM8990_LEFT_LINE_INPUT_3_4_VOLUME 0x19
+#define WM8990_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A
+#define WM8990_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B
+#define WM8990_LEFT_OUTPUT_VOLUME 0x1C
+#define WM8990_RIGHT_OUTPUT_VOLUME 0x1D
+#define WM8990_LINE_OUTPUTS_VOLUME 0x1E
+#define WM8990_OUT3_4_VOLUME 0x1F
+#define WM8990_LEFT_OPGA_VOLUME 0x20
+#define WM8990_RIGHT_OPGA_VOLUME 0x21
+#define WM8990_SPEAKER_VOLUME 0x22
+#define WM8990_CLASSD1 0x23
+#define WM8990_CLASSD3 0x25
+#define WM8990_CLASSD4 0x26
+#define WM8990_INPUT_MIXER1 0x27
+#define WM8990_INPUT_MIXER2 0x28
+#define WM8990_INPUT_MIXER3 0x29
+#define WM8990_INPUT_MIXER4 0x2A
+#define WM8990_INPUT_MIXER5 0x2B
+#define WM8990_INPUT_MIXER6 0x2C
+#define WM8990_OUTPUT_MIXER1 0x2D
+#define WM8990_OUTPUT_MIXER2 0x2E
+#define WM8990_OUTPUT_MIXER3 0x2F
+#define WM8990_OUTPUT_MIXER4 0x30
+#define WM8990_OUTPUT_MIXER5 0x31
+#define WM8990_OUTPUT_MIXER6 0x32
+#define WM8990_OUT3_4_MIXER 0x33
+#define WM8990_LINE_MIXER1 0x34
+#define WM8990_LINE_MIXER2 0x35
+#define WM8990_SPEAKER_MIXER 0x36
+#define WM8990_ADDITIONAL_CONTROL 0x37
+#define WM8990_ANTIPOP1 0x38
+#define WM8990_ANTIPOP2 0x39
+#define WM8990_MICBIAS 0x3A
+#define WM8990_PLL1 0x3C
+#define WM8990_PLL2 0x3D
+#define WM8990_PLL3 0x3E
+
+#define WM8990_EXT_ACCESS_ENA 0x75
+#define WM8990_EXT_CTL1 0x7a
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset
+ */
+#define WM8990_SW_RESET_CHIP_ID_MASK 0xFFFF /* SW_RESET_CHIP_ID */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8990_SPK_ENA 0x1000 /* SPK_ENA */
+#define WM8990_SPK_ENA_BIT 12
+#define WM8990_OUT3_ENA 0x0800 /* OUT3_ENA */
+#define WM8990_OUT3_ENA_BIT 11
+#define WM8990_OUT4_ENA 0x0400 /* OUT4_ENA */
+#define WM8990_OUT4_ENA_BIT 10
+#define WM8990_LOUT_ENA 0x0200 /* LOUT_ENA */
+#define WM8990_LOUT_ENA_BIT 9
+#define WM8990_ROUT_ENA 0x0100 /* ROUT_ENA */
+#define WM8990_ROUT_ENA_BIT 8
+#define WM8990_MICBIAS_ENA 0x0010 /* MICBIAS_ENA */
+#define WM8990_MICBIAS_ENA_BIT 4
+#define WM8990_VMID_MODE_MASK 0x0006 /* VMID_MODE - [2:1] */
+#define WM8990_VREF_ENA 0x0001 /* VREF_ENA */
+#define WM8990_VREF_ENA_BIT 0
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8990_PLL_ENA 0x8000 /* PLL_ENA */
+#define WM8990_PLL_ENA_BIT 15
+#define WM8990_TSHUT_ENA 0x4000 /* TSHUT_ENA */
+#define WM8990_TSHUT_ENA_BIT 14
+#define WM8990_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */
+#define WM8990_TSHUT_OPDIS_BIT 13
+#define WM8990_OPCLK_ENA 0x0800 /* OPCLK_ENA */
+#define WM8990_OPCLK_ENA_BIT 11
+#define WM8990_AINL_ENA 0x0200 /* AINL_ENA */
+#define WM8990_AINL_ENA_BIT 9
+#define WM8990_AINR_ENA 0x0100 /* AINR_ENA */
+#define WM8990_AINR_ENA_BIT 8
+#define WM8990_LIN34_ENA 0x0080 /* LIN34_ENA */
+#define WM8990_LIN34_ENA_BIT 7
+#define WM8990_LIN12_ENA 0x0040 /* LIN12_ENA */
+#define WM8990_LIN12_ENA_BIT 6
+#define WM8990_RIN34_ENA 0x0020 /* RIN34_ENA */
+#define WM8990_RIN34_ENA_BIT 5
+#define WM8990_RIN12_ENA 0x0010 /* RIN12_ENA */
+#define WM8990_RIN12_ENA_BIT 4
+#define WM8990_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8990_ADCL_ENA_BIT 1
+#define WM8990_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8990_ADCR_ENA_BIT 0
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8990_LON_ENA 0x2000 /* LON_ENA */
+#define WM8990_LON_ENA_BIT 13
+#define WM8990_LOP_ENA 0x1000 /* LOP_ENA */
+#define WM8990_LOP_ENA_BIT 12
+#define WM8990_RON_ENA 0x0800 /* RON_ENA */
+#define WM8990_RON_ENA_BIT 11
+#define WM8990_ROP_ENA 0x0400 /* ROP_ENA */
+#define WM8990_ROP_ENA_BIT 10
+#define WM8990_LOPGA_ENA 0x0080 /* LOPGA_ENA */
+#define WM8990_LOPGA_ENA_BIT 7
+#define WM8990_ROPGA_ENA 0x0040 /* ROPGA_ENA */
+#define WM8990_ROPGA_ENA_BIT 6
+#define WM8990_LOMIX_ENA 0x0020 /* LOMIX_ENA */
+#define WM8990_LOMIX_ENA_BIT 5
+#define WM8990_ROMIX_ENA 0x0010 /* ROMIX_ENA */
+#define WM8990_ROMIX_ENA_BIT 4
+#define WM8990_DACL_ENA 0x0002 /* DACL_ENA */
+#define WM8990_DACL_ENA_BIT 1
+#define WM8990_DACR_ENA 0x0001 /* DACR_ENA */
+#define WM8990_DACR_ENA_BIT 0
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8990_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */
+#define WM8990_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */
+#define WM8990_AIFADC_TDM 0x2000 /* AIFADC_TDM */
+#define WM8990_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */
+#define WM8990_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */
+#define WM8990_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */
+#define WM8990_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */
+#define WM8990_AIF_WL_16BITS (0 << 5)
+#define WM8990_AIF_WL_20BITS (1 << 5)
+#define WM8990_AIF_WL_24BITS (2 << 5)
+#define WM8990_AIF_WL_32BITS (3 << 5)
+#define WM8990_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */
+#define WM8990_AIF_TMF_RIGHTJ (0 << 3)
+#define WM8990_AIF_TMF_LEFTJ (1 << 3)
+#define WM8990_AIF_TMF_I2S (2 << 3)
+#define WM8990_AIF_TMF_DSP (3 << 3)
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8990_DACL_SRC 0x8000 /* DACL_SRC */
+#define WM8990_DACR_SRC 0x4000 /* DACR_SRC */
+#define WM8990_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */
+#define WM8990_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8990_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST */
+#define WM8990_DAC_COMP 0x0010 /* DAC_COMP */
+#define WM8990_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */
+#define WM8990_ADC_COMP 0x0004 /* ADC_COMP */
+#define WM8990_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */
+#define WM8990_LOOPBACK 0x0001 /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking (1)
+ */
+#define WM8990_TOCLK_RATE 0x8000 /* TOCLK_RATE */
+#define WM8990_TOCLK_ENA 0x4000 /* TOCLK_ENA */
+#define WM8990_OPCLKDIV_MASK 0x1E00 /* OPCLKDIV - [12:9] */
+#define WM8990_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */
+#define WM8990_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */
+#define WM8990_BCLK_DIV_1 (0x0 << 1)
+#define WM8990_BCLK_DIV_1_5 (0x1 << 1)
+#define WM8990_BCLK_DIV_2 (0x2 << 1)
+#define WM8990_BCLK_DIV_3 (0x3 << 1)
+#define WM8990_BCLK_DIV_4 (0x4 << 1)
+#define WM8990_BCLK_DIV_5_5 (0x5 << 1)
+#define WM8990_BCLK_DIV_6 (0x6 << 1)
+#define WM8990_BCLK_DIV_8 (0x7 << 1)
+#define WM8990_BCLK_DIV_11 (0x8 << 1)
+#define WM8990_BCLK_DIV_12 (0x9 << 1)
+#define WM8990_BCLK_DIV_16 (0xA << 1)
+#define WM8990_BCLK_DIV_22 (0xB << 1)
+#define WM8990_BCLK_DIV_24 (0xC << 1)
+#define WM8990_BCLK_DIV_32 (0xD << 1)
+#define WM8990_BCLK_DIV_44 (0xE << 1)
+#define WM8990_BCLK_DIV_48 (0xF << 1)
+
+/*
+ * R7 (0x07) - Clocking (2)
+ */
+#define WM8990_MCLK_SRC 0x8000 /* MCLK_SRC */
+#define WM8990_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */
+#define WM8990_CLK_FORCE 0x2000 /* CLK_FORCE */
+#define WM8990_MCLK_DIV_MASK 0x1800 /* MCLK_DIV - [12:11] */
+#define WM8990_MCLK_DIV_1 (0 << 11)
+#define WM8990_MCLK_DIV_2 (2 << 11)
+#define WM8990_MCLK_INV 0x0400 /* MCLK_INV */
+#define WM8990_ADC_CLKDIV_MASK 0x00E0 /* ADC_CLKDIV */
+#define WM8990_ADC_CLKDIV_1 (0 << 5)
+#define WM8990_ADC_CLKDIV_1_5 (1 << 5)
+#define WM8990_ADC_CLKDIV_2 (2 << 5)
+#define WM8990_ADC_CLKDIV_3 (3 << 5)
+#define WM8990_ADC_CLKDIV_4 (4 << 5)
+#define WM8990_ADC_CLKDIV_5_5 (5 << 5)
+#define WM8990_ADC_CLKDIV_6 (6 << 5)
+#define WM8990_DAC_CLKDIV_MASK 0x001C /* DAC_CLKDIV - [4:2] */
+#define WM8990_DAC_CLKDIV_1 (0 << 2)
+#define WM8990_DAC_CLKDIV_1_5 (1 << 2)
+#define WM8990_DAC_CLKDIV_2 (2 << 2)
+#define WM8990_DAC_CLKDIV_3 (3 << 2)
+#define WM8990_DAC_CLKDIV_4 (4 << 2)
+#define WM8990_DAC_CLKDIV_5_5 (5 << 2)
+#define WM8990_DAC_CLKDIV_6 (6 << 2)
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8990_AIF_MSTR1 0x8000 /* AIF_MSTR1 */
+#define WM8990_AIF_MSTR2 0x4000 /* AIF_MSTR2 */
+#define WM8990_AIF_SEL 0x2000 /* AIF_SEL */
+#define WM8990_ADCLRC_DIR 0x0800 /* ADCLRC_DIR */
+#define WM8990_ADCLRC_RATE_MASK 0x07FF /* ADCLRC_RATE */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8990_ALRCGPIO1 0x8000 /* ALRCGPIO1 */
+#define WM8990_ALRCBGPIO6 0x4000 /* ALRCBGPIO6 */
+#define WM8990_AIF_TRIS 0x2000 /* AIF_TRIS */
+#define WM8990_DACLRC_DIR 0x0800 /* DACLRC_DIR */
+#define WM8990_DACLRC_RATE_MASK 0x07FF /* DACLRC_RATE */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8990_AIF_LRCLKRATE 0x0400 /* AIF_LRCLKRATE */
+#define WM8990_DAC_MONO 0x0200 /* DAC_MONO */
+#define WM8990_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */
+#define WM8990_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */
+#define WM8990_DAC_MUTEMODE 0x0040 /* DAC_MUTEMODE */
+#define WM8990_DEEMP_MASK 0x0030 /* DEEMP - [5:4] */
+#define WM8990_DAC_MUTE 0x0004 /* DAC_MUTE */
+#define WM8990_DACL_DATINV 0x0002 /* DACL_DATINV */
+#define WM8990_DACR_DATINV 0x0001 /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8990_DAC_VU 0x0100 /* DAC_VU */
+#define WM8990_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */
+#define WM8990_DACL_VOL_SHIFT 0
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8990_DAC_VU 0x0100 /* DAC_VU */
+#define WM8990_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */
+#define WM8990_DACR_VOL_SHIFT 0
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8990_ADCL_DAC_SVOL_MASK 0x0F /* ADCL_DAC_SVOL */
+#define WM8990_ADCL_DAC_SVOL_SHIFT 9
+#define WM8990_ADCR_DAC_SVOL_MASK 0x0F /* ADCR_DAC_SVOL */
+#define WM8990_ADCR_DAC_SVOL_SHIFT 5
+#define WM8990_ADC_TO_DACL_MASK 0x03 /* ADC_TO_DACL - [3:2] */
+#define WM8990_ADC_TO_DACL_SHIFT 2
+#define WM8990_ADC_TO_DACR_MASK 0x03 /* ADC_TO_DACR - [1:0] */
+#define WM8990_ADC_TO_DACR_SHIFT 0
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8990_ADC_HPF_ENA 0x0100 /* ADC_HPF_ENA */
+#define WM8990_ADC_HPF_ENA_BIT 8
+#define WM8990_ADC_HPF_CUT_MASK 0x03 /* ADC_HPF_CUT - [6:5] */
+#define WM8990_ADC_HPF_CUT_SHIFT 5
+#define WM8990_ADCL_DATINV 0x0002 /* ADCL_DATINV */
+#define WM8990_ADCL_DATINV_BIT 1
+#define WM8990_ADCR_DATINV 0x0001 /* ADCR_DATINV */
+#define WM8990_ADCR_DATINV_BIT 0
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8990_ADC_VU 0x0100 /* ADC_VU */
+#define WM8990_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */
+#define WM8990_ADCL_VOL_SHIFT 0
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8990_ADC_VU 0x0100 /* ADC_VU */
+#define WM8990_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */
+#define WM8990_ADCR_VOL_SHIFT 0
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8990_IRQ 0x1000 /* IRQ */
+#define WM8990_TEMPOK 0x0800 /* TEMPOK */
+#define WM8990_MICSHRT 0x0400 /* MICSHRT */
+#define WM8990_MICDET 0x0200 /* MICDET */
+#define WM8990_PLL_LCK 0x0100 /* PLL_LCK */
+#define WM8990_GPI8_STATUS 0x0080 /* GPI8_STATUS */
+#define WM8990_GPI7_STATUS 0x0040 /* GPI7_STATUS */
+#define WM8990_GPIO6_STATUS 0x0020 /* GPIO6_STATUS */
+#define WM8990_GPIO5_STATUS 0x0010 /* GPIO5_STATUS */
+#define WM8990_GPIO4_STATUS 0x0008 /* GPIO4_STATUS */
+#define WM8990_GPIO3_STATUS 0x0004 /* GPIO3_STATUS */
+#define WM8990_GPIO2_STATUS 0x0002 /* GPIO2_STATUS */
+#define WM8990_GPIO1_STATUS 0x0001 /* GPIO1_STATUS */
+
+/*
+ * R19 (0x13) - GPIO1 & GPIO2
+ */
+#define WM8990_GPIO2_DEB_ENA 0x8000 /* GPIO2_DEB_ENA */
+#define WM8990_GPIO2_IRQ_ENA 0x4000 /* GPIO2_IRQ_ENA */
+#define WM8990_GPIO2_PU 0x2000 /* GPIO2_PU */
+#define WM8990_GPIO2_PD 0x1000 /* GPIO2_PD */
+#define WM8990_GPIO2_SEL_MASK 0x0F00 /* GPIO2_SEL - [11:8] */
+#define WM8990_GPIO1_DEB_ENA 0x0080 /* GPIO1_DEB_ENA */
+#define WM8990_GPIO1_IRQ_ENA 0x0040 /* GPIO1_IRQ_ENA */
+#define WM8990_GPIO1_PU 0x0020 /* GPIO1_PU */
+#define WM8990_GPIO1_PD 0x0010 /* GPIO1_PD */
+#define WM8990_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - GPIO3 & GPIO4
+ */
+#define WM8990_GPIO4_DEB_ENA 0x8000 /* GPIO4_DEB_ENA */
+#define WM8990_GPIO4_IRQ_ENA 0x4000 /* GPIO4_IRQ_ENA */
+#define WM8990_GPIO4_PU 0x2000 /* GPIO4_PU */
+#define WM8990_GPIO4_PD 0x1000 /* GPIO4_PD */
+#define WM8990_GPIO4_SEL_MASK 0x0F00 /* GPIO4_SEL - [11:8] */
+#define WM8990_GPIO3_DEB_ENA 0x0080 /* GPIO3_DEB_ENA */
+#define WM8990_GPIO3_IRQ_ENA 0x0040 /* GPIO3_IRQ_ENA */
+#define WM8990_GPIO3_PU 0x0020 /* GPIO3_PU */
+#define WM8990_GPIO3_PD 0x0010 /* GPIO3_PD */
+#define WM8990_GPIO3_SEL_MASK 0x000F /* GPIO3_SEL - [3:0] */
+
+/*
+ * R21 (0x15) - GPIO5 & GPIO6
+ */
+#define WM8990_GPIO6_DEB_ENA 0x8000 /* GPIO6_DEB_ENA */
+#define WM8990_GPIO6_IRQ_ENA 0x4000 /* GPIO6_IRQ_ENA */
+#define WM8990_GPIO6_PU 0x2000 /* GPIO6_PU */
+#define WM8990_GPIO6_PD 0x1000 /* GPIO6_PD */
+#define WM8990_GPIO6_SEL_MASK 0x0F00 /* GPIO6_SEL - [11:8] */
+#define WM8990_GPIO5_DEB_ENA 0x0080 /* GPIO5_DEB_ENA */
+#define WM8990_GPIO5_IRQ_ENA 0x0040 /* GPIO5_IRQ_ENA */
+#define WM8990_GPIO5_PU 0x0020 /* GPIO5_PU */
+#define WM8990_GPIO5_PD 0x0010 /* GPIO5_PD */
+#define WM8990_GPIO5_SEL_MASK 0x000F /* GPIO5_SEL - [3:0] */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8990_RD_3W_ENA 0x8000 /* RD_3W_ENA */
+#define WM8990_MODE_3W4W 0x4000 /* MODE_3W4W */
+#define WM8990_TEMPOK_IRQ_ENA 0x0800 /* TEMPOK_IRQ_ENA */
+#define WM8990_MICSHRT_IRQ_ENA 0x0400 /* MICSHRT_IRQ_ENA */
+#define WM8990_MICDET_IRQ_ENA 0x0200 /* MICDET_IRQ_ENA */
+#define WM8990_PLL_LCK_IRQ_ENA 0x0100 /* PLL_LCK_IRQ_ENA */
+#define WM8990_GPI8_DEB_ENA 0x0080 /* GPI8_DEB_ENA */
+#define WM8990_GPI8_IRQ_ENA 0x0040 /* GPI8_IRQ_ENA */
+#define WM8990_GPI8_ENA 0x0010 /* GPI8_ENA */
+#define WM8990_GPI7_DEB_ENA 0x0008 /* GPI7_DEB_ENA */
+#define WM8990_GPI7_IRQ_ENA 0x0004 /* GPI7_IRQ_ENA */
+#define WM8990_GPI7_ENA 0x0001 /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8990_IRQ_INV 0x1000 /* IRQ_INV */
+#define WM8990_TEMPOK_POL 0x0800 /* TEMPOK_POL */
+#define WM8990_MICSHRT_POL 0x0400 /* MICSHRT_POL */
+#define WM8990_MICDET_POL 0x0200 /* MICDET_POL */
+#define WM8990_PLL_LCK_POL 0x0100 /* PLL_LCK_POL */
+#define WM8990_GPI8_POL 0x0080 /* GPI8_POL */
+#define WM8990_GPI7_POL 0x0040 /* GPI7_POL */
+#define WM8990_GPIO6_POL 0x0020 /* GPIO6_POL */
+#define WM8990_GPIO5_POL 0x0010 /* GPIO5_POL */
+#define WM8990_GPIO4_POL 0x0008 /* GPIO4_POL */
+#define WM8990_GPIO3_POL 0x0004 /* GPIO3_POL */
+#define WM8990_GPIO2_POL 0x0002 /* GPIO2_POL */
+#define WM8990_GPIO1_POL 0x0001 /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8990_IPVU 0x0100 /* IPVU */
+#define WM8990_LI12MUTE 0x0080 /* LI12MUTE */
+#define WM8990_LI12MUTE_BIT 7
+#define WM8990_LI12ZC 0x0040 /* LI12ZC */
+#define WM8990_LI12ZC_BIT 6
+#define WM8990_LIN12VOL_MASK 0x001F /* LIN12VOL - [4:0] */
+#define WM8990_LIN12VOL_SHIFT 0
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8990_IPVU 0x0100 /* IPVU */
+#define WM8990_LI34MUTE 0x0080 /* LI34MUTE */
+#define WM8990_LI34MUTE_BIT 7
+#define WM8990_LI34ZC 0x0040 /* LI34ZC */
+#define WM8990_LI34ZC_BIT 6
+#define WM8990_LIN34VOL_MASK 0x001F /* LIN34VOL - [4:0] */
+#define WM8990_LIN34VOL_SHIFT 0
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8990_IPVU 0x0100 /* IPVU */
+#define WM8990_RI12MUTE 0x0080 /* RI12MUTE */
+#define WM8990_RI12MUTE_BIT 7
+#define WM8990_RI12ZC 0x0040 /* RI12ZC */
+#define WM8990_RI12ZC_BIT 6
+#define WM8990_RIN12VOL_MASK 0x001F /* RIN12VOL - [4:0] */
+#define WM8990_RIN12VOL_SHIFT 0
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8990_IPVU 0x0100 /* IPVU */
+#define WM8990_RI34MUTE 0x0080 /* RI34MUTE */
+#define WM8990_RI34MUTE_BIT 7
+#define WM8990_RI34ZC 0x0040 /* RI34ZC */
+#define WM8990_RI34ZC_BIT 6
+#define WM8990_RIN34VOL_MASK 0x001F /* RIN34VOL - [4:0] */
+#define WM8990_RIN34VOL_SHIFT 0
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8990_OPVU 0x0100 /* OPVU */
+#define WM8990_LOZC 0x0080 /* LOZC */
+#define WM8990_LOZC_BIT 7
+#define WM8990_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */
+#define WM8990_LOUTVOL_SHIFT 0
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8990_OPVU 0x0100 /* OPVU */
+#define WM8990_ROZC 0x0080 /* ROZC */
+#define WM8990_ROZC_BIT 7
+#define WM8990_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */
+#define WM8990_ROUTVOL_SHIFT 0
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8990_LONMUTE 0x0040 /* LONMUTE */
+#define WM8990_LONMUTE_BIT 6
+#define WM8990_LOPMUTE 0x0020 /* LOPMUTE */
+#define WM8990_LOPMUTE_BIT 5
+#define WM8990_LOATTN 0x0010 /* LOATTN */
+#define WM8990_LOATTN_BIT 4
+#define WM8990_RONMUTE 0x0004 /* RONMUTE */
+#define WM8990_RONMUTE_BIT 2
+#define WM8990_ROPMUTE 0x0002 /* ROPMUTE */
+#define WM8990_ROPMUTE_BIT 1
+#define WM8990_ROATTN 0x0001 /* ROATTN */
+#define WM8990_ROATTN_BIT 0
+
+/*
+ * R31 (0x1F) - Out3/4 Volume
+ */
+#define WM8990_OUT3MUTE 0x0020 /* OUT3MUTE */
+#define WM8990_OUT3MUTE_BIT 5
+#define WM8990_OUT3ATTN 0x0010 /* OUT3ATTN */
+#define WM8990_OUT3ATTN_BIT 4
+#define WM8990_OUT4MUTE 0x0002 /* OUT4MUTE */
+#define WM8990_OUT4MUTE_BIT 1
+#define WM8990_OUT4ATTN 0x0001 /* OUT4ATTN */
+#define WM8990_OUT4ATTN_BIT 0
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8990_OPVU 0x0100 /* OPVU */
+#define WM8990_LOPGAZC 0x0080 /* LOPGAZC */
+#define WM8990_LOPGAZC_BIT 7
+#define WM8990_LOPGAVOL_MASK 0x007F /* LOPGAVOL - [6:0] */
+#define WM8990_LOPGAVOL_SHIFT 0
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8990_OPVU 0x0100 /* OPVU */
+#define WM8990_ROPGAZC 0x0080 /* ROPGAZC */
+#define WM8990_ROPGAZC_BIT 7
+#define WM8990_ROPGAVOL_MASK 0x007F /* ROPGAVOL - [6:0] */
+#define WM8990_ROPGAVOL_SHIFT 0
+/*
+ * R34 (0x22) - Speaker Volume
+ */
+#define WM8990_SPKATTN_MASK 0x0003 /* SPKATTN - [1:0] */
+#define WM8990_SPKATTN_SHIFT 0
+
+/*
+ * R35 (0x23) - ClassD1
+ */
+#define WM8990_CDMODE 0x0100 /* CDMODE */
+#define WM8990_CDMODE_BIT 8
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM8990_DCGAIN_MASK 0x0007 /* DCGAIN - [5:3] */
+#define WM8990_DCGAIN_SHIFT 3
+#define WM8990_ACGAIN_MASK 0x0007 /* ACGAIN - [2:0] */
+#define WM8990_ACGAIN_SHIFT 0
+
+/*
+ * R38 (0x26) - ClassD4
+ */
+#define WM8990_SPKZC_MASK 0x0001 /* SPKZC */
+#define WM8990_SPKZC_SHIFT 7 /* SPKZC */
+#define WM8990_SPKVOL_MASK 0x007F /* SPKVOL - [6:0] */
+#define WM8990_SPKVOL_SHIFT 0 /* SPKVOL - [6:0] */
+
+/*
+ * R39 (0x27) - Input Mixer1
+ */
+#define WM8990_AINLMODE_MASK 0x000C /* AINLMODE - [3:2] */
+#define WM8990_AINLMODE_SHIFT 2
+#define WM8990_AINRMODE_MASK 0x0003 /* AINRMODE - [1:0] */
+#define WM8990_AINRMODE_SHIFT 0
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8990_LMP4 0x0080 /* LMP4 */
+#define WM8990_LMP4_BIT 7 /* LMP4 */
+#define WM8990_LMN3 0x0040 /* LMN3 */
+#define WM8990_LMN3_BIT 6 /* LMN3 */
+#define WM8990_LMP2 0x0020 /* LMP2 */
+#define WM8990_LMP2_BIT 5 /* LMP2 */
+#define WM8990_LMN1 0x0010 /* LMN1 */
+#define WM8990_LMN1_BIT 4 /* LMN1 */
+#define WM8990_RMP4 0x0008 /* RMP4 */
+#define WM8990_RMP4_BIT 3 /* RMP4 */
+#define WM8990_RMN3 0x0004 /* RMN3 */
+#define WM8990_RMN3_BIT 2 /* RMN3 */
+#define WM8990_RMP2 0x0002 /* RMP2 */
+#define WM8990_RMP2_BIT 1 /* RMP2 */
+#define WM8990_RMN1 0x0001 /* RMN1 */
+#define WM8990_RMN1_BIT 0 /* RMN1 */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8990_L34MNB 0x0100 /* L34MNB */
+#define WM8990_L34MNB_BIT 8
+#define WM8990_L34MNBST 0x0080 /* L34MNBST */
+#define WM8990_L34MNBST_BIT 7
+#define WM8990_L12MNB 0x0020 /* L12MNB */
+#define WM8990_L12MNB_BIT 5
+#define WM8990_L12MNBST 0x0010 /* L12MNBST */
+#define WM8990_L12MNBST_BIT 4
+#define WM8990_LDBVOL_MASK 0x0007 /* LDBVOL - [2:0] */
+#define WM8990_LDBVOL_SHIFT 0
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8990_R34MNB 0x0100 /* R34MNB */
+#define WM8990_R34MNB_BIT 8
+#define WM8990_R34MNBST 0x0080 /* R34MNBST */
+#define WM8990_R34MNBST_BIT 7
+#define WM8990_R12MNB 0x0020 /* R12MNB */
+#define WM8990_R12MNB_BIT 5
+#define WM8990_R12MNBST 0x0010 /* R12MNBST */
+#define WM8990_R12MNBST_BIT 4
+#define WM8990_RDBVOL_MASK 0x0007 /* RDBVOL - [2:0] */
+#define WM8990_RDBVOL_SHIFT 0
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8990_LI2BVOL_MASK 0x07 /* LI2BVOL - [8:6] */
+#define WM8990_LI2BVOL_SHIFT 6
+#define WM8990_LR4BVOL_MASK 0x07 /* LR4BVOL - [5:3] */
+#define WM8990_LR4BVOL_SHIFT 3
+#define WM8990_LL4BVOL_MASK 0x07 /* LL4BVOL - [2:0] */
+#define WM8990_LL4BVOL_SHIFT 0
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8990_RI2BVOL_MASK 0x07 /* RI2BVOL - [8:6] */
+#define WM8990_RI2BVOL_SHIFT 6
+#define WM8990_RL4BVOL_MASK 0x07 /* RL4BVOL - [5:3] */
+#define WM8990_RL4BVOL_SHIFT 3
+#define WM8990_RR4BVOL_MASK 0x07 /* RR4BVOL - [2:0] */
+#define WM8990_RR4BVOL_SHIFT 0
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8990_LRBLO 0x0080 /* LRBLO */
+#define WM8990_LRBLO_BIT 7
+#define WM8990_LLBLO 0x0040 /* LLBLO */
+#define WM8990_LLBLO_BIT 6
+#define WM8990_LRI3LO 0x0020 /* LRI3LO */
+#define WM8990_LRI3LO_BIT 5
+#define WM8990_LLI3LO 0x0010 /* LLI3LO */
+#define WM8990_LLI3LO_BIT 4
+#define WM8990_LR12LO 0x0008 /* LR12LO */
+#define WM8990_LR12LO_BIT 3
+#define WM8990_LL12LO 0x0004 /* LL12LO */
+#define WM8990_LL12LO_BIT 2
+#define WM8990_LDLO 0x0001 /* LDLO */
+#define WM8990_LDLO_BIT 0
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8990_RLBRO 0x0080 /* RLBRO */
+#define WM8990_RLBRO_BIT 7
+#define WM8990_RRBRO 0x0040 /* RRBRO */
+#define WM8990_RRBRO_BIT 6
+#define WM8990_RLI3RO 0x0020 /* RLI3RO */
+#define WM8990_RLI3RO_BIT 5
+#define WM8990_RRI3RO 0x0010 /* RRI3RO */
+#define WM8990_RRI3RO_BIT 4
+#define WM8990_RL12RO 0x0008 /* RL12RO */
+#define WM8990_RL12RO_BIT 3
+#define WM8990_RR12RO 0x0004 /* RR12RO */
+#define WM8990_RR12RO_BIT 2
+#define WM8990_RDRO 0x0001 /* RDRO */
+#define WM8990_RDRO_BIT 0
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8990_LLI3LOVOL_MASK 0x07 /* LLI3LOVOL - [8:6] */
+#define WM8990_LLI3LOVOL_SHIFT 6
+#define WM8990_LR12LOVOL_MASK 0x07 /* LR12LOVOL - [5:3] */
+#define WM8990_LR12LOVOL_SHIFT 3
+#define WM8990_LL12LOVOL_MASK 0x07 /* LL12LOVOL - [2:0] */
+#define WM8990_LL12LOVOL_SHIFT 0
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8990_RRI3ROVOL_MASK 0x07 /* RRI3ROVOL - [8:6] */
+#define WM8990_RRI3ROVOL_SHIFT 6
+#define WM8990_RL12ROVOL_MASK 0x07 /* RL12ROVOL - [5:3] */
+#define WM8990_RL12ROVOL_SHIFT 3
+#define WM8990_RR12ROVOL_MASK 0x07 /* RR12ROVOL - [2:0] */
+#define WM8990_RR12ROVOL_SHIFT 0
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8990_LRI3LOVOL_MASK 0x07 /* LRI3LOVOL - [8:6] */
+#define WM8990_LRI3LOVOL_SHIFT 6
+#define WM8990_LRBLOVOL_MASK 0x07 /* LRBLOVOL - [5:3] */
+#define WM8990_LRBLOVOL_SHIFT 3
+#define WM8990_LLBLOVOL_MASK 0x07 /* LLBLOVOL - [2:0] */
+#define WM8990_LLBLOVOL_SHIFT 0
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8990_RLI3ROVOL_MASK 0x07 /* RLI3ROVOL - [8:6] */
+#define WM8990_RLI3ROVOL_SHIFT 6
+#define WM8990_RLBROVOL_MASK 0x07 /* RLBROVOL - [5:3] */
+#define WM8990_RLBROVOL_SHIFT 3
+#define WM8990_RRBROVOL_MASK 0x07 /* RRBROVOL - [2:0] */
+#define WM8990_RRBROVOL_SHIFT 0
+
+/*
+ * R51 (0x33) - Out3/4 Mixer
+ */
+#define WM8990_VSEL_MASK 0x0180 /* VSEL - [8:7] */
+#define WM8990_LI4O3 0x0020 /* LI4O3 */
+#define WM8990_LI4O3_BIT 5
+#define WM8990_LPGAO3 0x0010 /* LPGAO3 */
+#define WM8990_LPGAO3_BIT 4
+#define WM8990_RI4O4 0x0002 /* RI4O4 */
+#define WM8990_RI4O4_BIT 1
+#define WM8990_RPGAO4 0x0001 /* RPGAO4 */
+#define WM8990_RPGAO4_BIT 0
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8990_LLOPGALON 0x0040 /* LLOPGALON */
+#define WM8990_LLOPGALON_BIT 6
+#define WM8990_LROPGALON 0x0020 /* LROPGALON */
+#define WM8990_LROPGALON_BIT 5
+#define WM8990_LOPLON 0x0010 /* LOPLON */
+#define WM8990_LOPLON_BIT 4
+#define WM8990_LR12LOP 0x0004 /* LR12LOP */
+#define WM8990_LR12LOP_BIT 2
+#define WM8990_LL12LOP 0x0002 /* LL12LOP */
+#define WM8990_LL12LOP_BIT 1
+#define WM8990_LLOPGALOP 0x0001 /* LLOPGALOP */
+#define WM8990_LLOPGALOP_BIT 0
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8990_RROPGARON 0x0040 /* RROPGARON */
+#define WM8990_RROPGARON_BIT 6
+#define WM8990_RLOPGARON 0x0020 /* RLOPGARON */
+#define WM8990_RLOPGARON_BIT 5
+#define WM8990_ROPRON 0x0010 /* ROPRON */
+#define WM8990_ROPRON_BIT 4
+#define WM8990_RL12ROP 0x0004 /* RL12ROP */
+#define WM8990_RL12ROP_BIT 2
+#define WM8990_RR12ROP 0x0002 /* RR12ROP */
+#define WM8990_RR12ROP_BIT 1
+#define WM8990_RROPGAROP 0x0001 /* RROPGAROP */
+#define WM8990_RROPGAROP_BIT 0
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8990_LB2SPK 0x0080 /* LB2SPK */
+#define WM8990_LB2SPK_BIT 7
+#define WM8990_RB2SPK 0x0040 /* RB2SPK */
+#define WM8990_RB2SPK_BIT 6
+#define WM8990_LI2SPK 0x0020 /* LI2SPK */
+#define WM8990_LI2SPK_BIT 5
+#define WM8990_RI2SPK 0x0010 /* RI2SPK */
+#define WM8990_RI2SPK_BIT 4
+#define WM8990_LOPGASPK 0x0008 /* LOPGASPK */
+#define WM8990_LOPGASPK_BIT 3
+#define WM8990_ROPGASPK 0x0004 /* ROPGASPK */
+#define WM8990_ROPGASPK_BIT 2
+#define WM8990_LDSPK 0x0002 /* LDSPK */
+#define WM8990_LDSPK_BIT 1
+#define WM8990_RDSPK 0x0001 /* RDSPK */
+#define WM8990_RDSPK_BIT 0
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8990_VROI 0x0001 /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8990_DIS_LLINE 0x0020 /* DIS_LLINE */
+#define WM8990_DIS_RLINE 0x0010 /* DIS_RLINE */
+#define WM8990_DIS_OUT3 0x0008 /* DIS_OUT3 */
+#define WM8990_DIS_OUT4 0x0004 /* DIS_OUT4 */
+#define WM8990_DIS_LOUT 0x0002 /* DIS_LOUT */
+#define WM8990_DIS_ROUT 0x0001 /* DIS_ROUT */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8990_SOFTST 0x0040 /* SOFTST */
+#define WM8990_BUFIOEN 0x0008 /* BUFIOEN */
+#define WM8990_BUFDCOPEN 0x0004 /* BUFDCOPEN */
+#define WM8990_POBCTRL 0x0002 /* POBCTRL */
+#define WM8990_VMIDTOG 0x0001 /* VMIDTOG */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8990_MCDSCTH_MASK 0x00C0 /* MCDSCTH - [7:6] */
+#define WM8990_MCDTHR_MASK 0x0038 /* MCDTHR - [5:3] */
+#define WM8990_MCD 0x0004 /* MCD */
+#define WM8990_MBSEL 0x0001 /* MBSEL */
+
+/*
+ * R60 (0x3C) - PLL1
+ */
+#define WM8990_SDM 0x0080 /* SDM */
+#define WM8990_PRESCALE 0x0040 /* PRESCALE */
+#define WM8990_PLLN_MASK 0x000F /* PLLN - [3:0] */
+
+/*
+ * R61 (0x3D) - PLL2
+ */
+#define WM8990_PLLK1_MASK 0x00FF /* PLLK1 - [7:0] */
+
+/*
+ * R62 (0x3E) - PLL3
+ */
+#define WM8990_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */
+
+#define WM8990_MCLK_DIV 0
+#define WM8990_DACCLK_DIV 1
+#define WM8990_ADCCLK_DIV 2
+#define WM8990_BCLK_DIV 3
+
+#endif /* __WM8990REGISTERDEFS_H__ */
+/*------------------------------ END OF FILE ---------------------------------*/
diff --git a/kernel/sound/soc/codecs/wm8991.c b/kernel/sound/soc/codecs/wm8991.c
new file mode 100644
index 000000000..49df0dc60
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8991.c
@@ -0,0 +1,1378 @@
+/*
+ * wm8991.c -- WM8991 ALSA Soc Audio driver
+ *
+ * Copyright 2007-2010 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * Graeme.Gregory@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/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/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+
+#include "wm8991.h"
+
+struct wm8991_priv {
+ struct regmap *regmap;
+ unsigned int pcmclk;
+};
+
+static const struct reg_default wm8991_reg_defaults[] = {
+ { 1, 0x0000 }, /* R1 - Power Management (1) */
+ { 2, 0x6000 }, /* R2 - Power Management (2) */
+ { 3, 0x0000 }, /* R3 - Power Management (3) */
+ { 4, 0x4050 }, /* R4 - Audio Interface (1) */
+ { 5, 0x4000 }, /* R5 - Audio Interface (2) */
+ { 6, 0x01C8 }, /* R6 - Clocking (1) */
+ { 7, 0x0000 }, /* R7 - Clocking (2) */
+ { 8, 0x0040 }, /* R8 - Audio Interface (3) */
+ { 9, 0x0040 }, /* R9 - Audio Interface (4) */
+ { 10, 0x0004 }, /* R10 - DAC CTRL */
+ { 11, 0x00C0 }, /* R11 - Left DAC Digital Volume */
+ { 12, 0x00C0 }, /* R12 - Right DAC Digital Volume */
+ { 13, 0x0000 }, /* R13 - Digital Side Tone */
+ { 14, 0x0100 }, /* R14 - ADC CTRL */
+ { 15, 0x00C0 }, /* R15 - Left ADC Digital Volume */
+ { 16, 0x00C0 }, /* R16 - Right ADC Digital Volume */
+
+ { 18, 0x0000 }, /* R18 - GPIO CTRL 1 */
+ { 19, 0x1000 }, /* R19 - GPIO1 & GPIO2 */
+ { 20, 0x1010 }, /* R20 - GPIO3 & GPIO4 */
+ { 21, 0x1010 }, /* R21 - GPIO5 & GPIO6 */
+ { 22, 0x8000 }, /* R22 - GPIOCTRL 2 */
+ { 23, 0x0800 }, /* R23 - GPIO_POL */
+ { 24, 0x008B }, /* R24 - Left Line Input 1&2 Volume */
+ { 25, 0x008B }, /* R25 - Left Line Input 3&4 Volume */
+ { 26, 0x008B }, /* R26 - Right Line Input 1&2 Volume */
+ { 27, 0x008B }, /* R27 - Right Line Input 3&4 Volume */
+ { 28, 0x0000 }, /* R28 - Left Output Volume */
+ { 29, 0x0000 }, /* R29 - Right Output Volume */
+ { 30, 0x0066 }, /* R30 - Line Outputs Volume */
+ { 31, 0x0022 }, /* R31 - Out3/4 Volume */
+ { 32, 0x0079 }, /* R32 - Left OPGA Volume */
+ { 33, 0x0079 }, /* R33 - Right OPGA Volume */
+ { 34, 0x0003 }, /* R34 - Speaker Volume */
+ { 35, 0x0003 }, /* R35 - ClassD1 */
+
+ { 37, 0x0100 }, /* R37 - ClassD3 */
+
+ { 39, 0x0000 }, /* R39 - Input Mixer1 */
+ { 40, 0x0000 }, /* R40 - Input Mixer2 */
+ { 41, 0x0000 }, /* R41 - Input Mixer3 */
+ { 42, 0x0000 }, /* R42 - Input Mixer4 */
+ { 43, 0x0000 }, /* R43 - Input Mixer5 */
+ { 44, 0x0000 }, /* R44 - Input Mixer6 */
+ { 45, 0x0000 }, /* R45 - Output Mixer1 */
+ { 46, 0x0000 }, /* R46 - Output Mixer2 */
+ { 47, 0x0000 }, /* R47 - Output Mixer3 */
+ { 48, 0x0000 }, /* R48 - Output Mixer4 */
+ { 49, 0x0000 }, /* R49 - Output Mixer5 */
+ { 50, 0x0000 }, /* R50 - Output Mixer6 */
+ { 51, 0x0180 }, /* R51 - Out3/4 Mixer */
+ { 52, 0x0000 }, /* R52 - Line Mixer1 */
+ { 53, 0x0000 }, /* R53 - Line Mixer2 */
+ { 54, 0x0000 }, /* R54 - Speaker Mixer */
+ { 55, 0x0000 }, /* R55 - Additional Control */
+ { 56, 0x0000 }, /* R56 - AntiPOP1 */
+ { 57, 0x0000 }, /* R57 - AntiPOP2 */
+ { 58, 0x0000 }, /* R58 - MICBIAS */
+
+ { 60, 0x0008 }, /* R60 - PLL1 */
+ { 61, 0x0031 }, /* R61 - PLL2 */
+ { 62, 0x0026 }, /* R62 - PLL3 */
+};
+
+static bool wm8991_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8991_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const unsigned int rec_mix_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 7, TLV_DB_LINEAR_ITEM(-1500, 600),
+};
+
+static const unsigned int in_pga_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 0x1F, TLV_DB_LINEAR_ITEM(-1650, 3000),
+};
+
+static const unsigned int out_mix_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 7, TLV_DB_LINEAR_ITEM(0, -2100),
+};
+
+static const unsigned int out_pga_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 127, TLV_DB_LINEAR_ITEM(-7300, 600),
+};
+
+static const unsigned int out_omix_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 7, TLV_DB_LINEAR_ITEM(-600, 0),
+};
+
+static const unsigned int out_dac_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 255, TLV_DB_LINEAR_ITEM(-7163, 0),
+};
+
+static const unsigned int in_adc_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 255, TLV_DB_LINEAR_ITEM(-7163, 1763),
+};
+
+static const unsigned int out_sidetone_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 31, TLV_DB_LINEAR_ITEM(-3600, 0),
+};
+
+static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int ret;
+ u16 val;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /* now hit the volume update bits (always bit 8) */
+ val = snd_soc_read(codec, reg);
+ return snd_soc_write(codec, reg, val | 0x0100);
+}
+
+static const char *wm8991_digital_sidetone[] =
+{"None", "Left ADC", "Right ADC", "Reserved"};
+
+static SOC_ENUM_SINGLE_DECL(wm8991_left_digital_sidetone_enum,
+ WM8991_DIGITAL_SIDE_TONE,
+ WM8991_ADC_TO_DACL_SHIFT,
+ wm8991_digital_sidetone);
+
+static SOC_ENUM_SINGLE_DECL(wm8991_right_digital_sidetone_enum,
+ WM8991_DIGITAL_SIDE_TONE,
+ WM8991_ADC_TO_DACR_SHIFT,
+ wm8991_digital_sidetone);
+
+static const char *wm8991_adcmode[] =
+{"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
+
+static SOC_ENUM_SINGLE_DECL(wm8991_right_adcmode_enum,
+ WM8991_ADC_CTRL,
+ WM8991_ADC_HPF_CUT_SHIFT,
+ wm8991_adcmode);
+
+static const struct snd_kcontrol_new wm8991_snd_controls[] = {
+ /* INMIXL */
+ SOC_SINGLE("LIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L12MNBST_BIT, 1, 0),
+ SOC_SINGLE("LIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L34MNBST_BIT, 1, 0),
+ /* INMIXR */
+ SOC_SINGLE("RIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R12MNBST_BIT, 1, 0),
+ SOC_SINGLE("RIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R34MNBST_BIT, 1, 0),
+
+ /* LOMIX */
+ SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER3,
+ WM8991_LLI3LOVOL_SHIFT, WM8991_LLI3LOVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3,
+ WM8991_LR12LOVOL_SHIFT, WM8991_LR12LOVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3,
+ WM8991_LL12LOVOL_SHIFT, WM8991_LL12LOVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER5,
+ WM8991_LRI3LOVOL_SHIFT, WM8991_LRI3LOVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER5,
+ WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER5,
+ WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv),
+
+ /* ROMIX */
+ SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER4,
+ WM8991_RRI3ROVOL_SHIFT, WM8991_RRI3ROVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4,
+ WM8991_RL12ROVOL_SHIFT, WM8991_RL12ROVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4,
+ WM8991_RR12ROVOL_SHIFT, WM8991_RR12ROVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER6,
+ WM8991_RLI3ROVOL_SHIFT, WM8991_RLI3ROVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER6,
+ WM8991_RLBROVOL_SHIFT, WM8991_RLBROVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER6,
+ WM8991_RRBROVOL_SHIFT, WM8991_RRBROVOL_MASK, 1, out_mix_tlv),
+
+ /* LOUT */
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8991_LEFT_OUTPUT_VOLUME,
+ WM8991_LOUTVOL_SHIFT, WM8991_LOUTVOL_MASK, 0, out_pga_tlv),
+ SOC_SINGLE("LOUT ZC", WM8991_LEFT_OUTPUT_VOLUME, WM8991_LOZC_BIT, 1, 0),
+
+ /* ROUT */
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8991_RIGHT_OUTPUT_VOLUME,
+ WM8991_ROUTVOL_SHIFT, WM8991_ROUTVOL_MASK, 0, out_pga_tlv),
+ SOC_SINGLE("ROUT ZC", WM8991_RIGHT_OUTPUT_VOLUME, WM8991_ROZC_BIT, 1, 0),
+
+ /* LOPGA */
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8991_LEFT_OPGA_VOLUME,
+ WM8991_LOPGAVOL_SHIFT, WM8991_LOPGAVOL_MASK, 0, out_pga_tlv),
+ SOC_SINGLE("LOPGA ZC Switch", WM8991_LEFT_OPGA_VOLUME,
+ WM8991_LOPGAZC_BIT, 1, 0),
+
+ /* ROPGA */
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8991_RIGHT_OPGA_VOLUME,
+ WM8991_ROPGAVOL_SHIFT, WM8991_ROPGAVOL_MASK, 0, out_pga_tlv),
+ SOC_SINGLE("ROPGA ZC Switch", WM8991_RIGHT_OPGA_VOLUME,
+ WM8991_ROPGAZC_BIT, 1, 0),
+
+ SOC_SINGLE("LON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+ WM8991_LONMUTE_BIT, 1, 0),
+ SOC_SINGLE("LOP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+ WM8991_LOPMUTE_BIT, 1, 0),
+ SOC_SINGLE("LOP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME,
+ WM8991_LOATTN_BIT, 1, 0),
+ SOC_SINGLE("RON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+ WM8991_RONMUTE_BIT, 1, 0),
+ SOC_SINGLE("ROP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+ WM8991_ROPMUTE_BIT, 1, 0),
+ SOC_SINGLE("ROP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME,
+ WM8991_ROATTN_BIT, 1, 0),
+
+ SOC_SINGLE("OUT3 Mute Switch", WM8991_OUT3_4_VOLUME,
+ WM8991_OUT3MUTE_BIT, 1, 0),
+ SOC_SINGLE("OUT3 Attenuation Switch", WM8991_OUT3_4_VOLUME,
+ WM8991_OUT3ATTN_BIT, 1, 0),
+
+ SOC_SINGLE("OUT4 Mute Switch", WM8991_OUT3_4_VOLUME,
+ WM8991_OUT4MUTE_BIT, 1, 0),
+ SOC_SINGLE("OUT4 Attenuation Switch", WM8991_OUT3_4_VOLUME,
+ WM8991_OUT4ATTN_BIT, 1, 0),
+
+ SOC_SINGLE("Speaker Mode Switch", WM8991_CLASSD1,
+ WM8991_CDMODE_BIT, 1, 0),
+
+ SOC_SINGLE("Speaker Output Attenuation Volume", WM8991_SPEAKER_VOLUME,
+ WM8991_SPKVOL_SHIFT, WM8991_SPKVOL_MASK, 0),
+ SOC_SINGLE("Speaker DC Boost Volume", WM8991_CLASSD3,
+ WM8991_DCGAIN_SHIFT, WM8991_DCGAIN_MASK, 0),
+ SOC_SINGLE("Speaker AC Boost Volume", WM8991_CLASSD3,
+ WM8991_ACGAIN_SHIFT, WM8991_ACGAIN_MASK, 0),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
+ WM8991_LEFT_DAC_DIGITAL_VOLUME,
+ WM8991_DACL_VOL_SHIFT,
+ WM8991_DACL_VOL_MASK,
+ 0,
+ out_dac_tlv),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
+ WM8991_RIGHT_DAC_DIGITAL_VOLUME,
+ WM8991_DACR_VOL_SHIFT,
+ WM8991_DACR_VOL_MASK,
+ 0,
+ out_dac_tlv),
+
+ SOC_ENUM("Left Digital Sidetone", wm8991_left_digital_sidetone_enum),
+ SOC_ENUM("Right Digital Sidetone", wm8991_right_digital_sidetone_enum),
+
+ SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE,
+ WM8991_ADCL_DAC_SVOL_SHIFT, WM8991_ADCL_DAC_SVOL_MASK, 0,
+ out_sidetone_tlv),
+ SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE,
+ WM8991_ADCR_DAC_SVOL_SHIFT, WM8991_ADCR_DAC_SVOL_MASK, 0,
+ out_sidetone_tlv),
+
+ SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8991_ADC_CTRL,
+ WM8991_ADC_HPF_ENA_BIT, 1, 0),
+
+ SOC_ENUM("ADC HPF Mode", wm8991_right_adcmode_enum),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
+ WM8991_LEFT_ADC_DIGITAL_VOLUME,
+ WM8991_ADCL_VOL_SHIFT,
+ WM8991_ADCL_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
+ WM8991_RIGHT_ADC_DIGITAL_VOLUME,
+ WM8991_ADCR_VOL_SHIFT,
+ WM8991_ADCR_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
+ WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8991_LIN12VOL_SHIFT,
+ WM8991_LIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+ SOC_SINGLE("LIN12 ZC Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8991_LI12ZC_BIT, 1, 0),
+
+ SOC_SINGLE("LIN12 Mute Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8991_LI12MUTE_BIT, 1, 0),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
+ WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8991_LIN34VOL_SHIFT,
+ WM8991_LIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+ SOC_SINGLE("LIN34 ZC Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8991_LI34ZC_BIT, 1, 0),
+
+ SOC_SINGLE("LIN34 Mute Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8991_LI34MUTE_BIT, 1, 0),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
+ WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8991_RIN12VOL_SHIFT,
+ WM8991_RIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+ SOC_SINGLE("RIN12 ZC Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8991_RI12ZC_BIT, 1, 0),
+
+ SOC_SINGLE("RIN12 Mute Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8991_RI12MUTE_BIT, 1, 0),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
+ WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8991_RIN34VOL_SHIFT,
+ WM8991_RIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+ SOC_SINGLE("RIN34 ZC Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8991_RI34ZC_BIT, 1, 0),
+
+ SOC_SINGLE("RIN34 Mute Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8991_RI34MUTE_BIT, 1, 0),
+};
+
+/*
+ * _DAPM_ Controls
+ */
+static int outmixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u32 reg_shift = kcontrol->private_value & 0xfff;
+ int ret = 0;
+ u16 reg;
+
+ switch (reg_shift) {
+ case WM8991_SPEAKER_MIXER | (WM8991_LDSPK_BIT << 8):
+ reg = snd_soc_read(codec, WM8991_OUTPUT_MIXER1);
+ if (reg & WM8991_LDLO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 1 LDLO Set\n");
+ ret = -1;
+ }
+ break;
+
+ case WM8991_SPEAKER_MIXER | (WM8991_RDSPK_BIT << 8):
+ reg = snd_soc_read(codec, WM8991_OUTPUT_MIXER2);
+ if (reg & WM8991_RDRO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 2 RDRO Set\n");
+ ret = -1;
+ }
+ break;
+
+ case WM8991_OUTPUT_MIXER1 | (WM8991_LDLO_BIT << 8):
+ reg = snd_soc_read(codec, WM8991_SPEAKER_MIXER);
+ if (reg & WM8991_LDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer LDSPK Set\n");
+ ret = -1;
+ }
+ break;
+
+ case WM8991_OUTPUT_MIXER2 | (WM8991_RDRO_BIT << 8):
+ reg = snd_soc_read(codec, WM8991_SPEAKER_MIXER);
+ if (reg & WM8991_RDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer RDSPK Set\n");
+ ret = -1;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+/* INMIX dB values */
+static const unsigned int in_mix_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 7, TLV_DB_LINEAR_ITEM(-1200, 600),
+};
+
+/* Left In PGA Connections */
+static const struct snd_kcontrol_new wm8991_dapm_lin12_pga_controls[] = {
+ SOC_DAPM_SINGLE("LIN1 Switch", WM8991_INPUT_MIXER2, WM8991_LMN1_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LIN2 Switch", WM8991_INPUT_MIXER2, WM8991_LMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8991_dapm_lin34_pga_controls[] = {
+ SOC_DAPM_SINGLE("LIN3 Switch", WM8991_INPUT_MIXER2, WM8991_LMN3_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LIN4 Switch", WM8991_INPUT_MIXER2, WM8991_LMP4_BIT, 1, 0),
+};
+
+/* Right In PGA Connections */
+static const struct snd_kcontrol_new wm8991_dapm_rin12_pga_controls[] = {
+ SOC_DAPM_SINGLE("RIN1 Switch", WM8991_INPUT_MIXER2, WM8991_RMN1_BIT, 1, 0),
+ SOC_DAPM_SINGLE("RIN2 Switch", WM8991_INPUT_MIXER2, WM8991_RMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8991_dapm_rin34_pga_controls[] = {
+ SOC_DAPM_SINGLE("RIN3 Switch", WM8991_INPUT_MIXER2, WM8991_RMN3_BIT, 1, 0),
+ SOC_DAPM_SINGLE("RIN4 Switch", WM8991_INPUT_MIXER2, WM8991_RMP4_BIT, 1, 0),
+};
+
+/* INMIXL */
+static const struct snd_kcontrol_new wm8991_dapm_inmixl_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8991_INPUT_MIXER3,
+ WM8991_LDBVOL_SHIFT, WM8991_LDBVOL_MASK, 0, in_mix_tlv),
+ SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8991_INPUT_MIXER5, WM8991_LI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+ SOC_DAPM_SINGLE("LINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT,
+ 1, 0),
+ SOC_DAPM_SINGLE("LINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT,
+ 1, 0),
+};
+
+/* INMIXR */
+static const struct snd_kcontrol_new wm8991_dapm_inmixr_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8991_INPUT_MIXER4,
+ WM8991_RDBVOL_SHIFT, WM8991_RDBVOL_MASK, 0, in_mix_tlv),
+ SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8991_INPUT_MIXER6, WM8991_RI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+ SOC_DAPM_SINGLE("RINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT,
+ 1, 0),
+ SOC_DAPM_SINGLE("RINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT,
+ 1, 0),
+};
+
+/* AINLMUX */
+static const char *wm8991_ainlmux[] =
+{"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
+
+static SOC_ENUM_SINGLE_DECL(wm8991_ainlmux_enum,
+ WM8991_INPUT_MIXER1, WM8991_AINLMODE_SHIFT,
+ wm8991_ainlmux);
+
+static const struct snd_kcontrol_new wm8991_dapm_ainlmux_controls =
+ SOC_DAPM_ENUM("Route", wm8991_ainlmux_enum);
+
+/* DIFFINL */
+
+/* AINRMUX */
+static const char *wm8991_ainrmux[] =
+{"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
+
+static SOC_ENUM_SINGLE_DECL(wm8991_ainrmux_enum,
+ WM8991_INPUT_MIXER1, WM8991_AINRMODE_SHIFT,
+ wm8991_ainrmux);
+
+static const struct snd_kcontrol_new wm8991_dapm_ainrmux_controls =
+ SOC_DAPM_ENUM("Route", wm8991_ainrmux_enum);
+
+/* RXVOICE */
+static const struct snd_kcontrol_new wm8991_dapm_rxvoice_controls[] = {
+ SOC_DAPM_SINGLE_TLV("LIN4RXN", WM8991_INPUT_MIXER5, WM8991_LR4BVOL_SHIFT,
+ WM8991_LR4BVOL_MASK, 0, in_mix_tlv),
+ SOC_DAPM_SINGLE_TLV("RIN4RXP", WM8991_INPUT_MIXER6, WM8991_RL4BVOL_SHIFT,
+ WM8991_RL4BVOL_MASK, 0, in_mix_tlv),
+};
+
+/* LOMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lomix_controls[] = {
+ SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LRBLO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LLBLO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LRI3LO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LLI3LO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LR12LO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LL12LO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LDLO_BIT, 1, 0),
+};
+
+/* ROMIX */
+static const struct snd_kcontrol_new wm8991_dapm_romix_controls[] = {
+ SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RLBRO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RRBRO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RLI3RO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RRI3RO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RL12RO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RR12RO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RDRO_BIT, 1, 0),
+};
+
+/* LONMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lonmix_controls[] = {
+ SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1,
+ WM8991_LLOPGALON_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER1,
+ WM8991_LROPGALON_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8991_LINE_MIXER1,
+ WM8991_LOPLON_BIT, 1, 0),
+};
+
+/* LOPMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lopmix_controls[] = {
+ SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER1,
+ WM8991_LR12LOP_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER1,
+ WM8991_LL12LOP_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1,
+ WM8991_LLOPGALOP_BIT, 1, 0),
+};
+
+/* RONMIX */
+static const struct snd_kcontrol_new wm8991_dapm_ronmix_controls[] = {
+ SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2,
+ WM8991_RROPGARON_BIT, 1, 0),
+ SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER2,
+ WM8991_RLOPGARON_BIT, 1, 0),
+ SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8991_LINE_MIXER2,
+ WM8991_ROPRON_BIT, 1, 0),
+};
+
+/* ROPMIX */
+static const struct snd_kcontrol_new wm8991_dapm_ropmix_controls[] = {
+ SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER2,
+ WM8991_RL12ROP_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER2,
+ WM8991_RR12ROP_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2,
+ WM8991_RROPGAROP_BIT, 1, 0),
+};
+
+/* OUT3MIX */
+static const struct snd_kcontrol_new wm8991_dapm_out3mix_controls[] = {
+ SOC_DAPM_SINGLE("OUT3MIX LIN4RXN Bypass Switch", WM8991_OUT3_4_MIXER,
+ WM8991_LI4O3_BIT, 1, 0),
+ SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8991_OUT3_4_MIXER,
+ WM8991_LPGAO3_BIT, 1, 0),
+};
+
+/* OUT4MIX */
+static const struct snd_kcontrol_new wm8991_dapm_out4mix_controls[] = {
+ SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8991_OUT3_4_MIXER,
+ WM8991_RPGAO4_BIT, 1, 0),
+ SOC_DAPM_SINGLE("OUT4MIX RIN4RXP Bypass Switch", WM8991_OUT3_4_MIXER,
+ WM8991_RI4O4_BIT, 1, 0),
+};
+
+/* SPKMIX */
+static const struct snd_kcontrol_new wm8991_dapm_spkmix_controls[] = {
+ SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8991_SPEAKER_MIXER,
+ WM8991_LI2SPK_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8991_SPEAKER_MIXER,
+ WM8991_LB2SPK_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8991_SPEAKER_MIXER,
+ WM8991_LOPGASPK_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8991_SPEAKER_MIXER,
+ WM8991_LDSPK_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8991_SPEAKER_MIXER,
+ WM8991_RDSPK_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8991_SPEAKER_MIXER,
+ WM8991_ROPGASPK_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8991_SPEAKER_MIXER,
+ WM8991_RL12ROP_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8991_SPEAKER_MIXER,
+ WM8991_RI2SPK_BIT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = {
+ /* Input Side */
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("LIN1"),
+ SND_SOC_DAPM_INPUT("LIN2"),
+ SND_SOC_DAPM_INPUT("LIN3"),
+ SND_SOC_DAPM_INPUT("LIN4RXN"),
+ SND_SOC_DAPM_INPUT("RIN3"),
+ SND_SOC_DAPM_INPUT("RIN4RXP"),
+ SND_SOC_DAPM_INPUT("RIN1"),
+ SND_SOC_DAPM_INPUT("RIN2"),
+ SND_SOC_DAPM_INPUT("Internal ADC Source"),
+
+ SND_SOC_DAPM_SUPPLY("INL", WM8991_POWER_MANAGEMENT_2,
+ WM8991_AINL_ENA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("INR", WM8991_POWER_MANAGEMENT_2,
+ WM8991_AINR_ENA_BIT, 0, NULL, 0),
+
+ /* DACs */
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8991_POWER_MANAGEMENT_2,
+ WM8991_ADCL_ENA_BIT, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8991_POWER_MANAGEMENT_2,
+ WM8991_ADCR_ENA_BIT, 0),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_MIXER("LIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN12_ENA_BIT,
+ 0, &wm8991_dapm_lin12_pga_controls[0],
+ ARRAY_SIZE(wm8991_dapm_lin12_pga_controls)),
+ SND_SOC_DAPM_MIXER("LIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN34_ENA_BIT,
+ 0, &wm8991_dapm_lin34_pga_controls[0],
+ ARRAY_SIZE(wm8991_dapm_lin34_pga_controls)),
+ SND_SOC_DAPM_MIXER("RIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN12_ENA_BIT,
+ 0, &wm8991_dapm_rin12_pga_controls[0],
+ ARRAY_SIZE(wm8991_dapm_rin12_pga_controls)),
+ SND_SOC_DAPM_MIXER("RIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN34_ENA_BIT,
+ 0, &wm8991_dapm_rin34_pga_controls[0],
+ ARRAY_SIZE(wm8991_dapm_rin34_pga_controls)),
+
+ /* INMIXL */
+ SND_SOC_DAPM_MIXER("INMIXL", SND_SOC_NOPM, 0, 0,
+ &wm8991_dapm_inmixl_controls[0],
+ ARRAY_SIZE(wm8991_dapm_inmixl_controls)),
+
+ /* AINLMUX */
+ SND_SOC_DAPM_MUX("AINLMUX", SND_SOC_NOPM, 0, 0,
+ &wm8991_dapm_ainlmux_controls),
+
+ /* INMIXR */
+ SND_SOC_DAPM_MIXER("INMIXR", SND_SOC_NOPM, 0, 0,
+ &wm8991_dapm_inmixr_controls[0],
+ ARRAY_SIZE(wm8991_dapm_inmixr_controls)),
+
+ /* AINRMUX */
+ SND_SOC_DAPM_MUX("AINRMUX", SND_SOC_NOPM, 0, 0,
+ &wm8991_dapm_ainrmux_controls),
+
+ /* Output Side */
+ /* DACs */
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8991_POWER_MANAGEMENT_3,
+ WM8991_DACL_ENA_BIT, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8991_POWER_MANAGEMENT_3,
+ WM8991_DACR_ENA_BIT, 0),
+
+ /* LOMIX */
+ SND_SOC_DAPM_MIXER_E("LOMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOMIX_ENA_BIT,
+ 0, &wm8991_dapm_lomix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_lomix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+ /* LONMIX */
+ SND_SOC_DAPM_MIXER("LONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LON_ENA_BIT, 0,
+ &wm8991_dapm_lonmix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_lonmix_controls)),
+
+ /* LOPMIX */
+ SND_SOC_DAPM_MIXER("LOPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOP_ENA_BIT, 0,
+ &wm8991_dapm_lopmix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_lopmix_controls)),
+
+ /* OUT3MIX */
+ SND_SOC_DAPM_MIXER("OUT3MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT3_ENA_BIT, 0,
+ &wm8991_dapm_out3mix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_out3mix_controls)),
+
+ /* SPKMIX */
+ SND_SOC_DAPM_MIXER_E("SPKMIX", WM8991_POWER_MANAGEMENT_1, WM8991_SPK_ENA_BIT, 0,
+ &wm8991_dapm_spkmix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_spkmix_controls), outmixer_event,
+ SND_SOC_DAPM_PRE_REG),
+
+ /* OUT4MIX */
+ SND_SOC_DAPM_MIXER("OUT4MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT4_ENA_BIT, 0,
+ &wm8991_dapm_out4mix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_out4mix_controls)),
+
+ /* ROPMIX */
+ SND_SOC_DAPM_MIXER("ROPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROP_ENA_BIT, 0,
+ &wm8991_dapm_ropmix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_ropmix_controls)),
+
+ /* RONMIX */
+ SND_SOC_DAPM_MIXER("RONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_RON_ENA_BIT, 0,
+ &wm8991_dapm_ronmix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_ronmix_controls)),
+
+ /* ROMIX */
+ SND_SOC_DAPM_MIXER_E("ROMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROMIX_ENA_BIT,
+ 0, &wm8991_dapm_romix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_romix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+ /* LOUT PGA */
+ SND_SOC_DAPM_PGA("LOUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_LOUT_ENA_BIT, 0,
+ NULL, 0),
+
+ /* ROUT PGA */
+ SND_SOC_DAPM_PGA("ROUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_ROUT_ENA_BIT, 0,
+ NULL, 0),
+
+ /* LOPGA */
+ SND_SOC_DAPM_PGA("LOPGA", WM8991_POWER_MANAGEMENT_3, WM8991_LOPGA_ENA_BIT, 0,
+ NULL, 0),
+
+ /* ROPGA */
+ SND_SOC_DAPM_PGA("ROPGA", WM8991_POWER_MANAGEMENT_3, WM8991_ROPGA_ENA_BIT, 0,
+ NULL, 0),
+
+ /* MICBIAS */
+ SND_SOC_DAPM_SUPPLY("MICBIAS", WM8991_POWER_MANAGEMENT_1,
+ WM8991_MICBIAS_ENA_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LON"),
+ SND_SOC_DAPM_OUTPUT("LOP"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("SPKN"),
+ SND_SOC_DAPM_OUTPUT("SPKP"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("OUT4"),
+ SND_SOC_DAPM_OUTPUT("ROP"),
+ SND_SOC_DAPM_OUTPUT("RON"),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+
+ SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
+};
+
+static const struct snd_soc_dapm_route wm8991_dapm_routes[] = {
+ /* Make DACs turn on when playing even if not mixed into any outputs */
+ {"Internal DAC Sink", NULL, "Left DAC"},
+ {"Internal DAC Sink", NULL, "Right DAC"},
+
+ /* Make ADCs turn on when recording even if not mixed from any inputs */
+ {"Left ADC", NULL, "Internal ADC Source"},
+ {"Right ADC", NULL, "Internal ADC Source"},
+
+ /* Input Side */
+ {"INMIXL", NULL, "INL"},
+ {"AINLMUX", NULL, "INL"},
+ {"INMIXR", NULL, "INR"},
+ {"AINRMUX", NULL, "INR"},
+ /* LIN12 PGA */
+ {"LIN12 PGA", "LIN1 Switch", "LIN1"},
+ {"LIN12 PGA", "LIN2 Switch", "LIN2"},
+ /* LIN34 PGA */
+ {"LIN34 PGA", "LIN3 Switch", "LIN3"},
+ {"LIN34 PGA", "LIN4 Switch", "LIN4RXN"},
+ /* INMIXL */
+ {"INMIXL", "Record Left Volume", "LOMIX"},
+ {"INMIXL", "LIN2 Volume", "LIN2"},
+ {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"},
+ {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"},
+ /* AINLMUX */
+ {"AINLMUX", "INMIXL Mix", "INMIXL"},
+ {"AINLMUX", "DIFFINL Mix", "LIN12 PGA"},
+ {"AINLMUX", "DIFFINL Mix", "LIN34 PGA"},
+ {"AINLMUX", "RXVOICE Mix", "LIN4RXN"},
+ {"AINLMUX", "RXVOICE Mix", "RIN4RXP"},
+ /* ADC */
+ {"Left ADC", NULL, "AINLMUX"},
+
+ /* RIN12 PGA */
+ {"RIN12 PGA", "RIN1 Switch", "RIN1"},
+ {"RIN12 PGA", "RIN2 Switch", "RIN2"},
+ /* RIN34 PGA */
+ {"RIN34 PGA", "RIN3 Switch", "RIN3"},
+ {"RIN34 PGA", "RIN4 Switch", "RIN4RXP"},
+ /* INMIXL */
+ {"INMIXR", "Record Right Volume", "ROMIX"},
+ {"INMIXR", "RIN2 Volume", "RIN2"},
+ {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"},
+ {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"},
+ /* AINRMUX */
+ {"AINRMUX", "INMIXR Mix", "INMIXR"},
+ {"AINRMUX", "DIFFINR Mix", "RIN12 PGA"},
+ {"AINRMUX", "DIFFINR Mix", "RIN34 PGA"},
+ {"AINRMUX", "RXVOICE Mix", "LIN4RXN"},
+ {"AINRMUX", "RXVOICE Mix", "RIN4RXP"},
+ /* ADC */
+ {"Right ADC", NULL, "AINRMUX"},
+
+ /* LOMIX */
+ {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"},
+ {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"},
+ {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"},
+ {"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"},
+ {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
+
+ /* ROMIX */
+ {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"},
+ {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"},
+ {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"},
+ {"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"},
+ {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"},
+
+ /* SPKMIX */
+ {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"},
+ {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"},
+ {"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"},
+ {"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"},
+ {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"},
+ {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"},
+ {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"},
+ {"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"},
+
+ /* LONMIX */
+ {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"},
+
+ /* LOPMIX */
+ {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"},
+
+ /* OUT3MIX */
+ {"OUT3MIX", "OUT3MIX LIN4RXN Bypass Switch", "LIN4RXN"},
+ {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"},
+
+ /* OUT4MIX */
+ {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"},
+ {"OUT4MIX", "OUT4MIX RIN4RXP Bypass Switch", "RIN4RXP"},
+
+ /* RONMIX */
+ {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"},
+
+ /* ROPMIX */
+ {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"},
+
+ /* Out Mixer PGAs */
+ {"LOPGA", NULL, "LOMIX"},
+ {"ROPGA", NULL, "ROMIX"},
+
+ {"LOUT PGA", NULL, "LOMIX"},
+ {"ROUT PGA", NULL, "ROMIX"},
+
+ /* Output Pins */
+ {"LON", NULL, "LONMIX"},
+ {"LOP", NULL, "LOPMIX"},
+ {"OUT", NULL, "OUT3MIX"},
+ {"LOUT", NULL, "LOUT PGA"},
+ {"SPKN", NULL, "SPKMIX"},
+ {"ROUT", NULL, "ROUT PGA"},
+ {"OUT4", NULL, "OUT4MIX"},
+ {"ROP", NULL, "ROPMIX"},
+ {"RON", NULL, "RONMIX"},
+};
+
+/* PLL divisors */
+struct _pll_div {
+ u32 div2;
+ u32 n;
+ u32 k;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 16) * 10)
+
+static void pll_factors(struct _pll_div *pll_div, unsigned int target,
+ unsigned int source)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div->div2 = 1;
+ Ndiv = target / source;
+ } else
+ pll_div->div2 = 0;
+
+ if ((Ndiv < 6) || (Ndiv > 12))
+ printk(KERN_WARNING
+ "WM8991 N value outwith recommended range! N = %d\n", Ndiv);
+
+ pll_div->n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div->k = K;
+}
+
+static int wm8991_set_dai_pll(struct snd_soc_dai *codec_dai,
+ int pll_id, int src, unsigned int freq_in, unsigned int freq_out)
+{
+ u16 reg;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct _pll_div pll_div;
+
+ if (freq_in && freq_out) {
+ pll_factors(&pll_div, freq_out * 4, freq_in);
+
+ /* Turn on PLL */
+ reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+ reg |= WM8991_PLL_ENA;
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg);
+
+ /* sysclk comes from PLL */
+ reg = snd_soc_read(codec, WM8991_CLOCKING_2);
+ snd_soc_write(codec, WM8991_CLOCKING_2, reg | WM8991_SYSCLK_SRC);
+
+ /* set up N , fractional mode and pre-divisor if necessary */
+ snd_soc_write(codec, WM8991_PLL1, pll_div.n | WM8991_SDM |
+ (pll_div.div2 ? WM8991_PRESCALE : 0));
+ snd_soc_write(codec, WM8991_PLL2, (u8)(pll_div.k>>8));
+ snd_soc_write(codec, WM8991_PLL3, (u8)(pll_div.k & 0xFF));
+ } else {
+ /* Turn on PLL */
+ reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+ reg &= ~WM8991_PLL_ENA;
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg);
+ }
+ return 0;
+}
+
+/*
+ * Set's ADC and Voice DAC format.
+ */
+static int wm8991_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 audio1, audio3;
+
+ audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1);
+ audio3 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_3);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ audio3 &= ~WM8991_AIF_MSTR1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ audio3 |= WM8991_AIF_MSTR1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ audio1 &= ~WM8991_AIF_FMT_MASK;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ audio1 |= WM8991_AIF_TMF_I2S;
+ audio1 &= ~WM8991_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audio1 |= WM8991_AIF_TMF_RIGHTJ;
+ audio1 &= ~WM8991_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audio1 |= WM8991_AIF_TMF_LEFTJ;
+ audio1 &= ~WM8991_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audio1 |= WM8991_AIF_TMF_DSP;
+ audio1 &= ~WM8991_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audio1 |= WM8991_AIF_TMF_DSP | WM8991_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1);
+ snd_soc_write(codec, WM8991_AUDIO_INTERFACE_3, audio3);
+ return 0;
+}
+
+static int wm8991_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8991_MCLK_DIV:
+ reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+ ~WM8991_MCLK_DIV_MASK;
+ snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+ break;
+ case WM8991_DACCLK_DIV:
+ reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+ ~WM8991_DAC_CLKDIV_MASK;
+ snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+ break;
+ case WM8991_ADCCLK_DIV:
+ reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+ ~WM8991_ADC_CLKDIV_MASK;
+ snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+ break;
+ case WM8991_BCLK_DIV:
+ reg = snd_soc_read(codec, WM8991_CLOCKING_1) &
+ ~WM8991_BCLK_DIV_MASK;
+ snd_soc_write(codec, WM8991_CLOCKING_1, reg | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8991_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1);
+
+ audio1 &= ~WM8991_AIF_WL_MASK;
+ /* bit size */
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ audio1 |= WM8991_AIF_WL_20BITS;
+ break;
+ case 24:
+ audio1 |= WM8991_AIF_WL_24BITS;
+ break;
+ case 32:
+ audio1 |= WM8991_AIF_WL_32BITS;
+ break;
+ }
+
+ snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1);
+ return 0;
+}
+
+static int wm8991_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 val;
+
+ val = snd_soc_read(codec, WM8991_DAC_CTRL) & ~WM8991_DAC_MUTE;
+ if (mute)
+ snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
+ else
+ snd_soc_write(codec, WM8991_DAC_CTRL, val);
+ return 0;
+}
+
+static int wm8991_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8991_priv *wm8991 = snd_soc_codec_get_drvdata(codec);
+ u16 val;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID=2*50k */
+ val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) &
+ ~WM8991_VMID_MODE_MASK;
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x2);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ regcache_sync(wm8991->regmap);
+ /* Enable all output discharge bits */
+ snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
+ WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
+ WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
+ WM8991_DIS_ROUT);
+
+ /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
+ snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ WM8991_BUFDCOPEN | WM8991_POBCTRL |
+ WM8991_VMIDTOG);
+
+ /* Delay to allow output caps to discharge */
+ msleep(300);
+
+ /* Disable VMIDTOG */
+ snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ WM8991_BUFDCOPEN | WM8991_POBCTRL);
+
+ /* disable all output discharge bits */
+ snd_soc_write(codec, WM8991_ANTIPOP1, 0);
+
+ /* Enable outputs */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1b00);
+
+ msleep(50);
+
+ /* Enable VMID at 2x50k */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f02);
+
+ msleep(100);
+
+ /* Enable VREF */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03);
+
+ msleep(600);
+
+ /* Enable BUFIOEN */
+ snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ WM8991_BUFDCOPEN | WM8991_POBCTRL |
+ WM8991_BUFIOEN);
+
+ /* Disable outputs */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x3);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_BUFIOEN);
+ }
+
+ /* VMID=2*250k */
+ val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) &
+ ~WM8991_VMID_MODE_MASK;
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x4);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Enable POBCTRL and SOFT_ST */
+ snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ WM8991_POBCTRL | WM8991_BUFIOEN);
+
+ /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
+ snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ WM8991_BUFDCOPEN | WM8991_POBCTRL |
+ WM8991_BUFIOEN);
+
+ /* mute DAC */
+ val = snd_soc_read(codec, WM8991_DAC_CTRL);
+ snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
+
+ /* Enable any disabled outputs */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03);
+
+ /* Disable VMID */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f01);
+
+ msleep(300);
+
+ /* Enable all output discharge bits */
+ snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
+ WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
+ WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
+ WM8991_DIS_ROUT);
+
+ /* Disable VREF */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x0);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ snd_soc_write(codec, WM8991_ANTIPOP2, 0x0);
+ regcache_mark_dirty(wm8991->regmap);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define WM8991_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops wm8991_ops = {
+ .hw_params = wm8991_hw_params,
+ .digital_mute = wm8991_mute,
+ .set_fmt = wm8991_set_dai_fmt,
+ .set_clkdiv = wm8991_set_dai_clkdiv,
+ .set_pll = wm8991_set_dai_pll
+};
+
+/*
+ * The WM8991 supports 2 different and mutually exclusive DAI
+ * configurations.
+ *
+ * 1. ADC/DAC on Primary Interface
+ * 2. ADC on Primary Interface/DAC on secondary
+ */
+static struct snd_soc_dai_driver wm8991_dai = {
+ /* ADC/DAC on primary */
+ .name = "wm8991",
+ .id = 1,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = WM8991_FORMATS
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = WM8991_FORMATS
+ },
+ .ops = &wm8991_ops
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8991 = {
+ .set_bias_level = wm8991_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = wm8991_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8991_snd_controls),
+ .dapm_widgets = wm8991_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8991_dapm_widgets),
+ .dapm_routes = wm8991_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8991_dapm_routes),
+};
+
+static const struct regmap_config wm8991_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = WM8991_PLL3,
+ .volatile_reg = wm8991_volatile,
+ .reg_defaults = wm8991_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8991_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int wm8991_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8991_priv *wm8991;
+ unsigned int val;
+ int ret;
+
+ wm8991 = devm_kzalloc(&i2c->dev, sizeof(*wm8991), GFP_KERNEL);
+ if (!wm8991)
+ return -ENOMEM;
+
+ wm8991->regmap = devm_regmap_init_i2c(i2c, &wm8991_regmap);
+ if (IS_ERR(wm8991->regmap))
+ return PTR_ERR(wm8991->regmap);
+
+ i2c_set_clientdata(i2c, wm8991);
+
+ ret = regmap_read(wm8991->regmap, WM8991_RESET, &val);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to read device ID: %d\n", ret);
+ return ret;
+ }
+ if (val != 0x8991) {
+ dev_err(&i2c->dev, "Device with ID %x is not a WM8991\n", val);
+ return -EINVAL;
+ }
+
+ ret = regmap_write(wm8991->regmap, WM8991_RESET, 0);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
+
+ regmap_update_bits(wm8991->regmap, WM8991_AUDIO_INTERFACE_4,
+ WM8991_ALRCGPIO1, WM8991_ALRCGPIO1);
+
+ regmap_update_bits(wm8991->regmap, WM8991_GPIO1_GPIO2,
+ WM8991_GPIO1_SEL_MASK, 1);
+
+ regmap_update_bits(wm8991->regmap, WM8991_POWER_MANAGEMENT_1,
+ WM8991_VREF_ENA | WM8991_VMID_MODE_MASK,
+ WM8991_VREF_ENA | WM8991_VMID_MODE_MASK);
+
+ regmap_update_bits(wm8991->regmap, WM8991_POWER_MANAGEMENT_2,
+ WM8991_OPCLK_ENA, WM8991_OPCLK_ENA);
+
+ regmap_write(wm8991->regmap, WM8991_DAC_CTRL, 0);
+ regmap_write(wm8991->regmap, WM8991_LEFT_OUTPUT_VOLUME,
+ 0x50 | (1<<8));
+ regmap_write(wm8991->regmap, WM8991_RIGHT_OUTPUT_VOLUME,
+ 0x50 | (1<<8));
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8991, &wm8991_dai, 1);
+
+ return ret;
+}
+
+static int wm8991_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm8991_i2c_id[] = {
+ { "wm8991", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id);
+
+static struct i2c_driver wm8991_i2c_driver = {
+ .driver = {
+ .name = "wm8991",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8991_i2c_probe,
+ .remove = wm8991_i2c_remove,
+ .id_table = wm8991_i2c_id,
+};
+
+module_i2c_driver(wm8991_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8991 driver");
+MODULE_AUTHOR("Graeme Gregory");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8991.h b/kernel/sound/soc/codecs/wm8991.h
new file mode 100644
index 000000000..08ed38330
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8991.h
@@ -0,0 +1,819 @@
+/*
+ * wm8991.h -- audio driver for WM8991
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * graeme.gregory@wolfsonmicro.com or linux@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 _WM8991_H
+#define _WM8991_H
+
+/*
+ * Register values.
+ */
+#define WM8991_RESET 0x00
+#define WM8991_POWER_MANAGEMENT_1 0x01
+#define WM8991_POWER_MANAGEMENT_2 0x02
+#define WM8991_POWER_MANAGEMENT_3 0x03
+#define WM8991_AUDIO_INTERFACE_1 0x04
+#define WM8991_AUDIO_INTERFACE_2 0x05
+#define WM8991_CLOCKING_1 0x06
+#define WM8991_CLOCKING_2 0x07
+#define WM8991_AUDIO_INTERFACE_3 0x08
+#define WM8991_AUDIO_INTERFACE_4 0x09
+#define WM8991_DAC_CTRL 0x0A
+#define WM8991_LEFT_DAC_DIGITAL_VOLUME 0x0B
+#define WM8991_RIGHT_DAC_DIGITAL_VOLUME 0x0C
+#define WM8991_DIGITAL_SIDE_TONE 0x0D
+#define WM8991_ADC_CTRL 0x0E
+#define WM8991_LEFT_ADC_DIGITAL_VOLUME 0x0F
+#define WM8991_RIGHT_ADC_DIGITAL_VOLUME 0x10
+#define WM8991_GPIO_CTRL_1 0x12
+#define WM8991_GPIO1_GPIO2 0x13
+#define WM8991_GPIO3_GPIO4 0x14
+#define WM8991_GPIO5_GPIO6 0x15
+#define WM8991_GPIOCTRL_2 0x16
+#define WM8991_GPIO_POL 0x17
+#define WM8991_LEFT_LINE_INPUT_1_2_VOLUME 0x18
+#define WM8991_LEFT_LINE_INPUT_3_4_VOLUME 0x19
+#define WM8991_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A
+#define WM8991_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B
+#define WM8991_LEFT_OUTPUT_VOLUME 0x1C
+#define WM8991_RIGHT_OUTPUT_VOLUME 0x1D
+#define WM8991_LINE_OUTPUTS_VOLUME 0x1E
+#define WM8991_OUT3_4_VOLUME 0x1F
+#define WM8991_LEFT_OPGA_VOLUME 0x20
+#define WM8991_RIGHT_OPGA_VOLUME 0x21
+#define WM8991_SPEAKER_VOLUME 0x22
+#define WM8991_CLASSD1 0x23
+#define WM8991_CLASSD3 0x25
+#define WM8991_INPUT_MIXER1 0x27
+#define WM8991_INPUT_MIXER2 0x28
+#define WM8991_INPUT_MIXER3 0x29
+#define WM8991_INPUT_MIXER4 0x2A
+#define WM8991_INPUT_MIXER5 0x2B
+#define WM8991_INPUT_MIXER6 0x2C
+#define WM8991_OUTPUT_MIXER1 0x2D
+#define WM8991_OUTPUT_MIXER2 0x2E
+#define WM8991_OUTPUT_MIXER3 0x2F
+#define WM8991_OUTPUT_MIXER4 0x30
+#define WM8991_OUTPUT_MIXER5 0x31
+#define WM8991_OUTPUT_MIXER6 0x32
+#define WM8991_OUT3_4_MIXER 0x33
+#define WM8991_LINE_MIXER1 0x34
+#define WM8991_LINE_MIXER2 0x35
+#define WM8991_SPEAKER_MIXER 0x36
+#define WM8991_ADDITIONAL_CONTROL 0x37
+#define WM8991_ANTIPOP1 0x38
+#define WM8991_ANTIPOP2 0x39
+#define WM8991_MICBIAS 0x3A
+#define WM8991_PLL1 0x3C
+#define WM8991_PLL2 0x3D
+#define WM8991_PLL3 0x3E
+
+#define WM8991_REGISTER_COUNT 60
+#define WM8991_MAX_REGISTER 0x3F
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset
+ */
+#define WM8991_SW_RESET_CHIP_ID_MASK 0xFFFF /* SW_RESET_CHIP_ID - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8991_SPK_ENA 0x1000 /* SPK_ENA */
+#define WM8991_SPK_ENA_BIT 12
+#define WM8991_OUT3_ENA 0x0800 /* OUT3_ENA */
+#define WM8991_OUT3_ENA_BIT 11
+#define WM8991_OUT4_ENA 0x0400 /* OUT4_ENA */
+#define WM8991_OUT4_ENA_BIT 10
+#define WM8991_LOUT_ENA 0x0200 /* LOUT_ENA */
+#define WM8991_LOUT_ENA_BIT 9
+#define WM8991_ROUT_ENA 0x0100 /* ROUT_ENA */
+#define WM8991_ROUT_ENA_BIT 8
+#define WM8991_MICBIAS_ENA 0x0010 /* MICBIAS_ENA */
+#define WM8991_MICBIAS_ENA_BIT 4
+#define WM8991_VMID_MODE_MASK 0x0006 /* VMID_MODE - [2:1] */
+#define WM8991_VREF_ENA 0x0001 /* VREF_ENA */
+#define WM8991_VREF_ENA_BIT 0
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8991_PLL_ENA 0x8000 /* PLL_ENA */
+#define WM8991_PLL_ENA_BIT 15
+#define WM8991_TSHUT_ENA 0x4000 /* TSHUT_ENA */
+#define WM8991_TSHUT_ENA_BIT 14
+#define WM8991_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */
+#define WM8991_TSHUT_OPDIS_BIT 13
+#define WM8991_OPCLK_ENA 0x0800 /* OPCLK_ENA */
+#define WM8991_OPCLK_ENA_BIT 11
+#define WM8991_AINL_ENA 0x0200 /* AINL_ENA */
+#define WM8991_AINL_ENA_BIT 9
+#define WM8991_AINR_ENA 0x0100 /* AINR_ENA */
+#define WM8991_AINR_ENA_BIT 8
+#define WM8991_LIN34_ENA 0x0080 /* LIN34_ENA */
+#define WM8991_LIN34_ENA_BIT 7
+#define WM8991_LIN12_ENA 0x0040 /* LIN12_ENA */
+#define WM8991_LIN12_ENA_BIT 6
+#define WM8991_RIN34_ENA 0x0020 /* RIN34_ENA */
+#define WM8991_RIN34_ENA_BIT 5
+#define WM8991_RIN12_ENA 0x0010 /* RIN12_ENA */
+#define WM8991_RIN12_ENA_BIT 4
+#define WM8991_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8991_ADCL_ENA_BIT 1
+#define WM8991_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8991_ADCR_ENA_BIT 0
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8991_LON_ENA 0x2000 /* LON_ENA */
+#define WM8991_LON_ENA_BIT 13
+#define WM8991_LOP_ENA 0x1000 /* LOP_ENA */
+#define WM8991_LOP_ENA_BIT 12
+#define WM8991_RON_ENA 0x0800 /* RON_ENA */
+#define WM8991_RON_ENA_BIT 11
+#define WM8991_ROP_ENA 0x0400 /* ROP_ENA */
+#define WM8991_ROP_ENA_BIT 10
+#define WM8991_LOPGA_ENA 0x0080 /* LOPGA_ENA */
+#define WM8991_LOPGA_ENA_BIT 7
+#define WM8991_ROPGA_ENA 0x0040 /* ROPGA_ENA */
+#define WM8991_ROPGA_ENA_BIT 6
+#define WM8991_LOMIX_ENA 0x0020 /* LOMIX_ENA */
+#define WM8991_LOMIX_ENA_BIT 5
+#define WM8991_ROMIX_ENA 0x0010 /* ROMIX_ENA */
+#define WM8991_ROMIX_ENA_BIT 4
+#define WM8991_DACL_ENA 0x0002 /* DACL_ENA */
+#define WM8991_DACL_ENA_BIT 1
+#define WM8991_DACR_ENA 0x0001 /* DACR_ENA */
+#define WM8991_DACR_ENA_BIT 0
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8991_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */
+#define WM8991_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */
+#define WM8991_AIFADC_TDM 0x2000 /* AIFADC_TDM */
+#define WM8991_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */
+#define WM8991_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */
+#define WM8991_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */
+#define WM8991_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */
+#define WM8991_AIF_WL_16BITS (0 << 5)
+#define WM8991_AIF_WL_20BITS (1 << 5)
+#define WM8991_AIF_WL_24BITS (2 << 5)
+#define WM8991_AIF_WL_32BITS (3 << 5)
+#define WM8991_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */
+#define WM8991_AIF_TMF_RIGHTJ (0 << 3)
+#define WM8991_AIF_TMF_LEFTJ (1 << 3)
+#define WM8991_AIF_TMF_I2S (2 << 3)
+#define WM8991_AIF_TMF_DSP (3 << 3)
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8991_DACL_SRC 0x8000 /* DACL_SRC */
+#define WM8991_DACR_SRC 0x4000 /* DACR_SRC */
+#define WM8991_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */
+#define WM8991_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8991_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST - [11:10] */
+#define WM8991_DAC_COMP 0x0010 /* DAC_COMP */
+#define WM8991_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */
+#define WM8991_ADC_COMP 0x0004 /* ADC_COMP */
+#define WM8991_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */
+#define WM8991_LOOPBACK 0x0001 /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking (1)
+ */
+#define WM8991_TOCLK_RATE 0x8000 /* TOCLK_RATE */
+#define WM8991_TOCLK_ENA 0x4000 /* TOCLK_ENA */
+#define WM8991_OPCLKDIV_MASK 0x1E00 /* OPCLKDIV - [12:9] */
+#define WM8991_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */
+#define WM8991_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */
+#define WM8991_BCLK_DIV_1 (0x0 << 1)
+#define WM8991_BCLK_DIV_1_5 (0x1 << 1)
+#define WM8991_BCLK_DIV_2 (0x2 << 1)
+#define WM8991_BCLK_DIV_3 (0x3 << 1)
+#define WM8991_BCLK_DIV_4 (0x4 << 1)
+#define WM8991_BCLK_DIV_5_5 (0x5 << 1)
+#define WM8991_BCLK_DIV_6 (0x6 << 1)
+#define WM8991_BCLK_DIV_8 (0x7 << 1)
+#define WM8991_BCLK_DIV_11 (0x8 << 1)
+#define WM8991_BCLK_DIV_12 (0x9 << 1)
+#define WM8991_BCLK_DIV_16 (0xA << 1)
+#define WM8991_BCLK_DIV_22 (0xB << 1)
+#define WM8991_BCLK_DIV_24 (0xC << 1)
+#define WM8991_BCLK_DIV_32 (0xD << 1)
+#define WM8991_BCLK_DIV_44 (0xE << 1)
+#define WM8991_BCLK_DIV_48 (0xF << 1)
+
+/*
+ * R7 (0x07) - Clocking (2)
+ */
+#define WM8991_MCLK_SRC 0x8000 /* MCLK_SRC */
+#define WM8991_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */
+#define WM8991_CLK_FORCE 0x2000 /* CLK_FORCE */
+#define WM8991_MCLK_DIV_MASK 0x1800 /* MCLK_DIV - [12:11] */
+#define WM8991_MCLK_DIV_1 (0 << 11)
+#define WM8991_MCLK_DIV_2 ( 2 << 11)
+#define WM8991_MCLK_INV 0x0400 /* MCLK_INV */
+#define WM8991_ADC_CLKDIV_MASK 0x00E0 /* ADC_CLKDIV - [7:5] */
+#define WM8991_ADC_CLKDIV_1 (0 << 5)
+#define WM8991_ADC_CLKDIV_1_5 (1 << 5)
+#define WM8991_ADC_CLKDIV_2 (2 << 5)
+#define WM8991_ADC_CLKDIV_3 (3 << 5)
+#define WM8991_ADC_CLKDIV_4 (4 << 5)
+#define WM8991_ADC_CLKDIV_5_5 (5 << 5)
+#define WM8991_ADC_CLKDIV_6 (6 << 5)
+#define WM8991_DAC_CLKDIV_MASK 0x001C /* DAC_CLKDIV - [4:2] */
+#define WM8991_DAC_CLKDIV_1 (0 << 2)
+#define WM8991_DAC_CLKDIV_1_5 (1 << 2)
+#define WM8991_DAC_CLKDIV_2 (2 << 2)
+#define WM8991_DAC_CLKDIV_3 (3 << 2)
+#define WM8991_DAC_CLKDIV_4 (4 << 2)
+#define WM8991_DAC_CLKDIV_5_5 (5 << 2)
+#define WM8991_DAC_CLKDIV_6 (6 << 2)
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8991_AIF_MSTR1 0x8000 /* AIF_MSTR1 */
+#define WM8991_AIF_MSTR2 0x4000 /* AIF_MSTR2 */
+#define WM8991_AIF_SEL 0x2000 /* AIF_SEL */
+#define WM8991_ADCLRC_DIR 0x0800 /* ADCLRC_DIR */
+#define WM8991_ADCLRC_RATE_MASK 0x07FF /* ADCLRC_RATE - [10:0] */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8991_ALRCGPIO1 0x8000 /* ALRCGPIO1 */
+#define WM8991_ALRCBGPIO6 0x4000 /* ALRCBGPIO6 */
+#define WM8991_AIF_TRIS 0x2000 /* AIF_TRIS */
+#define WM8991_DACLRC_DIR 0x0800 /* DACLRC_DIR */
+#define WM8991_DACLRC_RATE_MASK 0x07FF /* DACLRC_RATE - [10:0] */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8991_AIF_LRCLKRATE 0x0400 /* AIF_LRCLKRATE */
+#define WM8991_DAC_MONO 0x0200 /* DAC_MONO */
+#define WM8991_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */
+#define WM8991_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */
+#define WM8991_DAC_MUTEMODE 0x0040 /* DAC_MUTEMODE */
+#define WM8991_DEEMP_MASK 0x0030 /* DEEMP - [5:4] */
+#define WM8991_DAC_MUTE 0x0004 /* DAC_MUTE */
+#define WM8991_DACL_DATINV 0x0002 /* DACL_DATINV */
+#define WM8991_DACR_DATINV 0x0001 /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8991_DAC_VU 0x0100 /* DAC_VU */
+#define WM8991_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */
+#define WM8991_DACL_VOL_SHIFT 0
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8991_DAC_VU 0x0100 /* DAC_VU */
+#define WM8991_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */
+#define WM8991_DACR_VOL_SHIFT 0
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8991_ADCL_DAC_SVOL_MASK 0x0F /* ADCL_DAC_SVOL - [12:9] */
+#define WM8991_ADCL_DAC_SVOL_SHIFT 9
+#define WM8991_ADCR_DAC_SVOL_MASK 0x0F /* ADCR_DAC_SVOL - [8:5] */
+#define WM8991_ADCR_DAC_SVOL_SHIFT 5
+#define WM8991_ADC_TO_DACL_MASK 0x03 /* ADC_TO_DACL - [3:2] */
+#define WM8991_ADC_TO_DACL_SHIFT 2
+#define WM8991_ADC_TO_DACR_MASK 0x03 /* ADC_TO_DACR - [1:0] */
+#define WM8991_ADC_TO_DACR_SHIFT 0
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8991_ADC_HPF_ENA 0x0100 /* ADC_HPF_ENA */
+#define WM8991_ADC_HPF_ENA_BIT 8
+#define WM8991_ADC_HPF_CUT_MASK 0x03 /* ADC_HPF_CUT - [6:5] */
+#define WM8991_ADC_HPF_CUT_SHIFT 5
+#define WM8991_ADCL_DATINV 0x0002 /* ADCL_DATINV */
+#define WM8991_ADCL_DATINV_BIT 1
+#define WM8991_ADCR_DATINV 0x0001 /* ADCR_DATINV */
+#define WM8991_ADCR_DATINV_BIT 0
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8991_ADC_VU 0x0100 /* ADC_VU */
+#define WM8991_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */
+#define WM8991_ADCL_VOL_SHIFT 0
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8991_ADC_VU 0x0100 /* ADC_VU */
+#define WM8991_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */
+#define WM8991_ADCR_VOL_SHIFT 0
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8991_IRQ 0x1000 /* IRQ */
+#define WM8991_TEMPOK 0x0800 /* TEMPOK */
+#define WM8991_MICSHRT 0x0400 /* MICSHRT */
+#define WM8991_MICDET 0x0200 /* MICDET */
+#define WM8991_PLL_LCK 0x0100 /* PLL_LCK */
+#define WM8991_GPI8_STATUS 0x0080 /* GPI8_STATUS */
+#define WM8991_GPI7_STATUS 0x0040 /* GPI7_STATUS */
+#define WM8991_GPIO6_STATUS 0x0020 /* GPIO6_STATUS */
+#define WM8991_GPIO5_STATUS 0x0010 /* GPIO5_STATUS */
+#define WM8991_GPIO4_STATUS 0x0008 /* GPIO4_STATUS */
+#define WM8991_GPIO3_STATUS 0x0004 /* GPIO3_STATUS */
+#define WM8991_GPIO2_STATUS 0x0002 /* GPIO2_STATUS */
+#define WM8991_GPIO1_STATUS 0x0001 /* GPIO1_STATUS */
+
+/*
+ * R19 (0x13) - GPIO1 & GPIO2
+ */
+#define WM8991_GPIO2_DEB_ENA 0x8000 /* GPIO2_DEB_ENA */
+#define WM8991_GPIO2_IRQ_ENA 0x4000 /* GPIO2_IRQ_ENA */
+#define WM8991_GPIO2_PU 0x2000 /* GPIO2_PU */
+#define WM8991_GPIO2_PD 0x1000 /* GPIO2_PD */
+#define WM8991_GPIO2_SEL_MASK 0x0F00 /* GPIO2_SEL - [11:8] */
+#define WM8991_GPIO1_DEB_ENA 0x0080 /* GPIO1_DEB_ENA */
+#define WM8991_GPIO1_IRQ_ENA 0x0040 /* GPIO1_IRQ_ENA */
+#define WM8991_GPIO1_PU 0x0020 /* GPIO1_PU */
+#define WM8991_GPIO1_PD 0x0010 /* GPIO1_PD */
+#define WM8991_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - GPIO3 & GPIO4
+ */
+#define WM8991_GPIO4_DEB_ENA 0x8000 /* GPIO4_DEB_ENA */
+#define WM8991_GPIO4_IRQ_ENA 0x4000 /* GPIO4_IRQ_ENA */
+#define WM8991_GPIO4_PU 0x2000 /* GPIO4_PU */
+#define WM8991_GPIO4_PD 0x1000 /* GPIO4_PD */
+#define WM8991_GPIO4_SEL_MASK 0x0F00 /* GPIO4_SEL - [11:8] */
+#define WM8991_GPIO3_DEB_ENA 0x0080 /* GPIO3_DEB_ENA */
+#define WM8991_GPIO3_IRQ_ENA 0x0040 /* GPIO3_IRQ_ENA */
+#define WM8991_GPIO3_PU 0x0020 /* GPIO3_PU */
+#define WM8991_GPIO3_PD 0x0010 /* GPIO3_PD */
+#define WM8991_GPIO3_SEL_MASK 0x000F /* GPIO3_SEL - [3:0] */
+
+/*
+ * R21 (0x15) - GPIO5 & GPIO6
+ */
+#define WM8991_GPIO6_DEB_ENA 0x8000 /* GPIO6_DEB_ENA */
+#define WM8991_GPIO6_IRQ_ENA 0x4000 /* GPIO6_IRQ_ENA */
+#define WM8991_GPIO6_PU 0x2000 /* GPIO6_PU */
+#define WM8991_GPIO6_PD 0x1000 /* GPIO6_PD */
+#define WM8991_GPIO6_SEL_MASK 0x0F00 /* GPIO6_SEL - [11:8] */
+#define WM8991_GPIO5_DEB_ENA 0x0080 /* GPIO5_DEB_ENA */
+#define WM8991_GPIO5_IRQ_ENA 0x0040 /* GPIO5_IRQ_ENA */
+#define WM8991_GPIO5_PU 0x0020 /* GPIO5_PU */
+#define WM8991_GPIO5_PD 0x0010 /* GPIO5_PD */
+#define WM8991_GPIO5_SEL_MASK 0x000F /* GPIO5_SEL - [3:0] */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8991_RD_3W_ENA 0x8000 /* RD_3W_ENA */
+#define WM8991_MODE_3W4W 0x4000 /* MODE_3W4W */
+#define WM8991_TEMPOK_IRQ_ENA 0x0800 /* TEMPOK_IRQ_ENA */
+#define WM8991_MICSHRT_IRQ_ENA 0x0400 /* MICSHRT_IRQ_ENA */
+#define WM8991_MICDET_IRQ_ENA 0x0200 /* MICDET_IRQ_ENA */
+#define WM8991_PLL_LCK_IRQ_ENA 0x0100 /* PLL_LCK_IRQ_ENA */
+#define WM8991_GPI8_DEB_ENA 0x0080 /* GPI8_DEB_ENA */
+#define WM8991_GPI8_IRQ_ENA 0x0040 /* GPI8_IRQ_ENA */
+#define WM8991_GPI8_ENA 0x0010 /* GPI8_ENA */
+#define WM8991_GPI7_DEB_ENA 0x0008 /* GPI7_DEB_ENA */
+#define WM8991_GPI7_IRQ_ENA 0x0004 /* GPI7_IRQ_ENA */
+#define WM8991_GPI7_ENA 0x0001 /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8991_IRQ_INV 0x1000 /* IRQ_INV */
+#define WM8991_TEMPOK_POL 0x0800 /* TEMPOK_POL */
+#define WM8991_MICSHRT_POL 0x0400 /* MICSHRT_POL */
+#define WM8991_MICDET_POL 0x0200 /* MICDET_POL */
+#define WM8991_PLL_LCK_POL 0x0100 /* PLL_LCK_POL */
+#define WM8991_GPI8_POL 0x0080 /* GPI8_POL */
+#define WM8991_GPI7_POL 0x0040 /* GPI7_POL */
+#define WM8991_GPIO6_POL 0x0020 /* GPIO6_POL */
+#define WM8991_GPIO5_POL 0x0010 /* GPIO5_POL */
+#define WM8991_GPIO4_POL 0x0008 /* GPIO4_POL */
+#define WM8991_GPIO3_POL 0x0004 /* GPIO3_POL */
+#define WM8991_GPIO2_POL 0x0002 /* GPIO2_POL */
+#define WM8991_GPIO1_POL 0x0001 /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8991_IPVU 0x0100 /* IPVU */
+#define WM8991_LI12MUTE 0x0080 /* LI12MUTE */
+#define WM8991_LI12MUTE_BIT 7
+#define WM8991_LI12ZC 0x0040 /* LI12ZC */
+#define WM8991_LI12ZC_BIT 6
+#define WM8991_LIN12VOL_MASK 0x001F /* LIN12VOL - [4:0] */
+#define WM8991_LIN12VOL_SHIFT 0
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8991_IPVU 0x0100 /* IPVU */
+#define WM8991_LI34MUTE 0x0080 /* LI34MUTE */
+#define WM8991_LI34MUTE_BIT 7
+#define WM8991_LI34ZC 0x0040 /* LI34ZC */
+#define WM8991_LI34ZC_BIT 6
+#define WM8991_LIN34VOL_MASK 0x001F /* LIN34VOL - [4:0] */
+#define WM8991_LIN34VOL_SHIFT 0
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8991_IPVU 0x0100 /* IPVU */
+#define WM8991_RI12MUTE 0x0080 /* RI12MUTE */
+#define WM8991_RI12MUTE_BIT 7
+#define WM8991_RI12ZC 0x0040 /* RI12ZC */
+#define WM8991_RI12ZC_BIT 6
+#define WM8991_RIN12VOL_MASK 0x001F /* RIN12VOL - [4:0] */
+#define WM8991_RIN12VOL_SHIFT 0
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8991_IPVU 0x0100 /* IPVU */
+#define WM8991_RI34MUTE 0x0080 /* RI34MUTE */
+#define WM8991_RI34MUTE_BIT 7
+#define WM8991_RI34ZC 0x0040 /* RI34ZC */
+#define WM8991_RI34ZC_BIT 6
+#define WM8991_RIN34VOL_MASK 0x001F /* RIN34VOL - [4:0] */
+#define WM8991_RIN34VOL_SHIFT 0
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8991_OPVU 0x0100 /* OPVU */
+#define WM8991_LOZC 0x0080 /* LOZC */
+#define WM8991_LOZC_BIT 7
+#define WM8991_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */
+#define WM8991_LOUTVOL_SHIFT 0
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8991_OPVU 0x0100 /* OPVU */
+#define WM8991_ROZC 0x0080 /* ROZC */
+#define WM8991_ROZC_BIT 7
+#define WM8991_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */
+#define WM8991_ROUTVOL_SHIFT 0
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8991_LONMUTE 0x0040 /* LONMUTE */
+#define WM8991_LONMUTE_BIT 6
+#define WM8991_LOPMUTE 0x0020 /* LOPMUTE */
+#define WM8991_LOPMUTE_BIT 5
+#define WM8991_LOATTN 0x0010 /* LOATTN */
+#define WM8991_LOATTN_BIT 4
+#define WM8991_RONMUTE 0x0004 /* RONMUTE */
+#define WM8991_RONMUTE_BIT 2
+#define WM8991_ROPMUTE 0x0002 /* ROPMUTE */
+#define WM8991_ROPMUTE_BIT 1
+#define WM8991_ROATTN 0x0001 /* ROATTN */
+#define WM8991_ROATTN_BIT 0
+
+/*
+ * R31 (0x1F) - Out3/4 Volume
+ */
+#define WM8991_OUT3MUTE 0x0020 /* OUT3MUTE */
+#define WM8991_OUT3MUTE_BIT 5
+#define WM8991_OUT3ATTN 0x0010 /* OUT3ATTN */
+#define WM8991_OUT3ATTN_BIT 4
+#define WM8991_OUT4MUTE 0x0002 /* OUT4MUTE */
+#define WM8991_OUT4MUTE_BIT 1
+#define WM8991_OUT4ATTN 0x0001 /* OUT4ATTN */
+#define WM8991_OUT4ATTN_BIT 0
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8991_OPVU 0x0100 /* OPVU */
+#define WM8991_LOPGAZC 0x0080 /* LOPGAZC */
+#define WM8991_LOPGAZC_BIT 7
+#define WM8991_LOPGAVOL_MASK 0x007F /* LOPGAVOL - [6:0] */
+#define WM8991_LOPGAVOL_SHIFT 0
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8991_OPVU 0x0100 /* OPVU */
+#define WM8991_ROPGAZC 0x0080 /* ROPGAZC */
+#define WM8991_ROPGAZC_BIT 7
+#define WM8991_ROPGAVOL_MASK 0x007F /* ROPGAVOL - [6:0] */
+#define WM8991_ROPGAVOL_SHIFT 0
+/*
+ * R34 (0x22) - Speaker Volume
+ */
+#define WM8991_SPKVOL_MASK 0x0003 /* SPKVOL - [1:0] */
+#define WM8991_SPKVOL_SHIFT 0
+
+/*
+ * R35 (0x23) - ClassD1
+ */
+#define WM8991_CDMODE 0x0100 /* CDMODE */
+#define WM8991_CDMODE_BIT 8
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM8991_DCGAIN_MASK 0x0007 /* DCGAIN - [5:3] */
+#define WM8991_DCGAIN_SHIFT 3
+#define WM8991_ACGAIN_MASK 0x0007 /* ACGAIN - [2:0] */
+#define WM8991_ACGAIN_SHIFT 0
+/*
+ * R39 (0x27) - Input Mixer1
+ */
+#define WM8991_AINLMODE_MASK 0x000C /* AINLMODE - [3:2] */
+#define WM8991_AINLMODE_SHIFT 2
+#define WM8991_AINRMODE_MASK 0x0003 /* AINRMODE - [1:0] */
+#define WM8991_AINRMODE_SHIFT 0
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8991_LMP4 0x0080 /* LMP4 */
+#define WM8991_LMP4_BIT 7 /* LMP4 */
+#define WM8991_LMN3 0x0040 /* LMN3 */
+#define WM8991_LMN3_BIT 6 /* LMN3 */
+#define WM8991_LMP2 0x0020 /* LMP2 */
+#define WM8991_LMP2_BIT 5 /* LMP2 */
+#define WM8991_LMN1 0x0010 /* LMN1 */
+#define WM8991_LMN1_BIT 4 /* LMN1 */
+#define WM8991_RMP4 0x0008 /* RMP4 */
+#define WM8991_RMP4_BIT 3 /* RMP4 */
+#define WM8991_RMN3 0x0004 /* RMN3 */
+#define WM8991_RMN3_BIT 2 /* RMN3 */
+#define WM8991_RMP2 0x0002 /* RMP2 */
+#define WM8991_RMP2_BIT 1 /* RMP2 */
+#define WM8991_RMN1 0x0001 /* RMN1 */
+#define WM8991_RMN1_BIT 0 /* RMN1 */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8991_L34MNB 0x0100 /* L34MNB */
+#define WM8991_L34MNB_BIT 8
+#define WM8991_L34MNBST 0x0080 /* L34MNBST */
+#define WM8991_L34MNBST_BIT 7
+#define WM8991_L12MNB 0x0020 /* L12MNB */
+#define WM8991_L12MNB_BIT 5
+#define WM8991_L12MNBST 0x0010 /* L12MNBST */
+#define WM8991_L12MNBST_BIT 4
+#define WM8991_LDBVOL_MASK 0x0007 /* LDBVOL - [2:0] */
+#define WM8991_LDBVOL_SHIFT 0
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8991_R34MNB 0x0100 /* R34MNB */
+#define WM8991_R34MNB_BIT 8
+#define WM8991_R34MNBST 0x0080 /* R34MNBST */
+#define WM8991_R34MNBST_BIT 7
+#define WM8991_R12MNB 0x0020 /* R12MNB */
+#define WM8991_R12MNB_BIT 5
+#define WM8991_R12MNBST 0x0010 /* R12MNBST */
+#define WM8991_R12MNBST_BIT 4
+#define WM8991_RDBVOL_MASK 0x0007 /* RDBVOL - [2:0] */
+#define WM8991_RDBVOL_SHIFT 0
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8991_LI2BVOL_MASK 0x07 /* LI2BVOL - [8:6] */
+#define WM8991_LI2BVOL_SHIFT 6
+#define WM8991_LR4BVOL_MASK 0x07 /* LR4BVOL - [5:3] */
+#define WM8991_LR4BVOL_SHIFT 3
+#define WM8991_LL4BVOL_MASK 0x07 /* LL4BVOL - [2:0] */
+#define WM8991_LL4BVOL_SHIFT 0
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8991_RI2BVOL_MASK 0x07 /* RI2BVOL - [8:6] */
+#define WM8991_RI2BVOL_SHIFT 6
+#define WM8991_RL4BVOL_MASK 0x07 /* RL4BVOL - [5:3] */
+#define WM8991_RL4BVOL_SHIFT 3
+#define WM8991_RR4BVOL_MASK 0x07 /* RR4BVOL - [2:0] */
+#define WM8991_RR4BVOL_SHIFT 0
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8991_LRBLO 0x0080 /* LRBLO */
+#define WM8991_LRBLO_BIT 7
+#define WM8991_LLBLO 0x0040 /* LLBLO */
+#define WM8991_LLBLO_BIT 6
+#define WM8991_LRI3LO 0x0020 /* LRI3LO */
+#define WM8991_LRI3LO_BIT 5
+#define WM8991_LLI3LO 0x0010 /* LLI3LO */
+#define WM8991_LLI3LO_BIT 4
+#define WM8991_LR12LO 0x0008 /* LR12LO */
+#define WM8991_LR12LO_BIT 3
+#define WM8991_LL12LO 0x0004 /* LL12LO */
+#define WM8991_LL12LO_BIT 2
+#define WM8991_LDLO 0x0001 /* LDLO */
+#define WM8991_LDLO_BIT 0
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8991_RLBRO 0x0080 /* RLBRO */
+#define WM8991_RLBRO_BIT 7
+#define WM8991_RRBRO 0x0040 /* RRBRO */
+#define WM8991_RRBRO_BIT 6
+#define WM8991_RLI3RO 0x0020 /* RLI3RO */
+#define WM8991_RLI3RO_BIT 5
+#define WM8991_RRI3RO 0x0010 /* RRI3RO */
+#define WM8991_RRI3RO_BIT 4
+#define WM8991_RL12RO 0x0008 /* RL12RO */
+#define WM8991_RL12RO_BIT 3
+#define WM8991_RR12RO 0x0004 /* RR12RO */
+#define WM8991_RR12RO_BIT 2
+#define WM8991_RDRO 0x0001 /* RDRO */
+#define WM8991_RDRO_BIT 0
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8991_LLI3LOVOL_MASK 0x07 /* LLI3LOVOL - [8:6] */
+#define WM8991_LLI3LOVOL_SHIFT 6
+#define WM8991_LR12LOVOL_MASK 0x07 /* LR12LOVOL - [5:3] */
+#define WM8991_LR12LOVOL_SHIFT 3
+#define WM8991_LL12LOVOL_MASK 0x07 /* LL12LOVOL - [2:0] */
+#define WM8991_LL12LOVOL_SHIFT 0
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8991_RRI3ROVOL_MASK 0x07 /* RRI3ROVOL - [8:6] */
+#define WM8991_RRI3ROVOL_SHIFT 6
+#define WM8991_RL12ROVOL_MASK 0x07 /* RL12ROVOL - [5:3] */
+#define WM8991_RL12ROVOL_SHIFT 3
+#define WM8991_RR12ROVOL_MASK 0x07 /* RR12ROVOL - [2:0] */
+#define WM8991_RR12ROVOL_SHIFT 0
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8991_LRI3LOVOL_MASK 0x07 /* LRI3LOVOL - [8:6] */
+#define WM8991_LRI3LOVOL_SHIFT 6
+#define WM8991_LRBLOVOL_MASK 0x07 /* LRBLOVOL - [5:3] */
+#define WM8991_LRBLOVOL_SHIFT 3
+#define WM8991_LLBLOVOL_MASK 0x07 /* LLBLOVOL - [2:0] */
+#define WM8991_LLBLOVOL_SHIFT 0
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8991_RLI3ROVOL_MASK 0x07 /* RLI3ROVOL - [8:6] */
+#define WM8991_RLI3ROVOL_SHIFT 6
+#define WM8991_RLBROVOL_MASK 0x07 /* RLBROVOL - [5:3] */
+#define WM8991_RLBROVOL_SHIFT 3
+#define WM8991_RRBROVOL_MASK 0x07 /* RRBROVOL - [2:0] */
+#define WM8991_RRBROVOL_SHIFT 0
+
+/*
+ * R51 (0x33) - Out3/4 Mixer
+ */
+#define WM8991_VSEL_MASK 0x0180 /* VSEL - [8:7] */
+#define WM8991_LI4O3 0x0020 /* LI4O3 */
+#define WM8991_LI4O3_BIT 5
+#define WM8991_LPGAO3 0x0010 /* LPGAO3 */
+#define WM8991_LPGAO3_BIT 4
+#define WM8991_RI4O4 0x0002 /* RI4O4 */
+#define WM8991_RI4O4_BIT 1
+#define WM8991_RPGAO4 0x0001 /* RPGAO4 */
+#define WM8991_RPGAO4_BIT 0
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8991_LLOPGALON 0x0040 /* LLOPGALON */
+#define WM8991_LLOPGALON_BIT 6
+#define WM8991_LROPGALON 0x0020 /* LROPGALON */
+#define WM8991_LROPGALON_BIT 5
+#define WM8991_LOPLON 0x0010 /* LOPLON */
+#define WM8991_LOPLON_BIT 4
+#define WM8991_LR12LOP 0x0004 /* LR12LOP */
+#define WM8991_LR12LOP_BIT 2
+#define WM8991_LL12LOP 0x0002 /* LL12LOP */
+#define WM8991_LL12LOP_BIT 1
+#define WM8991_LLOPGALOP 0x0001 /* LLOPGALOP */
+#define WM8991_LLOPGALOP_BIT 0
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8991_RROPGARON 0x0040 /* RROPGARON */
+#define WM8991_RROPGARON_BIT 6
+#define WM8991_RLOPGARON 0x0020 /* RLOPGARON */
+#define WM8991_RLOPGARON_BIT 5
+#define WM8991_ROPRON 0x0010 /* ROPRON */
+#define WM8991_ROPRON_BIT 4
+#define WM8991_RL12ROP 0x0004 /* RL12ROP */
+#define WM8991_RL12ROP_BIT 2
+#define WM8991_RR12ROP 0x0002 /* RR12ROP */
+#define WM8991_RR12ROP_BIT 1
+#define WM8991_RROPGAROP 0x0001 /* RROPGAROP */
+#define WM8991_RROPGAROP_BIT 0
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8991_LB2SPK 0x0080 /* LB2SPK */
+#define WM8991_LB2SPK_BIT 7
+#define WM8991_RB2SPK 0x0040 /* RB2SPK */
+#define WM8991_RB2SPK_BIT 6
+#define WM8991_LI2SPK 0x0020 /* LI2SPK */
+#define WM8991_LI2SPK_BIT 5
+#define WM8991_RI2SPK 0x0010 /* RI2SPK */
+#define WM8991_RI2SPK_BIT 4
+#define WM8991_LOPGASPK 0x0008 /* LOPGASPK */
+#define WM8991_LOPGASPK_BIT 3
+#define WM8991_ROPGASPK 0x0004 /* ROPGASPK */
+#define WM8991_ROPGASPK_BIT 2
+#define WM8991_LDSPK 0x0002 /* LDSPK */
+#define WM8991_LDSPK_BIT 1
+#define WM8991_RDSPK 0x0001 /* RDSPK */
+#define WM8991_RDSPK_BIT 0
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8991_VROI 0x0001 /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8991_DIS_LLINE 0x0020 /* DIS_LLINE */
+#define WM8991_DIS_RLINE 0x0010 /* DIS_RLINE */
+#define WM8991_DIS_OUT3 0x0008 /* DIS_OUT3 */
+#define WM8991_DIS_OUT4 0x0004 /* DIS_OUT4 */
+#define WM8991_DIS_LOUT 0x0002 /* DIS_LOUT */
+#define WM8991_DIS_ROUT 0x0001 /* DIS_ROUT */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8991_SOFTST 0x0040 /* SOFTST */
+#define WM8991_BUFIOEN 0x0008 /* BUFIOEN */
+#define WM8991_BUFDCOPEN 0x0004 /* BUFDCOPEN */
+#define WM8991_POBCTRL 0x0002 /* POBCTRL */
+#define WM8991_VMIDTOG 0x0001 /* VMIDTOG */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8991_MCDSCTH_MASK 0x00C0 /* MCDSCTH - [7:6] */
+#define WM8991_MCDTHR_MASK 0x0038 /* MCDTHR - [5:3] */
+#define WM8991_MCD 0x0004 /* MCD */
+#define WM8991_MBSEL 0x0001 /* MBSEL */
+
+/*
+ * R60 (0x3C) - PLL1
+ */
+#define WM8991_SDM 0x0080 /* SDM */
+#define WM8991_PRESCALE 0x0040 /* PRESCALE */
+#define WM8991_PLLN_MASK 0x000F /* PLLN - [3:0] */
+
+/*
+ * R61 (0x3D) - PLL2
+ */
+#define WM8991_PLLK1_MASK 0x00FF /* PLLK1 - [7:0] */
+
+/*
+ * R62 (0x3E) - PLL3
+ */
+#define WM8991_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */
+
+#define WM8991_MCLK_DIV 0
+#define WM8991_DACCLK_DIV 1
+#define WM8991_ADCCLK_DIV 2
+#define WM8991_BCLK_DIV 3
+
+#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
+ tlv_array) \
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm899x_outpga_put_volsw_vu, tlv_array)
+
+#endif /* _WM8991_H */
diff --git a/kernel/sound/soc/codecs/wm8993.c b/kernel/sound/soc/codecs/wm8993.c
new file mode 100644
index 000000000..2e70a270e
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8993.c
@@ -0,0 +1,1758 @@
+/*
+ * wm8993.c -- WM8993 ALSA SoC audio driver
+ *
+ * Copyright 2009-12 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/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 <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/wm8993.h>
+
+#include "wm8993.h"
+#include "wm_hubs.h"
+
+#define WM8993_NUM_SUPPLIES 6
+static const char *wm8993_supply_names[WM8993_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD",
+ "AVDD1",
+ "AVDD2",
+ "CPVDD",
+ "SPKVDD",
+};
+
+static struct reg_default wm8993_reg_defaults[] = {
+ { 1, 0x0000 }, /* R1 - Power Management (1) */
+ { 2, 0x6000 }, /* R2 - Power Management (2) */
+ { 3, 0x0000 }, /* R3 - Power Management (3) */
+ { 4, 0x4050 }, /* R4 - Audio Interface (1) */
+ { 5, 0x4000 }, /* R5 - Audio Interface (2) */
+ { 6, 0x01C8 }, /* R6 - Clocking 1 */
+ { 7, 0x0000 }, /* R7 - Clocking 2 */
+ { 8, 0x0000 }, /* R8 - Audio Interface (3) */
+ { 9, 0x0040 }, /* R9 - Audio Interface (4) */
+ { 10, 0x0004 }, /* R10 - DAC CTRL */
+ { 11, 0x00C0 }, /* R11 - Left DAC Digital Volume */
+ { 12, 0x00C0 }, /* R12 - Right DAC Digital Volume */
+ { 13, 0x0000 }, /* R13 - Digital Side Tone */
+ { 14, 0x0300 }, /* R14 - ADC CTRL */
+ { 15, 0x00C0 }, /* R15 - Left ADC Digital Volume */
+ { 16, 0x00C0 }, /* R16 - Right ADC Digital Volume */
+ { 18, 0x0000 }, /* R18 - GPIO CTRL 1 */
+ { 19, 0x0010 }, /* R19 - GPIO1 */
+ { 20, 0x0000 }, /* R20 - IRQ_DEBOUNCE */
+ { 21, 0x0000 }, /* R21 - Inputs Clamp */
+ { 22, 0x8000 }, /* R22 - GPIOCTRL 2 */
+ { 23, 0x0800 }, /* R23 - GPIO_POL */
+ { 24, 0x008B }, /* R24 - Left Line Input 1&2 Volume */
+ { 25, 0x008B }, /* R25 - Left Line Input 3&4 Volume */
+ { 26, 0x008B }, /* R26 - Right Line Input 1&2 Volume */
+ { 27, 0x008B }, /* R27 - Right Line Input 3&4 Volume */
+ { 28, 0x006D }, /* R28 - Left Output Volume */
+ { 29, 0x006D }, /* R29 - Right Output Volume */
+ { 30, 0x0066 }, /* R30 - Line Outputs Volume */
+ { 31, 0x0020 }, /* R31 - HPOUT2 Volume */
+ { 32, 0x0079 }, /* R32 - Left OPGA Volume */
+ { 33, 0x0079 }, /* R33 - Right OPGA Volume */
+ { 34, 0x0003 }, /* R34 - SPKMIXL Attenuation */
+ { 35, 0x0003 }, /* R35 - SPKMIXR Attenuation */
+ { 36, 0x0011 }, /* R36 - SPKOUT Mixers */
+ { 37, 0x0100 }, /* R37 - SPKOUT Boost */
+ { 38, 0x0079 }, /* R38 - Speaker Volume Left */
+ { 39, 0x0079 }, /* R39 - Speaker Volume Right */
+ { 40, 0x0000 }, /* R40 - Input Mixer2 */
+ { 41, 0x0000 }, /* R41 - Input Mixer3 */
+ { 42, 0x0000 }, /* R42 - Input Mixer4 */
+ { 43, 0x0000 }, /* R43 - Input Mixer5 */
+ { 44, 0x0000 }, /* R44 - Input Mixer6 */
+ { 45, 0x0000 }, /* R45 - Output Mixer1 */
+ { 46, 0x0000 }, /* R46 - Output Mixer2 */
+ { 47, 0x0000 }, /* R47 - Output Mixer3 */
+ { 48, 0x0000 }, /* R48 - Output Mixer4 */
+ { 49, 0x0000 }, /* R49 - Output Mixer5 */
+ { 50, 0x0000 }, /* R50 - Output Mixer6 */
+ { 51, 0x0000 }, /* R51 - HPOUT2 Mixer */
+ { 52, 0x0000 }, /* R52 - Line Mixer1 */
+ { 53, 0x0000 }, /* R53 - Line Mixer2 */
+ { 54, 0x0000 }, /* R54 - Speaker Mixer */
+ { 55, 0x0000 }, /* R55 - Additional Control */
+ { 56, 0x0000 }, /* R56 - AntiPOP1 */
+ { 57, 0x0000 }, /* R57 - AntiPOP2 */
+ { 58, 0x0000 }, /* R58 - MICBIAS */
+ { 60, 0x0000 }, /* R60 - FLL Control 1 */
+ { 61, 0x0000 }, /* R61 - FLL Control 2 */
+ { 62, 0x0000 }, /* R62 - FLL Control 3 */
+ { 63, 0x2EE0 }, /* R63 - FLL Control 4 */
+ { 64, 0x0002 }, /* R64 - FLL Control 5 */
+ { 65, 0x2287 }, /* R65 - Clocking 3 */
+ { 66, 0x025F }, /* R66 - Clocking 4 */
+ { 67, 0x0000 }, /* R67 - MW Slave Control */
+ { 69, 0x0002 }, /* R69 - Bus Control 1 */
+ { 70, 0x0000 }, /* R70 - Write Sequencer 0 */
+ { 71, 0x0000 }, /* R71 - Write Sequencer 1 */
+ { 72, 0x0000 }, /* R72 - Write Sequencer 2 */
+ { 73, 0x0000 }, /* R73 - Write Sequencer 3 */
+ { 74, 0x0000 }, /* R74 - Write Sequencer 4 */
+ { 75, 0x0000 }, /* R75 - Write Sequencer 5 */
+ { 76, 0x1F25 }, /* R76 - Charge Pump 1 */
+ { 81, 0x0000 }, /* R81 - Class W 0 */
+ { 85, 0x054A }, /* R85 - DC Servo 1 */
+ { 87, 0x0000 }, /* R87 - DC Servo 3 */
+ { 96, 0x0100 }, /* R96 - Analogue HP 0 */
+ { 98, 0x0000 }, /* R98 - EQ1 */
+ { 99, 0x000C }, /* R99 - EQ2 */
+ { 100, 0x000C }, /* R100 - EQ3 */
+ { 101, 0x000C }, /* R101 - EQ4 */
+ { 102, 0x000C }, /* R102 - EQ5 */
+ { 103, 0x000C }, /* R103 - EQ6 */
+ { 104, 0x0FCA }, /* R104 - EQ7 */
+ { 105, 0x0400 }, /* R105 - EQ8 */
+ { 106, 0x00D8 }, /* R106 - EQ9 */
+ { 107, 0x1EB5 }, /* R107 - EQ10 */
+ { 108, 0xF145 }, /* R108 - EQ11 */
+ { 109, 0x0B75 }, /* R109 - EQ12 */
+ { 110, 0x01C5 }, /* R110 - EQ13 */
+ { 111, 0x1C58 }, /* R111 - EQ14 */
+ { 112, 0xF373 }, /* R112 - EQ15 */
+ { 113, 0x0A54 }, /* R113 - EQ16 */
+ { 114, 0x0558 }, /* R114 - EQ17 */
+ { 115, 0x168E }, /* R115 - EQ18 */
+ { 116, 0xF829 }, /* R116 - EQ19 */
+ { 117, 0x07AD }, /* R117 - EQ20 */
+ { 118, 0x1103 }, /* R118 - EQ21 */
+ { 119, 0x0564 }, /* R119 - EQ22 */
+ { 120, 0x0559 }, /* R120 - EQ23 */
+ { 121, 0x4000 }, /* R121 - EQ24 */
+ { 122, 0x0000 }, /* R122 - Digital Pulls */
+ { 123, 0x0F08 }, /* R123 - DRC Control 1 */
+ { 124, 0x0000 }, /* R124 - DRC Control 2 */
+ { 125, 0x0080 }, /* R125 - DRC Control 3 */
+ { 126, 0x0000 }, /* R126 - DRC Control 4 */
+};
+
+static struct {
+ int ratio;
+ int clk_sys_rate;
+} clk_sys_rates[] = {
+ { 64, 0 },
+ { 128, 1 },
+ { 192, 2 },
+ { 256, 3 },
+ { 384, 4 },
+ { 512, 5 },
+ { 768, 6 },
+ { 1024, 7 },
+ { 1408, 8 },
+ { 1536, 9 },
+};
+
+static struct {
+ int rate;
+ int sample_rate;
+} sample_rates[] = {
+ { 8000, 0 },
+ { 11025, 1 },
+ { 12000, 1 },
+ { 16000, 2 },
+ { 22050, 3 },
+ { 24000, 3 },
+ { 32000, 4 },
+ { 44100, 5 },
+ { 48000, 5 },
+};
+
+static struct {
+ int div; /* *10 due to .5s */
+ int bclk_div;
+} bclk_divs[] = {
+ { 10, 0 },
+ { 15, 1 },
+ { 20, 2 },
+ { 30, 3 },
+ { 40, 4 },
+ { 55, 5 },
+ { 60, 6 },
+ { 80, 7 },
+ { 110, 8 },
+ { 120, 9 },
+ { 160, 10 },
+ { 220, 11 },
+ { 240, 12 },
+ { 320, 13 },
+ { 440, 14 },
+ { 480, 15 },
+};
+
+struct wm8993_priv {
+ struct wm_hubs_data hubs_data;
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[WM8993_NUM_SUPPLIES];
+ struct wm8993_platform_data pdata;
+ struct completion fll_lock;
+ int master;
+ int sysclk_source;
+ int tdm_slots;
+ int tdm_width;
+ unsigned int mclk_rate;
+ unsigned int sysclk_rate;
+ unsigned int fs;
+ unsigned int bclk;
+ unsigned int fll_fref;
+ unsigned int fll_fout;
+ int fll_src;
+};
+
+static bool wm8993_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8993_SOFTWARE_RESET:
+ case WM8993_GPIO_CTRL_1:
+ case WM8993_DC_SERVO_0:
+ case WM8993_DC_SERVO_READBACK_0:
+ case WM8993_DC_SERVO_READBACK_1:
+ case WM8993_DC_SERVO_READBACK_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wm8993_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8993_SOFTWARE_RESET:
+ case WM8993_POWER_MANAGEMENT_1:
+ case WM8993_POWER_MANAGEMENT_2:
+ case WM8993_POWER_MANAGEMENT_3:
+ case WM8993_AUDIO_INTERFACE_1:
+ case WM8993_AUDIO_INTERFACE_2:
+ case WM8993_CLOCKING_1:
+ case WM8993_CLOCKING_2:
+ case WM8993_AUDIO_INTERFACE_3:
+ case WM8993_AUDIO_INTERFACE_4:
+ case WM8993_DAC_CTRL:
+ case WM8993_LEFT_DAC_DIGITAL_VOLUME:
+ case WM8993_RIGHT_DAC_DIGITAL_VOLUME:
+ case WM8993_DIGITAL_SIDE_TONE:
+ case WM8993_ADC_CTRL:
+ case WM8993_LEFT_ADC_DIGITAL_VOLUME:
+ case WM8993_RIGHT_ADC_DIGITAL_VOLUME:
+ case WM8993_GPIO_CTRL_1:
+ case WM8993_GPIO1:
+ case WM8993_IRQ_DEBOUNCE:
+ case WM8993_GPIOCTRL_2:
+ case WM8993_GPIO_POL:
+ case WM8993_LEFT_LINE_INPUT_1_2_VOLUME:
+ case WM8993_LEFT_LINE_INPUT_3_4_VOLUME:
+ case WM8993_RIGHT_LINE_INPUT_1_2_VOLUME:
+ case WM8993_RIGHT_LINE_INPUT_3_4_VOLUME:
+ case WM8993_LEFT_OUTPUT_VOLUME:
+ case WM8993_RIGHT_OUTPUT_VOLUME:
+ case WM8993_LINE_OUTPUTS_VOLUME:
+ case WM8993_HPOUT2_VOLUME:
+ case WM8993_LEFT_OPGA_VOLUME:
+ case WM8993_RIGHT_OPGA_VOLUME:
+ case WM8993_SPKMIXL_ATTENUATION:
+ case WM8993_SPKMIXR_ATTENUATION:
+ case WM8993_SPKOUT_MIXERS:
+ case WM8993_SPKOUT_BOOST:
+ case WM8993_SPEAKER_VOLUME_LEFT:
+ case WM8993_SPEAKER_VOLUME_RIGHT:
+ case WM8993_INPUT_MIXER2:
+ case WM8993_INPUT_MIXER3:
+ case WM8993_INPUT_MIXER4:
+ case WM8993_INPUT_MIXER5:
+ case WM8993_INPUT_MIXER6:
+ case WM8993_OUTPUT_MIXER1:
+ case WM8993_OUTPUT_MIXER2:
+ case WM8993_OUTPUT_MIXER3:
+ case WM8993_OUTPUT_MIXER4:
+ case WM8993_OUTPUT_MIXER5:
+ case WM8993_OUTPUT_MIXER6:
+ case WM8993_HPOUT2_MIXER:
+ case WM8993_LINE_MIXER1:
+ case WM8993_LINE_MIXER2:
+ case WM8993_SPEAKER_MIXER:
+ case WM8993_ADDITIONAL_CONTROL:
+ case WM8993_ANTIPOP1:
+ case WM8993_ANTIPOP2:
+ case WM8993_MICBIAS:
+ case WM8993_FLL_CONTROL_1:
+ case WM8993_FLL_CONTROL_2:
+ case WM8993_FLL_CONTROL_3:
+ case WM8993_FLL_CONTROL_4:
+ case WM8993_FLL_CONTROL_5:
+ case WM8993_CLOCKING_3:
+ case WM8993_CLOCKING_4:
+ case WM8993_MW_SLAVE_CONTROL:
+ case WM8993_BUS_CONTROL_1:
+ case WM8993_WRITE_SEQUENCER_0:
+ case WM8993_WRITE_SEQUENCER_1:
+ case WM8993_WRITE_SEQUENCER_2:
+ case WM8993_WRITE_SEQUENCER_3:
+ case WM8993_WRITE_SEQUENCER_4:
+ case WM8993_WRITE_SEQUENCER_5:
+ case WM8993_CHARGE_PUMP_1:
+ case WM8993_CLASS_W_0:
+ case WM8993_DC_SERVO_0:
+ case WM8993_DC_SERVO_1:
+ case WM8993_DC_SERVO_3:
+ case WM8993_DC_SERVO_READBACK_0:
+ case WM8993_DC_SERVO_READBACK_1:
+ case WM8993_DC_SERVO_READBACK_2:
+ case WM8993_ANALOGUE_HP_0:
+ case WM8993_EQ1:
+ case WM8993_EQ2:
+ case WM8993_EQ3:
+ case WM8993_EQ4:
+ case WM8993_EQ5:
+ case WM8993_EQ6:
+ case WM8993_EQ7:
+ case WM8993_EQ8:
+ case WM8993_EQ9:
+ case WM8993_EQ10:
+ case WM8993_EQ11:
+ case WM8993_EQ12:
+ case WM8993_EQ13:
+ case WM8993_EQ14:
+ case WM8993_EQ15:
+ case WM8993_EQ16:
+ case WM8993_EQ17:
+ case WM8993_EQ18:
+ case WM8993_EQ19:
+ case WM8993_EQ20:
+ case WM8993_EQ21:
+ case WM8993_EQ22:
+ case WM8993_EQ23:
+ case WM8993_EQ24:
+ case WM8993_DIGITAL_PULLS:
+ case WM8993_DRC_CONTROL_1:
+ case WM8993_DRC_CONTROL_2:
+ case WM8993_DRC_CONTROL_3:
+ case WM8993_DRC_CONTROL_4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+struct _fll_div {
+ u16 fll_fratio;
+ u16 fll_outdiv;
+ u16 fll_clk_ref_div;
+ u16 n;
+ u16 k;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fll_fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod, target;
+ unsigned int div;
+ int i;
+
+ /* Fref must be <=13.5MHz */
+ div = 1;
+ fll_div->fll_clk_ref_div = 0;
+ while ((Fref / div) > 13500000) {
+ div *= 2;
+ fll_div->fll_clk_ref_div++;
+
+ if (div > 8) {
+ pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+ }
+
+ pr_debug("Fref=%u Fout=%u\n", Fref, Fout);
+
+ /* Apply the division for our remaining calculations */
+ Fref /= div;
+
+ /* Fvco should be 90-100MHz; don't check the upper bound */
+ div = 0;
+ target = Fout * 2;
+ while (target < 90000000) {
+ div++;
+ target *= 2;
+ if (div > 7) {
+ pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ fll_div->fll_outdiv = div;
+
+ pr_debug("Fvco=%dHz\n", target);
+
+ /* Find an appropriate FLL_FRATIO and factor it out of the target */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+ target /= fll_fratios[i].ratio;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_fratios)) {
+ pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+ return -EINVAL;
+ }
+
+ /* Now, calculate N.K */
+ Ndiv = target / Fref;
+
+ fll_div->n = Ndiv;
+ Nmod = target % Fref;
+ pr_debug("Nmod=%d\n", Nmod);
+
+ /* Calculate fractional part - scale up so we can round. */
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, Fref);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ fll_div->k = K / 10;
+
+ pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n",
+ fll_div->n, fll_div->k,
+ fll_div->fll_fratio, fll_div->fll_outdiv,
+ fll_div->fll_clk_ref_div);
+
+ return 0;
+}
+
+static int _wm8993_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ u16 reg1, reg4, reg5;
+ struct _fll_div fll_div;
+ unsigned int timeout;
+ int ret;
+
+ /* Any change? */
+ if (Fref == wm8993->fll_fref && Fout == wm8993->fll_fout)
+ return 0;
+
+ /* Disable the FLL */
+ if (Fout == 0) {
+ dev_dbg(codec->dev, "FLL disabled\n");
+ wm8993->fll_fref = 0;
+ wm8993->fll_fout = 0;
+
+ reg1 = snd_soc_read(codec, WM8993_FLL_CONTROL_1);
+ reg1 &= ~WM8993_FLL_ENA;
+ snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);
+
+ return 0;
+ }
+
+ ret = fll_factors(&fll_div, Fref, Fout);
+ if (ret != 0)
+ return ret;
+
+ reg5 = snd_soc_read(codec, WM8993_FLL_CONTROL_5);
+ reg5 &= ~WM8993_FLL_CLK_SRC_MASK;
+
+ switch (fll_id) {
+ case WM8993_FLL_MCLK:
+ break;
+
+ case WM8993_FLL_LRCLK:
+ reg5 |= 1;
+ break;
+
+ case WM8993_FLL_BCLK:
+ reg5 |= 2;
+ break;
+
+ default:
+ dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
+ return -EINVAL;
+ }
+
+ /* Any FLL configuration change requires that the FLL be
+ * disabled first. */
+ reg1 = snd_soc_read(codec, WM8993_FLL_CONTROL_1);
+ reg1 &= ~WM8993_FLL_ENA;
+ snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);
+
+ /* Apply the configuration */
+ if (fll_div.k)
+ reg1 |= WM8993_FLL_FRAC_MASK;
+ else
+ reg1 &= ~WM8993_FLL_FRAC_MASK;
+ snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);
+
+ snd_soc_write(codec, WM8993_FLL_CONTROL_2,
+ (fll_div.fll_outdiv << WM8993_FLL_OUTDIV_SHIFT) |
+ (fll_div.fll_fratio << WM8993_FLL_FRATIO_SHIFT));
+ snd_soc_write(codec, WM8993_FLL_CONTROL_3, fll_div.k);
+
+ reg4 = snd_soc_read(codec, WM8993_FLL_CONTROL_4);
+ reg4 &= ~WM8993_FLL_N_MASK;
+ reg4 |= fll_div.n << WM8993_FLL_N_SHIFT;
+ snd_soc_write(codec, WM8993_FLL_CONTROL_4, reg4);
+
+ reg5 &= ~WM8993_FLL_CLK_REF_DIV_MASK;
+ reg5 |= fll_div.fll_clk_ref_div << WM8993_FLL_CLK_REF_DIV_SHIFT;
+ snd_soc_write(codec, WM8993_FLL_CONTROL_5, reg5);
+
+ /* If we've got an interrupt wired up make sure we get it */
+ if (i2c->irq)
+ timeout = msecs_to_jiffies(20);
+ else if (Fref < 1000000)
+ timeout = msecs_to_jiffies(3);
+ else
+ timeout = msecs_to_jiffies(1);
+
+ try_wait_for_completion(&wm8993->fll_lock);
+
+ /* Enable the FLL */
+ snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA);
+
+ timeout = wait_for_completion_timeout(&wm8993->fll_lock, timeout);
+ if (i2c->irq && !timeout)
+ dev_warn(codec->dev, "Timed out waiting for FLL\n");
+
+ dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
+
+ wm8993->fll_fref = Fref;
+ wm8993->fll_fout = Fout;
+ wm8993->fll_src = source;
+
+ return 0;
+}
+
+static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ return _wm8993_set_fll(dai->codec, fll_id, source, Fref, Fout);
+}
+
+static int configure_clock(struct snd_soc_codec *codec)
+{
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg;
+
+ /* This should be done on init() for bypass paths */
+ switch (wm8993->sysclk_source) {
+ case WM8993_SYSCLK_MCLK:
+ dev_dbg(codec->dev, "Using %dHz MCLK\n", wm8993->mclk_rate);
+
+ reg = snd_soc_read(codec, WM8993_CLOCKING_2);
+ reg &= ~(WM8993_MCLK_DIV | WM8993_SYSCLK_SRC);
+ if (wm8993->mclk_rate > 13500000) {
+ reg |= WM8993_MCLK_DIV;
+ wm8993->sysclk_rate = wm8993->mclk_rate / 2;
+ } else {
+ reg &= ~WM8993_MCLK_DIV;
+ wm8993->sysclk_rate = wm8993->mclk_rate;
+ }
+ snd_soc_write(codec, WM8993_CLOCKING_2, reg);
+ break;
+
+ case WM8993_SYSCLK_FLL:
+ dev_dbg(codec->dev, "Using %dHz FLL clock\n",
+ wm8993->fll_fout);
+
+ reg = snd_soc_read(codec, WM8993_CLOCKING_2);
+ reg |= WM8993_SYSCLK_SRC;
+ if (wm8993->fll_fout > 13500000) {
+ reg |= WM8993_MCLK_DIV;
+ wm8993->sysclk_rate = wm8993->fll_fout / 2;
+ } else {
+ reg &= ~WM8993_MCLK_DIV;
+ wm8993->sysclk_rate = wm8993->fll_fout;
+ }
+ snd_soc_write(codec, WM8993_CLOCKING_2, reg);
+ break;
+
+ default:
+ dev_err(codec->dev, "System clock not configured\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm8993->sysclk_rate);
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(drc_comp_threash, -4500, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_comp_amp, -2250, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0);
+static const unsigned int drc_max_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -1800, 300, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+
+static const char *dac_deemph_text[] = {
+ "None",
+ "32kHz",
+ "44.1kHz",
+ "48kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(dac_deemph,
+ WM8993_DAC_CTRL, 4, dac_deemph_text);
+
+static const char *adc_hpf_text[] = {
+ "Hi-Fi",
+ "Voice 1",
+ "Voice 2",
+ "Voice 3",
+};
+
+static SOC_ENUM_SINGLE_DECL(adc_hpf,
+ WM8993_ADC_CTRL, 5, adc_hpf_text);
+
+static const char *drc_path_text[] = {
+ "ADC",
+ "DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_path,
+ WM8993_DRC_CONTROL_1, 14, drc_path_text);
+
+static const char *drc_r0_text[] = {
+ "1",
+ "1/2",
+ "1/4",
+ "1/8",
+ "1/16",
+ "0",
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_r0,
+ WM8993_DRC_CONTROL_3, 8, drc_r0_text);
+
+static const char *drc_r1_text[] = {
+ "1",
+ "1/2",
+ "1/4",
+ "1/8",
+ "0",
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_r1,
+ WM8993_DRC_CONTROL_4, 13, drc_r1_text);
+
+static const char *drc_attack_text[] = {
+ "Reserved",
+ "181us",
+ "363us",
+ "726us",
+ "1.45ms",
+ "2.9ms",
+ "5.8ms",
+ "11.6ms",
+ "23.2ms",
+ "46.4ms",
+ "92.8ms",
+ "185.6ms",
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_attack,
+ WM8993_DRC_CONTROL_2, 12, drc_attack_text);
+
+static const char *drc_decay_text[] = {
+ "186ms",
+ "372ms",
+ "743ms",
+ "1.49s",
+ "2.97ms",
+ "5.94ms",
+ "11.89ms",
+ "23.78ms",
+ "47.56ms",
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_decay,
+ WM8993_DRC_CONTROL_2, 8, drc_decay_text);
+
+static const char *drc_ff_text[] = {
+ "5 samples",
+ "9 samples",
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_ff,
+ WM8993_DRC_CONTROL_3, 7, drc_ff_text);
+
+static const char *drc_qr_rate_text[] = {
+ "0.725ms",
+ "1.45ms",
+ "5.8ms",
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_qr_rate,
+ WM8993_DRC_CONTROL_3, 0, drc_qr_rate_text);
+
+static const char *drc_smooth_text[] = {
+ "Low",
+ "Medium",
+ "High",
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_smooth,
+ WM8993_DRC_CONTROL_1, 4, drc_smooth_text);
+
+static const struct snd_kcontrol_new wm8993_snd_controls[] = {
+SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8993_DIGITAL_SIDE_TONE,
+ 5, 9, 12, 0, sidetone_tlv),
+
+SOC_SINGLE("DRC Switch", WM8993_DRC_CONTROL_1, 15, 1, 0),
+SOC_ENUM("DRC Path", drc_path),
+SOC_SINGLE_TLV("DRC Compressor Threshold Volume", WM8993_DRC_CONTROL_2,
+ 2, 60, 1, drc_comp_threash),
+SOC_SINGLE_TLV("DRC Compressor Amplitude Volume", WM8993_DRC_CONTROL_3,
+ 11, 30, 1, drc_comp_amp),
+SOC_ENUM("DRC R0", drc_r0),
+SOC_ENUM("DRC R1", drc_r1),
+SOC_SINGLE_TLV("DRC Minimum Volume", WM8993_DRC_CONTROL_1, 2, 3, 1,
+ drc_min_tlv),
+SOC_SINGLE_TLV("DRC Maximum Volume", WM8993_DRC_CONTROL_1, 0, 3, 0,
+ drc_max_tlv),
+SOC_ENUM("DRC Attack Rate", drc_attack),
+SOC_ENUM("DRC Decay Rate", drc_decay),
+SOC_ENUM("DRC FF Delay", drc_ff),
+SOC_SINGLE("DRC Anti-clip Switch", WM8993_DRC_CONTROL_1, 9, 1, 0),
+SOC_SINGLE("DRC Quick Release Switch", WM8993_DRC_CONTROL_1, 10, 1, 0),
+SOC_SINGLE_TLV("DRC Quick Release Volume", WM8993_DRC_CONTROL_3, 2, 3, 0,
+ drc_qr_tlv),
+SOC_ENUM("DRC Quick Release Rate", drc_qr_rate),
+SOC_SINGLE("DRC Smoothing Switch", WM8993_DRC_CONTROL_1, 11, 1, 0),
+SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8993_DRC_CONTROL_1, 8, 1, 0),
+SOC_ENUM("DRC Smoothing Hysteresis Threshold", drc_smooth),
+SOC_SINGLE_TLV("DRC Startup Volume", WM8993_DRC_CONTROL_4, 8, 18, 0,
+ drc_startup_tlv),
+
+SOC_SINGLE("EQ Switch", WM8993_EQ1, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("Capture Volume", WM8993_LEFT_ADC_DIGITAL_VOLUME,
+ WM8993_RIGHT_ADC_DIGITAL_VOLUME, 1, 96, 0, digital_tlv),
+SOC_SINGLE("ADC High Pass Filter Switch", WM8993_ADC_CTRL, 8, 1, 0),
+SOC_ENUM("ADC High Pass Filter Mode", adc_hpf),
+
+SOC_DOUBLE_R_TLV("Playback Volume", WM8993_LEFT_DAC_DIGITAL_VOLUME,
+ WM8993_RIGHT_DAC_DIGITAL_VOLUME, 1, 96, 0, digital_tlv),
+SOC_SINGLE_TLV("Playback Boost Volume", WM8993_AUDIO_INTERFACE_2, 10, 3, 0,
+ dac_boost_tlv),
+SOC_ENUM("DAC Deemphasis", dac_deemph),
+
+SOC_SINGLE_TLV("SPKL DAC Volume", WM8993_SPKMIXL_ATTENUATION,
+ 2, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_SINGLE_TLV("SPKR DAC Volume", WM8993_SPKMIXR_ATTENUATION,
+ 2, 1, 1, wm_hubs_spkmix_tlv),
+};
+
+static const struct snd_kcontrol_new wm8993_eq_controls[] = {
+SOC_SINGLE_TLV("EQ1 Volume", WM8993_EQ2, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Volume", WM8993_EQ3, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Volume", WM8993_EQ4, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Volume", WM8993_EQ5, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ5 Volume", WM8993_EQ6, 0, 24, 0, eq_tlv),
+};
+
+static int clk_sys_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return configure_clock(codec);
+
+ case SND_SOC_DAPM_POST_PMD:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new left_speaker_mixer[] = {
+SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0),
+SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 3, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_mixer[] = {
+SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),
+SOC_DAPM_SINGLE("IN1RP Switch", WM8993_SPEAKER_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 0, 1, 0),
+};
+
+static const char *aif_text[] = {
+ "Left", "Right"
+};
+
+static SOC_ENUM_SINGLE_DECL(aifoutl_enum,
+ WM8993_AUDIO_INTERFACE_1, 15, aif_text);
+
+static const struct snd_kcontrol_new aifoutl_mux =
+ SOC_DAPM_ENUM("AIFOUTL Mux", aifoutl_enum);
+
+static SOC_ENUM_SINGLE_DECL(aifoutr_enum,
+ WM8993_AUDIO_INTERFACE_1, 14, aif_text);
+
+static const struct snd_kcontrol_new aifoutr_mux =
+ SOC_DAPM_ENUM("AIFOUTR Mux", aifoutr_enum);
+
+static SOC_ENUM_SINGLE_DECL(aifinl_enum,
+ WM8993_AUDIO_INTERFACE_2, 15, aif_text);
+
+static const struct snd_kcontrol_new aifinl_mux =
+ SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum);
+
+static SOC_ENUM_SINGLE_DECL(aifinr_enum,
+ WM8993_AUDIO_INTERFACE_2, 14, aif_text);
+
+static const struct snd_kcontrol_new aifinr_mux =
+ SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum);
+
+static const char *sidetone_text[] = {
+ "None", "Left", "Right"
+};
+
+static SOC_ENUM_SINGLE_DECL(sidetonel_enum,
+ WM8993_DIGITAL_SIDE_TONE, 2, sidetone_text);
+
+static const struct snd_kcontrol_new sidetonel_mux =
+ SOC_DAPM_ENUM("Left Sidetone", sidetonel_enum);
+
+static SOC_ENUM_SINGLE_DECL(sidetoner_enum,
+ WM8993_DIGITAL_SIDE_TONE, 0, sidetone_text);
+
+static const struct snd_kcontrol_new sidetoner_mux =
+ SOC_DAPM_ENUM("Right Sidetone", sidetoner_enum);
+
+static const struct snd_soc_dapm_widget wm8993_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8993_BUS_CONTROL_1, 1, 0, clk_sys_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("TOCLK", WM8993_CLOCKING_1, 14, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8993_CLOCKING_3, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("VMID", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8993_POWER_MANAGEMENT_2, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8993_POWER_MANAGEMENT_2, 0, 0),
+
+SND_SOC_DAPM_MUX("AIFOUTL Mux", SND_SOC_NOPM, 0, 0, &aifoutl_mux),
+SND_SOC_DAPM_MUX("AIFOUTR Mux", SND_SOC_NOPM, 0, 0, &aifoutr_mux),
+
+SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &aifinl_mux),
+SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &aifinr_mux),
+
+SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &sidetonel_mux),
+SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &sidetoner_mux),
+
+SND_SOC_DAPM_DAC("DACL", NULL, WM8993_POWER_MANAGEMENT_3, 1, 0),
+SND_SOC_DAPM_DAC("DACR", NULL, WM8993_POWER_MANAGEMENT_3, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux),
+
+SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0,
+ left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
+SND_SOC_DAPM_MIXER("SPKR", WM8993_POWER_MANAGEMENT_3, 9, 0,
+ right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
+SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route routes[] = {
+ { "MICBIAS1", NULL, "VMID" },
+ { "MICBIAS2", NULL, "VMID" },
+
+ { "ADCL", NULL, "CLK_SYS" },
+ { "ADCL", NULL, "CLK_DSP" },
+ { "ADCR", NULL, "CLK_SYS" },
+ { "ADCR", NULL, "CLK_DSP" },
+
+ { "AIFOUTL Mux", "Left", "ADCL" },
+ { "AIFOUTL Mux", "Right", "ADCR" },
+ { "AIFOUTR Mux", "Left", "ADCL" },
+ { "AIFOUTR Mux", "Right", "ADCR" },
+
+ { "AIFOUTL", NULL, "AIFOUTL Mux" },
+ { "AIFOUTR", NULL, "AIFOUTR Mux" },
+
+ { "DACL Mux", "Left", "AIFINL" },
+ { "DACL Mux", "Right", "AIFINR" },
+ { "DACR Mux", "Left", "AIFINL" },
+ { "DACR Mux", "Right", "AIFINR" },
+
+ { "DACL Sidetone", "Left", "ADCL" },
+ { "DACL Sidetone", "Right", "ADCR" },
+ { "DACR Sidetone", "Left", "ADCL" },
+ { "DACR Sidetone", "Right", "ADCR" },
+
+ { "DACL", NULL, "CLK_SYS" },
+ { "DACL", NULL, "CLK_DSP" },
+ { "DACL", NULL, "DACL Mux" },
+ { "DACL", NULL, "DACL Sidetone" },
+ { "DACR", NULL, "CLK_SYS" },
+ { "DACR", NULL, "CLK_DSP" },
+ { "DACR", NULL, "DACR Mux" },
+ { "DACR", NULL, "DACR Sidetone" },
+
+ { "Left Output Mixer", "DAC Switch", "DACL" },
+
+ { "Right Output Mixer", "DAC Switch", "DACR" },
+
+ { "Left Output PGA", NULL, "CLK_SYS" },
+
+ { "Right Output PGA", NULL, "CLK_SYS" },
+
+ { "SPKL", "DAC Switch", "DACL" },
+ { "SPKL", NULL, "CLK_SYS" },
+
+ { "SPKR", "DAC Switch", "DACR" },
+ { "SPKR", NULL, "CLK_SYS" },
+
+ { "Left Headphone Mux", "DAC", "DACL" },
+ { "Right Headphone Mux", "DAC", "DACR" },
+};
+
+static int wm8993_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ wm_hubs_set_bias_level(codec, level);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID=2*40k */
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_VMID_SEL_MASK, 0x2);
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_2,
+ WM8993_TSHUT_ENA, WM8993_TSHUT_ENA);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies),
+ wm8993->supplies);
+ if (ret != 0)
+ return ret;
+
+ regcache_cache_only(wm8993->regmap, false);
+ regcache_sync(wm8993->regmap);
+
+ wm_hubs_vmid_ena(codec);
+
+ /* Bring up VMID with fast soft start */
+ snd_soc_update_bits(codec, WM8993_ANTIPOP2,
+ WM8993_STARTUP_BIAS_ENA |
+ WM8993_VMID_BUF_ENA |
+ WM8993_VMID_RAMP_MASK |
+ WM8993_BIAS_SRC,
+ WM8993_STARTUP_BIAS_ENA |
+ WM8993_VMID_BUF_ENA |
+ WM8993_VMID_RAMP_MASK |
+ WM8993_BIAS_SRC);
+
+ /* If either line output is single ended we
+ * need the VMID buffer */
+ if (!wm8993->pdata.lineout1_diff ||
+ !wm8993->pdata.lineout2_diff)
+ snd_soc_update_bits(codec, WM8993_ANTIPOP1,
+ WM8993_LINEOUT_VMID_BUF_ENA,
+ WM8993_LINEOUT_VMID_BUF_ENA);
+
+ /* VMID=2*40k */
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_VMID_SEL_MASK |
+ WM8993_BIAS_ENA,
+ WM8993_BIAS_ENA | 0x2);
+ msleep(32);
+
+ /* Switch to normal bias */
+ snd_soc_update_bits(codec, WM8993_ANTIPOP2,
+ WM8993_BIAS_SRC |
+ WM8993_STARTUP_BIAS_ENA, 0);
+ }
+
+ /* VMID=2*240k */
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_VMID_SEL_MASK, 0x4);
+
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_2,
+ WM8993_TSHUT_ENA, 0);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, WM8993_ANTIPOP1,
+ WM8993_LINEOUT_VMID_BUF_ENA, 0);
+
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_VMID_SEL_MASK | WM8993_BIAS_ENA,
+ 0);
+
+ snd_soc_update_bits(codec, WM8993_ANTIPOP2,
+ WM8993_STARTUP_BIAS_ENA |
+ WM8993_VMID_BUF_ENA |
+ WM8993_VMID_RAMP_MASK |
+ WM8993_BIAS_SRC, 0);
+
+ regcache_cache_only(wm8993->regmap, true);
+ regcache_mark_dirty(wm8993->regmap);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies),
+ wm8993->supplies);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int wm8993_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 wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case WM8993_SYSCLK_MCLK:
+ wm8993->mclk_rate = freq;
+ case WM8993_SYSCLK_FLL:
+ wm8993->sysclk_source = clk_id;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8993_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ unsigned int aif1 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_1);
+ unsigned int aif4 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_4);
+
+ aif1 &= ~(WM8993_BCLK_DIR | WM8993_AIF_BCLK_INV |
+ WM8993_AIF_LRCLK_INV | WM8993_AIF_FMT_MASK);
+ aif4 &= ~WM8993_LRCLK_DIR;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ wm8993->master = 0;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ aif4 |= WM8993_LRCLK_DIR;
+ wm8993->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ aif1 |= WM8993_BCLK_DIR;
+ wm8993->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif1 |= WM8993_BCLK_DIR;
+ aif4 |= WM8993_LRCLK_DIR;
+ wm8993->master = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_B:
+ aif1 |= WM8993_AIF_LRCLK_INV;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif1 |= 0x18;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aif1 |= 0x10;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif1 |= 0x8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8993_AIF_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif1 |= WM8993_AIF_BCLK_INV | WM8993_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8993_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif1 |= WM8993_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
+ snd_soc_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
+
+ return 0;
+}
+
+static int wm8993_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ int ret, i, best, best_val, cur_val;
+ unsigned int clocking1, clocking3, aif1, aif4;
+
+ clocking1 = snd_soc_read(codec, WM8993_CLOCKING_1);
+ clocking1 &= ~WM8993_BCLK_DIV_MASK;
+
+ clocking3 = snd_soc_read(codec, WM8993_CLOCKING_3);
+ clocking3 &= ~(WM8993_CLK_SYS_RATE_MASK | WM8993_SAMPLE_RATE_MASK);
+
+ aif1 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_1);
+ aif1 &= ~WM8993_AIF_WL_MASK;
+
+ aif4 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_4);
+ aif4 &= ~WM8993_LRCLK_RATE_MASK;
+
+ /* What BCLK do we need? */
+ wm8993->fs = params_rate(params);
+ wm8993->bclk = 2 * wm8993->fs;
+ if (wm8993->tdm_slots) {
+ dev_dbg(codec->dev, "Configuring for %d %d bit TDM slots\n",
+ wm8993->tdm_slots, wm8993->tdm_width);
+ wm8993->bclk *= wm8993->tdm_width * wm8993->tdm_slots;
+ } else {
+ switch (params_width(params)) {
+ case 16:
+ wm8993->bclk *= 16;
+ break;
+ case 20:
+ wm8993->bclk *= 20;
+ aif1 |= 0x8;
+ break;
+ case 24:
+ wm8993->bclk *= 24;
+ aif1 |= 0x10;
+ break;
+ case 32:
+ wm8993->bclk *= 32;
+ aif1 |= 0x18;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8993->bclk);
+
+ ret = configure_clock(codec);
+ if (ret != 0)
+ return ret;
+
+ /* Select nearest CLK_SYS_RATE */
+ best = 0;
+ best_val = abs((wm8993->sysclk_rate / clk_sys_rates[0].ratio)
+ - wm8993->fs);
+ for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) {
+ cur_val = abs((wm8993->sysclk_rate /
+ clk_sys_rates[i].ratio) - wm8993->fs);
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+ dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n",
+ clk_sys_rates[best].ratio);
+ clocking3 |= (clk_sys_rates[best].clk_sys_rate
+ << WM8993_CLK_SYS_RATE_SHIFT);
+
+ /* SAMPLE_RATE */
+ best = 0;
+ best_val = abs(wm8993->fs - sample_rates[0].rate);
+ for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+ /* Closest match */
+ cur_val = abs(wm8993->fs - sample_rates[i].rate);
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+ dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n",
+ sample_rates[best].rate);
+ clocking3 |= (sample_rates[best].sample_rate
+ << WM8993_SAMPLE_RATE_SHIFT);
+
+ /* BCLK_DIV */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ cur_val = ((wm8993->sysclk_rate * 10) / bclk_divs[i].div)
+ - wm8993->bclk;
+ if (cur_val < 0) /* Table is sorted */
+ break;
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+ wm8993->bclk = (wm8993->sysclk_rate * 10) / bclk_divs[best].div;
+ dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
+ bclk_divs[best].div, wm8993->bclk);
+ clocking1 |= bclk_divs[best].bclk_div << WM8993_BCLK_DIV_SHIFT;
+
+ /* LRCLK is a simple fraction of BCLK */
+ dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm8993->bclk / wm8993->fs);
+ aif4 |= wm8993->bclk / wm8993->fs;
+
+ snd_soc_write(codec, WM8993_CLOCKING_1, clocking1);
+ snd_soc_write(codec, WM8993_CLOCKING_3, clocking3);
+ snd_soc_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
+ snd_soc_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
+
+ /* ReTune Mobile? */
+ if (wm8993->pdata.num_retune_configs) {
+ u16 eq1 = snd_soc_read(codec, WM8993_EQ1);
+ struct wm8993_retune_mobile_setting *s;
+
+ best = 0;
+ best_val = abs(wm8993->pdata.retune_configs[0].rate
+ - wm8993->fs);
+ for (i = 0; i < wm8993->pdata.num_retune_configs; i++) {
+ cur_val = abs(wm8993->pdata.retune_configs[i].rate
+ - wm8993->fs);
+ if (cur_val < best_val) {
+ best_val = cur_val;
+ best = i;
+ }
+ }
+ s = &wm8993->pdata.retune_configs[best];
+
+ dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n",
+ s->name, s->rate);
+
+ /* Disable EQ while we reconfigure */
+ snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, 0);
+
+ for (i = 1; i < ARRAY_SIZE(s->config); i++)
+ snd_soc_write(codec, WM8993_EQ1 + i, s->config[i]);
+
+ snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, eq1);
+ }
+
+ return 0;
+}
+
+static int wm8993_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, WM8993_DAC_CTRL);
+
+ if (mute)
+ reg |= WM8993_DAC_MUTE;
+ else
+ reg &= ~WM8993_DAC_MUTE;
+
+ snd_soc_write(codec, WM8993_DAC_CTRL, reg);
+
+ return 0;
+}
+
+static int wm8993_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ int aif1 = 0;
+ int aif2 = 0;
+
+ /* Don't need to validate anything if we're turning off TDM */
+ if (slots == 0) {
+ wm8993->tdm_slots = 0;
+ goto out;
+ }
+
+ /* Note that we allow configurations we can't handle ourselves -
+ * for example, we can generate clocks for slots 2 and up even if
+ * we can't use those slots ourselves.
+ */
+ aif1 |= WM8993_AIFADC_TDM;
+ aif2 |= WM8993_AIFDAC_TDM;
+
+ switch (rx_mask) {
+ case 3:
+ break;
+ case 0xc:
+ aif1 |= WM8993_AIFADC_TDM_CHAN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ switch (tx_mask) {
+ case 3:
+ break;
+ case 0xc:
+ aif2 |= WM8993_AIFDAC_TDM_CHAN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+out:
+ wm8993->tdm_width = slot_width;
+ wm8993->tdm_slots = slots / 2;
+
+ snd_soc_update_bits(codec, WM8993_AUDIO_INTERFACE_1,
+ WM8993_AIFADC_TDM | WM8993_AIFADC_TDM_CHAN, aif1);
+ snd_soc_update_bits(codec, WM8993_AUDIO_INTERFACE_2,
+ WM8993_AIFDAC_TDM | WM8993_AIFDAC_TDM_CHAN, aif2);
+
+ return 0;
+}
+
+static irqreturn_t wm8993_irq(int irq, void *data)
+{
+ struct wm8993_priv *wm8993 = data;
+ int mask, val, ret;
+
+ ret = regmap_read(wm8993->regmap, WM8993_GPIO_CTRL_1, &val);
+ if (ret != 0) {
+ dev_err(wm8993->dev, "Failed to read interrupt status: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ ret = regmap_read(wm8993->regmap, WM8993_GPIOCTRL_2, &mask);
+ if (ret != 0) {
+ dev_err(wm8993->dev, "Failed to read interrupt mask: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ /* The IRQ pin status is visible in the register too */
+ val &= ~(mask | WM8993_IRQ);
+ if (!val)
+ return IRQ_NONE;
+
+ if (val & WM8993_TEMPOK_EINT)
+ dev_crit(wm8993->dev, "Thermal warning\n");
+
+ if (val & WM8993_FLL_LOCK_EINT) {
+ dev_dbg(wm8993->dev, "FLL locked\n");
+ complete(&wm8993->fll_lock);
+ }
+
+ ret = regmap_write(wm8993->regmap, WM8993_GPIO_CTRL_1, val);
+ if (ret != 0)
+ dev_err(wm8993->dev, "Failed to ack interrupt: %d\n", ret);
+
+ return IRQ_HANDLED;
+}
+
+static const struct snd_soc_dai_ops wm8993_ops = {
+ .set_sysclk = wm8993_set_sysclk,
+ .set_fmt = wm8993_set_dai_fmt,
+ .hw_params = wm8993_hw_params,
+ .digital_mute = wm8993_digital_mute,
+ .set_pll = wm8993_set_fll,
+ .set_tdm_slot = wm8993_set_tdm_slot,
+};
+
+#define WM8993_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8993_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver wm8993_dai = {
+ .name = "wm8993-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8993_RATES,
+ .formats = WM8993_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8993_RATES,
+ .formats = WM8993_FORMATS,
+ .sig_bits = 24,
+ },
+ .ops = &wm8993_ops,
+ .symmetric_rates = 1,
+};
+
+static int wm8993_probe(struct snd_soc_codec *codec)
+{
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ wm8993->hubs_data.hp_startup_mode = 1;
+ wm8993->hubs_data.dcs_codes_l = -2;
+ wm8993->hubs_data.dcs_codes_r = -2;
+ wm8993->hubs_data.series_startup = 1;
+
+ /* Latch volume update bits and default ZC on */
+ snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME,
+ WM8993_DAC_VU, WM8993_DAC_VU);
+ snd_soc_update_bits(codec, WM8993_RIGHT_ADC_DIGITAL_VOLUME,
+ WM8993_ADC_VU, WM8993_ADC_VU);
+
+ /* Manualy manage the HPOUT sequencing for independent stereo
+ * control. */
+ snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ WM8993_HPOUT1_AUTO_PU, 0);
+
+ /* Use automatic clock configuration */
+ snd_soc_update_bits(codec, WM8993_CLOCKING_4, WM8993_SR_MODE, 0);
+
+ wm_hubs_handle_analogue_pdata(codec, wm8993->pdata.lineout1_diff,
+ wm8993->pdata.lineout2_diff,
+ wm8993->pdata.lineout1fb,
+ wm8993->pdata.lineout2fb,
+ wm8993->pdata.jd_scthr,
+ wm8993->pdata.jd_thr,
+ wm8993->pdata.micbias1_delay,
+ wm8993->pdata.micbias2_delay,
+ wm8993->pdata.micbias1_lvl,
+ wm8993->pdata.micbias2_lvl);
+
+ snd_soc_add_codec_controls(codec, wm8993_snd_controls,
+ ARRAY_SIZE(wm8993_snd_controls));
+ if (wm8993->pdata.num_retune_configs != 0) {
+ dev_dbg(codec->dev, "Using ReTune Mobile\n");
+ } else {
+ dev_dbg(codec->dev, "No ReTune Mobile, using normal EQ\n");
+ snd_soc_add_codec_controls(codec, wm8993_eq_controls,
+ ARRAY_SIZE(wm8993_eq_controls));
+ }
+
+ snd_soc_dapm_new_controls(dapm, wm8993_dapm_widgets,
+ ARRAY_SIZE(wm8993_dapm_widgets));
+ wm_hubs_add_analogue_controls(codec);
+
+ snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
+ wm_hubs_add_analogue_routes(codec, wm8993->pdata.lineout1_diff,
+ wm8993->pdata.lineout2_diff);
+
+ /* If the line outputs are differential then we aren't presenting
+ * VMID as an output and can disable it.
+ */
+ if (wm8993->pdata.lineout1_diff && wm8993->pdata.lineout2_diff)
+ codec->dapm.idle_bias_off = 1;
+
+ return 0;
+
+}
+
+#ifdef CONFIG_PM
+static int wm8993_suspend(struct snd_soc_codec *codec)
+{
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ int fll_fout = wm8993->fll_fout;
+ int fll_fref = wm8993->fll_fref;
+ int ret;
+
+ /* Stop the FLL in an orderly fashion */
+ ret = _wm8993_set_fll(codec, 0, 0, 0, 0);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to stop FLL\n");
+ return ret;
+ }
+
+ wm8993->fll_fout = fll_fout;
+ wm8993->fll_fref = fll_fref;
+
+ wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int wm8993_resume(struct snd_soc_codec *codec)
+{
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Restart the FLL? */
+ if (wm8993->fll_fout) {
+ int fll_fout = wm8993->fll_fout;
+ int fll_fref = wm8993->fll_fref;
+
+ wm8993->fll_fref = 0;
+ wm8993->fll_fout = 0;
+
+ ret = _wm8993_set_fll(codec, 0, wm8993->fll_src,
+ fll_fref, fll_fout);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to restart FLL\n");
+ }
+
+ return 0;
+}
+#else
+#define wm8993_suspend NULL
+#define wm8993_resume NULL
+#endif
+
+/* Tune DC servo configuration */
+static struct reg_default wm8993_regmap_patch[] = {
+ { 0x44, 3 },
+ { 0x56, 3 },
+ { 0x44, 0 },
+};
+
+static const struct regmap_config wm8993_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = WM8993_MAX_REGISTER,
+ .volatile_reg = wm8993_volatile,
+ .readable_reg = wm8993_readable,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm8993_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8993_reg_defaults),
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8993 = {
+ .probe = wm8993_probe,
+ .suspend = wm8993_suspend,
+ .resume = wm8993_resume,
+ .set_bias_level = wm8993_set_bias_level,
+};
+
+static int wm8993_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8993_priv *wm8993;
+ unsigned int reg;
+ int ret, i;
+
+ wm8993 = devm_kzalloc(&i2c->dev, sizeof(struct wm8993_priv),
+ GFP_KERNEL);
+ if (wm8993 == NULL)
+ return -ENOMEM;
+
+ wm8993->dev = &i2c->dev;
+ init_completion(&wm8993->fll_lock);
+
+ wm8993->regmap = devm_regmap_init_i2c(i2c, &wm8993_regmap);
+ if (IS_ERR(wm8993->regmap)) {
+ ret = PTR_ERR(wm8993->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, wm8993);
+
+ for (i = 0; i < ARRAY_SIZE(wm8993->supplies); i++)
+ wm8993->supplies[i].supply = wm8993_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8993->supplies),
+ wm8993->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies),
+ wm8993->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(wm8993->regmap, WM8993_SOFTWARE_RESET, &reg);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret);
+ goto err_enable;
+ }
+
+ if (reg != 0x8993) {
+ dev_err(&i2c->dev, "Invalid ID register value %x\n", reg);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ ret = regmap_write(wm8993->regmap, WM8993_SOFTWARE_RESET, 0xffff);
+ if (ret != 0)
+ goto err_enable;
+
+ ret = regmap_register_patch(wm8993->regmap, wm8993_regmap_patch,
+ ARRAY_SIZE(wm8993_regmap_patch));
+ if (ret != 0)
+ dev_warn(wm8993->dev, "Failed to apply regmap patch: %d\n",
+ ret);
+
+ if (i2c->irq) {
+ /* Put GPIO1 into interrupt mode (only GPIO1 can output IRQ) */
+ ret = regmap_update_bits(wm8993->regmap, WM8993_GPIO1,
+ WM8993_GPIO1_PD |
+ WM8993_GPIO1_SEL_MASK, 7);
+ if (ret != 0)
+ goto err_enable;
+
+ ret = request_threaded_irq(i2c->irq, NULL, wm8993_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "wm8993", wm8993);
+ if (ret != 0)
+ goto err_enable;
+
+ }
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
+
+ regcache_cache_only(wm8993->regmap, true);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8993, &wm8993_dai, 1);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+ goto err_irq;
+ }
+
+ return 0;
+
+err_irq:
+ if (i2c->irq)
+ free_irq(i2c->irq, wm8993);
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
+ return ret;
+}
+
+static int wm8993_i2c_remove(struct i2c_client *i2c)
+{
+ struct wm8993_priv *wm8993 = i2c_get_clientdata(i2c);
+
+ snd_soc_unregister_codec(&i2c->dev);
+ if (i2c->irq)
+ free_irq(i2c->irq, wm8993);
+ regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm8993_i2c_id[] = {
+ { "wm8993", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8993_i2c_id);
+
+static struct i2c_driver wm8993_i2c_driver = {
+ .driver = {
+ .name = "wm8993",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8993_i2c_probe,
+ .remove = wm8993_i2c_remove,
+ .id_table = wm8993_i2c_id,
+};
+
+module_i2c_driver(wm8993_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8993 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8993.h b/kernel/sound/soc/codecs/wm8993.h
new file mode 100644
index 000000000..4478b40c8
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8993.h
@@ -0,0 +1,2138 @@
+#ifndef WM8993_H
+#define WM8993_H
+
+#define WM8993_SYSCLK_MCLK 1
+#define WM8993_SYSCLK_FLL 2
+
+#define WM8993_FLL_MCLK 1
+#define WM8993_FLL_BCLK 2
+#define WM8993_FLL_LRCLK 3
+
+/*
+ * Register values.
+ */
+#define WM8993_SOFTWARE_RESET 0x00
+#define WM8993_POWER_MANAGEMENT_1 0x01
+#define WM8993_POWER_MANAGEMENT_2 0x02
+#define WM8993_POWER_MANAGEMENT_3 0x03
+#define WM8993_AUDIO_INTERFACE_1 0x04
+#define WM8993_AUDIO_INTERFACE_2 0x05
+#define WM8993_CLOCKING_1 0x06
+#define WM8993_CLOCKING_2 0x07
+#define WM8993_AUDIO_INTERFACE_3 0x08
+#define WM8993_AUDIO_INTERFACE_4 0x09
+#define WM8993_DAC_CTRL 0x0A
+#define WM8993_LEFT_DAC_DIGITAL_VOLUME 0x0B
+#define WM8993_RIGHT_DAC_DIGITAL_VOLUME 0x0C
+#define WM8993_DIGITAL_SIDE_TONE 0x0D
+#define WM8993_ADC_CTRL 0x0E
+#define WM8993_LEFT_ADC_DIGITAL_VOLUME 0x0F
+#define WM8993_RIGHT_ADC_DIGITAL_VOLUME 0x10
+#define WM8993_GPIO_CTRL_1 0x12
+#define WM8993_GPIO1 0x13
+#define WM8993_IRQ_DEBOUNCE 0x14
+#define WM8993_INPUTS_CLAMP_REG 0x15
+#define WM8993_GPIOCTRL_2 0x16
+#define WM8993_GPIO_POL 0x17
+#define WM8993_LEFT_LINE_INPUT_1_2_VOLUME 0x18
+#define WM8993_LEFT_LINE_INPUT_3_4_VOLUME 0x19
+#define WM8993_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A
+#define WM8993_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B
+#define WM8993_LEFT_OUTPUT_VOLUME 0x1C
+#define WM8993_RIGHT_OUTPUT_VOLUME 0x1D
+#define WM8993_LINE_OUTPUTS_VOLUME 0x1E
+#define WM8993_HPOUT2_VOLUME 0x1F
+#define WM8993_LEFT_OPGA_VOLUME 0x20
+#define WM8993_RIGHT_OPGA_VOLUME 0x21
+#define WM8993_SPKMIXL_ATTENUATION 0x22
+#define WM8993_SPKMIXR_ATTENUATION 0x23
+#define WM8993_SPKOUT_MIXERS 0x24
+#define WM8993_SPKOUT_BOOST 0x25
+#define WM8993_SPEAKER_VOLUME_LEFT 0x26
+#define WM8993_SPEAKER_VOLUME_RIGHT 0x27
+#define WM8993_INPUT_MIXER2 0x28
+#define WM8993_INPUT_MIXER3 0x29
+#define WM8993_INPUT_MIXER4 0x2A
+#define WM8993_INPUT_MIXER5 0x2B
+#define WM8993_INPUT_MIXER6 0x2C
+#define WM8993_OUTPUT_MIXER1 0x2D
+#define WM8993_OUTPUT_MIXER2 0x2E
+#define WM8993_OUTPUT_MIXER3 0x2F
+#define WM8993_OUTPUT_MIXER4 0x30
+#define WM8993_OUTPUT_MIXER5 0x31
+#define WM8993_OUTPUT_MIXER6 0x32
+#define WM8993_HPOUT2_MIXER 0x33
+#define WM8993_LINE_MIXER1 0x34
+#define WM8993_LINE_MIXER2 0x35
+#define WM8993_SPEAKER_MIXER 0x36
+#define WM8993_ADDITIONAL_CONTROL 0x37
+#define WM8993_ANTIPOP1 0x38
+#define WM8993_ANTIPOP2 0x39
+#define WM8993_MICBIAS 0x3A
+#define WM8993_FLL_CONTROL_1 0x3C
+#define WM8993_FLL_CONTROL_2 0x3D
+#define WM8993_FLL_CONTROL_3 0x3E
+#define WM8993_FLL_CONTROL_4 0x3F
+#define WM8993_FLL_CONTROL_5 0x40
+#define WM8993_CLOCKING_3 0x41
+#define WM8993_CLOCKING_4 0x42
+#define WM8993_MW_SLAVE_CONTROL 0x43
+#define WM8993_BUS_CONTROL_1 0x45
+#define WM8993_WRITE_SEQUENCER_0 0x46
+#define WM8993_WRITE_SEQUENCER_1 0x47
+#define WM8993_WRITE_SEQUENCER_2 0x48
+#define WM8993_WRITE_SEQUENCER_3 0x49
+#define WM8993_WRITE_SEQUENCER_4 0x4A
+#define WM8993_WRITE_SEQUENCER_5 0x4B
+#define WM8993_CHARGE_PUMP_1 0x4C
+#define WM8993_CLASS_W_0 0x51
+#define WM8993_DC_SERVO_0 0x54
+#define WM8993_DC_SERVO_1 0x55
+#define WM8993_DC_SERVO_3 0x57
+#define WM8993_DC_SERVO_READBACK_0 0x58
+#define WM8993_DC_SERVO_READBACK_1 0x59
+#define WM8993_DC_SERVO_READBACK_2 0x5A
+#define WM8993_ANALOGUE_HP_0 0x60
+#define WM8993_EQ1 0x62
+#define WM8993_EQ2 0x63
+#define WM8993_EQ3 0x64
+#define WM8993_EQ4 0x65
+#define WM8993_EQ5 0x66
+#define WM8993_EQ6 0x67
+#define WM8993_EQ7 0x68
+#define WM8993_EQ8 0x69
+#define WM8993_EQ9 0x6A
+#define WM8993_EQ10 0x6B
+#define WM8993_EQ11 0x6C
+#define WM8993_EQ12 0x6D
+#define WM8993_EQ13 0x6E
+#define WM8993_EQ14 0x6F
+#define WM8993_EQ15 0x70
+#define WM8993_EQ16 0x71
+#define WM8993_EQ17 0x72
+#define WM8993_EQ18 0x73
+#define WM8993_EQ19 0x74
+#define WM8993_EQ20 0x75
+#define WM8993_EQ21 0x76
+#define WM8993_EQ22 0x77
+#define WM8993_EQ23 0x78
+#define WM8993_EQ24 0x79
+#define WM8993_DIGITAL_PULLS 0x7A
+#define WM8993_DRC_CONTROL_1 0x7B
+#define WM8993_DRC_CONTROL_2 0x7C
+#define WM8993_DRC_CONTROL_3 0x7D
+#define WM8993_DRC_CONTROL_4 0x7E
+
+#define WM8993_REGISTER_COUNT 0x7F
+#define WM8993_MAX_REGISTER 0x7E
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM8993_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */
+#define WM8993_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */
+#define WM8993_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8993_SPKOUTR_ENA 0x2000 /* SPKOUTR_ENA */
+#define WM8993_SPKOUTR_ENA_MASK 0x2000 /* SPKOUTR_ENA */
+#define WM8993_SPKOUTR_ENA_SHIFT 13 /* SPKOUTR_ENA */
+#define WM8993_SPKOUTR_ENA_WIDTH 1 /* SPKOUTR_ENA */
+#define WM8993_SPKOUTL_ENA 0x1000 /* SPKOUTL_ENA */
+#define WM8993_SPKOUTL_ENA_MASK 0x1000 /* SPKOUTL_ENA */
+#define WM8993_SPKOUTL_ENA_SHIFT 12 /* SPKOUTL_ENA */
+#define WM8993_SPKOUTL_ENA_WIDTH 1 /* SPKOUTL_ENA */
+#define WM8993_HPOUT2_ENA 0x0800 /* HPOUT2_ENA */
+#define WM8993_HPOUT2_ENA_MASK 0x0800 /* HPOUT2_ENA */
+#define WM8993_HPOUT2_ENA_SHIFT 11 /* HPOUT2_ENA */
+#define WM8993_HPOUT2_ENA_WIDTH 1 /* HPOUT2_ENA */
+#define WM8993_HPOUT1L_ENA 0x0200 /* HPOUT1L_ENA */
+#define WM8993_HPOUT1L_ENA_MASK 0x0200 /* HPOUT1L_ENA */
+#define WM8993_HPOUT1L_ENA_SHIFT 9 /* HPOUT1L_ENA */
+#define WM8993_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */
+#define WM8993_HPOUT1R_ENA 0x0100 /* HPOUT1R_ENA */
+#define WM8993_HPOUT1R_ENA_MASK 0x0100 /* HPOUT1R_ENA */
+#define WM8993_HPOUT1R_ENA_SHIFT 8 /* HPOUT1R_ENA */
+#define WM8993_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */
+#define WM8993_MICB2_ENA 0x0020 /* MICB2_ENA */
+#define WM8993_MICB2_ENA_MASK 0x0020 /* MICB2_ENA */
+#define WM8993_MICB2_ENA_SHIFT 5 /* MICB2_ENA */
+#define WM8993_MICB2_ENA_WIDTH 1 /* MICB2_ENA */
+#define WM8993_MICB1_ENA 0x0010 /* MICB1_ENA */
+#define WM8993_MICB1_ENA_MASK 0x0010 /* MICB1_ENA */
+#define WM8993_MICB1_ENA_SHIFT 4 /* MICB1_ENA */
+#define WM8993_MICB1_ENA_WIDTH 1 /* MICB1_ENA */
+#define WM8993_VMID_SEL_MASK 0x0006 /* VMID_SEL - [2:1] */
+#define WM8993_VMID_SEL_SHIFT 1 /* VMID_SEL - [2:1] */
+#define WM8993_VMID_SEL_WIDTH 2 /* VMID_SEL - [2:1] */
+#define WM8993_BIAS_ENA 0x0001 /* BIAS_ENA */
+#define WM8993_BIAS_ENA_MASK 0x0001 /* BIAS_ENA */
+#define WM8993_BIAS_ENA_SHIFT 0 /* BIAS_ENA */
+#define WM8993_BIAS_ENA_WIDTH 1 /* BIAS_ENA */
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8993_TSHUT_ENA 0x4000 /* TSHUT_ENA */
+#define WM8993_TSHUT_ENA_MASK 0x4000 /* TSHUT_ENA */
+#define WM8993_TSHUT_ENA_SHIFT 14 /* TSHUT_ENA */
+#define WM8993_TSHUT_ENA_WIDTH 1 /* TSHUT_ENA */
+#define WM8993_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */
+#define WM8993_TSHUT_OPDIS_MASK 0x2000 /* TSHUT_OPDIS */
+#define WM8993_TSHUT_OPDIS_SHIFT 13 /* TSHUT_OPDIS */
+#define WM8993_TSHUT_OPDIS_WIDTH 1 /* TSHUT_OPDIS */
+#define WM8993_OPCLK_ENA 0x0800 /* OPCLK_ENA */
+#define WM8993_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */
+#define WM8993_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */
+#define WM8993_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */
+#define WM8993_MIXINL_ENA 0x0200 /* MIXINL_ENA */
+#define WM8993_MIXINL_ENA_MASK 0x0200 /* MIXINL_ENA */
+#define WM8993_MIXINL_ENA_SHIFT 9 /* MIXINL_ENA */
+#define WM8993_MIXINL_ENA_WIDTH 1 /* MIXINL_ENA */
+#define WM8993_MIXINR_ENA 0x0100 /* MIXINR_ENA */
+#define WM8993_MIXINR_ENA_MASK 0x0100 /* MIXINR_ENA */
+#define WM8993_MIXINR_ENA_SHIFT 8 /* MIXINR_ENA */
+#define WM8993_MIXINR_ENA_WIDTH 1 /* MIXINR_ENA */
+#define WM8993_IN2L_ENA 0x0080 /* IN2L_ENA */
+#define WM8993_IN2L_ENA_MASK 0x0080 /* IN2L_ENA */
+#define WM8993_IN2L_ENA_SHIFT 7 /* IN2L_ENA */
+#define WM8993_IN2L_ENA_WIDTH 1 /* IN2L_ENA */
+#define WM8993_IN1L_ENA 0x0040 /* IN1L_ENA */
+#define WM8993_IN1L_ENA_MASK 0x0040 /* IN1L_ENA */
+#define WM8993_IN1L_ENA_SHIFT 6 /* IN1L_ENA */
+#define WM8993_IN1L_ENA_WIDTH 1 /* IN1L_ENA */
+#define WM8993_IN2R_ENA 0x0020 /* IN2R_ENA */
+#define WM8993_IN2R_ENA_MASK 0x0020 /* IN2R_ENA */
+#define WM8993_IN2R_ENA_SHIFT 5 /* IN2R_ENA */
+#define WM8993_IN2R_ENA_WIDTH 1 /* IN2R_ENA */
+#define WM8993_IN1R_ENA 0x0010 /* IN1R_ENA */
+#define WM8993_IN1R_ENA_MASK 0x0010 /* IN1R_ENA */
+#define WM8993_IN1R_ENA_SHIFT 4 /* IN1R_ENA */
+#define WM8993_IN1R_ENA_WIDTH 1 /* IN1R_ENA */
+#define WM8993_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8993_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */
+#define WM8993_ADCL_ENA_SHIFT 1 /* ADCL_ENA */
+#define WM8993_ADCL_ENA_WIDTH 1 /* ADCL_ENA */
+#define WM8993_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8993_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */
+#define WM8993_ADCR_ENA_SHIFT 0 /* ADCR_ENA */
+#define WM8993_ADCR_ENA_WIDTH 1 /* ADCR_ENA */
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8993_LINEOUT1N_ENA 0x2000 /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1N_ENA_MASK 0x2000 /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1N_ENA_SHIFT 13 /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1N_ENA_WIDTH 1 /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1P_ENA 0x1000 /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT1P_ENA_MASK 0x1000 /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT1P_ENA_SHIFT 12 /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT1P_ENA_WIDTH 1 /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT2N_ENA 0x0800 /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2N_ENA_MASK 0x0800 /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2N_ENA_SHIFT 11 /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2N_ENA_WIDTH 1 /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2P_ENA 0x0400 /* LINEOUT2P_ENA */
+#define WM8993_LINEOUT2P_ENA_MASK 0x0400 /* LINEOUT2P_ENA */
+#define WM8993_LINEOUT2P_ENA_SHIFT 10 /* LINEOUT2P_ENA */
+#define WM8993_LINEOUT2P_ENA_WIDTH 1 /* LINEOUT2P_ENA */
+#define WM8993_SPKRVOL_ENA 0x0200 /* SPKRVOL_ENA */
+#define WM8993_SPKRVOL_ENA_MASK 0x0200 /* SPKRVOL_ENA */
+#define WM8993_SPKRVOL_ENA_SHIFT 9 /* SPKRVOL_ENA */
+#define WM8993_SPKRVOL_ENA_WIDTH 1 /* SPKRVOL_ENA */
+#define WM8993_SPKLVOL_ENA 0x0100 /* SPKLVOL_ENA */
+#define WM8993_SPKLVOL_ENA_MASK 0x0100 /* SPKLVOL_ENA */
+#define WM8993_SPKLVOL_ENA_SHIFT 8 /* SPKLVOL_ENA */
+#define WM8993_SPKLVOL_ENA_WIDTH 1 /* SPKLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA 0x0080 /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA_MASK 0x0080 /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA_SHIFT 7 /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA_WIDTH 1 /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA 0x0040 /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA_MASK 0x0040 /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA_SHIFT 6 /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA_WIDTH 1 /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTL_ENA 0x0020 /* MIXOUTL_ENA */
+#define WM8993_MIXOUTL_ENA_MASK 0x0020 /* MIXOUTL_ENA */
+#define WM8993_MIXOUTL_ENA_SHIFT 5 /* MIXOUTL_ENA */
+#define WM8993_MIXOUTL_ENA_WIDTH 1 /* MIXOUTL_ENA */
+#define WM8993_MIXOUTR_ENA 0x0010 /* MIXOUTR_ENA */
+#define WM8993_MIXOUTR_ENA_MASK 0x0010 /* MIXOUTR_ENA */
+#define WM8993_MIXOUTR_ENA_SHIFT 4 /* MIXOUTR_ENA */
+#define WM8993_MIXOUTR_ENA_WIDTH 1 /* MIXOUTR_ENA */
+#define WM8993_DACL_ENA 0x0002 /* DACL_ENA */
+#define WM8993_DACL_ENA_MASK 0x0002 /* DACL_ENA */
+#define WM8993_DACL_ENA_SHIFT 1 /* DACL_ENA */
+#define WM8993_DACL_ENA_WIDTH 1 /* DACL_ENA */
+#define WM8993_DACR_ENA 0x0001 /* DACR_ENA */
+#define WM8993_DACR_ENA_MASK 0x0001 /* DACR_ENA */
+#define WM8993_DACR_ENA_SHIFT 0 /* DACR_ENA */
+#define WM8993_DACR_ENA_WIDTH 1 /* DACR_ENA */
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8993_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */
+#define WM8993_AIFADCL_SRC_MASK 0x8000 /* AIFADCL_SRC */
+#define WM8993_AIFADCL_SRC_SHIFT 15 /* AIFADCL_SRC */
+#define WM8993_AIFADCL_SRC_WIDTH 1 /* AIFADCL_SRC */
+#define WM8993_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */
+#define WM8993_AIFADCR_SRC_MASK 0x4000 /* AIFADCR_SRC */
+#define WM8993_AIFADCR_SRC_SHIFT 14 /* AIFADCR_SRC */
+#define WM8993_AIFADCR_SRC_WIDTH 1 /* AIFADCR_SRC */
+#define WM8993_AIFADC_TDM 0x2000 /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_MASK 0x2000 /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_SHIFT 13 /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_WIDTH 1 /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */
+#define WM8993_AIFADC_TDM_CHAN_MASK 0x1000 /* AIFADC_TDM_CHAN */
+#define WM8993_AIFADC_TDM_CHAN_SHIFT 12 /* AIFADC_TDM_CHAN */
+#define WM8993_AIFADC_TDM_CHAN_WIDTH 1 /* AIFADC_TDM_CHAN */
+#define WM8993_BCLK_DIR 0x0200 /* BCLK_DIR */
+#define WM8993_BCLK_DIR_MASK 0x0200 /* BCLK_DIR */
+#define WM8993_BCLK_DIR_SHIFT 9 /* BCLK_DIR */
+#define WM8993_BCLK_DIR_WIDTH 1 /* BCLK_DIR */
+#define WM8993_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */
+#define WM8993_AIF_BCLK_INV_MASK 0x0100 /* AIF_BCLK_INV */
+#define WM8993_AIF_BCLK_INV_SHIFT 8 /* AIF_BCLK_INV */
+#define WM8993_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */
+#define WM8993_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */
+#define WM8993_AIF_LRCLK_INV_MASK 0x0080 /* AIF_LRCLK_INV */
+#define WM8993_AIF_LRCLK_INV_SHIFT 7 /* AIF_LRCLK_INV */
+#define WM8993_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */
+#define WM8993_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */
+#define WM8993_AIF_WL_SHIFT 5 /* AIF_WL - [6:5] */
+#define WM8993_AIF_WL_WIDTH 2 /* AIF_WL - [6:5] */
+#define WM8993_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */
+#define WM8993_AIF_FMT_SHIFT 3 /* AIF_FMT - [4:3] */
+#define WM8993_AIF_FMT_WIDTH 2 /* AIF_FMT - [4:3] */
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8993_AIFDACL_SRC 0x8000 /* AIFDACL_SRC */
+#define WM8993_AIFDACL_SRC_MASK 0x8000 /* AIFDACL_SRC */
+#define WM8993_AIFDACL_SRC_SHIFT 15 /* AIFDACL_SRC */
+#define WM8993_AIFDACL_SRC_WIDTH 1 /* AIFDACL_SRC */
+#define WM8993_AIFDACR_SRC 0x4000 /* AIFDACR_SRC */
+#define WM8993_AIFDACR_SRC_MASK 0x4000 /* AIFDACR_SRC */
+#define WM8993_AIFDACR_SRC_SHIFT 14 /* AIFDACR_SRC */
+#define WM8993_AIFDACR_SRC_WIDTH 1 /* AIFDACR_SRC */
+#define WM8993_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_MASK 0x2000 /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_SHIFT 13 /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_WIDTH 1 /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8993_AIFDAC_TDM_CHAN_MASK 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8993_AIFDAC_TDM_CHAN_SHIFT 12 /* AIFDAC_TDM_CHAN */
+#define WM8993_AIFDAC_TDM_CHAN_WIDTH 1 /* AIFDAC_TDM_CHAN */
+#define WM8993_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST - [11:10] */
+#define WM8993_DAC_BOOST_SHIFT 10 /* DAC_BOOST - [11:10] */
+#define WM8993_DAC_BOOST_WIDTH 2 /* DAC_BOOST - [11:10] */
+#define WM8993_DAC_COMP 0x0010 /* DAC_COMP */
+#define WM8993_DAC_COMP_MASK 0x0010 /* DAC_COMP */
+#define WM8993_DAC_COMP_SHIFT 4 /* DAC_COMP */
+#define WM8993_DAC_COMP_WIDTH 1 /* DAC_COMP */
+#define WM8993_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */
+#define WM8993_DAC_COMPMODE_MASK 0x0008 /* DAC_COMPMODE */
+#define WM8993_DAC_COMPMODE_SHIFT 3 /* DAC_COMPMODE */
+#define WM8993_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */
+#define WM8993_ADC_COMP 0x0004 /* ADC_COMP */
+#define WM8993_ADC_COMP_MASK 0x0004 /* ADC_COMP */
+#define WM8993_ADC_COMP_SHIFT 2 /* ADC_COMP */
+#define WM8993_ADC_COMP_WIDTH 1 /* ADC_COMP */
+#define WM8993_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */
+#define WM8993_ADC_COMPMODE_MASK 0x0002 /* ADC_COMPMODE */
+#define WM8993_ADC_COMPMODE_SHIFT 1 /* ADC_COMPMODE */
+#define WM8993_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */
+#define WM8993_LOOPBACK 0x0001 /* LOOPBACK */
+#define WM8993_LOOPBACK_MASK 0x0001 /* LOOPBACK */
+#define WM8993_LOOPBACK_SHIFT 0 /* LOOPBACK */
+#define WM8993_LOOPBACK_WIDTH 1 /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking 1
+ */
+#define WM8993_TOCLK_RATE 0x8000 /* TOCLK_RATE */
+#define WM8993_TOCLK_RATE_MASK 0x8000 /* TOCLK_RATE */
+#define WM8993_TOCLK_RATE_SHIFT 15 /* TOCLK_RATE */
+#define WM8993_TOCLK_RATE_WIDTH 1 /* TOCLK_RATE */
+#define WM8993_TOCLK_ENA 0x4000 /* TOCLK_ENA */
+#define WM8993_TOCLK_ENA_MASK 0x4000 /* TOCLK_ENA */
+#define WM8993_TOCLK_ENA_SHIFT 14 /* TOCLK_ENA */
+#define WM8993_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */
+#define WM8993_OPCLK_DIV_MASK 0x1E00 /* OPCLK_DIV - [12:9] */
+#define WM8993_OPCLK_DIV_SHIFT 9 /* OPCLK_DIV - [12:9] */
+#define WM8993_OPCLK_DIV_WIDTH 4 /* OPCLK_DIV - [12:9] */
+#define WM8993_DCLK_DIV_MASK 0x01C0 /* DCLK_DIV - [8:6] */
+#define WM8993_DCLK_DIV_SHIFT 6 /* DCLK_DIV - [8:6] */
+#define WM8993_DCLK_DIV_WIDTH 3 /* DCLK_DIV - [8:6] */
+#define WM8993_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */
+#define WM8993_BCLK_DIV_SHIFT 1 /* BCLK_DIV - [4:1] */
+#define WM8993_BCLK_DIV_WIDTH 4 /* BCLK_DIV - [4:1] */
+
+/*
+ * R7 (0x07) - Clocking 2
+ */
+#define WM8993_MCLK_SRC 0x8000 /* MCLK_SRC */
+#define WM8993_MCLK_SRC_MASK 0x8000 /* MCLK_SRC */
+#define WM8993_MCLK_SRC_SHIFT 15 /* MCLK_SRC */
+#define WM8993_MCLK_SRC_WIDTH 1 /* MCLK_SRC */
+#define WM8993_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */
+#define WM8993_SYSCLK_SRC_MASK 0x4000 /* SYSCLK_SRC */
+#define WM8993_SYSCLK_SRC_SHIFT 14 /* SYSCLK_SRC */
+#define WM8993_SYSCLK_SRC_WIDTH 1 /* SYSCLK_SRC */
+#define WM8993_MCLK_DIV 0x1000 /* MCLK_DIV */
+#define WM8993_MCLK_DIV_MASK 0x1000 /* MCLK_DIV */
+#define WM8993_MCLK_DIV_SHIFT 12 /* MCLK_DIV */
+#define WM8993_MCLK_DIV_WIDTH 1 /* MCLK_DIV */
+#define WM8993_MCLK_INV 0x0400 /* MCLK_INV */
+#define WM8993_MCLK_INV_MASK 0x0400 /* MCLK_INV */
+#define WM8993_MCLK_INV_SHIFT 10 /* MCLK_INV */
+#define WM8993_MCLK_INV_WIDTH 1 /* MCLK_INV */
+#define WM8993_ADC_DIV_MASK 0x00E0 /* ADC_DIV - [7:5] */
+#define WM8993_ADC_DIV_SHIFT 5 /* ADC_DIV - [7:5] */
+#define WM8993_ADC_DIV_WIDTH 3 /* ADC_DIV - [7:5] */
+#define WM8993_DAC_DIV_MASK 0x001C /* DAC_DIV - [4:2] */
+#define WM8993_DAC_DIV_SHIFT 2 /* DAC_DIV - [4:2] */
+#define WM8993_DAC_DIV_WIDTH 3 /* DAC_DIV - [4:2] */
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8993_AIF_MSTR1 0x8000 /* AIF_MSTR1 */
+#define WM8993_AIF_MSTR1_MASK 0x8000 /* AIF_MSTR1 */
+#define WM8993_AIF_MSTR1_SHIFT 15 /* AIF_MSTR1 */
+#define WM8993_AIF_MSTR1_WIDTH 1 /* AIF_MSTR1 */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8993_AIF_TRIS 0x2000 /* AIF_TRIS */
+#define WM8993_AIF_TRIS_MASK 0x2000 /* AIF_TRIS */
+#define WM8993_AIF_TRIS_SHIFT 13 /* AIF_TRIS */
+#define WM8993_AIF_TRIS_WIDTH 1 /* AIF_TRIS */
+#define WM8993_LRCLK_DIR 0x0800 /* LRCLK_DIR */
+#define WM8993_LRCLK_DIR_MASK 0x0800 /* LRCLK_DIR */
+#define WM8993_LRCLK_DIR_SHIFT 11 /* LRCLK_DIR */
+#define WM8993_LRCLK_DIR_WIDTH 1 /* LRCLK_DIR */
+#define WM8993_LRCLK_RATE_MASK 0x07FF /* LRCLK_RATE - [10:0] */
+#define WM8993_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [10:0] */
+#define WM8993_LRCLK_RATE_WIDTH 11 /* LRCLK_RATE - [10:0] */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8993_DAC_OSR128 0x2000 /* DAC_OSR128 */
+#define WM8993_DAC_OSR128_MASK 0x2000 /* DAC_OSR128 */
+#define WM8993_DAC_OSR128_SHIFT 13 /* DAC_OSR128 */
+#define WM8993_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */
+#define WM8993_DAC_MONO 0x0200 /* DAC_MONO */
+#define WM8993_DAC_MONO_MASK 0x0200 /* DAC_MONO */
+#define WM8993_DAC_MONO_SHIFT 9 /* DAC_MONO */
+#define WM8993_DAC_MONO_WIDTH 1 /* DAC_MONO */
+#define WM8993_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */
+#define WM8993_DAC_SB_FILT_MASK 0x0100 /* DAC_SB_FILT */
+#define WM8993_DAC_SB_FILT_SHIFT 8 /* DAC_SB_FILT */
+#define WM8993_DAC_SB_FILT_WIDTH 1 /* DAC_SB_FILT */
+#define WM8993_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */
+#define WM8993_DAC_MUTERATE_MASK 0x0080 /* DAC_MUTERATE */
+#define WM8993_DAC_MUTERATE_SHIFT 7 /* DAC_MUTERATE */
+#define WM8993_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
+#define WM8993_DAC_UNMUTE_RAMP 0x0040 /* DAC_UNMUTE_RAMP */
+#define WM8993_DAC_UNMUTE_RAMP_MASK 0x0040 /* DAC_UNMUTE_RAMP */
+#define WM8993_DAC_UNMUTE_RAMP_SHIFT 6 /* DAC_UNMUTE_RAMP */
+#define WM8993_DAC_UNMUTE_RAMP_WIDTH 1 /* DAC_UNMUTE_RAMP */
+#define WM8993_DEEMPH_MASK 0x0030 /* DEEMPH - [5:4] */
+#define WM8993_DEEMPH_SHIFT 4 /* DEEMPH - [5:4] */
+#define WM8993_DEEMPH_WIDTH 2 /* DEEMPH - [5:4] */
+#define WM8993_DAC_MUTE 0x0004 /* DAC_MUTE */
+#define WM8993_DAC_MUTE_MASK 0x0004 /* DAC_MUTE */
+#define WM8993_DAC_MUTE_SHIFT 2 /* DAC_MUTE */
+#define WM8993_DAC_MUTE_WIDTH 1 /* DAC_MUTE */
+#define WM8993_DACL_DATINV 0x0002 /* DACL_DATINV */
+#define WM8993_DACL_DATINV_MASK 0x0002 /* DACL_DATINV */
+#define WM8993_DACL_DATINV_SHIFT 1 /* DACL_DATINV */
+#define WM8993_DACL_DATINV_WIDTH 1 /* DACL_DATINV */
+#define WM8993_DACR_DATINV 0x0001 /* DACR_DATINV */
+#define WM8993_DACR_DATINV_MASK 0x0001 /* DACR_DATINV */
+#define WM8993_DACR_DATINV_SHIFT 0 /* DACR_DATINV */
+#define WM8993_DACR_DATINV_WIDTH 1 /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8993_DAC_VU 0x0100 /* DAC_VU */
+#define WM8993_DAC_VU_MASK 0x0100 /* DAC_VU */
+#define WM8993_DAC_VU_SHIFT 8 /* DAC_VU */
+#define WM8993_DAC_VU_WIDTH 1 /* DAC_VU */
+#define WM8993_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */
+#define WM8993_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */
+#define WM8993_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */
+
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8993_DAC_VU 0x0100 /* DAC_VU */
+#define WM8993_DAC_VU_MASK 0x0100 /* DAC_VU */
+#define WM8993_DAC_VU_SHIFT 8 /* DAC_VU */
+#define WM8993_DAC_VU_WIDTH 1 /* DAC_VU */
+#define WM8993_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */
+#define WM8993_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */
+#define WM8993_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */
+
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8993_ADCL_DAC_SVOL_MASK 0x1E00 /* ADCL_DAC_SVOL - [12:9] */
+#define WM8993_ADCL_DAC_SVOL_SHIFT 9 /* ADCL_DAC_SVOL - [12:9] */
+#define WM8993_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [12:9] */
+#define WM8993_ADCR_DAC_SVOL_MASK 0x01E0 /* ADCR_DAC_SVOL - [8:5] */
+#define WM8993_ADCR_DAC_SVOL_SHIFT 5 /* ADCR_DAC_SVOL - [8:5] */
+#define WM8993_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [8:5] */
+#define WM8993_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */
+#define WM8993_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */
+#define WM8993_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */
+#define WM8993_ADC_TO_DACR_MASK 0x0003 /* ADC_TO_DACR - [1:0] */
+#define WM8993_ADC_TO_DACR_SHIFT 0 /* ADC_TO_DACR - [1:0] */
+#define WM8993_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [1:0] */
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8993_ADC_OSR128 0x0200 /* ADC_OSR128 */
+#define WM8993_ADC_OSR128_MASK 0x0200 /* ADC_OSR128 */
+#define WM8993_ADC_OSR128_SHIFT 9 /* ADC_OSR128 */
+#define WM8993_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */
+#define WM8993_ADC_HPF 0x0100 /* ADC_HPF */
+#define WM8993_ADC_HPF_MASK 0x0100 /* ADC_HPF */
+#define WM8993_ADC_HPF_SHIFT 8 /* ADC_HPF */
+#define WM8993_ADC_HPF_WIDTH 1 /* ADC_HPF */
+#define WM8993_ADC_HPF_CUT_MASK 0x0060 /* ADC_HPF_CUT - [6:5] */
+#define WM8993_ADC_HPF_CUT_SHIFT 5 /* ADC_HPF_CUT - [6:5] */
+#define WM8993_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [6:5] */
+#define WM8993_ADCL_DATINV 0x0002 /* ADCL_DATINV */
+#define WM8993_ADCL_DATINV_MASK 0x0002 /* ADCL_DATINV */
+#define WM8993_ADCL_DATINV_SHIFT 1 /* ADCL_DATINV */
+#define WM8993_ADCL_DATINV_WIDTH 1 /* ADCL_DATINV */
+#define WM8993_ADCR_DATINV 0x0001 /* ADCR_DATINV */
+#define WM8993_ADCR_DATINV_MASK 0x0001 /* ADCR_DATINV */
+#define WM8993_ADCR_DATINV_SHIFT 0 /* ADCR_DATINV */
+#define WM8993_ADCR_DATINV_WIDTH 1 /* ADCR_DATINV */
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8993_ADC_VU 0x0100 /* ADC_VU */
+#define WM8993_ADC_VU_MASK 0x0100 /* ADC_VU */
+#define WM8993_ADC_VU_SHIFT 8 /* ADC_VU */
+#define WM8993_ADC_VU_WIDTH 1 /* ADC_VU */
+#define WM8993_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */
+#define WM8993_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */
+#define WM8993_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8993_ADC_VU 0x0100 /* ADC_VU */
+#define WM8993_ADC_VU_MASK 0x0100 /* ADC_VU */
+#define WM8993_ADC_VU_SHIFT 8 /* ADC_VU */
+#define WM8993_ADC_VU_WIDTH 1 /* ADC_VU */
+#define WM8993_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */
+#define WM8993_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */
+#define WM8993_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8993_JD2_SC_EINT 0x8000 /* JD2_SC_EINT */
+#define WM8993_JD2_SC_EINT_MASK 0x8000 /* JD2_SC_EINT */
+#define WM8993_JD2_SC_EINT_SHIFT 15 /* JD2_SC_EINT */
+#define WM8993_JD2_SC_EINT_WIDTH 1 /* JD2_SC_EINT */
+#define WM8993_JD2_EINT 0x4000 /* JD2_EINT */
+#define WM8993_JD2_EINT_MASK 0x4000 /* JD2_EINT */
+#define WM8993_JD2_EINT_SHIFT 14 /* JD2_EINT */
+#define WM8993_JD2_EINT_WIDTH 1 /* JD2_EINT */
+#define WM8993_WSEQ_EINT 0x2000 /* WSEQ_EINT */
+#define WM8993_WSEQ_EINT_MASK 0x2000 /* WSEQ_EINT */
+#define WM8993_WSEQ_EINT_SHIFT 13 /* WSEQ_EINT */
+#define WM8993_WSEQ_EINT_WIDTH 1 /* WSEQ_EINT */
+#define WM8993_IRQ 0x1000 /* IRQ */
+#define WM8993_IRQ_MASK 0x1000 /* IRQ */
+#define WM8993_IRQ_SHIFT 12 /* IRQ */
+#define WM8993_IRQ_WIDTH 1 /* IRQ */
+#define WM8993_TEMPOK_EINT 0x0800 /* TEMPOK_EINT */
+#define WM8993_TEMPOK_EINT_MASK 0x0800 /* TEMPOK_EINT */
+#define WM8993_TEMPOK_EINT_SHIFT 11 /* TEMPOK_EINT */
+#define WM8993_TEMPOK_EINT_WIDTH 1 /* TEMPOK_EINT */
+#define WM8993_JD1_SC_EINT 0x0400 /* JD1_SC_EINT */
+#define WM8993_JD1_SC_EINT_MASK 0x0400 /* JD1_SC_EINT */
+#define WM8993_JD1_SC_EINT_SHIFT 10 /* JD1_SC_EINT */
+#define WM8993_JD1_SC_EINT_WIDTH 1 /* JD1_SC_EINT */
+#define WM8993_JD1_EINT 0x0200 /* JD1_EINT */
+#define WM8993_JD1_EINT_MASK 0x0200 /* JD1_EINT */
+#define WM8993_JD1_EINT_SHIFT 9 /* JD1_EINT */
+#define WM8993_JD1_EINT_WIDTH 1 /* JD1_EINT */
+#define WM8993_FLL_LOCK_EINT 0x0100 /* FLL_LOCK_EINT */
+#define WM8993_FLL_LOCK_EINT_MASK 0x0100 /* FLL_LOCK_EINT */
+#define WM8993_FLL_LOCK_EINT_SHIFT 8 /* FLL_LOCK_EINT */
+#define WM8993_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */
+#define WM8993_GPI8_EINT 0x0080 /* GPI8_EINT */
+#define WM8993_GPI8_EINT_MASK 0x0080 /* GPI8_EINT */
+#define WM8993_GPI8_EINT_SHIFT 7 /* GPI8_EINT */
+#define WM8993_GPI8_EINT_WIDTH 1 /* GPI8_EINT */
+#define WM8993_GPI7_EINT 0x0040 /* GPI7_EINT */
+#define WM8993_GPI7_EINT_MASK 0x0040 /* GPI7_EINT */
+#define WM8993_GPI7_EINT_SHIFT 6 /* GPI7_EINT */
+#define WM8993_GPI7_EINT_WIDTH 1 /* GPI7_EINT */
+#define WM8993_GPIO1_EINT 0x0001 /* GPIO1_EINT */
+#define WM8993_GPIO1_EINT_MASK 0x0001 /* GPIO1_EINT */
+#define WM8993_GPIO1_EINT_SHIFT 0 /* GPIO1_EINT */
+#define WM8993_GPIO1_EINT_WIDTH 1 /* GPIO1_EINT */
+
+/*
+ * R19 (0x13) - GPIO1
+ */
+#define WM8993_GPIO1_PU 0x0020 /* GPIO1_PU */
+#define WM8993_GPIO1_PU_MASK 0x0020 /* GPIO1_PU */
+#define WM8993_GPIO1_PU_SHIFT 5 /* GPIO1_PU */
+#define WM8993_GPIO1_PU_WIDTH 1 /* GPIO1_PU */
+#define WM8993_GPIO1_PD 0x0010 /* GPIO1_PD */
+#define WM8993_GPIO1_PD_MASK 0x0010 /* GPIO1_PD */
+#define WM8993_GPIO1_PD_SHIFT 4 /* GPIO1_PD */
+#define WM8993_GPIO1_PD_WIDTH 1 /* GPIO1_PD */
+#define WM8993_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */
+#define WM8993_GPIO1_SEL_SHIFT 0 /* GPIO1_SEL - [3:0] */
+#define WM8993_GPIO1_SEL_WIDTH 4 /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - IRQ_DEBOUNCE
+ */
+#define WM8993_JD2_SC_DB 0x8000 /* JD2_SC_DB */
+#define WM8993_JD2_SC_DB_MASK 0x8000 /* JD2_SC_DB */
+#define WM8993_JD2_SC_DB_SHIFT 15 /* JD2_SC_DB */
+#define WM8993_JD2_SC_DB_WIDTH 1 /* JD2_SC_DB */
+#define WM8993_JD2_DB 0x4000 /* JD2_DB */
+#define WM8993_JD2_DB_MASK 0x4000 /* JD2_DB */
+#define WM8993_JD2_DB_SHIFT 14 /* JD2_DB */
+#define WM8993_JD2_DB_WIDTH 1 /* JD2_DB */
+#define WM8993_WSEQ_DB 0x2000 /* WSEQ_DB */
+#define WM8993_WSEQ_DB_MASK 0x2000 /* WSEQ_DB */
+#define WM8993_WSEQ_DB_SHIFT 13 /* WSEQ_DB */
+#define WM8993_WSEQ_DB_WIDTH 1 /* WSEQ_DB */
+#define WM8993_TEMPOK_DB 0x0800 /* TEMPOK_DB */
+#define WM8993_TEMPOK_DB_MASK 0x0800 /* TEMPOK_DB */
+#define WM8993_TEMPOK_DB_SHIFT 11 /* TEMPOK_DB */
+#define WM8993_TEMPOK_DB_WIDTH 1 /* TEMPOK_DB */
+#define WM8993_JD1_SC_DB 0x0400 /* JD1_SC_DB */
+#define WM8993_JD1_SC_DB_MASK 0x0400 /* JD1_SC_DB */
+#define WM8993_JD1_SC_DB_SHIFT 10 /* JD1_SC_DB */
+#define WM8993_JD1_SC_DB_WIDTH 1 /* JD1_SC_DB */
+#define WM8993_JD1_DB 0x0200 /* JD1_DB */
+#define WM8993_JD1_DB_MASK 0x0200 /* JD1_DB */
+#define WM8993_JD1_DB_SHIFT 9 /* JD1_DB */
+#define WM8993_JD1_DB_WIDTH 1 /* JD1_DB */
+#define WM8993_FLL_LOCK_DB 0x0100 /* FLL_LOCK_DB */
+#define WM8993_FLL_LOCK_DB_MASK 0x0100 /* FLL_LOCK_DB */
+#define WM8993_FLL_LOCK_DB_SHIFT 8 /* FLL_LOCK_DB */
+#define WM8993_FLL_LOCK_DB_WIDTH 1 /* FLL_LOCK_DB */
+#define WM8993_GPI8_DB 0x0080 /* GPI8_DB */
+#define WM8993_GPI8_DB_MASK 0x0080 /* GPI8_DB */
+#define WM8993_GPI8_DB_SHIFT 7 /* GPI8_DB */
+#define WM8993_GPI8_DB_WIDTH 1 /* GPI8_DB */
+#define WM8993_GPI7_DB 0x0008 /* GPI7_DB */
+#define WM8993_GPI7_DB_MASK 0x0008 /* GPI7_DB */
+#define WM8993_GPI7_DB_SHIFT 3 /* GPI7_DB */
+#define WM8993_GPI7_DB_WIDTH 1 /* GPI7_DB */
+#define WM8993_GPIO1_DB 0x0001 /* GPIO1_DB */
+#define WM8993_GPIO1_DB_MASK 0x0001 /* GPIO1_DB */
+#define WM8993_GPIO1_DB_SHIFT 0 /* GPIO1_DB */
+#define WM8993_GPIO1_DB_WIDTH 1 /* GPIO1_DB */
+
+/*
+ * R21 (0x15) - Inputs Clamp
+ */
+#define WM8993_INPUTS_CLAMP 0x0040 /* INPUTS_CLAMP */
+#define WM8993_INPUTS_CLAMP_MASK 0x0040 /* INPUTS_CLAMP */
+#define WM8993_INPUTS_CLAMP_SHIFT 7 /* INPUTS_CLAMP */
+#define WM8993_INPUTS_CLAMP_WIDTH 1 /* INPUTS_CLAMP */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8993_IM_JD2_EINT 0x2000 /* IM_JD2_EINT */
+#define WM8993_IM_JD2_EINT_MASK 0x2000 /* IM_JD2_EINT */
+#define WM8993_IM_JD2_EINT_SHIFT 13 /* IM_JD2_EINT */
+#define WM8993_IM_JD2_EINT_WIDTH 1 /* IM_JD2_EINT */
+#define WM8993_IM_JD2_SC_EINT 0x1000 /* IM_JD2_SC_EINT */
+#define WM8993_IM_JD2_SC_EINT_MASK 0x1000 /* IM_JD2_SC_EINT */
+#define WM8993_IM_JD2_SC_EINT_SHIFT 12 /* IM_JD2_SC_EINT */
+#define WM8993_IM_JD2_SC_EINT_WIDTH 1 /* IM_JD2_SC_EINT */
+#define WM8993_IM_TEMPOK_EINT 0x0800 /* IM_TEMPOK_EINT */
+#define WM8993_IM_TEMPOK_EINT_MASK 0x0800 /* IM_TEMPOK_EINT */
+#define WM8993_IM_TEMPOK_EINT_SHIFT 11 /* IM_TEMPOK_EINT */
+#define WM8993_IM_TEMPOK_EINT_WIDTH 1 /* IM_TEMPOK_EINT */
+#define WM8993_IM_JD1_SC_EINT 0x0400 /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_SC_EINT_MASK 0x0400 /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_SC_EINT_SHIFT 10 /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_SC_EINT_WIDTH 1 /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_EINT 0x0200 /* IM_JD1_EINT */
+#define WM8993_IM_JD1_EINT_MASK 0x0200 /* IM_JD1_EINT */
+#define WM8993_IM_JD1_EINT_SHIFT 9 /* IM_JD1_EINT */
+#define WM8993_IM_JD1_EINT_WIDTH 1 /* IM_JD1_EINT */
+#define WM8993_IM_FLL_LOCK_EINT 0x0100 /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_FLL_LOCK_EINT_MASK 0x0100 /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_FLL_LOCK_EINT_SHIFT 8 /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_GPI8_EINT 0x0040 /* IM_GPI8_EINT */
+#define WM8993_IM_GPI8_EINT_MASK 0x0040 /* IM_GPI8_EINT */
+#define WM8993_IM_GPI8_EINT_SHIFT 6 /* IM_GPI8_EINT */
+#define WM8993_IM_GPI8_EINT_WIDTH 1 /* IM_GPI8_EINT */
+#define WM8993_IM_GPIO1_EINT 0x0020 /* IM_GPIO1_EINT */
+#define WM8993_IM_GPIO1_EINT_MASK 0x0020 /* IM_GPIO1_EINT */
+#define WM8993_IM_GPIO1_EINT_SHIFT 5 /* IM_GPIO1_EINT */
+#define WM8993_IM_GPIO1_EINT_WIDTH 1 /* IM_GPIO1_EINT */
+#define WM8993_GPI8_ENA 0x0010 /* GPI8_ENA */
+#define WM8993_GPI8_ENA_MASK 0x0010 /* GPI8_ENA */
+#define WM8993_GPI8_ENA_SHIFT 4 /* GPI8_ENA */
+#define WM8993_GPI8_ENA_WIDTH 1 /* GPI8_ENA */
+#define WM8993_IM_GPI7_EINT 0x0004 /* IM_GPI7_EINT */
+#define WM8993_IM_GPI7_EINT_MASK 0x0004 /* IM_GPI7_EINT */
+#define WM8993_IM_GPI7_EINT_SHIFT 2 /* IM_GPI7_EINT */
+#define WM8993_IM_GPI7_EINT_WIDTH 1 /* IM_GPI7_EINT */
+#define WM8993_IM_WSEQ_EINT 0x0002 /* IM_WSEQ_EINT */
+#define WM8993_IM_WSEQ_EINT_MASK 0x0002 /* IM_WSEQ_EINT */
+#define WM8993_IM_WSEQ_EINT_SHIFT 1 /* IM_WSEQ_EINT */
+#define WM8993_IM_WSEQ_EINT_WIDTH 1 /* IM_WSEQ_EINT */
+#define WM8993_GPI7_ENA 0x0001 /* GPI7_ENA */
+#define WM8993_GPI7_ENA_MASK 0x0001 /* GPI7_ENA */
+#define WM8993_GPI7_ENA_SHIFT 0 /* GPI7_ENA */
+#define WM8993_GPI7_ENA_WIDTH 1 /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8993_JD2_SC_POL 0x8000 /* JD2_SC_POL */
+#define WM8993_JD2_SC_POL_MASK 0x8000 /* JD2_SC_POL */
+#define WM8993_JD2_SC_POL_SHIFT 15 /* JD2_SC_POL */
+#define WM8993_JD2_SC_POL_WIDTH 1 /* JD2_SC_POL */
+#define WM8993_JD2_POL 0x4000 /* JD2_POL */
+#define WM8993_JD2_POL_MASK 0x4000 /* JD2_POL */
+#define WM8993_JD2_POL_SHIFT 14 /* JD2_POL */
+#define WM8993_JD2_POL_WIDTH 1 /* JD2_POL */
+#define WM8993_WSEQ_POL 0x2000 /* WSEQ_POL */
+#define WM8993_WSEQ_POL_MASK 0x2000 /* WSEQ_POL */
+#define WM8993_WSEQ_POL_SHIFT 13 /* WSEQ_POL */
+#define WM8993_WSEQ_POL_WIDTH 1 /* WSEQ_POL */
+#define WM8993_IRQ_POL 0x1000 /* IRQ_POL */
+#define WM8993_IRQ_POL_MASK 0x1000 /* IRQ_POL */
+#define WM8993_IRQ_POL_SHIFT 12 /* IRQ_POL */
+#define WM8993_IRQ_POL_WIDTH 1 /* IRQ_POL */
+#define WM8993_TEMPOK_POL 0x0800 /* TEMPOK_POL */
+#define WM8993_TEMPOK_POL_MASK 0x0800 /* TEMPOK_POL */
+#define WM8993_TEMPOK_POL_SHIFT 11 /* TEMPOK_POL */
+#define WM8993_TEMPOK_POL_WIDTH 1 /* TEMPOK_POL */
+#define WM8993_JD1_SC_POL 0x0400 /* JD1_SC_POL */
+#define WM8993_JD1_SC_POL_MASK 0x0400 /* JD1_SC_POL */
+#define WM8993_JD1_SC_POL_SHIFT 10 /* JD1_SC_POL */
+#define WM8993_JD1_SC_POL_WIDTH 1 /* JD1_SC_POL */
+#define WM8993_JD1_POL 0x0200 /* JD1_POL */
+#define WM8993_JD1_POL_MASK 0x0200 /* JD1_POL */
+#define WM8993_JD1_POL_SHIFT 9 /* JD1_POL */
+#define WM8993_JD1_POL_WIDTH 1 /* JD1_POL */
+#define WM8993_FLL_LOCK_POL 0x0100 /* FLL_LOCK_POL */
+#define WM8993_FLL_LOCK_POL_MASK 0x0100 /* FLL_LOCK_POL */
+#define WM8993_FLL_LOCK_POL_SHIFT 8 /* FLL_LOCK_POL */
+#define WM8993_FLL_LOCK_POL_WIDTH 1 /* FLL_LOCK_POL */
+#define WM8993_GPI8_POL 0x0080 /* GPI8_POL */
+#define WM8993_GPI8_POL_MASK 0x0080 /* GPI8_POL */
+#define WM8993_GPI8_POL_SHIFT 7 /* GPI8_POL */
+#define WM8993_GPI8_POL_WIDTH 1 /* GPI8_POL */
+#define WM8993_GPI7_POL 0x0040 /* GPI7_POL */
+#define WM8993_GPI7_POL_MASK 0x0040 /* GPI7_POL */
+#define WM8993_GPI7_POL_SHIFT 6 /* GPI7_POL */
+#define WM8993_GPI7_POL_WIDTH 1 /* GPI7_POL */
+#define WM8993_GPIO1_POL 0x0001 /* GPIO1_POL */
+#define WM8993_GPIO1_POL_MASK 0x0001 /* GPIO1_POL */
+#define WM8993_GPIO1_POL_SHIFT 0 /* GPIO1_POL */
+#define WM8993_GPIO1_POL_WIDTH 1 /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8993_IN1_VU 0x0100 /* IN1_VU */
+#define WM8993_IN1_VU_MASK 0x0100 /* IN1_VU */
+#define WM8993_IN1_VU_SHIFT 8 /* IN1_VU */
+#define WM8993_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8993_IN1L_MUTE 0x0080 /* IN1L_MUTE */
+#define WM8993_IN1L_MUTE_MASK 0x0080 /* IN1L_MUTE */
+#define WM8993_IN1L_MUTE_SHIFT 7 /* IN1L_MUTE */
+#define WM8993_IN1L_MUTE_WIDTH 1 /* IN1L_MUTE */
+#define WM8993_IN1L_ZC 0x0040 /* IN1L_ZC */
+#define WM8993_IN1L_ZC_MASK 0x0040 /* IN1L_ZC */
+#define WM8993_IN1L_ZC_SHIFT 6 /* IN1L_ZC */
+#define WM8993_IN1L_ZC_WIDTH 1 /* IN1L_ZC */
+#define WM8993_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */
+#define WM8993_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */
+#define WM8993_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */
+
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8993_IN2_VU 0x0100 /* IN2_VU */
+#define WM8993_IN2_VU_MASK 0x0100 /* IN2_VU */
+#define WM8993_IN2_VU_SHIFT 8 /* IN2_VU */
+#define WM8993_IN2_VU_WIDTH 1 /* IN2_VU */
+#define WM8993_IN2L_MUTE 0x0080 /* IN2L_MUTE */
+#define WM8993_IN2L_MUTE_MASK 0x0080 /* IN2L_MUTE */
+#define WM8993_IN2L_MUTE_SHIFT 7 /* IN2L_MUTE */
+#define WM8993_IN2L_MUTE_WIDTH 1 /* IN2L_MUTE */
+#define WM8993_IN2L_ZC 0x0040 /* IN2L_ZC */
+#define WM8993_IN2L_ZC_MASK 0x0040 /* IN2L_ZC */
+#define WM8993_IN2L_ZC_SHIFT 6 /* IN2L_ZC */
+#define WM8993_IN2L_ZC_WIDTH 1 /* IN2L_ZC */
+#define WM8993_IN2L_VOL_MASK 0x001F /* IN2L_VOL - [4:0] */
+#define WM8993_IN2L_VOL_SHIFT 0 /* IN2L_VOL - [4:0] */
+#define WM8993_IN2L_VOL_WIDTH 5 /* IN2L_VOL - [4:0] */
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8993_IN1_VU 0x0100 /* IN1_VU */
+#define WM8993_IN1_VU_MASK 0x0100 /* IN1_VU */
+#define WM8993_IN1_VU_SHIFT 8 /* IN1_VU */
+#define WM8993_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8993_IN1R_MUTE 0x0080 /* IN1R_MUTE */
+#define WM8993_IN1R_MUTE_MASK 0x0080 /* IN1R_MUTE */
+#define WM8993_IN1R_MUTE_SHIFT 7 /* IN1R_MUTE */
+#define WM8993_IN1R_MUTE_WIDTH 1 /* IN1R_MUTE */
+#define WM8993_IN1R_ZC 0x0040 /* IN1R_ZC */
+#define WM8993_IN1R_ZC_MASK 0x0040 /* IN1R_ZC */
+#define WM8993_IN1R_ZC_SHIFT 6 /* IN1R_ZC */
+#define WM8993_IN1R_ZC_WIDTH 1 /* IN1R_ZC */
+#define WM8993_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */
+#define WM8993_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */
+#define WM8993_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8993_IN2_VU 0x0100 /* IN2_VU */
+#define WM8993_IN2_VU_MASK 0x0100 /* IN2_VU */
+#define WM8993_IN2_VU_SHIFT 8 /* IN2_VU */
+#define WM8993_IN2_VU_WIDTH 1 /* IN2_VU */
+#define WM8993_IN2R_MUTE 0x0080 /* IN2R_MUTE */
+#define WM8993_IN2R_MUTE_MASK 0x0080 /* IN2R_MUTE */
+#define WM8993_IN2R_MUTE_SHIFT 7 /* IN2R_MUTE */
+#define WM8993_IN2R_MUTE_WIDTH 1 /* IN2R_MUTE */
+#define WM8993_IN2R_ZC 0x0040 /* IN2R_ZC */
+#define WM8993_IN2R_ZC_MASK 0x0040 /* IN2R_ZC */
+#define WM8993_IN2R_ZC_SHIFT 6 /* IN2R_ZC */
+#define WM8993_IN2R_ZC_WIDTH 1 /* IN2R_ZC */
+#define WM8993_IN2R_VOL_MASK 0x001F /* IN2R_VOL - [4:0] */
+#define WM8993_IN2R_VOL_SHIFT 0 /* IN2R_VOL - [4:0] */
+#define WM8993_IN2R_VOL_WIDTH 5 /* IN2R_VOL - [4:0] */
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8993_HPOUT1_VU 0x0100 /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */
+#define WM8993_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_MUTE_N 0x0040 /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_MUTE_N_MASK 0x0040 /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_MUTE_N_SHIFT 6 /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_MUTE_N_WIDTH 1 /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_VOL_MASK 0x003F /* HPOUT1L_VOL - [5:0] */
+#define WM8993_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [5:0] */
+#define WM8993_HPOUT1L_VOL_WIDTH 6 /* HPOUT1L_VOL - [5:0] */
+
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8993_HPOUT1_VU 0x0100 /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */
+#define WM8993_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_MUTE_N 0x0040 /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_MUTE_N_MASK 0x0040 /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_MUTE_N_SHIFT 6 /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_MUTE_N_WIDTH 1 /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_VOL_MASK 0x003F /* HPOUT1R_VOL - [5:0] */
+#define WM8993_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [5:0] */
+#define WM8993_HPOUT1R_VOL_WIDTH 6 /* HPOUT1R_VOL - [5:0] */
+
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8993_LINEOUT1N_MUTE 0x0040 /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1N_MUTE_MASK 0x0040 /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1N_MUTE_SHIFT 6 /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1N_MUTE_WIDTH 1 /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1P_MUTE 0x0020 /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1P_MUTE_MASK 0x0020 /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1P_MUTE_SHIFT 5 /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1P_MUTE_WIDTH 1 /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1_VOL 0x0010 /* LINEOUT1_VOL */
+#define WM8993_LINEOUT1_VOL_MASK 0x0010 /* LINEOUT1_VOL */
+#define WM8993_LINEOUT1_VOL_SHIFT 4 /* LINEOUT1_VOL */
+#define WM8993_LINEOUT1_VOL_WIDTH 1 /* LINEOUT1_VOL */
+#define WM8993_LINEOUT2N_MUTE 0x0004 /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2N_MUTE_MASK 0x0004 /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2N_MUTE_SHIFT 2 /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2N_MUTE_WIDTH 1 /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2P_MUTE 0x0002 /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2P_MUTE_MASK 0x0002 /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2P_MUTE_SHIFT 1 /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2P_MUTE_WIDTH 1 /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2_VOL 0x0001 /* LINEOUT2_VOL */
+#define WM8993_LINEOUT2_VOL_MASK 0x0001 /* LINEOUT2_VOL */
+#define WM8993_LINEOUT2_VOL_SHIFT 0 /* LINEOUT2_VOL */
+#define WM8993_LINEOUT2_VOL_WIDTH 1 /* LINEOUT2_VOL */
+
+/*
+ * R31 (0x1F) - HPOUT2 Volume
+ */
+#define WM8993_HPOUT2_MUTE 0x0020 /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_MUTE_MASK 0x0020 /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_MUTE_SHIFT 5 /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_MUTE_WIDTH 1 /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_VOL 0x0010 /* HPOUT2_VOL */
+#define WM8993_HPOUT2_VOL_MASK 0x0010 /* HPOUT2_VOL */
+#define WM8993_HPOUT2_VOL_SHIFT 4 /* HPOUT2_VOL */
+#define WM8993_HPOUT2_VOL_WIDTH 1 /* HPOUT2_VOL */
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8993_MIXOUT_VU 0x0100 /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_MASK 0x0100 /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_SHIFT 8 /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_WIDTH 1 /* MIXOUT_VU */
+#define WM8993_MIXOUTL_ZC 0x0080 /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_ZC_MASK 0x0080 /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_ZC_SHIFT 7 /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_ZC_WIDTH 1 /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_MUTE_N 0x0040 /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_MUTE_N_MASK 0x0040 /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_MUTE_N_SHIFT 6 /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_MUTE_N_WIDTH 1 /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_VOL_MASK 0x003F /* MIXOUTL_VOL - [5:0] */
+#define WM8993_MIXOUTL_VOL_SHIFT 0 /* MIXOUTL_VOL - [5:0] */
+#define WM8993_MIXOUTL_VOL_WIDTH 6 /* MIXOUTL_VOL - [5:0] */
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8993_MIXOUT_VU 0x0100 /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_MASK 0x0100 /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_SHIFT 8 /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_WIDTH 1 /* MIXOUT_VU */
+#define WM8993_MIXOUTR_ZC 0x0080 /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_ZC_MASK 0x0080 /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_ZC_SHIFT 7 /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_ZC_WIDTH 1 /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_MUTE_N 0x0040 /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_MUTE_N_MASK 0x0040 /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_MUTE_N_SHIFT 6 /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_MUTE_N_WIDTH 1 /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_VOL_MASK 0x003F /* MIXOUTR_VOL - [5:0] */
+#define WM8993_MIXOUTR_VOL_SHIFT 0 /* MIXOUTR_VOL - [5:0] */
+#define WM8993_MIXOUTR_VOL_WIDTH 6 /* MIXOUTR_VOL - [5:0] */
+
+/*
+ * R34 (0x22) - SPKMIXL Attenuation
+ */
+#define WM8993_MIXINL_SPKMIXL_VOL 0x0020 /* MIXINL_SPKMIXL_VOL */
+#define WM8993_MIXINL_SPKMIXL_VOL_MASK 0x0020 /* MIXINL_SPKMIXL_VOL */
+#define WM8993_MIXINL_SPKMIXL_VOL_SHIFT 5 /* MIXINL_SPKMIXL_VOL */
+#define WM8993_MIXINL_SPKMIXL_VOL_WIDTH 1 /* MIXINL_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL 0x0010 /* IN1LP_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL_MASK 0x0010 /* IN1LP_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL_SHIFT 4 /* IN1LP_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL_WIDTH 1 /* IN1LP_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL 0x0008 /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL_MASK 0x0008 /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL_SHIFT 3 /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL_WIDTH 1 /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL 0x0004 /* DACL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL_MASK 0x0004 /* DACL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL_SHIFT 2 /* DACL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL_WIDTH 1 /* DACL_SPKMIXL_VOL */
+#define WM8993_SPKMIXL_VOL_MASK 0x0003 /* SPKMIXL_VOL - [1:0] */
+#define WM8993_SPKMIXL_VOL_SHIFT 0 /* SPKMIXL_VOL - [1:0] */
+#define WM8993_SPKMIXL_VOL_WIDTH 2 /* SPKMIXL_VOL - [1:0] */
+
+/*
+ * R35 (0x23) - SPKMIXR Attenuation
+ */
+#define WM8993_SPKOUT_CLASSAB_MODE 0x0100 /* SPKOUT_CLASSAB_MODE */
+#define WM8993_SPKOUT_CLASSAB_MODE_MASK 0x0100 /* SPKOUT_CLASSAB_MODE */
+#define WM8993_SPKOUT_CLASSAB_MODE_SHIFT 8 /* SPKOUT_CLASSAB_MODE */
+#define WM8993_SPKOUT_CLASSAB_MODE_WIDTH 1 /* SPKOUT_CLASSAB_MODE */
+#define WM8993_MIXINR_SPKMIXR_VOL 0x0020 /* MIXINR_SPKMIXR_VOL */
+#define WM8993_MIXINR_SPKMIXR_VOL_MASK 0x0020 /* MIXINR_SPKMIXR_VOL */
+#define WM8993_MIXINR_SPKMIXR_VOL_SHIFT 5 /* MIXINR_SPKMIXR_VOL */
+#define WM8993_MIXINR_SPKMIXR_VOL_WIDTH 1 /* MIXINR_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL 0x0010 /* IN1RP_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL_MASK 0x0010 /* IN1RP_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL_SHIFT 4 /* IN1RP_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL_WIDTH 1 /* IN1RP_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL 0x0008 /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL_MASK 0x0008 /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL_SHIFT 3 /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL_WIDTH 1 /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL 0x0004 /* DACR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL_MASK 0x0004 /* DACR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL_SHIFT 2 /* DACR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL_WIDTH 1 /* DACR_SPKMIXR_VOL */
+#define WM8993_SPKMIXR_VOL_MASK 0x0003 /* SPKMIXR_VOL - [1:0] */
+#define WM8993_SPKMIXR_VOL_SHIFT 0 /* SPKMIXR_VOL - [1:0] */
+#define WM8993_SPKMIXR_VOL_WIDTH 2 /* SPKMIXR_VOL - [1:0] */
+
+/*
+ * R36 (0x24) - SPKOUT Mixers
+ */
+#define WM8993_VRX_TO_SPKOUTL 0x0020 /* VRX_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTL_MASK 0x0020 /* VRX_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTL_SHIFT 5 /* VRX_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTL_WIDTH 1 /* VRX_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL 0x0010 /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL_MASK 0x0010 /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL_SHIFT 4 /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL_WIDTH 1 /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL 0x0008 /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL_MASK 0x0008 /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL_SHIFT 3 /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL_WIDTH 1 /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTR 0x0004 /* VRX_TO_SPKOUTR */
+#define WM8993_VRX_TO_SPKOUTR_MASK 0x0004 /* VRX_TO_SPKOUTR */
+#define WM8993_VRX_TO_SPKOUTR_SHIFT 2 /* VRX_TO_SPKOUTR */
+#define WM8993_VRX_TO_SPKOUTR_WIDTH 1 /* VRX_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR 0x0002 /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR_MASK 0x0002 /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR_SHIFT 1 /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR_WIDTH 1 /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR 0x0001 /* SPKMIXR_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR_MASK 0x0001 /* SPKMIXR_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR_SHIFT 0 /* SPKMIXR_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR_WIDTH 1 /* SPKMIXR_TO_SPKOUTR */
+
+/*
+ * R37 (0x25) - SPKOUT Boost
+ */
+#define WM8993_SPKOUTL_BOOST_MASK 0x0038 /* SPKOUTL_BOOST - [5:3] */
+#define WM8993_SPKOUTL_BOOST_SHIFT 3 /* SPKOUTL_BOOST - [5:3] */
+#define WM8993_SPKOUTL_BOOST_WIDTH 3 /* SPKOUTL_BOOST - [5:3] */
+#define WM8993_SPKOUTR_BOOST_MASK 0x0007 /* SPKOUTR_BOOST - [2:0] */
+#define WM8993_SPKOUTR_BOOST_SHIFT 0 /* SPKOUTR_BOOST - [2:0] */
+#define WM8993_SPKOUTR_BOOST_WIDTH 3 /* SPKOUTR_BOOST - [2:0] */
+
+/*
+ * R38 (0x26) - Speaker Volume Left
+ */
+#define WM8993_SPKOUT_VU 0x0100 /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */
+#define WM8993_SPKOUTL_ZC 0x0080 /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_ZC_MASK 0x0080 /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_ZC_SHIFT 7 /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_ZC_WIDTH 1 /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_MUTE_N 0x0040 /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_MUTE_N_MASK 0x0040 /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_MUTE_N_SHIFT 6 /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_MUTE_N_WIDTH 1 /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_VOL_MASK 0x003F /* SPKOUTL_VOL - [5:0] */
+#define WM8993_SPKOUTL_VOL_SHIFT 0 /* SPKOUTL_VOL - [5:0] */
+#define WM8993_SPKOUTL_VOL_WIDTH 6 /* SPKOUTL_VOL - [5:0] */
+
+/*
+ * R39 (0x27) - Speaker Volume Right
+ */
+#define WM8993_SPKOUT_VU 0x0100 /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */
+#define WM8993_SPKOUTR_ZC 0x0080 /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_ZC_MASK 0x0080 /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_ZC_SHIFT 7 /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_ZC_WIDTH 1 /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_MUTE_N 0x0040 /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_MUTE_N_MASK 0x0040 /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_MUTE_N_SHIFT 6 /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_MUTE_N_WIDTH 1 /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_VOL_MASK 0x003F /* SPKOUTR_VOL - [5:0] */
+#define WM8993_SPKOUTR_VOL_SHIFT 0 /* SPKOUTR_VOL - [5:0] */
+#define WM8993_SPKOUTR_VOL_WIDTH 6 /* SPKOUTR_VOL - [5:0] */
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8993_IN2LP_TO_IN2L 0x0080 /* IN2LP_TO_IN2L */
+#define WM8993_IN2LP_TO_IN2L_MASK 0x0080 /* IN2LP_TO_IN2L */
+#define WM8993_IN2LP_TO_IN2L_SHIFT 7 /* IN2LP_TO_IN2L */
+#define WM8993_IN2LP_TO_IN2L_WIDTH 1 /* IN2LP_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L 0x0040 /* IN2LN_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L_MASK 0x0040 /* IN2LN_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L_SHIFT 6 /* IN2LN_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L_WIDTH 1 /* IN2LN_TO_IN2L */
+#define WM8993_IN1LP_TO_IN1L 0x0020 /* IN1LP_TO_IN1L */
+#define WM8993_IN1LP_TO_IN1L_MASK 0x0020 /* IN1LP_TO_IN1L */
+#define WM8993_IN1LP_TO_IN1L_SHIFT 5 /* IN1LP_TO_IN1L */
+#define WM8993_IN1LP_TO_IN1L_WIDTH 1 /* IN1LP_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L 0x0010 /* IN1LN_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L_MASK 0x0010 /* IN1LN_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L_SHIFT 4 /* IN1LN_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L_WIDTH 1 /* IN1LN_TO_IN1L */
+#define WM8993_IN2RP_TO_IN2R 0x0008 /* IN2RP_TO_IN2R */
+#define WM8993_IN2RP_TO_IN2R_MASK 0x0008 /* IN2RP_TO_IN2R */
+#define WM8993_IN2RP_TO_IN2R_SHIFT 3 /* IN2RP_TO_IN2R */
+#define WM8993_IN2RP_TO_IN2R_WIDTH 1 /* IN2RP_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R 0x0004 /* IN2RN_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R_MASK 0x0004 /* IN2RN_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R_SHIFT 2 /* IN2RN_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R_WIDTH 1 /* IN2RN_TO_IN2R */
+#define WM8993_IN1RP_TO_IN1R 0x0002 /* IN1RP_TO_IN1R */
+#define WM8993_IN1RP_TO_IN1R_MASK 0x0002 /* IN1RP_TO_IN1R */
+#define WM8993_IN1RP_TO_IN1R_SHIFT 1 /* IN1RP_TO_IN1R */
+#define WM8993_IN1RP_TO_IN1R_WIDTH 1 /* IN1RP_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R 0x0001 /* IN1RN_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R_MASK 0x0001 /* IN1RN_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R_SHIFT 0 /* IN1RN_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R_WIDTH 1 /* IN1RN_TO_IN1R */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8993_IN2L_TO_MIXINL 0x0100 /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_TO_MIXINL_MASK 0x0100 /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_TO_MIXINL_SHIFT 8 /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_TO_MIXINL_WIDTH 1 /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_MIXINL_VOL 0x0080 /* IN2L_MIXINL_VOL */
+#define WM8993_IN2L_MIXINL_VOL_MASK 0x0080 /* IN2L_MIXINL_VOL */
+#define WM8993_IN2L_MIXINL_VOL_SHIFT 7 /* IN2L_MIXINL_VOL */
+#define WM8993_IN2L_MIXINL_VOL_WIDTH 1 /* IN2L_MIXINL_VOL */
+#define WM8993_IN1L_TO_MIXINL 0x0020 /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_TO_MIXINL_MASK 0x0020 /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_TO_MIXINL_SHIFT 5 /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_TO_MIXINL_WIDTH 1 /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_MIXINL_VOL 0x0010 /* IN1L_MIXINL_VOL */
+#define WM8993_IN1L_MIXINL_VOL_MASK 0x0010 /* IN1L_MIXINL_VOL */
+#define WM8993_IN1L_MIXINL_VOL_SHIFT 4 /* IN1L_MIXINL_VOL */
+#define WM8993_IN1L_MIXINL_VOL_WIDTH 1 /* IN1L_MIXINL_VOL */
+#define WM8993_MIXOUTL_MIXINL_VOL_MASK 0x0007 /* MIXOUTL_MIXINL_VOL - [2:0] */
+#define WM8993_MIXOUTL_MIXINL_VOL_SHIFT 0 /* MIXOUTL_MIXINL_VOL - [2:0] */
+#define WM8993_MIXOUTL_MIXINL_VOL_WIDTH 3 /* MIXOUTL_MIXINL_VOL - [2:0] */
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8993_IN2R_TO_MIXINR 0x0100 /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_TO_MIXINR_MASK 0x0100 /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_TO_MIXINR_SHIFT 8 /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_TO_MIXINR_WIDTH 1 /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_MIXINR_VOL 0x0080 /* IN2R_MIXINR_VOL */
+#define WM8993_IN2R_MIXINR_VOL_MASK 0x0080 /* IN2R_MIXINR_VOL */
+#define WM8993_IN2R_MIXINR_VOL_SHIFT 7 /* IN2R_MIXINR_VOL */
+#define WM8993_IN2R_MIXINR_VOL_WIDTH 1 /* IN2R_MIXINR_VOL */
+#define WM8993_IN1R_TO_MIXINR 0x0020 /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_TO_MIXINR_MASK 0x0020 /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_TO_MIXINR_SHIFT 5 /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_TO_MIXINR_WIDTH 1 /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_MIXINR_VOL 0x0010 /* IN1R_MIXINR_VOL */
+#define WM8993_IN1R_MIXINR_VOL_MASK 0x0010 /* IN1R_MIXINR_VOL */
+#define WM8993_IN1R_MIXINR_VOL_SHIFT 4 /* IN1R_MIXINR_VOL */
+#define WM8993_IN1R_MIXINR_VOL_WIDTH 1 /* IN1R_MIXINR_VOL */
+#define WM8993_MIXOUTR_MIXINR_VOL_MASK 0x0007 /* MIXOUTR_MIXINR_VOL - [2:0] */
+#define WM8993_MIXOUTR_MIXINR_VOL_SHIFT 0 /* MIXOUTR_MIXINR_VOL - [2:0] */
+#define WM8993_MIXOUTR_MIXINR_VOL_WIDTH 3 /* MIXOUTR_MIXINR_VOL - [2:0] */
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8993_IN1LP_MIXINL_VOL_MASK 0x01C0 /* IN1LP_MIXINL_VOL - [8:6] */
+#define WM8993_IN1LP_MIXINL_VOL_SHIFT 6 /* IN1LP_MIXINL_VOL - [8:6] */
+#define WM8993_IN1LP_MIXINL_VOL_WIDTH 3 /* IN1LP_MIXINL_VOL - [8:6] */
+#define WM8993_VRX_MIXINL_VOL_MASK 0x0007 /* VRX_MIXINL_VOL - [2:0] */
+#define WM8993_VRX_MIXINL_VOL_SHIFT 0 /* VRX_MIXINL_VOL - [2:0] */
+#define WM8993_VRX_MIXINL_VOL_WIDTH 3 /* VRX_MIXINL_VOL - [2:0] */
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8993_IN1RP_MIXINR_VOL_MASK 0x01C0 /* IN1RP_MIXINR_VOL - [8:6] */
+#define WM8993_IN1RP_MIXINR_VOL_SHIFT 6 /* IN1RP_MIXINR_VOL - [8:6] */
+#define WM8993_IN1RP_MIXINR_VOL_WIDTH 3 /* IN1RP_MIXINR_VOL - [8:6] */
+#define WM8993_VRX_MIXINR_VOL_MASK 0x0007 /* VRX_MIXINR_VOL - [2:0] */
+#define WM8993_VRX_MIXINR_VOL_SHIFT 0 /* VRX_MIXINR_VOL - [2:0] */
+#define WM8993_VRX_MIXINR_VOL_WIDTH 3 /* VRX_MIXINR_VOL - [2:0] */
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8993_DACL_TO_HPOUT1L 0x0100 /* DACL_TO_HPOUT1L */
+#define WM8993_DACL_TO_HPOUT1L_MASK 0x0100 /* DACL_TO_HPOUT1L */
+#define WM8993_DACL_TO_HPOUT1L_SHIFT 8 /* DACL_TO_HPOUT1L */
+#define WM8993_DACL_TO_HPOUT1L_WIDTH 1 /* DACL_TO_HPOUT1L */
+#define WM8993_MIXINR_TO_MIXOUTL 0x0080 /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINR_TO_MIXOUTL_MASK 0x0080 /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINR_TO_MIXOUTL_SHIFT 7 /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINR_TO_MIXOUTL_WIDTH 1 /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL 0x0040 /* MIXINL_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL_MASK 0x0040 /* MIXINL_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL_SHIFT 6 /* MIXINL_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL_WIDTH 1 /* MIXINL_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL 0x0020 /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL_MASK 0x0020 /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL_SHIFT 5 /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL_WIDTH 1 /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL 0x0010 /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL_MASK 0x0010 /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL_SHIFT 4 /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL_WIDTH 1 /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL 0x0008 /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL_MASK 0x0008 /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL_SHIFT 3 /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL_WIDTH 1 /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL 0x0004 /* IN1L_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL_MASK 0x0004 /* IN1L_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL_SHIFT 2 /* IN1L_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL_WIDTH 1 /* IN1L_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL 0x0002 /* IN2LP_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL_MASK 0x0002 /* IN2LP_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL_SHIFT 1 /* IN2LP_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL_WIDTH 1 /* IN2LP_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL 0x0001 /* DACL_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL_MASK 0x0001 /* DACL_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL_SHIFT 0 /* DACL_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL_WIDTH 1 /* DACL_TO_MIXOUTL */
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8993_DACR_TO_HPOUT1R 0x0100 /* DACR_TO_HPOUT1R */
+#define WM8993_DACR_TO_HPOUT1R_MASK 0x0100 /* DACR_TO_HPOUT1R */
+#define WM8993_DACR_TO_HPOUT1R_SHIFT 8 /* DACR_TO_HPOUT1R */
+#define WM8993_DACR_TO_HPOUT1R_WIDTH 1 /* DACR_TO_HPOUT1R */
+#define WM8993_MIXINL_TO_MIXOUTR 0x0080 /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINL_TO_MIXOUTR_MASK 0x0080 /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINL_TO_MIXOUTR_SHIFT 7 /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINL_TO_MIXOUTR_WIDTH 1 /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR 0x0040 /* MIXINR_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR_MASK 0x0040 /* MIXINR_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR_SHIFT 6 /* MIXINR_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR_WIDTH 1 /* MIXINR_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR 0x0020 /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR_MASK 0x0020 /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR_SHIFT 5 /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR_WIDTH 1 /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR 0x0010 /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR_MASK 0x0010 /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR_SHIFT 4 /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR_WIDTH 1 /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR 0x0008 /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR_MASK 0x0008 /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR_SHIFT 3 /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR_WIDTH 1 /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR 0x0004 /* IN1R_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR_MASK 0x0004 /* IN1R_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR_SHIFT 2 /* IN1R_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR_WIDTH 1 /* IN1R_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR 0x0002 /* IN2RP_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR_MASK 0x0002 /* IN2RP_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR_SHIFT 1 /* IN2RP_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR_WIDTH 1 /* IN2RP_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR 0x0001 /* DACR_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR_MASK 0x0001 /* DACR_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR_SHIFT 0 /* DACR_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR_WIDTH 1 /* DACR_TO_MIXOUTR */
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8993_IN2LP_MIXOUTL_VOL_MASK 0x0E00 /* IN2LP_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2LP_MIXOUTL_VOL_SHIFT 9 /* IN2LP_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2LP_MIXOUTL_VOL_WIDTH 3 /* IN2LP_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2LN_MIXOUTL_VOL_MASK 0x01C0 /* IN2LN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTL_VOL_SHIFT 6 /* IN2LN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTL_VOL_WIDTH 3 /* IN2LN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN1R_MIXOUTL_VOL_MASK 0x0038 /* IN1R_MIXOUTL_VOL - [5:3] */
+#define WM8993_IN1R_MIXOUTL_VOL_SHIFT 3 /* IN1R_MIXOUTL_VOL - [5:3] */
+#define WM8993_IN1R_MIXOUTL_VOL_WIDTH 3 /* IN1R_MIXOUTL_VOL - [5:3] */
+#define WM8993_IN1L_MIXOUTL_VOL_MASK 0x0007 /* IN1L_MIXOUTL_VOL - [2:0] */
+#define WM8993_IN1L_MIXOUTL_VOL_SHIFT 0 /* IN1L_MIXOUTL_VOL - [2:0] */
+#define WM8993_IN1L_MIXOUTL_VOL_WIDTH 3 /* IN1L_MIXOUTL_VOL - [2:0] */
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8993_IN2RP_MIXOUTR_VOL_MASK 0x0E00 /* IN2RP_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2RP_MIXOUTR_VOL_SHIFT 9 /* IN2RP_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2RP_MIXOUTR_VOL_WIDTH 3 /* IN2RP_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2RN_MIXOUTR_VOL_MASK 0x01C0 /* IN2RN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTR_VOL_SHIFT 6 /* IN2RN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTR_VOL_WIDTH 3 /* IN2RN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN1L_MIXOUTR_VOL_MASK 0x0038 /* IN1L_MIXOUTR_VOL - [5:3] */
+#define WM8993_IN1L_MIXOUTR_VOL_SHIFT 3 /* IN1L_MIXOUTR_VOL - [5:3] */
+#define WM8993_IN1L_MIXOUTR_VOL_WIDTH 3 /* IN1L_MIXOUTR_VOL - [5:3] */
+#define WM8993_IN1R_MIXOUTR_VOL_MASK 0x0007 /* IN1R_MIXOUTR_VOL - [2:0] */
+#define WM8993_IN1R_MIXOUTR_VOL_SHIFT 0 /* IN1R_MIXOUTR_VOL - [2:0] */
+#define WM8993_IN1R_MIXOUTR_VOL_WIDTH 3 /* IN1R_MIXOUTR_VOL - [2:0] */
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8993_DACL_MIXOUTL_VOL_MASK 0x0E00 /* DACL_MIXOUTL_VOL - [11:9] */
+#define WM8993_DACL_MIXOUTL_VOL_SHIFT 9 /* DACL_MIXOUTL_VOL - [11:9] */
+#define WM8993_DACL_MIXOUTL_VOL_WIDTH 3 /* DACL_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2RN_MIXOUTL_VOL_MASK 0x01C0 /* IN2RN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTL_VOL_SHIFT 6 /* IN2RN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTL_VOL_WIDTH 3 /* IN2RN_MIXOUTL_VOL - [8:6] */
+#define WM8993_MIXINR_MIXOUTL_VOL_MASK 0x0038 /* MIXINR_MIXOUTL_VOL - [5:3] */
+#define WM8993_MIXINR_MIXOUTL_VOL_SHIFT 3 /* MIXINR_MIXOUTL_VOL - [5:3] */
+#define WM8993_MIXINR_MIXOUTL_VOL_WIDTH 3 /* MIXINR_MIXOUTL_VOL - [5:3] */
+#define WM8993_MIXINL_MIXOUTL_VOL_MASK 0x0007 /* MIXINL_MIXOUTL_VOL - [2:0] */
+#define WM8993_MIXINL_MIXOUTL_VOL_SHIFT 0 /* MIXINL_MIXOUTL_VOL - [2:0] */
+#define WM8993_MIXINL_MIXOUTL_VOL_WIDTH 3 /* MIXINL_MIXOUTL_VOL - [2:0] */
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8993_DACR_MIXOUTR_VOL_MASK 0x0E00 /* DACR_MIXOUTR_VOL - [11:9] */
+#define WM8993_DACR_MIXOUTR_VOL_SHIFT 9 /* DACR_MIXOUTR_VOL - [11:9] */
+#define WM8993_DACR_MIXOUTR_VOL_WIDTH 3 /* DACR_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2LN_MIXOUTR_VOL_MASK 0x01C0 /* IN2LN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTR_VOL_SHIFT 6 /* IN2LN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTR_VOL_WIDTH 3 /* IN2LN_MIXOUTR_VOL - [8:6] */
+#define WM8993_MIXINL_MIXOUTR_VOL_MASK 0x0038 /* MIXINL_MIXOUTR_VOL - [5:3] */
+#define WM8993_MIXINL_MIXOUTR_VOL_SHIFT 3 /* MIXINL_MIXOUTR_VOL - [5:3] */
+#define WM8993_MIXINL_MIXOUTR_VOL_WIDTH 3 /* MIXINL_MIXOUTR_VOL - [5:3] */
+#define WM8993_MIXINR_MIXOUTR_VOL_MASK 0x0007 /* MIXINR_MIXOUTR_VOL - [2:0] */
+#define WM8993_MIXINR_MIXOUTR_VOL_SHIFT 0 /* MIXINR_MIXOUTR_VOL - [2:0] */
+#define WM8993_MIXINR_MIXOUTR_VOL_WIDTH 3 /* MIXINR_MIXOUTR_VOL - [2:0] */
+
+/*
+ * R51 (0x33) - HPOUT2 Mixer
+ */
+#define WM8993_VRX_TO_HPOUT2 0x0020 /* VRX_TO_HPOUT2 */
+#define WM8993_VRX_TO_HPOUT2_MASK 0x0020 /* VRX_TO_HPOUT2 */
+#define WM8993_VRX_TO_HPOUT2_SHIFT 5 /* VRX_TO_HPOUT2 */
+#define WM8993_VRX_TO_HPOUT2_WIDTH 1 /* VRX_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2 0x0010 /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2_MASK 0x0010 /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2_SHIFT 4 /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2_WIDTH 1 /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2 0x0008 /* MIXOUTRVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2_MASK 0x0008 /* MIXOUTRVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2_SHIFT 3 /* MIXOUTRVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2_WIDTH 1 /* MIXOUTRVOL_TO_HPOUT2 */
+
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8993_MIXOUTL_TO_LINEOUT1N 0x0040 /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTL_TO_LINEOUT1N_MASK 0x0040 /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTL_TO_LINEOUT1N_SHIFT 6 /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTL_TO_LINEOUT1N_WIDTH 1 /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N 0x0020 /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N_MASK 0x0020 /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N_SHIFT 5 /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N_WIDTH 1 /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_LINEOUT1_MODE 0x0010 /* LINEOUT1_MODE */
+#define WM8993_LINEOUT1_MODE_MASK 0x0010 /* LINEOUT1_MODE */
+#define WM8993_LINEOUT1_MODE_SHIFT 4 /* LINEOUT1_MODE */
+#define WM8993_LINEOUT1_MODE_WIDTH 1 /* LINEOUT1_MODE */
+#define WM8993_IN1R_TO_LINEOUT1P 0x0004 /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1R_TO_LINEOUT1P_MASK 0x0004 /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1R_TO_LINEOUT1P_SHIFT 2 /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1R_TO_LINEOUT1P_WIDTH 1 /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P 0x0002 /* IN1L_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P_MASK 0x0002 /* IN1L_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P_SHIFT 1 /* IN1L_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P_WIDTH 1 /* IN1L_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P 0x0001 /* MIXOUTL_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P_MASK 0x0001 /* MIXOUTL_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P_SHIFT 0 /* MIXOUTL_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P_WIDTH 1 /* MIXOUTL_TO_LINEOUT1P */
+
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8993_MIXOUTR_TO_LINEOUT2N 0x0040 /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTR_TO_LINEOUT2N_MASK 0x0040 /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTR_TO_LINEOUT2N_SHIFT 6 /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTR_TO_LINEOUT2N_WIDTH 1 /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N 0x0020 /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N_MASK 0x0020 /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N_SHIFT 5 /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N_WIDTH 1 /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_LINEOUT2_MODE 0x0010 /* LINEOUT2_MODE */
+#define WM8993_LINEOUT2_MODE_MASK 0x0010 /* LINEOUT2_MODE */
+#define WM8993_LINEOUT2_MODE_SHIFT 4 /* LINEOUT2_MODE */
+#define WM8993_LINEOUT2_MODE_WIDTH 1 /* LINEOUT2_MODE */
+#define WM8993_IN1L_TO_LINEOUT2P 0x0004 /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1L_TO_LINEOUT2P_MASK 0x0004 /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1L_TO_LINEOUT2P_SHIFT 2 /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1L_TO_LINEOUT2P_WIDTH 1 /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P 0x0002 /* IN1R_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P_MASK 0x0002 /* IN1R_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P_SHIFT 1 /* IN1R_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P_WIDTH 1 /* IN1R_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P 0x0001 /* MIXOUTR_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P_MASK 0x0001 /* MIXOUTR_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P_SHIFT 0 /* MIXOUTR_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P_WIDTH 1 /* MIXOUTR_TO_LINEOUT2P */
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8993_SPKAB_REF_SEL 0x0100 /* SPKAB_REF_SEL */
+#define WM8993_SPKAB_REF_SEL_MASK 0x0100 /* SPKAB_REF_SEL */
+#define WM8993_SPKAB_REF_SEL_SHIFT 8 /* SPKAB_REF_SEL */
+#define WM8993_SPKAB_REF_SEL_WIDTH 1 /* SPKAB_REF_SEL */
+#define WM8993_MIXINL_TO_SPKMIXL 0x0080 /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINL_TO_SPKMIXL_MASK 0x0080 /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINL_TO_SPKMIXL_SHIFT 7 /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINL_TO_SPKMIXL_WIDTH 1 /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINR_TO_SPKMIXR 0x0040 /* MIXINR_TO_SPKMIXR */
+#define WM8993_MIXINR_TO_SPKMIXR_MASK 0x0040 /* MIXINR_TO_SPKMIXR */
+#define WM8993_MIXINR_TO_SPKMIXR_SHIFT 6 /* MIXINR_TO_SPKMIXR */
+#define WM8993_MIXINR_TO_SPKMIXR_WIDTH 1 /* MIXINR_TO_SPKMIXR */
+#define WM8993_IN1LP_TO_SPKMIXL 0x0020 /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1LP_TO_SPKMIXL_MASK 0x0020 /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1LP_TO_SPKMIXL_SHIFT 5 /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1LP_TO_SPKMIXL_WIDTH 1 /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1RP_TO_SPKMIXR 0x0010 /* IN1RP_TO_SPKMIXR */
+#define WM8993_IN1RP_TO_SPKMIXR_MASK 0x0010 /* IN1RP_TO_SPKMIXR */
+#define WM8993_IN1RP_TO_SPKMIXR_SHIFT 4 /* IN1RP_TO_SPKMIXR */
+#define WM8993_IN1RP_TO_SPKMIXR_WIDTH 1 /* IN1RP_TO_SPKMIXR */
+#define WM8993_MIXOUTL_TO_SPKMIXL 0x0008 /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTL_TO_SPKMIXL_MASK 0x0008 /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTL_TO_SPKMIXL_SHIFT 3 /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTL_TO_SPKMIXL_WIDTH 1 /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTR_TO_SPKMIXR 0x0004 /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_MIXOUTR_TO_SPKMIXR_MASK 0x0004 /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_MIXOUTR_TO_SPKMIXR_SHIFT 2 /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_MIXOUTR_TO_SPKMIXR_WIDTH 1 /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_DACL_TO_SPKMIXL 0x0002 /* DACL_TO_SPKMIXL */
+#define WM8993_DACL_TO_SPKMIXL_MASK 0x0002 /* DACL_TO_SPKMIXL */
+#define WM8993_DACL_TO_SPKMIXL_SHIFT 1 /* DACL_TO_SPKMIXL */
+#define WM8993_DACL_TO_SPKMIXL_WIDTH 1 /* DACL_TO_SPKMIXL */
+#define WM8993_DACR_TO_SPKMIXR 0x0001 /* DACR_TO_SPKMIXR */
+#define WM8993_DACR_TO_SPKMIXR_MASK 0x0001 /* DACR_TO_SPKMIXR */
+#define WM8993_DACR_TO_SPKMIXR_SHIFT 0 /* DACR_TO_SPKMIXR */
+#define WM8993_DACR_TO_SPKMIXR_WIDTH 1 /* DACR_TO_SPKMIXR */
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8993_LINEOUT1_FB 0x0080 /* LINEOUT1_FB */
+#define WM8993_LINEOUT1_FB_MASK 0x0080 /* LINEOUT1_FB */
+#define WM8993_LINEOUT1_FB_SHIFT 7 /* LINEOUT1_FB */
+#define WM8993_LINEOUT1_FB_WIDTH 1 /* LINEOUT1_FB */
+#define WM8993_LINEOUT2_FB 0x0040 /* LINEOUT2_FB */
+#define WM8993_LINEOUT2_FB_MASK 0x0040 /* LINEOUT2_FB */
+#define WM8993_LINEOUT2_FB_SHIFT 6 /* LINEOUT2_FB */
+#define WM8993_LINEOUT2_FB_WIDTH 1 /* LINEOUT2_FB */
+#define WM8993_VROI 0x0001 /* VROI */
+#define WM8993_VROI_MASK 0x0001 /* VROI */
+#define WM8993_VROI_SHIFT 0 /* VROI */
+#define WM8993_VROI_WIDTH 1 /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8993_LINEOUT_VMID_BUF_ENA 0x0080 /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_LINEOUT_VMID_BUF_ENA_MASK 0x0080 /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_LINEOUT_VMID_BUF_ENA_SHIFT 7 /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_LINEOUT_VMID_BUF_ENA_WIDTH 1 /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_HPOUT2_IN_ENA 0x0040 /* HPOUT2_IN_ENA */
+#define WM8993_HPOUT2_IN_ENA_MASK 0x0040 /* HPOUT2_IN_ENA */
+#define WM8993_HPOUT2_IN_ENA_SHIFT 6 /* HPOUT2_IN_ENA */
+#define WM8993_HPOUT2_IN_ENA_WIDTH 1 /* HPOUT2_IN_ENA */
+#define WM8993_LINEOUT1_DISCH 0x0020 /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT1_DISCH_MASK 0x0020 /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT1_DISCH_SHIFT 5 /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT1_DISCH_WIDTH 1 /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT2_DISCH 0x0010 /* LINEOUT2_DISCH */
+#define WM8993_LINEOUT2_DISCH_MASK 0x0010 /* LINEOUT2_DISCH */
+#define WM8993_LINEOUT2_DISCH_SHIFT 4 /* LINEOUT2_DISCH */
+#define WM8993_LINEOUT2_DISCH_WIDTH 1 /* LINEOUT2_DISCH */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8993_VMID_RAMP_MASK 0x0060 /* VMID_RAMP - [6:5] */
+#define WM8993_VMID_RAMP_SHIFT 5 /* VMID_RAMP - [6:5] */
+#define WM8993_VMID_RAMP_WIDTH 2 /* VMID_RAMP - [6:5] */
+#define WM8993_VMID_BUF_ENA 0x0008 /* VMID_BUF_ENA */
+#define WM8993_VMID_BUF_ENA_MASK 0x0008 /* VMID_BUF_ENA */
+#define WM8993_VMID_BUF_ENA_SHIFT 3 /* VMID_BUF_ENA */
+#define WM8993_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */
+#define WM8993_STARTUP_BIAS_ENA 0x0004 /* STARTUP_BIAS_ENA */
+#define WM8993_STARTUP_BIAS_ENA_MASK 0x0004 /* STARTUP_BIAS_ENA */
+#define WM8993_STARTUP_BIAS_ENA_SHIFT 2 /* STARTUP_BIAS_ENA */
+#define WM8993_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */
+#define WM8993_BIAS_SRC 0x0002 /* BIAS_SRC */
+#define WM8993_BIAS_SRC_MASK 0x0002 /* BIAS_SRC */
+#define WM8993_BIAS_SRC_SHIFT 1 /* BIAS_SRC */
+#define WM8993_BIAS_SRC_WIDTH 1 /* BIAS_SRC */
+#define WM8993_VMID_DISCH 0x0001 /* VMID_DISCH */
+#define WM8993_VMID_DISCH_MASK 0x0001 /* VMID_DISCH */
+#define WM8993_VMID_DISCH_SHIFT 0 /* VMID_DISCH */
+#define WM8993_VMID_DISCH_WIDTH 1 /* VMID_DISCH */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8993_JD_SCTHR_MASK 0x00C0 /* JD_SCTHR - [7:6] */
+#define WM8993_JD_SCTHR_SHIFT 6 /* JD_SCTHR - [7:6] */
+#define WM8993_JD_SCTHR_WIDTH 2 /* JD_SCTHR - [7:6] */
+#define WM8993_JD_THR_MASK 0x0030 /* JD_THR - [5:4] */
+#define WM8993_JD_THR_SHIFT 4 /* JD_THR - [5:4] */
+#define WM8993_JD_THR_WIDTH 2 /* JD_THR - [5:4] */
+#define WM8993_JD_ENA 0x0004 /* JD_ENA */
+#define WM8993_JD_ENA_MASK 0x0004 /* JD_ENA */
+#define WM8993_JD_ENA_SHIFT 2 /* JD_ENA */
+#define WM8993_JD_ENA_WIDTH 1 /* JD_ENA */
+#define WM8993_MICB2_LVL 0x0002 /* MICB2_LVL */
+#define WM8993_MICB2_LVL_MASK 0x0002 /* MICB2_LVL */
+#define WM8993_MICB2_LVL_SHIFT 1 /* MICB2_LVL */
+#define WM8993_MICB2_LVL_WIDTH 1 /* MICB2_LVL */
+#define WM8993_MICB1_LVL 0x0001 /* MICB1_LVL */
+#define WM8993_MICB1_LVL_MASK 0x0001 /* MICB1_LVL */
+#define WM8993_MICB1_LVL_SHIFT 0 /* MICB1_LVL */
+#define WM8993_MICB1_LVL_WIDTH 1 /* MICB1_LVL */
+
+/*
+ * R60 (0x3C) - FLL Control 1
+ */
+#define WM8993_FLL_FRAC 0x0004 /* FLL_FRAC */
+#define WM8993_FLL_FRAC_MASK 0x0004 /* FLL_FRAC */
+#define WM8993_FLL_FRAC_SHIFT 2 /* FLL_FRAC */
+#define WM8993_FLL_FRAC_WIDTH 1 /* FLL_FRAC */
+#define WM8993_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */
+#define WM8993_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */
+#define WM8993_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */
+#define WM8993_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */
+#define WM8993_FLL_ENA 0x0001 /* FLL_ENA */
+#define WM8993_FLL_ENA_MASK 0x0001 /* FLL_ENA */
+#define WM8993_FLL_ENA_SHIFT 0 /* FLL_ENA */
+#define WM8993_FLL_ENA_WIDTH 1 /* FLL_ENA */
+
+/*
+ * R61 (0x3D) - FLL Control 2
+ */
+#define WM8993_FLL_OUTDIV_MASK 0x0700 /* FLL_OUTDIV - [10:8] */
+#define WM8993_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [10:8] */
+#define WM8993_FLL_OUTDIV_WIDTH 3 /* FLL_OUTDIV - [10:8] */
+#define WM8993_FLL_CTRL_RATE_MASK 0x0070 /* FLL_CTRL_RATE - [6:4] */
+#define WM8993_FLL_CTRL_RATE_SHIFT 4 /* FLL_CTRL_RATE - [6:4] */
+#define WM8993_FLL_CTRL_RATE_WIDTH 3 /* FLL_CTRL_RATE - [6:4] */
+#define WM8993_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
+#define WM8993_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
+#define WM8993_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
+
+/*
+ * R62 (0x3E) - FLL Control 3
+ */
+#define WM8993_FLL_K_MASK 0xFFFF /* FLL_K - [15:0] */
+#define WM8993_FLL_K_SHIFT 0 /* FLL_K - [15:0] */
+#define WM8993_FLL_K_WIDTH 16 /* FLL_K - [15:0] */
+
+/*
+ * R63 (0x3F) - FLL Control 4
+ */
+#define WM8993_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */
+#define WM8993_FLL_N_SHIFT 5 /* FLL_N - [14:5] */
+#define WM8993_FLL_N_WIDTH 10 /* FLL_N - [14:5] */
+#define WM8993_FLL_GAIN_MASK 0x000F /* FLL_GAIN - [3:0] */
+#define WM8993_FLL_GAIN_SHIFT 0 /* FLL_GAIN - [3:0] */
+#define WM8993_FLL_GAIN_WIDTH 4 /* FLL_GAIN - [3:0] */
+
+/*
+ * R64 (0x40) - FLL Control 5
+ */
+#define WM8993_FLL_FRC_NCO_VAL_MASK 0x1F80 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8993_FLL_FRC_NCO_VAL_SHIFT 7 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8993_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8993_FLL_FRC_NCO 0x0040 /* FLL_FRC_NCO */
+#define WM8993_FLL_FRC_NCO_MASK 0x0040 /* FLL_FRC_NCO */
+#define WM8993_FLL_FRC_NCO_SHIFT 6 /* FLL_FRC_NCO */
+#define WM8993_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */
+#define WM8993_FLL_CLK_REF_DIV_MASK 0x0018 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8993_FLL_CLK_REF_DIV_SHIFT 3 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8993_FLL_CLK_REF_DIV_WIDTH 2 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8993_FLL_CLK_SRC_MASK 0x0003 /* FLL_CLK_SRC - [1:0] */
+#define WM8993_FLL_CLK_SRC_SHIFT 0 /* FLL_CLK_SRC - [1:0] */
+#define WM8993_FLL_CLK_SRC_WIDTH 2 /* FLL_CLK_SRC - [1:0] */
+
+/*
+ * R65 (0x41) - Clocking 3
+ */
+#define WM8993_CLK_DCS_DIV_MASK 0x3C00 /* CLK_DCS_DIV - [13:10] */
+#define WM8993_CLK_DCS_DIV_SHIFT 10 /* CLK_DCS_DIV - [13:10] */
+#define WM8993_CLK_DCS_DIV_WIDTH 4 /* CLK_DCS_DIV - [13:10] */
+#define WM8993_SAMPLE_RATE_MASK 0x0380 /* SAMPLE_RATE - [9:7] */
+#define WM8993_SAMPLE_RATE_SHIFT 7 /* SAMPLE_RATE - [9:7] */
+#define WM8993_SAMPLE_RATE_WIDTH 3 /* SAMPLE_RATE - [9:7] */
+#define WM8993_CLK_SYS_RATE_MASK 0x001E /* CLK_SYS_RATE - [4:1] */
+#define WM8993_CLK_SYS_RATE_SHIFT 1 /* CLK_SYS_RATE - [4:1] */
+#define WM8993_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [4:1] */
+#define WM8993_CLK_DSP_ENA 0x0001 /* CLK_DSP_ENA */
+#define WM8993_CLK_DSP_ENA_MASK 0x0001 /* CLK_DSP_ENA */
+#define WM8993_CLK_DSP_ENA_SHIFT 0 /* CLK_DSP_ENA */
+#define WM8993_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */
+
+/*
+ * R66 (0x42) - Clocking 4
+ */
+#define WM8993_DAC_DIV4 0x0200 /* DAC_DIV4 */
+#define WM8993_DAC_DIV4_MASK 0x0200 /* DAC_DIV4 */
+#define WM8993_DAC_DIV4_SHIFT 9 /* DAC_DIV4 */
+#define WM8993_DAC_DIV4_WIDTH 1 /* DAC_DIV4 */
+#define WM8993_CLK_256K_DIV_MASK 0x007E /* CLK_256K_DIV - [6:1] */
+#define WM8993_CLK_256K_DIV_SHIFT 1 /* CLK_256K_DIV - [6:1] */
+#define WM8993_CLK_256K_DIV_WIDTH 6 /* CLK_256K_DIV - [6:1] */
+#define WM8993_SR_MODE 0x0001 /* SR_MODE */
+#define WM8993_SR_MODE_MASK 0x0001 /* SR_MODE */
+#define WM8993_SR_MODE_SHIFT 0 /* SR_MODE */
+#define WM8993_SR_MODE_WIDTH 1 /* SR_MODE */
+
+/*
+ * R67 (0x43) - MW Slave Control
+ */
+#define WM8993_MASK_WRITE_ENA 0x0001 /* MASK_WRITE_ENA */
+#define WM8993_MASK_WRITE_ENA_MASK 0x0001 /* MASK_WRITE_ENA */
+#define WM8993_MASK_WRITE_ENA_SHIFT 0 /* MASK_WRITE_ENA */
+#define WM8993_MASK_WRITE_ENA_WIDTH 1 /* MASK_WRITE_ENA */
+
+/*
+ * R69 (0x45) - Bus Control 1
+ */
+#define WM8993_CLK_SYS_ENA 0x0002 /* CLK_SYS_ENA */
+#define WM8993_CLK_SYS_ENA_MASK 0x0002 /* CLK_SYS_ENA */
+#define WM8993_CLK_SYS_ENA_SHIFT 1 /* CLK_SYS_ENA */
+#define WM8993_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */
+
+/*
+ * R70 (0x46) - Write Sequencer 0
+ */
+#define WM8993_WSEQ_ENA 0x0100 /* WSEQ_ENA */
+#define WM8993_WSEQ_ENA_MASK 0x0100 /* WSEQ_ENA */
+#define WM8993_WSEQ_ENA_SHIFT 8 /* WSEQ_ENA */
+#define WM8993_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM8993_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8993_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8993_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R71 (0x47) - Write Sequencer 1
+ */
+#define WM8993_WSEQ_DATA_WIDTH_MASK 0x7000 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8993_WSEQ_DATA_WIDTH_SHIFT 12 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8993_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8993_WSEQ_DATA_START_MASK 0x0F00 /* WSEQ_DATA_START - [11:8] */
+#define WM8993_WSEQ_DATA_START_SHIFT 8 /* WSEQ_DATA_START - [11:8] */
+#define WM8993_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [11:8] */
+#define WM8993_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */
+#define WM8993_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */
+#define WM8993_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R72 (0x48) - Write Sequencer 2
+ */
+#define WM8993_WSEQ_EOS 0x4000 /* WSEQ_EOS */
+#define WM8993_WSEQ_EOS_MASK 0x4000 /* WSEQ_EOS */
+#define WM8993_WSEQ_EOS_SHIFT 14 /* WSEQ_EOS */
+#define WM8993_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */
+#define WM8993_WSEQ_DELAY_MASK 0x0F00 /* WSEQ_DELAY - [11:8] */
+#define WM8993_WSEQ_DELAY_SHIFT 8 /* WSEQ_DELAY - [11:8] */
+#define WM8993_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [11:8] */
+#define WM8993_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */
+#define WM8993_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */
+#define WM8993_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */
+
+/*
+ * R73 (0x49) - Write Sequencer 3
+ */
+#define WM8993_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM8993_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM8993_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM8993_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8993_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM8993_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM8993_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM8993_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8993_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */
+#define WM8993_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */
+#define WM8993_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R74 (0x4A) - Write Sequencer 4
+ */
+#define WM8993_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */
+#define WM8993_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */
+#define WM8993_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */
+#define WM8993_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+
+/*
+ * R75 (0x4B) - Write Sequencer 5
+ */
+#define WM8993_WSEQ_CURRENT_INDEX_MASK 0x003F /* WSEQ_CURRENT_INDEX - [5:0] */
+#define WM8993_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [5:0] */
+#define WM8993_WSEQ_CURRENT_INDEX_WIDTH 6 /* WSEQ_CURRENT_INDEX - [5:0] */
+
+/*
+ * R76 (0x4C) - Charge Pump 1
+ */
+#define WM8993_CP_ENA 0x8000 /* CP_ENA */
+#define WM8993_CP_ENA_MASK 0x8000 /* CP_ENA */
+#define WM8993_CP_ENA_SHIFT 15 /* CP_ENA */
+#define WM8993_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R81 (0x51) - Class W 0
+ */
+#define WM8993_CP_DYN_FREQ 0x0002 /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_FREQ_MASK 0x0002 /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_FREQ_SHIFT 1 /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_FREQ_WIDTH 1 /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_V 0x0001 /* CP_DYN_V */
+#define WM8993_CP_DYN_V_MASK 0x0001 /* CP_DYN_V */
+#define WM8993_CP_DYN_V_SHIFT 0 /* CP_DYN_V */
+#define WM8993_CP_DYN_V_WIDTH 1 /* CP_DYN_V */
+
+/*
+ * R84 (0x54) - DC Servo 0
+ */
+#define WM8993_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_DAC_WR_1 0x0008 /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_1_MASK 0x0008 /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_1_SHIFT 3 /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_0 0x0004 /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_TRIG_DAC_WR_0_MASK 0x0004 /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_TRIG_DAC_WR_0_SHIFT 2 /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8993_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8993_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */
+#define WM8993_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */
+
+/*
+ * R85 (0x55) - DC Servo 1
+ */
+#define WM8993_DCS_SERIES_NO_01_MASK 0x0FE0 /* DCS_SERIES_NO_01 - [11:5] */
+#define WM8993_DCS_SERIES_NO_01_SHIFT 5 /* DCS_SERIES_NO_01 - [11:5] */
+#define WM8993_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [11:5] */
+#define WM8993_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8993_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8993_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R87 (0x57) - DC Servo 3
+ */
+#define WM8993_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8993_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8993_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8993_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8993_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8993_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R88 (0x58) - DC Servo Readback 0
+ */
+#define WM8993_DCS_DATAPATH_BUSY 0x4000 /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_DATAPATH_BUSY_MASK 0x4000 /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_DATAPATH_BUSY_SHIFT 14 /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_DATAPATH_BUSY_WIDTH 1 /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_CHANNEL_MASK 0x3000 /* DCS_CHANNEL - [13:12] */
+#define WM8993_DCS_CHANNEL_SHIFT 12 /* DCS_CHANNEL - [13:12] */
+#define WM8993_DCS_CHANNEL_WIDTH 2 /* DCS_CHANNEL - [13:12] */
+#define WM8993_DCS_CAL_COMPLETE_MASK 0x0300 /* DCS_CAL_COMPLETE - [9:8] */
+#define WM8993_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [9:8] */
+#define WM8993_DCS_CAL_COMPLETE_WIDTH 2 /* DCS_CAL_COMPLETE - [9:8] */
+#define WM8993_DCS_DAC_WR_COMPLETE_MASK 0x0030 /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM8993_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM8993_DCS_DAC_WR_COMPLETE_WIDTH 2 /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM8993_DCS_STARTUP_COMPLETE_MASK 0x0003 /* DCS_STARTUP_COMPLETE - [1:0] */
+#define WM8993_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [1:0] */
+#define WM8993_DCS_STARTUP_COMPLETE_WIDTH 2 /* DCS_STARTUP_COMPLETE - [1:0] */
+
+/*
+ * R89 (0x59) - DC Servo Readback 1
+ */
+#define WM8993_DCS_INTEG_CHAN_1_MASK 0x00FF /* DCS_INTEG_CHAN_1 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_1_SHIFT 0 /* DCS_INTEG_CHAN_1 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_1_WIDTH 8 /* DCS_INTEG_CHAN_1 - [7:0] */
+
+/*
+ * R90 (0x5A) - DC Servo Readback 2
+ */
+#define WM8993_DCS_INTEG_CHAN_0_MASK 0x00FF /* DCS_INTEG_CHAN_0 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_0_SHIFT 0 /* DCS_INTEG_CHAN_0 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_0_WIDTH 8 /* DCS_INTEG_CHAN_0 - [7:0] */
+
+/*
+ * R96 (0x60) - Analogue HP 0
+ */
+#define WM8993_HPOUT1_AUTO_PU 0x0100 /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1_AUTO_PU_MASK 0x0100 /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1_AUTO_PU_SHIFT 8 /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1_AUTO_PU_WIDTH 1 /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */
+#define WM8993_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */
+#define WM8993_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */
+#define WM8993_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */
+#define WM8993_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */
+#define WM8993_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */
+#define WM8993_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */
+#define WM8993_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */
+
+/*
+ * R98 (0x62) - EQ1
+ */
+#define WM8993_EQ_ENA 0x0001 /* EQ_ENA */
+#define WM8993_EQ_ENA_MASK 0x0001 /* EQ_ENA */
+#define WM8993_EQ_ENA_SHIFT 0 /* EQ_ENA */
+#define WM8993_EQ_ENA_WIDTH 1 /* EQ_ENA */
+
+/*
+ * R99 (0x63) - EQ2
+ */
+#define WM8993_EQ_B1_GAIN_MASK 0x001F /* EQ_B1_GAIN - [4:0] */
+#define WM8993_EQ_B1_GAIN_SHIFT 0 /* EQ_B1_GAIN - [4:0] */
+#define WM8993_EQ_B1_GAIN_WIDTH 5 /* EQ_B1_GAIN - [4:0] */
+
+/*
+ * R100 (0x64) - EQ3
+ */
+#define WM8993_EQ_B2_GAIN_MASK 0x001F /* EQ_B2_GAIN - [4:0] */
+#define WM8993_EQ_B2_GAIN_SHIFT 0 /* EQ_B2_GAIN - [4:0] */
+#define WM8993_EQ_B2_GAIN_WIDTH 5 /* EQ_B2_GAIN - [4:0] */
+
+/*
+ * R101 (0x65) - EQ4
+ */
+#define WM8993_EQ_B3_GAIN_MASK 0x001F /* EQ_B3_GAIN - [4:0] */
+#define WM8993_EQ_B3_GAIN_SHIFT 0 /* EQ_B3_GAIN - [4:0] */
+#define WM8993_EQ_B3_GAIN_WIDTH 5 /* EQ_B3_GAIN - [4:0] */
+
+/*
+ * R102 (0x66) - EQ5
+ */
+#define WM8993_EQ_B4_GAIN_MASK 0x001F /* EQ_B4_GAIN - [4:0] */
+#define WM8993_EQ_B4_GAIN_SHIFT 0 /* EQ_B4_GAIN - [4:0] */
+#define WM8993_EQ_B4_GAIN_WIDTH 5 /* EQ_B4_GAIN - [4:0] */
+
+/*
+ * R103 (0x67) - EQ6
+ */
+#define WM8993_EQ_B5_GAIN_MASK 0x001F /* EQ_B5_GAIN - [4:0] */
+#define WM8993_EQ_B5_GAIN_SHIFT 0 /* EQ_B5_GAIN - [4:0] */
+#define WM8993_EQ_B5_GAIN_WIDTH 5 /* EQ_B5_GAIN - [4:0] */
+
+/*
+ * R104 (0x68) - EQ7
+ */
+#define WM8993_EQ_B1_A_MASK 0xFFFF /* EQ_B1_A - [15:0] */
+#define WM8993_EQ_B1_A_SHIFT 0 /* EQ_B1_A - [15:0] */
+#define WM8993_EQ_B1_A_WIDTH 16 /* EQ_B1_A - [15:0] */
+
+/*
+ * R105 (0x69) - EQ8
+ */
+#define WM8993_EQ_B1_B_MASK 0xFFFF /* EQ_B1_B - [15:0] */
+#define WM8993_EQ_B1_B_SHIFT 0 /* EQ_B1_B - [15:0] */
+#define WM8993_EQ_B1_B_WIDTH 16 /* EQ_B1_B - [15:0] */
+
+/*
+ * R106 (0x6A) - EQ9
+ */
+#define WM8993_EQ_B1_PG_MASK 0xFFFF /* EQ_B1_PG - [15:0] */
+#define WM8993_EQ_B1_PG_SHIFT 0 /* EQ_B1_PG - [15:0] */
+#define WM8993_EQ_B1_PG_WIDTH 16 /* EQ_B1_PG - [15:0] */
+
+/*
+ * R107 (0x6B) - EQ10
+ */
+#define WM8993_EQ_B2_A_MASK 0xFFFF /* EQ_B2_A - [15:0] */
+#define WM8993_EQ_B2_A_SHIFT 0 /* EQ_B2_A - [15:0] */
+#define WM8993_EQ_B2_A_WIDTH 16 /* EQ_B2_A - [15:0] */
+
+/*
+ * R108 (0x6C) - EQ11
+ */
+#define WM8993_EQ_B2_B_MASK 0xFFFF /* EQ_B2_B - [15:0] */
+#define WM8993_EQ_B2_B_SHIFT 0 /* EQ_B2_B - [15:0] */
+#define WM8993_EQ_B2_B_WIDTH 16 /* EQ_B2_B - [15:0] */
+
+/*
+ * R109 (0x6D) - EQ12
+ */
+#define WM8993_EQ_B2_C_MASK 0xFFFF /* EQ_B2_C - [15:0] */
+#define WM8993_EQ_B2_C_SHIFT 0 /* EQ_B2_C - [15:0] */
+#define WM8993_EQ_B2_C_WIDTH 16 /* EQ_B2_C - [15:0] */
+
+/*
+ * R110 (0x6E) - EQ13
+ */
+#define WM8993_EQ_B2_PG_MASK 0xFFFF /* EQ_B2_PG - [15:0] */
+#define WM8993_EQ_B2_PG_SHIFT 0 /* EQ_B2_PG - [15:0] */
+#define WM8993_EQ_B2_PG_WIDTH 16 /* EQ_B2_PG - [15:0] */
+
+/*
+ * R111 (0x6F) - EQ14
+ */
+#define WM8993_EQ_B3_A_MASK 0xFFFF /* EQ_B3_A - [15:0] */
+#define WM8993_EQ_B3_A_SHIFT 0 /* EQ_B3_A - [15:0] */
+#define WM8993_EQ_B3_A_WIDTH 16 /* EQ_B3_A - [15:0] */
+
+/*
+ * R112 (0x70) - EQ15
+ */
+#define WM8993_EQ_B3_B_MASK 0xFFFF /* EQ_B3_B - [15:0] */
+#define WM8993_EQ_B3_B_SHIFT 0 /* EQ_B3_B - [15:0] */
+#define WM8993_EQ_B3_B_WIDTH 16 /* EQ_B3_B - [15:0] */
+
+/*
+ * R113 (0x71) - EQ16
+ */
+#define WM8993_EQ_B3_C_MASK 0xFFFF /* EQ_B3_C - [15:0] */
+#define WM8993_EQ_B3_C_SHIFT 0 /* EQ_B3_C - [15:0] */
+#define WM8993_EQ_B3_C_WIDTH 16 /* EQ_B3_C - [15:0] */
+
+/*
+ * R114 (0x72) - EQ17
+ */
+#define WM8993_EQ_B3_PG_MASK 0xFFFF /* EQ_B3_PG - [15:0] */
+#define WM8993_EQ_B3_PG_SHIFT 0 /* EQ_B3_PG - [15:0] */
+#define WM8993_EQ_B3_PG_WIDTH 16 /* EQ_B3_PG - [15:0] */
+
+/*
+ * R115 (0x73) - EQ18
+ */
+#define WM8993_EQ_B4_A_MASK 0xFFFF /* EQ_B4_A - [15:0] */
+#define WM8993_EQ_B4_A_SHIFT 0 /* EQ_B4_A - [15:0] */
+#define WM8993_EQ_B4_A_WIDTH 16 /* EQ_B4_A - [15:0] */
+
+/*
+ * R116 (0x74) - EQ19
+ */
+#define WM8993_EQ_B4_B_MASK 0xFFFF /* EQ_B4_B - [15:0] */
+#define WM8993_EQ_B4_B_SHIFT 0 /* EQ_B4_B - [15:0] */
+#define WM8993_EQ_B4_B_WIDTH 16 /* EQ_B4_B - [15:0] */
+
+/*
+ * R117 (0x75) - EQ20
+ */
+#define WM8993_EQ_B4_C_MASK 0xFFFF /* EQ_B4_C - [15:0] */
+#define WM8993_EQ_B4_C_SHIFT 0 /* EQ_B4_C - [15:0] */
+#define WM8993_EQ_B4_C_WIDTH 16 /* EQ_B4_C - [15:0] */
+
+/*
+ * R118 (0x76) - EQ21
+ */
+#define WM8993_EQ_B4_PG_MASK 0xFFFF /* EQ_B4_PG - [15:0] */
+#define WM8993_EQ_B4_PG_SHIFT 0 /* EQ_B4_PG - [15:0] */
+#define WM8993_EQ_B4_PG_WIDTH 16 /* EQ_B4_PG - [15:0] */
+
+/*
+ * R119 (0x77) - EQ22
+ */
+#define WM8993_EQ_B5_A_MASK 0xFFFF /* EQ_B5_A - [15:0] */
+#define WM8993_EQ_B5_A_SHIFT 0 /* EQ_B5_A - [15:0] */
+#define WM8993_EQ_B5_A_WIDTH 16 /* EQ_B5_A - [15:0] */
+
+/*
+ * R120 (0x78) - EQ23
+ */
+#define WM8993_EQ_B5_B_MASK 0xFFFF /* EQ_B5_B - [15:0] */
+#define WM8993_EQ_B5_B_SHIFT 0 /* EQ_B5_B - [15:0] */
+#define WM8993_EQ_B5_B_WIDTH 16 /* EQ_B5_B - [15:0] */
+
+/*
+ * R121 (0x79) - EQ24
+ */
+#define WM8993_EQ_B5_PG_MASK 0xFFFF /* EQ_B5_PG - [15:0] */
+#define WM8993_EQ_B5_PG_SHIFT 0 /* EQ_B5_PG - [15:0] */
+#define WM8993_EQ_B5_PG_WIDTH 16 /* EQ_B5_PG - [15:0] */
+
+/*
+ * R122 (0x7A) - Digital Pulls
+ */
+#define WM8993_MCLK_PU 0x0080 /* MCLK_PU */
+#define WM8993_MCLK_PU_MASK 0x0080 /* MCLK_PU */
+#define WM8993_MCLK_PU_SHIFT 7 /* MCLK_PU */
+#define WM8993_MCLK_PU_WIDTH 1 /* MCLK_PU */
+#define WM8993_MCLK_PD 0x0040 /* MCLK_PD */
+#define WM8993_MCLK_PD_MASK 0x0040 /* MCLK_PD */
+#define WM8993_MCLK_PD_SHIFT 6 /* MCLK_PD */
+#define WM8993_MCLK_PD_WIDTH 1 /* MCLK_PD */
+#define WM8993_DACDAT_PU 0x0020 /* DACDAT_PU */
+#define WM8993_DACDAT_PU_MASK 0x0020 /* DACDAT_PU */
+#define WM8993_DACDAT_PU_SHIFT 5 /* DACDAT_PU */
+#define WM8993_DACDAT_PU_WIDTH 1 /* DACDAT_PU */
+#define WM8993_DACDAT_PD 0x0010 /* DACDAT_PD */
+#define WM8993_DACDAT_PD_MASK 0x0010 /* DACDAT_PD */
+#define WM8993_DACDAT_PD_SHIFT 4 /* DACDAT_PD */
+#define WM8993_DACDAT_PD_WIDTH 1 /* DACDAT_PD */
+#define WM8993_LRCLK_PU 0x0008 /* LRCLK_PU */
+#define WM8993_LRCLK_PU_MASK 0x0008 /* LRCLK_PU */
+#define WM8993_LRCLK_PU_SHIFT 3 /* LRCLK_PU */
+#define WM8993_LRCLK_PU_WIDTH 1 /* LRCLK_PU */
+#define WM8993_LRCLK_PD 0x0004 /* LRCLK_PD */
+#define WM8993_LRCLK_PD_MASK 0x0004 /* LRCLK_PD */
+#define WM8993_LRCLK_PD_SHIFT 2 /* LRCLK_PD */
+#define WM8993_LRCLK_PD_WIDTH 1 /* LRCLK_PD */
+#define WM8993_BCLK_PU 0x0002 /* BCLK_PU */
+#define WM8993_BCLK_PU_MASK 0x0002 /* BCLK_PU */
+#define WM8993_BCLK_PU_SHIFT 1 /* BCLK_PU */
+#define WM8993_BCLK_PU_WIDTH 1 /* BCLK_PU */
+#define WM8993_BCLK_PD 0x0001 /* BCLK_PD */
+#define WM8993_BCLK_PD_MASK 0x0001 /* BCLK_PD */
+#define WM8993_BCLK_PD_SHIFT 0 /* BCLK_PD */
+#define WM8993_BCLK_PD_WIDTH 1 /* BCLK_PD */
+
+/*
+ * R123 (0x7B) - DRC Control 1
+ */
+#define WM8993_DRC_ENA 0x8000 /* DRC_ENA */
+#define WM8993_DRC_ENA_MASK 0x8000 /* DRC_ENA */
+#define WM8993_DRC_ENA_SHIFT 15 /* DRC_ENA */
+#define WM8993_DRC_ENA_WIDTH 1 /* DRC_ENA */
+#define WM8993_DRC_DAC_PATH 0x4000 /* DRC_DAC_PATH */
+#define WM8993_DRC_DAC_PATH_MASK 0x4000 /* DRC_DAC_PATH */
+#define WM8993_DRC_DAC_PATH_SHIFT 14 /* DRC_DAC_PATH */
+#define WM8993_DRC_DAC_PATH_WIDTH 1 /* DRC_DAC_PATH */
+#define WM8993_DRC_SMOOTH_ENA 0x0800 /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_SMOOTH_ENA_MASK 0x0800 /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_SMOOTH_ENA_SHIFT 11 /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_SMOOTH_ENA_WIDTH 1 /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_QR_ENA 0x0400 /* DRC_QR_ENA */
+#define WM8993_DRC_QR_ENA_MASK 0x0400 /* DRC_QR_ENA */
+#define WM8993_DRC_QR_ENA_SHIFT 10 /* DRC_QR_ENA */
+#define WM8993_DRC_QR_ENA_WIDTH 1 /* DRC_QR_ENA */
+#define WM8993_DRC_ANTICLIP_ENA 0x0200 /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_ANTICLIP_ENA_MASK 0x0200 /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_ANTICLIP_ENA_SHIFT 9 /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_ANTICLIP_ENA_WIDTH 1 /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_HYST_ENA 0x0100 /* DRC_HYST_ENA */
+#define WM8993_DRC_HYST_ENA_MASK 0x0100 /* DRC_HYST_ENA */
+#define WM8993_DRC_HYST_ENA_SHIFT 8 /* DRC_HYST_ENA */
+#define WM8993_DRC_HYST_ENA_WIDTH 1 /* DRC_HYST_ENA */
+#define WM8993_DRC_THRESH_HYST_MASK 0x0030 /* DRC_THRESH_HYST - [5:4] */
+#define WM8993_DRC_THRESH_HYST_SHIFT 4 /* DRC_THRESH_HYST - [5:4] */
+#define WM8993_DRC_THRESH_HYST_WIDTH 2 /* DRC_THRESH_HYST - [5:4] */
+#define WM8993_DRC_MINGAIN_MASK 0x000C /* DRC_MINGAIN - [3:2] */
+#define WM8993_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [3:2] */
+#define WM8993_DRC_MINGAIN_WIDTH 2 /* DRC_MINGAIN - [3:2] */
+#define WM8993_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */
+#define WM8993_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */
+#define WM8993_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R124 (0x7C) - DRC Control 2
+ */
+#define WM8993_DRC_ATTACK_RATE_MASK 0xF000 /* DRC_ATTACK_RATE - [15:12] */
+#define WM8993_DRC_ATTACK_RATE_SHIFT 12 /* DRC_ATTACK_RATE - [15:12] */
+#define WM8993_DRC_ATTACK_RATE_WIDTH 4 /* DRC_ATTACK_RATE - [15:12] */
+#define WM8993_DRC_DECAY_RATE_MASK 0x0F00 /* DRC_DECAY_RATE - [11:8] */
+#define WM8993_DRC_DECAY_RATE_SHIFT 8 /* DRC_DECAY_RATE - [11:8] */
+#define WM8993_DRC_DECAY_RATE_WIDTH 4 /* DRC_DECAY_RATE - [11:8] */
+#define WM8993_DRC_THRESH_COMP_MASK 0x00FC /* DRC_THRESH_COMP - [7:2] */
+#define WM8993_DRC_THRESH_COMP_SHIFT 2 /* DRC_THRESH_COMP - [7:2] */
+#define WM8993_DRC_THRESH_COMP_WIDTH 6 /* DRC_THRESH_COMP - [7:2] */
+
+/*
+ * R125 (0x7D) - DRC Control 3
+ */
+#define WM8993_DRC_AMP_COMP_MASK 0xF800 /* DRC_AMP_COMP - [15:11] */
+#define WM8993_DRC_AMP_COMP_SHIFT 11 /* DRC_AMP_COMP - [15:11] */
+#define WM8993_DRC_AMP_COMP_WIDTH 5 /* DRC_AMP_COMP - [15:11] */
+#define WM8993_DRC_R0_SLOPE_COMP_MASK 0x0700 /* DRC_R0_SLOPE_COMP - [10:8] */
+#define WM8993_DRC_R0_SLOPE_COMP_SHIFT 8 /* DRC_R0_SLOPE_COMP - [10:8] */
+#define WM8993_DRC_R0_SLOPE_COMP_WIDTH 3 /* DRC_R0_SLOPE_COMP - [10:8] */
+#define WM8993_DRC_FF_DELAY 0x0080 /* DRC_FF_DELAY */
+#define WM8993_DRC_FF_DELAY_MASK 0x0080 /* DRC_FF_DELAY */
+#define WM8993_DRC_FF_DELAY_SHIFT 7 /* DRC_FF_DELAY */
+#define WM8993_DRC_FF_DELAY_WIDTH 1 /* DRC_FF_DELAY */
+#define WM8993_DRC_THRESH_QR_MASK 0x000C /* DRC_THRESH_QR - [3:2] */
+#define WM8993_DRC_THRESH_QR_SHIFT 2 /* DRC_THRESH_QR - [3:2] */
+#define WM8993_DRC_THRESH_QR_WIDTH 2 /* DRC_THRESH_QR - [3:2] */
+#define WM8993_DRC_RATE_QR_MASK 0x0003 /* DRC_RATE_QR - [1:0] */
+#define WM8993_DRC_RATE_QR_SHIFT 0 /* DRC_RATE_QR - [1:0] */
+#define WM8993_DRC_RATE_QR_WIDTH 2 /* DRC_RATE_QR - [1:0] */
+
+/*
+ * R126 (0x7E) - DRC Control 4
+ */
+#define WM8993_DRC_R1_SLOPE_COMP_MASK 0xE000 /* DRC_R1_SLOPE_COMP - [15:13] */
+#define WM8993_DRC_R1_SLOPE_COMP_SHIFT 13 /* DRC_R1_SLOPE_COMP - [15:13] */
+#define WM8993_DRC_R1_SLOPE_COMP_WIDTH 3 /* DRC_R1_SLOPE_COMP - [15:13] */
+#define WM8993_DRC_STARTUP_GAIN_MASK 0x1F00 /* DRC_STARTUP_GAIN - [12:8] */
+#define WM8993_DRC_STARTUP_GAIN_SHIFT 8 /* DRC_STARTUP_GAIN - [12:8] */
+#define WM8993_DRC_STARTUP_GAIN_WIDTH 5 /* DRC_STARTUP_GAIN - [12:8] */
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8994.c b/kernel/sound/soc/codecs/wm8994.c
new file mode 100644
index 000000000..a1c04dab6
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8994.c
@@ -0,0 +1,4523 @@
+/*
+ * wm8994.c -- WM8994 ALSA SoC Audio driver
+ *
+ * Copyright 2009-12 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gcd.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <trace/events/asoc.h>
+
+#include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/mfd/wm8994/pdata.h>
+#include <linux/mfd/wm8994/gpio.h>
+
+#include "wm8994.h"
+#include "wm_hubs.h"
+
+#define WM1811_JACKDET_MODE_NONE 0x0000
+#define WM1811_JACKDET_MODE_JACK 0x0100
+#define WM1811_JACKDET_MODE_MIC 0x0080
+#define WM1811_JACKDET_MODE_AUDIO 0x0180
+
+#define WM8994_NUM_DRC 3
+#define WM8994_NUM_EQ 3
+
+static struct {
+ unsigned int reg;
+ unsigned int mask;
+} wm8994_vu_bits[] = {
+ { WM8994_LEFT_LINE_INPUT_1_2_VOLUME, WM8994_IN1_VU },
+ { WM8994_RIGHT_LINE_INPUT_1_2_VOLUME, WM8994_IN1_VU },
+ { WM8994_LEFT_LINE_INPUT_3_4_VOLUME, WM8994_IN2_VU },
+ { WM8994_RIGHT_LINE_INPUT_3_4_VOLUME, WM8994_IN2_VU },
+ { WM8994_SPEAKER_VOLUME_LEFT, WM8994_SPKOUT_VU },
+ { WM8994_SPEAKER_VOLUME_RIGHT, WM8994_SPKOUT_VU },
+ { WM8994_LEFT_OUTPUT_VOLUME, WM8994_HPOUT1_VU },
+ { WM8994_RIGHT_OUTPUT_VOLUME, WM8994_HPOUT1_VU },
+ { WM8994_LEFT_OPGA_VOLUME, WM8994_MIXOUT_VU },
+ { WM8994_RIGHT_OPGA_VOLUME, WM8994_MIXOUT_VU },
+
+ { WM8994_AIF1_DAC1_LEFT_VOLUME, WM8994_AIF1DAC1_VU },
+ { WM8994_AIF1_DAC1_RIGHT_VOLUME, WM8994_AIF1DAC1_VU },
+ { WM8994_AIF1_DAC2_LEFT_VOLUME, WM8994_AIF1DAC2_VU },
+ { WM8994_AIF1_DAC2_RIGHT_VOLUME, WM8994_AIF1DAC2_VU },
+ { WM8994_AIF2_DAC_LEFT_VOLUME, WM8994_AIF2DAC_VU },
+ { WM8994_AIF2_DAC_RIGHT_VOLUME, WM8994_AIF2DAC_VU },
+ { WM8994_AIF1_ADC1_LEFT_VOLUME, WM8994_AIF1ADC1_VU },
+ { WM8994_AIF1_ADC1_RIGHT_VOLUME, WM8994_AIF1ADC1_VU },
+ { WM8994_AIF1_ADC2_LEFT_VOLUME, WM8994_AIF1ADC2_VU },
+ { WM8994_AIF1_ADC2_RIGHT_VOLUME, WM8994_AIF1ADC2_VU },
+ { WM8994_AIF2_ADC_LEFT_VOLUME, WM8994_AIF2ADC_VU },
+ { WM8994_AIF2_ADC_RIGHT_VOLUME, WM8994_AIF1ADC2_VU },
+ { WM8994_DAC1_LEFT_VOLUME, WM8994_DAC1_VU },
+ { WM8994_DAC1_RIGHT_VOLUME, WM8994_DAC1_VU },
+ { WM8994_DAC2_LEFT_VOLUME, WM8994_DAC2_VU },
+ { WM8994_DAC2_RIGHT_VOLUME, WM8994_DAC2_VU },
+};
+
+static int wm8994_drc_base[] = {
+ WM8994_AIF1_DRC1_1,
+ WM8994_AIF1_DRC2_1,
+ WM8994_AIF2_DRC_1,
+};
+
+static int wm8994_retune_mobile_base[] = {
+ WM8994_AIF1_DAC1_EQ_GAINS_1,
+ WM8994_AIF1_DAC2_EQ_GAINS_1,
+ WM8994_AIF2_EQ_GAINS_1,
+};
+
+static const struct wm8958_micd_rate micdet_rates[] = {
+ { 32768, true, 1, 4 },
+ { 32768, false, 1, 1 },
+ { 44100 * 256, true, 7, 10 },
+ { 44100 * 256, false, 7, 10 },
+};
+
+static const struct wm8958_micd_rate jackdet_rates[] = {
+ { 32768, true, 0, 1 },
+ { 32768, false, 0, 1 },
+ { 44100 * 256, true, 10, 10 },
+ { 44100 * 256, false, 7, 8 },
+};
+
+static void wm8958_micd_set_rate(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ int best, i, sysclk, val;
+ bool idle;
+ const struct wm8958_micd_rate *rates;
+ int num_rates;
+
+ idle = !wm8994->jack_mic;
+
+ sysclk = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (sysclk & WM8994_SYSCLK_SRC)
+ sysclk = wm8994->aifclk[1];
+ else
+ sysclk = wm8994->aifclk[0];
+
+ if (control->pdata.micd_rates) {
+ rates = control->pdata.micd_rates;
+ num_rates = control->pdata.num_micd_rates;
+ } else if (wm8994->jackdet) {
+ rates = jackdet_rates;
+ num_rates = ARRAY_SIZE(jackdet_rates);
+ } else {
+ rates = micdet_rates;
+ num_rates = ARRAY_SIZE(micdet_rates);
+ }
+
+ best = 0;
+ for (i = 0; i < num_rates; i++) {
+ if (rates[i].idle != idle)
+ continue;
+ if (abs(rates[i].sysclk - sysclk) <
+ abs(rates[best].sysclk - sysclk))
+ best = i;
+ else if (rates[best].idle != idle)
+ best = i;
+ }
+
+ val = rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT
+ | rates[best].rate << WM8958_MICD_RATE_SHIFT;
+
+ dev_dbg(codec->dev, "MICD rate %d,%d for %dHz %s\n",
+ rates[best].start, rates[best].rate, sysclk,
+ idle ? "idle" : "active");
+
+ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ WM8958_MICD_BIAS_STARTTIME_MASK |
+ WM8958_MICD_RATE_MASK, val);
+}
+
+static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int rate;
+ int reg1 = 0;
+ int offset;
+
+ if (aif)
+ offset = 4;
+ else
+ offset = 0;
+
+ switch (wm8994->sysclk[aif]) {
+ case WM8994_SYSCLK_MCLK1:
+ rate = wm8994->mclk[0];
+ break;
+
+ case WM8994_SYSCLK_MCLK2:
+ reg1 |= 0x8;
+ rate = wm8994->mclk[1];
+ break;
+
+ case WM8994_SYSCLK_FLL1:
+ reg1 |= 0x10;
+ rate = wm8994->fll[0].out;
+ break;
+
+ case WM8994_SYSCLK_FLL2:
+ reg1 |= 0x18;
+ rate = wm8994->fll[1].out;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (rate >= 13500000) {
+ rate /= 2;
+ reg1 |= WM8994_AIF1CLK_DIV;
+
+ dev_dbg(codec->dev, "Dividing AIF%d clock to %dHz\n",
+ aif + 1, rate);
+ }
+
+ wm8994->aifclk[aif] = rate;
+
+ snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1 + offset,
+ WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV,
+ reg1);
+
+ return 0;
+}
+
+static int configure_clock(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int change, new;
+
+ /* Bring up the AIF clocks first */
+ configure_aif_clock(codec, 0);
+ configure_aif_clock(codec, 1);
+
+ /* Then switch CLK_SYS over to the higher of them; a change
+ * can only happen as a result of a clocking change which can
+ * only be made outside of DAPM so we can safely redo the
+ * clocking.
+ */
+
+ /* If they're equal it doesn't matter which is used */
+ if (wm8994->aifclk[0] == wm8994->aifclk[1]) {
+ wm8958_micd_set_rate(codec);
+ return 0;
+ }
+
+ if (wm8994->aifclk[0] < wm8994->aifclk[1])
+ new = WM8994_SYSCLK_SRC;
+ else
+ new = 0;
+
+ change = snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ WM8994_SYSCLK_SRC, new);
+ if (change)
+ snd_soc_dapm_sync(&codec->dapm);
+
+ wm8958_micd_set_rate(codec);
+
+ return 0;
+}
+
+static int check_clk_sys(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ int reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ const char *clk;
+
+ /* Check what we're currently using for CLK_SYS */
+ if (reg & WM8994_SYSCLK_SRC)
+ clk = "AIF2CLK";
+ else
+ clk = "AIF1CLK";
+
+ return strcmp(source->name, clk) == 0;
+}
+
+static const char *sidetone_hpf_text[] = {
+ "2.7kHz", "1.35kHz", "675Hz", "370Hz", "180Hz", "90Hz", "45Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(sidetone_hpf,
+ WM8994_SIDETONE, 7, sidetone_hpf_text);
+
+static const char *adc_hpf_text[] = {
+ "HiFi", "Voice 1", "Voice 2", "Voice 3"
+};
+
+static SOC_ENUM_SINGLE_DECL(aif1adc1_hpf,
+ WM8994_AIF1_ADC1_FILTERS, 13, adc_hpf_text);
+
+static SOC_ENUM_SINGLE_DECL(aif1adc2_hpf,
+ WM8994_AIF1_ADC2_FILTERS, 13, adc_hpf_text);
+
+static SOC_ENUM_SINGLE_DECL(aif2adc_hpf,
+ WM8994_AIF2_ADC_FILTERS, 13, adc_hpf_text);
+
+static const DECLARE_TLV_DB_SCALE(aif_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(st_tlv, -3600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(wm8994_3d_tlv, -1600, 183, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(mixin_boost_tlv, 0, 900, 0);
+
+#define WM8994_DRC_SWITCH(xname, reg, shift) \
+ SOC_SINGLE_EXT(xname, reg, shift, 1, 0, \
+ snd_soc_get_volsw, wm8994_put_drc_sw)
+
+static int wm8994_put_drc_sw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int mask, ret;
+
+ /* Can't enable both ADC and DAC paths simultaneously */
+ if (mc->shift == WM8994_AIF1DAC1_DRC_ENA_SHIFT)
+ mask = WM8994_AIF1ADC1L_DRC_ENA_MASK |
+ WM8994_AIF1ADC1R_DRC_ENA_MASK;
+ else
+ mask = WM8994_AIF1DAC1_DRC_ENA_MASK;
+
+ ret = snd_soc_read(codec, mc->reg);
+ if (ret < 0)
+ return ret;
+ if (ret & mask)
+ return -EINVAL;
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+static void wm8994_set_drc(struct snd_soc_codec *codec, int drc)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ struct wm8994_pdata *pdata = &control->pdata;
+ int base = wm8994_drc_base[drc];
+ int cfg = wm8994->drc_cfg[drc];
+ int save, i;
+
+ /* Save any enables; the configuration should clear them. */
+ save = snd_soc_read(codec, base);
+ save &= WM8994_AIF1DAC1_DRC_ENA | WM8994_AIF1ADC1L_DRC_ENA |
+ WM8994_AIF1ADC1R_DRC_ENA;
+
+ for (i = 0; i < WM8994_DRC_REGS; i++)
+ snd_soc_update_bits(codec, base + i, 0xffff,
+ pdata->drc_cfgs[cfg].regs[i]);
+
+ snd_soc_update_bits(codec, base, WM8994_AIF1DAC1_DRC_ENA |
+ WM8994_AIF1ADC1L_DRC_ENA |
+ WM8994_AIF1ADC1R_DRC_ENA, save);
+}
+
+/* Icky as hell but saves code duplication */
+static int wm8994_get_drc(const char *name)
+{
+ if (strcmp(name, "AIF1DRC1 Mode") == 0)
+ return 0;
+ if (strcmp(name, "AIF1DRC2 Mode") == 0)
+ return 1;
+ if (strcmp(name, "AIF2DRC Mode") == 0)
+ return 2;
+ return -EINVAL;
+}
+
+static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ struct wm8994_pdata *pdata = &control->pdata;
+ int drc = wm8994_get_drc(kcontrol->id.name);
+ int value = ucontrol->value.integer.value[0];
+
+ if (drc < 0)
+ return drc;
+
+ if (value >= pdata->num_drc_cfgs)
+ return -EINVAL;
+
+ wm8994->drc_cfg[drc] = value;
+
+ wm8994_set_drc(codec, drc);
+
+ return 0;
+}
+
+static int wm8994_get_drc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int drc = wm8994_get_drc(kcontrol->id.name);
+
+ if (drc < 0)
+ return drc;
+ ucontrol->value.enumerated.item[0] = wm8994->drc_cfg[drc];
+
+ return 0;
+}
+
+static void wm8994_set_retune_mobile(struct snd_soc_codec *codec, int block)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ struct wm8994_pdata *pdata = &control->pdata;
+ int base = wm8994_retune_mobile_base[block];
+ int iface, best, best_val, save, i, cfg;
+
+ if (!pdata || !wm8994->num_retune_mobile_texts)
+ return;
+
+ switch (block) {
+ case 0:
+ case 1:
+ iface = 0;
+ break;
+ case 2:
+ iface = 1;
+ break;
+ default:
+ return;
+ }
+
+ /* Find the version of the currently selected configuration
+ * with the nearest sample rate. */
+ cfg = wm8994->retune_mobile_cfg[block];
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8994->retune_mobile_texts[cfg]) == 0 &&
+ abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8994->dac_rates[iface]) < best_val) {
+ best = i;
+ best_val = abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8994->dac_rates[iface]);
+ }
+ }
+
+ dev_dbg(codec->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
+ block,
+ pdata->retune_mobile_cfgs[best].name,
+ pdata->retune_mobile_cfgs[best].rate,
+ wm8994->dac_rates[iface]);
+
+ /* The EQ will be disabled while reconfiguring it, remember the
+ * current configuration.
+ */
+ save = snd_soc_read(codec, base);
+ save &= WM8994_AIF1DAC1_EQ_ENA;
+
+ for (i = 0; i < WM8994_EQ_REGS; i++)
+ snd_soc_update_bits(codec, base + i, 0xffff,
+ pdata->retune_mobile_cfgs[best].regs[i]);
+
+ snd_soc_update_bits(codec, base, WM8994_AIF1DAC1_EQ_ENA, save);
+}
+
+/* Icky as hell but saves code duplication */
+static int wm8994_get_retune_mobile_block(const char *name)
+{
+ if (strcmp(name, "AIF1.1 EQ Mode") == 0)
+ return 0;
+ if (strcmp(name, "AIF1.2 EQ Mode") == 0)
+ return 1;
+ if (strcmp(name, "AIF2 EQ Mode") == 0)
+ return 2;
+ return -EINVAL;
+}
+
+static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ struct wm8994_pdata *pdata = &control->pdata;
+ int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
+ int value = ucontrol->value.integer.value[0];
+
+ if (block < 0)
+ return block;
+
+ if (value >= pdata->num_retune_mobile_cfgs)
+ return -EINVAL;
+
+ wm8994->retune_mobile_cfg[block] = value;
+
+ wm8994_set_retune_mobile(codec, block);
+
+ return 0;
+}
+
+static int wm8994_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
+
+ if (block < 0)
+ return block;
+
+ ucontrol->value.enumerated.item[0] = wm8994->retune_mobile_cfg[block];
+
+ return 0;
+}
+
+static const char *aif_chan_src_text[] = {
+ "Left", "Right"
+};
+
+static SOC_ENUM_SINGLE_DECL(aif1adcl_src,
+ WM8994_AIF1_CONTROL_1, 15, aif_chan_src_text);
+
+static SOC_ENUM_SINGLE_DECL(aif1adcr_src,
+ WM8994_AIF1_CONTROL_1, 14, aif_chan_src_text);
+
+static SOC_ENUM_SINGLE_DECL(aif2adcl_src,
+ WM8994_AIF2_CONTROL_1, 15, aif_chan_src_text);
+
+static SOC_ENUM_SINGLE_DECL(aif2adcr_src,
+ WM8994_AIF2_CONTROL_1, 14, aif_chan_src_text);
+
+static SOC_ENUM_SINGLE_DECL(aif1dacl_src,
+ WM8994_AIF1_CONTROL_2, 15, aif_chan_src_text);
+
+static SOC_ENUM_SINGLE_DECL(aif1dacr_src,
+ WM8994_AIF1_CONTROL_2, 14, aif_chan_src_text);
+
+static SOC_ENUM_SINGLE_DECL(aif2dacl_src,
+ WM8994_AIF2_CONTROL_2, 15, aif_chan_src_text);
+
+static SOC_ENUM_SINGLE_DECL(aif2dacr_src,
+ WM8994_AIF2_CONTROL_2, 14, aif_chan_src_text);
+
+static const char *osr_text[] = {
+ "Low Power", "High Performance",
+};
+
+static SOC_ENUM_SINGLE_DECL(dac_osr,
+ WM8994_OVERSAMPLING, 0, osr_text);
+
+static SOC_ENUM_SINGLE_DECL(adc_osr,
+ WM8994_OVERSAMPLING, 1, osr_text);
+
+static const struct snd_kcontrol_new wm8994_snd_controls[] = {
+SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME,
+ WM8994_AIF1_ADC1_RIGHT_VOLUME,
+ 1, 119, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8994_AIF1_ADC2_LEFT_VOLUME,
+ WM8994_AIF1_ADC2_RIGHT_VOLUME,
+ 1, 119, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8994_AIF2_ADC_LEFT_VOLUME,
+ WM8994_AIF2_ADC_RIGHT_VOLUME,
+ 1, 119, 0, digital_tlv),
+
+SOC_ENUM("AIF1ADCL Source", aif1adcl_src),
+SOC_ENUM("AIF1ADCR Source", aif1adcr_src),
+SOC_ENUM("AIF2ADCL Source", aif2adcl_src),
+SOC_ENUM("AIF2ADCR Source", aif2adcr_src),
+
+SOC_ENUM("AIF1DACL Source", aif1dacl_src),
+SOC_ENUM("AIF1DACR Source", aif1dacr_src),
+SOC_ENUM("AIF2DACL Source", aif2dacl_src),
+SOC_ENUM("AIF2DACR Source", aif2dacr_src),
+
+SOC_DOUBLE_R_TLV("AIF1DAC1 Volume", WM8994_AIF1_DAC1_LEFT_VOLUME,
+ WM8994_AIF1_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF1DAC2 Volume", WM8994_AIF1_DAC2_LEFT_VOLUME,
+ WM8994_AIF1_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF2DAC Volume", WM8994_AIF2_DAC_LEFT_VOLUME,
+ WM8994_AIF2_DAC_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+
+SOC_SINGLE_TLV("AIF1 Boost Volume", WM8994_AIF1_CONTROL_2, 10, 3, 0, aif_tlv),
+SOC_SINGLE_TLV("AIF2 Boost Volume", WM8994_AIF2_CONTROL_2, 10, 3, 0, aif_tlv),
+
+SOC_SINGLE("AIF1DAC1 EQ Switch", WM8994_AIF1_DAC1_EQ_GAINS_1, 0, 1, 0),
+SOC_SINGLE("AIF1DAC2 EQ Switch", WM8994_AIF1_DAC2_EQ_GAINS_1, 0, 1, 0),
+SOC_SINGLE("AIF2 EQ Switch", WM8994_AIF2_EQ_GAINS_1, 0, 1, 0),
+
+WM8994_DRC_SWITCH("AIF1DAC1 DRC Switch", WM8994_AIF1_DRC1_1, 2),
+WM8994_DRC_SWITCH("AIF1ADC1L DRC Switch", WM8994_AIF1_DRC1_1, 1),
+WM8994_DRC_SWITCH("AIF1ADC1R DRC Switch", WM8994_AIF1_DRC1_1, 0),
+
+WM8994_DRC_SWITCH("AIF1DAC2 DRC Switch", WM8994_AIF1_DRC2_1, 2),
+WM8994_DRC_SWITCH("AIF1ADC2L DRC Switch", WM8994_AIF1_DRC2_1, 1),
+WM8994_DRC_SWITCH("AIF1ADC2R DRC Switch", WM8994_AIF1_DRC2_1, 0),
+
+WM8994_DRC_SWITCH("AIF2DAC DRC Switch", WM8994_AIF2_DRC_1, 2),
+WM8994_DRC_SWITCH("AIF2ADCL DRC Switch", WM8994_AIF2_DRC_1, 1),
+WM8994_DRC_SWITCH("AIF2ADCR DRC Switch", WM8994_AIF2_DRC_1, 0),
+
+SOC_SINGLE_TLV("DAC1 Right Sidetone Volume", WM8994_DAC1_MIXER_VOLUMES,
+ 5, 12, 0, st_tlv),
+SOC_SINGLE_TLV("DAC1 Left Sidetone Volume", WM8994_DAC1_MIXER_VOLUMES,
+ 0, 12, 0, st_tlv),
+SOC_SINGLE_TLV("DAC2 Right Sidetone Volume", WM8994_DAC2_MIXER_VOLUMES,
+ 5, 12, 0, st_tlv),
+SOC_SINGLE_TLV("DAC2 Left Sidetone Volume", WM8994_DAC2_MIXER_VOLUMES,
+ 0, 12, 0, st_tlv),
+SOC_ENUM("Sidetone HPF Mux", sidetone_hpf),
+SOC_SINGLE("Sidetone HPF Switch", WM8994_SIDETONE, 6, 1, 0),
+
+SOC_ENUM("AIF1ADC1 HPF Mode", aif1adc1_hpf),
+SOC_DOUBLE("AIF1ADC1 HPF Switch", WM8994_AIF1_ADC1_FILTERS, 12, 11, 1, 0),
+
+SOC_ENUM("AIF1ADC2 HPF Mode", aif1adc2_hpf),
+SOC_DOUBLE("AIF1ADC2 HPF Switch", WM8994_AIF1_ADC2_FILTERS, 12, 11, 1, 0),
+
+SOC_ENUM("AIF2ADC HPF Mode", aif2adc_hpf),
+SOC_DOUBLE("AIF2ADC HPF Switch", WM8994_AIF2_ADC_FILTERS, 12, 11, 1, 0),
+
+SOC_ENUM("ADC OSR", adc_osr),
+SOC_ENUM("DAC OSR", dac_osr),
+
+SOC_DOUBLE_R_TLV("DAC1 Volume", WM8994_DAC1_LEFT_VOLUME,
+ WM8994_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R("DAC1 Switch", WM8994_DAC1_LEFT_VOLUME,
+ WM8994_DAC1_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DAC2 Volume", WM8994_DAC2_LEFT_VOLUME,
+ WM8994_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R("DAC2 Switch", WM8994_DAC2_LEFT_VOLUME,
+ WM8994_DAC2_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_SINGLE_TLV("SPKL DAC2 Volume", WM8994_SPKMIXL_ATTENUATION,
+ 6, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKL DAC1 Volume", WM8994_SPKMIXL_ATTENUATION,
+ 2, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_SINGLE_TLV("SPKR DAC2 Volume", WM8994_SPKMIXR_ATTENUATION,
+ 6, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKR DAC1 Volume", WM8994_SPKMIXR_ATTENUATION,
+ 2, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_SINGLE_TLV("AIF1DAC1 3D Stereo Volume", WM8994_AIF1_DAC1_FILTERS_2,
+ 10, 15, 0, wm8994_3d_tlv),
+SOC_SINGLE("AIF1DAC1 3D Stereo Switch", WM8994_AIF1_DAC1_FILTERS_2,
+ 8, 1, 0),
+SOC_SINGLE_TLV("AIF1DAC2 3D Stereo Volume", WM8994_AIF1_DAC2_FILTERS_2,
+ 10, 15, 0, wm8994_3d_tlv),
+SOC_SINGLE("AIF1DAC2 3D Stereo Switch", WM8994_AIF1_DAC2_FILTERS_2,
+ 8, 1, 0),
+SOC_SINGLE_TLV("AIF2DAC 3D Stereo Volume", WM8994_AIF2_DAC_FILTERS_2,
+ 10, 15, 0, wm8994_3d_tlv),
+SOC_SINGLE("AIF2DAC 3D Stereo Switch", WM8994_AIF2_DAC_FILTERS_2,
+ 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8994_eq_controls[] = {
+SOC_SINGLE_TLV("AIF1DAC1 EQ1 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC1 EQ2 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC1 EQ3 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC1 EQ4 Volume", WM8994_AIF1_DAC1_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC1 EQ5 Volume", WM8994_AIF1_DAC1_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+
+SOC_SINGLE_TLV("AIF1DAC2 EQ1 Volume", WM8994_AIF1_DAC2_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC2 EQ2 Volume", WM8994_AIF1_DAC2_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC2 EQ3 Volume", WM8994_AIF1_DAC2_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC2 EQ4 Volume", WM8994_AIF1_DAC2_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC2 EQ5 Volume", WM8994_AIF1_DAC2_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+
+SOC_SINGLE_TLV("AIF2 EQ1 Volume", WM8994_AIF2_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF2 EQ2 Volume", WM8994_AIF2_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF2 EQ3 Volume", WM8994_AIF2_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF2 EQ4 Volume", WM8994_AIF2_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+};
+
+static const struct snd_kcontrol_new wm8994_drc_controls[] = {
+SND_SOC_BYTES_MASK("AIF1.1 DRC", WM8994_AIF1_DRC1_1, 5,
+ WM8994_AIF1DAC1_DRC_ENA | WM8994_AIF1ADC1L_DRC_ENA |
+ WM8994_AIF1ADC1R_DRC_ENA),
+SND_SOC_BYTES_MASK("AIF1.2 DRC", WM8994_AIF1_DRC2_1, 5,
+ WM8994_AIF1DAC2_DRC_ENA | WM8994_AIF1ADC2L_DRC_ENA |
+ WM8994_AIF1ADC2R_DRC_ENA),
+SND_SOC_BYTES_MASK("AIF2 DRC", WM8994_AIF2_DRC_1, 5,
+ WM8994_AIF2DAC_DRC_ENA | WM8994_AIF2ADCL_DRC_ENA |
+ WM8994_AIF2ADCR_DRC_ENA),
+};
+
+static const char *wm8958_ng_text[] = {
+ "30ms", "125ms", "250ms", "500ms",
+};
+
+static SOC_ENUM_SINGLE_DECL(wm8958_aif1dac1_ng_hold,
+ WM8958_AIF1_DAC1_NOISE_GATE,
+ WM8958_AIF1DAC1_NG_THR_SHIFT,
+ wm8958_ng_text);
+
+static SOC_ENUM_SINGLE_DECL(wm8958_aif1dac2_ng_hold,
+ WM8958_AIF1_DAC2_NOISE_GATE,
+ WM8958_AIF1DAC2_NG_THR_SHIFT,
+ wm8958_ng_text);
+
+static SOC_ENUM_SINGLE_DECL(wm8958_aif2dac_ng_hold,
+ WM8958_AIF2_DAC_NOISE_GATE,
+ WM8958_AIF2DAC_NG_THR_SHIFT,
+ wm8958_ng_text);
+
+static const struct snd_kcontrol_new wm8958_snd_controls[] = {
+SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv),
+
+SOC_SINGLE("AIF1DAC1 Noise Gate Switch", WM8958_AIF1_DAC1_NOISE_GATE,
+ WM8958_AIF1DAC1_NG_ENA_SHIFT, 1, 0),
+SOC_ENUM("AIF1DAC1 Noise Gate Hold Time", wm8958_aif1dac1_ng_hold),
+SOC_SINGLE_TLV("AIF1DAC1 Noise Gate Threshold Volume",
+ WM8958_AIF1_DAC1_NOISE_GATE, WM8958_AIF1DAC1_NG_THR_SHIFT,
+ 7, 1, ng_tlv),
+
+SOC_SINGLE("AIF1DAC2 Noise Gate Switch", WM8958_AIF1_DAC2_NOISE_GATE,
+ WM8958_AIF1DAC2_NG_ENA_SHIFT, 1, 0),
+SOC_ENUM("AIF1DAC2 Noise Gate Hold Time", wm8958_aif1dac2_ng_hold),
+SOC_SINGLE_TLV("AIF1DAC2 Noise Gate Threshold Volume",
+ WM8958_AIF1_DAC2_NOISE_GATE, WM8958_AIF1DAC2_NG_THR_SHIFT,
+ 7, 1, ng_tlv),
+
+SOC_SINGLE("AIF2DAC Noise Gate Switch", WM8958_AIF2_DAC_NOISE_GATE,
+ WM8958_AIF2DAC_NG_ENA_SHIFT, 1, 0),
+SOC_ENUM("AIF2DAC Noise Gate Hold Time", wm8958_aif2dac_ng_hold),
+SOC_SINGLE_TLV("AIF2DAC Noise Gate Threshold Volume",
+ WM8958_AIF2_DAC_NOISE_GATE, WM8958_AIF2DAC_NG_THR_SHIFT,
+ 7, 1, ng_tlv),
+};
+
+static const struct snd_kcontrol_new wm1811_snd_controls[] = {
+SOC_SINGLE_TLV("MIXINL IN1LP Boost Volume", WM8994_INPUT_MIXER_1, 7, 1, 0,
+ mixin_boost_tlv),
+SOC_SINGLE_TLV("MIXINL IN1RP Boost Volume", WM8994_INPUT_MIXER_1, 8, 1, 0,
+ mixin_boost_tlv),
+};
+
+/* We run all mode setting through a function to enforce audio mode */
+static void wm1811_jackdet_set_mode(struct snd_soc_codec *codec, u16 mode)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (!wm8994->jackdet || !wm8994->micdet[0].jack)
+ return;
+
+ if (wm8994->active_refcount)
+ mode = WM1811_JACKDET_MODE_AUDIO;
+
+ if (mode == wm8994->jackdet_mode)
+ return;
+
+ wm8994->jackdet_mode = mode;
+
+ /* Always use audio mode to detect while the system is active */
+ if (mode != WM1811_JACKDET_MODE_NONE)
+ mode = WM1811_JACKDET_MODE_AUDIO;
+
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ WM1811_JACKDET_MODE_MASK, mode);
+}
+
+static void active_reference(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ mutex_lock(&wm8994->accdet_lock);
+
+ wm8994->active_refcount++;
+
+ dev_dbg(codec->dev, "Active refcount incremented, now %d\n",
+ wm8994->active_refcount);
+
+ /* If we're using jack detection go into audio mode */
+ wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_AUDIO);
+
+ mutex_unlock(&wm8994->accdet_lock);
+}
+
+static void active_dereference(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ u16 mode;
+
+ mutex_lock(&wm8994->accdet_lock);
+
+ wm8994->active_refcount--;
+
+ dev_dbg(codec->dev, "Active refcount decremented, now %d\n",
+ wm8994->active_refcount);
+
+ if (wm8994->active_refcount == 0) {
+ /* Go into appropriate detection only mode */
+ if (wm8994->jack_mic || wm8994->mic_detecting)
+ mode = WM1811_JACKDET_MODE_MIC;
+ else
+ mode = WM1811_JACKDET_MODE_JACK;
+
+ wm1811_jackdet_set_mode(codec, mode);
+ }
+
+ mutex_unlock(&wm8994->accdet_lock);
+}
+
+static int clk_sys_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return configure_clock(codec);
+
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * JACKDET won't run until we start the clock and it
+ * only reports deltas, make sure we notify the state
+ * up the stack on startup. Use a *very* generous
+ * timeout for paranoia, there's no urgency and we
+ * don't want false reports.
+ */
+ if (wm8994->jackdet && !wm8994->clk_has_run) {
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8994->jackdet_bootstrap,
+ msecs_to_jiffies(1000));
+ wm8994->clk_has_run = true;
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ configure_clock(codec);
+ break;
+ }
+
+ return 0;
+}
+
+static void vmid_reference(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ pm_runtime_get_sync(codec->dev);
+
+ wm8994->vmid_refcount++;
+
+ dev_dbg(codec->dev, "Referencing VMID, refcount is now %d\n",
+ wm8994->vmid_refcount);
+
+ if (wm8994->vmid_refcount == 1) {
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
+ WM8994_LINEOUT1_DISCH |
+ WM8994_LINEOUT2_DISCH, 0);
+
+ wm_hubs_vmid_ena(codec);
+
+ switch (wm8994->vmid_mode) {
+ default:
+ WARN_ON(NULL == "Invalid VMID mode");
+ case WM8994_VMID_NORMAL:
+ /* Startup bias, VMID ramp & buffer */
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ WM8994_BIAS_SRC |
+ WM8994_VMID_DISCH |
+ WM8994_STARTUP_BIAS_ENA |
+ WM8994_VMID_BUF_ENA |
+ WM8994_VMID_RAMP_MASK,
+ WM8994_BIAS_SRC |
+ WM8994_STARTUP_BIAS_ENA |
+ WM8994_VMID_BUF_ENA |
+ (0x2 << WM8994_VMID_RAMP_SHIFT));
+
+ /* Main bias enable, VMID=2x40k */
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+ WM8994_BIAS_ENA |
+ WM8994_VMID_SEL_MASK,
+ WM8994_BIAS_ENA | 0x2);
+
+ msleep(300);
+
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ WM8994_VMID_RAMP_MASK |
+ WM8994_BIAS_SRC,
+ 0);
+ break;
+
+ case WM8994_VMID_FORCE:
+ /* Startup bias, slow VMID ramp & buffer */
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ WM8994_BIAS_SRC |
+ WM8994_VMID_DISCH |
+ WM8994_STARTUP_BIAS_ENA |
+ WM8994_VMID_BUF_ENA |
+ WM8994_VMID_RAMP_MASK,
+ WM8994_BIAS_SRC |
+ WM8994_STARTUP_BIAS_ENA |
+ WM8994_VMID_BUF_ENA |
+ (0x2 << WM8994_VMID_RAMP_SHIFT));
+
+ /* Main bias enable, VMID=2x40k */
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+ WM8994_BIAS_ENA |
+ WM8994_VMID_SEL_MASK,
+ WM8994_BIAS_ENA | 0x2);
+
+ msleep(400);
+
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ WM8994_VMID_RAMP_MASK |
+ WM8994_BIAS_SRC,
+ 0);
+ break;
+ }
+ }
+}
+
+static void vmid_dereference(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ wm8994->vmid_refcount--;
+
+ dev_dbg(codec->dev, "Dereferencing VMID, refcount is now %d\n",
+ wm8994->vmid_refcount);
+
+ if (wm8994->vmid_refcount == 0) {
+ if (wm8994->hubs.lineout1_se)
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_3,
+ WM8994_LINEOUT1N_ENA |
+ WM8994_LINEOUT1P_ENA,
+ WM8994_LINEOUT1N_ENA |
+ WM8994_LINEOUT1P_ENA);
+
+ if (wm8994->hubs.lineout2_se)
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_3,
+ WM8994_LINEOUT2N_ENA |
+ WM8994_LINEOUT2P_ENA,
+ WM8994_LINEOUT2N_ENA |
+ WM8994_LINEOUT2P_ENA);
+
+ /* Start discharging VMID */
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ WM8994_BIAS_SRC |
+ WM8994_VMID_DISCH,
+ WM8994_BIAS_SRC |
+ WM8994_VMID_DISCH);
+
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+ WM8994_VMID_SEL_MASK, 0);
+
+ msleep(400);
+
+ /* Active discharge */
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
+ WM8994_LINEOUT1_DISCH |
+ WM8994_LINEOUT2_DISCH,
+ WM8994_LINEOUT1_DISCH |
+ WM8994_LINEOUT2_DISCH);
+
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_3,
+ WM8994_LINEOUT1N_ENA |
+ WM8994_LINEOUT1P_ENA |
+ WM8994_LINEOUT2N_ENA |
+ WM8994_LINEOUT2P_ENA, 0);
+
+ /* Switch off startup biases */
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ WM8994_BIAS_SRC |
+ WM8994_STARTUP_BIAS_ENA |
+ WM8994_VMID_BUF_ENA |
+ WM8994_VMID_RAMP_MASK, 0);
+
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+ WM8994_VMID_SEL_MASK, 0);
+ }
+
+ pm_runtime_put(codec->dev);
+}
+
+static int vmid_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:
+ vmid_reference(codec);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ vmid_dereference(codec);
+ break;
+ }
+
+ return 0;
+}
+
+static bool wm8994_check_class_w_digital(struct snd_soc_codec *codec)
+{
+ int source = 0; /* GCC flow analysis can't track enable */
+ int reg, reg_r;
+
+ /* We also need the same AIF source for L/R and only one path */
+ reg = snd_soc_read(codec, WM8994_DAC1_LEFT_MIXER_ROUTING);
+ switch (reg) {
+ case WM8994_AIF2DACL_TO_DAC1L:
+ dev_vdbg(codec->dev, "Class W source AIF2DAC\n");
+ source = 2 << WM8994_CP_DYN_SRC_SEL_SHIFT;
+ break;
+ case WM8994_AIF1DAC2L_TO_DAC1L:
+ dev_vdbg(codec->dev, "Class W source AIF1DAC2\n");
+ source = 1 << WM8994_CP_DYN_SRC_SEL_SHIFT;
+ break;
+ case WM8994_AIF1DAC1L_TO_DAC1L:
+ dev_vdbg(codec->dev, "Class W source AIF1DAC1\n");
+ source = 0 << WM8994_CP_DYN_SRC_SEL_SHIFT;
+ break;
+ default:
+ dev_vdbg(codec->dev, "DAC mixer setting: %x\n", reg);
+ return false;
+ }
+
+ reg_r = snd_soc_read(codec, WM8994_DAC1_RIGHT_MIXER_ROUTING);
+ if (reg_r != reg) {
+ dev_vdbg(codec->dev, "Left and right DAC mixers different\n");
+ return false;
+ }
+
+ /* Set the source up */
+ snd_soc_update_bits(codec, WM8994_CLASS_W_1,
+ WM8994_CP_DYN_SRC_SEL_MASK, source);
+
+ return true;
+}
+
+static int aif1clk_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 wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ int mask = WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA;
+ int i;
+ int dac;
+ int adc;
+ int val;
+
+ switch (control->type) {
+ case WM8994:
+ case WM8958:
+ mask |= WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA;
+ break;
+ default:
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Don't enable timeslot 2 if not in use */
+ if (wm8994->channels[0] <= 2)
+ mask &= ~(WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
+
+ val = snd_soc_read(codec, WM8994_AIF1_CONTROL_1);
+ if ((val & WM8994_AIF1ADCL_SRC) &&
+ (val & WM8994_AIF1ADCR_SRC))
+ adc = WM8994_AIF1ADC1R_ENA | WM8994_AIF1ADC2R_ENA;
+ else if (!(val & WM8994_AIF1ADCL_SRC) &&
+ !(val & WM8994_AIF1ADCR_SRC))
+ adc = WM8994_AIF1ADC1L_ENA | WM8994_AIF1ADC2L_ENA;
+ else
+ adc = WM8994_AIF1ADC1R_ENA | WM8994_AIF1ADC2R_ENA |
+ WM8994_AIF1ADC1L_ENA | WM8994_AIF1ADC2L_ENA;
+
+ val = snd_soc_read(codec, WM8994_AIF1_CONTROL_2);
+ if ((val & WM8994_AIF1DACL_SRC) &&
+ (val & WM8994_AIF1DACR_SRC))
+ dac = WM8994_AIF1DAC1R_ENA | WM8994_AIF1DAC2R_ENA;
+ else if (!(val & WM8994_AIF1DACL_SRC) &&
+ !(val & WM8994_AIF1DACR_SRC))
+ dac = WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC2L_ENA;
+ else
+ dac = WM8994_AIF1DAC1R_ENA | WM8994_AIF1DAC2R_ENA |
+ WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC2L_ENA;
+
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4,
+ mask, adc);
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ mask, dac);
+ snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ WM8994_AIF1DSPCLK_ENA |
+ WM8994_SYSDSPCLK_ENA,
+ WM8994_AIF1DSPCLK_ENA |
+ WM8994_SYSDSPCLK_ENA);
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4, mask,
+ WM8994_AIF1ADC1R_ENA |
+ WM8994_AIF1ADC1L_ENA |
+ WM8994_AIF1ADC2R_ENA |
+ WM8994_AIF1ADC2L_ENA);
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, mask,
+ WM8994_AIF1DAC1R_ENA |
+ WM8994_AIF1DAC1L_ENA |
+ WM8994_AIF1DAC2R_ENA |
+ WM8994_AIF1DAC2L_ENA);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ for (i = 0; i < ARRAY_SIZE(wm8994_vu_bits); i++)
+ snd_soc_write(codec, wm8994_vu_bits[i].reg,
+ snd_soc_read(codec,
+ wm8994_vu_bits[i].reg));
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ mask, 0);
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4,
+ mask, 0);
+
+ val = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (val & WM8994_AIF2DSPCLK_ENA)
+ val = WM8994_SYSDSPCLK_ENA;
+ else
+ val = 0;
+ snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ WM8994_SYSDSPCLK_ENA |
+ WM8994_AIF1DSPCLK_ENA, val);
+ break;
+ }
+
+ return 0;
+}
+
+static int aif2clk_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);
+ int i;
+ int dac;
+ int adc;
+ int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ val = snd_soc_read(codec, WM8994_AIF2_CONTROL_1);
+ if ((val & WM8994_AIF2ADCL_SRC) &&
+ (val & WM8994_AIF2ADCR_SRC))
+ adc = WM8994_AIF2ADCR_ENA;
+ else if (!(val & WM8994_AIF2ADCL_SRC) &&
+ !(val & WM8994_AIF2ADCR_SRC))
+ adc = WM8994_AIF2ADCL_ENA;
+ else
+ adc = WM8994_AIF2ADCL_ENA | WM8994_AIF2ADCR_ENA;
+
+
+ val = snd_soc_read(codec, WM8994_AIF2_CONTROL_2);
+ if ((val & WM8994_AIF2DACL_SRC) &&
+ (val & WM8994_AIF2DACR_SRC))
+ dac = WM8994_AIF2DACR_ENA;
+ else if (!(val & WM8994_AIF2DACL_SRC) &&
+ !(val & WM8994_AIF2DACR_SRC))
+ dac = WM8994_AIF2DACL_ENA;
+ else
+ dac = WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA;
+
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4,
+ WM8994_AIF2ADCL_ENA |
+ WM8994_AIF2ADCR_ENA, adc);
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ WM8994_AIF2DACL_ENA |
+ WM8994_AIF2DACR_ENA, dac);
+ snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ WM8994_AIF2DSPCLK_ENA |
+ WM8994_SYSDSPCLK_ENA,
+ WM8994_AIF2DSPCLK_ENA |
+ WM8994_SYSDSPCLK_ENA);
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4,
+ WM8994_AIF2ADCL_ENA |
+ WM8994_AIF2ADCR_ENA,
+ WM8994_AIF2ADCL_ENA |
+ WM8994_AIF2ADCR_ENA);
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ WM8994_AIF2DACL_ENA |
+ WM8994_AIF2DACR_ENA,
+ WM8994_AIF2DACL_ENA |
+ WM8994_AIF2DACR_ENA);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ for (i = 0; i < ARRAY_SIZE(wm8994_vu_bits); i++)
+ snd_soc_write(codec, wm8994_vu_bits[i].reg,
+ snd_soc_read(codec,
+ wm8994_vu_bits[i].reg));
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ WM8994_AIF2DACL_ENA |
+ WM8994_AIF2DACR_ENA, 0);
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4,
+ WM8994_AIF2ADCL_ENA |
+ WM8994_AIF2ADCR_ENA, 0);
+
+ val = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (val & WM8994_AIF1DSPCLK_ENA)
+ val = WM8994_SYSDSPCLK_ENA;
+ else
+ val = 0;
+ snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ WM8994_SYSDSPCLK_ENA |
+ WM8994_AIF2DSPCLK_ENA, val);
+ break;
+ }
+
+ return 0;
+}
+
+static int aif1clk_late_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 wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wm8994->aif1clk_enable = 1;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wm8994->aif1clk_disable = 1;
+ break;
+ }
+
+ return 0;
+}
+
+static int aif2clk_late_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wm8994->aif2clk_enable = 1;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wm8994->aif2clk_disable = 1;
+ break;
+ }
+
+ return 0;
+}
+
+static int late_enable_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (wm8994->aif1clk_enable) {
+ aif1clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMU);
+ snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
+ WM8994_AIF1CLK_ENA_MASK,
+ WM8994_AIF1CLK_ENA);
+ aif1clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMU);
+ wm8994->aif1clk_enable = 0;
+ }
+ if (wm8994->aif2clk_enable) {
+ aif2clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMU);
+ snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
+ WM8994_AIF2CLK_ENA_MASK,
+ WM8994_AIF2CLK_ENA);
+ aif2clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMU);
+ wm8994->aif2clk_enable = 0;
+ }
+ break;
+ }
+
+ /* We may also have postponed startup of DSP, handle that. */
+ wm8958_aif_ev(w, kcontrol, event);
+
+ return 0;
+}
+
+static int late_disable_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ if (wm8994->aif1clk_disable) {
+ aif1clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMD);
+ snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
+ WM8994_AIF1CLK_ENA_MASK, 0);
+ aif1clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMD);
+ wm8994->aif1clk_disable = 0;
+ }
+ if (wm8994->aif2clk_disable) {
+ aif2clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMD);
+ snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
+ WM8994_AIF2CLK_ENA_MASK, 0);
+ aif2clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMD);
+ wm8994->aif2clk_disable = 0;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int adc_mux_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ late_enable_ev(w, kcontrol, event);
+ return 0;
+}
+
+static int micbias_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ late_enable_ev(w, kcontrol, event);
+ return 0;
+}
+
+static int dac_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ unsigned int mask = 1 << w->shift;
+
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ mask, mask);
+ return 0;
+}
+
+static const char *adc_mux_text[] = {
+ "ADC",
+ "DMIC",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(adc_enum, adc_mux_text);
+
+static const struct snd_kcontrol_new adcl_mux =
+ SOC_DAPM_ENUM("ADCL Mux", adc_enum);
+
+static const struct snd_kcontrol_new adcr_mux =
+ SOC_DAPM_ENUM("ADCR Mux", adc_enum);
+
+static const struct snd_kcontrol_new left_speaker_mixer[] = {
+SOC_DAPM_SINGLE("DAC2 Switch", WM8994_SPEAKER_MIXER, 9, 1, 0),
+SOC_DAPM_SINGLE("Input Switch", WM8994_SPEAKER_MIXER, 7, 1, 0),
+SOC_DAPM_SINGLE("IN1LP Switch", WM8994_SPEAKER_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8994_SPEAKER_MIXER, 3, 1, 0),
+SOC_DAPM_SINGLE("DAC1 Switch", WM8994_SPEAKER_MIXER, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_mixer[] = {
+SOC_DAPM_SINGLE("DAC2 Switch", WM8994_SPEAKER_MIXER, 8, 1, 0),
+SOC_DAPM_SINGLE("Input Switch", WM8994_SPEAKER_MIXER, 6, 1, 0),
+SOC_DAPM_SINGLE("IN1RP Switch", WM8994_SPEAKER_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8994_SPEAKER_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("DAC1 Switch", WM8994_SPEAKER_MIXER, 0, 1, 0),
+};
+
+/* Debugging; dump chip status after DAPM transitions */
+static int post_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ dev_dbg(codec->dev, "SRC status: %x\n",
+ snd_soc_read(codec,
+ WM8994_RATE_STATUS));
+ return 0;
+}
+
+static const struct snd_kcontrol_new aif1adc1l_mix[] = {
+SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8994_AIF1_ADC1_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC1_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif1adc1r_mix[] = {
+SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8994_AIF1_ADC1_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC1_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif1adc2l_mix[] = {
+SOC_DAPM_SINGLE("DMIC Switch", WM8994_AIF1_ADC2_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC2_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif1adc2r_mix[] = {
+SOC_DAPM_SINGLE("DMIC Switch", WM8994_AIF1_ADC2_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC2_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif2dac2l_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+ 2, 1, 0),
+SOC_DAPM_SINGLE("AIF1.2 Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif2dac2r_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+ 2, 1, 0),
+SOC_DAPM_SINGLE("AIF1.2 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+#define WM8994_CLASS_W_SWITCH(xname, reg, shift, max, invert) \
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, wm8994_put_class_w)
+
+static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ int ret;
+
+ ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ wm_hubs_update_class_w(codec);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new dac1l_mix[] = {
+WM8994_CLASS_W_SWITCH("Right Sidetone Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+WM8994_CLASS_W_SWITCH("Left Sidetone Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF2 Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+ 2, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF1.2 Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF1.1 Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1r_mix[] = {
+WM8994_CLASS_W_SWITCH("Right Sidetone Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+WM8994_CLASS_W_SWITCH("Left Sidetone Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF2 Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+ 2, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF1.2 Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF1.1 Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const char *sidetone_text[] = {
+ "ADC/DMIC1", "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_DECL(sidetone1_enum,
+ WM8994_SIDETONE, 0, sidetone_text);
+
+static const struct snd_kcontrol_new sidetone1_mux =
+ SOC_DAPM_ENUM("Left Sidetone Mux", sidetone1_enum);
+
+static SOC_ENUM_SINGLE_DECL(sidetone2_enum,
+ WM8994_SIDETONE, 1, sidetone_text);
+
+static const struct snd_kcontrol_new sidetone2_mux =
+ SOC_DAPM_ENUM("Right Sidetone Mux", sidetone2_enum);
+
+static const char *aif1dac_text[] = {
+ "AIF1DACDAT", "AIF3DACDAT",
+};
+
+static const char *loopback_text[] = {
+ "None", "ADCDAT",
+};
+
+static SOC_ENUM_SINGLE_DECL(aif1_loopback_enum,
+ WM8994_AIF1_CONTROL_2,
+ WM8994_AIF1_LOOPBACK_SHIFT,
+ loopback_text);
+
+static const struct snd_kcontrol_new aif1_loopback =
+ SOC_DAPM_ENUM("AIF1 Loopback", aif1_loopback_enum);
+
+static SOC_ENUM_SINGLE_DECL(aif2_loopback_enum,
+ WM8994_AIF2_CONTROL_2,
+ WM8994_AIF2_LOOPBACK_SHIFT,
+ loopback_text);
+
+static const struct snd_kcontrol_new aif2_loopback =
+ SOC_DAPM_ENUM("AIF2 Loopback", aif2_loopback_enum);
+
+static SOC_ENUM_SINGLE_DECL(aif1dac_enum,
+ WM8994_POWER_MANAGEMENT_6, 0, aif1dac_text);
+
+static const struct snd_kcontrol_new aif1dac_mux =
+ SOC_DAPM_ENUM("AIF1DAC Mux", aif1dac_enum);
+
+static const char *aif2dac_text[] = {
+ "AIF2DACDAT", "AIF3DACDAT",
+};
+
+static SOC_ENUM_SINGLE_DECL(aif2dac_enum,
+ WM8994_POWER_MANAGEMENT_6, 1, aif2dac_text);
+
+static const struct snd_kcontrol_new aif2dac_mux =
+ SOC_DAPM_ENUM("AIF2DAC Mux", aif2dac_enum);
+
+static const char *aif2adc_text[] = {
+ "AIF2ADCDAT", "AIF3DACDAT",
+};
+
+static SOC_ENUM_SINGLE_DECL(aif2adc_enum,
+ WM8994_POWER_MANAGEMENT_6, 2, aif2adc_text);
+
+static const struct snd_kcontrol_new aif2adc_mux =
+ SOC_DAPM_ENUM("AIF2ADC Mux", aif2adc_enum);
+
+static const char *aif3adc_text[] = {
+ "AIF1ADCDAT", "AIF2ADCDAT", "AIF2DACDAT", "Mono PCM",
+};
+
+static SOC_ENUM_SINGLE_DECL(wm8994_aif3adc_enum,
+ WM8994_POWER_MANAGEMENT_6, 3, aif3adc_text);
+
+static const struct snd_kcontrol_new wm8994_aif3adc_mux =
+ SOC_DAPM_ENUM("AIF3ADC Mux", wm8994_aif3adc_enum);
+
+static SOC_ENUM_SINGLE_DECL(wm8958_aif3adc_enum,
+ WM8994_POWER_MANAGEMENT_6, 3, aif3adc_text);
+
+static const struct snd_kcontrol_new wm8958_aif3adc_mux =
+ SOC_DAPM_ENUM("AIF3ADC Mux", wm8958_aif3adc_enum);
+
+static const char *mono_pcm_out_text[] = {
+ "None", "AIF2ADCL", "AIF2ADCR",
+};
+
+static SOC_ENUM_SINGLE_DECL(mono_pcm_out_enum,
+ WM8994_POWER_MANAGEMENT_6, 9, mono_pcm_out_text);
+
+static const struct snd_kcontrol_new mono_pcm_out_mux =
+ SOC_DAPM_ENUM("Mono PCM Out Mux", mono_pcm_out_enum);
+
+static const char *aif2dac_src_text[] = {
+ "AIF2", "AIF3",
+};
+
+/* Note that these two control shouldn't be simultaneously switched to AIF3 */
+static SOC_ENUM_SINGLE_DECL(aif2dacl_src_enum,
+ WM8994_POWER_MANAGEMENT_6, 7, aif2dac_src_text);
+
+static const struct snd_kcontrol_new aif2dacl_src_mux =
+ SOC_DAPM_ENUM("AIF2DACL Mux", aif2dacl_src_enum);
+
+static SOC_ENUM_SINGLE_DECL(aif2dacr_src_enum,
+ WM8994_POWER_MANAGEMENT_6, 8, aif2dac_src_text);
+
+static const struct snd_kcontrol_new aif2dacr_src_mux =
+ SOC_DAPM_ENUM("AIF2DACR Mux", aif2dacr_src_enum);
+
+static const struct snd_soc_dapm_widget wm8994_lateclk_revd_widgets[] = {
+SND_SOC_DAPM_SUPPLY("AIF1CLK", SND_SOC_NOPM, 0, 0, aif1clk_late_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("AIF2CLK", SND_SOC_NOPM, 0, 0, aif2clk_late_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_PGA_E("Late DAC1L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("Late DAC1R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("Late DAC2L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("Late DAC2R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0,
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+
+SND_SOC_DAPM_MIXER_E("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
+ left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer),
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_MIXER_E("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
+ right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer),
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux,
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux,
+ late_enable_ev, SND_SOC_DAPM_PRE_PMU),
+
+SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev)
+};
+
+static const struct snd_soc_dapm_widget wm8994_lateclk_widgets[] = {
+SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, aif1clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, aif2clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
+ left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
+SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
+ right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux),
+};
+
+static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = {
+SND_SOC_DAPM_DAC_E("DAC2L", NULL, SND_SOC_NOPM, 3, 0,
+ dac_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_DAC_E("DAC2R", NULL, SND_SOC_NOPM, 2, 0,
+ dac_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_DAC_E("DAC1L", NULL, SND_SOC_NOPM, 1, 0,
+ dac_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_DAC_E("DAC1R", NULL, SND_SOC_NOPM, 0, 0,
+ dac_ev, SND_SOC_DAPM_PRE_PMU),
+};
+
+static const struct snd_soc_dapm_widget wm8994_dac_widgets[] = {
+SND_SOC_DAPM_DAC("DAC2L", NULL, WM8994_POWER_MANAGEMENT_5, 3, 0),
+SND_SOC_DAPM_DAC("DAC2R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0),
+SND_SOC_DAPM_DAC("DAC1L", NULL, WM8994_POWER_MANAGEMENT_5, 1, 0),
+SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8994_adc_revd_widgets[] = {
+SND_SOC_DAPM_MUX_E("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux,
+ adc_mux_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_MUX_E("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux,
+ adc_mux_ev, SND_SOC_DAPM_PRE_PMU),
+};
+
+static const struct snd_soc_dapm_widget wm8994_adc_widgets[] = {
+SND_SOC_DAPM_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux),
+SND_SOC_DAPM_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux),
+};
+
+static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("DMIC1DAT"),
+SND_SOC_DAPM_INPUT("DMIC2DAT"),
+SND_SOC_DAPM_INPUT("Clock"),
+
+SND_SOC_DAPM_SUPPLY_S("MICBIAS Supply", 1, SND_SOC_NOPM, 0, 0, micbias_ev,
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_SUPPLY("VMID", SND_SOC_NOPM, 0, 0, vmid_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM, 3, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSP2CLK", SND_SOC_NOPM, 2, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSPINTCLK", SND_SOC_NOPM, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", NULL,
+ 0, SND_SOC_NOPM, 9, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", NULL,
+ 0, SND_SOC_NOPM, 8, 0),
+SND_SOC_DAPM_AIF_IN_E("AIF1DAC1L", NULL, 0,
+ SND_SOC_NOPM, 9, 0, wm8958_aif_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_AIF_IN_E("AIF1DAC1R", NULL, 0,
+ SND_SOC_NOPM, 8, 0, wm8958_aif_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", NULL,
+ 0, SND_SOC_NOPM, 11, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", NULL,
+ 0, SND_SOC_NOPM, 10, 0),
+SND_SOC_DAPM_AIF_IN_E("AIF1DAC2L", NULL, 0,
+ SND_SOC_NOPM, 11, 0, wm8958_aif_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_AIF_IN_E("AIF1DAC2R", NULL, 0,
+ SND_SOC_NOPM, 10, 0, wm8958_aif_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_MIXER("AIF1ADC1L Mixer", SND_SOC_NOPM, 0, 0,
+ aif1adc1l_mix, ARRAY_SIZE(aif1adc1l_mix)),
+SND_SOC_DAPM_MIXER("AIF1ADC1R Mixer", SND_SOC_NOPM, 0, 0,
+ aif1adc1r_mix, ARRAY_SIZE(aif1adc1r_mix)),
+
+SND_SOC_DAPM_MIXER("AIF1ADC2L Mixer", SND_SOC_NOPM, 0, 0,
+ aif1adc2l_mix, ARRAY_SIZE(aif1adc2l_mix)),
+SND_SOC_DAPM_MIXER("AIF1ADC2R Mixer", SND_SOC_NOPM, 0, 0,
+ aif1adc2r_mix, ARRAY_SIZE(aif1adc2r_mix)),
+
+SND_SOC_DAPM_MIXER("AIF2DAC2L Mixer", SND_SOC_NOPM, 0, 0,
+ aif2dac2l_mix, ARRAY_SIZE(aif2dac2l_mix)),
+SND_SOC_DAPM_MIXER("AIF2DAC2R Mixer", SND_SOC_NOPM, 0, 0,
+ aif2dac2r_mix, ARRAY_SIZE(aif2dac2r_mix)),
+
+SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &sidetone1_mux),
+SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &sidetone2_mux),
+
+SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0,
+ dac1l_mix, ARRAY_SIZE(dac1l_mix)),
+SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0,
+ dac1r_mix, ARRAY_SIZE(dac1r_mix)),
+
+SND_SOC_DAPM_AIF_OUT("AIF2ADCL", NULL, 0,
+ SND_SOC_NOPM, 13, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2ADCR", NULL, 0,
+ SND_SOC_NOPM, 12, 0),
+SND_SOC_DAPM_AIF_IN_E("AIF2DACL", NULL, 0,
+ SND_SOC_NOPM, 13, 0, wm8958_aif_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_AIF_IN_E("AIF2DACR", NULL, 0,
+ SND_SOC_NOPM, 12, 0, wm8958_aif_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_AIF_IN("AIF1DACDAT", NULL, 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIF2DACDAT", NULL, 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1ADCDAT", NULL, 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2ADCDAT", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("AIF1DAC Mux", SND_SOC_NOPM, 0, 0, &aif1dac_mux),
+SND_SOC_DAPM_MUX("AIF2DAC Mux", SND_SOC_NOPM, 0, 0, &aif2dac_mux),
+SND_SOC_DAPM_MUX("AIF2ADC Mux", SND_SOC_NOPM, 0, 0, &aif2adc_mux),
+
+SND_SOC_DAPM_AIF_IN("AIF3DACDAT", NULL, 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3ADCDAT", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_SUPPLY("TOCLK", WM8994_CLOCKING_1, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8994_POWER_MANAGEMENT_4, 5, 0),
+SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8994_POWER_MANAGEMENT_4, 4, 0),
+SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8994_POWER_MANAGEMENT_4, 3, 0),
+SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0),
+
+/* Power is done with the muxes since the ADC power also controls the
+ * downsampling chain, the chip will automatically manage the analogue
+ * specific portions.
+ */
+SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("AIF1 Loopback", SND_SOC_NOPM, 0, 0, &aif1_loopback),
+SND_SOC_DAPM_MUX("AIF2 Loopback", SND_SOC_NOPM, 0, 0, &aif2_loopback),
+
+SND_SOC_DAPM_POST("Debug log", post_ev),
+};
+
+static const struct snd_soc_dapm_widget wm8994_specific_dapm_widgets[] = {
+SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8994_aif3adc_mux),
+};
+
+static const struct snd_soc_dapm_widget wm8958_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("AIF3", WM8994_POWER_MANAGEMENT_6, 5, 1, NULL, 0),
+SND_SOC_DAPM_MUX("Mono PCM Out Mux", SND_SOC_NOPM, 0, 0, &mono_pcm_out_mux),
+SND_SOC_DAPM_MUX("AIF2DACL Mux", SND_SOC_NOPM, 0, 0, &aif2dacl_src_mux),
+SND_SOC_DAPM_MUX("AIF2DACR Mux", SND_SOC_NOPM, 0, 0, &aif2dacr_src_mux),
+SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8958_aif3adc_mux),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ { "CLK_SYS", NULL, "AIF1CLK", check_clk_sys },
+ { "CLK_SYS", NULL, "AIF2CLK", check_clk_sys },
+
+ { "DSP1CLK", NULL, "CLK_SYS" },
+ { "DSP2CLK", NULL, "CLK_SYS" },
+ { "DSPINTCLK", NULL, "CLK_SYS" },
+
+ { "AIF1ADC1L", NULL, "AIF1CLK" },
+ { "AIF1ADC1L", NULL, "DSP1CLK" },
+ { "AIF1ADC1R", NULL, "AIF1CLK" },
+ { "AIF1ADC1R", NULL, "DSP1CLK" },
+ { "AIF1ADC1R", NULL, "DSPINTCLK" },
+
+ { "AIF1DAC1L", NULL, "AIF1CLK" },
+ { "AIF1DAC1L", NULL, "DSP1CLK" },
+ { "AIF1DAC1R", NULL, "AIF1CLK" },
+ { "AIF1DAC1R", NULL, "DSP1CLK" },
+ { "AIF1DAC1R", NULL, "DSPINTCLK" },
+
+ { "AIF1ADC2L", NULL, "AIF1CLK" },
+ { "AIF1ADC2L", NULL, "DSP1CLK" },
+ { "AIF1ADC2R", NULL, "AIF1CLK" },
+ { "AIF1ADC2R", NULL, "DSP1CLK" },
+ { "AIF1ADC2R", NULL, "DSPINTCLK" },
+
+ { "AIF1DAC2L", NULL, "AIF1CLK" },
+ { "AIF1DAC2L", NULL, "DSP1CLK" },
+ { "AIF1DAC2R", NULL, "AIF1CLK" },
+ { "AIF1DAC2R", NULL, "DSP1CLK" },
+ { "AIF1DAC2R", NULL, "DSPINTCLK" },
+
+ { "AIF2ADCL", NULL, "AIF2CLK" },
+ { "AIF2ADCL", NULL, "DSP2CLK" },
+ { "AIF2ADCR", NULL, "AIF2CLK" },
+ { "AIF2ADCR", NULL, "DSP2CLK" },
+ { "AIF2ADCR", NULL, "DSPINTCLK" },
+
+ { "AIF2DACL", NULL, "AIF2CLK" },
+ { "AIF2DACL", NULL, "DSP2CLK" },
+ { "AIF2DACR", NULL, "AIF2CLK" },
+ { "AIF2DACR", NULL, "DSP2CLK" },
+ { "AIF2DACR", NULL, "DSPINTCLK" },
+
+ { "DMIC1L", NULL, "DMIC1DAT" },
+ { "DMIC1L", NULL, "CLK_SYS" },
+ { "DMIC1R", NULL, "DMIC1DAT" },
+ { "DMIC1R", NULL, "CLK_SYS" },
+ { "DMIC2L", NULL, "DMIC2DAT" },
+ { "DMIC2L", NULL, "CLK_SYS" },
+ { "DMIC2R", NULL, "DMIC2DAT" },
+ { "DMIC2R", NULL, "CLK_SYS" },
+
+ { "ADCL", NULL, "AIF1CLK" },
+ { "ADCL", NULL, "DSP1CLK" },
+ { "ADCL", NULL, "DSPINTCLK" },
+
+ { "ADCR", NULL, "AIF1CLK" },
+ { "ADCR", NULL, "DSP1CLK" },
+ { "ADCR", NULL, "DSPINTCLK" },
+
+ { "ADCL Mux", "ADC", "ADCL" },
+ { "ADCL Mux", "DMIC", "DMIC1L" },
+ { "ADCR Mux", "ADC", "ADCR" },
+ { "ADCR Mux", "DMIC", "DMIC1R" },
+
+ { "DAC1L", NULL, "AIF1CLK" },
+ { "DAC1L", NULL, "DSP1CLK" },
+ { "DAC1L", NULL, "DSPINTCLK" },
+
+ { "DAC1R", NULL, "AIF1CLK" },
+ { "DAC1R", NULL, "DSP1CLK" },
+ { "DAC1R", NULL, "DSPINTCLK" },
+
+ { "DAC2L", NULL, "AIF2CLK" },
+ { "DAC2L", NULL, "DSP2CLK" },
+ { "DAC2L", NULL, "DSPINTCLK" },
+
+ { "DAC2R", NULL, "AIF2DACR" },
+ { "DAC2R", NULL, "AIF2CLK" },
+ { "DAC2R", NULL, "DSP2CLK" },
+ { "DAC2R", NULL, "DSPINTCLK" },
+
+ { "TOCLK", NULL, "CLK_SYS" },
+
+ { "AIF1DACDAT", NULL, "AIF1 Playback" },
+ { "AIF2DACDAT", NULL, "AIF2 Playback" },
+ { "AIF3DACDAT", NULL, "AIF3 Playback" },
+
+ { "AIF1 Capture", NULL, "AIF1ADCDAT" },
+ { "AIF2 Capture", NULL, "AIF2ADCDAT" },
+ { "AIF3 Capture", NULL, "AIF3ADCDAT" },
+
+ /* AIF1 outputs */
+ { "AIF1ADC1L", NULL, "AIF1ADC1L Mixer" },
+ { "AIF1ADC1L Mixer", "ADC/DMIC Switch", "ADCL Mux" },
+ { "AIF1ADC1L Mixer", "AIF2 Switch", "AIF2DACL" },
+
+ { "AIF1ADC1R", NULL, "AIF1ADC1R Mixer" },
+ { "AIF1ADC1R Mixer", "ADC/DMIC Switch", "ADCR Mux" },
+ { "AIF1ADC1R Mixer", "AIF2 Switch", "AIF2DACR" },
+
+ { "AIF1ADC2L", NULL, "AIF1ADC2L Mixer" },
+ { "AIF1ADC2L Mixer", "DMIC Switch", "DMIC2L" },
+ { "AIF1ADC2L Mixer", "AIF2 Switch", "AIF2DACL" },
+
+ { "AIF1ADC2R", NULL, "AIF1ADC2R Mixer" },
+ { "AIF1ADC2R Mixer", "DMIC Switch", "DMIC2R" },
+ { "AIF1ADC2R Mixer", "AIF2 Switch", "AIF2DACR" },
+
+ /* Pin level routing for AIF3 */
+ { "AIF1DAC1L", NULL, "AIF1DAC Mux" },
+ { "AIF1DAC1R", NULL, "AIF1DAC Mux" },
+ { "AIF1DAC2L", NULL, "AIF1DAC Mux" },
+ { "AIF1DAC2R", NULL, "AIF1DAC Mux" },
+
+ { "AIF1DAC Mux", "AIF1DACDAT", "AIF1 Loopback" },
+ { "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
+ { "AIF2DAC Mux", "AIF2DACDAT", "AIF2 Loopback" },
+ { "AIF2DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
+ { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCL" },
+ { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCR" },
+ { "AIF2ADC Mux", "AIF3DACDAT", "AIF3ADCDAT" },
+
+ /* DAC1 inputs */
+ { "DAC1L Mixer", "AIF2 Switch", "AIF2DACL" },
+ { "DAC1L Mixer", "AIF1.2 Switch", "AIF1DAC2L" },
+ { "DAC1L Mixer", "AIF1.1 Switch", "AIF1DAC1L" },
+ { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+ { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+ { "DAC1R Mixer", "AIF2 Switch", "AIF2DACR" },
+ { "DAC1R Mixer", "AIF1.2 Switch", "AIF1DAC2R" },
+ { "DAC1R Mixer", "AIF1.1 Switch", "AIF1DAC1R" },
+ { "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+ { "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+ /* DAC2/AIF2 outputs */
+ { "AIF2ADCL", NULL, "AIF2DAC2L Mixer" },
+ { "AIF2DAC2L Mixer", "AIF2 Switch", "AIF2DACL" },
+ { "AIF2DAC2L Mixer", "AIF1.2 Switch", "AIF1DAC2L" },
+ { "AIF2DAC2L Mixer", "AIF1.1 Switch", "AIF1DAC1L" },
+ { "AIF2DAC2L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+ { "AIF2DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+ { "AIF2ADCR", NULL, "AIF2DAC2R Mixer" },
+ { "AIF2DAC2R Mixer", "AIF2 Switch", "AIF2DACR" },
+ { "AIF2DAC2R Mixer", "AIF1.2 Switch", "AIF1DAC2R" },
+ { "AIF2DAC2R Mixer", "AIF1.1 Switch", "AIF1DAC1R" },
+ { "AIF2DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+ { "AIF2DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+ { "AIF1ADCDAT", NULL, "AIF1ADC1L" },
+ { "AIF1ADCDAT", NULL, "AIF1ADC1R" },
+ { "AIF1ADCDAT", NULL, "AIF1ADC2L" },
+ { "AIF1ADCDAT", NULL, "AIF1ADC2R" },
+
+ { "AIF2ADCDAT", NULL, "AIF2ADC Mux" },
+
+ /* AIF3 output */
+ { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC1L" },
+ { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC1R" },
+ { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC2L" },
+ { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC2R" },
+ { "AIF3ADCDAT", "AIF2ADCDAT", "AIF2ADCL" },
+ { "AIF3ADCDAT", "AIF2ADCDAT", "AIF2ADCR" },
+ { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACL" },
+ { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACR" },
+
+ /* Loopback */
+ { "AIF1 Loopback", "ADCDAT", "AIF1ADCDAT" },
+ { "AIF1 Loopback", "None", "AIF1DACDAT" },
+ { "AIF2 Loopback", "ADCDAT", "AIF2ADCDAT" },
+ { "AIF2 Loopback", "None", "AIF2DACDAT" },
+
+ /* Sidetone */
+ { "Left Sidetone", "ADC/DMIC1", "ADCL Mux" },
+ { "Left Sidetone", "DMIC2", "DMIC2L" },
+ { "Right Sidetone", "ADC/DMIC1", "ADCR Mux" },
+ { "Right Sidetone", "DMIC2", "DMIC2R" },
+
+ /* Output stages */
+ { "Left Output Mixer", "DAC Switch", "DAC1L" },
+ { "Right Output Mixer", "DAC Switch", "DAC1R" },
+
+ { "SPKL", "DAC1 Switch", "DAC1L" },
+ { "SPKL", "DAC2 Switch", "DAC2L" },
+
+ { "SPKR", "DAC1 Switch", "DAC1R" },
+ { "SPKR", "DAC2 Switch", "DAC2R" },
+
+ { "Left Headphone Mux", "DAC", "DAC1L" },
+ { "Right Headphone Mux", "DAC", "DAC1R" },
+};
+
+static const struct snd_soc_dapm_route wm8994_lateclk_revd_intercon[] = {
+ { "DAC1L", NULL, "Late DAC1L Enable PGA" },
+ { "Late DAC1L Enable PGA", NULL, "DAC1L Mixer" },
+ { "DAC1R", NULL, "Late DAC1R Enable PGA" },
+ { "Late DAC1R Enable PGA", NULL, "DAC1R Mixer" },
+ { "DAC2L", NULL, "Late DAC2L Enable PGA" },
+ { "Late DAC2L Enable PGA", NULL, "AIF2DAC2L Mixer" },
+ { "DAC2R", NULL, "Late DAC2R Enable PGA" },
+ { "Late DAC2R Enable PGA", NULL, "AIF2DAC2R Mixer" }
+};
+
+static const struct snd_soc_dapm_route wm8994_lateclk_intercon[] = {
+ { "DAC1L", NULL, "DAC1L Mixer" },
+ { "DAC1R", NULL, "DAC1R Mixer" },
+ { "DAC2L", NULL, "AIF2DAC2L Mixer" },
+ { "DAC2R", NULL, "AIF2DAC2R Mixer" },
+};
+
+static const struct snd_soc_dapm_route wm8994_revd_intercon[] = {
+ { "AIF1DACDAT", NULL, "AIF2DACDAT" },
+ { "AIF2DACDAT", NULL, "AIF1DACDAT" },
+ { "AIF1ADCDAT", NULL, "AIF2ADCDAT" },
+ { "AIF2ADCDAT", NULL, "AIF1ADCDAT" },
+ { "MICBIAS1", NULL, "CLK_SYS" },
+ { "MICBIAS1", NULL, "MICBIAS Supply" },
+ { "MICBIAS2", NULL, "CLK_SYS" },
+ { "MICBIAS2", NULL, "MICBIAS Supply" },
+};
+
+static const struct snd_soc_dapm_route wm8994_intercon[] = {
+ { "AIF2DACL", NULL, "AIF2DAC Mux" },
+ { "AIF2DACR", NULL, "AIF2DAC Mux" },
+ { "MICBIAS1", NULL, "VMID" },
+ { "MICBIAS2", NULL, "VMID" },
+};
+
+static const struct snd_soc_dapm_route wm8958_intercon[] = {
+ { "AIF2DACL", NULL, "AIF2DACL Mux" },
+ { "AIF2DACR", NULL, "AIF2DACR Mux" },
+
+ { "AIF2DACL Mux", "AIF2", "AIF2DAC Mux" },
+ { "AIF2DACL Mux", "AIF3", "AIF3DACDAT" },
+ { "AIF2DACR Mux", "AIF2", "AIF2DAC Mux" },
+ { "AIF2DACR Mux", "AIF3", "AIF3DACDAT" },
+
+ { "AIF3DACDAT", NULL, "AIF3" },
+ { "AIF3ADCDAT", NULL, "AIF3" },
+
+ { "Mono PCM Out Mux", "AIF2ADCL", "AIF2ADCL" },
+ { "Mono PCM Out Mux", "AIF2ADCR", "AIF2ADCR" },
+
+ { "AIF3ADC Mux", "Mono PCM", "Mono PCM Out Mux" },
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+struct fll_div {
+ u16 outdiv;
+ u16 n;
+ u16 k;
+ u16 lambda;
+ u16 clk_ref_div;
+ u16 fll_fratio;
+};
+
+static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll,
+ int freq_in, int freq_out)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod, gcd_fll;
+
+ pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out);
+
+ /* Scale the input frequency down to <= 13.5MHz */
+ fll->clk_ref_div = 0;
+ while (freq_in > 13500000) {
+ fll->clk_ref_div++;
+ freq_in /= 2;
+
+ if (fll->clk_ref_div > 3)
+ return -EINVAL;
+ }
+ pr_debug("CLK_REF_DIV=%d, Fref=%dHz\n", fll->clk_ref_div, freq_in);
+
+ /* Scale the output to give 90MHz<=Fvco<=100MHz */
+ fll->outdiv = 3;
+ while (freq_out * (fll->outdiv + 1) < 90000000) {
+ fll->outdiv++;
+ if (fll->outdiv > 63)
+ return -EINVAL;
+ }
+ freq_out *= fll->outdiv + 1;
+ pr_debug("OUTDIV=%d, Fvco=%dHz\n", fll->outdiv, freq_out);
+
+ if (freq_in > 1000000) {
+ fll->fll_fratio = 0;
+ } else if (freq_in > 256000) {
+ fll->fll_fratio = 1;
+ freq_in *= 2;
+ } else if (freq_in > 128000) {
+ fll->fll_fratio = 2;
+ freq_in *= 4;
+ } else if (freq_in > 64000) {
+ fll->fll_fratio = 3;
+ freq_in *= 8;
+ } else {
+ fll->fll_fratio = 4;
+ freq_in *= 16;
+ }
+ pr_debug("FLL_FRATIO=%d, Fref=%dHz\n", fll->fll_fratio, freq_in);
+
+ /* Now, calculate N.K */
+ Ndiv = freq_out / freq_in;
+
+ fll->n = Ndiv;
+ Nmod = freq_out % freq_in;
+ pr_debug("Nmod=%d\n", Nmod);
+
+ switch (control->type) {
+ case WM8994:
+ /* Calculate fractional part - scale up so we can round. */
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, freq_in);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ fll->k = K / 10;
+ fll->lambda = 0;
+
+ pr_debug("N=%x K=%x\n", fll->n, fll->k);
+ break;
+
+ default:
+ gcd_fll = gcd(freq_out, freq_in);
+
+ fll->k = (freq_out - (freq_in * fll->n)) / gcd_fll;
+ fll->lambda = freq_in / gcd_fll;
+
+ }
+
+ return 0;
+}
+
+static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ int reg_offset, ret;
+ struct fll_div fll;
+ u16 reg, clk1, aif_reg, aif_src;
+ unsigned long timeout;
+ bool was_enabled;
+
+ switch (id) {
+ case WM8994_FLL1:
+ reg_offset = 0;
+ id = 0;
+ aif_src = 0x10;
+ break;
+ case WM8994_FLL2:
+ reg_offset = 0x20;
+ id = 1;
+ aif_src = 0x18;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ reg = snd_soc_read(codec, WM8994_FLL1_CONTROL_1 + reg_offset);
+ was_enabled = reg & WM8994_FLL1_ENA;
+
+ switch (src) {
+ case 0:
+ /* Allow no source specification when stopping */
+ if (freq_out)
+ return -EINVAL;
+ src = wm8994->fll[id].src;
+ break;
+ case WM8994_FLL_SRC_MCLK1:
+ case WM8994_FLL_SRC_MCLK2:
+ case WM8994_FLL_SRC_LRCLK:
+ case WM8994_FLL_SRC_BCLK:
+ break;
+ case WM8994_FLL_SRC_INTERNAL:
+ freq_in = 12000000;
+ freq_out = 12000000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Are we changing anything? */
+ if (wm8994->fll[id].src == src &&
+ wm8994->fll[id].in == freq_in && wm8994->fll[id].out == freq_out)
+ return 0;
+
+ /* If we're stopping the FLL redo the old config - no
+ * registers will actually be written but we avoid GCC flow
+ * analysis bugs spewing warnings.
+ */
+ if (freq_out)
+ ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out);
+ else
+ ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in,
+ wm8994->fll[id].out);
+ if (ret < 0)
+ return ret;
+
+ /* Make sure that we're not providing SYSCLK right now */
+ clk1 = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (clk1 & WM8994_SYSCLK_SRC)
+ aif_reg = WM8994_AIF2_CLOCKING_1;
+ else
+ aif_reg = WM8994_AIF1_CLOCKING_1;
+ reg = snd_soc_read(codec, aif_reg);
+
+ if ((reg & WM8994_AIF1CLK_ENA) &&
+ (reg & WM8994_AIF1CLK_SRC_MASK) == aif_src) {
+ dev_err(codec->dev, "FLL%d is currently providing SYSCLK\n",
+ id + 1);
+ return -EBUSY;
+ }
+
+ /* We always need to disable the FLL while reconfiguring */
+ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
+ WM8994_FLL1_ENA, 0);
+
+ if (wm8994->fll_byp && src == WM8994_FLL_SRC_BCLK &&
+ freq_in == freq_out && freq_out) {
+ dev_dbg(codec->dev, "Bypassing FLL%d\n", id + 1);
+ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
+ WM8958_FLL1_BYP, WM8958_FLL1_BYP);
+ goto out;
+ }
+
+ reg = (fll.outdiv << WM8994_FLL1_OUTDIV_SHIFT) |
+ (fll.fll_fratio << WM8994_FLL1_FRATIO_SHIFT);
+ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_2 + reg_offset,
+ WM8994_FLL1_OUTDIV_MASK |
+ WM8994_FLL1_FRATIO_MASK, reg);
+
+ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_3 + reg_offset,
+ WM8994_FLL1_K_MASK, fll.k);
+
+ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_4 + reg_offset,
+ WM8994_FLL1_N_MASK,
+ fll.n << WM8994_FLL1_N_SHIFT);
+
+ if (fll.lambda) {
+ snd_soc_update_bits(codec, WM8958_FLL1_EFS_1 + reg_offset,
+ WM8958_FLL1_LAMBDA_MASK,
+ fll.lambda);
+ snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
+ WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA);
+ } else {
+ snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
+ WM8958_FLL1_EFS_ENA, 0);
+ }
+
+ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
+ WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP |
+ WM8994_FLL1_REFCLK_DIV_MASK |
+ WM8994_FLL1_REFCLK_SRC_MASK,
+ ((src == WM8994_FLL_SRC_INTERNAL)
+ << WM8994_FLL1_FRC_NCO_SHIFT) |
+ (fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT) |
+ (src - 1));
+
+ /* Clear any pending completion from a previous failure */
+ try_wait_for_completion(&wm8994->fll_locked[id]);
+
+ /* Enable (with fractional mode if required) */
+ if (freq_out) {
+ /* Enable VMID if we need it */
+ if (!was_enabled) {
+ active_reference(codec);
+
+ switch (control->type) {
+ case WM8994:
+ vmid_reference(codec);
+ break;
+ case WM8958:
+ if (control->revision < 1)
+ vmid_reference(codec);
+ break;
+ default:
+ break;
+ }
+ }
+
+ reg = WM8994_FLL1_ENA;
+
+ if (fll.k)
+ reg |= WM8994_FLL1_FRAC;
+ if (src == WM8994_FLL_SRC_INTERNAL)
+ reg |= WM8994_FLL1_OSC_ENA;
+
+ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
+ WM8994_FLL1_ENA | WM8994_FLL1_OSC_ENA |
+ WM8994_FLL1_FRAC, reg);
+
+ if (wm8994->fll_locked_irq) {
+ timeout = wait_for_completion_timeout(&wm8994->fll_locked[id],
+ msecs_to_jiffies(10));
+ if (timeout == 0)
+ dev_warn(codec->dev,
+ "Timed out waiting for FLL lock\n");
+ } else {
+ msleep(5);
+ }
+ } else {
+ if (was_enabled) {
+ switch (control->type) {
+ case WM8994:
+ vmid_dereference(codec);
+ break;
+ case WM8958:
+ if (control->revision < 1)
+ vmid_dereference(codec);
+ break;
+ default:
+ break;
+ }
+
+ active_dereference(codec);
+ }
+ }
+
+out:
+ wm8994->fll[id].in = freq_in;
+ wm8994->fll[id].out = freq_out;
+ wm8994->fll[id].src = src;
+
+ configure_clock(codec);
+
+ /*
+ * If SYSCLK will be less than 50kHz adjust AIFnCLK dividers
+ * for detection.
+ */
+ if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) {
+ dev_dbg(codec->dev, "Configuring AIFs for 128fs\n");
+
+ wm8994->aifdiv[0] = snd_soc_read(codec, WM8994_AIF1_RATE)
+ & WM8994_AIF1CLK_RATE_MASK;
+ wm8994->aifdiv[1] = snd_soc_read(codec, WM8994_AIF2_RATE)
+ & WM8994_AIF1CLK_RATE_MASK;
+
+ snd_soc_update_bits(codec, WM8994_AIF1_RATE,
+ WM8994_AIF1CLK_RATE_MASK, 0x1);
+ snd_soc_update_bits(codec, WM8994_AIF2_RATE,
+ WM8994_AIF2CLK_RATE_MASK, 0x1);
+ } else if (wm8994->aifdiv[0]) {
+ snd_soc_update_bits(codec, WM8994_AIF1_RATE,
+ WM8994_AIF1CLK_RATE_MASK,
+ wm8994->aifdiv[0]);
+ snd_soc_update_bits(codec, WM8994_AIF2_RATE,
+ WM8994_AIF2CLK_RATE_MASK,
+ wm8994->aifdiv[1]);
+
+ wm8994->aifdiv[0] = 0;
+ wm8994->aifdiv[1] = 0;
+ }
+
+ return 0;
+}
+
+static irqreturn_t wm8994_fll_locked_irq(int irq, void *data)
+{
+ struct completion *completion = data;
+
+ complete(completion);
+
+ return IRQ_HANDLED;
+}
+
+static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 };
+
+static int wm8994_set_fll(struct snd_soc_dai *dai, int id, int src,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ return _wm8994_set_fll(dai->codec, id, src, freq_in, freq_out);
+}
+
+static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ switch (dai->id) {
+ case 1:
+ case 2:
+ break;
+
+ default:
+ /* AIF3 shares clocking with AIF1/2 */
+ return -EINVAL;
+ }
+
+ switch (clk_id) {
+ case WM8994_SYSCLK_MCLK1:
+ wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK1;
+ wm8994->mclk[0] = freq;
+ dev_dbg(dai->dev, "AIF%d using MCLK1 at %uHz\n",
+ dai->id, freq);
+ break;
+
+ case WM8994_SYSCLK_MCLK2:
+ /* TODO: Set GPIO AF */
+ wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK2;
+ wm8994->mclk[1] = freq;
+ dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n",
+ dai->id, freq);
+ break;
+
+ case WM8994_SYSCLK_FLL1:
+ wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_FLL1;
+ dev_dbg(dai->dev, "AIF%d using FLL1\n", dai->id);
+ break;
+
+ case WM8994_SYSCLK_FLL2:
+ wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_FLL2;
+ dev_dbg(dai->dev, "AIF%d using FLL2\n", dai->id);
+ break;
+
+ case WM8994_SYSCLK_OPCLK:
+ /* Special case - a division (times 10) is given and
+ * no effect on main clocking.
+ */
+ if (freq) {
+ for (i = 0; i < ARRAY_SIZE(opclk_divs); i++)
+ if (opclk_divs[i] == freq)
+ break;
+ if (i == ARRAY_SIZE(opclk_divs))
+ return -EINVAL;
+ snd_soc_update_bits(codec, WM8994_CLOCKING_2,
+ WM8994_OPCLK_DIV_MASK, i);
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_2,
+ WM8994_OPCLK_ENA, WM8994_OPCLK_ENA);
+ } else {
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_2,
+ WM8994_OPCLK_ENA, 0);
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ configure_clock(codec);
+
+ /*
+ * If SYSCLK will be less than 50kHz adjust AIFnCLK dividers
+ * for detection.
+ */
+ if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) {
+ dev_dbg(codec->dev, "Configuring AIFs for 128fs\n");
+
+ wm8994->aifdiv[0] = snd_soc_read(codec, WM8994_AIF1_RATE)
+ & WM8994_AIF1CLK_RATE_MASK;
+ wm8994->aifdiv[1] = snd_soc_read(codec, WM8994_AIF2_RATE)
+ & WM8994_AIF1CLK_RATE_MASK;
+
+ snd_soc_update_bits(codec, WM8994_AIF1_RATE,
+ WM8994_AIF1CLK_RATE_MASK, 0x1);
+ snd_soc_update_bits(codec, WM8994_AIF2_RATE,
+ WM8994_AIF2CLK_RATE_MASK, 0x1);
+ } else if (wm8994->aifdiv[0]) {
+ snd_soc_update_bits(codec, WM8994_AIF1_RATE,
+ WM8994_AIF1CLK_RATE_MASK,
+ wm8994->aifdiv[0]);
+ snd_soc_update_bits(codec, WM8994_AIF2_RATE,
+ WM8994_AIF2CLK_RATE_MASK,
+ wm8994->aifdiv[1]);
+
+ wm8994->aifdiv[0] = 0;
+ wm8994->aifdiv[1] = 0;
+ }
+
+ return 0;
+}
+
+static int wm8994_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+
+ wm_hubs_set_bias_level(codec, level);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* MICBIAS into regulating mode */
+ switch (control->type) {
+ case WM8958:
+ case WM1811:
+ snd_soc_update_bits(codec, WM8958_MICBIAS1,
+ WM8958_MICB1_MODE, 0);
+ snd_soc_update_bits(codec, WM8958_MICBIAS2,
+ WM8958_MICB2_MODE, 0);
+ break;
+ default:
+ break;
+ }
+
+ if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
+ active_reference(codec);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ switch (control->type) {
+ case WM8958:
+ if (control->revision == 0) {
+ /* Optimise performance for rev A */
+ snd_soc_update_bits(codec,
+ WM8958_CHARGE_PUMP_2,
+ WM8958_CP_DISCH,
+ WM8958_CP_DISCH);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* Discharge LINEOUT1 & 2 */
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
+ WM8994_LINEOUT1_DISCH |
+ WM8994_LINEOUT2_DISCH,
+ WM8994_LINEOUT1_DISCH |
+ WM8994_LINEOUT2_DISCH);
+ }
+
+ if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE)
+ active_dereference(codec);
+
+ /* MICBIAS into bypass mode on newer devices */
+ switch (control->type) {
+ case WM8958:
+ case WM1811:
+ snd_soc_update_bits(codec, WM8958_MICBIAS1,
+ WM8958_MICB1_MODE,
+ WM8958_MICB1_MODE);
+ snd_soc_update_bits(codec, WM8958_MICBIAS2,
+ WM8958_MICB2_MODE,
+ WM8958_MICB2_MODE);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
+ wm8994->cur_fw = NULL;
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ switch (mode) {
+ case WM8994_VMID_NORMAL:
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (wm8994->hubs.lineout1_se) {
+ snd_soc_dapm_disable_pin_unlocked(dapm,
+ "LINEOUT1N Driver");
+ snd_soc_dapm_disable_pin_unlocked(dapm,
+ "LINEOUT1P Driver");
+ }
+ if (wm8994->hubs.lineout2_se) {
+ snd_soc_dapm_disable_pin_unlocked(dapm,
+ "LINEOUT2N Driver");
+ snd_soc_dapm_disable_pin_unlocked(dapm,
+ "LINEOUT2P Driver");
+ }
+
+ /* Do the sync with the old mode to allow it to clean up */
+ snd_soc_dapm_sync_unlocked(dapm);
+ wm8994->vmid_mode = mode;
+
+ snd_soc_dapm_mutex_unlock(dapm);
+ break;
+
+ case WM8994_VMID_FORCE:
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (wm8994->hubs.lineout1_se) {
+ snd_soc_dapm_force_enable_pin_unlocked(dapm,
+ "LINEOUT1N Driver");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm,
+ "LINEOUT1P Driver");
+ }
+ if (wm8994->hubs.lineout2_se) {
+ snd_soc_dapm_force_enable_pin_unlocked(dapm,
+ "LINEOUT2N Driver");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm,
+ "LINEOUT2P Driver");
+ }
+
+ wm8994->vmid_mode = mode;
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ int ms_reg;
+ int aif1_reg;
+ int dac_reg;
+ int adc_reg;
+ int ms = 0;
+ int aif1 = 0;
+ int lrclk = 0;
+
+ switch (dai->id) {
+ case 1:
+ ms_reg = WM8994_AIF1_MASTER_SLAVE;
+ aif1_reg = WM8994_AIF1_CONTROL_1;
+ dac_reg = WM8994_AIF1DAC_LRCLK;
+ adc_reg = WM8994_AIF1ADC_LRCLK;
+ break;
+ case 2:
+ ms_reg = WM8994_AIF2_MASTER_SLAVE;
+ aif1_reg = WM8994_AIF2_CONTROL_1;
+ dac_reg = WM8994_AIF1DAC_LRCLK;
+ adc_reg = WM8994_AIF1ADC_LRCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ms = WM8994_AIF1_MSTR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_B:
+ aif1 |= WM8994_AIF1_LRCLK_INV;
+ lrclk |= WM8958_AIF1_LRCLK_INV;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif1 |= 0x18;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aif1 |= 0x10;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif1 |= 0x8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8994_AIF1_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif1 |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV;
+ lrclk |= WM8958_AIF1_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8994_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif1 |= WM8994_AIF1_LRCLK_INV;
+ lrclk |= WM8958_AIF1_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* The AIF2 format configuration needs to be mirrored to AIF3
+ * on WM8958 if it's in use so just do it all the time. */
+ switch (control->type) {
+ case WM1811:
+ case WM8958:
+ if (dai->id == 2)
+ snd_soc_update_bits(codec, WM8958_AIF3_CONTROL_1,
+ WM8994_AIF1_LRCLK_INV |
+ WM8958_AIF3_FMT_MASK, aif1);
+ break;
+
+ default:
+ break;
+ }
+
+ snd_soc_update_bits(codec, aif1_reg,
+ WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV |
+ WM8994_AIF1_FMT_MASK,
+ aif1);
+ snd_soc_update_bits(codec, ms_reg, WM8994_AIF1_MSTR,
+ ms);
+ snd_soc_update_bits(codec, dac_reg,
+ WM8958_AIF1_LRCLK_INV, lrclk);
+ snd_soc_update_bits(codec, adc_reg,
+ WM8958_AIF1_LRCLK_INV, lrclk);
+
+ return 0;
+}
+
+static struct {
+ int val, rate;
+} srs[] = {
+ { 0, 8000 },
+ { 1, 11025 },
+ { 2, 12000 },
+ { 3, 16000 },
+ { 4, 22050 },
+ { 5, 24000 },
+ { 6, 32000 },
+ { 7, 44100 },
+ { 8, 48000 },
+ { 9, 88200 },
+ { 10, 96000 },
+};
+
+static int fs_ratios[] = {
+ 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536
+};
+
+static int bclk_divs[] = {
+ 10, 15, 20, 30, 40, 50, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480,
+ 640, 880, 960, 1280, 1760, 1920
+};
+
+static int wm8994_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ struct wm8994_pdata *pdata = &control->pdata;
+ int aif1_reg;
+ int aif2_reg;
+ int bclk_reg;
+ int lrclk_reg;
+ int rate_reg;
+ int aif1 = 0;
+ int aif2 = 0;
+ int bclk = 0;
+ int lrclk = 0;
+ int rate_val = 0;
+ int id = dai->id - 1;
+
+ int i, cur_val, best_val, bclk_rate, best;
+
+ switch (dai->id) {
+ case 1:
+ aif1_reg = WM8994_AIF1_CONTROL_1;
+ aif2_reg = WM8994_AIF1_CONTROL_2;
+ bclk_reg = WM8994_AIF1_BCLK;
+ rate_reg = WM8994_AIF1_RATE;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ wm8994->lrclk_shared[0]) {
+ lrclk_reg = WM8994_AIF1DAC_LRCLK;
+ } else {
+ lrclk_reg = WM8994_AIF1ADC_LRCLK;
+ dev_dbg(codec->dev, "AIF1 using split LRCLK\n");
+ }
+ break;
+ case 2:
+ aif1_reg = WM8994_AIF2_CONTROL_1;
+ aif2_reg = WM8994_AIF2_CONTROL_2;
+ bclk_reg = WM8994_AIF2_BCLK;
+ rate_reg = WM8994_AIF2_RATE;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ wm8994->lrclk_shared[1]) {
+ lrclk_reg = WM8994_AIF2DAC_LRCLK;
+ } else {
+ lrclk_reg = WM8994_AIF2ADC_LRCLK;
+ dev_dbg(codec->dev, "AIF2 using split LRCLK\n");
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ bclk_rate = params_rate(params);
+ switch (params_width(params)) {
+ case 16:
+ bclk_rate *= 16;
+ break;
+ case 20:
+ bclk_rate *= 20;
+ aif1 |= 0x20;
+ break;
+ case 24:
+ bclk_rate *= 24;
+ aif1 |= 0x40;
+ break;
+ case 32:
+ bclk_rate *= 32;
+ aif1 |= 0x60;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8994->channels[id] = params_channels(params);
+ if (pdata->max_channels_clocked[id] &&
+ wm8994->channels[id] > pdata->max_channels_clocked[id]) {
+ dev_dbg(dai->dev, "Constraining channels to %d from %d\n",
+ pdata->max_channels_clocked[id], wm8994->channels[id]);
+ wm8994->channels[id] = pdata->max_channels_clocked[id];
+ }
+
+ switch (wm8994->channels[id]) {
+ case 1:
+ case 2:
+ bclk_rate *= 2;
+ break;
+ default:
+ bclk_rate *= 4;
+ break;
+ }
+
+ /* Try to find an appropriate sample rate; look for an exact match. */
+ for (i = 0; i < ARRAY_SIZE(srs); i++)
+ if (srs[i].rate == params_rate(params))
+ break;
+ if (i == ARRAY_SIZE(srs))
+ return -EINVAL;
+ rate_val |= srs[i].val << WM8994_AIF1_SR_SHIFT;
+
+ dev_dbg(dai->dev, "Sample rate is %dHz\n", srs[i].rate);
+ dev_dbg(dai->dev, "AIF%dCLK is %dHz, target BCLK %dHz\n",
+ dai->id, wm8994->aifclk[id], bclk_rate);
+
+ if (wm8994->channels[id] == 1 &&
+ (snd_soc_read(codec, aif1_reg) & 0x18) == 0x18)
+ aif2 |= WM8994_AIF1_MONO;
+
+ if (wm8994->aifclk[id] == 0) {
+ dev_err(dai->dev, "AIF%dCLK not configured\n", dai->id);
+ return -EINVAL;
+ }
+
+ /* AIFCLK/fs ratio; look for a close match in either direction */
+ best = 0;
+ best_val = abs((fs_ratios[0] * params_rate(params))
+ - wm8994->aifclk[id]);
+ for (i = 1; i < ARRAY_SIZE(fs_ratios); i++) {
+ cur_val = abs((fs_ratios[i] * params_rate(params))
+ - wm8994->aifclk[id]);
+ if (cur_val >= best_val)
+ continue;
+ best = i;
+ best_val = cur_val;
+ }
+ dev_dbg(dai->dev, "Selected AIF%dCLK/fs = %d\n",
+ dai->id, fs_ratios[best]);
+ rate_val |= best;
+
+ /* We may not get quite the right frequency if using
+ * approximate clocks so look for the closest match that is
+ * higher than the target (we need to ensure that there enough
+ * BCLKs to clock out the samples).
+ */
+ best = 0;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ cur_val = (wm8994->aifclk[id] * 10 / bclk_divs[i]) - bclk_rate;
+ if (cur_val < 0) /* BCLK table is sorted */
+ break;
+ best = i;
+ }
+ bclk_rate = wm8994->aifclk[id] * 10 / bclk_divs[best];
+ dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
+ bclk_divs[best], bclk_rate);
+ bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT;
+
+ lrclk = bclk_rate / params_rate(params);
+ if (!lrclk) {
+ dev_err(dai->dev, "Unable to generate LRCLK from %dHz BCLK\n",
+ bclk_rate);
+ return -EINVAL;
+ }
+ dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
+ lrclk, bclk_rate / lrclk);
+
+ snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
+ snd_soc_update_bits(codec, aif2_reg, WM8994_AIF1_MONO, aif2);
+ snd_soc_update_bits(codec, bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk);
+ snd_soc_update_bits(codec, lrclk_reg, WM8994_AIF1DAC_RATE_MASK,
+ lrclk);
+ snd_soc_update_bits(codec, rate_reg, WM8994_AIF1_SR_MASK |
+ WM8994_AIF1CLK_RATE_MASK, rate_val);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->id) {
+ case 1:
+ wm8994->dac_rates[0] = params_rate(params);
+ wm8994_set_retune_mobile(codec, 0);
+ wm8994_set_retune_mobile(codec, 1);
+ break;
+ case 2:
+ wm8994->dac_rates[1] = params_rate(params);
+ wm8994_set_retune_mobile(codec, 2);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ int aif1_reg;
+ int aif1 = 0;
+
+ switch (dai->id) {
+ case 3:
+ switch (control->type) {
+ case WM1811:
+ case WM8958:
+ aif1_reg = WM8958_AIF3_CONTROL_1;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ aif1 |= 0x20;
+ break;
+ case 24:
+ aif1 |= 0x40;
+ break;
+ case 32:
+ aif1 |= 0x60;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
+}
+
+static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int mute_reg;
+ int reg;
+
+ switch (codec_dai->id) {
+ case 1:
+ mute_reg = WM8994_AIF1_DAC1_FILTERS_1;
+ break;
+ case 2:
+ mute_reg = WM8994_AIF2_DAC_FILTERS_1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (mute)
+ reg = WM8994_AIF1DAC1_MUTE;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, mute_reg, WM8994_AIF1DAC1_MUTE, reg);
+
+ return 0;
+}
+
+static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int reg, val, mask;
+
+ switch (codec_dai->id) {
+ case 1:
+ reg = WM8994_AIF1_MASTER_SLAVE;
+ mask = WM8994_AIF1_TRI;
+ break;
+ case 2:
+ reg = WM8994_AIF2_MASTER_SLAVE;
+ mask = WM8994_AIF2_TRI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (tristate)
+ val = mask;
+ else
+ val = 0;
+
+ return snd_soc_update_bits(codec, reg, mask, val);
+}
+
+static int wm8994_aif2_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ /* Disable the pulls on the AIF if we're using it to save power. */
+ snd_soc_update_bits(codec, WM8994_GPIO_3,
+ WM8994_GPN_PU | WM8994_GPN_PD, 0);
+ snd_soc_update_bits(codec, WM8994_GPIO_4,
+ WM8994_GPN_PU | WM8994_GPN_PD, 0);
+ snd_soc_update_bits(codec, WM8994_GPIO_5,
+ WM8994_GPN_PU | WM8994_GPN_PD, 0);
+
+ return 0;
+}
+
+#define WM8994_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8994_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8994_aif1_dai_ops = {
+ .set_sysclk = wm8994_set_dai_sysclk,
+ .set_fmt = wm8994_set_dai_fmt,
+ .hw_params = wm8994_hw_params,
+ .digital_mute = wm8994_aif_mute,
+ .set_pll = wm8994_set_fll,
+ .set_tristate = wm8994_set_tristate,
+};
+
+static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
+ .set_sysclk = wm8994_set_dai_sysclk,
+ .set_fmt = wm8994_set_dai_fmt,
+ .hw_params = wm8994_hw_params,
+ .digital_mute = wm8994_aif_mute,
+ .set_pll = wm8994_set_fll,
+ .set_tristate = wm8994_set_tristate,
+};
+
+static const struct snd_soc_dai_ops wm8994_aif3_dai_ops = {
+ .hw_params = wm8994_aif3_hw_params,
+};
+
+static struct snd_soc_dai_driver wm8994_dai[] = {
+ {
+ .name = "wm8994-aif1",
+ .id = 1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8994_RATES,
+ .formats = WM8994_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8994_RATES,
+ .formats = WM8994_FORMATS,
+ .sig_bits = 24,
+ },
+ .ops = &wm8994_aif1_dai_ops,
+ },
+ {
+ .name = "wm8994-aif2",
+ .id = 2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8994_RATES,
+ .formats = WM8994_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8994_RATES,
+ .formats = WM8994_FORMATS,
+ .sig_bits = 24,
+ },
+ .probe = wm8994_aif2_probe,
+ .ops = &wm8994_aif2_dai_ops,
+ },
+ {
+ .name = "wm8994-aif3",
+ .id = 3,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8994_RATES,
+ .formats = WM8994_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8994_RATES,
+ .formats = WM8994_FORMATS,
+ .sig_bits = 24,
+ },
+ .ops = &wm8994_aif3_dai_ops,
+ }
+};
+
+#ifdef CONFIG_PM
+static int wm8994_codec_suspend(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
+ memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i],
+ sizeof(struct wm8994_fll_config));
+ ret = _wm8994_set_fll(codec, i + 1, 0, 0, 0);
+ if (ret < 0)
+ dev_warn(codec->dev, "Failed to stop FLL%d: %d\n",
+ i + 1, ret);
+ }
+
+ wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int wm8994_codec_resume(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
+ if (!wm8994->fll_suspend[i].out)
+ continue;
+
+ ret = _wm8994_set_fll(codec, i + 1,
+ wm8994->fll_suspend[i].src,
+ wm8994->fll_suspend[i].in,
+ wm8994->fll_suspend[i].out);
+ if (ret < 0)
+ dev_warn(codec->dev, "Failed to restore FLL%d: %d\n",
+ i + 1, ret);
+ }
+
+ return 0;
+}
+#else
+#define wm8994_codec_suspend NULL
+#define wm8994_codec_resume NULL
+#endif
+
+static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994)
+{
+ struct snd_soc_codec *codec = wm8994->hubs.codec;
+ struct wm8994 *control = wm8994->wm8994;
+ struct wm8994_pdata *pdata = &control->pdata;
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("AIF1.1 EQ Mode",
+ wm8994->retune_mobile_enum,
+ wm8994_get_retune_mobile_enum,
+ wm8994_put_retune_mobile_enum),
+ SOC_ENUM_EXT("AIF1.2 EQ Mode",
+ wm8994->retune_mobile_enum,
+ wm8994_get_retune_mobile_enum,
+ wm8994_put_retune_mobile_enum),
+ SOC_ENUM_EXT("AIF2 EQ Mode",
+ wm8994->retune_mobile_enum,
+ wm8994_get_retune_mobile_enum,
+ wm8994_put_retune_mobile_enum),
+ };
+ int ret, i, j;
+ const char **t;
+
+ /* We need an array of texts for the enum API but the number
+ * of texts is likely to be less than the number of
+ * configurations due to the sample rate dependency of the
+ * configurations. */
+ wm8994->num_retune_mobile_texts = 0;
+ wm8994->retune_mobile_texts = NULL;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ for (j = 0; j < wm8994->num_retune_mobile_texts; j++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8994->retune_mobile_texts[j]) == 0)
+ break;
+ }
+
+ if (j != wm8994->num_retune_mobile_texts)
+ continue;
+
+ /* Expand the array... */
+ t = krealloc(wm8994->retune_mobile_texts,
+ sizeof(char *) *
+ (wm8994->num_retune_mobile_texts + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* ...store the new entry... */
+ t[wm8994->num_retune_mobile_texts] =
+ pdata->retune_mobile_cfgs[i].name;
+
+ /* ...and remember the new version. */
+ wm8994->num_retune_mobile_texts++;
+ wm8994->retune_mobile_texts = t;
+ }
+
+ dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+ wm8994->num_retune_mobile_texts);
+
+ wm8994->retune_mobile_enum.items = wm8994->num_retune_mobile_texts;
+ wm8994->retune_mobile_enum.texts = wm8994->retune_mobile_texts;
+
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec, controls,
+ ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(wm8994->hubs.codec->dev,
+ "Failed to add ReTune Mobile controls: %d\n", ret);
+}
+
+static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
+{
+ struct snd_soc_codec *codec = wm8994->hubs.codec;
+ struct wm8994 *control = wm8994->wm8994;
+ struct wm8994_pdata *pdata = &control->pdata;
+ int ret, i;
+
+ if (!pdata)
+ return;
+
+ wm_hubs_handle_analogue_pdata(codec, pdata->lineout1_diff,
+ pdata->lineout2_diff,
+ pdata->lineout1fb,
+ pdata->lineout2fb,
+ pdata->jd_scthr,
+ pdata->jd_thr,
+ pdata->micb1_delay,
+ pdata->micb2_delay,
+ pdata->micbias1_lvl,
+ pdata->micbias2_lvl);
+
+ dev_dbg(codec->dev, "%d DRC configurations\n", pdata->num_drc_cfgs);
+
+ if (pdata->num_drc_cfgs) {
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("AIF1DRC1 Mode", wm8994->drc_enum,
+ wm8994_get_drc_enum, wm8994_put_drc_enum),
+ SOC_ENUM_EXT("AIF1DRC2 Mode", wm8994->drc_enum,
+ wm8994_get_drc_enum, wm8994_put_drc_enum),
+ SOC_ENUM_EXT("AIF2DRC Mode", wm8994->drc_enum,
+ wm8994_get_drc_enum, wm8994_put_drc_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->drc_texts = devm_kzalloc(wm8994->hubs.codec->dev,
+ sizeof(char *) * pdata->num_drc_cfgs, GFP_KERNEL);
+ if (!wm8994->drc_texts)
+ return;
+
+ for (i = 0; i < pdata->num_drc_cfgs; i++)
+ wm8994->drc_texts[i] = pdata->drc_cfgs[i].name;
+
+ wm8994->drc_enum.items = pdata->num_drc_cfgs;
+ wm8994->drc_enum.texts = wm8994->drc_texts;
+
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec, controls,
+ ARRAY_SIZE(controls));
+ for (i = 0; i < WM8994_NUM_DRC; i++)
+ wm8994_set_drc(codec, i);
+ } else {
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+ wm8994_drc_controls,
+ ARRAY_SIZE(wm8994_drc_controls));
+ }
+
+ if (ret != 0)
+ dev_err(wm8994->hubs.codec->dev,
+ "Failed to add DRC mode controls: %d\n", ret);
+
+
+ dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
+ pdata->num_retune_mobile_cfgs);
+
+ if (pdata->num_retune_mobile_cfgs)
+ wm8994_handle_retune_mobile_pdata(wm8994);
+ else
+ snd_soc_add_codec_controls(wm8994->hubs.codec, wm8994_eq_controls,
+ ARRAY_SIZE(wm8994_eq_controls));
+
+ for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) {
+ if (pdata->micbias[i]) {
+ snd_soc_write(codec, WM8958_MICBIAS1 + i,
+ pdata->micbias[i] & 0xffff);
+ }
+ }
+}
+
+/**
+ * wm8994_mic_detect - Enable microphone detection via the WM8994 IRQ
+ *
+ * @codec: WM8994 codec
+ * @jack: jack to report detection events on
+ * @micbias: microphone bias to detect on
+ *
+ * Enable microphone detection via IRQ on the WM8994. If GPIOs are
+ * being used to bring out signals to the processor then only platform
+ * data configuration is needed for WM8994 and processor GPIOs should
+ * be configured using snd_soc_jack_add_gpios() instead.
+ *
+ * Configuration of detection levels is available via the micbias1_lvl
+ * and micbias2_lvl platform data members.
+ */
+int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ int micbias)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_micdet *micdet;
+ struct wm8994 *control = wm8994->wm8994;
+ int reg, ret;
+
+ if (control->type != WM8994) {
+ dev_warn(codec->dev, "Not a WM8994\n");
+ return -EINVAL;
+ }
+
+ switch (micbias) {
+ case 1:
+ micdet = &wm8994->micdet[0];
+ if (jack)
+ ret = snd_soc_dapm_force_enable_pin(&codec->dapm,
+ "MICBIAS1");
+ else
+ ret = snd_soc_dapm_disable_pin(&codec->dapm,
+ "MICBIAS1");
+ break;
+ case 2:
+ micdet = &wm8994->micdet[1];
+ if (jack)
+ ret = snd_soc_dapm_force_enable_pin(&codec->dapm,
+ "MICBIAS1");
+ else
+ ret = snd_soc_dapm_disable_pin(&codec->dapm,
+ "MICBIAS1");
+ break;
+ default:
+ dev_warn(codec->dev, "Invalid MICBIAS %d\n", micbias);
+ return -EINVAL;
+ }
+
+ if (ret != 0)
+ dev_warn(codec->dev, "Failed to configure MICBIAS%d: %d\n",
+ micbias, ret);
+
+ dev_dbg(codec->dev, "Configuring microphone detection on %d %p\n",
+ micbias, jack);
+
+ /* Store the configuration */
+ micdet->jack = jack;
+ micdet->detecting = true;
+
+ /* If either of the jacks is set up then enable detection */
+ if (wm8994->micdet[0].jack || wm8994->micdet[1].jack)
+ reg = WM8994_MICD_ENA;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, reg);
+
+ /* enable MICDET and MICSHRT deboune */
+ snd_soc_update_bits(codec, WM8994_IRQ_DEBOUNCE,
+ WM8994_MIC1_DET_DB_MASK | WM8994_MIC1_SHRT_DB_MASK |
+ WM8994_MIC2_DET_DB_MASK | WM8994_MIC2_SHRT_DB_MASK,
+ WM8994_MIC1_DET_DB | WM8994_MIC1_SHRT_DB);
+
+ snd_soc_dapm_sync(&codec->dapm);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8994_mic_detect);
+
+static void wm8994_mic_work(struct work_struct *work)
+{
+ struct wm8994_priv *priv = container_of(work,
+ struct wm8994_priv,
+ mic_work.work);
+ struct regmap *regmap = priv->wm8994->regmap;
+ struct device *dev = priv->wm8994->dev;
+ unsigned int reg;
+ int ret;
+ int report;
+
+ pm_runtime_get_sync(dev);
+
+ ret = regmap_read(regmap, WM8994_INTERRUPT_RAW_STATUS_2, &reg);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read microphone status: %d\n",
+ ret);
+ pm_runtime_put(dev);
+ return;
+ }
+
+ dev_dbg(dev, "Microphone status: %x\n", reg);
+
+ report = 0;
+ if (reg & WM8994_MIC1_DET_STS) {
+ if (priv->micdet[0].detecting)
+ report = SND_JACK_HEADSET;
+ }
+ if (reg & WM8994_MIC1_SHRT_STS) {
+ if (priv->micdet[0].detecting)
+ report = SND_JACK_HEADPHONE;
+ else
+ report |= SND_JACK_BTN_0;
+ }
+ if (report)
+ priv->micdet[0].detecting = false;
+ else
+ priv->micdet[0].detecting = true;
+
+ snd_soc_jack_report(priv->micdet[0].jack, report,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+
+ report = 0;
+ if (reg & WM8994_MIC2_DET_STS) {
+ if (priv->micdet[1].detecting)
+ report = SND_JACK_HEADSET;
+ }
+ if (reg & WM8994_MIC2_SHRT_STS) {
+ if (priv->micdet[1].detecting)
+ report = SND_JACK_HEADPHONE;
+ else
+ report |= SND_JACK_BTN_0;
+ }
+ if (report)
+ priv->micdet[1].detecting = false;
+ else
+ priv->micdet[1].detecting = true;
+
+ snd_soc_jack_report(priv->micdet[1].jack, report,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+
+ pm_runtime_put(dev);
+}
+
+static irqreturn_t wm8994_mic_irq(int irq, void *data)
+{
+ struct wm8994_priv *priv = data;
+ struct snd_soc_codec *codec = priv->hubs.codec;
+
+#ifndef CONFIG_SND_SOC_WM8994_MODULE
+ trace_snd_soc_jack_irq(dev_name(codec->dev));
+#endif
+
+ pm_wakeup_event(codec->dev, 300);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &priv->mic_work, msecs_to_jiffies(250));
+
+ return IRQ_HANDLED;
+}
+
+/* Should be called with accdet_lock held */
+static void wm1811_micd_stop(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (!wm8994->jackdet)
+ return;
+
+ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0);
+
+ wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_JACK);
+
+ if (wm8994->wm8994->pdata.jd_ext_cap)
+ snd_soc_dapm_disable_pin(&codec->dapm,
+ "MICBIAS2");
+}
+
+static void wm8958_button_det(struct snd_soc_codec *codec, u16 status)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int report;
+
+ report = 0;
+ if (status & 0x4)
+ report |= SND_JACK_BTN_0;
+
+ if (status & 0x8)
+ report |= SND_JACK_BTN_1;
+
+ if (status & 0x10)
+ report |= SND_JACK_BTN_2;
+
+ if (status & 0x20)
+ report |= SND_JACK_BTN_3;
+
+ if (status & 0x40)
+ report |= SND_JACK_BTN_4;
+
+ if (status & 0x80)
+ report |= SND_JACK_BTN_5;
+
+ snd_soc_jack_report(wm8994->micdet[0].jack, report,
+ wm8994->btn_mask);
+}
+
+static void wm8958_open_circuit_work(struct work_struct *work)
+{
+ struct wm8994_priv *wm8994 = container_of(work,
+ struct wm8994_priv,
+ open_circuit_work.work);
+ struct device *dev = wm8994->wm8994->dev;
+
+ mutex_lock(&wm8994->accdet_lock);
+
+ wm1811_micd_stop(wm8994->hubs.codec);
+
+ dev_dbg(dev, "Reporting open circuit\n");
+
+ wm8994->jack_mic = false;
+ wm8994->mic_detecting = true;
+
+ wm8958_micd_set_rate(wm8994->hubs.codec);
+
+ snd_soc_jack_report(wm8994->micdet[0].jack, 0,
+ wm8994->btn_mask |
+ SND_JACK_HEADSET);
+
+ mutex_unlock(&wm8994->accdet_lock);
+}
+
+static void wm8958_mic_id(void *data, u16 status)
+{
+ struct snd_soc_codec *codec = data;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ /* Either nothing present or just starting detection */
+ if (!(status & WM8958_MICD_STS)) {
+ /* If nothing present then clear our statuses */
+ dev_dbg(codec->dev, "Detected open circuit\n");
+
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8994->open_circuit_work,
+ msecs_to_jiffies(2500));
+ return;
+ }
+
+ /* If the measurement is showing a high impedence we've got a
+ * microphone.
+ */
+ if (status & 0x600) {
+ dev_dbg(codec->dev, "Detected microphone\n");
+
+ wm8994->mic_detecting = false;
+ wm8994->jack_mic = true;
+
+ wm8958_micd_set_rate(codec);
+
+ snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET,
+ SND_JACK_HEADSET);
+ }
+
+
+ if (status & 0xfc) {
+ dev_dbg(codec->dev, "Detected headphone\n");
+ wm8994->mic_detecting = false;
+
+ wm8958_micd_set_rate(codec);
+
+ /* If we have jackdet that will detect removal */
+ wm1811_micd_stop(codec);
+
+ snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET);
+ }
+}
+
+/* Deferred mic detection to allow for extra settling time */
+static void wm1811_mic_work(struct work_struct *work)
+{
+ struct wm8994_priv *wm8994 = container_of(work, struct wm8994_priv,
+ mic_work.work);
+ struct wm8994 *control = wm8994->wm8994;
+ struct snd_soc_codec *codec = wm8994->hubs.codec;
+
+ pm_runtime_get_sync(codec->dev);
+
+ /* If required for an external cap force MICBIAS on */
+ if (control->pdata.jd_ext_cap) {
+ snd_soc_dapm_force_enable_pin(&codec->dapm,
+ "MICBIAS2");
+ snd_soc_dapm_sync(&codec->dapm);
+ }
+
+ mutex_lock(&wm8994->accdet_lock);
+
+ dev_dbg(codec->dev, "Starting mic detection\n");
+
+ /* Use a user-supplied callback if we have one */
+ if (wm8994->micd_cb) {
+ wm8994->micd_cb(wm8994->micd_cb_data);
+ } else {
+ /*
+ * Start off measument of microphone impedence to find out
+ * what's actually there.
+ */
+ wm8994->mic_detecting = true;
+ wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_MIC);
+
+ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ WM8958_MICD_ENA, WM8958_MICD_ENA);
+ }
+
+ mutex_unlock(&wm8994->accdet_lock);
+
+ pm_runtime_put(codec->dev);
+}
+
+static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
+{
+ struct wm8994_priv *wm8994 = data;
+ struct wm8994 *control = wm8994->wm8994;
+ struct snd_soc_codec *codec = wm8994->hubs.codec;
+ int reg, delay;
+ bool present;
+
+ pm_runtime_get_sync(codec->dev);
+
+ cancel_delayed_work_sync(&wm8994->mic_complete_work);
+
+ mutex_lock(&wm8994->accdet_lock);
+
+ reg = snd_soc_read(codec, WM1811_JACKDET_CTRL);
+ if (reg < 0) {
+ dev_err(codec->dev, "Failed to read jack status: %d\n", reg);
+ mutex_unlock(&wm8994->accdet_lock);
+ pm_runtime_put(codec->dev);
+ return IRQ_NONE;
+ }
+
+ dev_dbg(codec->dev, "JACKDET %x\n", reg);
+
+ present = reg & WM1811_JACKDET_LVL;
+
+ if (present) {
+ dev_dbg(codec->dev, "Jack detected\n");
+
+ wm8958_micd_set_rate(codec);
+
+ snd_soc_update_bits(codec, WM8958_MICBIAS2,
+ WM8958_MICB2_DISCH, 0);
+
+ /* Disable debounce while inserted */
+ snd_soc_update_bits(codec, WM1811_JACKDET_CTRL,
+ WM1811_JACKDET_DB, 0);
+
+ delay = control->pdata.micdet_delay;
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8994->mic_work,
+ msecs_to_jiffies(delay));
+ } else {
+ dev_dbg(codec->dev, "Jack not detected\n");
+
+ cancel_delayed_work_sync(&wm8994->mic_work);
+
+ snd_soc_update_bits(codec, WM8958_MICBIAS2,
+ WM8958_MICB2_DISCH, WM8958_MICB2_DISCH);
+
+ /* Enable debounce while removed */
+ snd_soc_update_bits(codec, WM1811_JACKDET_CTRL,
+ WM1811_JACKDET_DB, WM1811_JACKDET_DB);
+
+ wm8994->mic_detecting = false;
+ wm8994->jack_mic = false;
+ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ WM8958_MICD_ENA, 0);
+ wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_JACK);
+ }
+
+ mutex_unlock(&wm8994->accdet_lock);
+
+ /* Turn off MICBIAS if it was on for an external cap */
+ if (control->pdata.jd_ext_cap && !present)
+ snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS2");
+
+ if (present)
+ snd_soc_jack_report(wm8994->micdet[0].jack,
+ SND_JACK_MECHANICAL, SND_JACK_MECHANICAL);
+ else
+ snd_soc_jack_report(wm8994->micdet[0].jack, 0,
+ SND_JACK_MECHANICAL | SND_JACK_HEADSET |
+ wm8994->btn_mask);
+
+ /* Since we only report deltas force an update, ensures we
+ * avoid bootstrapping issues with the core. */
+ snd_soc_jack_report(wm8994->micdet[0].jack, 0, 0);
+
+ pm_runtime_put(codec->dev);
+ return IRQ_HANDLED;
+}
+
+static void wm1811_jackdet_bootstrap(struct work_struct *work)
+{
+ struct wm8994_priv *wm8994 = container_of(work,
+ struct wm8994_priv,
+ jackdet_bootstrap.work);
+ wm1811_jackdet_irq(0, wm8994);
+}
+
+/**
+ * wm8958_mic_detect - Enable microphone detection via the WM8958 IRQ
+ *
+ * @codec: WM8958 codec
+ * @jack: jack to report detection events on
+ *
+ * Enable microphone detection functionality for the WM8958. By
+ * default simple detection which supports the detection of up to 6
+ * buttons plus video and microphone functionality is supported.
+ *
+ * The WM8958 has an advanced jack detection facility which is able to
+ * support complex accessory detection, especially when used in
+ * conjunction with external circuitry. In order to provide maximum
+ * flexiblity a callback is provided which allows a completely custom
+ * detection algorithm.
+ */
+int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ wm1811_micdet_cb det_cb, void *det_cb_data,
+ wm1811_mic_id_cb id_cb, void *id_cb_data)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ u16 micd_lvl_sel;
+
+ switch (control->type) {
+ case WM1811:
+ case WM8958:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (jack) {
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "CLK_SYS");
+ snd_soc_dapm_sync(&codec->dapm);
+
+ wm8994->micdet[0].jack = jack;
+
+ if (det_cb) {
+ wm8994->micd_cb = det_cb;
+ wm8994->micd_cb_data = det_cb_data;
+ } else {
+ wm8994->mic_detecting = true;
+ wm8994->jack_mic = false;
+ }
+
+ if (id_cb) {
+ wm8994->mic_id_cb = id_cb;
+ wm8994->mic_id_cb_data = id_cb_data;
+ } else {
+ wm8994->mic_id_cb = wm8958_mic_id;
+ wm8994->mic_id_cb_data = codec;
+ }
+
+ wm8958_micd_set_rate(codec);
+
+ /* Detect microphones and short circuits by default */
+ if (control->pdata.micd_lvl_sel)
+ micd_lvl_sel = control->pdata.micd_lvl_sel;
+ else
+ micd_lvl_sel = 0x41;
+
+ wm8994->btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_BTN_4 | SND_JACK_BTN_5;
+
+ snd_soc_update_bits(codec, WM8958_MIC_DETECT_2,
+ WM8958_MICD_LVL_SEL_MASK, micd_lvl_sel);
+
+ WARN_ON(codec->dapm.bias_level > SND_SOC_BIAS_STANDBY);
+
+ /*
+ * If we can use jack detection start off with that,
+ * otherwise jump straight to microphone detection.
+ */
+ if (wm8994->jackdet) {
+ /* Disable debounce for the initial detect */
+ snd_soc_update_bits(codec, WM1811_JACKDET_CTRL,
+ WM1811_JACKDET_DB, 0);
+
+ snd_soc_update_bits(codec, WM8958_MICBIAS2,
+ WM8958_MICB2_DISCH,
+ WM8958_MICB2_DISCH);
+ snd_soc_update_bits(codec, WM8994_LDO_1,
+ WM8994_LDO1_DISCH, 0);
+ wm1811_jackdet_set_mode(codec,
+ WM1811_JACKDET_MODE_JACK);
+ } else {
+ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ WM8958_MICD_ENA, WM8958_MICD_ENA);
+ }
+
+ } else {
+ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ WM8958_MICD_ENA, 0);
+ wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_NONE);
+ snd_soc_dapm_disable_pin(&codec->dapm, "CLK_SYS");
+ snd_soc_dapm_sync(&codec->dapm);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8958_mic_detect);
+
+static void wm8958_mic_work(struct work_struct *work)
+{
+ struct wm8994_priv *wm8994 = container_of(work,
+ struct wm8994_priv,
+ mic_complete_work.work);
+ struct snd_soc_codec *codec = wm8994->hubs.codec;
+
+ pm_runtime_get_sync(codec->dev);
+
+ mutex_lock(&wm8994->accdet_lock);
+
+ wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status);
+
+ mutex_unlock(&wm8994->accdet_lock);
+
+ pm_runtime_put(codec->dev);
+}
+
+static irqreturn_t wm8958_mic_irq(int irq, void *data)
+{
+ struct wm8994_priv *wm8994 = data;
+ struct snd_soc_codec *codec = wm8994->hubs.codec;
+ int reg, count, ret, id_delay;
+
+ /*
+ * Jack detection may have detected a removal simulataneously
+ * with an update of the MICDET status; if so it will have
+ * stopped detection and we can ignore this interrupt.
+ */
+ if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA))
+ return IRQ_HANDLED;
+
+ cancel_delayed_work_sync(&wm8994->mic_complete_work);
+ cancel_delayed_work_sync(&wm8994->open_circuit_work);
+
+ pm_runtime_get_sync(codec->dev);
+
+ /* We may occasionally read a detection without an impedence
+ * range being provided - if that happens loop again.
+ */
+ count = 10;
+ do {
+ reg = snd_soc_read(codec, WM8958_MIC_DETECT_3);
+ if (reg < 0) {
+ dev_err(codec->dev,
+ "Failed to read mic detect status: %d\n",
+ reg);
+ pm_runtime_put(codec->dev);
+ return IRQ_NONE;
+ }
+
+ if (!(reg & WM8958_MICD_VALID)) {
+ dev_dbg(codec->dev, "Mic detect data not valid\n");
+ goto out;
+ }
+
+ if (!(reg & WM8958_MICD_STS) || (reg & WM8958_MICD_LVL_MASK))
+ break;
+
+ msleep(1);
+ } while (count--);
+
+ if (count == 0)
+ dev_warn(codec->dev, "No impedance range reported for jack\n");
+
+#ifndef CONFIG_SND_SOC_WM8994_MODULE
+ trace_snd_soc_jack_irq(dev_name(codec->dev));
+#endif
+
+ /* Avoid a transient report when the accessory is being removed */
+ if (wm8994->jackdet) {
+ ret = snd_soc_read(codec, WM1811_JACKDET_CTRL);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read jack status: %d\n",
+ ret);
+ } else if (!(ret & WM1811_JACKDET_LVL)) {
+ dev_dbg(codec->dev, "Ignoring removed jack\n");
+ goto out;
+ }
+ } else if (!(reg & WM8958_MICD_STS)) {
+ snd_soc_jack_report(wm8994->micdet[0].jack, 0,
+ SND_JACK_MECHANICAL | SND_JACK_HEADSET |
+ wm8994->btn_mask);
+ wm8994->mic_detecting = true;
+ goto out;
+ }
+
+ wm8994->mic_status = reg;
+ id_delay = wm8994->wm8994->pdata.mic_id_delay;
+
+ if (wm8994->mic_detecting)
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8994->mic_complete_work,
+ msecs_to_jiffies(id_delay));
+ else
+ wm8958_button_det(codec, reg);
+
+out:
+ pm_runtime_put(codec->dev);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wm8994_fifo_error(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+
+ dev_err(codec->dev, "FIFO error\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wm8994_temp_warn(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+
+ dev_err(codec->dev, "Thermal warning\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wm8994_temp_shut(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+
+ dev_crit(codec->dev, "Thermal shutdown\n");
+
+ return IRQ_HANDLED;
+}
+
+static int wm8994_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wm8994 *control = dev_get_drvdata(codec->dev->parent);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ unsigned int reg;
+ int ret, i;
+
+ wm8994->hubs.codec = codec;
+
+ mutex_init(&wm8994->accdet_lock);
+ INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap,
+ wm1811_jackdet_bootstrap);
+ INIT_DELAYED_WORK(&wm8994->open_circuit_work,
+ wm8958_open_circuit_work);
+
+ switch (control->type) {
+ case WM8994:
+ INIT_DELAYED_WORK(&wm8994->mic_work, wm8994_mic_work);
+ break;
+ case WM1811:
+ INIT_DELAYED_WORK(&wm8994->mic_work, wm1811_mic_work);
+ break;
+ default:
+ break;
+ }
+
+ INIT_DELAYED_WORK(&wm8994->mic_complete_work, wm8958_mic_work);
+
+ for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
+ init_completion(&wm8994->fll_locked[i]);
+
+ wm8994->micdet_irq = control->pdata.micdet_irq;
+
+ /* By default use idle_bias_off, will override for WM8994 */
+ codec->dapm.idle_bias_off = 1;
+
+ /* Set revision-specific configuration */
+ switch (control->type) {
+ case WM8994:
+ /* Single ended line outputs should have VMID on. */
+ if (!control->pdata.lineout1_diff ||
+ !control->pdata.lineout2_diff)
+ codec->dapm.idle_bias_off = 0;
+
+ switch (control->revision) {
+ case 2:
+ case 3:
+ wm8994->hubs.dcs_codes_l = -5;
+ wm8994->hubs.dcs_codes_r = -5;
+ wm8994->hubs.hp_startup_mode = 1;
+ wm8994->hubs.dcs_readback_mode = 1;
+ wm8994->hubs.series_startup = 1;
+ break;
+ default:
+ wm8994->hubs.dcs_readback_mode = 2;
+ break;
+ }
+ break;
+
+ case WM8958:
+ wm8994->hubs.dcs_readback_mode = 1;
+ wm8994->hubs.hp_startup_mode = 1;
+
+ switch (control->revision) {
+ case 0:
+ break;
+ default:
+ wm8994->fll_byp = true;
+ break;
+ }
+ break;
+
+ case WM1811:
+ wm8994->hubs.dcs_readback_mode = 2;
+ wm8994->hubs.no_series_update = 1;
+ wm8994->hubs.hp_startup_mode = 1;
+ wm8994->hubs.no_cache_dac_hp_direct = true;
+ wm8994->fll_byp = true;
+
+ wm8994->hubs.dcs_codes_l = -9;
+ wm8994->hubs.dcs_codes_r = -7;
+
+ snd_soc_update_bits(codec, WM8994_ANALOGUE_HP_1,
+ WM1811_HPOUT1_ATTN, WM1811_HPOUT1_ATTN);
+ break;
+
+ default:
+ break;
+ }
+
+ wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR,
+ wm8994_fifo_error, "FIFO error", codec);
+ wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN,
+ wm8994_temp_warn, "Thermal warning", codec);
+ wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT,
+ wm8994_temp_shut, "Thermal shutdown", codec);
+
+ switch (control->type) {
+ case WM8994:
+ if (wm8994->micdet_irq)
+ ret = request_threaded_irq(wm8994->micdet_irq, NULL,
+ wm8994_mic_irq,
+ IRQF_TRIGGER_RISING,
+ "Mic1 detect",
+ wm8994);
+ else
+ ret = wm8994_request_irq(wm8994->wm8994,
+ WM8994_IRQ_MIC1_DET,
+ wm8994_mic_irq, "Mic 1 detect",
+ wm8994);
+
+ if (ret != 0)
+ dev_warn(codec->dev,
+ "Failed to request Mic1 detect IRQ: %d\n",
+ ret);
+
+
+ ret = wm8994_request_irq(wm8994->wm8994,
+ WM8994_IRQ_MIC1_SHRT,
+ wm8994_mic_irq, "Mic 1 short",
+ wm8994);
+ if (ret != 0)
+ dev_warn(codec->dev,
+ "Failed to request Mic1 short IRQ: %d\n",
+ ret);
+
+ ret = wm8994_request_irq(wm8994->wm8994,
+ WM8994_IRQ_MIC2_DET,
+ wm8994_mic_irq, "Mic 2 detect",
+ wm8994);
+ if (ret != 0)
+ dev_warn(codec->dev,
+ "Failed to request Mic2 detect IRQ: %d\n",
+ ret);
+
+ ret = wm8994_request_irq(wm8994->wm8994,
+ WM8994_IRQ_MIC2_SHRT,
+ wm8994_mic_irq, "Mic 2 short",
+ wm8994);
+ if (ret != 0)
+ dev_warn(codec->dev,
+ "Failed to request Mic2 short IRQ: %d\n",
+ ret);
+ break;
+
+ case WM8958:
+ case WM1811:
+ if (wm8994->micdet_irq) {
+ ret = request_threaded_irq(wm8994->micdet_irq, NULL,
+ wm8958_mic_irq,
+ IRQF_TRIGGER_RISING,
+ "Mic detect",
+ wm8994);
+ if (ret != 0)
+ dev_warn(codec->dev,
+ "Failed to request Mic detect IRQ: %d\n",
+ ret);
+ } else {
+ wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_MIC1_DET,
+ wm8958_mic_irq, "Mic detect",
+ wm8994);
+ }
+ }
+
+ switch (control->type) {
+ case WM1811:
+ if (control->cust_id > 1 || control->revision > 1) {
+ ret = wm8994_request_irq(wm8994->wm8994,
+ WM8994_IRQ_GPIO(6),
+ wm1811_jackdet_irq, "JACKDET",
+ wm8994);
+ if (ret == 0)
+ wm8994->jackdet = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ wm8994->fll_locked_irq = true;
+ for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) {
+ ret = wm8994_request_irq(wm8994->wm8994,
+ WM8994_IRQ_FLL1_LOCK + i,
+ wm8994_fll_locked_irq, "FLL lock",
+ &wm8994->fll_locked[i]);
+ if (ret != 0)
+ wm8994->fll_locked_irq = false;
+ }
+
+ /* Make sure we can read from the GPIOs if they're inputs */
+ pm_runtime_get_sync(codec->dev);
+
+ /* Remember if AIFnLRCLK is configured as a GPIO. This should be
+ * configured on init - if a system wants to do this dynamically
+ * at runtime we can deal with that then.
+ */
+ ret = regmap_read(control->regmap, WM8994_GPIO_1, &reg);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read GPIO1 state: %d\n", ret);
+ goto err_irq;
+ }
+ if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) {
+ wm8994->lrclk_shared[0] = 1;
+ wm8994_dai[0].symmetric_rates = 1;
+ } else {
+ wm8994->lrclk_shared[0] = 0;
+ }
+
+ ret = regmap_read(control->regmap, WM8994_GPIO_6, &reg);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read GPIO6 state: %d\n", ret);
+ goto err_irq;
+ }
+ if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) {
+ wm8994->lrclk_shared[1] = 1;
+ wm8994_dai[1].symmetric_rates = 1;
+ } else {
+ wm8994->lrclk_shared[1] = 0;
+ }
+
+ pm_runtime_put(codec->dev);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(wm8994_vu_bits); i++)
+ snd_soc_update_bits(codec, wm8994_vu_bits[i].reg,
+ wm8994_vu_bits[i].mask,
+ wm8994_vu_bits[i].mask);
+
+ /* Set the low bit of the 3D stereo depth so TLV matches */
+ snd_soc_update_bits(codec, WM8994_AIF1_DAC1_FILTERS_2,
+ 1 << WM8994_AIF1DAC1_3D_GAIN_SHIFT,
+ 1 << WM8994_AIF1DAC1_3D_GAIN_SHIFT);
+ snd_soc_update_bits(codec, WM8994_AIF1_DAC2_FILTERS_2,
+ 1 << WM8994_AIF1DAC2_3D_GAIN_SHIFT,
+ 1 << WM8994_AIF1DAC2_3D_GAIN_SHIFT);
+ snd_soc_update_bits(codec, WM8994_AIF2_DAC_FILTERS_2,
+ 1 << WM8994_AIF2DAC_3D_GAIN_SHIFT,
+ 1 << WM8994_AIF2DAC_3D_GAIN_SHIFT);
+
+ /* Unconditionally enable AIF1 ADC TDM mode on chips which can
+ * use this; it only affects behaviour on idle TDM clock
+ * cycles. */
+ switch (control->type) {
+ case WM8994:
+ case WM8958:
+ snd_soc_update_bits(codec, WM8994_AIF1_CONTROL_1,
+ WM8994_AIF1ADC_TDM, WM8994_AIF1ADC_TDM);
+ break;
+ default:
+ break;
+ }
+
+ /* Put MICBIAS into bypass mode by default on newer devices */
+ switch (control->type) {
+ case WM8958:
+ case WM1811:
+ snd_soc_update_bits(codec, WM8958_MICBIAS1,
+ WM8958_MICB1_MODE, WM8958_MICB1_MODE);
+ snd_soc_update_bits(codec, WM8958_MICBIAS2,
+ WM8958_MICB2_MODE, WM8958_MICB2_MODE);
+ break;
+ default:
+ break;
+ }
+
+ wm8994->hubs.check_class_w_digital = wm8994_check_class_w_digital;
+ wm_hubs_update_class_w(codec);
+
+ wm8994_handle_pdata(wm8994);
+
+ wm_hubs_add_analogue_controls(codec);
+ snd_soc_add_codec_controls(codec, wm8994_snd_controls,
+ ARRAY_SIZE(wm8994_snd_controls));
+ snd_soc_dapm_new_controls(dapm, wm8994_dapm_widgets,
+ ARRAY_SIZE(wm8994_dapm_widgets));
+
+ switch (control->type) {
+ case WM8994:
+ snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets,
+ ARRAY_SIZE(wm8994_specific_dapm_widgets));
+ if (control->revision < 4) {
+ snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets,
+ ARRAY_SIZE(wm8994_lateclk_revd_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_adc_revd_widgets,
+ ARRAY_SIZE(wm8994_adc_revd_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_dac_revd_widgets,
+ ARRAY_SIZE(wm8994_dac_revd_widgets));
+ } else {
+ snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets,
+ ARRAY_SIZE(wm8994_lateclk_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets,
+ ARRAY_SIZE(wm8994_adc_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets,
+ ARRAY_SIZE(wm8994_dac_widgets));
+ }
+ break;
+ case WM8958:
+ snd_soc_add_codec_controls(codec, wm8958_snd_controls,
+ ARRAY_SIZE(wm8958_snd_controls));
+ snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
+ ARRAY_SIZE(wm8958_dapm_widgets));
+ if (control->revision < 1) {
+ snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets,
+ ARRAY_SIZE(wm8994_lateclk_revd_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_adc_revd_widgets,
+ ARRAY_SIZE(wm8994_adc_revd_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_dac_revd_widgets,
+ ARRAY_SIZE(wm8994_dac_revd_widgets));
+ } else {
+ snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets,
+ ARRAY_SIZE(wm8994_lateclk_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets,
+ ARRAY_SIZE(wm8994_adc_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets,
+ ARRAY_SIZE(wm8994_dac_widgets));
+ }
+ break;
+
+ case WM1811:
+ snd_soc_add_codec_controls(codec, wm8958_snd_controls,
+ ARRAY_SIZE(wm8958_snd_controls));
+ snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
+ ARRAY_SIZE(wm8958_dapm_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets,
+ ARRAY_SIZE(wm8994_lateclk_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets,
+ ARRAY_SIZE(wm8994_adc_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets,
+ ARRAY_SIZE(wm8994_dac_widgets));
+ break;
+ }
+
+ wm_hubs_add_analogue_routes(codec, 0, 0);
+ ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE,
+ wm_hubs_dcs_done, "DC servo done",
+ &wm8994->hubs);
+ if (ret == 0)
+ wm8994->hubs.dcs_done_irq = true;
+ snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
+
+ switch (control->type) {
+ case WM8994:
+ snd_soc_dapm_add_routes(dapm, wm8994_intercon,
+ ARRAY_SIZE(wm8994_intercon));
+
+ if (control->revision < 4) {
+ snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon,
+ ARRAY_SIZE(wm8994_revd_intercon));
+ snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon,
+ ARRAY_SIZE(wm8994_lateclk_revd_intercon));
+ } else {
+ snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon,
+ ARRAY_SIZE(wm8994_lateclk_intercon));
+ }
+ break;
+ case WM8958:
+ if (control->revision < 1) {
+ snd_soc_dapm_add_routes(dapm, wm8994_intercon,
+ ARRAY_SIZE(wm8994_intercon));
+ snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon,
+ ARRAY_SIZE(wm8994_revd_intercon));
+ snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon,
+ ARRAY_SIZE(wm8994_lateclk_revd_intercon));
+ } else {
+ snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon,
+ ARRAY_SIZE(wm8994_lateclk_intercon));
+ snd_soc_dapm_add_routes(dapm, wm8958_intercon,
+ ARRAY_SIZE(wm8958_intercon));
+ }
+
+ wm8958_dsp2_init(codec);
+ break;
+ case WM1811:
+ snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon,
+ ARRAY_SIZE(wm8994_lateclk_intercon));
+ snd_soc_dapm_add_routes(dapm, wm8958_intercon,
+ ARRAY_SIZE(wm8958_intercon));
+ break;
+ }
+
+ return 0;
+
+err_irq:
+ if (wm8994->jackdet)
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_GPIO(6), wm8994);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC2_SHRT, wm8994);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC2_DET, wm8994);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_SHRT, wm8994);
+ if (wm8994->micdet_irq)
+ free_irq(wm8994->micdet_irq, wm8994);
+ for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i,
+ &wm8994->fll_locked[i]);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE,
+ &wm8994->hubs);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, codec);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, codec);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, codec);
+
+ return ret;
+}
+
+static int wm8994_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i,
+ &wm8994->fll_locked[i]);
+
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE,
+ &wm8994->hubs);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, codec);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, codec);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, codec);
+
+ if (wm8994->jackdet)
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_GPIO(6), wm8994);
+
+ switch (control->type) {
+ case WM8994:
+ if (wm8994->micdet_irq)
+ free_irq(wm8994->micdet_irq, wm8994);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC2_DET,
+ wm8994);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_SHRT,
+ wm8994);
+ wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_DET,
+ wm8994);
+ break;
+
+ case WM1811:
+ case WM8958:
+ if (wm8994->micdet_irq)
+ free_irq(wm8994->micdet_irq, wm8994);
+ break;
+ }
+ release_firmware(wm8994->mbc);
+ release_firmware(wm8994->mbc_vss);
+ release_firmware(wm8994->enh_eq);
+ kfree(wm8994->retune_mobile_texts);
+ return 0;
+}
+
+static struct regmap *wm8994_get_regmap(struct device *dev)
+{
+ struct wm8994 *control = dev_get_drvdata(dev->parent);
+
+ return control->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8994 = {
+ .probe = wm8994_codec_probe,
+ .remove = wm8994_codec_remove,
+ .suspend = wm8994_codec_suspend,
+ .resume = wm8994_codec_resume,
+ .get_regmap = wm8994_get_regmap,
+ .set_bias_level = wm8994_set_bias_level,
+};
+
+static int wm8994_probe(struct platform_device *pdev)
+{
+ struct wm8994_priv *wm8994;
+
+ wm8994 = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_priv),
+ GFP_KERNEL);
+ if (wm8994 == NULL)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, wm8994);
+
+ mutex_init(&wm8994->fw_lock);
+
+ wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8994,
+ wm8994_dai, ARRAY_SIZE(wm8994_dai));
+}
+
+static int wm8994_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int wm8994_suspend(struct device *dev)
+{
+ struct wm8994_priv *wm8994 = dev_get_drvdata(dev);
+
+ /* Drop down to power saving mode when system is suspended */
+ if (wm8994->jackdet && !wm8994->active_refcount)
+ regmap_update_bits(wm8994->wm8994->regmap, WM8994_ANTIPOP_2,
+ WM1811_JACKDET_MODE_MASK,
+ wm8994->jackdet_mode);
+
+ return 0;
+}
+
+static int wm8994_resume(struct device *dev)
+{
+ struct wm8994_priv *wm8994 = dev_get_drvdata(dev);
+
+ if (wm8994->jackdet && wm8994->jackdet_mode)
+ regmap_update_bits(wm8994->wm8994->regmap, WM8994_ANTIPOP_2,
+ WM1811_JACKDET_MODE_MASK,
+ WM1811_JACKDET_MODE_AUDIO);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops wm8994_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(wm8994_suspend, wm8994_resume)
+};
+
+static struct platform_driver wm8994_codec_driver = {
+ .driver = {
+ .name = "wm8994-codec",
+ .pm = &wm8994_pm_ops,
+ },
+ .probe = wm8994_probe,
+ .remove = wm8994_remove,
+};
+
+module_platform_driver(wm8994_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM8994 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8994-codec");
diff --git a/kernel/sound/soc/codecs/wm8994.h b/kernel/sound/soc/codecs/wm8994.h
new file mode 100644
index 000000000..dd73387b1
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8994.h
@@ -0,0 +1,168 @@
+/*
+ * wm8994.h -- WM8994 Soc Audio driver
+ *
+ * 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 _WM8994_H
+#define _WM8994_H
+
+#include <sound/soc.h>
+#include <linux/firmware.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+
+#include "wm_hubs.h"
+
+/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
+#define WM8994_SYSCLK_MCLK1 1
+#define WM8994_SYSCLK_MCLK2 2
+#define WM8994_SYSCLK_FLL1 3
+#define WM8994_SYSCLK_FLL2 4
+
+/* OPCLK is also configured with set_dai_sysclk, specify division*10 as rate. */
+#define WM8994_SYSCLK_OPCLK 5
+
+#define WM8994_FLL1 1
+#define WM8994_FLL2 2
+
+#define WM8994_FLL_SRC_MCLK1 1
+#define WM8994_FLL_SRC_MCLK2 2
+#define WM8994_FLL_SRC_LRCLK 3
+#define WM8994_FLL_SRC_BCLK 4
+#define WM8994_FLL_SRC_INTERNAL 5
+
+enum wm8994_vmid_mode {
+ WM8994_VMID_NORMAL,
+ WM8994_VMID_FORCE,
+};
+
+typedef void (*wm1811_micdet_cb)(void *data);
+typedef void (*wm1811_mic_id_cb)(void *data, u16 status);
+
+int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ int micbias);
+int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ wm1811_micdet_cb cb, void *det_cb_data,
+ wm1811_mic_id_cb id_cb, void *id_cb_data);
+
+int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode);
+
+int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+void wm8958_dsp2_init(struct snd_soc_codec *codec);
+
+struct wm8994_micdet {
+ struct snd_soc_jack *jack;
+ bool detecting;
+};
+
+/* codec private data */
+struct wm8994_fll_config {
+ int src;
+ int in;
+ int out;
+};
+
+#define WM8994_NUM_DRC 3
+#define WM8994_NUM_EQ 3
+
+struct wm8994;
+
+struct wm8994_priv {
+ struct wm_hubs_data hubs;
+ struct wm8994 *wm8994;
+ int sysclk[2];
+ int sysclk_rate[2];
+ int mclk[2];
+ int aifclk[2];
+ int aifdiv[2];
+ int channels[2];
+ struct wm8994_fll_config fll[2], fll_suspend[2];
+ struct completion fll_locked[2];
+ bool fll_locked_irq;
+ bool fll_byp;
+ bool clk_has_run;
+
+ int vmid_refcount;
+ int active_refcount;
+ enum wm8994_vmid_mode vmid_mode;
+
+ int dac_rates[2];
+ int lrclk_shared[2];
+
+ int mbc_ena[3];
+ int hpf1_ena[3];
+ int hpf2_ena[3];
+ int vss_ena[3];
+ int enh_eq_ena[3];
+
+ /* Platform dependant DRC configuration */
+ const char **drc_texts;
+ int drc_cfg[WM8994_NUM_DRC];
+ struct soc_enum drc_enum;
+
+ /* Platform dependant ReTune mobile configuration */
+ int num_retune_mobile_texts;
+ const char **retune_mobile_texts;
+ int retune_mobile_cfg[WM8994_NUM_EQ];
+ struct soc_enum retune_mobile_enum;
+
+ /* Platform dependant MBC configuration */
+ int mbc_cfg;
+ const char **mbc_texts;
+ struct soc_enum mbc_enum;
+
+ /* Platform dependant VSS configuration */
+ int vss_cfg;
+ const char **vss_texts;
+ struct soc_enum vss_enum;
+
+ /* Platform dependant VSS HPF configuration */
+ int vss_hpf_cfg;
+ const char **vss_hpf_texts;
+ struct soc_enum vss_hpf_enum;
+
+ /* Platform dependant enhanced EQ configuration */
+ int enh_eq_cfg;
+ const char **enh_eq_texts;
+ struct soc_enum enh_eq_enum;
+
+ struct mutex accdet_lock;
+ struct wm8994_micdet micdet[2];
+ struct delayed_work mic_work;
+ struct delayed_work open_circuit_work;
+ struct delayed_work mic_complete_work;
+ u16 mic_status;
+ bool mic_detecting;
+ bool jack_mic;
+ int btn_mask;
+ bool jackdet;
+ int jackdet_mode;
+ struct delayed_work jackdet_bootstrap;
+
+ int micdet_irq;
+ wm1811_micdet_cb micd_cb;
+ void *micd_cb_data;
+ wm1811_mic_id_cb mic_id_cb;
+ void *mic_id_cb_data;
+
+ unsigned int aif1clk_enable:1;
+ unsigned int aif2clk_enable:1;
+
+ unsigned int aif1clk_disable:1;
+ unsigned int aif2clk_disable:1;
+
+ struct mutex fw_lock;
+ int dsp_active;
+ const struct firmware *cur_fw;
+ const struct firmware *mbc;
+ const struct firmware *mbc_vss;
+ const struct firmware *enh_eq;
+};
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8995.c b/kernel/sound/soc/codecs/wm8995.c
new file mode 100644
index 000000000..66103c2b0
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8995.c
@@ -0,0 +1,2346 @@
+/*
+ * wm8995.c -- WM8995 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
+ *
+ * Based on wm8994.c and wm_hubs.c by Mark Brown
+ *
+ * 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/i2c.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.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 "wm8995.h"
+
+#define WM8995_NUM_SUPPLIES 8
+static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD1",
+ "DBVDD2",
+ "DBVDD3",
+ "AVDD1",
+ "AVDD2",
+ "CPVDD",
+ "MICVDD"
+};
+
+static const struct reg_default wm8995_reg_defaults[] = {
+ { 0, 0x8995 },
+ { 5, 0x0100 },
+ { 16, 0x000b },
+ { 17, 0x000b },
+ { 24, 0x02c0 },
+ { 25, 0x02c0 },
+ { 26, 0x02c0 },
+ { 27, 0x02c0 },
+ { 28, 0x000f },
+ { 32, 0x0005 },
+ { 33, 0x0005 },
+ { 40, 0x0003 },
+ { 41, 0x0013 },
+ { 48, 0x0004 },
+ { 56, 0x09f8 },
+ { 64, 0x1f25 },
+ { 69, 0x0004 },
+ { 82, 0xaaaa },
+ { 84, 0x2a2a },
+ { 146, 0x0060 },
+ { 256, 0x0002 },
+ { 257, 0x8004 },
+ { 520, 0x0010 },
+ { 528, 0x0083 },
+ { 529, 0x0083 },
+ { 548, 0x0c80 },
+ { 580, 0x0c80 },
+ { 768, 0x4050 },
+ { 769, 0x4000 },
+ { 771, 0x0040 },
+ { 772, 0x0040 },
+ { 773, 0x0040 },
+ { 774, 0x0004 },
+ { 775, 0x0100 },
+ { 784, 0x4050 },
+ { 785, 0x4000 },
+ { 787, 0x0040 },
+ { 788, 0x0040 },
+ { 789, 0x0040 },
+ { 1024, 0x00c0 },
+ { 1025, 0x00c0 },
+ { 1026, 0x00c0 },
+ { 1027, 0x00c0 },
+ { 1028, 0x00c0 },
+ { 1029, 0x00c0 },
+ { 1030, 0x00c0 },
+ { 1031, 0x00c0 },
+ { 1056, 0x0200 },
+ { 1057, 0x0010 },
+ { 1058, 0x0200 },
+ { 1059, 0x0010 },
+ { 1088, 0x0098 },
+ { 1089, 0x0845 },
+ { 1104, 0x0098 },
+ { 1105, 0x0845 },
+ { 1152, 0x6318 },
+ { 1153, 0x6300 },
+ { 1154, 0x0fca },
+ { 1155, 0x0400 },
+ { 1156, 0x00d8 },
+ { 1157, 0x1eb5 },
+ { 1158, 0xf145 },
+ { 1159, 0x0b75 },
+ { 1160, 0x01c5 },
+ { 1161, 0x1c58 },
+ { 1162, 0xf373 },
+ { 1163, 0x0a54 },
+ { 1164, 0x0558 },
+ { 1165, 0x168e },
+ { 1166, 0xf829 },
+ { 1167, 0x07ad },
+ { 1168, 0x1103 },
+ { 1169, 0x0564 },
+ { 1170, 0x0559 },
+ { 1171, 0x4000 },
+ { 1184, 0x6318 },
+ { 1185, 0x6300 },
+ { 1186, 0x0fca },
+ { 1187, 0x0400 },
+ { 1188, 0x00d8 },
+ { 1189, 0x1eb5 },
+ { 1190, 0xf145 },
+ { 1191, 0x0b75 },
+ { 1192, 0x01c5 },
+ { 1193, 0x1c58 },
+ { 1194, 0xf373 },
+ { 1195, 0x0a54 },
+ { 1196, 0x0558 },
+ { 1197, 0x168e },
+ { 1198, 0xf829 },
+ { 1199, 0x07ad },
+ { 1200, 0x1103 },
+ { 1201, 0x0564 },
+ { 1202, 0x0559 },
+ { 1203, 0x4000 },
+ { 1280, 0x00c0 },
+ { 1281, 0x00c0 },
+ { 1282, 0x00c0 },
+ { 1283, 0x00c0 },
+ { 1312, 0x0200 },
+ { 1313, 0x0010 },
+ { 1344, 0x0098 },
+ { 1345, 0x0845 },
+ { 1408, 0x6318 },
+ { 1409, 0x6300 },
+ { 1410, 0x0fca },
+ { 1411, 0x0400 },
+ { 1412, 0x00d8 },
+ { 1413, 0x1eb5 },
+ { 1414, 0xf145 },
+ { 1415, 0x0b75 },
+ { 1416, 0x01c5 },
+ { 1417, 0x1c58 },
+ { 1418, 0xf373 },
+ { 1419, 0x0a54 },
+ { 1420, 0x0558 },
+ { 1421, 0x168e },
+ { 1422, 0xf829 },
+ { 1423, 0x07ad },
+ { 1424, 0x1103 },
+ { 1425, 0x0564 },
+ { 1426, 0x0559 },
+ { 1427, 0x4000 },
+ { 1568, 0x0002 },
+ { 1792, 0xa100 },
+ { 1793, 0xa101 },
+ { 1794, 0xa101 },
+ { 1795, 0xa101 },
+ { 1796, 0xa101 },
+ { 1797, 0xa101 },
+ { 1798, 0xa101 },
+ { 1799, 0xa101 },
+ { 1800, 0xa101 },
+ { 1801, 0xa101 },
+ { 1802, 0xa101 },
+ { 1803, 0xa101 },
+ { 1804, 0xa101 },
+ { 1805, 0xa101 },
+ { 1825, 0x0055 },
+ { 1848, 0x3fff },
+ { 1849, 0x1fff },
+ { 2049, 0x0001 },
+ { 2050, 0x0069 },
+ { 2056, 0x0002 },
+ { 2057, 0x0003 },
+ { 2058, 0x0069 },
+ { 12288, 0x0001 },
+ { 12289, 0x0001 },
+ { 12291, 0x0006 },
+ { 12292, 0x0040 },
+ { 12293, 0x0001 },
+ { 12294, 0x000f },
+ { 12295, 0x0006 },
+ { 12296, 0x0001 },
+ { 12297, 0x0003 },
+ { 12298, 0x0104 },
+ { 12300, 0x0060 },
+ { 12301, 0x0011 },
+ { 12302, 0x0401 },
+ { 12304, 0x0050 },
+ { 12305, 0x0003 },
+ { 12306, 0x0100 },
+ { 12308, 0x0051 },
+ { 12309, 0x0003 },
+ { 12310, 0x0104 },
+ { 12311, 0x000a },
+ { 12312, 0x0060 },
+ { 12313, 0x003b },
+ { 12314, 0x0502 },
+ { 12315, 0x0100 },
+ { 12316, 0x2fff },
+ { 12320, 0x2fff },
+ { 12324, 0x2fff },
+ { 12328, 0x2fff },
+ { 12332, 0x2fff },
+ { 12336, 0x2fff },
+ { 12340, 0x2fff },
+ { 12344, 0x2fff },
+ { 12348, 0x2fff },
+ { 12352, 0x0001 },
+ { 12353, 0x0001 },
+ { 12355, 0x0006 },
+ { 12356, 0x0040 },
+ { 12357, 0x0001 },
+ { 12358, 0x000f },
+ { 12359, 0x0006 },
+ { 12360, 0x0001 },
+ { 12361, 0x0003 },
+ { 12362, 0x0104 },
+ { 12364, 0x0060 },
+ { 12365, 0x0011 },
+ { 12366, 0x0401 },
+ { 12368, 0x0050 },
+ { 12369, 0x0003 },
+ { 12370, 0x0100 },
+ { 12372, 0x0060 },
+ { 12373, 0x003b },
+ { 12374, 0x0502 },
+ { 12375, 0x0100 },
+ { 12376, 0x2fff },
+ { 12380, 0x2fff },
+ { 12384, 0x2fff },
+ { 12388, 0x2fff },
+ { 12392, 0x2fff },
+ { 12396, 0x2fff },
+ { 12400, 0x2fff },
+ { 12404, 0x2fff },
+ { 12408, 0x2fff },
+ { 12412, 0x2fff },
+ { 12416, 0x0001 },
+ { 12417, 0x0001 },
+ { 12419, 0x0006 },
+ { 12420, 0x0040 },
+ { 12421, 0x0001 },
+ { 12422, 0x000f },
+ { 12423, 0x0006 },
+ { 12424, 0x0001 },
+ { 12425, 0x0003 },
+ { 12426, 0x0106 },
+ { 12428, 0x0061 },
+ { 12429, 0x0011 },
+ { 12430, 0x0401 },
+ { 12432, 0x0050 },
+ { 12433, 0x0003 },
+ { 12434, 0x0102 },
+ { 12436, 0x0051 },
+ { 12437, 0x0003 },
+ { 12438, 0x0106 },
+ { 12439, 0x000a },
+ { 12440, 0x0061 },
+ { 12441, 0x003b },
+ { 12442, 0x0502 },
+ { 12443, 0x0100 },
+ { 12444, 0x2fff },
+ { 12448, 0x2fff },
+ { 12452, 0x2fff },
+ { 12456, 0x2fff },
+ { 12460, 0x2fff },
+ { 12464, 0x2fff },
+ { 12468, 0x2fff },
+ { 12472, 0x2fff },
+ { 12476, 0x2fff },
+ { 12480, 0x0001 },
+ { 12481, 0x0001 },
+ { 12483, 0x0006 },
+ { 12484, 0x0040 },
+ { 12485, 0x0001 },
+ { 12486, 0x000f },
+ { 12487, 0x0006 },
+ { 12488, 0x0001 },
+ { 12489, 0x0003 },
+ { 12490, 0x0106 },
+ { 12492, 0x0061 },
+ { 12493, 0x0011 },
+ { 12494, 0x0401 },
+ { 12496, 0x0050 },
+ { 12497, 0x0003 },
+ { 12498, 0x0102 },
+ { 12500, 0x0061 },
+ { 12501, 0x003b },
+ { 12502, 0x0502 },
+ { 12503, 0x0100 },
+ { 12504, 0x2fff },
+ { 12508, 0x2fff },
+ { 12512, 0x2fff },
+ { 12516, 0x2fff },
+ { 12520, 0x2fff },
+ { 12524, 0x2fff },
+ { 12528, 0x2fff },
+ { 12532, 0x2fff },
+ { 12536, 0x2fff },
+ { 12540, 0x2fff },
+ { 12544, 0x0060 },
+ { 12546, 0x0601 },
+ { 12548, 0x0050 },
+ { 12550, 0x0100 },
+ { 12552, 0x0001 },
+ { 12554, 0x0104 },
+ { 12555, 0x0100 },
+ { 12556, 0x2fff },
+ { 12560, 0x2fff },
+ { 12564, 0x2fff },
+ { 12568, 0x2fff },
+ { 12572, 0x2fff },
+ { 12576, 0x2fff },
+ { 12580, 0x2fff },
+ { 12584, 0x2fff },
+ { 12588, 0x2fff },
+ { 12592, 0x2fff },
+ { 12596, 0x2fff },
+ { 12600, 0x2fff },
+ { 12604, 0x2fff },
+ { 12608, 0x0061 },
+ { 12610, 0x0601 },
+ { 12612, 0x0050 },
+ { 12614, 0x0102 },
+ { 12616, 0x0001 },
+ { 12618, 0x0106 },
+ { 12619, 0x0100 },
+ { 12620, 0x2fff },
+ { 12624, 0x2fff },
+ { 12628, 0x2fff },
+ { 12632, 0x2fff },
+ { 12636, 0x2fff },
+ { 12640, 0x2fff },
+ { 12644, 0x2fff },
+ { 12648, 0x2fff },
+ { 12652, 0x2fff },
+ { 12656, 0x2fff },
+ { 12660, 0x2fff },
+ { 12664, 0x2fff },
+ { 12668, 0x2fff },
+ { 12672, 0x0060 },
+ { 12674, 0x0601 },
+ { 12676, 0x0061 },
+ { 12678, 0x0601 },
+ { 12680, 0x0050 },
+ { 12682, 0x0300 },
+ { 12684, 0x0001 },
+ { 12686, 0x0304 },
+ { 12688, 0x0040 },
+ { 12690, 0x000f },
+ { 12692, 0x0001 },
+ { 12695, 0x0100 },
+};
+
+struct fll_config {
+ int src;
+ int in;
+ int out;
+};
+
+struct wm8995_priv {
+ struct regmap *regmap;
+ int sysclk[2];
+ int mclk[2];
+ int aifclk[2];
+ struct fll_config fll[2], fll_suspend[2];
+ struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES];
+ struct notifier_block disable_nb[WM8995_NUM_SUPPLIES];
+ struct snd_soc_codec *codec;
+};
+
+/*
+ * We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define WM8995_REGULATOR_EVENT(n) \
+static int wm8995_regulator_event_##n(struct notifier_block *nb, \
+ unsigned long event, void *data) \
+{ \
+ struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \
+ disable_nb[n]); \
+ if (event & REGULATOR_EVENT_DISABLE) { \
+ regcache_mark_dirty(wm8995->regmap); \
+ } \
+ return 0; \
+}
+
+WM8995_REGULATOR_EVENT(0)
+WM8995_REGULATOR_EVENT(1)
+WM8995_REGULATOR_EVENT(2)
+WM8995_REGULATOR_EVENT(3)
+WM8995_REGULATOR_EVENT(4)
+WM8995_REGULATOR_EVENT(5)
+WM8995_REGULATOR_EVENT(6)
+WM8995_REGULATOR_EVENT(7)
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 150, 0);
+
+static const char *in1l_text[] = {
+ "Differential", "Single-ended IN1LN", "Single-ended IN1LP"
+};
+
+static SOC_ENUM_SINGLE_DECL(in1l_enum, WM8995_LEFT_LINE_INPUT_CONTROL,
+ 2, in1l_text);
+
+static const char *in1r_text[] = {
+ "Differential", "Single-ended IN1RN", "Single-ended IN1RP"
+};
+
+static SOC_ENUM_SINGLE_DECL(in1r_enum, WM8995_LEFT_LINE_INPUT_CONTROL,
+ 0, in1r_text);
+
+static const char *dmic_src_text[] = {
+ "DMICDAT1", "DMICDAT2", "DMICDAT3"
+};
+
+static SOC_ENUM_SINGLE_DECL(dmic_src1_enum, WM8995_POWER_MANAGEMENT_5,
+ 8, dmic_src_text);
+static SOC_ENUM_SINGLE_DECL(dmic_src2_enum, WM8995_POWER_MANAGEMENT_5,
+ 6, dmic_src_text);
+
+static const struct snd_kcontrol_new wm8995_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC1 Volume", WM8995_DAC1_LEFT_VOLUME,
+ WM8995_DAC1_RIGHT_VOLUME, 0, 96, 0, digital_tlv),
+ SOC_DOUBLE_R("DAC1 Switch", WM8995_DAC1_LEFT_VOLUME,
+ WM8995_DAC1_RIGHT_VOLUME, 9, 1, 1),
+
+ SOC_DOUBLE_R_TLV("DAC2 Volume", WM8995_DAC2_LEFT_VOLUME,
+ WM8995_DAC2_RIGHT_VOLUME, 0, 96, 0, digital_tlv),
+ SOC_DOUBLE_R("DAC2 Switch", WM8995_DAC2_LEFT_VOLUME,
+ WM8995_DAC2_RIGHT_VOLUME, 9, 1, 1),
+
+ SOC_DOUBLE_R_TLV("AIF1DAC1 Volume", WM8995_AIF1_DAC1_LEFT_VOLUME,
+ WM8995_AIF1_DAC1_RIGHT_VOLUME, 0, 96, 0, digital_tlv),
+ SOC_DOUBLE_R_TLV("AIF1DAC2 Volume", WM8995_AIF1_DAC2_LEFT_VOLUME,
+ WM8995_AIF1_DAC2_RIGHT_VOLUME, 0, 96, 0, digital_tlv),
+ SOC_DOUBLE_R_TLV("AIF2DAC Volume", WM8995_AIF2_DAC_LEFT_VOLUME,
+ WM8995_AIF2_DAC_RIGHT_VOLUME, 0, 96, 0, digital_tlv),
+
+ SOC_DOUBLE_R_TLV("IN1LR Volume", WM8995_LEFT_LINE_INPUT_1_VOLUME,
+ WM8995_RIGHT_LINE_INPUT_1_VOLUME, 0, 31, 0, in1lr_pga_tlv),
+
+ SOC_SINGLE_TLV("IN1L Boost", WM8995_LEFT_LINE_INPUT_CONTROL,
+ 4, 3, 0, in1l_boost_tlv),
+
+ SOC_ENUM("IN1L Mode", in1l_enum),
+ SOC_ENUM("IN1R Mode", in1r_enum),
+
+ SOC_ENUM("DMIC1 SRC", dmic_src1_enum),
+ SOC_ENUM("DMIC2 SRC", dmic_src2_enum),
+
+ SOC_DOUBLE_TLV("DAC1 Sidetone Volume", WM8995_DAC1_MIXER_VOLUMES, 0, 5,
+ 24, 0, sidetone_tlv),
+ SOC_DOUBLE_TLV("DAC2 Sidetone Volume", WM8995_DAC2_MIXER_VOLUMES, 0, 5,
+ 24, 0, sidetone_tlv),
+
+ SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8995_AIF1_ADC1_LEFT_VOLUME,
+ WM8995_AIF1_ADC1_RIGHT_VOLUME, 0, 96, 0, digital_tlv),
+ SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8995_AIF1_ADC2_LEFT_VOLUME,
+ WM8995_AIF1_ADC2_RIGHT_VOLUME, 0, 96, 0, digital_tlv),
+ SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8995_AIF2_ADC_LEFT_VOLUME,
+ WM8995_AIF2_ADC_RIGHT_VOLUME, 0, 96, 0, digital_tlv)
+};
+
+static void wm8995_update_class_w(struct snd_soc_codec *codec)
+{
+ int enable = 1;
+ int source = 0; /* GCC flow analysis can't track enable */
+ int reg, reg_r;
+
+ /* We also need the same setting for L/R and only one path */
+ reg = snd_soc_read(codec, WM8995_DAC1_LEFT_MIXER_ROUTING);
+ switch (reg) {
+ case WM8995_AIF2DACL_TO_DAC1L:
+ dev_dbg(codec->dev, "Class W source AIF2DAC\n");
+ source = 2 << WM8995_CP_DYN_SRC_SEL_SHIFT;
+ break;
+ case WM8995_AIF1DAC2L_TO_DAC1L:
+ dev_dbg(codec->dev, "Class W source AIF1DAC2\n");
+ source = 1 << WM8995_CP_DYN_SRC_SEL_SHIFT;
+ break;
+ case WM8995_AIF1DAC1L_TO_DAC1L:
+ dev_dbg(codec->dev, "Class W source AIF1DAC1\n");
+ source = 0 << WM8995_CP_DYN_SRC_SEL_SHIFT;
+ break;
+ default:
+ dev_dbg(codec->dev, "DAC mixer setting: %x\n", reg);
+ enable = 0;
+ break;
+ }
+
+ reg_r = snd_soc_read(codec, WM8995_DAC1_RIGHT_MIXER_ROUTING);
+ if (reg_r != reg) {
+ dev_dbg(codec->dev, "Left and right DAC mixers different\n");
+ enable = 0;
+ }
+
+ if (enable) {
+ dev_dbg(codec->dev, "Class W enabled\n");
+ snd_soc_update_bits(codec, WM8995_CLASS_W_1,
+ WM8995_CP_DYN_PWR_MASK |
+ WM8995_CP_DYN_SRC_SEL_MASK,
+ source | WM8995_CP_DYN_PWR);
+ } else {
+ dev_dbg(codec->dev, "Class W disabled\n");
+ snd_soc_update_bits(codec, WM8995_CLASS_W_1,
+ WM8995_CP_DYN_PWR_MASK, 0);
+ }
+}
+
+static int check_clk_sys(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ unsigned int reg;
+ const char *clk;
+
+ reg = snd_soc_read(codec, WM8995_CLOCKING_1);
+ /* Check what we're currently using for CLK_SYS */
+ if (reg & WM8995_SYSCLK_SRC)
+ clk = "AIF2CLK";
+ else
+ clk = "AIF1CLK";
+ return !strcmp(source->name, clk);
+}
+
+static int wm8995_put_class_w(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ int ret;
+
+ ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+ wm8995_update_class_w(codec);
+ return ret;
+}
+
+static int hp_supply_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:
+ /* Enable the headphone amp */
+ snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
+ WM8995_HPOUT1L_ENA_MASK |
+ WM8995_HPOUT1R_ENA_MASK,
+ WM8995_HPOUT1L_ENA |
+ WM8995_HPOUT1R_ENA);
+
+ /* Enable the second stage */
+ snd_soc_update_bits(codec, WM8995_ANALOGUE_HP_1,
+ WM8995_HPOUT1L_DLY_MASK |
+ WM8995_HPOUT1R_DLY_MASK,
+ WM8995_HPOUT1L_DLY |
+ WM8995_HPOUT1R_DLY);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, WM8995_CHARGE_PUMP_1,
+ WM8995_CP_ENA_MASK, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static void dc_servo_cmd(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int val, unsigned int mask)
+{
+ int timeout = 10;
+
+ dev_dbg(codec->dev, "%s: reg = %#x, val = %#x, mask = %#x\n",
+ __func__, reg, val, mask);
+
+ snd_soc_write(codec, reg, val);
+ while (timeout--) {
+ msleep(10);
+ val = snd_soc_read(codec, WM8995_DC_SERVO_READBACK_0);
+ if ((val & mask) == mask)
+ return;
+ }
+
+ dev_err(codec->dev, "Timed out waiting for DC Servo\n");
+}
+
+static int hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, WM8995_ANALOGUE_HP_1);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, WM8995_CHARGE_PUMP_1,
+ WM8995_CP_ENA_MASK, WM8995_CP_ENA);
+
+ msleep(5);
+
+ snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
+ WM8995_HPOUT1L_ENA_MASK |
+ WM8995_HPOUT1R_ENA_MASK,
+ WM8995_HPOUT1L_ENA | WM8995_HPOUT1R_ENA);
+
+ udelay(20);
+
+ reg |= WM8995_HPOUT1L_DLY | WM8995_HPOUT1R_DLY;
+ snd_soc_write(codec, WM8995_ANALOGUE_HP_1, reg);
+
+ snd_soc_write(codec, WM8995_DC_SERVO_1, WM8995_DCS_ENA_CHAN_0 |
+ WM8995_DCS_ENA_CHAN_1);
+
+ dc_servo_cmd(codec, WM8995_DC_SERVO_2,
+ WM8995_DCS_TRIG_STARTUP_0 |
+ WM8995_DCS_TRIG_STARTUP_1,
+ WM8995_DCS_TRIG_DAC_WR_0 |
+ WM8995_DCS_TRIG_DAC_WR_1);
+
+ reg |= WM8995_HPOUT1R_OUTP | WM8995_HPOUT1R_RMV_SHORT |
+ WM8995_HPOUT1L_OUTP | WM8995_HPOUT1L_RMV_SHORT;
+ snd_soc_write(codec, WM8995_ANALOGUE_HP_1, reg);
+
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, WM8995_ANALOGUE_HP_1,
+ WM8995_HPOUT1L_OUTP_MASK |
+ WM8995_HPOUT1R_OUTP_MASK |
+ WM8995_HPOUT1L_RMV_SHORT_MASK |
+ WM8995_HPOUT1R_RMV_SHORT_MASK, 0);
+
+ snd_soc_update_bits(codec, WM8995_ANALOGUE_HP_1,
+ WM8995_HPOUT1L_DLY_MASK |
+ WM8995_HPOUT1R_DLY_MASK, 0);
+
+ snd_soc_write(codec, WM8995_DC_SERVO_1, 0);
+
+ snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
+ WM8995_HPOUT1L_ENA_MASK |
+ WM8995_HPOUT1R_ENA_MASK,
+ 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
+{
+ struct wm8995_priv *wm8995;
+ int rate;
+ int reg1 = 0;
+ int offset;
+
+ wm8995 = snd_soc_codec_get_drvdata(codec);
+
+ if (aif)
+ offset = 4;
+ else
+ offset = 0;
+
+ switch (wm8995->sysclk[aif]) {
+ case WM8995_SYSCLK_MCLK1:
+ rate = wm8995->mclk[0];
+ break;
+ case WM8995_SYSCLK_MCLK2:
+ reg1 |= 0x8;
+ rate = wm8995->mclk[1];
+ break;
+ case WM8995_SYSCLK_FLL1:
+ reg1 |= 0x10;
+ rate = wm8995->fll[0].out;
+ break;
+ case WM8995_SYSCLK_FLL2:
+ reg1 |= 0x18;
+ rate = wm8995->fll[1].out;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate >= 13500000) {
+ rate /= 2;
+ reg1 |= WM8995_AIF1CLK_DIV;
+
+ dev_dbg(codec->dev, "Dividing AIF%d clock to %dHz\n",
+ aif + 1, rate);
+ }
+
+ wm8995->aifclk[aif] = rate;
+
+ snd_soc_update_bits(codec, WM8995_AIF1_CLOCKING_1 + offset,
+ WM8995_AIF1CLK_SRC_MASK | WM8995_AIF1CLK_DIV_MASK,
+ reg1);
+ return 0;
+}
+
+static int configure_clock(struct snd_soc_codec *codec)
+{
+ struct wm8995_priv *wm8995;
+ int change, new;
+
+ wm8995 = snd_soc_codec_get_drvdata(codec);
+
+ /* Bring up the AIF clocks first */
+ configure_aif_clock(codec, 0);
+ configure_aif_clock(codec, 1);
+
+ /*
+ * Then switch CLK_SYS over to the higher of them; a change
+ * can only happen as a result of a clocking change which can
+ * only be made outside of DAPM so we can safely redo the
+ * clocking.
+ */
+
+ /* If they're equal it doesn't matter which is used */
+ if (wm8995->aifclk[0] == wm8995->aifclk[1])
+ return 0;
+
+ if (wm8995->aifclk[0] < wm8995->aifclk[1])
+ new = WM8995_SYSCLK_SRC;
+ else
+ new = 0;
+
+ change = snd_soc_update_bits(codec, WM8995_CLOCKING_1,
+ WM8995_SYSCLK_SRC_MASK, new);
+ if (!change)
+ return 0;
+
+ snd_soc_dapm_sync(&codec->dapm);
+
+ return 0;
+}
+
+static int clk_sys_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:
+ return configure_clock(codec);
+
+ case SND_SOC_DAPM_POST_PMD:
+ configure_clock(codec);
+ break;
+ }
+
+ return 0;
+}
+
+static const char *sidetone_text[] = {
+ "ADC/DMIC1", "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_DECL(sidetone1_enum, WM8995_SIDETONE, 0, sidetone_text);
+
+static const struct snd_kcontrol_new sidetone1_mux =
+ SOC_DAPM_ENUM("Left Sidetone Mux", sidetone1_enum);
+
+static SOC_ENUM_SINGLE_DECL(sidetone2_enum, WM8995_SIDETONE, 1, sidetone_text);
+
+static const struct snd_kcontrol_new sidetone2_mux =
+ SOC_DAPM_ENUM("Right Sidetone Mux", sidetone2_enum);
+
+static const struct snd_kcontrol_new aif1adc1l_mix[] = {
+ SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8995_AIF1_ADC1_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+ SOC_DAPM_SINGLE("AIF2 Switch", WM8995_AIF1_ADC1_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif1adc1r_mix[] = {
+ SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8995_AIF1_ADC1_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+ SOC_DAPM_SINGLE("AIF2 Switch", WM8995_AIF1_ADC1_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif1adc2l_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", WM8995_AIF1_ADC2_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+ SOC_DAPM_SINGLE("AIF2 Switch", WM8995_AIF1_ADC2_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif1adc2r_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", WM8995_AIF1_ADC2_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+ SOC_DAPM_SINGLE("AIF2 Switch", WM8995_AIF1_ADC2_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1l_mix[] = {
+ WM8995_CLASS_W_SWITCH("Right Sidetone Switch", WM8995_DAC1_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+ WM8995_CLASS_W_SWITCH("Left Sidetone Switch", WM8995_DAC1_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+ WM8995_CLASS_W_SWITCH("AIF2 Switch", WM8995_DAC1_LEFT_MIXER_ROUTING,
+ 2, 1, 0),
+ WM8995_CLASS_W_SWITCH("AIF1.2 Switch", WM8995_DAC1_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+ WM8995_CLASS_W_SWITCH("AIF1.1 Switch", WM8995_DAC1_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1r_mix[] = {
+ WM8995_CLASS_W_SWITCH("Right Sidetone Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+ WM8995_CLASS_W_SWITCH("Left Sidetone Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+ WM8995_CLASS_W_SWITCH("AIF2 Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING,
+ 2, 1, 0),
+ WM8995_CLASS_W_SWITCH("AIF1.2 Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+ WM8995_CLASS_W_SWITCH("AIF1.1 Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif2dac2l_mix[] = {
+ SOC_DAPM_SINGLE("Right Sidetone Switch", WM8995_DAC2_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+ SOC_DAPM_SINGLE("Left Sidetone Switch", WM8995_DAC2_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+ SOC_DAPM_SINGLE("AIF2 Switch", WM8995_DAC2_LEFT_MIXER_ROUTING,
+ 2, 1, 0),
+ SOC_DAPM_SINGLE("AIF1.2 Switch", WM8995_DAC2_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8995_DAC2_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif2dac2r_mix[] = {
+ SOC_DAPM_SINGLE("Right Sidetone Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+ SOC_DAPM_SINGLE("Left Sidetone Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+ SOC_DAPM_SINGLE("AIF2 Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING,
+ 2, 1, 0),
+ SOC_DAPM_SINGLE("AIF1.2 Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new in1l_pga =
+ SOC_DAPM_SINGLE("IN1L Switch", WM8995_POWER_MANAGEMENT_2, 5, 1, 0);
+
+static const struct snd_kcontrol_new in1r_pga =
+ SOC_DAPM_SINGLE("IN1R Switch", WM8995_POWER_MANAGEMENT_2, 4, 1, 0);
+
+static const char *adc_mux_text[] = {
+ "ADC",
+ "DMIC",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(adc_enum, adc_mux_text);
+
+static const struct snd_kcontrol_new adcl_mux =
+ SOC_DAPM_ENUM("ADCL Mux", adc_enum);
+
+static const struct snd_kcontrol_new adcr_mux =
+ SOC_DAPM_ENUM("ADCR Mux", adc_enum);
+
+static const char *spk_src_text[] = {
+ "DAC1L", "DAC1R", "DAC2L", "DAC2R"
+};
+
+static SOC_ENUM_SINGLE_DECL(spk1l_src_enum, WM8995_LEFT_PDM_SPEAKER_1,
+ 0, spk_src_text);
+static SOC_ENUM_SINGLE_DECL(spk1r_src_enum, WM8995_RIGHT_PDM_SPEAKER_1,
+ 0, spk_src_text);
+static SOC_ENUM_SINGLE_DECL(spk2l_src_enum, WM8995_LEFT_PDM_SPEAKER_2,
+ 0, spk_src_text);
+static SOC_ENUM_SINGLE_DECL(spk2r_src_enum, WM8995_RIGHT_PDM_SPEAKER_2,
+ 0, spk_src_text);
+
+static const struct snd_kcontrol_new spk1l_mux =
+ SOC_DAPM_ENUM("SPK1L SRC", spk1l_src_enum);
+static const struct snd_kcontrol_new spk1r_mux =
+ SOC_DAPM_ENUM("SPK1R SRC", spk1r_src_enum);
+static const struct snd_kcontrol_new spk2l_mux =
+ SOC_DAPM_ENUM("SPK2L SRC", spk2l_src_enum);
+static const struct snd_kcontrol_new spk2r_mux =
+ SOC_DAPM_ENUM("SPK2R SRC", spk2r_src_enum);
+
+static const struct snd_soc_dapm_widget wm8995_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC1DAT"),
+ SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+ SND_SOC_DAPM_INPUT("IN1L"),
+ SND_SOC_DAPM_INPUT("IN1R"),
+
+ SND_SOC_DAPM_MIXER("IN1L PGA", SND_SOC_NOPM, 0, 0,
+ &in1l_pga, 1),
+ SND_SOC_DAPM_MIXER("IN1R PGA", SND_SOC_NOPM, 0, 0,
+ &in1r_pga, 1),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", WM8995_POWER_MANAGEMENT_1, 8, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", WM8995_POWER_MANAGEMENT_1, 9, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8995_AIF1_CLOCKING_1, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8995_AIF2_CLOCKING_1, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DSP1CLK", WM8995_CLOCKING_1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DSP2CLK", WM8995_CLOCKING_1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SYSDSPCLK", WM8995_CLOCKING_1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", "AIF1 Capture", 0,
+ WM8995_POWER_MANAGEMENT_3, 9, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", "AIF1 Capture", 0,
+ WM8995_POWER_MANAGEMENT_3, 8, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1ADCDAT", "AIF1 Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", "AIF1 Capture",
+ 0, WM8995_POWER_MANAGEMENT_3, 11, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", "AIF1 Capture",
+ 0, WM8995_POWER_MANAGEMENT_3, 10, 0),
+
+ SND_SOC_DAPM_MUX("ADCL Mux", SND_SOC_NOPM, 1, 0, &adcl_mux),
+ SND_SOC_DAPM_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0, &adcr_mux),
+
+ SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8995_POWER_MANAGEMENT_3, 5, 0),
+ SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8995_POWER_MANAGEMENT_3, 4, 0),
+ SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8995_POWER_MANAGEMENT_3, 3, 0),
+ SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8995_POWER_MANAGEMENT_3, 2, 0),
+
+ SND_SOC_DAPM_ADC("ADCL", NULL, WM8995_POWER_MANAGEMENT_3, 1, 0),
+ SND_SOC_DAPM_ADC("ADCR", NULL, WM8995_POWER_MANAGEMENT_3, 0, 0),
+
+ SND_SOC_DAPM_MIXER("AIF1ADC1L Mixer", SND_SOC_NOPM, 0, 0,
+ aif1adc1l_mix, ARRAY_SIZE(aif1adc1l_mix)),
+ SND_SOC_DAPM_MIXER("AIF1ADC1R Mixer", SND_SOC_NOPM, 0, 0,
+ aif1adc1r_mix, ARRAY_SIZE(aif1adc1r_mix)),
+ SND_SOC_DAPM_MIXER("AIF1ADC2L Mixer", SND_SOC_NOPM, 0, 0,
+ aif1adc2l_mix, ARRAY_SIZE(aif1adc2l_mix)),
+ SND_SOC_DAPM_MIXER("AIF1ADC2R Mixer", SND_SOC_NOPM, 0, 0,
+ aif1adc2r_mix, ARRAY_SIZE(aif1adc2r_mix)),
+
+ SND_SOC_DAPM_AIF_IN("AIF1DAC1L", NULL, 0, WM8995_POWER_MANAGEMENT_4,
+ 9, 0),
+ SND_SOC_DAPM_AIF_IN("AIF1DAC1R", NULL, 0, WM8995_POWER_MANAGEMENT_4,
+ 8, 0),
+ SND_SOC_DAPM_AIF_IN("AIF1DACDAT", "AIF1 Playback", 0, SND_SOC_NOPM,
+ 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("AIF1DAC2L", NULL, 0, WM8995_POWER_MANAGEMENT_4,
+ 11, 0),
+ SND_SOC_DAPM_AIF_IN("AIF1DAC2R", NULL, 0, WM8995_POWER_MANAGEMENT_4,
+ 10, 0),
+
+ SND_SOC_DAPM_MIXER("AIF2DAC2L Mixer", SND_SOC_NOPM, 0, 0,
+ aif2dac2l_mix, ARRAY_SIZE(aif2dac2l_mix)),
+ SND_SOC_DAPM_MIXER("AIF2DAC2R Mixer", SND_SOC_NOPM, 0, 0,
+ aif2dac2r_mix, ARRAY_SIZE(aif2dac2r_mix)),
+
+ SND_SOC_DAPM_DAC("DAC2L", NULL, WM8995_POWER_MANAGEMENT_4, 3, 0),
+ SND_SOC_DAPM_DAC("DAC2R", NULL, WM8995_POWER_MANAGEMENT_4, 2, 0),
+ SND_SOC_DAPM_DAC("DAC1L", NULL, WM8995_POWER_MANAGEMENT_4, 1, 0),
+ SND_SOC_DAPM_DAC("DAC1R", NULL, WM8995_POWER_MANAGEMENT_4, 0, 0),
+
+ SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0, dac1l_mix,
+ ARRAY_SIZE(dac1l_mix)),
+ SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0, dac1r_mix,
+ ARRAY_SIZE(dac1r_mix)),
+
+ SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &sidetone1_mux),
+ SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &sidetone2_mux),
+
+ SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0,
+ hp_supply_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX("SPK1L Driver", WM8995_LEFT_PDM_SPEAKER_1,
+ 4, 0, &spk1l_mux),
+ SND_SOC_DAPM_MUX("SPK1R Driver", WM8995_RIGHT_PDM_SPEAKER_1,
+ 4, 0, &spk1r_mux),
+ SND_SOC_DAPM_MUX("SPK2L Driver", WM8995_LEFT_PDM_SPEAKER_2,
+ 4, 0, &spk2l_mux),
+ SND_SOC_DAPM_MUX("SPK2R Driver", WM8995_RIGHT_PDM_SPEAKER_2,
+ 4, 0, &spk2r_mux),
+
+ SND_SOC_DAPM_SUPPLY("LDO2", WM8995_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HP1L"),
+ SND_SOC_DAPM_OUTPUT("HP1R"),
+ SND_SOC_DAPM_OUTPUT("SPK1L"),
+ SND_SOC_DAPM_OUTPUT("SPK1R"),
+ SND_SOC_DAPM_OUTPUT("SPK2L"),
+ SND_SOC_DAPM_OUTPUT("SPK2R")
+};
+
+static const struct snd_soc_dapm_route wm8995_intercon[] = {
+ { "CLK_SYS", NULL, "AIF1CLK", check_clk_sys },
+ { "CLK_SYS", NULL, "AIF2CLK", check_clk_sys },
+
+ { "DSP1CLK", NULL, "CLK_SYS" },
+ { "DSP2CLK", NULL, "CLK_SYS" },
+ { "SYSDSPCLK", NULL, "CLK_SYS" },
+
+ { "AIF1ADC1L", NULL, "AIF1CLK" },
+ { "AIF1ADC1L", NULL, "DSP1CLK" },
+ { "AIF1ADC1R", NULL, "AIF1CLK" },
+ { "AIF1ADC1R", NULL, "DSP1CLK" },
+ { "AIF1ADC1R", NULL, "SYSDSPCLK" },
+
+ { "AIF1ADC2L", NULL, "AIF1CLK" },
+ { "AIF1ADC2L", NULL, "DSP1CLK" },
+ { "AIF1ADC2R", NULL, "AIF1CLK" },
+ { "AIF1ADC2R", NULL, "DSP1CLK" },
+ { "AIF1ADC2R", NULL, "SYSDSPCLK" },
+
+ { "DMIC1L", NULL, "DMIC1DAT" },
+ { "DMIC1L", NULL, "CLK_SYS" },
+ { "DMIC1R", NULL, "DMIC1DAT" },
+ { "DMIC1R", NULL, "CLK_SYS" },
+ { "DMIC2L", NULL, "DMIC2DAT" },
+ { "DMIC2L", NULL, "CLK_SYS" },
+ { "DMIC2R", NULL, "DMIC2DAT" },
+ { "DMIC2R", NULL, "CLK_SYS" },
+
+ { "ADCL", NULL, "AIF1CLK" },
+ { "ADCL", NULL, "DSP1CLK" },
+ { "ADCL", NULL, "SYSDSPCLK" },
+
+ { "ADCR", NULL, "AIF1CLK" },
+ { "ADCR", NULL, "DSP1CLK" },
+ { "ADCR", NULL, "SYSDSPCLK" },
+
+ { "IN1L PGA", "IN1L Switch", "IN1L" },
+ { "IN1R PGA", "IN1R Switch", "IN1R" },
+ { "IN1L PGA", NULL, "LDO2" },
+ { "IN1R PGA", NULL, "LDO2" },
+
+ { "ADCL", NULL, "IN1L PGA" },
+ { "ADCR", NULL, "IN1R PGA" },
+
+ { "ADCL Mux", "ADC", "ADCL" },
+ { "ADCL Mux", "DMIC", "DMIC1L" },
+ { "ADCR Mux", "ADC", "ADCR" },
+ { "ADCR Mux", "DMIC", "DMIC1R" },
+
+ /* AIF1 outputs */
+ { "AIF1ADC1L", NULL, "AIF1ADC1L Mixer" },
+ { "AIF1ADC1L Mixer", "ADC/DMIC Switch", "ADCL Mux" },
+
+ { "AIF1ADC1R", NULL, "AIF1ADC1R Mixer" },
+ { "AIF1ADC1R Mixer", "ADC/DMIC Switch", "ADCR Mux" },
+
+ { "AIF1ADC2L", NULL, "AIF1ADC2L Mixer" },
+ { "AIF1ADC2L Mixer", "DMIC Switch", "DMIC2L" },
+
+ { "AIF1ADC2R", NULL, "AIF1ADC2R Mixer" },
+ { "AIF1ADC2R Mixer", "DMIC Switch", "DMIC2R" },
+
+ /* Sidetone */
+ { "Left Sidetone", "ADC/DMIC1", "AIF1ADC1L" },
+ { "Left Sidetone", "DMIC2", "AIF1ADC2L" },
+ { "Right Sidetone", "ADC/DMIC1", "AIF1ADC1R" },
+ { "Right Sidetone", "DMIC2", "AIF1ADC2R" },
+
+ { "AIF1DAC1L", NULL, "AIF1CLK" },
+ { "AIF1DAC1L", NULL, "DSP1CLK" },
+ { "AIF1DAC1R", NULL, "AIF1CLK" },
+ { "AIF1DAC1R", NULL, "DSP1CLK" },
+ { "AIF1DAC1R", NULL, "SYSDSPCLK" },
+
+ { "AIF1DAC2L", NULL, "AIF1CLK" },
+ { "AIF1DAC2L", NULL, "DSP1CLK" },
+ { "AIF1DAC2R", NULL, "AIF1CLK" },
+ { "AIF1DAC2R", NULL, "DSP1CLK" },
+ { "AIF1DAC2R", NULL, "SYSDSPCLK" },
+
+ { "DAC1L", NULL, "AIF1CLK" },
+ { "DAC1L", NULL, "DSP1CLK" },
+ { "DAC1L", NULL, "SYSDSPCLK" },
+
+ { "DAC1R", NULL, "AIF1CLK" },
+ { "DAC1R", NULL, "DSP1CLK" },
+ { "DAC1R", NULL, "SYSDSPCLK" },
+
+ { "AIF1DAC1L", NULL, "AIF1DACDAT" },
+ { "AIF1DAC1R", NULL, "AIF1DACDAT" },
+ { "AIF1DAC2L", NULL, "AIF1DACDAT" },
+ { "AIF1DAC2R", NULL, "AIF1DACDAT" },
+
+ /* DAC1 inputs */
+ { "DAC1L", NULL, "DAC1L Mixer" },
+ { "DAC1L Mixer", "AIF1.1 Switch", "AIF1DAC1L" },
+ { "DAC1L Mixer", "AIF1.2 Switch", "AIF1DAC2L" },
+ { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+ { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+ { "DAC1R", NULL, "DAC1R Mixer" },
+ { "DAC1R Mixer", "AIF1.1 Switch", "AIF1DAC1R" },
+ { "DAC1R Mixer", "AIF1.2 Switch", "AIF1DAC2R" },
+ { "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+ { "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+ /* DAC2/AIF2 outputs */
+ { "DAC2L", NULL, "AIF2DAC2L Mixer" },
+ { "AIF2DAC2L Mixer", "AIF1.2 Switch", "AIF1DAC2L" },
+ { "AIF2DAC2L Mixer", "AIF1.1 Switch", "AIF1DAC1L" },
+
+ { "DAC2R", NULL, "AIF2DAC2R Mixer" },
+ { "AIF2DAC2R Mixer", "AIF1.2 Switch", "AIF1DAC2R" },
+ { "AIF2DAC2R Mixer", "AIF1.1 Switch", "AIF1DAC1R" },
+
+ /* Output stages */
+ { "Headphone PGA", NULL, "DAC1L" },
+ { "Headphone PGA", NULL, "DAC1R" },
+
+ { "Headphone PGA", NULL, "DAC2L" },
+ { "Headphone PGA", NULL, "DAC2R" },
+
+ { "Headphone PGA", NULL, "Headphone Supply" },
+ { "Headphone PGA", NULL, "CLK_SYS" },
+ { "Headphone PGA", NULL, "LDO2" },
+
+ { "HP1L", NULL, "Headphone PGA" },
+ { "HP1R", NULL, "Headphone PGA" },
+
+ { "SPK1L Driver", "DAC1L", "DAC1L" },
+ { "SPK1L Driver", "DAC1R", "DAC1R" },
+ { "SPK1L Driver", "DAC2L", "DAC2L" },
+ { "SPK1L Driver", "DAC2R", "DAC2R" },
+ { "SPK1L Driver", NULL, "CLK_SYS" },
+
+ { "SPK1R Driver", "DAC1L", "DAC1L" },
+ { "SPK1R Driver", "DAC1R", "DAC1R" },
+ { "SPK1R Driver", "DAC2L", "DAC2L" },
+ { "SPK1R Driver", "DAC2R", "DAC2R" },
+ { "SPK1R Driver", NULL, "CLK_SYS" },
+
+ { "SPK2L Driver", "DAC1L", "DAC1L" },
+ { "SPK2L Driver", "DAC1R", "DAC1R" },
+ { "SPK2L Driver", "DAC2L", "DAC2L" },
+ { "SPK2L Driver", "DAC2R", "DAC2R" },
+ { "SPK2L Driver", NULL, "CLK_SYS" },
+
+ { "SPK2R Driver", "DAC1L", "DAC1L" },
+ { "SPK2R Driver", "DAC1R", "DAC1R" },
+ { "SPK2R Driver", "DAC2L", "DAC2L" },
+ { "SPK2R Driver", "DAC2R", "DAC2R" },
+ { "SPK2R Driver", NULL, "CLK_SYS" },
+
+ { "SPK1L", NULL, "SPK1L Driver" },
+ { "SPK1R", NULL, "SPK1R Driver" },
+ { "SPK2L", NULL, "SPK2L Driver" },
+ { "SPK2R", NULL, "SPK2R Driver" }
+};
+
+static bool wm8995_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8995_SOFTWARE_RESET:
+ case WM8995_POWER_MANAGEMENT_1:
+ case WM8995_POWER_MANAGEMENT_2:
+ case WM8995_POWER_MANAGEMENT_3:
+ case WM8995_POWER_MANAGEMENT_4:
+ case WM8995_POWER_MANAGEMENT_5:
+ case WM8995_LEFT_LINE_INPUT_1_VOLUME:
+ case WM8995_RIGHT_LINE_INPUT_1_VOLUME:
+ case WM8995_LEFT_LINE_INPUT_CONTROL:
+ case WM8995_DAC1_LEFT_VOLUME:
+ case WM8995_DAC1_RIGHT_VOLUME:
+ case WM8995_DAC2_LEFT_VOLUME:
+ case WM8995_DAC2_RIGHT_VOLUME:
+ case WM8995_OUTPUT_VOLUME_ZC_1:
+ case WM8995_MICBIAS_1:
+ case WM8995_MICBIAS_2:
+ case WM8995_LDO_1:
+ case WM8995_LDO_2:
+ case WM8995_ACCESSORY_DETECT_MODE1:
+ case WM8995_ACCESSORY_DETECT_MODE2:
+ case WM8995_HEADPHONE_DETECT1:
+ case WM8995_HEADPHONE_DETECT2:
+ case WM8995_MIC_DETECT_1:
+ case WM8995_MIC_DETECT_2:
+ case WM8995_CHARGE_PUMP_1:
+ case WM8995_CLASS_W_1:
+ case WM8995_DC_SERVO_1:
+ case WM8995_DC_SERVO_2:
+ case WM8995_DC_SERVO_3:
+ case WM8995_DC_SERVO_5:
+ case WM8995_DC_SERVO_6:
+ case WM8995_DC_SERVO_7:
+ case WM8995_DC_SERVO_READBACK_0:
+ case WM8995_ANALOGUE_HP_1:
+ case WM8995_ANALOGUE_HP_2:
+ case WM8995_CHIP_REVISION:
+ case WM8995_CONTROL_INTERFACE_1:
+ case WM8995_CONTROL_INTERFACE_2:
+ case WM8995_WRITE_SEQUENCER_CTRL_1:
+ case WM8995_WRITE_SEQUENCER_CTRL_2:
+ case WM8995_AIF1_CLOCKING_1:
+ case WM8995_AIF1_CLOCKING_2:
+ case WM8995_AIF2_CLOCKING_1:
+ case WM8995_AIF2_CLOCKING_2:
+ case WM8995_CLOCKING_1:
+ case WM8995_CLOCKING_2:
+ case WM8995_AIF1_RATE:
+ case WM8995_AIF2_RATE:
+ case WM8995_RATE_STATUS:
+ case WM8995_FLL1_CONTROL_1:
+ case WM8995_FLL1_CONTROL_2:
+ case WM8995_FLL1_CONTROL_3:
+ case WM8995_FLL1_CONTROL_4:
+ case WM8995_FLL1_CONTROL_5:
+ case WM8995_FLL2_CONTROL_1:
+ case WM8995_FLL2_CONTROL_2:
+ case WM8995_FLL2_CONTROL_3:
+ case WM8995_FLL2_CONTROL_4:
+ case WM8995_FLL2_CONTROL_5:
+ case WM8995_AIF1_CONTROL_1:
+ case WM8995_AIF1_CONTROL_2:
+ case WM8995_AIF1_MASTER_SLAVE:
+ case WM8995_AIF1_BCLK:
+ case WM8995_AIF1ADC_LRCLK:
+ case WM8995_AIF1DAC_LRCLK:
+ case WM8995_AIF1DAC_DATA:
+ case WM8995_AIF1ADC_DATA:
+ case WM8995_AIF2_CONTROL_1:
+ case WM8995_AIF2_CONTROL_2:
+ case WM8995_AIF2_MASTER_SLAVE:
+ case WM8995_AIF2_BCLK:
+ case WM8995_AIF2ADC_LRCLK:
+ case WM8995_AIF2DAC_LRCLK:
+ case WM8995_AIF2DAC_DATA:
+ case WM8995_AIF2ADC_DATA:
+ case WM8995_AIF1_ADC1_LEFT_VOLUME:
+ case WM8995_AIF1_ADC1_RIGHT_VOLUME:
+ case WM8995_AIF1_DAC1_LEFT_VOLUME:
+ case WM8995_AIF1_DAC1_RIGHT_VOLUME:
+ case WM8995_AIF1_ADC2_LEFT_VOLUME:
+ case WM8995_AIF1_ADC2_RIGHT_VOLUME:
+ case WM8995_AIF1_DAC2_LEFT_VOLUME:
+ case WM8995_AIF1_DAC2_RIGHT_VOLUME:
+ case WM8995_AIF1_ADC1_FILTERS:
+ case WM8995_AIF1_ADC2_FILTERS:
+ case WM8995_AIF1_DAC1_FILTERS_1:
+ case WM8995_AIF1_DAC1_FILTERS_2:
+ case WM8995_AIF1_DAC2_FILTERS_1:
+ case WM8995_AIF1_DAC2_FILTERS_2:
+ case WM8995_AIF1_DRC1_1:
+ case WM8995_AIF1_DRC1_2:
+ case WM8995_AIF1_DRC1_3:
+ case WM8995_AIF1_DRC1_4:
+ case WM8995_AIF1_DRC1_5:
+ case WM8995_AIF1_DRC2_1:
+ case WM8995_AIF1_DRC2_2:
+ case WM8995_AIF1_DRC2_3:
+ case WM8995_AIF1_DRC2_4:
+ case WM8995_AIF1_DRC2_5:
+ case WM8995_AIF1_DAC1_EQ_GAINS_1:
+ case WM8995_AIF1_DAC1_EQ_GAINS_2:
+ case WM8995_AIF1_DAC1_EQ_BAND_1_A:
+ case WM8995_AIF1_DAC1_EQ_BAND_1_B:
+ case WM8995_AIF1_DAC1_EQ_BAND_1_PG:
+ case WM8995_AIF1_DAC1_EQ_BAND_2_A:
+ case WM8995_AIF1_DAC1_EQ_BAND_2_B:
+ case WM8995_AIF1_DAC1_EQ_BAND_2_C:
+ case WM8995_AIF1_DAC1_EQ_BAND_2_PG:
+ case WM8995_AIF1_DAC1_EQ_BAND_3_A:
+ case WM8995_AIF1_DAC1_EQ_BAND_3_B:
+ case WM8995_AIF1_DAC1_EQ_BAND_3_C:
+ case WM8995_AIF1_DAC1_EQ_BAND_3_PG:
+ case WM8995_AIF1_DAC1_EQ_BAND_4_A:
+ case WM8995_AIF1_DAC1_EQ_BAND_4_B:
+ case WM8995_AIF1_DAC1_EQ_BAND_4_C:
+ case WM8995_AIF1_DAC1_EQ_BAND_4_PG:
+ case WM8995_AIF1_DAC1_EQ_BAND_5_A:
+ case WM8995_AIF1_DAC1_EQ_BAND_5_B:
+ case WM8995_AIF1_DAC1_EQ_BAND_5_PG:
+ case WM8995_AIF1_DAC2_EQ_GAINS_1:
+ case WM8995_AIF1_DAC2_EQ_GAINS_2:
+ case WM8995_AIF1_DAC2_EQ_BAND_1_A:
+ case WM8995_AIF1_DAC2_EQ_BAND_1_B:
+ case WM8995_AIF1_DAC2_EQ_BAND_1_PG:
+ case WM8995_AIF1_DAC2_EQ_BAND_2_A:
+ case WM8995_AIF1_DAC2_EQ_BAND_2_B:
+ case WM8995_AIF1_DAC2_EQ_BAND_2_C:
+ case WM8995_AIF1_DAC2_EQ_BAND_2_PG:
+ case WM8995_AIF1_DAC2_EQ_BAND_3_A:
+ case WM8995_AIF1_DAC2_EQ_BAND_3_B:
+ case WM8995_AIF1_DAC2_EQ_BAND_3_C:
+ case WM8995_AIF1_DAC2_EQ_BAND_3_PG:
+ case WM8995_AIF1_DAC2_EQ_BAND_4_A:
+ case WM8995_AIF1_DAC2_EQ_BAND_4_B:
+ case WM8995_AIF1_DAC2_EQ_BAND_4_C:
+ case WM8995_AIF1_DAC2_EQ_BAND_4_PG:
+ case WM8995_AIF1_DAC2_EQ_BAND_5_A:
+ case WM8995_AIF1_DAC2_EQ_BAND_5_B:
+ case WM8995_AIF1_DAC2_EQ_BAND_5_PG:
+ case WM8995_AIF2_ADC_LEFT_VOLUME:
+ case WM8995_AIF2_ADC_RIGHT_VOLUME:
+ case WM8995_AIF2_DAC_LEFT_VOLUME:
+ case WM8995_AIF2_DAC_RIGHT_VOLUME:
+ case WM8995_AIF2_ADC_FILTERS:
+ case WM8995_AIF2_DAC_FILTERS_1:
+ case WM8995_AIF2_DAC_FILTERS_2:
+ case WM8995_AIF2_DRC_1:
+ case WM8995_AIF2_DRC_2:
+ case WM8995_AIF2_DRC_3:
+ case WM8995_AIF2_DRC_4:
+ case WM8995_AIF2_DRC_5:
+ case WM8995_AIF2_EQ_GAINS_1:
+ case WM8995_AIF2_EQ_GAINS_2:
+ case WM8995_AIF2_EQ_BAND_1_A:
+ case WM8995_AIF2_EQ_BAND_1_B:
+ case WM8995_AIF2_EQ_BAND_1_PG:
+ case WM8995_AIF2_EQ_BAND_2_A:
+ case WM8995_AIF2_EQ_BAND_2_B:
+ case WM8995_AIF2_EQ_BAND_2_C:
+ case WM8995_AIF2_EQ_BAND_2_PG:
+ case WM8995_AIF2_EQ_BAND_3_A:
+ case WM8995_AIF2_EQ_BAND_3_B:
+ case WM8995_AIF2_EQ_BAND_3_C:
+ case WM8995_AIF2_EQ_BAND_3_PG:
+ case WM8995_AIF2_EQ_BAND_4_A:
+ case WM8995_AIF2_EQ_BAND_4_B:
+ case WM8995_AIF2_EQ_BAND_4_C:
+ case WM8995_AIF2_EQ_BAND_4_PG:
+ case WM8995_AIF2_EQ_BAND_5_A:
+ case WM8995_AIF2_EQ_BAND_5_B:
+ case WM8995_AIF2_EQ_BAND_5_PG:
+ case WM8995_DAC1_MIXER_VOLUMES:
+ case WM8995_DAC1_LEFT_MIXER_ROUTING:
+ case WM8995_DAC1_RIGHT_MIXER_ROUTING:
+ case WM8995_DAC2_MIXER_VOLUMES:
+ case WM8995_DAC2_LEFT_MIXER_ROUTING:
+ case WM8995_DAC2_RIGHT_MIXER_ROUTING:
+ case WM8995_AIF1_ADC1_LEFT_MIXER_ROUTING:
+ case WM8995_AIF1_ADC1_RIGHT_MIXER_ROUTING:
+ case WM8995_AIF1_ADC2_LEFT_MIXER_ROUTING:
+ case WM8995_AIF1_ADC2_RIGHT_MIXER_ROUTING:
+ case WM8995_DAC_SOFTMUTE:
+ case WM8995_OVERSAMPLING:
+ case WM8995_SIDETONE:
+ case WM8995_GPIO_1:
+ case WM8995_GPIO_2:
+ case WM8995_GPIO_3:
+ case WM8995_GPIO_4:
+ case WM8995_GPIO_5:
+ case WM8995_GPIO_6:
+ case WM8995_GPIO_7:
+ case WM8995_GPIO_8:
+ case WM8995_GPIO_9:
+ case WM8995_GPIO_10:
+ case WM8995_GPIO_11:
+ case WM8995_GPIO_12:
+ case WM8995_GPIO_13:
+ case WM8995_GPIO_14:
+ case WM8995_PULL_CONTROL_1:
+ case WM8995_PULL_CONTROL_2:
+ case WM8995_INTERRUPT_STATUS_1:
+ case WM8995_INTERRUPT_STATUS_2:
+ case WM8995_INTERRUPT_RAW_STATUS_2:
+ case WM8995_INTERRUPT_STATUS_1_MASK:
+ case WM8995_INTERRUPT_STATUS_2_MASK:
+ case WM8995_INTERRUPT_CONTROL:
+ case WM8995_LEFT_PDM_SPEAKER_1:
+ case WM8995_RIGHT_PDM_SPEAKER_1:
+ case WM8995_PDM_SPEAKER_1_MUTE_SEQUENCE:
+ case WM8995_LEFT_PDM_SPEAKER_2:
+ case WM8995_RIGHT_PDM_SPEAKER_2:
+ case WM8995_PDM_SPEAKER_2_MUTE_SEQUENCE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wm8995_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8995_SOFTWARE_RESET:
+ case WM8995_DC_SERVO_READBACK_0:
+ case WM8995_INTERRUPT_STATUS_1:
+ case WM8995_INTERRUPT_STATUS_2:
+ case WM8995_INTERRUPT_CONTROL:
+ case WM8995_ACCESSORY_DETECT_MODE1:
+ case WM8995_ACCESSORY_DETECT_MODE2:
+ case WM8995_HEADPHONE_DETECT1:
+ case WM8995_HEADPHONE_DETECT2:
+ case WM8995_RATE_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int wm8995_aif_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int mute_reg;
+
+ switch (dai->id) {
+ case 0:
+ mute_reg = WM8995_AIF1_DAC1_FILTERS_1;
+ break;
+ case 1:
+ mute_reg = WM8995_AIF2_DAC_FILTERS_1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, mute_reg, WM8995_AIF1DAC1_MUTE_MASK,
+ !!mute << WM8995_AIF1DAC1_MUTE_SHIFT);
+ return 0;
+}
+
+static int wm8995_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec;
+ int master;
+ int aif;
+
+ codec = dai->codec;
+
+ master = 0;
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ master = WM8995_AIF1_MSTR;
+ break;
+ default:
+ dev_err(dai->dev, "Unknown master/slave configuration\n");
+ return -EINVAL;
+ }
+
+ aif = 0;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_B:
+ aif |= WM8995_AIF1_LRCLK_INV;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif |= (0x3 << WM8995_AIF1_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aif |= (0x2 << WM8995_AIF1_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif |= (0x1 << WM8995_AIF1_FMT_SHIFT);
+ break;
+ default:
+ dev_err(dai->dev, "Unknown dai format\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif |= WM8995_AIF1_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif |= WM8995_AIF1_BCLK_INV | WM8995_AIF1_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif |= WM8995_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif |= WM8995_AIF1_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8995_AIF1_CONTROL_1,
+ WM8995_AIF1_BCLK_INV_MASK |
+ WM8995_AIF1_LRCLK_INV_MASK |
+ WM8995_AIF1_FMT_MASK, aif);
+ snd_soc_update_bits(codec, WM8995_AIF1_MASTER_SLAVE,
+ WM8995_AIF1_MSTR_MASK, master);
+ return 0;
+}
+
+static const int srs[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100,
+ 48000, 88200, 96000
+};
+
+static const int fs_ratios[] = {
+ -1 /* reserved */,
+ 128, 192, 256, 384, 512, 768, 1024, 1408, 1536
+};
+
+static const int bclk_divs[] = {
+ 10, 15, 20, 30, 40, 55, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480
+};
+
+static int wm8995_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec;
+ struct wm8995_priv *wm8995;
+ int aif1_reg;
+ int bclk_reg;
+ int lrclk_reg;
+ int rate_reg;
+ int bclk_rate;
+ int aif1;
+ int lrclk, bclk;
+ int i, rate_val, best, best_val, cur_val;
+
+ codec = dai->codec;
+ wm8995 = snd_soc_codec_get_drvdata(codec);
+
+ switch (dai->id) {
+ case 0:
+ aif1_reg = WM8995_AIF1_CONTROL_1;
+ bclk_reg = WM8995_AIF1_BCLK;
+ rate_reg = WM8995_AIF1_RATE;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK /* ||
+ wm8995->lrclk_shared[0] */) {
+ lrclk_reg = WM8995_AIF1DAC_LRCLK;
+ } else {
+ lrclk_reg = WM8995_AIF1ADC_LRCLK;
+ dev_dbg(codec->dev, "AIF1 using split LRCLK\n");
+ }
+ break;
+ case 1:
+ aif1_reg = WM8995_AIF2_CONTROL_1;
+ bclk_reg = WM8995_AIF2_BCLK;
+ rate_reg = WM8995_AIF2_RATE;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK /* ||
+ wm8995->lrclk_shared[1] */) {
+ lrclk_reg = WM8995_AIF2DAC_LRCLK;
+ } else {
+ lrclk_reg = WM8995_AIF2ADC_LRCLK;
+ dev_dbg(codec->dev, "AIF2 using split LRCLK\n");
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0)
+ return bclk_rate;
+
+ aif1 = 0;
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ aif1 |= (0x1 << WM8995_AIF1_WL_SHIFT);
+ break;
+ case 24:
+ aif1 |= (0x2 << WM8995_AIF1_WL_SHIFT);
+ break;
+ case 32:
+ aif1 |= (0x3 << WM8995_AIF1_WL_SHIFT);
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported word length %u\n",
+ params_width(params));
+ return -EINVAL;
+ }
+
+ /* try to find a suitable sample rate */
+ for (i = 0; i < ARRAY_SIZE(srs); ++i)
+ if (srs[i] == params_rate(params))
+ break;
+ if (i == ARRAY_SIZE(srs)) {
+ dev_err(dai->dev, "Sample rate %d is not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ rate_val = i << WM8995_AIF1_SR_SHIFT;
+
+ dev_dbg(dai->dev, "Sample rate is %dHz\n", srs[i]);
+ dev_dbg(dai->dev, "AIF%dCLK is %dHz, target BCLK %dHz\n",
+ dai->id + 1, wm8995->aifclk[dai->id], bclk_rate);
+
+ /* AIFCLK/fs ratio; look for a close match in either direction */
+ best = 1;
+ best_val = abs((fs_ratios[1] * params_rate(params))
+ - wm8995->aifclk[dai->id]);
+ for (i = 2; i < ARRAY_SIZE(fs_ratios); i++) {
+ cur_val = abs((fs_ratios[i] * params_rate(params))
+ - wm8995->aifclk[dai->id]);
+ if (cur_val >= best_val)
+ continue;
+ best = i;
+ best_val = cur_val;
+ }
+ rate_val |= best;
+
+ dev_dbg(dai->dev, "Selected AIF%dCLK/fs = %d\n",
+ dai->id + 1, fs_ratios[best]);
+
+ /*
+ * We may not get quite the right frequency if using
+ * approximate clocks so look for the closest match that is
+ * higher than the target (we need to ensure that there enough
+ * BCLKs to clock out the samples).
+ */
+ best = 0;
+ bclk = 0;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ cur_val = (wm8995->aifclk[dai->id] * 10 / bclk_divs[i]) - bclk_rate;
+ if (cur_val < 0) /* BCLK table is sorted */
+ break;
+ best = i;
+ }
+ bclk |= best << WM8995_AIF1_BCLK_DIV_SHIFT;
+
+ bclk_rate = wm8995->aifclk[dai->id] * 10 / bclk_divs[best];
+ dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
+ bclk_divs[best], bclk_rate);
+
+ lrclk = bclk_rate / params_rate(params);
+ dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
+ lrclk, bclk_rate / lrclk);
+
+ snd_soc_update_bits(codec, aif1_reg,
+ WM8995_AIF1_WL_MASK, aif1);
+ snd_soc_update_bits(codec, bclk_reg,
+ WM8995_AIF1_BCLK_DIV_MASK, bclk);
+ snd_soc_update_bits(codec, lrclk_reg,
+ WM8995_AIF1DAC_RATE_MASK, lrclk);
+ snd_soc_update_bits(codec, rate_reg,
+ WM8995_AIF1_SR_MASK |
+ WM8995_AIF1CLK_RATE_MASK, rate_val);
+ return 0;
+}
+
+static int wm8995_set_tristate(struct snd_soc_dai *codec_dai, int tristate)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int reg, val, mask;
+
+ switch (codec_dai->id) {
+ case 0:
+ reg = WM8995_AIF1_MASTER_SLAVE;
+ mask = WM8995_AIF1_TRI;
+ break;
+ case 1:
+ reg = WM8995_AIF2_MASTER_SLAVE;
+ mask = WM8995_AIF2_TRI;
+ break;
+ case 2:
+ reg = WM8995_POWER_MANAGEMENT_5;
+ mask = WM8995_AIF3_TRI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (tristate)
+ val = mask;
+ else
+ val = 0;
+
+ return snd_soc_update_bits(codec, reg, mask, val);
+}
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+struct fll_div {
+ u16 outdiv;
+ u16 n;
+ u16 k;
+ u16 clk_ref_div;
+ u16 fll_fratio;
+};
+
+static int wm8995_get_fll_config(struct fll_div *fll,
+ int freq_in, int freq_out)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+ pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out);
+
+ /* Scale the input frequency down to <= 13.5MHz */
+ fll->clk_ref_div = 0;
+ while (freq_in > 13500000) {
+ fll->clk_ref_div++;
+ freq_in /= 2;
+
+ if (fll->clk_ref_div > 3)
+ return -EINVAL;
+ }
+ pr_debug("CLK_REF_DIV=%d, Fref=%dHz\n", fll->clk_ref_div, freq_in);
+
+ /* Scale the output to give 90MHz<=Fvco<=100MHz */
+ fll->outdiv = 3;
+ while (freq_out * (fll->outdiv + 1) < 90000000) {
+ fll->outdiv++;
+ if (fll->outdiv > 63)
+ return -EINVAL;
+ }
+ freq_out *= fll->outdiv + 1;
+ pr_debug("OUTDIV=%d, Fvco=%dHz\n", fll->outdiv, freq_out);
+
+ if (freq_in > 1000000) {
+ fll->fll_fratio = 0;
+ } else if (freq_in > 256000) {
+ fll->fll_fratio = 1;
+ freq_in *= 2;
+ } else if (freq_in > 128000) {
+ fll->fll_fratio = 2;
+ freq_in *= 4;
+ } else if (freq_in > 64000) {
+ fll->fll_fratio = 3;
+ freq_in *= 8;
+ } else {
+ fll->fll_fratio = 4;
+ freq_in *= 16;
+ }
+ pr_debug("FLL_FRATIO=%d, Fref=%dHz\n", fll->fll_fratio, freq_in);
+
+ /* Now, calculate N.K */
+ Ndiv = freq_out / freq_in;
+
+ fll->n = Ndiv;
+ Nmod = freq_out % freq_in;
+ pr_debug("Nmod=%d\n", Nmod);
+
+ /* Calculate fractional part - scale up so we can round. */
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, freq_in);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ fll->k = K / 10;
+
+ pr_debug("N=%x K=%x\n", fll->n, fll->k);
+
+ return 0;
+}
+
+static int wm8995_set_fll(struct snd_soc_dai *dai, int id,
+ int src, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct snd_soc_codec *codec;
+ struct wm8995_priv *wm8995;
+ int reg_offset, ret;
+ struct fll_div fll;
+ u16 reg, aif1, aif2;
+
+ codec = dai->codec;
+ wm8995 = snd_soc_codec_get_drvdata(codec);
+
+ aif1 = snd_soc_read(codec, WM8995_AIF1_CLOCKING_1)
+ & WM8995_AIF1CLK_ENA;
+
+ aif2 = snd_soc_read(codec, WM8995_AIF2_CLOCKING_1)
+ & WM8995_AIF2CLK_ENA;
+
+ switch (id) {
+ case WM8995_FLL1:
+ reg_offset = 0;
+ id = 0;
+ break;
+ case WM8995_FLL2:
+ reg_offset = 0x20;
+ id = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (src) {
+ case 0:
+ /* Allow no source specification when stopping */
+ if (freq_out)
+ return -EINVAL;
+ break;
+ case WM8995_FLL_SRC_MCLK1:
+ case WM8995_FLL_SRC_MCLK2:
+ case WM8995_FLL_SRC_LRCLK:
+ case WM8995_FLL_SRC_BCLK:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Are we changing anything? */
+ if (wm8995->fll[id].src == src &&
+ wm8995->fll[id].in == freq_in && wm8995->fll[id].out == freq_out)
+ return 0;
+
+ /* If we're stopping the FLL redo the old config - no
+ * registers will actually be written but we avoid GCC flow
+ * analysis bugs spewing warnings.
+ */
+ if (freq_out)
+ ret = wm8995_get_fll_config(&fll, freq_in, freq_out);
+ else
+ ret = wm8995_get_fll_config(&fll, wm8995->fll[id].in,
+ wm8995->fll[id].out);
+ if (ret < 0)
+ return ret;
+
+ /* Gate the AIF clocks while we reclock */
+ snd_soc_update_bits(codec, WM8995_AIF1_CLOCKING_1,
+ WM8995_AIF1CLK_ENA_MASK, 0);
+ snd_soc_update_bits(codec, WM8995_AIF2_CLOCKING_1,
+ WM8995_AIF2CLK_ENA_MASK, 0);
+
+ /* We always need to disable the FLL while reconfiguring */
+ snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_1 + reg_offset,
+ WM8995_FLL1_ENA_MASK, 0);
+
+ reg = (fll.outdiv << WM8995_FLL1_OUTDIV_SHIFT) |
+ (fll.fll_fratio << WM8995_FLL1_FRATIO_SHIFT);
+ snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_2 + reg_offset,
+ WM8995_FLL1_OUTDIV_MASK |
+ WM8995_FLL1_FRATIO_MASK, reg);
+
+ snd_soc_write(codec, WM8995_FLL1_CONTROL_3 + reg_offset, fll.k);
+
+ snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_4 + reg_offset,
+ WM8995_FLL1_N_MASK,
+ fll.n << WM8995_FLL1_N_SHIFT);
+
+ snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_5 + reg_offset,
+ WM8995_FLL1_REFCLK_DIV_MASK |
+ WM8995_FLL1_REFCLK_SRC_MASK,
+ (fll.clk_ref_div << WM8995_FLL1_REFCLK_DIV_SHIFT) |
+ (src - 1));
+
+ if (freq_out)
+ snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_1 + reg_offset,
+ WM8995_FLL1_ENA_MASK, WM8995_FLL1_ENA);
+
+ wm8995->fll[id].in = freq_in;
+ wm8995->fll[id].out = freq_out;
+ wm8995->fll[id].src = src;
+
+ /* Enable any gated AIF clocks */
+ snd_soc_update_bits(codec, WM8995_AIF1_CLOCKING_1,
+ WM8995_AIF1CLK_ENA_MASK, aif1);
+ snd_soc_update_bits(codec, WM8995_AIF2_CLOCKING_1,
+ WM8995_AIF2CLK_ENA_MASK, aif2);
+
+ configure_clock(codec);
+
+ return 0;
+}
+
+static int wm8995_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec;
+ struct wm8995_priv *wm8995;
+
+ codec = dai->codec;
+ wm8995 = snd_soc_codec_get_drvdata(codec);
+
+ switch (dai->id) {
+ case 0:
+ case 1:
+ break;
+ default:
+ /* AIF3 shares clocking with AIF1/2 */
+ return -EINVAL;
+ }
+
+ switch (clk_id) {
+ case WM8995_SYSCLK_MCLK1:
+ wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK1;
+ wm8995->mclk[0] = freq;
+ dev_dbg(dai->dev, "AIF%d using MCLK1 at %uHz\n",
+ dai->id + 1, freq);
+ break;
+ case WM8995_SYSCLK_MCLK2:
+ wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK1;
+ wm8995->mclk[1] = freq;
+ dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n",
+ dai->id + 1, freq);
+ break;
+ case WM8995_SYSCLK_FLL1:
+ wm8995->sysclk[dai->id] = WM8995_SYSCLK_FLL1;
+ dev_dbg(dai->dev, "AIF%d using FLL1\n", dai->id + 1);
+ break;
+ case WM8995_SYSCLK_FLL2:
+ wm8995->sysclk[dai->id] = WM8995_SYSCLK_FLL2;
+ dev_dbg(dai->dev, "AIF%d using FLL2\n", dai->id + 1);
+ break;
+ case WM8995_SYSCLK_OPCLK:
+ default:
+ dev_err(dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ configure_clock(codec);
+
+ return 0;
+}
+
+static int wm8995_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8995_priv *wm8995;
+ int ret;
+
+ wm8995 = 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(wm8995->supplies),
+ wm8995->supplies);
+ if (ret)
+ return ret;
+
+ ret = regcache_sync(wm8995->regmap);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+
+ snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
+ WM8995_BG_ENA_MASK, WM8995_BG_ENA);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
+ WM8995_BG_ENA_MASK, 0);
+ regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies),
+ wm8995->supplies);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int wm8995_remove(struct snd_soc_codec *codec)
+{
+ struct wm8995_priv *wm8995;
+ int i;
+
+ wm8995 = snd_soc_codec_get_drvdata(codec);
+
+ for (i = 0; i < ARRAY_SIZE(wm8995->supplies); ++i)
+ regulator_unregister_notifier(wm8995->supplies[i].consumer,
+ &wm8995->disable_nb[i]);
+
+ regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
+ return 0;
+}
+
+static int wm8995_probe(struct snd_soc_codec *codec)
+{
+ struct wm8995_priv *wm8995;
+ int i;
+ int ret;
+
+ wm8995 = snd_soc_codec_get_drvdata(codec);
+ wm8995->codec = codec;
+
+ for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++)
+ wm8995->supplies[i].supply = wm8995_supply_names[i];
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8995->supplies),
+ wm8995->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ wm8995->disable_nb[0].notifier_call = wm8995_regulator_event_0;
+ wm8995->disable_nb[1].notifier_call = wm8995_regulator_event_1;
+ wm8995->disable_nb[2].notifier_call = wm8995_regulator_event_2;
+ wm8995->disable_nb[3].notifier_call = wm8995_regulator_event_3;
+ wm8995->disable_nb[4].notifier_call = wm8995_regulator_event_4;
+ wm8995->disable_nb[5].notifier_call = wm8995_regulator_event_5;
+ wm8995->disable_nb[6].notifier_call = wm8995_regulator_event_6;
+ wm8995->disable_nb[7].notifier_call = wm8995_regulator_event_7;
+
+ /* This should really be moved into the regulator core */
+ for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) {
+ ret = regulator_register_notifier(wm8995->supplies[i].consumer,
+ &wm8995->disable_nb[i]);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
+ wm8995->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_reg_get;
+ }
+
+ ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read device ID: %d\n", ret);
+ goto err_reg_enable;
+ }
+
+ if (ret != 0x8995) {
+ dev_err(codec->dev, "Invalid device ID: %#x\n", ret);
+ ret = -EINVAL;
+ goto err_reg_enable;
+ }
+
+ ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ goto err_reg_enable;
+ }
+
+ /* Latch volume updates (right only; we always do left then right). */
+ snd_soc_update_bits(codec, WM8995_AIF1_DAC1_RIGHT_VOLUME,
+ WM8995_AIF1DAC1_VU_MASK, WM8995_AIF1DAC1_VU);
+ snd_soc_update_bits(codec, WM8995_AIF1_DAC2_RIGHT_VOLUME,
+ WM8995_AIF1DAC2_VU_MASK, WM8995_AIF1DAC2_VU);
+ snd_soc_update_bits(codec, WM8995_AIF2_DAC_RIGHT_VOLUME,
+ WM8995_AIF2DAC_VU_MASK, WM8995_AIF2DAC_VU);
+ snd_soc_update_bits(codec, WM8995_AIF1_ADC1_RIGHT_VOLUME,
+ WM8995_AIF1ADC1_VU_MASK, WM8995_AIF1ADC1_VU);
+ snd_soc_update_bits(codec, WM8995_AIF1_ADC2_RIGHT_VOLUME,
+ WM8995_AIF1ADC2_VU_MASK, WM8995_AIF1ADC2_VU);
+ snd_soc_update_bits(codec, WM8995_AIF2_ADC_RIGHT_VOLUME,
+ WM8995_AIF2ADC_VU_MASK, WM8995_AIF1ADC2_VU);
+ snd_soc_update_bits(codec, WM8995_DAC1_RIGHT_VOLUME,
+ WM8995_DAC1_VU_MASK, WM8995_DAC1_VU);
+ snd_soc_update_bits(codec, WM8995_DAC2_RIGHT_VOLUME,
+ WM8995_DAC2_VU_MASK, WM8995_DAC2_VU);
+ snd_soc_update_bits(codec, WM8995_RIGHT_LINE_INPUT_1_VOLUME,
+ WM8995_IN1_VU_MASK, WM8995_IN1_VU);
+
+ wm8995_update_class_w(codec);
+
+ return 0;
+
+err_reg_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
+err_reg_get:
+ regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
+ return ret;
+}
+
+#define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8995_aif1_dai_ops = {
+ .set_sysclk = wm8995_set_dai_sysclk,
+ .set_fmt = wm8995_set_dai_fmt,
+ .hw_params = wm8995_hw_params,
+ .digital_mute = wm8995_aif_mute,
+ .set_pll = wm8995_set_fll,
+ .set_tristate = wm8995_set_tristate,
+};
+
+static const struct snd_soc_dai_ops wm8995_aif2_dai_ops = {
+ .set_sysclk = wm8995_set_dai_sysclk,
+ .set_fmt = wm8995_set_dai_fmt,
+ .hw_params = wm8995_hw_params,
+ .digital_mute = wm8995_aif_mute,
+ .set_pll = wm8995_set_fll,
+ .set_tristate = wm8995_set_tristate,
+};
+
+static const struct snd_soc_dai_ops wm8995_aif3_dai_ops = {
+ .set_tristate = wm8995_set_tristate,
+};
+
+static struct snd_soc_dai_driver wm8995_dai[] = {
+ {
+ .name = "wm8995-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = WM8995_FORMATS
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = WM8995_FORMATS
+ },
+ .ops = &wm8995_aif1_dai_ops
+ },
+ {
+ .name = "wm8995-aif2",
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = WM8995_FORMATS
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = WM8995_FORMATS
+ },
+ .ops = &wm8995_aif2_dai_ops
+ },
+ {
+ .name = "wm8995-aif3",
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = WM8995_FORMATS
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = WM8995_FORMATS
+ },
+ .ops = &wm8995_aif3_dai_ops
+ }
+};
+
+static const struct snd_soc_codec_driver soc_codec_dev_wm8995 = {
+ .probe = wm8995_probe,
+ .remove = wm8995_remove,
+ .set_bias_level = wm8995_set_bias_level,
+ .idle_bias_off = true,
+
+ .controls = wm8995_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8995_snd_controls),
+ .dapm_widgets = wm8995_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8995_dapm_widgets),
+ .dapm_routes = wm8995_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8995_intercon),
+};
+
+static const struct regmap_config wm8995_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+
+ .max_register = WM8995_MAX_REGISTER,
+ .reg_defaults = wm8995_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8995_reg_defaults),
+ .volatile_reg = wm8995_volatile,
+ .readable_reg = wm8995_readable,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8995_spi_probe(struct spi_device *spi)
+{
+ struct wm8995_priv *wm8995;
+ int ret;
+
+ wm8995 = devm_kzalloc(&spi->dev, sizeof(*wm8995), GFP_KERNEL);
+ if (!wm8995)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, wm8995);
+
+ wm8995->regmap = devm_regmap_init_spi(spi, &wm8995_regmap);
+ if (IS_ERR(wm8995->regmap)) {
+ ret = PTR_ERR(wm8995->regmap);
+ dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm8995, wm8995_dai,
+ ARRAY_SIZE(wm8995_dai));
+ return ret;
+}
+
+static int wm8995_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver wm8995_spi_driver = {
+ .driver = {
+ .name = "wm8995",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8995_spi_probe,
+ .remove = wm8995_spi_remove
+};
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8995_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8995_priv *wm8995;
+ int ret;
+
+ wm8995 = devm_kzalloc(&i2c->dev, sizeof(*wm8995), GFP_KERNEL);
+ if (!wm8995)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm8995);
+
+ wm8995->regmap = devm_regmap_init_i2c(i2c, &wm8995_regmap);
+ if (IS_ERR(wm8995->regmap)) {
+ ret = PTR_ERR(wm8995->regmap);
+ dev_err(&i2c->dev, "Failed to register regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8995, wm8995_dai,
+ ARRAY_SIZE(wm8995_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+
+ return ret;
+}
+
+static int wm8995_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8995_i2c_id[] = {
+ {"wm8995", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, wm8995_i2c_id);
+
+static struct i2c_driver wm8995_i2c_driver = {
+ .driver = {
+ .name = "wm8995",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8995_i2c_probe,
+ .remove = wm8995_i2c_remove,
+ .id_table = wm8995_i2c_id
+};
+#endif
+
+static int __init wm8995_modinit(void)
+{
+ int ret = 0;
+
+#if IS_ENABLED(CONFIG_I2C)
+ ret = i2c_add_driver(&wm8995_i2c_driver);
+ if (ret) {
+ printk(KERN_ERR "Failed to register wm8995 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8995_spi_driver);
+ if (ret) {
+ printk(KERN_ERR "Failed to register wm8995 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+
+module_init(wm8995_modinit);
+
+static void __exit wm8995_exit(void)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_driver(&wm8995_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8995_spi_driver);
+#endif
+}
+
+module_exit(wm8995_exit);
+
+MODULE_DESCRIPTION("ASoC WM8995 driver");
+MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8995.h b/kernel/sound/soc/codecs/wm8995.h
new file mode 100644
index 000000000..508ad27fe
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8995.h
@@ -0,0 +1,4266 @@
+/*
+ * wm8995.h -- WM8995 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Wolfson Microelectronics plc
+ *
+ * Author: Dimitris Papastamos <dp@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 _WM8995_H
+#define _WM8995_H
+
+#include <asm/types.h>
+
+/*
+ * Register values.
+ */
+#define WM8995_SOFTWARE_RESET 0x00
+#define WM8995_POWER_MANAGEMENT_1 0x01
+#define WM8995_POWER_MANAGEMENT_2 0x02
+#define WM8995_POWER_MANAGEMENT_3 0x03
+#define WM8995_POWER_MANAGEMENT_4 0x04
+#define WM8995_POWER_MANAGEMENT_5 0x05
+#define WM8995_LEFT_LINE_INPUT_1_VOLUME 0x10
+#define WM8995_RIGHT_LINE_INPUT_1_VOLUME 0x11
+#define WM8995_LEFT_LINE_INPUT_CONTROL 0x12
+#define WM8995_DAC1_LEFT_VOLUME 0x18
+#define WM8995_DAC1_RIGHT_VOLUME 0x19
+#define WM8995_DAC2_LEFT_VOLUME 0x1A
+#define WM8995_DAC2_RIGHT_VOLUME 0x1B
+#define WM8995_OUTPUT_VOLUME_ZC_1 0x1C
+#define WM8995_MICBIAS_1 0x20
+#define WM8995_MICBIAS_2 0x21
+#define WM8995_LDO_1 0x28
+#define WM8995_LDO_2 0x29
+#define WM8995_ACCESSORY_DETECT_MODE1 0x30
+#define WM8995_ACCESSORY_DETECT_MODE2 0x31
+#define WM8995_HEADPHONE_DETECT1 0x34
+#define WM8995_HEADPHONE_DETECT2 0x35
+#define WM8995_MIC_DETECT_1 0x38
+#define WM8995_MIC_DETECT_2 0x39
+#define WM8995_CHARGE_PUMP_1 0x40
+#define WM8995_CLASS_W_1 0x45
+#define WM8995_DC_SERVO_1 0x50
+#define WM8995_DC_SERVO_2 0x51
+#define WM8995_DC_SERVO_3 0x52
+#define WM8995_DC_SERVO_5 0x54
+#define WM8995_DC_SERVO_6 0x55
+#define WM8995_DC_SERVO_7 0x56
+#define WM8995_DC_SERVO_READBACK_0 0x57
+#define WM8995_ANALOGUE_HP_1 0x60
+#define WM8995_ANALOGUE_HP_2 0x61
+#define WM8995_CHIP_REVISION 0x100
+#define WM8995_CONTROL_INTERFACE_1 0x101
+#define WM8995_CONTROL_INTERFACE_2 0x102
+#define WM8995_WRITE_SEQUENCER_CTRL_1 0x110
+#define WM8995_WRITE_SEQUENCER_CTRL_2 0x111
+#define WM8995_AIF1_CLOCKING_1 0x200
+#define WM8995_AIF1_CLOCKING_2 0x201
+#define WM8995_AIF2_CLOCKING_1 0x204
+#define WM8995_AIF2_CLOCKING_2 0x205
+#define WM8995_CLOCKING_1 0x208
+#define WM8995_CLOCKING_2 0x209
+#define WM8995_AIF1_RATE 0x210
+#define WM8995_AIF2_RATE 0x211
+#define WM8995_RATE_STATUS 0x212
+#define WM8995_FLL1_CONTROL_1 0x220
+#define WM8995_FLL1_CONTROL_2 0x221
+#define WM8995_FLL1_CONTROL_3 0x222
+#define WM8995_FLL1_CONTROL_4 0x223
+#define WM8995_FLL1_CONTROL_5 0x224
+#define WM8995_FLL2_CONTROL_1 0x240
+#define WM8995_FLL2_CONTROL_2 0x241
+#define WM8995_FLL2_CONTROL_3 0x242
+#define WM8995_FLL2_CONTROL_4 0x243
+#define WM8995_FLL2_CONTROL_5 0x244
+#define WM8995_AIF1_CONTROL_1 0x300
+#define WM8995_AIF1_CONTROL_2 0x301
+#define WM8995_AIF1_MASTER_SLAVE 0x302
+#define WM8995_AIF1_BCLK 0x303
+#define WM8995_AIF1ADC_LRCLK 0x304
+#define WM8995_AIF1DAC_LRCLK 0x305
+#define WM8995_AIF1DAC_DATA 0x306
+#define WM8995_AIF1ADC_DATA 0x307
+#define WM8995_AIF2_CONTROL_1 0x310
+#define WM8995_AIF2_CONTROL_2 0x311
+#define WM8995_AIF2_MASTER_SLAVE 0x312
+#define WM8995_AIF2_BCLK 0x313
+#define WM8995_AIF2ADC_LRCLK 0x314
+#define WM8995_AIF2DAC_LRCLK 0x315
+#define WM8995_AIF2DAC_DATA 0x316
+#define WM8995_AIF2ADC_DATA 0x317
+#define WM8995_AIF1_ADC1_LEFT_VOLUME 0x400
+#define WM8995_AIF1_ADC1_RIGHT_VOLUME 0x401
+#define WM8995_AIF1_DAC1_LEFT_VOLUME 0x402
+#define WM8995_AIF1_DAC1_RIGHT_VOLUME 0x403
+#define WM8995_AIF1_ADC2_LEFT_VOLUME 0x404
+#define WM8995_AIF1_ADC2_RIGHT_VOLUME 0x405
+#define WM8995_AIF1_DAC2_LEFT_VOLUME 0x406
+#define WM8995_AIF1_DAC2_RIGHT_VOLUME 0x407
+#define WM8995_AIF1_ADC1_FILTERS 0x410
+#define WM8995_AIF1_ADC2_FILTERS 0x411
+#define WM8995_AIF1_DAC1_FILTERS_1 0x420
+#define WM8995_AIF1_DAC1_FILTERS_2 0x421
+#define WM8995_AIF1_DAC2_FILTERS_1 0x422
+#define WM8995_AIF1_DAC2_FILTERS_2 0x423
+#define WM8995_AIF1_DRC1_1 0x440
+#define WM8995_AIF1_DRC1_2 0x441
+#define WM8995_AIF1_DRC1_3 0x442
+#define WM8995_AIF1_DRC1_4 0x443
+#define WM8995_AIF1_DRC1_5 0x444
+#define WM8995_AIF1_DRC2_1 0x450
+#define WM8995_AIF1_DRC2_2 0x451
+#define WM8995_AIF1_DRC2_3 0x452
+#define WM8995_AIF1_DRC2_4 0x453
+#define WM8995_AIF1_DRC2_5 0x454
+#define WM8995_AIF1_DAC1_EQ_GAINS_1 0x480
+#define WM8995_AIF1_DAC1_EQ_GAINS_2 0x481
+#define WM8995_AIF1_DAC1_EQ_BAND_1_A 0x482
+#define WM8995_AIF1_DAC1_EQ_BAND_1_B 0x483
+#define WM8995_AIF1_DAC1_EQ_BAND_1_PG 0x484
+#define WM8995_AIF1_DAC1_EQ_BAND_2_A 0x485
+#define WM8995_AIF1_DAC1_EQ_BAND_2_B 0x486
+#define WM8995_AIF1_DAC1_EQ_BAND_2_C 0x487
+#define WM8995_AIF1_DAC1_EQ_BAND_2_PG 0x488
+#define WM8995_AIF1_DAC1_EQ_BAND_3_A 0x489
+#define WM8995_AIF1_DAC1_EQ_BAND_3_B 0x48A
+#define WM8995_AIF1_DAC1_EQ_BAND_3_C 0x48B
+#define WM8995_AIF1_DAC1_EQ_BAND_3_PG 0x48C
+#define WM8995_AIF1_DAC1_EQ_BAND_4_A 0x48D
+#define WM8995_AIF1_DAC1_EQ_BAND_4_B 0x48E
+#define WM8995_AIF1_DAC1_EQ_BAND_4_C 0x48F
+#define WM8995_AIF1_DAC1_EQ_BAND_4_PG 0x490
+#define WM8995_AIF1_DAC1_EQ_BAND_5_A 0x491
+#define WM8995_AIF1_DAC1_EQ_BAND_5_B 0x492
+#define WM8995_AIF1_DAC1_EQ_BAND_5_PG 0x493
+#define WM8995_AIF1_DAC2_EQ_GAINS_1 0x4A0
+#define WM8995_AIF1_DAC2_EQ_GAINS_2 0x4A1
+#define WM8995_AIF1_DAC2_EQ_BAND_1_A 0x4A2
+#define WM8995_AIF1_DAC2_EQ_BAND_1_B 0x4A3
+#define WM8995_AIF1_DAC2_EQ_BAND_1_PG 0x4A4
+#define WM8995_AIF1_DAC2_EQ_BAND_2_A 0x4A5
+#define WM8995_AIF1_DAC2_EQ_BAND_2_B 0x4A6
+#define WM8995_AIF1_DAC2_EQ_BAND_2_C 0x4A7
+#define WM8995_AIF1_DAC2_EQ_BAND_2_PG 0x4A8
+#define WM8995_AIF1_DAC2_EQ_BAND_3_A 0x4A9
+#define WM8995_AIF1_DAC2_EQ_BAND_3_B 0x4AA
+#define WM8995_AIF1_DAC2_EQ_BAND_3_C 0x4AB
+#define WM8995_AIF1_DAC2_EQ_BAND_3_PG 0x4AC
+#define WM8995_AIF1_DAC2_EQ_BAND_4_A 0x4AD
+#define WM8995_AIF1_DAC2_EQ_BAND_4_B 0x4AE
+#define WM8995_AIF1_DAC2_EQ_BAND_4_C 0x4AF
+#define WM8995_AIF1_DAC2_EQ_BAND_4_PG 0x4B0
+#define WM8995_AIF1_DAC2_EQ_BAND_5_A 0x4B1
+#define WM8995_AIF1_DAC2_EQ_BAND_5_B 0x4B2
+#define WM8995_AIF1_DAC2_EQ_BAND_5_PG 0x4B3
+#define WM8995_AIF2_ADC_LEFT_VOLUME 0x500
+#define WM8995_AIF2_ADC_RIGHT_VOLUME 0x501
+#define WM8995_AIF2_DAC_LEFT_VOLUME 0x502
+#define WM8995_AIF2_DAC_RIGHT_VOLUME 0x503
+#define WM8995_AIF2_ADC_FILTERS 0x510
+#define WM8995_AIF2_DAC_FILTERS_1 0x520
+#define WM8995_AIF2_DAC_FILTERS_2 0x521
+#define WM8995_AIF2_DRC_1 0x540
+#define WM8995_AIF2_DRC_2 0x541
+#define WM8995_AIF2_DRC_3 0x542
+#define WM8995_AIF2_DRC_4 0x543
+#define WM8995_AIF2_DRC_5 0x544
+#define WM8995_AIF2_EQ_GAINS_1 0x580
+#define WM8995_AIF2_EQ_GAINS_2 0x581
+#define WM8995_AIF2_EQ_BAND_1_A 0x582
+#define WM8995_AIF2_EQ_BAND_1_B 0x583
+#define WM8995_AIF2_EQ_BAND_1_PG 0x584
+#define WM8995_AIF2_EQ_BAND_2_A 0x585
+#define WM8995_AIF2_EQ_BAND_2_B 0x586
+#define WM8995_AIF2_EQ_BAND_2_C 0x587
+#define WM8995_AIF2_EQ_BAND_2_PG 0x588
+#define WM8995_AIF2_EQ_BAND_3_A 0x589
+#define WM8995_AIF2_EQ_BAND_3_B 0x58A
+#define WM8995_AIF2_EQ_BAND_3_C 0x58B
+#define WM8995_AIF2_EQ_BAND_3_PG 0x58C
+#define WM8995_AIF2_EQ_BAND_4_A 0x58D
+#define WM8995_AIF2_EQ_BAND_4_B 0x58E
+#define WM8995_AIF2_EQ_BAND_4_C 0x58F
+#define WM8995_AIF2_EQ_BAND_4_PG 0x590
+#define WM8995_AIF2_EQ_BAND_5_A 0x591
+#define WM8995_AIF2_EQ_BAND_5_B 0x592
+#define WM8995_AIF2_EQ_BAND_5_PG 0x593
+#define WM8995_DAC1_MIXER_VOLUMES 0x600
+#define WM8995_DAC1_LEFT_MIXER_ROUTING 0x601
+#define WM8995_DAC1_RIGHT_MIXER_ROUTING 0x602
+#define WM8995_DAC2_MIXER_VOLUMES 0x603
+#define WM8995_DAC2_LEFT_MIXER_ROUTING 0x604
+#define WM8995_DAC2_RIGHT_MIXER_ROUTING 0x605
+#define WM8995_AIF1_ADC1_LEFT_MIXER_ROUTING 0x606
+#define WM8995_AIF1_ADC1_RIGHT_MIXER_ROUTING 0x607
+#define WM8995_AIF1_ADC2_LEFT_MIXER_ROUTING 0x608
+#define WM8995_AIF1_ADC2_RIGHT_MIXER_ROUTING 0x609
+#define WM8995_DAC_SOFTMUTE 0x610
+#define WM8995_OVERSAMPLING 0x620
+#define WM8995_SIDETONE 0x621
+#define WM8995_GPIO_1 0x700
+#define WM8995_GPIO_2 0x701
+#define WM8995_GPIO_3 0x702
+#define WM8995_GPIO_4 0x703
+#define WM8995_GPIO_5 0x704
+#define WM8995_GPIO_6 0x705
+#define WM8995_GPIO_7 0x706
+#define WM8995_GPIO_8 0x707
+#define WM8995_GPIO_9 0x708
+#define WM8995_GPIO_10 0x709
+#define WM8995_GPIO_11 0x70A
+#define WM8995_GPIO_12 0x70B
+#define WM8995_GPIO_13 0x70C
+#define WM8995_GPIO_14 0x70D
+#define WM8995_PULL_CONTROL_1 0x720
+#define WM8995_PULL_CONTROL_2 0x721
+#define WM8995_INTERRUPT_STATUS_1 0x730
+#define WM8995_INTERRUPT_STATUS_2 0x731
+#define WM8995_INTERRUPT_RAW_STATUS_2 0x732
+#define WM8995_INTERRUPT_STATUS_1_MASK 0x738
+#define WM8995_INTERRUPT_STATUS_2_MASK 0x739
+#define WM8995_INTERRUPT_CONTROL 0x740
+#define WM8995_LEFT_PDM_SPEAKER_1 0x800
+#define WM8995_RIGHT_PDM_SPEAKER_1 0x801
+#define WM8995_PDM_SPEAKER_1_MUTE_SEQUENCE 0x802
+#define WM8995_LEFT_PDM_SPEAKER_2 0x808
+#define WM8995_RIGHT_PDM_SPEAKER_2 0x809
+#define WM8995_PDM_SPEAKER_2_MUTE_SEQUENCE 0x80A
+#define WM8995_WRITE_SEQUENCER_0 0x3000
+#define WM8995_WRITE_SEQUENCER_1 0x3001
+#define WM8995_WRITE_SEQUENCER_2 0x3002
+#define WM8995_WRITE_SEQUENCER_3 0x3003
+#define WM8995_WRITE_SEQUENCER_4 0x3004
+#define WM8995_WRITE_SEQUENCER_5 0x3005
+#define WM8995_WRITE_SEQUENCER_6 0x3006
+#define WM8995_WRITE_SEQUENCER_7 0x3007
+#define WM8995_WRITE_SEQUENCER_8 0x3008
+#define WM8995_WRITE_SEQUENCER_9 0x3009
+#define WM8995_WRITE_SEQUENCER_10 0x300A
+#define WM8995_WRITE_SEQUENCER_11 0x300B
+#define WM8995_WRITE_SEQUENCER_12 0x300C
+#define WM8995_WRITE_SEQUENCER_13 0x300D
+#define WM8995_WRITE_SEQUENCER_14 0x300E
+#define WM8995_WRITE_SEQUENCER_15 0x300F
+#define WM8995_WRITE_SEQUENCER_16 0x3010
+#define WM8995_WRITE_SEQUENCER_17 0x3011
+#define WM8995_WRITE_SEQUENCER_18 0x3012
+#define WM8995_WRITE_SEQUENCER_19 0x3013
+#define WM8995_WRITE_SEQUENCER_20 0x3014
+#define WM8995_WRITE_SEQUENCER_21 0x3015
+#define WM8995_WRITE_SEQUENCER_22 0x3016
+#define WM8995_WRITE_SEQUENCER_23 0x3017
+#define WM8995_WRITE_SEQUENCER_24 0x3018
+#define WM8995_WRITE_SEQUENCER_25 0x3019
+#define WM8995_WRITE_SEQUENCER_26 0x301A
+#define WM8995_WRITE_SEQUENCER_27 0x301B
+#define WM8995_WRITE_SEQUENCER_28 0x301C
+#define WM8995_WRITE_SEQUENCER_29 0x301D
+#define WM8995_WRITE_SEQUENCER_30 0x301E
+#define WM8995_WRITE_SEQUENCER_31 0x301F
+#define WM8995_WRITE_SEQUENCER_32 0x3020
+#define WM8995_WRITE_SEQUENCER_33 0x3021
+#define WM8995_WRITE_SEQUENCER_34 0x3022
+#define WM8995_WRITE_SEQUENCER_35 0x3023
+#define WM8995_WRITE_SEQUENCER_36 0x3024
+#define WM8995_WRITE_SEQUENCER_37 0x3025
+#define WM8995_WRITE_SEQUENCER_38 0x3026
+#define WM8995_WRITE_SEQUENCER_39 0x3027
+#define WM8995_WRITE_SEQUENCER_40 0x3028
+#define WM8995_WRITE_SEQUENCER_41 0x3029
+#define WM8995_WRITE_SEQUENCER_42 0x302A
+#define WM8995_WRITE_SEQUENCER_43 0x302B
+#define WM8995_WRITE_SEQUENCER_44 0x302C
+#define WM8995_WRITE_SEQUENCER_45 0x302D
+#define WM8995_WRITE_SEQUENCER_46 0x302E
+#define WM8995_WRITE_SEQUENCER_47 0x302F
+#define WM8995_WRITE_SEQUENCER_48 0x3030
+#define WM8995_WRITE_SEQUENCER_49 0x3031
+#define WM8995_WRITE_SEQUENCER_50 0x3032
+#define WM8995_WRITE_SEQUENCER_51 0x3033
+#define WM8995_WRITE_SEQUENCER_52 0x3034
+#define WM8995_WRITE_SEQUENCER_53 0x3035
+#define WM8995_WRITE_SEQUENCER_54 0x3036
+#define WM8995_WRITE_SEQUENCER_55 0x3037
+#define WM8995_WRITE_SEQUENCER_56 0x3038
+#define WM8995_WRITE_SEQUENCER_57 0x3039
+#define WM8995_WRITE_SEQUENCER_58 0x303A
+#define WM8995_WRITE_SEQUENCER_59 0x303B
+#define WM8995_WRITE_SEQUENCER_60 0x303C
+#define WM8995_WRITE_SEQUENCER_61 0x303D
+#define WM8995_WRITE_SEQUENCER_62 0x303E
+#define WM8995_WRITE_SEQUENCER_63 0x303F
+#define WM8995_WRITE_SEQUENCER_64 0x3040
+#define WM8995_WRITE_SEQUENCER_65 0x3041
+#define WM8995_WRITE_SEQUENCER_66 0x3042
+#define WM8995_WRITE_SEQUENCER_67 0x3043
+#define WM8995_WRITE_SEQUENCER_68 0x3044
+#define WM8995_WRITE_SEQUENCER_69 0x3045
+#define WM8995_WRITE_SEQUENCER_70 0x3046
+#define WM8995_WRITE_SEQUENCER_71 0x3047
+#define WM8995_WRITE_SEQUENCER_72 0x3048
+#define WM8995_WRITE_SEQUENCER_73 0x3049
+#define WM8995_WRITE_SEQUENCER_74 0x304A
+#define WM8995_WRITE_SEQUENCER_75 0x304B
+#define WM8995_WRITE_SEQUENCER_76 0x304C
+#define WM8995_WRITE_SEQUENCER_77 0x304D
+#define WM8995_WRITE_SEQUENCER_78 0x304E
+#define WM8995_WRITE_SEQUENCER_79 0x304F
+#define WM8995_WRITE_SEQUENCER_80 0x3050
+#define WM8995_WRITE_SEQUENCER_81 0x3051
+#define WM8995_WRITE_SEQUENCER_82 0x3052
+#define WM8995_WRITE_SEQUENCER_83 0x3053
+#define WM8995_WRITE_SEQUENCER_84 0x3054
+#define WM8995_WRITE_SEQUENCER_85 0x3055
+#define WM8995_WRITE_SEQUENCER_86 0x3056
+#define WM8995_WRITE_SEQUENCER_87 0x3057
+#define WM8995_WRITE_SEQUENCER_88 0x3058
+#define WM8995_WRITE_SEQUENCER_89 0x3059
+#define WM8995_WRITE_SEQUENCER_90 0x305A
+#define WM8995_WRITE_SEQUENCER_91 0x305B
+#define WM8995_WRITE_SEQUENCER_92 0x305C
+#define WM8995_WRITE_SEQUENCER_93 0x305D
+#define WM8995_WRITE_SEQUENCER_94 0x305E
+#define WM8995_WRITE_SEQUENCER_95 0x305F
+#define WM8995_WRITE_SEQUENCER_96 0x3060
+#define WM8995_WRITE_SEQUENCER_97 0x3061
+#define WM8995_WRITE_SEQUENCER_98 0x3062
+#define WM8995_WRITE_SEQUENCER_99 0x3063
+#define WM8995_WRITE_SEQUENCER_100 0x3064
+#define WM8995_WRITE_SEQUENCER_101 0x3065
+#define WM8995_WRITE_SEQUENCER_102 0x3066
+#define WM8995_WRITE_SEQUENCER_103 0x3067
+#define WM8995_WRITE_SEQUENCER_104 0x3068
+#define WM8995_WRITE_SEQUENCER_105 0x3069
+#define WM8995_WRITE_SEQUENCER_106 0x306A
+#define WM8995_WRITE_SEQUENCER_107 0x306B
+#define WM8995_WRITE_SEQUENCER_108 0x306C
+#define WM8995_WRITE_SEQUENCER_109 0x306D
+#define WM8995_WRITE_SEQUENCER_110 0x306E
+#define WM8995_WRITE_SEQUENCER_111 0x306F
+#define WM8995_WRITE_SEQUENCER_112 0x3070
+#define WM8995_WRITE_SEQUENCER_113 0x3071
+#define WM8995_WRITE_SEQUENCER_114 0x3072
+#define WM8995_WRITE_SEQUENCER_115 0x3073
+#define WM8995_WRITE_SEQUENCER_116 0x3074
+#define WM8995_WRITE_SEQUENCER_117 0x3075
+#define WM8995_WRITE_SEQUENCER_118 0x3076
+#define WM8995_WRITE_SEQUENCER_119 0x3077
+#define WM8995_WRITE_SEQUENCER_120 0x3078
+#define WM8995_WRITE_SEQUENCER_121 0x3079
+#define WM8995_WRITE_SEQUENCER_122 0x307A
+#define WM8995_WRITE_SEQUENCER_123 0x307B
+#define WM8995_WRITE_SEQUENCER_124 0x307C
+#define WM8995_WRITE_SEQUENCER_125 0x307D
+#define WM8995_WRITE_SEQUENCER_126 0x307E
+#define WM8995_WRITE_SEQUENCER_127 0x307F
+#define WM8995_WRITE_SEQUENCER_128 0x3080
+#define WM8995_WRITE_SEQUENCER_129 0x3081
+#define WM8995_WRITE_SEQUENCER_130 0x3082
+#define WM8995_WRITE_SEQUENCER_131 0x3083
+#define WM8995_WRITE_SEQUENCER_132 0x3084
+#define WM8995_WRITE_SEQUENCER_133 0x3085
+#define WM8995_WRITE_SEQUENCER_134 0x3086
+#define WM8995_WRITE_SEQUENCER_135 0x3087
+#define WM8995_WRITE_SEQUENCER_136 0x3088
+#define WM8995_WRITE_SEQUENCER_137 0x3089
+#define WM8995_WRITE_SEQUENCER_138 0x308A
+#define WM8995_WRITE_SEQUENCER_139 0x308B
+#define WM8995_WRITE_SEQUENCER_140 0x308C
+#define WM8995_WRITE_SEQUENCER_141 0x308D
+#define WM8995_WRITE_SEQUENCER_142 0x308E
+#define WM8995_WRITE_SEQUENCER_143 0x308F
+#define WM8995_WRITE_SEQUENCER_144 0x3090
+#define WM8995_WRITE_SEQUENCER_145 0x3091
+#define WM8995_WRITE_SEQUENCER_146 0x3092
+#define WM8995_WRITE_SEQUENCER_147 0x3093
+#define WM8995_WRITE_SEQUENCER_148 0x3094
+#define WM8995_WRITE_SEQUENCER_149 0x3095
+#define WM8995_WRITE_SEQUENCER_150 0x3096
+#define WM8995_WRITE_SEQUENCER_151 0x3097
+#define WM8995_WRITE_SEQUENCER_152 0x3098
+#define WM8995_WRITE_SEQUENCER_153 0x3099
+#define WM8995_WRITE_SEQUENCER_154 0x309A
+#define WM8995_WRITE_SEQUENCER_155 0x309B
+#define WM8995_WRITE_SEQUENCER_156 0x309C
+#define WM8995_WRITE_SEQUENCER_157 0x309D
+#define WM8995_WRITE_SEQUENCER_158 0x309E
+#define WM8995_WRITE_SEQUENCER_159 0x309F
+#define WM8995_WRITE_SEQUENCER_160 0x30A0
+#define WM8995_WRITE_SEQUENCER_161 0x30A1
+#define WM8995_WRITE_SEQUENCER_162 0x30A2
+#define WM8995_WRITE_SEQUENCER_163 0x30A3
+#define WM8995_WRITE_SEQUENCER_164 0x30A4
+#define WM8995_WRITE_SEQUENCER_165 0x30A5
+#define WM8995_WRITE_SEQUENCER_166 0x30A6
+#define WM8995_WRITE_SEQUENCER_167 0x30A7
+#define WM8995_WRITE_SEQUENCER_168 0x30A8
+#define WM8995_WRITE_SEQUENCER_169 0x30A9
+#define WM8995_WRITE_SEQUENCER_170 0x30AA
+#define WM8995_WRITE_SEQUENCER_171 0x30AB
+#define WM8995_WRITE_SEQUENCER_172 0x30AC
+#define WM8995_WRITE_SEQUENCER_173 0x30AD
+#define WM8995_WRITE_SEQUENCER_174 0x30AE
+#define WM8995_WRITE_SEQUENCER_175 0x30AF
+#define WM8995_WRITE_SEQUENCER_176 0x30B0
+#define WM8995_WRITE_SEQUENCER_177 0x30B1
+#define WM8995_WRITE_SEQUENCER_178 0x30B2
+#define WM8995_WRITE_SEQUENCER_179 0x30B3
+#define WM8995_WRITE_SEQUENCER_180 0x30B4
+#define WM8995_WRITE_SEQUENCER_181 0x30B5
+#define WM8995_WRITE_SEQUENCER_182 0x30B6
+#define WM8995_WRITE_SEQUENCER_183 0x30B7
+#define WM8995_WRITE_SEQUENCER_184 0x30B8
+#define WM8995_WRITE_SEQUENCER_185 0x30B9
+#define WM8995_WRITE_SEQUENCER_186 0x30BA
+#define WM8995_WRITE_SEQUENCER_187 0x30BB
+#define WM8995_WRITE_SEQUENCER_188 0x30BC
+#define WM8995_WRITE_SEQUENCER_189 0x30BD
+#define WM8995_WRITE_SEQUENCER_190 0x30BE
+#define WM8995_WRITE_SEQUENCER_191 0x30BF
+#define WM8995_WRITE_SEQUENCER_192 0x30C0
+#define WM8995_WRITE_SEQUENCER_193 0x30C1
+#define WM8995_WRITE_SEQUENCER_194 0x30C2
+#define WM8995_WRITE_SEQUENCER_195 0x30C3
+#define WM8995_WRITE_SEQUENCER_196 0x30C4
+#define WM8995_WRITE_SEQUENCER_197 0x30C5
+#define WM8995_WRITE_SEQUENCER_198 0x30C6
+#define WM8995_WRITE_SEQUENCER_199 0x30C7
+#define WM8995_WRITE_SEQUENCER_200 0x30C8
+#define WM8995_WRITE_SEQUENCER_201 0x30C9
+#define WM8995_WRITE_SEQUENCER_202 0x30CA
+#define WM8995_WRITE_SEQUENCER_203 0x30CB
+#define WM8995_WRITE_SEQUENCER_204 0x30CC
+#define WM8995_WRITE_SEQUENCER_205 0x30CD
+#define WM8995_WRITE_SEQUENCER_206 0x30CE
+#define WM8995_WRITE_SEQUENCER_207 0x30CF
+#define WM8995_WRITE_SEQUENCER_208 0x30D0
+#define WM8995_WRITE_SEQUENCER_209 0x30D1
+#define WM8995_WRITE_SEQUENCER_210 0x30D2
+#define WM8995_WRITE_SEQUENCER_211 0x30D3
+#define WM8995_WRITE_SEQUENCER_212 0x30D4
+#define WM8995_WRITE_SEQUENCER_213 0x30D5
+#define WM8995_WRITE_SEQUENCER_214 0x30D6
+#define WM8995_WRITE_SEQUENCER_215 0x30D7
+#define WM8995_WRITE_SEQUENCER_216 0x30D8
+#define WM8995_WRITE_SEQUENCER_217 0x30D9
+#define WM8995_WRITE_SEQUENCER_218 0x30DA
+#define WM8995_WRITE_SEQUENCER_219 0x30DB
+#define WM8995_WRITE_SEQUENCER_220 0x30DC
+#define WM8995_WRITE_SEQUENCER_221 0x30DD
+#define WM8995_WRITE_SEQUENCER_222 0x30DE
+#define WM8995_WRITE_SEQUENCER_223 0x30DF
+#define WM8995_WRITE_SEQUENCER_224 0x30E0
+#define WM8995_WRITE_SEQUENCER_225 0x30E1
+#define WM8995_WRITE_SEQUENCER_226 0x30E2
+#define WM8995_WRITE_SEQUENCER_227 0x30E3
+#define WM8995_WRITE_SEQUENCER_228 0x30E4
+#define WM8995_WRITE_SEQUENCER_229 0x30E5
+#define WM8995_WRITE_SEQUENCER_230 0x30E6
+#define WM8995_WRITE_SEQUENCER_231 0x30E7
+#define WM8995_WRITE_SEQUENCER_232 0x30E8
+#define WM8995_WRITE_SEQUENCER_233 0x30E9
+#define WM8995_WRITE_SEQUENCER_234 0x30EA
+#define WM8995_WRITE_SEQUENCER_235 0x30EB
+#define WM8995_WRITE_SEQUENCER_236 0x30EC
+#define WM8995_WRITE_SEQUENCER_237 0x30ED
+#define WM8995_WRITE_SEQUENCER_238 0x30EE
+#define WM8995_WRITE_SEQUENCER_239 0x30EF
+#define WM8995_WRITE_SEQUENCER_240 0x30F0
+#define WM8995_WRITE_SEQUENCER_241 0x30F1
+#define WM8995_WRITE_SEQUENCER_242 0x30F2
+#define WM8995_WRITE_SEQUENCER_243 0x30F3
+#define WM8995_WRITE_SEQUENCER_244 0x30F4
+#define WM8995_WRITE_SEQUENCER_245 0x30F5
+#define WM8995_WRITE_SEQUENCER_246 0x30F6
+#define WM8995_WRITE_SEQUENCER_247 0x30F7
+#define WM8995_WRITE_SEQUENCER_248 0x30F8
+#define WM8995_WRITE_SEQUENCER_249 0x30F9
+#define WM8995_WRITE_SEQUENCER_250 0x30FA
+#define WM8995_WRITE_SEQUENCER_251 0x30FB
+#define WM8995_WRITE_SEQUENCER_252 0x30FC
+#define WM8995_WRITE_SEQUENCER_253 0x30FD
+#define WM8995_WRITE_SEQUENCER_254 0x30FE
+#define WM8995_WRITE_SEQUENCER_255 0x30FF
+#define WM8995_WRITE_SEQUENCER_256 0x3100
+#define WM8995_WRITE_SEQUENCER_257 0x3101
+#define WM8995_WRITE_SEQUENCER_258 0x3102
+#define WM8995_WRITE_SEQUENCER_259 0x3103
+#define WM8995_WRITE_SEQUENCER_260 0x3104
+#define WM8995_WRITE_SEQUENCER_261 0x3105
+#define WM8995_WRITE_SEQUENCER_262 0x3106
+#define WM8995_WRITE_SEQUENCER_263 0x3107
+#define WM8995_WRITE_SEQUENCER_264 0x3108
+#define WM8995_WRITE_SEQUENCER_265 0x3109
+#define WM8995_WRITE_SEQUENCER_266 0x310A
+#define WM8995_WRITE_SEQUENCER_267 0x310B
+#define WM8995_WRITE_SEQUENCER_268 0x310C
+#define WM8995_WRITE_SEQUENCER_269 0x310D
+#define WM8995_WRITE_SEQUENCER_270 0x310E
+#define WM8995_WRITE_SEQUENCER_271 0x310F
+#define WM8995_WRITE_SEQUENCER_272 0x3110
+#define WM8995_WRITE_SEQUENCER_273 0x3111
+#define WM8995_WRITE_SEQUENCER_274 0x3112
+#define WM8995_WRITE_SEQUENCER_275 0x3113
+#define WM8995_WRITE_SEQUENCER_276 0x3114
+#define WM8995_WRITE_SEQUENCER_277 0x3115
+#define WM8995_WRITE_SEQUENCER_278 0x3116
+#define WM8995_WRITE_SEQUENCER_279 0x3117
+#define WM8995_WRITE_SEQUENCER_280 0x3118
+#define WM8995_WRITE_SEQUENCER_281 0x3119
+#define WM8995_WRITE_SEQUENCER_282 0x311A
+#define WM8995_WRITE_SEQUENCER_283 0x311B
+#define WM8995_WRITE_SEQUENCER_284 0x311C
+#define WM8995_WRITE_SEQUENCER_285 0x311D
+#define WM8995_WRITE_SEQUENCER_286 0x311E
+#define WM8995_WRITE_SEQUENCER_287 0x311F
+#define WM8995_WRITE_SEQUENCER_288 0x3120
+#define WM8995_WRITE_SEQUENCER_289 0x3121
+#define WM8995_WRITE_SEQUENCER_290 0x3122
+#define WM8995_WRITE_SEQUENCER_291 0x3123
+#define WM8995_WRITE_SEQUENCER_292 0x3124
+#define WM8995_WRITE_SEQUENCER_293 0x3125
+#define WM8995_WRITE_SEQUENCER_294 0x3126
+#define WM8995_WRITE_SEQUENCER_295 0x3127
+#define WM8995_WRITE_SEQUENCER_296 0x3128
+#define WM8995_WRITE_SEQUENCER_297 0x3129
+#define WM8995_WRITE_SEQUENCER_298 0x312A
+#define WM8995_WRITE_SEQUENCER_299 0x312B
+#define WM8995_WRITE_SEQUENCER_300 0x312C
+#define WM8995_WRITE_SEQUENCER_301 0x312D
+#define WM8995_WRITE_SEQUENCER_302 0x312E
+#define WM8995_WRITE_SEQUENCER_303 0x312F
+#define WM8995_WRITE_SEQUENCER_304 0x3130
+#define WM8995_WRITE_SEQUENCER_305 0x3131
+#define WM8995_WRITE_SEQUENCER_306 0x3132
+#define WM8995_WRITE_SEQUENCER_307 0x3133
+#define WM8995_WRITE_SEQUENCER_308 0x3134
+#define WM8995_WRITE_SEQUENCER_309 0x3135
+#define WM8995_WRITE_SEQUENCER_310 0x3136
+#define WM8995_WRITE_SEQUENCER_311 0x3137
+#define WM8995_WRITE_SEQUENCER_312 0x3138
+#define WM8995_WRITE_SEQUENCER_313 0x3139
+#define WM8995_WRITE_SEQUENCER_314 0x313A
+#define WM8995_WRITE_SEQUENCER_315 0x313B
+#define WM8995_WRITE_SEQUENCER_316 0x313C
+#define WM8995_WRITE_SEQUENCER_317 0x313D
+#define WM8995_WRITE_SEQUENCER_318 0x313E
+#define WM8995_WRITE_SEQUENCER_319 0x313F
+#define WM8995_WRITE_SEQUENCER_320 0x3140
+#define WM8995_WRITE_SEQUENCER_321 0x3141
+#define WM8995_WRITE_SEQUENCER_322 0x3142
+#define WM8995_WRITE_SEQUENCER_323 0x3143
+#define WM8995_WRITE_SEQUENCER_324 0x3144
+#define WM8995_WRITE_SEQUENCER_325 0x3145
+#define WM8995_WRITE_SEQUENCER_326 0x3146
+#define WM8995_WRITE_SEQUENCER_327 0x3147
+#define WM8995_WRITE_SEQUENCER_328 0x3148
+#define WM8995_WRITE_SEQUENCER_329 0x3149
+#define WM8995_WRITE_SEQUENCER_330 0x314A
+#define WM8995_WRITE_SEQUENCER_331 0x314B
+#define WM8995_WRITE_SEQUENCER_332 0x314C
+#define WM8995_WRITE_SEQUENCER_333 0x314D
+#define WM8995_WRITE_SEQUENCER_334 0x314E
+#define WM8995_WRITE_SEQUENCER_335 0x314F
+#define WM8995_WRITE_SEQUENCER_336 0x3150
+#define WM8995_WRITE_SEQUENCER_337 0x3151
+#define WM8995_WRITE_SEQUENCER_338 0x3152
+#define WM8995_WRITE_SEQUENCER_339 0x3153
+#define WM8995_WRITE_SEQUENCER_340 0x3154
+#define WM8995_WRITE_SEQUENCER_341 0x3155
+#define WM8995_WRITE_SEQUENCER_342 0x3156
+#define WM8995_WRITE_SEQUENCER_343 0x3157
+#define WM8995_WRITE_SEQUENCER_344 0x3158
+#define WM8995_WRITE_SEQUENCER_345 0x3159
+#define WM8995_WRITE_SEQUENCER_346 0x315A
+#define WM8995_WRITE_SEQUENCER_347 0x315B
+#define WM8995_WRITE_SEQUENCER_348 0x315C
+#define WM8995_WRITE_SEQUENCER_349 0x315D
+#define WM8995_WRITE_SEQUENCER_350 0x315E
+#define WM8995_WRITE_SEQUENCER_351 0x315F
+#define WM8995_WRITE_SEQUENCER_352 0x3160
+#define WM8995_WRITE_SEQUENCER_353 0x3161
+#define WM8995_WRITE_SEQUENCER_354 0x3162
+#define WM8995_WRITE_SEQUENCER_355 0x3163
+#define WM8995_WRITE_SEQUENCER_356 0x3164
+#define WM8995_WRITE_SEQUENCER_357 0x3165
+#define WM8995_WRITE_SEQUENCER_358 0x3166
+#define WM8995_WRITE_SEQUENCER_359 0x3167
+#define WM8995_WRITE_SEQUENCER_360 0x3168
+#define WM8995_WRITE_SEQUENCER_361 0x3169
+#define WM8995_WRITE_SEQUENCER_362 0x316A
+#define WM8995_WRITE_SEQUENCER_363 0x316B
+#define WM8995_WRITE_SEQUENCER_364 0x316C
+#define WM8995_WRITE_SEQUENCER_365 0x316D
+#define WM8995_WRITE_SEQUENCER_366 0x316E
+#define WM8995_WRITE_SEQUENCER_367 0x316F
+#define WM8995_WRITE_SEQUENCER_368 0x3170
+#define WM8995_WRITE_SEQUENCER_369 0x3171
+#define WM8995_WRITE_SEQUENCER_370 0x3172
+#define WM8995_WRITE_SEQUENCER_371 0x3173
+#define WM8995_WRITE_SEQUENCER_372 0x3174
+#define WM8995_WRITE_SEQUENCER_373 0x3175
+#define WM8995_WRITE_SEQUENCER_374 0x3176
+#define WM8995_WRITE_SEQUENCER_375 0x3177
+#define WM8995_WRITE_SEQUENCER_376 0x3178
+#define WM8995_WRITE_SEQUENCER_377 0x3179
+#define WM8995_WRITE_SEQUENCER_378 0x317A
+#define WM8995_WRITE_SEQUENCER_379 0x317B
+#define WM8995_WRITE_SEQUENCER_380 0x317C
+#define WM8995_WRITE_SEQUENCER_381 0x317D
+#define WM8995_WRITE_SEQUENCER_382 0x317E
+#define WM8995_WRITE_SEQUENCER_383 0x317F
+#define WM8995_WRITE_SEQUENCER_384 0x3180
+#define WM8995_WRITE_SEQUENCER_385 0x3181
+#define WM8995_WRITE_SEQUENCER_386 0x3182
+#define WM8995_WRITE_SEQUENCER_387 0x3183
+#define WM8995_WRITE_SEQUENCER_388 0x3184
+#define WM8995_WRITE_SEQUENCER_389 0x3185
+#define WM8995_WRITE_SEQUENCER_390 0x3186
+#define WM8995_WRITE_SEQUENCER_391 0x3187
+#define WM8995_WRITE_SEQUENCER_392 0x3188
+#define WM8995_WRITE_SEQUENCER_393 0x3189
+#define WM8995_WRITE_SEQUENCER_394 0x318A
+#define WM8995_WRITE_SEQUENCER_395 0x318B
+#define WM8995_WRITE_SEQUENCER_396 0x318C
+#define WM8995_WRITE_SEQUENCER_397 0x318D
+#define WM8995_WRITE_SEQUENCER_398 0x318E
+#define WM8995_WRITE_SEQUENCER_399 0x318F
+#define WM8995_WRITE_SEQUENCER_400 0x3190
+#define WM8995_WRITE_SEQUENCER_401 0x3191
+#define WM8995_WRITE_SEQUENCER_402 0x3192
+#define WM8995_WRITE_SEQUENCER_403 0x3193
+#define WM8995_WRITE_SEQUENCER_404 0x3194
+#define WM8995_WRITE_SEQUENCER_405 0x3195
+#define WM8995_WRITE_SEQUENCER_406 0x3196
+#define WM8995_WRITE_SEQUENCER_407 0x3197
+#define WM8995_WRITE_SEQUENCER_408 0x3198
+#define WM8995_WRITE_SEQUENCER_409 0x3199
+#define WM8995_WRITE_SEQUENCER_410 0x319A
+#define WM8995_WRITE_SEQUENCER_411 0x319B
+#define WM8995_WRITE_SEQUENCER_412 0x319C
+#define WM8995_WRITE_SEQUENCER_413 0x319D
+#define WM8995_WRITE_SEQUENCER_414 0x319E
+#define WM8995_WRITE_SEQUENCER_415 0x319F
+#define WM8995_WRITE_SEQUENCER_416 0x31A0
+#define WM8995_WRITE_SEQUENCER_417 0x31A1
+#define WM8995_WRITE_SEQUENCER_418 0x31A2
+#define WM8995_WRITE_SEQUENCER_419 0x31A3
+#define WM8995_WRITE_SEQUENCER_420 0x31A4
+#define WM8995_WRITE_SEQUENCER_421 0x31A5
+#define WM8995_WRITE_SEQUENCER_422 0x31A6
+#define WM8995_WRITE_SEQUENCER_423 0x31A7
+#define WM8995_WRITE_SEQUENCER_424 0x31A8
+#define WM8995_WRITE_SEQUENCER_425 0x31A9
+#define WM8995_WRITE_SEQUENCER_426 0x31AA
+#define WM8995_WRITE_SEQUENCER_427 0x31AB
+#define WM8995_WRITE_SEQUENCER_428 0x31AC
+#define WM8995_WRITE_SEQUENCER_429 0x31AD
+#define WM8995_WRITE_SEQUENCER_430 0x31AE
+#define WM8995_WRITE_SEQUENCER_431 0x31AF
+#define WM8995_WRITE_SEQUENCER_432 0x31B0
+#define WM8995_WRITE_SEQUENCER_433 0x31B1
+#define WM8995_WRITE_SEQUENCER_434 0x31B2
+#define WM8995_WRITE_SEQUENCER_435 0x31B3
+#define WM8995_WRITE_SEQUENCER_436 0x31B4
+#define WM8995_WRITE_SEQUENCER_437 0x31B5
+#define WM8995_WRITE_SEQUENCER_438 0x31B6
+#define WM8995_WRITE_SEQUENCER_439 0x31B7
+#define WM8995_WRITE_SEQUENCER_440 0x31B8
+#define WM8995_WRITE_SEQUENCER_441 0x31B9
+#define WM8995_WRITE_SEQUENCER_442 0x31BA
+#define WM8995_WRITE_SEQUENCER_443 0x31BB
+#define WM8995_WRITE_SEQUENCER_444 0x31BC
+#define WM8995_WRITE_SEQUENCER_445 0x31BD
+#define WM8995_WRITE_SEQUENCER_446 0x31BE
+#define WM8995_WRITE_SEQUENCER_447 0x31BF
+#define WM8995_WRITE_SEQUENCER_448 0x31C0
+#define WM8995_WRITE_SEQUENCER_449 0x31C1
+#define WM8995_WRITE_SEQUENCER_450 0x31C2
+#define WM8995_WRITE_SEQUENCER_451 0x31C3
+#define WM8995_WRITE_SEQUENCER_452 0x31C4
+#define WM8995_WRITE_SEQUENCER_453 0x31C5
+#define WM8995_WRITE_SEQUENCER_454 0x31C6
+#define WM8995_WRITE_SEQUENCER_455 0x31C7
+#define WM8995_WRITE_SEQUENCER_456 0x31C8
+#define WM8995_WRITE_SEQUENCER_457 0x31C9
+#define WM8995_WRITE_SEQUENCER_458 0x31CA
+#define WM8995_WRITE_SEQUENCER_459 0x31CB
+#define WM8995_WRITE_SEQUENCER_460 0x31CC
+#define WM8995_WRITE_SEQUENCER_461 0x31CD
+#define WM8995_WRITE_SEQUENCER_462 0x31CE
+#define WM8995_WRITE_SEQUENCER_463 0x31CF
+#define WM8995_WRITE_SEQUENCER_464 0x31D0
+#define WM8995_WRITE_SEQUENCER_465 0x31D1
+#define WM8995_WRITE_SEQUENCER_466 0x31D2
+#define WM8995_WRITE_SEQUENCER_467 0x31D3
+#define WM8995_WRITE_SEQUENCER_468 0x31D4
+#define WM8995_WRITE_SEQUENCER_469 0x31D5
+#define WM8995_WRITE_SEQUENCER_470 0x31D6
+#define WM8995_WRITE_SEQUENCER_471 0x31D7
+#define WM8995_WRITE_SEQUENCER_472 0x31D8
+#define WM8995_WRITE_SEQUENCER_473 0x31D9
+#define WM8995_WRITE_SEQUENCER_474 0x31DA
+#define WM8995_WRITE_SEQUENCER_475 0x31DB
+#define WM8995_WRITE_SEQUENCER_476 0x31DC
+#define WM8995_WRITE_SEQUENCER_477 0x31DD
+#define WM8995_WRITE_SEQUENCER_478 0x31DE
+#define WM8995_WRITE_SEQUENCER_479 0x31DF
+#define WM8995_WRITE_SEQUENCER_480 0x31E0
+#define WM8995_WRITE_SEQUENCER_481 0x31E1
+#define WM8995_WRITE_SEQUENCER_482 0x31E2
+#define WM8995_WRITE_SEQUENCER_483 0x31E3
+#define WM8995_WRITE_SEQUENCER_484 0x31E4
+#define WM8995_WRITE_SEQUENCER_485 0x31E5
+#define WM8995_WRITE_SEQUENCER_486 0x31E6
+#define WM8995_WRITE_SEQUENCER_487 0x31E7
+#define WM8995_WRITE_SEQUENCER_488 0x31E8
+#define WM8995_WRITE_SEQUENCER_489 0x31E9
+#define WM8995_WRITE_SEQUENCER_490 0x31EA
+#define WM8995_WRITE_SEQUENCER_491 0x31EB
+#define WM8995_WRITE_SEQUENCER_492 0x31EC
+#define WM8995_WRITE_SEQUENCER_493 0x31ED
+#define WM8995_WRITE_SEQUENCER_494 0x31EE
+#define WM8995_WRITE_SEQUENCER_495 0x31EF
+#define WM8995_WRITE_SEQUENCER_496 0x31F0
+#define WM8995_WRITE_SEQUENCER_497 0x31F1
+#define WM8995_WRITE_SEQUENCER_498 0x31F2
+#define WM8995_WRITE_SEQUENCER_499 0x31F3
+#define WM8995_WRITE_SEQUENCER_500 0x31F4
+#define WM8995_WRITE_SEQUENCER_501 0x31F5
+#define WM8995_WRITE_SEQUENCER_502 0x31F6
+#define WM8995_WRITE_SEQUENCER_503 0x31F7
+#define WM8995_WRITE_SEQUENCER_504 0x31F8
+#define WM8995_WRITE_SEQUENCER_505 0x31F9
+#define WM8995_WRITE_SEQUENCER_506 0x31FA
+#define WM8995_WRITE_SEQUENCER_507 0x31FB
+#define WM8995_WRITE_SEQUENCER_508 0x31FC
+#define WM8995_WRITE_SEQUENCER_509 0x31FD
+#define WM8995_WRITE_SEQUENCER_510 0x31FE
+#define WM8995_WRITE_SEQUENCER_511 0x31FF
+
+#define WM8995_REGISTER_COUNT 725
+#define WM8995_MAX_REGISTER 0x31FF
+
+#define WM8995_MAX_CACHED_REGISTER WM8995_MAX_REGISTER
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM8995_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */
+#define WM8995_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */
+#define WM8995_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8995_MICB2_ENA 0x0200 /* MICB2_ENA */
+#define WM8995_MICB2_ENA_MASK 0x0200 /* MICB2_ENA */
+#define WM8995_MICB2_ENA_SHIFT 9 /* MICB2_ENA */
+#define WM8995_MICB2_ENA_WIDTH 1 /* MICB2_ENA */
+#define WM8995_MICB1_ENA 0x0100 /* MICB1_ENA */
+#define WM8995_MICB1_ENA_MASK 0x0100 /* MICB1_ENA */
+#define WM8995_MICB1_ENA_SHIFT 8 /* MICB1_ENA */
+#define WM8995_MICB1_ENA_WIDTH 1 /* MICB1_ENA */
+#define WM8995_HPOUT2L_ENA 0x0080 /* HPOUT2L_ENA */
+#define WM8995_HPOUT2L_ENA_MASK 0x0080 /* HPOUT2L_ENA */
+#define WM8995_HPOUT2L_ENA_SHIFT 7 /* HPOUT2L_ENA */
+#define WM8995_HPOUT2L_ENA_WIDTH 1 /* HPOUT2L_ENA */
+#define WM8995_HPOUT2R_ENA 0x0040 /* HPOUT2R_ENA */
+#define WM8995_HPOUT2R_ENA_MASK 0x0040 /* HPOUT2R_ENA */
+#define WM8995_HPOUT2R_ENA_SHIFT 6 /* HPOUT2R_ENA */
+#define WM8995_HPOUT2R_ENA_WIDTH 1 /* HPOUT2R_ENA */
+#define WM8995_HPOUT1L_ENA 0x0020 /* HPOUT1L_ENA */
+#define WM8995_HPOUT1L_ENA_MASK 0x0020 /* HPOUT1L_ENA */
+#define WM8995_HPOUT1L_ENA_SHIFT 5 /* HPOUT1L_ENA */
+#define WM8995_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */
+#define WM8995_HPOUT1R_ENA 0x0010 /* HPOUT1R_ENA */
+#define WM8995_HPOUT1R_ENA_MASK 0x0010 /* HPOUT1R_ENA */
+#define WM8995_HPOUT1R_ENA_SHIFT 4 /* HPOUT1R_ENA */
+#define WM8995_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */
+#define WM8995_BG_ENA 0x0001 /* BG_ENA */
+#define WM8995_BG_ENA_MASK 0x0001 /* BG_ENA */
+#define WM8995_BG_ENA_SHIFT 0 /* BG_ENA */
+#define WM8995_BG_ENA_WIDTH 1 /* BG_ENA */
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8995_OPCLK_ENA 0x0800 /* OPCLK_ENA */
+#define WM8995_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */
+#define WM8995_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */
+#define WM8995_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */
+#define WM8995_IN1L_ENA 0x0020 /* IN1L_ENA */
+#define WM8995_IN1L_ENA_MASK 0x0020 /* IN1L_ENA */
+#define WM8995_IN1L_ENA_SHIFT 5 /* IN1L_ENA */
+#define WM8995_IN1L_ENA_WIDTH 1 /* IN1L_ENA */
+#define WM8995_IN1R_ENA 0x0010 /* IN1R_ENA */
+#define WM8995_IN1R_ENA_MASK 0x0010 /* IN1R_ENA */
+#define WM8995_IN1R_ENA_SHIFT 4 /* IN1R_ENA */
+#define WM8995_IN1R_ENA_WIDTH 1 /* IN1R_ENA */
+#define WM8995_LDO2_ENA 0x0002 /* LDO2_ENA */
+#define WM8995_LDO2_ENA_MASK 0x0002 /* LDO2_ENA */
+#define WM8995_LDO2_ENA_SHIFT 1 /* LDO2_ENA */
+#define WM8995_LDO2_ENA_WIDTH 1 /* LDO2_ENA */
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8995_AIF2ADCL_ENA 0x2000 /* AIF2ADCL_ENA */
+#define WM8995_AIF2ADCL_ENA_MASK 0x2000 /* AIF2ADCL_ENA */
+#define WM8995_AIF2ADCL_ENA_SHIFT 13 /* AIF2ADCL_ENA */
+#define WM8995_AIF2ADCL_ENA_WIDTH 1 /* AIF2ADCL_ENA */
+#define WM8995_AIF2ADCR_ENA 0x1000 /* AIF2ADCR_ENA */
+#define WM8995_AIF2ADCR_ENA_MASK 0x1000 /* AIF2ADCR_ENA */
+#define WM8995_AIF2ADCR_ENA_SHIFT 12 /* AIF2ADCR_ENA */
+#define WM8995_AIF2ADCR_ENA_WIDTH 1 /* AIF2ADCR_ENA */
+#define WM8995_AIF1ADC2L_ENA 0x0800 /* AIF1ADC2L_ENA */
+#define WM8995_AIF1ADC2L_ENA_MASK 0x0800 /* AIF1ADC2L_ENA */
+#define WM8995_AIF1ADC2L_ENA_SHIFT 11 /* AIF1ADC2L_ENA */
+#define WM8995_AIF1ADC2L_ENA_WIDTH 1 /* AIF1ADC2L_ENA */
+#define WM8995_AIF1ADC2R_ENA 0x0400 /* AIF1ADC2R_ENA */
+#define WM8995_AIF1ADC2R_ENA_MASK 0x0400 /* AIF1ADC2R_ENA */
+#define WM8995_AIF1ADC2R_ENA_SHIFT 10 /* AIF1ADC2R_ENA */
+#define WM8995_AIF1ADC2R_ENA_WIDTH 1 /* AIF1ADC2R_ENA */
+#define WM8995_AIF1ADC1L_ENA 0x0200 /* AIF1ADC1L_ENA */
+#define WM8995_AIF1ADC1L_ENA_MASK 0x0200 /* AIF1ADC1L_ENA */
+#define WM8995_AIF1ADC1L_ENA_SHIFT 9 /* AIF1ADC1L_ENA */
+#define WM8995_AIF1ADC1L_ENA_WIDTH 1 /* AIF1ADC1L_ENA */
+#define WM8995_AIF1ADC1R_ENA 0x0100 /* AIF1ADC1R_ENA */
+#define WM8995_AIF1ADC1R_ENA_MASK 0x0100 /* AIF1ADC1R_ENA */
+#define WM8995_AIF1ADC1R_ENA_SHIFT 8 /* AIF1ADC1R_ENA */
+#define WM8995_AIF1ADC1R_ENA_WIDTH 1 /* AIF1ADC1R_ENA */
+#define WM8995_DMIC3L_ENA 0x0080 /* DMIC3L_ENA */
+#define WM8995_DMIC3L_ENA_MASK 0x0080 /* DMIC3L_ENA */
+#define WM8995_DMIC3L_ENA_SHIFT 7 /* DMIC3L_ENA */
+#define WM8995_DMIC3L_ENA_WIDTH 1 /* DMIC3L_ENA */
+#define WM8995_DMIC3R_ENA 0x0040 /* DMIC3R_ENA */
+#define WM8995_DMIC3R_ENA_MASK 0x0040 /* DMIC3R_ENA */
+#define WM8995_DMIC3R_ENA_SHIFT 6 /* DMIC3R_ENA */
+#define WM8995_DMIC3R_ENA_WIDTH 1 /* DMIC3R_ENA */
+#define WM8995_DMIC2L_ENA 0x0020 /* DMIC2L_ENA */
+#define WM8995_DMIC2L_ENA_MASK 0x0020 /* DMIC2L_ENA */
+#define WM8995_DMIC2L_ENA_SHIFT 5 /* DMIC2L_ENA */
+#define WM8995_DMIC2L_ENA_WIDTH 1 /* DMIC2L_ENA */
+#define WM8995_DMIC2R_ENA 0x0010 /* DMIC2R_ENA */
+#define WM8995_DMIC2R_ENA_MASK 0x0010 /* DMIC2R_ENA */
+#define WM8995_DMIC2R_ENA_SHIFT 4 /* DMIC2R_ENA */
+#define WM8995_DMIC2R_ENA_WIDTH 1 /* DMIC2R_ENA */
+#define WM8995_DMIC1L_ENA 0x0008 /* DMIC1L_ENA */
+#define WM8995_DMIC1L_ENA_MASK 0x0008 /* DMIC1L_ENA */
+#define WM8995_DMIC1L_ENA_SHIFT 3 /* DMIC1L_ENA */
+#define WM8995_DMIC1L_ENA_WIDTH 1 /* DMIC1L_ENA */
+#define WM8995_DMIC1R_ENA 0x0004 /* DMIC1R_ENA */
+#define WM8995_DMIC1R_ENA_MASK 0x0004 /* DMIC1R_ENA */
+#define WM8995_DMIC1R_ENA_SHIFT 2 /* DMIC1R_ENA */
+#define WM8995_DMIC1R_ENA_WIDTH 1 /* DMIC1R_ENA */
+#define WM8995_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8995_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */
+#define WM8995_ADCL_ENA_SHIFT 1 /* ADCL_ENA */
+#define WM8995_ADCL_ENA_WIDTH 1 /* ADCL_ENA */
+#define WM8995_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8995_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */
+#define WM8995_ADCR_ENA_SHIFT 0 /* ADCR_ENA */
+#define WM8995_ADCR_ENA_WIDTH 1 /* ADCR_ENA */
+
+/*
+ * R4 (0x04) - Power Management (4)
+ */
+#define WM8995_AIF2DACL_ENA 0x2000 /* AIF2DACL_ENA */
+#define WM8995_AIF2DACL_ENA_MASK 0x2000 /* AIF2DACL_ENA */
+#define WM8995_AIF2DACL_ENA_SHIFT 13 /* AIF2DACL_ENA */
+#define WM8995_AIF2DACL_ENA_WIDTH 1 /* AIF2DACL_ENA */
+#define WM8995_AIF2DACR_ENA 0x1000 /* AIF2DACR_ENA */
+#define WM8995_AIF2DACR_ENA_MASK 0x1000 /* AIF2DACR_ENA */
+#define WM8995_AIF2DACR_ENA_SHIFT 12 /* AIF2DACR_ENA */
+#define WM8995_AIF2DACR_ENA_WIDTH 1 /* AIF2DACR_ENA */
+#define WM8995_AIF1DAC2L_ENA 0x0800 /* AIF1DAC2L_ENA */
+#define WM8995_AIF1DAC2L_ENA_MASK 0x0800 /* AIF1DAC2L_ENA */
+#define WM8995_AIF1DAC2L_ENA_SHIFT 11 /* AIF1DAC2L_ENA */
+#define WM8995_AIF1DAC2L_ENA_WIDTH 1 /* AIF1DAC2L_ENA */
+#define WM8995_AIF1DAC2R_ENA 0x0400 /* AIF1DAC2R_ENA */
+#define WM8995_AIF1DAC2R_ENA_MASK 0x0400 /* AIF1DAC2R_ENA */
+#define WM8995_AIF1DAC2R_ENA_SHIFT 10 /* AIF1DAC2R_ENA */
+#define WM8995_AIF1DAC2R_ENA_WIDTH 1 /* AIF1DAC2R_ENA */
+#define WM8995_AIF1DAC1L_ENA 0x0200 /* AIF1DAC1L_ENA */
+#define WM8995_AIF1DAC1L_ENA_MASK 0x0200 /* AIF1DAC1L_ENA */
+#define WM8995_AIF1DAC1L_ENA_SHIFT 9 /* AIF1DAC1L_ENA */
+#define WM8995_AIF1DAC1L_ENA_WIDTH 1 /* AIF1DAC1L_ENA */
+#define WM8995_AIF1DAC1R_ENA 0x0100 /* AIF1DAC1R_ENA */
+#define WM8995_AIF1DAC1R_ENA_MASK 0x0100 /* AIF1DAC1R_ENA */
+#define WM8995_AIF1DAC1R_ENA_SHIFT 8 /* AIF1DAC1R_ENA */
+#define WM8995_AIF1DAC1R_ENA_WIDTH 1 /* AIF1DAC1R_ENA */
+#define WM8995_DAC2L_ENA 0x0008 /* DAC2L_ENA */
+#define WM8995_DAC2L_ENA_MASK 0x0008 /* DAC2L_ENA */
+#define WM8995_DAC2L_ENA_SHIFT 3 /* DAC2L_ENA */
+#define WM8995_DAC2L_ENA_WIDTH 1 /* DAC2L_ENA */
+#define WM8995_DAC2R_ENA 0x0004 /* DAC2R_ENA */
+#define WM8995_DAC2R_ENA_MASK 0x0004 /* DAC2R_ENA */
+#define WM8995_DAC2R_ENA_SHIFT 2 /* DAC2R_ENA */
+#define WM8995_DAC2R_ENA_WIDTH 1 /* DAC2R_ENA */
+#define WM8995_DAC1L_ENA 0x0002 /* DAC1L_ENA */
+#define WM8995_DAC1L_ENA_MASK 0x0002 /* DAC1L_ENA */
+#define WM8995_DAC1L_ENA_SHIFT 1 /* DAC1L_ENA */
+#define WM8995_DAC1L_ENA_WIDTH 1 /* DAC1L_ENA */
+#define WM8995_DAC1R_ENA 0x0001 /* DAC1R_ENA */
+#define WM8995_DAC1R_ENA_MASK 0x0001 /* DAC1R_ENA */
+#define WM8995_DAC1R_ENA_SHIFT 0 /* DAC1R_ENA */
+#define WM8995_DAC1R_ENA_WIDTH 1 /* DAC1R_ENA */
+
+/*
+ * R5 (0x05) - Power Management (5)
+ */
+#define WM8995_DMIC_SRC2_MASK 0x0300 /* DMIC_SRC2 - [9:8] */
+#define WM8995_DMIC_SRC2_SHIFT 8 /* DMIC_SRC2 - [9:8] */
+#define WM8995_DMIC_SRC2_WIDTH 2 /* DMIC_SRC2 - [9:8] */
+#define WM8995_DMIC_SRC1_MASK 0x00C0 /* DMIC_SRC1 - [7:6] */
+#define WM8995_DMIC_SRC1_SHIFT 6 /* DMIC_SRC1 - [7:6] */
+#define WM8995_DMIC_SRC1_WIDTH 2 /* DMIC_SRC1 - [7:6] */
+#define WM8995_AIF3_TRI 0x0020 /* AIF3_TRI */
+#define WM8995_AIF3_TRI_MASK 0x0020 /* AIF3_TRI */
+#define WM8995_AIF3_TRI_SHIFT 5 /* AIF3_TRI */
+#define WM8995_AIF3_TRI_WIDTH 1 /* AIF3_TRI */
+#define WM8995_AIF3_ADCDAT_SRC_MASK 0x0018 /* AIF3_ADCDAT_SRC - [4:3] */
+#define WM8995_AIF3_ADCDAT_SRC_SHIFT 3 /* AIF3_ADCDAT_SRC - [4:3] */
+#define WM8995_AIF3_ADCDAT_SRC_WIDTH 2 /* AIF3_ADCDAT_SRC - [4:3] */
+#define WM8995_AIF2_ADCDAT_SRC 0x0004 /* AIF2_ADCDAT_SRC */
+#define WM8995_AIF2_ADCDAT_SRC_MASK 0x0004 /* AIF2_ADCDAT_SRC */
+#define WM8995_AIF2_ADCDAT_SRC_SHIFT 2 /* AIF2_ADCDAT_SRC */
+#define WM8995_AIF2_ADCDAT_SRC_WIDTH 1 /* AIF2_ADCDAT_SRC */
+#define WM8995_AIF2_DACDAT_SRC 0x0002 /* AIF2_DACDAT_SRC */
+#define WM8995_AIF2_DACDAT_SRC_MASK 0x0002 /* AIF2_DACDAT_SRC */
+#define WM8995_AIF2_DACDAT_SRC_SHIFT 1 /* AIF2_DACDAT_SRC */
+#define WM8995_AIF2_DACDAT_SRC_WIDTH 1 /* AIF2_DACDAT_SRC */
+#define WM8995_AIF1_DACDAT_SRC 0x0001 /* AIF1_DACDAT_SRC */
+#define WM8995_AIF1_DACDAT_SRC_MASK 0x0001 /* AIF1_DACDAT_SRC */
+#define WM8995_AIF1_DACDAT_SRC_SHIFT 0 /* AIF1_DACDAT_SRC */
+#define WM8995_AIF1_DACDAT_SRC_WIDTH 1 /* AIF1_DACDAT_SRC */
+
+/*
+ * R16 (0x10) - Left Line Input 1 Volume
+ */
+#define WM8995_IN1_VU 0x0080 /* IN1_VU */
+#define WM8995_IN1_VU_MASK 0x0080 /* IN1_VU */
+#define WM8995_IN1_VU_SHIFT 7 /* IN1_VU */
+#define WM8995_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8995_IN1L_ZC 0x0020 /* IN1L_ZC */
+#define WM8995_IN1L_ZC_MASK 0x0020 /* IN1L_ZC */
+#define WM8995_IN1L_ZC_SHIFT 5 /* IN1L_ZC */
+#define WM8995_IN1L_ZC_WIDTH 1 /* IN1L_ZC */
+#define WM8995_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */
+#define WM8995_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */
+#define WM8995_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */
+
+/*
+ * R17 (0x11) - Right Line Input 1 Volume
+ */
+#define WM8995_IN1_VU 0x0080 /* IN1_VU */
+#define WM8995_IN1_VU_MASK 0x0080 /* IN1_VU */
+#define WM8995_IN1_VU_SHIFT 7 /* IN1_VU */
+#define WM8995_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8995_IN1R_ZC 0x0020 /* IN1R_ZC */
+#define WM8995_IN1R_ZC_MASK 0x0020 /* IN1R_ZC */
+#define WM8995_IN1R_ZC_SHIFT 5 /* IN1R_ZC */
+#define WM8995_IN1R_ZC_WIDTH 1 /* IN1R_ZC */
+#define WM8995_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */
+#define WM8995_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */
+#define WM8995_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */
+
+/*
+ * R18 (0x12) - Left Line Input Control
+ */
+#define WM8995_IN1L_BOOST_MASK 0x0030 /* IN1L_BOOST - [5:4] */
+#define WM8995_IN1L_BOOST_SHIFT 4 /* IN1L_BOOST - [5:4] */
+#define WM8995_IN1L_BOOST_WIDTH 2 /* IN1L_BOOST - [5:4] */
+#define WM8995_IN1L_MODE_MASK 0x000C /* IN1L_MODE - [3:2] */
+#define WM8995_IN1L_MODE_SHIFT 2 /* IN1L_MODE - [3:2] */
+#define WM8995_IN1L_MODE_WIDTH 2 /* IN1L_MODE - [3:2] */
+#define WM8995_IN1R_MODE_MASK 0x0003 /* IN1R_MODE - [1:0] */
+#define WM8995_IN1R_MODE_SHIFT 0 /* IN1R_MODE - [1:0] */
+#define WM8995_IN1R_MODE_WIDTH 2 /* IN1R_MODE - [1:0] */
+
+/*
+ * R24 (0x18) - DAC1 Left Volume
+ */
+#define WM8995_DAC1L_MUTE 0x0200 /* DAC1L_MUTE */
+#define WM8995_DAC1L_MUTE_MASK 0x0200 /* DAC1L_MUTE */
+#define WM8995_DAC1L_MUTE_SHIFT 9 /* DAC1L_MUTE */
+#define WM8995_DAC1L_MUTE_WIDTH 1 /* DAC1L_MUTE */
+#define WM8995_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8995_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8995_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8995_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8995_DAC1L_VOL_MASK 0x00FF /* DAC1L_VOL - [7:0] */
+#define WM8995_DAC1L_VOL_SHIFT 0 /* DAC1L_VOL - [7:0] */
+#define WM8995_DAC1L_VOL_WIDTH 8 /* DAC1L_VOL - [7:0] */
+
+/*
+ * R25 (0x19) - DAC1 Right Volume
+ */
+#define WM8995_DAC1R_MUTE 0x0200 /* DAC1R_MUTE */
+#define WM8995_DAC1R_MUTE_MASK 0x0200 /* DAC1R_MUTE */
+#define WM8995_DAC1R_MUTE_SHIFT 9 /* DAC1R_MUTE */
+#define WM8995_DAC1R_MUTE_WIDTH 1 /* DAC1R_MUTE */
+#define WM8995_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8995_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8995_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8995_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8995_DAC1R_VOL_MASK 0x00FF /* DAC1R_VOL - [7:0] */
+#define WM8995_DAC1R_VOL_SHIFT 0 /* DAC1R_VOL - [7:0] */
+#define WM8995_DAC1R_VOL_WIDTH 8 /* DAC1R_VOL - [7:0] */
+
+/*
+ * R26 (0x1A) - DAC2 Left Volume
+ */
+#define WM8995_DAC2L_MUTE 0x0200 /* DAC2L_MUTE */
+#define WM8995_DAC2L_MUTE_MASK 0x0200 /* DAC2L_MUTE */
+#define WM8995_DAC2L_MUTE_SHIFT 9 /* DAC2L_MUTE */
+#define WM8995_DAC2L_MUTE_WIDTH 1 /* DAC2L_MUTE */
+#define WM8995_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8995_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8995_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8995_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8995_DAC2L_VOL_MASK 0x00FF /* DAC2L_VOL - [7:0] */
+#define WM8995_DAC2L_VOL_SHIFT 0 /* DAC2L_VOL - [7:0] */
+#define WM8995_DAC2L_VOL_WIDTH 8 /* DAC2L_VOL - [7:0] */
+
+/*
+ * R27 (0x1B) - DAC2 Right Volume
+ */
+#define WM8995_DAC2R_MUTE 0x0200 /* DAC2R_MUTE */
+#define WM8995_DAC2R_MUTE_MASK 0x0200 /* DAC2R_MUTE */
+#define WM8995_DAC2R_MUTE_SHIFT 9 /* DAC2R_MUTE */
+#define WM8995_DAC2R_MUTE_WIDTH 1 /* DAC2R_MUTE */
+#define WM8995_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8995_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8995_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8995_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8995_DAC2R_VOL_MASK 0x00FF /* DAC2R_VOL - [7:0] */
+#define WM8995_DAC2R_VOL_SHIFT 0 /* DAC2R_VOL - [7:0] */
+#define WM8995_DAC2R_VOL_WIDTH 8 /* DAC2R_VOL - [7:0] */
+
+/*
+ * R28 (0x1C) - Output Volume ZC (1)
+ */
+#define WM8995_HPOUT2L_ZC 0x0008 /* HPOUT2L_ZC */
+#define WM8995_HPOUT2L_ZC_MASK 0x0008 /* HPOUT2L_ZC */
+#define WM8995_HPOUT2L_ZC_SHIFT 3 /* HPOUT2L_ZC */
+#define WM8995_HPOUT2L_ZC_WIDTH 1 /* HPOUT2L_ZC */
+#define WM8995_HPOUT2R_ZC 0x0004 /* HPOUT2R_ZC */
+#define WM8995_HPOUT2R_ZC_MASK 0x0004 /* HPOUT2R_ZC */
+#define WM8995_HPOUT2R_ZC_SHIFT 2 /* HPOUT2R_ZC */
+#define WM8995_HPOUT2R_ZC_WIDTH 1 /* HPOUT2R_ZC */
+#define WM8995_HPOUT1L_ZC 0x0002 /* HPOUT1L_ZC */
+#define WM8995_HPOUT1L_ZC_MASK 0x0002 /* HPOUT1L_ZC */
+#define WM8995_HPOUT1L_ZC_SHIFT 1 /* HPOUT1L_ZC */
+#define WM8995_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */
+#define WM8995_HPOUT1R_ZC 0x0001 /* HPOUT1R_ZC */
+#define WM8995_HPOUT1R_ZC_MASK 0x0001 /* HPOUT1R_ZC */
+#define WM8995_HPOUT1R_ZC_SHIFT 0 /* HPOUT1R_ZC */
+#define WM8995_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */
+
+/*
+ * R32 (0x20) - MICBIAS (1)
+ */
+#define WM8995_MICB1_MODE 0x0008 /* MICB1_MODE */
+#define WM8995_MICB1_MODE_MASK 0x0008 /* MICB1_MODE */
+#define WM8995_MICB1_MODE_SHIFT 3 /* MICB1_MODE */
+#define WM8995_MICB1_MODE_WIDTH 1 /* MICB1_MODE */
+#define WM8995_MICB1_LVL_MASK 0x0006 /* MICB1_LVL - [2:1] */
+#define WM8995_MICB1_LVL_SHIFT 1 /* MICB1_LVL - [2:1] */
+#define WM8995_MICB1_LVL_WIDTH 2 /* MICB1_LVL - [2:1] */
+#define WM8995_MICB1_DISCH 0x0001 /* MICB1_DISCH */
+#define WM8995_MICB1_DISCH_MASK 0x0001 /* MICB1_DISCH */
+#define WM8995_MICB1_DISCH_SHIFT 0 /* MICB1_DISCH */
+#define WM8995_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */
+
+/*
+ * R33 (0x21) - MICBIAS (2)
+ */
+#define WM8995_MICB2_MODE 0x0008 /* MICB2_MODE */
+#define WM8995_MICB2_MODE_MASK 0x0008 /* MICB2_MODE */
+#define WM8995_MICB2_MODE_SHIFT 3 /* MICB2_MODE */
+#define WM8995_MICB2_MODE_WIDTH 1 /* MICB2_MODE */
+#define WM8995_MICB2_LVL_MASK 0x0006 /* MICB2_LVL - [2:1] */
+#define WM8995_MICB2_LVL_SHIFT 1 /* MICB2_LVL - [2:1] */
+#define WM8995_MICB2_LVL_WIDTH 2 /* MICB2_LVL - [2:1] */
+#define WM8995_MICB2_DISCH 0x0001 /* MICB2_DISCH */
+#define WM8995_MICB2_DISCH_MASK 0x0001 /* MICB2_DISCH */
+#define WM8995_MICB2_DISCH_SHIFT 0 /* MICB2_DISCH */
+#define WM8995_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */
+
+/*
+ * R40 (0x28) - LDO 1
+ */
+#define WM8995_LDO1_MODE 0x0020 /* LDO1_MODE */
+#define WM8995_LDO1_MODE_MASK 0x0020 /* LDO1_MODE */
+#define WM8995_LDO1_MODE_SHIFT 5 /* LDO1_MODE */
+#define WM8995_LDO1_MODE_WIDTH 1 /* LDO1_MODE */
+#define WM8995_LDO1_VSEL_MASK 0x0006 /* LDO1_VSEL - [2:1] */
+#define WM8995_LDO1_VSEL_SHIFT 1 /* LDO1_VSEL - [2:1] */
+#define WM8995_LDO1_VSEL_WIDTH 2 /* LDO1_VSEL - [2:1] */
+#define WM8995_LDO1_DISCH 0x0001 /* LDO1_DISCH */
+#define WM8995_LDO1_DISCH_MASK 0x0001 /* LDO1_DISCH */
+#define WM8995_LDO1_DISCH_SHIFT 0 /* LDO1_DISCH */
+#define WM8995_LDO1_DISCH_WIDTH 1 /* LDO1_DISCH */
+
+/*
+ * R41 (0x29) - LDO 2
+ */
+#define WM8995_LDO2_MODE 0x0020 /* LDO2_MODE */
+#define WM8995_LDO2_MODE_MASK 0x0020 /* LDO2_MODE */
+#define WM8995_LDO2_MODE_SHIFT 5 /* LDO2_MODE */
+#define WM8995_LDO2_MODE_WIDTH 1 /* LDO2_MODE */
+#define WM8995_LDO2_VSEL_MASK 0x001E /* LDO2_VSEL - [4:1] */
+#define WM8995_LDO2_VSEL_SHIFT 1 /* LDO2_VSEL - [4:1] */
+#define WM8995_LDO2_VSEL_WIDTH 4 /* LDO2_VSEL - [4:1] */
+#define WM8995_LDO2_DISCH 0x0001 /* LDO2_DISCH */
+#define WM8995_LDO2_DISCH_MASK 0x0001 /* LDO2_DISCH */
+#define WM8995_LDO2_DISCH_SHIFT 0 /* LDO2_DISCH */
+#define WM8995_LDO2_DISCH_WIDTH 1 /* LDO2_DISCH */
+
+/*
+ * R48 (0x30) - Accessory Detect Mode1
+ */
+#define WM8995_JD_MODE_MASK 0x0003 /* JD_MODE - [1:0] */
+#define WM8995_JD_MODE_SHIFT 0 /* JD_MODE - [1:0] */
+#define WM8995_JD_MODE_WIDTH 2 /* JD_MODE - [1:0] */
+
+/*
+ * R49 (0x31) - Accessory Detect Mode2
+ */
+#define WM8995_VID_ENA 0x0001 /* VID_ENA */
+#define WM8995_VID_ENA_MASK 0x0001 /* VID_ENA */
+#define WM8995_VID_ENA_SHIFT 0 /* VID_ENA */
+#define WM8995_VID_ENA_WIDTH 1 /* VID_ENA */
+
+/*
+ * R52 (0x34) - Headphone Detect1
+ */
+#define WM8995_HP_RAMPRATE 0x0002 /* HP_RAMPRATE */
+#define WM8995_HP_RAMPRATE_MASK 0x0002 /* HP_RAMPRATE */
+#define WM8995_HP_RAMPRATE_SHIFT 1 /* HP_RAMPRATE */
+#define WM8995_HP_RAMPRATE_WIDTH 1 /* HP_RAMPRATE */
+#define WM8995_HP_POLL 0x0001 /* HP_POLL */
+#define WM8995_HP_POLL_MASK 0x0001 /* HP_POLL */
+#define WM8995_HP_POLL_SHIFT 0 /* HP_POLL */
+#define WM8995_HP_POLL_WIDTH 1 /* HP_POLL */
+
+/*
+ * R53 (0x35) - Headphone Detect2
+ */
+#define WM8995_HP_DONE 0x0080 /* HP_DONE */
+#define WM8995_HP_DONE_MASK 0x0080 /* HP_DONE */
+#define WM8995_HP_DONE_SHIFT 7 /* HP_DONE */
+#define WM8995_HP_DONE_WIDTH 1 /* HP_DONE */
+#define WM8995_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */
+#define WM8995_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */
+#define WM8995_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */
+
+/*
+ * R56 (0x38) - Mic Detect (1)
+ */
+#define WM8995_MICD_RATE_MASK 0x7800 /* MICD_RATE - [14:11] */
+#define WM8995_MICD_RATE_SHIFT 11 /* MICD_RATE - [14:11] */
+#define WM8995_MICD_RATE_WIDTH 4 /* MICD_RATE - [14:11] */
+#define WM8995_MICD_LVL_SEL_MASK 0x01F8 /* MICD_LVL_SEL - [8:3] */
+#define WM8995_MICD_LVL_SEL_SHIFT 3 /* MICD_LVL_SEL - [8:3] */
+#define WM8995_MICD_LVL_SEL_WIDTH 6 /* MICD_LVL_SEL - [8:3] */
+#define WM8995_MICD_DBTIME 0x0002 /* MICD_DBTIME */
+#define WM8995_MICD_DBTIME_MASK 0x0002 /* MICD_DBTIME */
+#define WM8995_MICD_DBTIME_SHIFT 1 /* MICD_DBTIME */
+#define WM8995_MICD_DBTIME_WIDTH 1 /* MICD_DBTIME */
+#define WM8995_MICD_ENA 0x0001 /* MICD_ENA */
+#define WM8995_MICD_ENA_MASK 0x0001 /* MICD_ENA */
+#define WM8995_MICD_ENA_SHIFT 0 /* MICD_ENA */
+#define WM8995_MICD_ENA_WIDTH 1 /* MICD_ENA */
+
+/*
+ * R57 (0x39) - Mic Detect (2)
+ */
+#define WM8995_MICD_LVL_MASK 0x01FC /* MICD_LVL - [8:2] */
+#define WM8995_MICD_LVL_SHIFT 2 /* MICD_LVL - [8:2] */
+#define WM8995_MICD_LVL_WIDTH 7 /* MICD_LVL - [8:2] */
+#define WM8995_MICD_VALID 0x0002 /* MICD_VALID */
+#define WM8995_MICD_VALID_MASK 0x0002 /* MICD_VALID */
+#define WM8995_MICD_VALID_SHIFT 1 /* MICD_VALID */
+#define WM8995_MICD_VALID_WIDTH 1 /* MICD_VALID */
+#define WM8995_MICD_STS 0x0001 /* MICD_STS */
+#define WM8995_MICD_STS_MASK 0x0001 /* MICD_STS */
+#define WM8995_MICD_STS_SHIFT 0 /* MICD_STS */
+#define WM8995_MICD_STS_WIDTH 1 /* MICD_STS */
+
+/*
+ * R64 (0x40) - Charge Pump (1)
+ */
+#define WM8995_CP_ENA 0x8000 /* CP_ENA */
+#define WM8995_CP_ENA_MASK 0x8000 /* CP_ENA */
+#define WM8995_CP_ENA_SHIFT 15 /* CP_ENA */
+#define WM8995_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R69 (0x45) - Class W (1)
+ */
+#define WM8995_CP_DYN_SRC_SEL_MASK 0x0300 /* CP_DYN_SRC_SEL - [9:8] */
+#define WM8995_CP_DYN_SRC_SEL_SHIFT 8 /* CP_DYN_SRC_SEL - [9:8] */
+#define WM8995_CP_DYN_SRC_SEL_WIDTH 2 /* CP_DYN_SRC_SEL - [9:8] */
+#define WM8995_CP_DYN_PWR 0x0001 /* CP_DYN_PWR */
+#define WM8995_CP_DYN_PWR_MASK 0x0001 /* CP_DYN_PWR */
+#define WM8995_CP_DYN_PWR_SHIFT 0 /* CP_DYN_PWR */
+#define WM8995_CP_DYN_PWR_WIDTH 1 /* CP_DYN_PWR */
+
+/*
+ * R80 (0x50) - DC Servo (1)
+ */
+#define WM8995_DCS_ENA_CHAN_3 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8995_DCS_ENA_CHAN_3_MASK 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8995_DCS_ENA_CHAN_3_SHIFT 3 /* DCS_ENA_CHAN_3 */
+#define WM8995_DCS_ENA_CHAN_3_WIDTH 1 /* DCS_ENA_CHAN_3 */
+#define WM8995_DCS_ENA_CHAN_2 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8995_DCS_ENA_CHAN_2_MASK 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8995_DCS_ENA_CHAN_2_SHIFT 2 /* DCS_ENA_CHAN_2 */
+#define WM8995_DCS_ENA_CHAN_2_WIDTH 1 /* DCS_ENA_CHAN_2 */
+#define WM8995_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8995_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8995_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */
+#define WM8995_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */
+#define WM8995_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8995_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8995_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */
+#define WM8995_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */
+
+/*
+ * R81 (0x51) - DC Servo (2)
+ */
+#define WM8995_DCS_TRIG_SINGLE_3 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8995_DCS_TRIG_SINGLE_3_MASK 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8995_DCS_TRIG_SINGLE_3_SHIFT 15 /* DCS_TRIG_SINGLE_3 */
+#define WM8995_DCS_TRIG_SINGLE_3_WIDTH 1 /* DCS_TRIG_SINGLE_3 */
+#define WM8995_DCS_TRIG_SINGLE_2 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8995_DCS_TRIG_SINGLE_2_MASK 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8995_DCS_TRIG_SINGLE_2_SHIFT 14 /* DCS_TRIG_SINGLE_2 */
+#define WM8995_DCS_TRIG_SINGLE_2_WIDTH 1 /* DCS_TRIG_SINGLE_2 */
+#define WM8995_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8995_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8995_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */
+#define WM8995_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */
+#define WM8995_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8995_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8995_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */
+#define WM8995_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */
+#define WM8995_DCS_TRIG_SERIES_3 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8995_DCS_TRIG_SERIES_3_MASK 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8995_DCS_TRIG_SERIES_3_SHIFT 11 /* DCS_TRIG_SERIES_3 */
+#define WM8995_DCS_TRIG_SERIES_3_WIDTH 1 /* DCS_TRIG_SERIES_3 */
+#define WM8995_DCS_TRIG_SERIES_2 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8995_DCS_TRIG_SERIES_2_MASK 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8995_DCS_TRIG_SERIES_2_SHIFT 10 /* DCS_TRIG_SERIES_2 */
+#define WM8995_DCS_TRIG_SERIES_2_WIDTH 1 /* DCS_TRIG_SERIES_2 */
+#define WM8995_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8995_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8995_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */
+#define WM8995_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */
+#define WM8995_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8995_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8995_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */
+#define WM8995_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */
+#define WM8995_DCS_TRIG_STARTUP_3 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8995_DCS_TRIG_STARTUP_3_MASK 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8995_DCS_TRIG_STARTUP_3_SHIFT 7 /* DCS_TRIG_STARTUP_3 */
+#define WM8995_DCS_TRIG_STARTUP_3_WIDTH 1 /* DCS_TRIG_STARTUP_3 */
+#define WM8995_DCS_TRIG_STARTUP_2 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8995_DCS_TRIG_STARTUP_2_MASK 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8995_DCS_TRIG_STARTUP_2_SHIFT 6 /* DCS_TRIG_STARTUP_2 */
+#define WM8995_DCS_TRIG_STARTUP_2_WIDTH 1 /* DCS_TRIG_STARTUP_2 */
+#define WM8995_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8995_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8995_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */
+#define WM8995_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */
+#define WM8995_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8995_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8995_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */
+#define WM8995_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */
+#define WM8995_DCS_TRIG_DAC_WR_3 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8995_DCS_TRIG_DAC_WR_3_MASK 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8995_DCS_TRIG_DAC_WR_3_SHIFT 3 /* DCS_TRIG_DAC_WR_3 */
+#define WM8995_DCS_TRIG_DAC_WR_3_WIDTH 1 /* DCS_TRIG_DAC_WR_3 */
+#define WM8995_DCS_TRIG_DAC_WR_2 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8995_DCS_TRIG_DAC_WR_2_MASK 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8995_DCS_TRIG_DAC_WR_2_SHIFT 2 /* DCS_TRIG_DAC_WR_2 */
+#define WM8995_DCS_TRIG_DAC_WR_2_WIDTH 1 /* DCS_TRIG_DAC_WR_2 */
+#define WM8995_DCS_TRIG_DAC_WR_1 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8995_DCS_TRIG_DAC_WR_1_MASK 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8995_DCS_TRIG_DAC_WR_1_SHIFT 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8995_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8995_DCS_TRIG_DAC_WR_0 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8995_DCS_TRIG_DAC_WR_0_MASK 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8995_DCS_TRIG_DAC_WR_0_SHIFT 0 /* DCS_TRIG_DAC_WR_0 */
+#define WM8995_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */
+
+/*
+ * R82 (0x52) - DC Servo (3)
+ */
+#define WM8995_DCS_TIMER_PERIOD_23_MASK 0x0F00 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8995_DCS_TIMER_PERIOD_23_SHIFT 8 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8995_DCS_TIMER_PERIOD_23_WIDTH 4 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8995_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8995_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8995_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R84 (0x54) - DC Servo (5)
+ */
+#define WM8995_DCS_SERIES_NO_23_MASK 0x7F00 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8995_DCS_SERIES_NO_23_SHIFT 8 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8995_DCS_SERIES_NO_23_WIDTH 7 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8995_DCS_SERIES_NO_01_MASK 0x007F /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8995_DCS_SERIES_NO_01_SHIFT 0 /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8995_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [6:0] */
+
+/*
+ * R85 (0x55) - DC Servo (6)
+ */
+#define WM8995_DCS_DAC_WR_VAL_3_MASK 0xFF00 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8995_DCS_DAC_WR_VAL_3_SHIFT 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8995_DCS_DAC_WR_VAL_3_WIDTH 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8995_DCS_DAC_WR_VAL_2_MASK 0x00FF /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8995_DCS_DAC_WR_VAL_2_SHIFT 0 /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8995_DCS_DAC_WR_VAL_2_WIDTH 8 /* DCS_DAC_WR_VAL_2 - [7:0] */
+
+/*
+ * R86 (0x56) - DC Servo (7)
+ */
+#define WM8995_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8995_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8995_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8995_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8995_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8995_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R87 (0x57) - DC Servo Readback 0
+ */
+#define WM8995_DCS_CAL_COMPLETE_MASK 0x0F00 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8995_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8995_DCS_CAL_COMPLETE_WIDTH 4 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8995_DCS_DAC_WR_COMPLETE_MASK 0x00F0 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8995_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8995_DCS_DAC_WR_COMPLETE_WIDTH 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8995_DCS_STARTUP_COMPLETE_MASK 0x000F /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8995_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8995_DCS_STARTUP_COMPLETE_WIDTH 4 /* DCS_STARTUP_COMPLETE - [3:0] */
+
+/*
+ * R96 (0x60) - Analogue HP (1)
+ */
+#define WM8995_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8995_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8995_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */
+#define WM8995_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */
+#define WM8995_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */
+#define WM8995_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */
+#define WM8995_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */
+#define WM8995_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */
+#define WM8995_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */
+#define WM8995_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */
+#define WM8995_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */
+#define WM8995_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */
+#define WM8995_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8995_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8995_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */
+#define WM8995_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */
+#define WM8995_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */
+#define WM8995_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */
+#define WM8995_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */
+#define WM8995_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */
+#define WM8995_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */
+#define WM8995_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */
+#define WM8995_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */
+#define WM8995_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */
+
+/*
+ * R97 (0x61) - Analogue HP (2)
+ */
+#define WM8995_HPOUT2L_RMV_SHORT 0x0080 /* HPOUT2L_RMV_SHORT */
+#define WM8995_HPOUT2L_RMV_SHORT_MASK 0x0080 /* HPOUT2L_RMV_SHORT */
+#define WM8995_HPOUT2L_RMV_SHORT_SHIFT 7 /* HPOUT2L_RMV_SHORT */
+#define WM8995_HPOUT2L_RMV_SHORT_WIDTH 1 /* HPOUT2L_RMV_SHORT */
+#define WM8995_HPOUT2L_OUTP 0x0040 /* HPOUT2L_OUTP */
+#define WM8995_HPOUT2L_OUTP_MASK 0x0040 /* HPOUT2L_OUTP */
+#define WM8995_HPOUT2L_OUTP_SHIFT 6 /* HPOUT2L_OUTP */
+#define WM8995_HPOUT2L_OUTP_WIDTH 1 /* HPOUT2L_OUTP */
+#define WM8995_HPOUT2L_DLY 0x0020 /* HPOUT2L_DLY */
+#define WM8995_HPOUT2L_DLY_MASK 0x0020 /* HPOUT2L_DLY */
+#define WM8995_HPOUT2L_DLY_SHIFT 5 /* HPOUT2L_DLY */
+#define WM8995_HPOUT2L_DLY_WIDTH 1 /* HPOUT2L_DLY */
+#define WM8995_HPOUT2R_RMV_SHORT 0x0008 /* HPOUT2R_RMV_SHORT */
+#define WM8995_HPOUT2R_RMV_SHORT_MASK 0x0008 /* HPOUT2R_RMV_SHORT */
+#define WM8995_HPOUT2R_RMV_SHORT_SHIFT 3 /* HPOUT2R_RMV_SHORT */
+#define WM8995_HPOUT2R_RMV_SHORT_WIDTH 1 /* HPOUT2R_RMV_SHORT */
+#define WM8995_HPOUT2R_OUTP 0x0004 /* HPOUT2R_OUTP */
+#define WM8995_HPOUT2R_OUTP_MASK 0x0004 /* HPOUT2R_OUTP */
+#define WM8995_HPOUT2R_OUTP_SHIFT 2 /* HPOUT2R_OUTP */
+#define WM8995_HPOUT2R_OUTP_WIDTH 1 /* HPOUT2R_OUTP */
+#define WM8995_HPOUT2R_DLY 0x0002 /* HPOUT2R_DLY */
+#define WM8995_HPOUT2R_DLY_MASK 0x0002 /* HPOUT2R_DLY */
+#define WM8995_HPOUT2R_DLY_SHIFT 1 /* HPOUT2R_DLY */
+#define WM8995_HPOUT2R_DLY_WIDTH 1 /* HPOUT2R_DLY */
+
+/*
+ * R256 (0x100) - Chip Revision
+ */
+#define WM8995_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */
+#define WM8995_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */
+#define WM8995_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */
+
+/*
+ * R257 (0x101) - Control Interface (1)
+ */
+#define WM8995_REG_SYNC 0x8000 /* REG_SYNC */
+#define WM8995_REG_SYNC_MASK 0x8000 /* REG_SYNC */
+#define WM8995_REG_SYNC_SHIFT 15 /* REG_SYNC */
+#define WM8995_REG_SYNC_WIDTH 1 /* REG_SYNC */
+#define WM8995_SPI_CONTRD 0x0040 /* SPI_CONTRD */
+#define WM8995_SPI_CONTRD_MASK 0x0040 /* SPI_CONTRD */
+#define WM8995_SPI_CONTRD_SHIFT 6 /* SPI_CONTRD */
+#define WM8995_SPI_CONTRD_WIDTH 1 /* SPI_CONTRD */
+#define WM8995_SPI_4WIRE 0x0020 /* SPI_4WIRE */
+#define WM8995_SPI_4WIRE_MASK 0x0020 /* SPI_4WIRE */
+#define WM8995_SPI_4WIRE_SHIFT 5 /* SPI_4WIRE */
+#define WM8995_SPI_4WIRE_WIDTH 1 /* SPI_4WIRE */
+#define WM8995_SPI_CFG 0x0010 /* SPI_CFG */
+#define WM8995_SPI_CFG_MASK 0x0010 /* SPI_CFG */
+#define WM8995_SPI_CFG_SHIFT 4 /* SPI_CFG */
+#define WM8995_SPI_CFG_WIDTH 1 /* SPI_CFG */
+#define WM8995_AUTO_INC 0x0004 /* AUTO_INC */
+#define WM8995_AUTO_INC_MASK 0x0004 /* AUTO_INC */
+#define WM8995_AUTO_INC_SHIFT 2 /* AUTO_INC */
+#define WM8995_AUTO_INC_WIDTH 1 /* AUTO_INC */
+
+/*
+ * R258 (0x102) - Control Interface (2)
+ */
+#define WM8995_CTRL_IF_SRC 0x0001 /* CTRL_IF_SRC */
+#define WM8995_CTRL_IF_SRC_MASK 0x0001 /* CTRL_IF_SRC */
+#define WM8995_CTRL_IF_SRC_SHIFT 0 /* CTRL_IF_SRC */
+#define WM8995_CTRL_IF_SRC_WIDTH 1 /* CTRL_IF_SRC */
+
+/*
+ * R272 (0x110) - Write Sequencer Ctrl (1)
+ */
+#define WM8995_WSEQ_ENA 0x8000 /* WSEQ_ENA */
+#define WM8995_WSEQ_ENA_MASK 0x8000 /* WSEQ_ENA */
+#define WM8995_WSEQ_ENA_SHIFT 15 /* WSEQ_ENA */
+#define WM8995_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM8995_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM8995_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM8995_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM8995_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8995_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM8995_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM8995_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM8995_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8995_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */
+#define WM8995_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */
+#define WM8995_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */
+
+/*
+ * R273 (0x111) - Write Sequencer Ctrl (2)
+ */
+#define WM8995_WSEQ_BUSY 0x0100 /* WSEQ_BUSY */
+#define WM8995_WSEQ_BUSY_MASK 0x0100 /* WSEQ_BUSY */
+#define WM8995_WSEQ_BUSY_SHIFT 8 /* WSEQ_BUSY */
+#define WM8995_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+#define WM8995_WSEQ_CURRENT_INDEX_MASK 0x007F /* WSEQ_CURRENT_INDEX - [6:0] */
+#define WM8995_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [6:0] */
+#define WM8995_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [6:0] */
+
+/*
+ * R512 (0x200) - AIF1 Clocking (1)
+ */
+#define WM8995_AIF1CLK_SRC_MASK 0x0018 /* AIF1CLK_SRC - [4:3] */
+#define WM8995_AIF1CLK_SRC_SHIFT 3 /* AIF1CLK_SRC - [4:3] */
+#define WM8995_AIF1CLK_SRC_WIDTH 2 /* AIF1CLK_SRC - [4:3] */
+#define WM8995_AIF1CLK_INV 0x0004 /* AIF1CLK_INV */
+#define WM8995_AIF1CLK_INV_MASK 0x0004 /* AIF1CLK_INV */
+#define WM8995_AIF1CLK_INV_SHIFT 2 /* AIF1CLK_INV */
+#define WM8995_AIF1CLK_INV_WIDTH 1 /* AIF1CLK_INV */
+#define WM8995_AIF1CLK_DIV 0x0002 /* AIF1CLK_DIV */
+#define WM8995_AIF1CLK_DIV_MASK 0x0002 /* AIF1CLK_DIV */
+#define WM8995_AIF1CLK_DIV_SHIFT 1 /* AIF1CLK_DIV */
+#define WM8995_AIF1CLK_DIV_WIDTH 1 /* AIF1CLK_DIV */
+#define WM8995_AIF1CLK_ENA 0x0001 /* AIF1CLK_ENA */
+#define WM8995_AIF1CLK_ENA_MASK 0x0001 /* AIF1CLK_ENA */
+#define WM8995_AIF1CLK_ENA_SHIFT 0 /* AIF1CLK_ENA */
+#define WM8995_AIF1CLK_ENA_WIDTH 1 /* AIF1CLK_ENA */
+
+/*
+ * R513 (0x201) - AIF1 Clocking (2)
+ */
+#define WM8995_AIF1DAC_DIV_MASK 0x0038 /* AIF1DAC_DIV - [5:3] */
+#define WM8995_AIF1DAC_DIV_SHIFT 3 /* AIF1DAC_DIV - [5:3] */
+#define WM8995_AIF1DAC_DIV_WIDTH 3 /* AIF1DAC_DIV - [5:3] */
+#define WM8995_AIF1ADC_DIV_MASK 0x0007 /* AIF1ADC_DIV - [2:0] */
+#define WM8995_AIF1ADC_DIV_SHIFT 0 /* AIF1ADC_DIV - [2:0] */
+#define WM8995_AIF1ADC_DIV_WIDTH 3 /* AIF1ADC_DIV - [2:0] */
+
+/*
+ * R516 (0x204) - AIF2 Clocking (1)
+ */
+#define WM8995_AIF2CLK_SRC_MASK 0x0018 /* AIF2CLK_SRC - [4:3] */
+#define WM8995_AIF2CLK_SRC_SHIFT 3 /* AIF2CLK_SRC - [4:3] */
+#define WM8995_AIF2CLK_SRC_WIDTH 2 /* AIF2CLK_SRC - [4:3] */
+#define WM8995_AIF2CLK_INV 0x0004 /* AIF2CLK_INV */
+#define WM8995_AIF2CLK_INV_MASK 0x0004 /* AIF2CLK_INV */
+#define WM8995_AIF2CLK_INV_SHIFT 2 /* AIF2CLK_INV */
+#define WM8995_AIF2CLK_INV_WIDTH 1 /* AIF2CLK_INV */
+#define WM8995_AIF2CLK_DIV 0x0002 /* AIF2CLK_DIV */
+#define WM8995_AIF2CLK_DIV_MASK 0x0002 /* AIF2CLK_DIV */
+#define WM8995_AIF2CLK_DIV_SHIFT 1 /* AIF2CLK_DIV */
+#define WM8995_AIF2CLK_DIV_WIDTH 1 /* AIF2CLK_DIV */
+#define WM8995_AIF2CLK_ENA 0x0001 /* AIF2CLK_ENA */
+#define WM8995_AIF2CLK_ENA_MASK 0x0001 /* AIF2CLK_ENA */
+#define WM8995_AIF2CLK_ENA_SHIFT 0 /* AIF2CLK_ENA */
+#define WM8995_AIF2CLK_ENA_WIDTH 1 /* AIF2CLK_ENA */
+
+/*
+ * R517 (0x205) - AIF2 Clocking (2)
+ */
+#define WM8995_AIF2DAC_DIV_MASK 0x0038 /* AIF2DAC_DIV - [5:3] */
+#define WM8995_AIF2DAC_DIV_SHIFT 3 /* AIF2DAC_DIV - [5:3] */
+#define WM8995_AIF2DAC_DIV_WIDTH 3 /* AIF2DAC_DIV - [5:3] */
+#define WM8995_AIF2ADC_DIV_MASK 0x0007 /* AIF2ADC_DIV - [2:0] */
+#define WM8995_AIF2ADC_DIV_SHIFT 0 /* AIF2ADC_DIV - [2:0] */
+#define WM8995_AIF2ADC_DIV_WIDTH 3 /* AIF2ADC_DIV - [2:0] */
+
+/*
+ * R520 (0x208) - Clocking (1)
+ */
+#define WM8995_LFCLK_ENA 0x0020 /* LFCLK_ENA */
+#define WM8995_LFCLK_ENA_MASK 0x0020 /* LFCLK_ENA */
+#define WM8995_LFCLK_ENA_SHIFT 5 /* LFCLK_ENA */
+#define WM8995_LFCLK_ENA_WIDTH 1 /* LFCLK_ENA */
+#define WM8995_TOCLK_ENA 0x0010 /* TOCLK_ENA */
+#define WM8995_TOCLK_ENA_MASK 0x0010 /* TOCLK_ENA */
+#define WM8995_TOCLK_ENA_SHIFT 4 /* TOCLK_ENA */
+#define WM8995_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */
+#define WM8995_AIF1DSPCLK_ENA 0x0008 /* AIF1DSPCLK_ENA */
+#define WM8995_AIF1DSPCLK_ENA_MASK 0x0008 /* AIF1DSPCLK_ENA */
+#define WM8995_AIF1DSPCLK_ENA_SHIFT 3 /* AIF1DSPCLK_ENA */
+#define WM8995_AIF1DSPCLK_ENA_WIDTH 1 /* AIF1DSPCLK_ENA */
+#define WM8995_AIF2DSPCLK_ENA 0x0004 /* AIF2DSPCLK_ENA */
+#define WM8995_AIF2DSPCLK_ENA_MASK 0x0004 /* AIF2DSPCLK_ENA */
+#define WM8995_AIF2DSPCLK_ENA_SHIFT 2 /* AIF2DSPCLK_ENA */
+#define WM8995_AIF2DSPCLK_ENA_WIDTH 1 /* AIF2DSPCLK_ENA */
+#define WM8995_SYSDSPCLK_ENA 0x0002 /* SYSDSPCLK_ENA */
+#define WM8995_SYSDSPCLK_ENA_MASK 0x0002 /* SYSDSPCLK_ENA */
+#define WM8995_SYSDSPCLK_ENA_SHIFT 1 /* SYSDSPCLK_ENA */
+#define WM8995_SYSDSPCLK_ENA_WIDTH 1 /* SYSDSPCLK_ENA */
+#define WM8995_SYSCLK_SRC 0x0001 /* SYSCLK_SRC */
+#define WM8995_SYSCLK_SRC_MASK 0x0001 /* SYSCLK_SRC */
+#define WM8995_SYSCLK_SRC_SHIFT 0 /* SYSCLK_SRC */
+#define WM8995_SYSCLK_SRC_WIDTH 1 /* SYSCLK_SRC */
+
+/*
+ * R521 (0x209) - Clocking (2)
+ */
+#define WM8995_TOCLK_DIV_MASK 0x0700 /* TOCLK_DIV - [10:8] */
+#define WM8995_TOCLK_DIV_SHIFT 8 /* TOCLK_DIV - [10:8] */
+#define WM8995_TOCLK_DIV_WIDTH 3 /* TOCLK_DIV - [10:8] */
+#define WM8995_DBCLK_DIV_MASK 0x00F0 /* DBCLK_DIV - [7:4] */
+#define WM8995_DBCLK_DIV_SHIFT 4 /* DBCLK_DIV - [7:4] */
+#define WM8995_DBCLK_DIV_WIDTH 4 /* DBCLK_DIV - [7:4] */
+#define WM8995_OPCLK_DIV_MASK 0x0007 /* OPCLK_DIV - [2:0] */
+#define WM8995_OPCLK_DIV_SHIFT 0 /* OPCLK_DIV - [2:0] */
+#define WM8995_OPCLK_DIV_WIDTH 3 /* OPCLK_DIV - [2:0] */
+
+/*
+ * R528 (0x210) - AIF1 Rate
+ */
+#define WM8995_AIF1_SR_MASK 0x00F0 /* AIF1_SR - [7:4] */
+#define WM8995_AIF1_SR_SHIFT 4 /* AIF1_SR - [7:4] */
+#define WM8995_AIF1_SR_WIDTH 4 /* AIF1_SR - [7:4] */
+#define WM8995_AIF1CLK_RATE_MASK 0x000F /* AIF1CLK_RATE - [3:0] */
+#define WM8995_AIF1CLK_RATE_SHIFT 0 /* AIF1CLK_RATE - [3:0] */
+#define WM8995_AIF1CLK_RATE_WIDTH 4 /* AIF1CLK_RATE - [3:0] */
+
+/*
+ * R529 (0x211) - AIF2 Rate
+ */
+#define WM8995_AIF2_SR_MASK 0x00F0 /* AIF2_SR - [7:4] */
+#define WM8995_AIF2_SR_SHIFT 4 /* AIF2_SR - [7:4] */
+#define WM8995_AIF2_SR_WIDTH 4 /* AIF2_SR - [7:4] */
+#define WM8995_AIF2CLK_RATE_MASK 0x000F /* AIF2CLK_RATE - [3:0] */
+#define WM8995_AIF2CLK_RATE_SHIFT 0 /* AIF2CLK_RATE - [3:0] */
+#define WM8995_AIF2CLK_RATE_WIDTH 4 /* AIF2CLK_RATE - [3:0] */
+
+/*
+ * R530 (0x212) - Rate Status
+ */
+#define WM8995_SR_ERROR_MASK 0x000F /* SR_ERROR - [3:0] */
+#define WM8995_SR_ERROR_SHIFT 0 /* SR_ERROR - [3:0] */
+#define WM8995_SR_ERROR_WIDTH 4 /* SR_ERROR - [3:0] */
+
+/*
+ * R544 (0x220) - FLL1 Control (1)
+ */
+#define WM8995_FLL1_OSC_ENA 0x0002 /* FLL1_OSC_ENA */
+#define WM8995_FLL1_OSC_ENA_MASK 0x0002 /* FLL1_OSC_ENA */
+#define WM8995_FLL1_OSC_ENA_SHIFT 1 /* FLL1_OSC_ENA */
+#define WM8995_FLL1_OSC_ENA_WIDTH 1 /* FLL1_OSC_ENA */
+#define WM8995_FLL1_ENA 0x0001 /* FLL1_ENA */
+#define WM8995_FLL1_ENA_MASK 0x0001 /* FLL1_ENA */
+#define WM8995_FLL1_ENA_SHIFT 0 /* FLL1_ENA */
+#define WM8995_FLL1_ENA_WIDTH 1 /* FLL1_ENA */
+
+/*
+ * R545 (0x221) - FLL1 Control (2)
+ */
+#define WM8995_FLL1_OUTDIV_MASK 0x3F00 /* FLL1_OUTDIV - [13:8] */
+#define WM8995_FLL1_OUTDIV_SHIFT 8 /* FLL1_OUTDIV - [13:8] */
+#define WM8995_FLL1_OUTDIV_WIDTH 6 /* FLL1_OUTDIV - [13:8] */
+#define WM8995_FLL1_CTRL_RATE_MASK 0x0070 /* FLL1_CTRL_RATE - [6:4] */
+#define WM8995_FLL1_CTRL_RATE_SHIFT 4 /* FLL1_CTRL_RATE - [6:4] */
+#define WM8995_FLL1_CTRL_RATE_WIDTH 3 /* FLL1_CTRL_RATE - [6:4] */
+#define WM8995_FLL1_FRATIO_MASK 0x0007 /* FLL1_FRATIO - [2:0] */
+#define WM8995_FLL1_FRATIO_SHIFT 0 /* FLL1_FRATIO - [2:0] */
+#define WM8995_FLL1_FRATIO_WIDTH 3 /* FLL1_FRATIO - [2:0] */
+
+/*
+ * R546 (0x222) - FLL1 Control (3)
+ */
+#define WM8995_FLL1_K_MASK 0xFFFF /* FLL1_K - [15:0] */
+#define WM8995_FLL1_K_SHIFT 0 /* FLL1_K - [15:0] */
+#define WM8995_FLL1_K_WIDTH 16 /* FLL1_K - [15:0] */
+
+/*
+ * R547 (0x223) - FLL1 Control (4)
+ */
+#define WM8995_FLL1_N_MASK 0x7FE0 /* FLL1_N - [14:5] */
+#define WM8995_FLL1_N_SHIFT 5 /* FLL1_N - [14:5] */
+#define WM8995_FLL1_N_WIDTH 10 /* FLL1_N - [14:5] */
+#define WM8995_FLL1_LOOP_GAIN_MASK 0x000F /* FLL1_LOOP_GAIN - [3:0] */
+#define WM8995_FLL1_LOOP_GAIN_SHIFT 0 /* FLL1_LOOP_GAIN - [3:0] */
+#define WM8995_FLL1_LOOP_GAIN_WIDTH 4 /* FLL1_LOOP_GAIN - [3:0] */
+
+/*
+ * R548 (0x224) - FLL1 Control (5)
+ */
+#define WM8995_FLL1_FRC_NCO_VAL_MASK 0x1F80 /* FLL1_FRC_NCO_VAL - [12:7] */
+#define WM8995_FLL1_FRC_NCO_VAL_SHIFT 7 /* FLL1_FRC_NCO_VAL - [12:7] */
+#define WM8995_FLL1_FRC_NCO_VAL_WIDTH 6 /* FLL1_FRC_NCO_VAL - [12:7] */
+#define WM8995_FLL1_FRC_NCO 0x0040 /* FLL1_FRC_NCO */
+#define WM8995_FLL1_FRC_NCO_MASK 0x0040 /* FLL1_FRC_NCO */
+#define WM8995_FLL1_FRC_NCO_SHIFT 6 /* FLL1_FRC_NCO */
+#define WM8995_FLL1_FRC_NCO_WIDTH 1 /* FLL1_FRC_NCO */
+#define WM8995_FLL1_REFCLK_DIV_MASK 0x0018 /* FLL1_REFCLK_DIV - [4:3] */
+#define WM8995_FLL1_REFCLK_DIV_SHIFT 3 /* FLL1_REFCLK_DIV - [4:3] */
+#define WM8995_FLL1_REFCLK_DIV_WIDTH 2 /* FLL1_REFCLK_DIV - [4:3] */
+#define WM8995_FLL1_REFCLK_SRC_MASK 0x0003 /* FLL1_REFCLK_SRC - [1:0] */
+#define WM8995_FLL1_REFCLK_SRC_SHIFT 0 /* FLL1_REFCLK_SRC - [1:0] */
+#define WM8995_FLL1_REFCLK_SRC_WIDTH 2 /* FLL1_REFCLK_SRC - [1:0] */
+
+/*
+ * R576 (0x240) - FLL2 Control (1)
+ */
+#define WM8995_FLL2_OSC_ENA 0x0002 /* FLL2_OSC_ENA */
+#define WM8995_FLL2_OSC_ENA_MASK 0x0002 /* FLL2_OSC_ENA */
+#define WM8995_FLL2_OSC_ENA_SHIFT 1 /* FLL2_OSC_ENA */
+#define WM8995_FLL2_OSC_ENA_WIDTH 1 /* FLL2_OSC_ENA */
+#define WM8995_FLL2_ENA 0x0001 /* FLL2_ENA */
+#define WM8995_FLL2_ENA_MASK 0x0001 /* FLL2_ENA */
+#define WM8995_FLL2_ENA_SHIFT 0 /* FLL2_ENA */
+#define WM8995_FLL2_ENA_WIDTH 1 /* FLL2_ENA */
+
+/*
+ * R577 (0x241) - FLL2 Control (2)
+ */
+#define WM8995_FLL2_OUTDIV_MASK 0x3F00 /* FLL2_OUTDIV - [13:8] */
+#define WM8995_FLL2_OUTDIV_SHIFT 8 /* FLL2_OUTDIV - [13:8] */
+#define WM8995_FLL2_OUTDIV_WIDTH 6 /* FLL2_OUTDIV - [13:8] */
+#define WM8995_FLL2_CTRL_RATE_MASK 0x0070 /* FLL2_CTRL_RATE - [6:4] */
+#define WM8995_FLL2_CTRL_RATE_SHIFT 4 /* FLL2_CTRL_RATE - [6:4] */
+#define WM8995_FLL2_CTRL_RATE_WIDTH 3 /* FLL2_CTRL_RATE - [6:4] */
+#define WM8995_FLL2_FRATIO_MASK 0x0007 /* FLL2_FRATIO - [2:0] */
+#define WM8995_FLL2_FRATIO_SHIFT 0 /* FLL2_FRATIO - [2:0] */
+#define WM8995_FLL2_FRATIO_WIDTH 3 /* FLL2_FRATIO - [2:0] */
+
+/*
+ * R578 (0x242) - FLL2 Control (3)
+ */
+#define WM8995_FLL2_K_MASK 0xFFFF /* FLL2_K - [15:0] */
+#define WM8995_FLL2_K_SHIFT 0 /* FLL2_K - [15:0] */
+#define WM8995_FLL2_K_WIDTH 16 /* FLL2_K - [15:0] */
+
+/*
+ * R579 (0x243) - FLL2 Control (4)
+ */
+#define WM8995_FLL2_N_MASK 0x7FE0 /* FLL2_N - [14:5] */
+#define WM8995_FLL2_N_SHIFT 5 /* FLL2_N - [14:5] */
+#define WM8995_FLL2_N_WIDTH 10 /* FLL2_N - [14:5] */
+#define WM8995_FLL2_LOOP_GAIN_MASK 0x000F /* FLL2_LOOP_GAIN - [3:0] */
+#define WM8995_FLL2_LOOP_GAIN_SHIFT 0 /* FLL2_LOOP_GAIN - [3:0] */
+#define WM8995_FLL2_LOOP_GAIN_WIDTH 4 /* FLL2_LOOP_GAIN - [3:0] */
+
+/*
+ * R580 (0x244) - FLL2 Control (5)
+ */
+#define WM8995_FLL2_FRC_NCO_VAL_MASK 0x1F80 /* FLL2_FRC_NCO_VAL - [12:7] */
+#define WM8995_FLL2_FRC_NCO_VAL_SHIFT 7 /* FLL2_FRC_NCO_VAL - [12:7] */
+#define WM8995_FLL2_FRC_NCO_VAL_WIDTH 6 /* FLL2_FRC_NCO_VAL - [12:7] */
+#define WM8995_FLL2_FRC_NCO 0x0040 /* FLL2_FRC_NCO */
+#define WM8995_FLL2_FRC_NCO_MASK 0x0040 /* FLL2_FRC_NCO */
+#define WM8995_FLL2_FRC_NCO_SHIFT 6 /* FLL2_FRC_NCO */
+#define WM8995_FLL2_FRC_NCO_WIDTH 1 /* FLL2_FRC_NCO */
+#define WM8995_FLL2_REFCLK_DIV_MASK 0x0018 /* FLL2_REFCLK_DIV - [4:3] */
+#define WM8995_FLL2_REFCLK_DIV_SHIFT 3 /* FLL2_REFCLK_DIV - [4:3] */
+#define WM8995_FLL2_REFCLK_DIV_WIDTH 2 /* FLL2_REFCLK_DIV - [4:3] */
+#define WM8995_FLL2_REFCLK_SRC_MASK 0x0003 /* FLL2_REFCLK_SRC - [1:0] */
+#define WM8995_FLL2_REFCLK_SRC_SHIFT 0 /* FLL2_REFCLK_SRC - [1:0] */
+#define WM8995_FLL2_REFCLK_SRC_WIDTH 2 /* FLL2_REFCLK_SRC - [1:0] */
+
+/*
+ * R768 (0x300) - AIF1 Control (1)
+ */
+#define WM8995_AIF1ADCL_SRC 0x8000 /* AIF1ADCL_SRC */
+#define WM8995_AIF1ADCL_SRC_MASK 0x8000 /* AIF1ADCL_SRC */
+#define WM8995_AIF1ADCL_SRC_SHIFT 15 /* AIF1ADCL_SRC */
+#define WM8995_AIF1ADCL_SRC_WIDTH 1 /* AIF1ADCL_SRC */
+#define WM8995_AIF1ADCR_SRC 0x4000 /* AIF1ADCR_SRC */
+#define WM8995_AIF1ADCR_SRC_MASK 0x4000 /* AIF1ADCR_SRC */
+#define WM8995_AIF1ADCR_SRC_SHIFT 14 /* AIF1ADCR_SRC */
+#define WM8995_AIF1ADCR_SRC_WIDTH 1 /* AIF1ADCR_SRC */
+#define WM8995_AIF1ADC_TDM 0x2000 /* AIF1ADC_TDM */
+#define WM8995_AIF1ADC_TDM_MASK 0x2000 /* AIF1ADC_TDM */
+#define WM8995_AIF1ADC_TDM_SHIFT 13 /* AIF1ADC_TDM */
+#define WM8995_AIF1ADC_TDM_WIDTH 1 /* AIF1ADC_TDM */
+#define WM8995_AIF1_BCLK_INV 0x0100 /* AIF1_BCLK_INV */
+#define WM8995_AIF1_BCLK_INV_MASK 0x0100 /* AIF1_BCLK_INV */
+#define WM8995_AIF1_BCLK_INV_SHIFT 8 /* AIF1_BCLK_INV */
+#define WM8995_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */
+#define WM8995_AIF1_LRCLK_INV 0x0080 /* AIF1_LRCLK_INV */
+#define WM8995_AIF1_LRCLK_INV_MASK 0x0080 /* AIF1_LRCLK_INV */
+#define WM8995_AIF1_LRCLK_INV_SHIFT 7 /* AIF1_LRCLK_INV */
+#define WM8995_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */
+#define WM8995_AIF1_WL_MASK 0x0060 /* AIF1_WL - [6:5] */
+#define WM8995_AIF1_WL_SHIFT 5 /* AIF1_WL - [6:5] */
+#define WM8995_AIF1_WL_WIDTH 2 /* AIF1_WL - [6:5] */
+#define WM8995_AIF1_FMT_MASK 0x0018 /* AIF1_FMT - [4:3] */
+#define WM8995_AIF1_FMT_SHIFT 3 /* AIF1_FMT - [4:3] */
+#define WM8995_AIF1_FMT_WIDTH 2 /* AIF1_FMT - [4:3] */
+
+/*
+ * R769 (0x301) - AIF1 Control (2)
+ */
+#define WM8995_AIF1DACL_SRC 0x8000 /* AIF1DACL_SRC */
+#define WM8995_AIF1DACL_SRC_MASK 0x8000 /* AIF1DACL_SRC */
+#define WM8995_AIF1DACL_SRC_SHIFT 15 /* AIF1DACL_SRC */
+#define WM8995_AIF1DACL_SRC_WIDTH 1 /* AIF1DACL_SRC */
+#define WM8995_AIF1DACR_SRC 0x4000 /* AIF1DACR_SRC */
+#define WM8995_AIF1DACR_SRC_MASK 0x4000 /* AIF1DACR_SRC */
+#define WM8995_AIF1DACR_SRC_SHIFT 14 /* AIF1DACR_SRC */
+#define WM8995_AIF1DACR_SRC_WIDTH 1 /* AIF1DACR_SRC */
+#define WM8995_AIF1DAC_BOOST_MASK 0x0C00 /* AIF1DAC_BOOST - [11:10] */
+#define WM8995_AIF1DAC_BOOST_SHIFT 10 /* AIF1DAC_BOOST - [11:10] */
+#define WM8995_AIF1DAC_BOOST_WIDTH 2 /* AIF1DAC_BOOST - [11:10] */
+#define WM8995_AIF1DAC_COMP 0x0010 /* AIF1DAC_COMP */
+#define WM8995_AIF1DAC_COMP_MASK 0x0010 /* AIF1DAC_COMP */
+#define WM8995_AIF1DAC_COMP_SHIFT 4 /* AIF1DAC_COMP */
+#define WM8995_AIF1DAC_COMP_WIDTH 1 /* AIF1DAC_COMP */
+#define WM8995_AIF1DAC_COMPMODE 0x0008 /* AIF1DAC_COMPMODE */
+#define WM8995_AIF1DAC_COMPMODE_MASK 0x0008 /* AIF1DAC_COMPMODE */
+#define WM8995_AIF1DAC_COMPMODE_SHIFT 3 /* AIF1DAC_COMPMODE */
+#define WM8995_AIF1DAC_COMPMODE_WIDTH 1 /* AIF1DAC_COMPMODE */
+#define WM8995_AIF1ADC_COMP 0x0004 /* AIF1ADC_COMP */
+#define WM8995_AIF1ADC_COMP_MASK 0x0004 /* AIF1ADC_COMP */
+#define WM8995_AIF1ADC_COMP_SHIFT 2 /* AIF1ADC_COMP */
+#define WM8995_AIF1ADC_COMP_WIDTH 1 /* AIF1ADC_COMP */
+#define WM8995_AIF1ADC_COMPMODE 0x0002 /* AIF1ADC_COMPMODE */
+#define WM8995_AIF1ADC_COMPMODE_MASK 0x0002 /* AIF1ADC_COMPMODE */
+#define WM8995_AIF1ADC_COMPMODE_SHIFT 1 /* AIF1ADC_COMPMODE */
+#define WM8995_AIF1ADC_COMPMODE_WIDTH 1 /* AIF1ADC_COMPMODE */
+#define WM8995_AIF1_LOOPBACK 0x0001 /* AIF1_LOOPBACK */
+#define WM8995_AIF1_LOOPBACK_MASK 0x0001 /* AIF1_LOOPBACK */
+#define WM8995_AIF1_LOOPBACK_SHIFT 0 /* AIF1_LOOPBACK */
+#define WM8995_AIF1_LOOPBACK_WIDTH 1 /* AIF1_LOOPBACK */
+
+/*
+ * R770 (0x302) - AIF1 Master/Slave
+ */
+#define WM8995_AIF1_TRI 0x8000 /* AIF1_TRI */
+#define WM8995_AIF1_TRI_MASK 0x8000 /* AIF1_TRI */
+#define WM8995_AIF1_TRI_SHIFT 15 /* AIF1_TRI */
+#define WM8995_AIF1_TRI_WIDTH 1 /* AIF1_TRI */
+#define WM8995_AIF1_MSTR 0x4000 /* AIF1_MSTR */
+#define WM8995_AIF1_MSTR_MASK 0x4000 /* AIF1_MSTR */
+#define WM8995_AIF1_MSTR_SHIFT 14 /* AIF1_MSTR */
+#define WM8995_AIF1_MSTR_WIDTH 1 /* AIF1_MSTR */
+#define WM8995_AIF1_CLK_FRC 0x2000 /* AIF1_CLK_FRC */
+#define WM8995_AIF1_CLK_FRC_MASK 0x2000 /* AIF1_CLK_FRC */
+#define WM8995_AIF1_CLK_FRC_SHIFT 13 /* AIF1_CLK_FRC */
+#define WM8995_AIF1_CLK_FRC_WIDTH 1 /* AIF1_CLK_FRC */
+#define WM8995_AIF1_LRCLK_FRC 0x1000 /* AIF1_LRCLK_FRC */
+#define WM8995_AIF1_LRCLK_FRC_MASK 0x1000 /* AIF1_LRCLK_FRC */
+#define WM8995_AIF1_LRCLK_FRC_SHIFT 12 /* AIF1_LRCLK_FRC */
+#define WM8995_AIF1_LRCLK_FRC_WIDTH 1 /* AIF1_LRCLK_FRC */
+
+/*
+ * R771 (0x303) - AIF1 BCLK
+ */
+#define WM8995_AIF1_BCLK_DIV_MASK 0x00F0 /* AIF1_BCLK_DIV - [7:4] */
+#define WM8995_AIF1_BCLK_DIV_SHIFT 4 /* AIF1_BCLK_DIV - [7:4] */
+#define WM8995_AIF1_BCLK_DIV_WIDTH 4 /* AIF1_BCLK_DIV - [7:4] */
+
+/*
+ * R772 (0x304) - AIF1ADC LRCLK
+ */
+#define WM8995_AIF1ADC_LRCLK_DIR 0x0800 /* AIF1ADC_LRCLK_DIR */
+#define WM8995_AIF1ADC_LRCLK_DIR_MASK 0x0800 /* AIF1ADC_LRCLK_DIR */
+#define WM8995_AIF1ADC_LRCLK_DIR_SHIFT 11 /* AIF1ADC_LRCLK_DIR */
+#define WM8995_AIF1ADC_LRCLK_DIR_WIDTH 1 /* AIF1ADC_LRCLK_DIR */
+#define WM8995_AIF1ADC_RATE_MASK 0x07FF /* AIF1ADC_RATE - [10:0] */
+#define WM8995_AIF1ADC_RATE_SHIFT 0 /* AIF1ADC_RATE - [10:0] */
+#define WM8995_AIF1ADC_RATE_WIDTH 11 /* AIF1ADC_RATE - [10:0] */
+
+/*
+ * R773 (0x305) - AIF1DAC LRCLK
+ */
+#define WM8995_AIF1DAC_LRCLK_DIR 0x0800 /* AIF1DAC_LRCLK_DIR */
+#define WM8995_AIF1DAC_LRCLK_DIR_MASK 0x0800 /* AIF1DAC_LRCLK_DIR */
+#define WM8995_AIF1DAC_LRCLK_DIR_SHIFT 11 /* AIF1DAC_LRCLK_DIR */
+#define WM8995_AIF1DAC_LRCLK_DIR_WIDTH 1 /* AIF1DAC_LRCLK_DIR */
+#define WM8995_AIF1DAC_RATE_MASK 0x07FF /* AIF1DAC_RATE - [10:0] */
+#define WM8995_AIF1DAC_RATE_SHIFT 0 /* AIF1DAC_RATE - [10:0] */
+#define WM8995_AIF1DAC_RATE_WIDTH 11 /* AIF1DAC_RATE - [10:0] */
+
+/*
+ * R774 (0x306) - AIF1DAC Data
+ */
+#define WM8995_AIF1DACL_DAT_INV 0x0002 /* AIF1DACL_DAT_INV */
+#define WM8995_AIF1DACL_DAT_INV_MASK 0x0002 /* AIF1DACL_DAT_INV */
+#define WM8995_AIF1DACL_DAT_INV_SHIFT 1 /* AIF1DACL_DAT_INV */
+#define WM8995_AIF1DACL_DAT_INV_WIDTH 1 /* AIF1DACL_DAT_INV */
+#define WM8995_AIF1DACR_DAT_INV 0x0001 /* AIF1DACR_DAT_INV */
+#define WM8995_AIF1DACR_DAT_INV_MASK 0x0001 /* AIF1DACR_DAT_INV */
+#define WM8995_AIF1DACR_DAT_INV_SHIFT 0 /* AIF1DACR_DAT_INV */
+#define WM8995_AIF1DACR_DAT_INV_WIDTH 1 /* AIF1DACR_DAT_INV */
+
+/*
+ * R775 (0x307) - AIF1ADC Data
+ */
+#define WM8995_AIF1ADCL_DAT_INV 0x0002 /* AIF1ADCL_DAT_INV */
+#define WM8995_AIF1ADCL_DAT_INV_MASK 0x0002 /* AIF1ADCL_DAT_INV */
+#define WM8995_AIF1ADCL_DAT_INV_SHIFT 1 /* AIF1ADCL_DAT_INV */
+#define WM8995_AIF1ADCL_DAT_INV_WIDTH 1 /* AIF1ADCL_DAT_INV */
+#define WM8995_AIF1ADCR_DAT_INV 0x0001 /* AIF1ADCR_DAT_INV */
+#define WM8995_AIF1ADCR_DAT_INV_MASK 0x0001 /* AIF1ADCR_DAT_INV */
+#define WM8995_AIF1ADCR_DAT_INV_SHIFT 0 /* AIF1ADCR_DAT_INV */
+#define WM8995_AIF1ADCR_DAT_INV_WIDTH 1 /* AIF1ADCR_DAT_INV */
+
+/*
+ * R784 (0x310) - AIF2 Control (1)
+ */
+#define WM8995_AIF2ADCL_SRC 0x8000 /* AIF2ADCL_SRC */
+#define WM8995_AIF2ADCL_SRC_MASK 0x8000 /* AIF2ADCL_SRC */
+#define WM8995_AIF2ADCL_SRC_SHIFT 15 /* AIF2ADCL_SRC */
+#define WM8995_AIF2ADCL_SRC_WIDTH 1 /* AIF2ADCL_SRC */
+#define WM8995_AIF2ADCR_SRC 0x4000 /* AIF2ADCR_SRC */
+#define WM8995_AIF2ADCR_SRC_MASK 0x4000 /* AIF2ADCR_SRC */
+#define WM8995_AIF2ADCR_SRC_SHIFT 14 /* AIF2ADCR_SRC */
+#define WM8995_AIF2ADCR_SRC_WIDTH 1 /* AIF2ADCR_SRC */
+#define WM8995_AIF2ADC_TDM 0x2000 /* AIF2ADC_TDM */
+#define WM8995_AIF2ADC_TDM_MASK 0x2000 /* AIF2ADC_TDM */
+#define WM8995_AIF2ADC_TDM_SHIFT 13 /* AIF2ADC_TDM */
+#define WM8995_AIF2ADC_TDM_WIDTH 1 /* AIF2ADC_TDM */
+#define WM8995_AIF2ADC_TDM_CHAN 0x1000 /* AIF2ADC_TDM_CHAN */
+#define WM8995_AIF2ADC_TDM_CHAN_MASK 0x1000 /* AIF2ADC_TDM_CHAN */
+#define WM8995_AIF2ADC_TDM_CHAN_SHIFT 12 /* AIF2ADC_TDM_CHAN */
+#define WM8995_AIF2ADC_TDM_CHAN_WIDTH 1 /* AIF2ADC_TDM_CHAN */
+#define WM8995_AIF2_BCLK_INV 0x0100 /* AIF2_BCLK_INV */
+#define WM8995_AIF2_BCLK_INV_MASK 0x0100 /* AIF2_BCLK_INV */
+#define WM8995_AIF2_BCLK_INV_SHIFT 8 /* AIF2_BCLK_INV */
+#define WM8995_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */
+#define WM8995_AIF2_LRCLK_INV 0x0080 /* AIF2_LRCLK_INV */
+#define WM8995_AIF2_LRCLK_INV_MASK 0x0080 /* AIF2_LRCLK_INV */
+#define WM8995_AIF2_LRCLK_INV_SHIFT 7 /* AIF2_LRCLK_INV */
+#define WM8995_AIF2_LRCLK_INV_WIDTH 1 /* AIF2_LRCLK_INV */
+#define WM8995_AIF2_WL_MASK 0x0060 /* AIF2_WL - [6:5] */
+#define WM8995_AIF2_WL_SHIFT 5 /* AIF2_WL - [6:5] */
+#define WM8995_AIF2_WL_WIDTH 2 /* AIF2_WL - [6:5] */
+#define WM8995_AIF2_FMT_MASK 0x0018 /* AIF2_FMT - [4:3] */
+#define WM8995_AIF2_FMT_SHIFT 3 /* AIF2_FMT - [4:3] */
+#define WM8995_AIF2_FMT_WIDTH 2 /* AIF2_FMT - [4:3] */
+
+/*
+ * R785 (0x311) - AIF2 Control (2)
+ */
+#define WM8995_AIF2DACL_SRC 0x8000 /* AIF2DACL_SRC */
+#define WM8995_AIF2DACL_SRC_MASK 0x8000 /* AIF2DACL_SRC */
+#define WM8995_AIF2DACL_SRC_SHIFT 15 /* AIF2DACL_SRC */
+#define WM8995_AIF2DACL_SRC_WIDTH 1 /* AIF2DACL_SRC */
+#define WM8995_AIF2DACR_SRC 0x4000 /* AIF2DACR_SRC */
+#define WM8995_AIF2DACR_SRC_MASK 0x4000 /* AIF2DACR_SRC */
+#define WM8995_AIF2DACR_SRC_SHIFT 14 /* AIF2DACR_SRC */
+#define WM8995_AIF2DACR_SRC_WIDTH 1 /* AIF2DACR_SRC */
+#define WM8995_AIF2DAC_TDM 0x2000 /* AIF2DAC_TDM */
+#define WM8995_AIF2DAC_TDM_MASK 0x2000 /* AIF2DAC_TDM */
+#define WM8995_AIF2DAC_TDM_SHIFT 13 /* AIF2DAC_TDM */
+#define WM8995_AIF2DAC_TDM_WIDTH 1 /* AIF2DAC_TDM */
+#define WM8995_AIF2DAC_TDM_CHAN 0x1000 /* AIF2DAC_TDM_CHAN */
+#define WM8995_AIF2DAC_TDM_CHAN_MASK 0x1000 /* AIF2DAC_TDM_CHAN */
+#define WM8995_AIF2DAC_TDM_CHAN_SHIFT 12 /* AIF2DAC_TDM_CHAN */
+#define WM8995_AIF2DAC_TDM_CHAN_WIDTH 1 /* AIF2DAC_TDM_CHAN */
+#define WM8995_AIF2DAC_BOOST_MASK 0x0C00 /* AIF2DAC_BOOST - [11:10] */
+#define WM8995_AIF2DAC_BOOST_SHIFT 10 /* AIF2DAC_BOOST - [11:10] */
+#define WM8995_AIF2DAC_BOOST_WIDTH 2 /* AIF2DAC_BOOST - [11:10] */
+#define WM8995_AIF2DAC_COMP 0x0010 /* AIF2DAC_COMP */
+#define WM8995_AIF2DAC_COMP_MASK 0x0010 /* AIF2DAC_COMP */
+#define WM8995_AIF2DAC_COMP_SHIFT 4 /* AIF2DAC_COMP */
+#define WM8995_AIF2DAC_COMP_WIDTH 1 /* AIF2DAC_COMP */
+#define WM8995_AIF2DAC_COMPMODE 0x0008 /* AIF2DAC_COMPMODE */
+#define WM8995_AIF2DAC_COMPMODE_MASK 0x0008 /* AIF2DAC_COMPMODE */
+#define WM8995_AIF2DAC_COMPMODE_SHIFT 3 /* AIF2DAC_COMPMODE */
+#define WM8995_AIF2DAC_COMPMODE_WIDTH 1 /* AIF2DAC_COMPMODE */
+#define WM8995_AIF2ADC_COMP 0x0004 /* AIF2ADC_COMP */
+#define WM8995_AIF2ADC_COMP_MASK 0x0004 /* AIF2ADC_COMP */
+#define WM8995_AIF2ADC_COMP_SHIFT 2 /* AIF2ADC_COMP */
+#define WM8995_AIF2ADC_COMP_WIDTH 1 /* AIF2ADC_COMP */
+#define WM8995_AIF2ADC_COMPMODE 0x0002 /* AIF2ADC_COMPMODE */
+#define WM8995_AIF2ADC_COMPMODE_MASK 0x0002 /* AIF2ADC_COMPMODE */
+#define WM8995_AIF2ADC_COMPMODE_SHIFT 1 /* AIF2ADC_COMPMODE */
+#define WM8995_AIF2ADC_COMPMODE_WIDTH 1 /* AIF2ADC_COMPMODE */
+#define WM8995_AIF2_LOOPBACK 0x0001 /* AIF2_LOOPBACK */
+#define WM8995_AIF2_LOOPBACK_MASK 0x0001 /* AIF2_LOOPBACK */
+#define WM8995_AIF2_LOOPBACK_SHIFT 0 /* AIF2_LOOPBACK */
+#define WM8995_AIF2_LOOPBACK_WIDTH 1 /* AIF2_LOOPBACK */
+
+/*
+ * R786 (0x312) - AIF2 Master/Slave
+ */
+#define WM8995_AIF2_TRI 0x8000 /* AIF2_TRI */
+#define WM8995_AIF2_TRI_MASK 0x8000 /* AIF2_TRI */
+#define WM8995_AIF2_TRI_SHIFT 15 /* AIF2_TRI */
+#define WM8995_AIF2_TRI_WIDTH 1 /* AIF2_TRI */
+#define WM8995_AIF2_MSTR 0x4000 /* AIF2_MSTR */
+#define WM8995_AIF2_MSTR_MASK 0x4000 /* AIF2_MSTR */
+#define WM8995_AIF2_MSTR_SHIFT 14 /* AIF2_MSTR */
+#define WM8995_AIF2_MSTR_WIDTH 1 /* AIF2_MSTR */
+#define WM8995_AIF2_CLK_FRC 0x2000 /* AIF2_CLK_FRC */
+#define WM8995_AIF2_CLK_FRC_MASK 0x2000 /* AIF2_CLK_FRC */
+#define WM8995_AIF2_CLK_FRC_SHIFT 13 /* AIF2_CLK_FRC */
+#define WM8995_AIF2_CLK_FRC_WIDTH 1 /* AIF2_CLK_FRC */
+#define WM8995_AIF2_LRCLK_FRC 0x1000 /* AIF2_LRCLK_FRC */
+#define WM8995_AIF2_LRCLK_FRC_MASK 0x1000 /* AIF2_LRCLK_FRC */
+#define WM8995_AIF2_LRCLK_FRC_SHIFT 12 /* AIF2_LRCLK_FRC */
+#define WM8995_AIF2_LRCLK_FRC_WIDTH 1 /* AIF2_LRCLK_FRC */
+
+/*
+ * R787 (0x313) - AIF2 BCLK
+ */
+#define WM8995_AIF2_BCLK_DIV_MASK 0x00F0 /* AIF2_BCLK_DIV - [7:4] */
+#define WM8995_AIF2_BCLK_DIV_SHIFT 4 /* AIF2_BCLK_DIV - [7:4] */
+#define WM8995_AIF2_BCLK_DIV_WIDTH 4 /* AIF2_BCLK_DIV - [7:4] */
+
+/*
+ * R788 (0x314) - AIF2ADC LRCLK
+ */
+#define WM8995_AIF2ADC_LRCLK_DIR 0x0800 /* AIF2ADC_LRCLK_DIR */
+#define WM8995_AIF2ADC_LRCLK_DIR_MASK 0x0800 /* AIF2ADC_LRCLK_DIR */
+#define WM8995_AIF2ADC_LRCLK_DIR_SHIFT 11 /* AIF2ADC_LRCLK_DIR */
+#define WM8995_AIF2ADC_LRCLK_DIR_WIDTH 1 /* AIF2ADC_LRCLK_DIR */
+#define WM8995_AIF2ADC_RATE_MASK 0x07FF /* AIF2ADC_RATE - [10:0] */
+#define WM8995_AIF2ADC_RATE_SHIFT 0 /* AIF2ADC_RATE - [10:0] */
+#define WM8995_AIF2ADC_RATE_WIDTH 11 /* AIF2ADC_RATE - [10:0] */
+
+/*
+ * R789 (0x315) - AIF2DAC LRCLK
+ */
+#define WM8995_AIF2DAC_LRCLK_DIR 0x0800 /* AIF2DAC_LRCLK_DIR */
+#define WM8995_AIF2DAC_LRCLK_DIR_MASK 0x0800 /* AIF2DAC_LRCLK_DIR */
+#define WM8995_AIF2DAC_LRCLK_DIR_SHIFT 11 /* AIF2DAC_LRCLK_DIR */
+#define WM8995_AIF2DAC_LRCLK_DIR_WIDTH 1 /* AIF2DAC_LRCLK_DIR */
+#define WM8995_AIF2DAC_RATE_MASK 0x07FF /* AIF2DAC_RATE - [10:0] */
+#define WM8995_AIF2DAC_RATE_SHIFT 0 /* AIF2DAC_RATE - [10:0] */
+#define WM8995_AIF2DAC_RATE_WIDTH 11 /* AIF2DAC_RATE - [10:0] */
+
+/*
+ * R790 (0x316) - AIF2DAC Data
+ */
+#define WM8995_AIF2DACL_DAT_INV 0x0002 /* AIF2DACL_DAT_INV */
+#define WM8995_AIF2DACL_DAT_INV_MASK 0x0002 /* AIF2DACL_DAT_INV */
+#define WM8995_AIF2DACL_DAT_INV_SHIFT 1 /* AIF2DACL_DAT_INV */
+#define WM8995_AIF2DACL_DAT_INV_WIDTH 1 /* AIF2DACL_DAT_INV */
+#define WM8995_AIF2DACR_DAT_INV 0x0001 /* AIF2DACR_DAT_INV */
+#define WM8995_AIF2DACR_DAT_INV_MASK 0x0001 /* AIF2DACR_DAT_INV */
+#define WM8995_AIF2DACR_DAT_INV_SHIFT 0 /* AIF2DACR_DAT_INV */
+#define WM8995_AIF2DACR_DAT_INV_WIDTH 1 /* AIF2DACR_DAT_INV */
+
+/*
+ * R791 (0x317) - AIF2ADC Data
+ */
+#define WM8995_AIF2ADCL_DAT_INV 0x0002 /* AIF2ADCL_DAT_INV */
+#define WM8995_AIF2ADCL_DAT_INV_MASK 0x0002 /* AIF2ADCL_DAT_INV */
+#define WM8995_AIF2ADCL_DAT_INV_SHIFT 1 /* AIF2ADCL_DAT_INV */
+#define WM8995_AIF2ADCL_DAT_INV_WIDTH 1 /* AIF2ADCL_DAT_INV */
+#define WM8995_AIF2ADCR_DAT_INV 0x0001 /* AIF2ADCR_DAT_INV */
+#define WM8995_AIF2ADCR_DAT_INV_MASK 0x0001 /* AIF2ADCR_DAT_INV */
+#define WM8995_AIF2ADCR_DAT_INV_SHIFT 0 /* AIF2ADCR_DAT_INV */
+#define WM8995_AIF2ADCR_DAT_INV_WIDTH 1 /* AIF2ADCR_DAT_INV */
+
+/*
+ * R1024 (0x400) - AIF1 ADC1 Left Volume
+ */
+#define WM8995_AIF1ADC1_VU 0x0100 /* AIF1ADC1_VU */
+#define WM8995_AIF1ADC1_VU_MASK 0x0100 /* AIF1ADC1_VU */
+#define WM8995_AIF1ADC1_VU_SHIFT 8 /* AIF1ADC1_VU */
+#define WM8995_AIF1ADC1_VU_WIDTH 1 /* AIF1ADC1_VU */
+#define WM8995_AIF1ADC1L_VOL_MASK 0x00FF /* AIF1ADC1L_VOL - [7:0] */
+#define WM8995_AIF1ADC1L_VOL_SHIFT 0 /* AIF1ADC1L_VOL - [7:0] */
+#define WM8995_AIF1ADC1L_VOL_WIDTH 8 /* AIF1ADC1L_VOL - [7:0] */
+
+/*
+ * R1025 (0x401) - AIF1 ADC1 Right Volume
+ */
+#define WM8995_AIF1ADC1_VU 0x0100 /* AIF1ADC1_VU */
+#define WM8995_AIF1ADC1_VU_MASK 0x0100 /* AIF1ADC1_VU */
+#define WM8995_AIF1ADC1_VU_SHIFT 8 /* AIF1ADC1_VU */
+#define WM8995_AIF1ADC1_VU_WIDTH 1 /* AIF1ADC1_VU */
+#define WM8995_AIF1ADC1R_VOL_MASK 0x00FF /* AIF1ADC1R_VOL - [7:0] */
+#define WM8995_AIF1ADC1R_VOL_SHIFT 0 /* AIF1ADC1R_VOL - [7:0] */
+#define WM8995_AIF1ADC1R_VOL_WIDTH 8 /* AIF1ADC1R_VOL - [7:0] */
+
+/*
+ * R1026 (0x402) - AIF1 DAC1 Left Volume
+ */
+#define WM8995_AIF1DAC1_VU 0x0100 /* AIF1DAC1_VU */
+#define WM8995_AIF1DAC1_VU_MASK 0x0100 /* AIF1DAC1_VU */
+#define WM8995_AIF1DAC1_VU_SHIFT 8 /* AIF1DAC1_VU */
+#define WM8995_AIF1DAC1_VU_WIDTH 1 /* AIF1DAC1_VU */
+#define WM8995_AIF1DAC1L_VOL_MASK 0x00FF /* AIF1DAC1L_VOL - [7:0] */
+#define WM8995_AIF1DAC1L_VOL_SHIFT 0 /* AIF1DAC1L_VOL - [7:0] */
+#define WM8995_AIF1DAC1L_VOL_WIDTH 8 /* AIF1DAC1L_VOL - [7:0] */
+
+/*
+ * R1027 (0x403) - AIF1 DAC1 Right Volume
+ */
+#define WM8995_AIF1DAC1_VU 0x0100 /* AIF1DAC1_VU */
+#define WM8995_AIF1DAC1_VU_MASK 0x0100 /* AIF1DAC1_VU */
+#define WM8995_AIF1DAC1_VU_SHIFT 8 /* AIF1DAC1_VU */
+#define WM8995_AIF1DAC1_VU_WIDTH 1 /* AIF1DAC1_VU */
+#define WM8995_AIF1DAC1R_VOL_MASK 0x00FF /* AIF1DAC1R_VOL - [7:0] */
+#define WM8995_AIF1DAC1R_VOL_SHIFT 0 /* AIF1DAC1R_VOL - [7:0] */
+#define WM8995_AIF1DAC1R_VOL_WIDTH 8 /* AIF1DAC1R_VOL - [7:0] */
+
+/*
+ * R1028 (0x404) - AIF1 ADC2 Left Volume
+ */
+#define WM8995_AIF1ADC2_VU 0x0100 /* AIF1ADC2_VU */
+#define WM8995_AIF1ADC2_VU_MASK 0x0100 /* AIF1ADC2_VU */
+#define WM8995_AIF1ADC2_VU_SHIFT 8 /* AIF1ADC2_VU */
+#define WM8995_AIF1ADC2_VU_WIDTH 1 /* AIF1ADC2_VU */
+#define WM8995_AIF1ADC2L_VOL_MASK 0x00FF /* AIF1ADC2L_VOL - [7:0] */
+#define WM8995_AIF1ADC2L_VOL_SHIFT 0 /* AIF1ADC2L_VOL - [7:0] */
+#define WM8995_AIF1ADC2L_VOL_WIDTH 8 /* AIF1ADC2L_VOL - [7:0] */
+
+/*
+ * R1029 (0x405) - AIF1 ADC2 Right Volume
+ */
+#define WM8995_AIF1ADC2_VU 0x0100 /* AIF1ADC2_VU */
+#define WM8995_AIF1ADC2_VU_MASK 0x0100 /* AIF1ADC2_VU */
+#define WM8995_AIF1ADC2_VU_SHIFT 8 /* AIF1ADC2_VU */
+#define WM8995_AIF1ADC2_VU_WIDTH 1 /* AIF1ADC2_VU */
+#define WM8995_AIF1ADC2R_VOL_MASK 0x00FF /* AIF1ADC2R_VOL - [7:0] */
+#define WM8995_AIF1ADC2R_VOL_SHIFT 0 /* AIF1ADC2R_VOL - [7:0] */
+#define WM8995_AIF1ADC2R_VOL_WIDTH 8 /* AIF1ADC2R_VOL - [7:0] */
+
+/*
+ * R1030 (0x406) - AIF1 DAC2 Left Volume
+ */
+#define WM8995_AIF1DAC2_VU 0x0100 /* AIF1DAC2_VU */
+#define WM8995_AIF1DAC2_VU_MASK 0x0100 /* AIF1DAC2_VU */
+#define WM8995_AIF1DAC2_VU_SHIFT 8 /* AIF1DAC2_VU */
+#define WM8995_AIF1DAC2_VU_WIDTH 1 /* AIF1DAC2_VU */
+#define WM8995_AIF1DAC2L_VOL_MASK 0x00FF /* AIF1DAC2L_VOL - [7:0] */
+#define WM8995_AIF1DAC2L_VOL_SHIFT 0 /* AIF1DAC2L_VOL - [7:0] */
+#define WM8995_AIF1DAC2L_VOL_WIDTH 8 /* AIF1DAC2L_VOL - [7:0] */
+
+/*
+ * R1031 (0x407) - AIF1 DAC2 Right Volume
+ */
+#define WM8995_AIF1DAC2_VU 0x0100 /* AIF1DAC2_VU */
+#define WM8995_AIF1DAC2_VU_MASK 0x0100 /* AIF1DAC2_VU */
+#define WM8995_AIF1DAC2_VU_SHIFT 8 /* AIF1DAC2_VU */
+#define WM8995_AIF1DAC2_VU_WIDTH 1 /* AIF1DAC2_VU */
+#define WM8995_AIF1DAC2R_VOL_MASK 0x00FF /* AIF1DAC2R_VOL - [7:0] */
+#define WM8995_AIF1DAC2R_VOL_SHIFT 0 /* AIF1DAC2R_VOL - [7:0] */
+#define WM8995_AIF1DAC2R_VOL_WIDTH 8 /* AIF1DAC2R_VOL - [7:0] */
+
+/*
+ * R1040 (0x410) - AIF1 ADC1 Filters
+ */
+#define WM8995_AIF1ADC_4FS 0x8000 /* AIF1ADC_4FS */
+#define WM8995_AIF1ADC_4FS_MASK 0x8000 /* AIF1ADC_4FS */
+#define WM8995_AIF1ADC_4FS_SHIFT 15 /* AIF1ADC_4FS */
+#define WM8995_AIF1ADC_4FS_WIDTH 1 /* AIF1ADC_4FS */
+#define WM8995_AIF1ADC1L_HPF 0x1000 /* AIF1ADC1L_HPF */
+#define WM8995_AIF1ADC1L_HPF_MASK 0x1000 /* AIF1ADC1L_HPF */
+#define WM8995_AIF1ADC1L_HPF_SHIFT 12 /* AIF1ADC1L_HPF */
+#define WM8995_AIF1ADC1L_HPF_WIDTH 1 /* AIF1ADC1L_HPF */
+#define WM8995_AIF1ADC1R_HPF 0x0800 /* AIF1ADC1R_HPF */
+#define WM8995_AIF1ADC1R_HPF_MASK 0x0800 /* AIF1ADC1R_HPF */
+#define WM8995_AIF1ADC1R_HPF_SHIFT 11 /* AIF1ADC1R_HPF */
+#define WM8995_AIF1ADC1R_HPF_WIDTH 1 /* AIF1ADC1R_HPF */
+#define WM8995_AIF1ADC1_HPF_MODE 0x0008 /* AIF1ADC1_HPF_MODE */
+#define WM8995_AIF1ADC1_HPF_MODE_MASK 0x0008 /* AIF1ADC1_HPF_MODE */
+#define WM8995_AIF1ADC1_HPF_MODE_SHIFT 3 /* AIF1ADC1_HPF_MODE */
+#define WM8995_AIF1ADC1_HPF_MODE_WIDTH 1 /* AIF1ADC1_HPF_MODE */
+#define WM8995_AIF1ADC1_HPF_CUT_MASK 0x0007 /* AIF1ADC1_HPF_CUT - [2:0] */
+#define WM8995_AIF1ADC1_HPF_CUT_SHIFT 0 /* AIF1ADC1_HPF_CUT - [2:0] */
+#define WM8995_AIF1ADC1_HPF_CUT_WIDTH 3 /* AIF1ADC1_HPF_CUT - [2:0] */
+
+/*
+ * R1041 (0x411) - AIF1 ADC2 Filters
+ */
+#define WM8995_AIF1ADC2L_HPF 0x1000 /* AIF1ADC2L_HPF */
+#define WM8995_AIF1ADC2L_HPF_MASK 0x1000 /* AIF1ADC2L_HPF */
+#define WM8995_AIF1ADC2L_HPF_SHIFT 12 /* AIF1ADC2L_HPF */
+#define WM8995_AIF1ADC2L_HPF_WIDTH 1 /* AIF1ADC2L_HPF */
+#define WM8995_AIF1ADC2R_HPF 0x0800 /* AIF1ADC2R_HPF */
+#define WM8995_AIF1ADC2R_HPF_MASK 0x0800 /* AIF1ADC2R_HPF */
+#define WM8995_AIF1ADC2R_HPF_SHIFT 11 /* AIF1ADC2R_HPF */
+#define WM8995_AIF1ADC2R_HPF_WIDTH 1 /* AIF1ADC2R_HPF */
+#define WM8995_AIF1ADC2_HPF_MODE 0x0008 /* AIF1ADC2_HPF_MODE */
+#define WM8995_AIF1ADC2_HPF_MODE_MASK 0x0008 /* AIF1ADC2_HPF_MODE */
+#define WM8995_AIF1ADC2_HPF_MODE_SHIFT 3 /* AIF1ADC2_HPF_MODE */
+#define WM8995_AIF1ADC2_HPF_MODE_WIDTH 1 /* AIF1ADC2_HPF_MODE */
+#define WM8995_AIF1ADC2_HPF_CUT_MASK 0x0007 /* AIF1ADC2_HPF_CUT - [2:0] */
+#define WM8995_AIF1ADC2_HPF_CUT_SHIFT 0 /* AIF1ADC2_HPF_CUT - [2:0] */
+#define WM8995_AIF1ADC2_HPF_CUT_WIDTH 3 /* AIF1ADC2_HPF_CUT - [2:0] */
+
+/*
+ * R1056 (0x420) - AIF1 DAC1 Filters (1)
+ */
+#define WM8995_AIF1DAC1_MUTE 0x0200 /* AIF1DAC1_MUTE */
+#define WM8995_AIF1DAC1_MUTE_MASK 0x0200 /* AIF1DAC1_MUTE */
+#define WM8995_AIF1DAC1_MUTE_SHIFT 9 /* AIF1DAC1_MUTE */
+#define WM8995_AIF1DAC1_MUTE_WIDTH 1 /* AIF1DAC1_MUTE */
+#define WM8995_AIF1DAC1_MONO 0x0080 /* AIF1DAC1_MONO */
+#define WM8995_AIF1DAC1_MONO_MASK 0x0080 /* AIF1DAC1_MONO */
+#define WM8995_AIF1DAC1_MONO_SHIFT 7 /* AIF1DAC1_MONO */
+#define WM8995_AIF1DAC1_MONO_WIDTH 1 /* AIF1DAC1_MONO */
+#define WM8995_AIF1DAC1_MUTERATE 0x0020 /* AIF1DAC1_MUTERATE */
+#define WM8995_AIF1DAC1_MUTERATE_MASK 0x0020 /* AIF1DAC1_MUTERATE */
+#define WM8995_AIF1DAC1_MUTERATE_SHIFT 5 /* AIF1DAC1_MUTERATE */
+#define WM8995_AIF1DAC1_MUTERATE_WIDTH 1 /* AIF1DAC1_MUTERATE */
+#define WM8995_AIF1DAC1_UNMUTE_RAMP 0x0010 /* AIF1DAC1_UNMUTE_RAMP */
+#define WM8995_AIF1DAC1_UNMUTE_RAMP_MASK 0x0010 /* AIF1DAC1_UNMUTE_RAMP */
+#define WM8995_AIF1DAC1_UNMUTE_RAMP_SHIFT 4 /* AIF1DAC1_UNMUTE_RAMP */
+#define WM8995_AIF1DAC1_UNMUTE_RAMP_WIDTH 1 /* AIF1DAC1_UNMUTE_RAMP */
+#define WM8995_AIF1DAC1_DEEMP_MASK 0x0006 /* AIF1DAC1_DEEMP - [2:1] */
+#define WM8995_AIF1DAC1_DEEMP_SHIFT 1 /* AIF1DAC1_DEEMP - [2:1] */
+#define WM8995_AIF1DAC1_DEEMP_WIDTH 2 /* AIF1DAC1_DEEMP - [2:1] */
+
+/*
+ * R1057 (0x421) - AIF1 DAC1 Filters (2)
+ */
+#define WM8995_AIF1DAC1_3D_GAIN_MASK 0x3E00 /* AIF1DAC1_3D_GAIN - [13:9] */
+#define WM8995_AIF1DAC1_3D_GAIN_SHIFT 9 /* AIF1DAC1_3D_GAIN - [13:9] */
+#define WM8995_AIF1DAC1_3D_GAIN_WIDTH 5 /* AIF1DAC1_3D_GAIN - [13:9] */
+#define WM8995_AIF1DAC1_3D_ENA 0x0100 /* AIF1DAC1_3D_ENA */
+#define WM8995_AIF1DAC1_3D_ENA_MASK 0x0100 /* AIF1DAC1_3D_ENA */
+#define WM8995_AIF1DAC1_3D_ENA_SHIFT 8 /* AIF1DAC1_3D_ENA */
+#define WM8995_AIF1DAC1_3D_ENA_WIDTH 1 /* AIF1DAC1_3D_ENA */
+
+/*
+ * R1058 (0x422) - AIF1 DAC2 Filters (1)
+ */
+#define WM8995_AIF1DAC2_MUTE 0x0200 /* AIF1DAC2_MUTE */
+#define WM8995_AIF1DAC2_MUTE_MASK 0x0200 /* AIF1DAC2_MUTE */
+#define WM8995_AIF1DAC2_MUTE_SHIFT 9 /* AIF1DAC2_MUTE */
+#define WM8995_AIF1DAC2_MUTE_WIDTH 1 /* AIF1DAC2_MUTE */
+#define WM8995_AIF1DAC2_MONO 0x0080 /* AIF1DAC2_MONO */
+#define WM8995_AIF1DAC2_MONO_MASK 0x0080 /* AIF1DAC2_MONO */
+#define WM8995_AIF1DAC2_MONO_SHIFT 7 /* AIF1DAC2_MONO */
+#define WM8995_AIF1DAC2_MONO_WIDTH 1 /* AIF1DAC2_MONO */
+#define WM8995_AIF1DAC2_MUTERATE 0x0020 /* AIF1DAC2_MUTERATE */
+#define WM8995_AIF1DAC2_MUTERATE_MASK 0x0020 /* AIF1DAC2_MUTERATE */
+#define WM8995_AIF1DAC2_MUTERATE_SHIFT 5 /* AIF1DAC2_MUTERATE */
+#define WM8995_AIF1DAC2_MUTERATE_WIDTH 1 /* AIF1DAC2_MUTERATE */
+#define WM8995_AIF1DAC2_UNMUTE_RAMP 0x0010 /* AIF1DAC2_UNMUTE_RAMP */
+#define WM8995_AIF1DAC2_UNMUTE_RAMP_MASK 0x0010 /* AIF1DAC2_UNMUTE_RAMP */
+#define WM8995_AIF1DAC2_UNMUTE_RAMP_SHIFT 4 /* AIF1DAC2_UNMUTE_RAMP */
+#define WM8995_AIF1DAC2_UNMUTE_RAMP_WIDTH 1 /* AIF1DAC2_UNMUTE_RAMP */
+#define WM8995_AIF1DAC2_DEEMP_MASK 0x0006 /* AIF1DAC2_DEEMP - [2:1] */
+#define WM8995_AIF1DAC2_DEEMP_SHIFT 1 /* AIF1DAC2_DEEMP - [2:1] */
+#define WM8995_AIF1DAC2_DEEMP_WIDTH 2 /* AIF1DAC2_DEEMP - [2:1] */
+
+/*
+ * R1059 (0x423) - AIF1 DAC2 Filters (2)
+ */
+#define WM8995_AIF1DAC2_3D_GAIN_MASK 0x3E00 /* AIF1DAC2_3D_GAIN - [13:9] */
+#define WM8995_AIF1DAC2_3D_GAIN_SHIFT 9 /* AIF1DAC2_3D_GAIN - [13:9] */
+#define WM8995_AIF1DAC2_3D_GAIN_WIDTH 5 /* AIF1DAC2_3D_GAIN - [13:9] */
+#define WM8995_AIF1DAC2_3D_ENA 0x0100 /* AIF1DAC2_3D_ENA */
+#define WM8995_AIF1DAC2_3D_ENA_MASK 0x0100 /* AIF1DAC2_3D_ENA */
+#define WM8995_AIF1DAC2_3D_ENA_SHIFT 8 /* AIF1DAC2_3D_ENA */
+#define WM8995_AIF1DAC2_3D_ENA_WIDTH 1 /* AIF1DAC2_3D_ENA */
+
+/*
+ * R1088 (0x440) - AIF1 DRC1 (1)
+ */
+#define WM8995_AIF1DRC1_SIG_DET_RMS_MASK 0xF800 /* AIF1DRC1_SIG_DET_RMS - [15:11] */
+#define WM8995_AIF1DRC1_SIG_DET_RMS_SHIFT 11 /* AIF1DRC1_SIG_DET_RMS - [15:11] */
+#define WM8995_AIF1DRC1_SIG_DET_RMS_WIDTH 5 /* AIF1DRC1_SIG_DET_RMS - [15:11] */
+#define WM8995_AIF1DRC1_SIG_DET_PK_MASK 0x0600 /* AIF1DRC1_SIG_DET_PK - [10:9] */
+#define WM8995_AIF1DRC1_SIG_DET_PK_SHIFT 9 /* AIF1DRC1_SIG_DET_PK - [10:9] */
+#define WM8995_AIF1DRC1_SIG_DET_PK_WIDTH 2 /* AIF1DRC1_SIG_DET_PK - [10:9] */
+#define WM8995_AIF1DRC1_NG_ENA 0x0100 /* AIF1DRC1_NG_ENA */
+#define WM8995_AIF1DRC1_NG_ENA_MASK 0x0100 /* AIF1DRC1_NG_ENA */
+#define WM8995_AIF1DRC1_NG_ENA_SHIFT 8 /* AIF1DRC1_NG_ENA */
+#define WM8995_AIF1DRC1_NG_ENA_WIDTH 1 /* AIF1DRC1_NG_ENA */
+#define WM8995_AIF1DRC1_SIG_DET_MODE 0x0080 /* AIF1DRC1_SIG_DET_MODE */
+#define WM8995_AIF1DRC1_SIG_DET_MODE_MASK 0x0080 /* AIF1DRC1_SIG_DET_MODE */
+#define WM8995_AIF1DRC1_SIG_DET_MODE_SHIFT 7 /* AIF1DRC1_SIG_DET_MODE */
+#define WM8995_AIF1DRC1_SIG_DET_MODE_WIDTH 1 /* AIF1DRC1_SIG_DET_MODE */
+#define WM8995_AIF1DRC1_SIG_DET 0x0040 /* AIF1DRC1_SIG_DET */
+#define WM8995_AIF1DRC1_SIG_DET_MASK 0x0040 /* AIF1DRC1_SIG_DET */
+#define WM8995_AIF1DRC1_SIG_DET_SHIFT 6 /* AIF1DRC1_SIG_DET */
+#define WM8995_AIF1DRC1_SIG_DET_WIDTH 1 /* AIF1DRC1_SIG_DET */
+#define WM8995_AIF1DRC1_KNEE2_OP_ENA 0x0020 /* AIF1DRC1_KNEE2_OP_ENA */
+#define WM8995_AIF1DRC1_KNEE2_OP_ENA_MASK 0x0020 /* AIF1DRC1_KNEE2_OP_ENA */
+#define WM8995_AIF1DRC1_KNEE2_OP_ENA_SHIFT 5 /* AIF1DRC1_KNEE2_OP_ENA */
+#define WM8995_AIF1DRC1_KNEE2_OP_ENA_WIDTH 1 /* AIF1DRC1_KNEE2_OP_ENA */
+#define WM8995_AIF1DRC1_QR 0x0010 /* AIF1DRC1_QR */
+#define WM8995_AIF1DRC1_QR_MASK 0x0010 /* AIF1DRC1_QR */
+#define WM8995_AIF1DRC1_QR_SHIFT 4 /* AIF1DRC1_QR */
+#define WM8995_AIF1DRC1_QR_WIDTH 1 /* AIF1DRC1_QR */
+#define WM8995_AIF1DRC1_ANTICLIP 0x0008 /* AIF1DRC1_ANTICLIP */
+#define WM8995_AIF1DRC1_ANTICLIP_MASK 0x0008 /* AIF1DRC1_ANTICLIP */
+#define WM8995_AIF1DRC1_ANTICLIP_SHIFT 3 /* AIF1DRC1_ANTICLIP */
+#define WM8995_AIF1DRC1_ANTICLIP_WIDTH 1 /* AIF1DRC1_ANTICLIP */
+#define WM8995_AIF1DAC1_DRC_ENA 0x0004 /* AIF1DAC1_DRC_ENA */
+#define WM8995_AIF1DAC1_DRC_ENA_MASK 0x0004 /* AIF1DAC1_DRC_ENA */
+#define WM8995_AIF1DAC1_DRC_ENA_SHIFT 2 /* AIF1DAC1_DRC_ENA */
+#define WM8995_AIF1DAC1_DRC_ENA_WIDTH 1 /* AIF1DAC1_DRC_ENA */
+#define WM8995_AIF1ADC1L_DRC_ENA 0x0002 /* AIF1ADC1L_DRC_ENA */
+#define WM8995_AIF1ADC1L_DRC_ENA_MASK 0x0002 /* AIF1ADC1L_DRC_ENA */
+#define WM8995_AIF1ADC1L_DRC_ENA_SHIFT 1 /* AIF1ADC1L_DRC_ENA */
+#define WM8995_AIF1ADC1L_DRC_ENA_WIDTH 1 /* AIF1ADC1L_DRC_ENA */
+#define WM8995_AIF1ADC1R_DRC_ENA 0x0001 /* AIF1ADC1R_DRC_ENA */
+#define WM8995_AIF1ADC1R_DRC_ENA_MASK 0x0001 /* AIF1ADC1R_DRC_ENA */
+#define WM8995_AIF1ADC1R_DRC_ENA_SHIFT 0 /* AIF1ADC1R_DRC_ENA */
+#define WM8995_AIF1ADC1R_DRC_ENA_WIDTH 1 /* AIF1ADC1R_DRC_ENA */
+
+/*
+ * R1089 (0x441) - AIF1 DRC1 (2)
+ */
+#define WM8995_AIF1DRC1_ATK_MASK 0x1E00 /* AIF1DRC1_ATK - [12:9] */
+#define WM8995_AIF1DRC1_ATK_SHIFT 9 /* AIF1DRC1_ATK - [12:9] */
+#define WM8995_AIF1DRC1_ATK_WIDTH 4 /* AIF1DRC1_ATK - [12:9] */
+#define WM8995_AIF1DRC1_DCY_MASK 0x01E0 /* AIF1DRC1_DCY - [8:5] */
+#define WM8995_AIF1DRC1_DCY_SHIFT 5 /* AIF1DRC1_DCY - [8:5] */
+#define WM8995_AIF1DRC1_DCY_WIDTH 4 /* AIF1DRC1_DCY - [8:5] */
+#define WM8995_AIF1DRC1_MINGAIN_MASK 0x001C /* AIF1DRC1_MINGAIN - [4:2] */
+#define WM8995_AIF1DRC1_MINGAIN_SHIFT 2 /* AIF1DRC1_MINGAIN - [4:2] */
+#define WM8995_AIF1DRC1_MINGAIN_WIDTH 3 /* AIF1DRC1_MINGAIN - [4:2] */
+#define WM8995_AIF1DRC1_MAXGAIN_MASK 0x0003 /* AIF1DRC1_MAXGAIN - [1:0] */
+#define WM8995_AIF1DRC1_MAXGAIN_SHIFT 0 /* AIF1DRC1_MAXGAIN - [1:0] */
+#define WM8995_AIF1DRC1_MAXGAIN_WIDTH 2 /* AIF1DRC1_MAXGAIN - [1:0] */
+
+/*
+ * R1090 (0x442) - AIF1 DRC1 (3)
+ */
+#define WM8995_AIF1DRC1_NG_MINGAIN_MASK 0xF000 /* AIF1DRC1_NG_MINGAIN - [15:12] */
+#define WM8995_AIF1DRC1_NG_MINGAIN_SHIFT 12 /* AIF1DRC1_NG_MINGAIN - [15:12] */
+#define WM8995_AIF1DRC1_NG_MINGAIN_WIDTH 4 /* AIF1DRC1_NG_MINGAIN - [15:12] */
+#define WM8995_AIF1DRC1_NG_EXP_MASK 0x0C00 /* AIF1DRC1_NG_EXP - [11:10] */
+#define WM8995_AIF1DRC1_NG_EXP_SHIFT 10 /* AIF1DRC1_NG_EXP - [11:10] */
+#define WM8995_AIF1DRC1_NG_EXP_WIDTH 2 /* AIF1DRC1_NG_EXP - [11:10] */
+#define WM8995_AIF1DRC1_QR_THR_MASK 0x0300 /* AIF1DRC1_QR_THR - [9:8] */
+#define WM8995_AIF1DRC1_QR_THR_SHIFT 8 /* AIF1DRC1_QR_THR - [9:8] */
+#define WM8995_AIF1DRC1_QR_THR_WIDTH 2 /* AIF1DRC1_QR_THR - [9:8] */
+#define WM8995_AIF1DRC1_QR_DCY_MASK 0x00C0 /* AIF1DRC1_QR_DCY - [7:6] */
+#define WM8995_AIF1DRC1_QR_DCY_SHIFT 6 /* AIF1DRC1_QR_DCY - [7:6] */
+#define WM8995_AIF1DRC1_QR_DCY_WIDTH 2 /* AIF1DRC1_QR_DCY - [7:6] */
+#define WM8995_AIF1DRC1_HI_COMP_MASK 0x0038 /* AIF1DRC1_HI_COMP - [5:3] */
+#define WM8995_AIF1DRC1_HI_COMP_SHIFT 3 /* AIF1DRC1_HI_COMP - [5:3] */
+#define WM8995_AIF1DRC1_HI_COMP_WIDTH 3 /* AIF1DRC1_HI_COMP - [5:3] */
+#define WM8995_AIF1DRC1_LO_COMP_MASK 0x0007 /* AIF1DRC1_LO_COMP - [2:0] */
+#define WM8995_AIF1DRC1_LO_COMP_SHIFT 0 /* AIF1DRC1_LO_COMP - [2:0] */
+#define WM8995_AIF1DRC1_LO_COMP_WIDTH 3 /* AIF1DRC1_LO_COMP - [2:0] */
+
+/*
+ * R1091 (0x443) - AIF1 DRC1 (4)
+ */
+#define WM8995_AIF1DRC1_KNEE_IP_MASK 0x07E0 /* AIF1DRC1_KNEE_IP - [10:5] */
+#define WM8995_AIF1DRC1_KNEE_IP_SHIFT 5 /* AIF1DRC1_KNEE_IP - [10:5] */
+#define WM8995_AIF1DRC1_KNEE_IP_WIDTH 6 /* AIF1DRC1_KNEE_IP - [10:5] */
+#define WM8995_AIF1DRC1_KNEE_OP_MASK 0x001F /* AIF1DRC1_KNEE_OP - [4:0] */
+#define WM8995_AIF1DRC1_KNEE_OP_SHIFT 0 /* AIF1DRC1_KNEE_OP - [4:0] */
+#define WM8995_AIF1DRC1_KNEE_OP_WIDTH 5 /* AIF1DRC1_KNEE_OP - [4:0] */
+
+/*
+ * R1092 (0x444) - AIF1 DRC1 (5)
+ */
+#define WM8995_AIF1DRC1_KNEE2_IP_MASK 0x03E0 /* AIF1DRC1_KNEE2_IP - [9:5] */
+#define WM8995_AIF1DRC1_KNEE2_IP_SHIFT 5 /* AIF1DRC1_KNEE2_IP - [9:5] */
+#define WM8995_AIF1DRC1_KNEE2_IP_WIDTH 5 /* AIF1DRC1_KNEE2_IP - [9:5] */
+#define WM8995_AIF1DRC1_KNEE2_OP_MASK 0x001F /* AIF1DRC1_KNEE2_OP - [4:0] */
+#define WM8995_AIF1DRC1_KNEE2_OP_SHIFT 0 /* AIF1DRC1_KNEE2_OP - [4:0] */
+#define WM8995_AIF1DRC1_KNEE2_OP_WIDTH 5 /* AIF1DRC1_KNEE2_OP - [4:0] */
+
+/*
+ * R1104 (0x450) - AIF1 DRC2 (1)
+ */
+#define WM8995_AIF1DRC2_SIG_DET_RMS_MASK 0xF800 /* AIF1DRC2_SIG_DET_RMS - [15:11] */
+#define WM8995_AIF1DRC2_SIG_DET_RMS_SHIFT 11 /* AIF1DRC2_SIG_DET_RMS - [15:11] */
+#define WM8995_AIF1DRC2_SIG_DET_RMS_WIDTH 5 /* AIF1DRC2_SIG_DET_RMS - [15:11] */
+#define WM8995_AIF1DRC2_SIG_DET_PK_MASK 0x0600 /* AIF1DRC2_SIG_DET_PK - [10:9] */
+#define WM8995_AIF1DRC2_SIG_DET_PK_SHIFT 9 /* AIF1DRC2_SIG_DET_PK - [10:9] */
+#define WM8995_AIF1DRC2_SIG_DET_PK_WIDTH 2 /* AIF1DRC2_SIG_DET_PK - [10:9] */
+#define WM8995_AIF1DRC2_NG_ENA 0x0100 /* AIF1DRC2_NG_ENA */
+#define WM8995_AIF1DRC2_NG_ENA_MASK 0x0100 /* AIF1DRC2_NG_ENA */
+#define WM8995_AIF1DRC2_NG_ENA_SHIFT 8 /* AIF1DRC2_NG_ENA */
+#define WM8995_AIF1DRC2_NG_ENA_WIDTH 1 /* AIF1DRC2_NG_ENA */
+#define WM8995_AIF1DRC2_SIG_DET_MODE 0x0080 /* AIF1DRC2_SIG_DET_MODE */
+#define WM8995_AIF1DRC2_SIG_DET_MODE_MASK 0x0080 /* AIF1DRC2_SIG_DET_MODE */
+#define WM8995_AIF1DRC2_SIG_DET_MODE_SHIFT 7 /* AIF1DRC2_SIG_DET_MODE */
+#define WM8995_AIF1DRC2_SIG_DET_MODE_WIDTH 1 /* AIF1DRC2_SIG_DET_MODE */
+#define WM8995_AIF1DRC2_SIG_DET 0x0040 /* AIF1DRC2_SIG_DET */
+#define WM8995_AIF1DRC2_SIG_DET_MASK 0x0040 /* AIF1DRC2_SIG_DET */
+#define WM8995_AIF1DRC2_SIG_DET_SHIFT 6 /* AIF1DRC2_SIG_DET */
+#define WM8995_AIF1DRC2_SIG_DET_WIDTH 1 /* AIF1DRC2_SIG_DET */
+#define WM8995_AIF1DRC2_KNEE2_OP_ENA 0x0020 /* AIF1DRC2_KNEE2_OP_ENA */
+#define WM8995_AIF1DRC2_KNEE2_OP_ENA_MASK 0x0020 /* AIF1DRC2_KNEE2_OP_ENA */
+#define WM8995_AIF1DRC2_KNEE2_OP_ENA_SHIFT 5 /* AIF1DRC2_KNEE2_OP_ENA */
+#define WM8995_AIF1DRC2_KNEE2_OP_ENA_WIDTH 1 /* AIF1DRC2_KNEE2_OP_ENA */
+#define WM8995_AIF1DRC2_QR 0x0010 /* AIF1DRC2_QR */
+#define WM8995_AIF1DRC2_QR_MASK 0x0010 /* AIF1DRC2_QR */
+#define WM8995_AIF1DRC2_QR_SHIFT 4 /* AIF1DRC2_QR */
+#define WM8995_AIF1DRC2_QR_WIDTH 1 /* AIF1DRC2_QR */
+#define WM8995_AIF1DRC2_ANTICLIP 0x0008 /* AIF1DRC2_ANTICLIP */
+#define WM8995_AIF1DRC2_ANTICLIP_MASK 0x0008 /* AIF1DRC2_ANTICLIP */
+#define WM8995_AIF1DRC2_ANTICLIP_SHIFT 3 /* AIF1DRC2_ANTICLIP */
+#define WM8995_AIF1DRC2_ANTICLIP_WIDTH 1 /* AIF1DRC2_ANTICLIP */
+#define WM8995_AIF1DAC2_DRC_ENA 0x0004 /* AIF1DAC2_DRC_ENA */
+#define WM8995_AIF1DAC2_DRC_ENA_MASK 0x0004 /* AIF1DAC2_DRC_ENA */
+#define WM8995_AIF1DAC2_DRC_ENA_SHIFT 2 /* AIF1DAC2_DRC_ENA */
+#define WM8995_AIF1DAC2_DRC_ENA_WIDTH 1 /* AIF1DAC2_DRC_ENA */
+#define WM8995_AIF1ADC2L_DRC_ENA 0x0002 /* AIF1ADC2L_DRC_ENA */
+#define WM8995_AIF1ADC2L_DRC_ENA_MASK 0x0002 /* AIF1ADC2L_DRC_ENA */
+#define WM8995_AIF1ADC2L_DRC_ENA_SHIFT 1 /* AIF1ADC2L_DRC_ENA */
+#define WM8995_AIF1ADC2L_DRC_ENA_WIDTH 1 /* AIF1ADC2L_DRC_ENA */
+#define WM8995_AIF1ADC2R_DRC_ENA 0x0001 /* AIF1ADC2R_DRC_ENA */
+#define WM8995_AIF1ADC2R_DRC_ENA_MASK 0x0001 /* AIF1ADC2R_DRC_ENA */
+#define WM8995_AIF1ADC2R_DRC_ENA_SHIFT 0 /* AIF1ADC2R_DRC_ENA */
+#define WM8995_AIF1ADC2R_DRC_ENA_WIDTH 1 /* AIF1ADC2R_DRC_ENA */
+
+/*
+ * R1105 (0x451) - AIF1 DRC2 (2)
+ */
+#define WM8995_AIF1DRC2_ATK_MASK 0x1E00 /* AIF1DRC2_ATK - [12:9] */
+#define WM8995_AIF1DRC2_ATK_SHIFT 9 /* AIF1DRC2_ATK - [12:9] */
+#define WM8995_AIF1DRC2_ATK_WIDTH 4 /* AIF1DRC2_ATK - [12:9] */
+#define WM8995_AIF1DRC2_DCY_MASK 0x01E0 /* AIF1DRC2_DCY - [8:5] */
+#define WM8995_AIF1DRC2_DCY_SHIFT 5 /* AIF1DRC2_DCY - [8:5] */
+#define WM8995_AIF1DRC2_DCY_WIDTH 4 /* AIF1DRC2_DCY - [8:5] */
+#define WM8995_AIF1DRC2_MINGAIN_MASK 0x001C /* AIF1DRC2_MINGAIN - [4:2] */
+#define WM8995_AIF1DRC2_MINGAIN_SHIFT 2 /* AIF1DRC2_MINGAIN - [4:2] */
+#define WM8995_AIF1DRC2_MINGAIN_WIDTH 3 /* AIF1DRC2_MINGAIN - [4:2] */
+#define WM8995_AIF1DRC2_MAXGAIN_MASK 0x0003 /* AIF1DRC2_MAXGAIN - [1:0] */
+#define WM8995_AIF1DRC2_MAXGAIN_SHIFT 0 /* AIF1DRC2_MAXGAIN - [1:0] */
+#define WM8995_AIF1DRC2_MAXGAIN_WIDTH 2 /* AIF1DRC2_MAXGAIN - [1:0] */
+
+/*
+ * R1106 (0x452) - AIF1 DRC2 (3)
+ */
+#define WM8995_AIF1DRC2_NG_MINGAIN_MASK 0xF000 /* AIF1DRC2_NG_MINGAIN - [15:12] */
+#define WM8995_AIF1DRC2_NG_MINGAIN_SHIFT 12 /* AIF1DRC2_NG_MINGAIN - [15:12] */
+#define WM8995_AIF1DRC2_NG_MINGAIN_WIDTH 4 /* AIF1DRC2_NG_MINGAIN - [15:12] */
+#define WM8995_AIF1DRC2_NG_EXP_MASK 0x0C00 /* AIF1DRC2_NG_EXP - [11:10] */
+#define WM8995_AIF1DRC2_NG_EXP_SHIFT 10 /* AIF1DRC2_NG_EXP - [11:10] */
+#define WM8995_AIF1DRC2_NG_EXP_WIDTH 2 /* AIF1DRC2_NG_EXP - [11:10] */
+#define WM8995_AIF1DRC2_QR_THR_MASK 0x0300 /* AIF1DRC2_QR_THR - [9:8] */
+#define WM8995_AIF1DRC2_QR_THR_SHIFT 8 /* AIF1DRC2_QR_THR - [9:8] */
+#define WM8995_AIF1DRC2_QR_THR_WIDTH 2 /* AIF1DRC2_QR_THR - [9:8] */
+#define WM8995_AIF1DRC2_QR_DCY_MASK 0x00C0 /* AIF1DRC2_QR_DCY - [7:6] */
+#define WM8995_AIF1DRC2_QR_DCY_SHIFT 6 /* AIF1DRC2_QR_DCY - [7:6] */
+#define WM8995_AIF1DRC2_QR_DCY_WIDTH 2 /* AIF1DRC2_QR_DCY - [7:6] */
+#define WM8995_AIF1DRC2_HI_COMP_MASK 0x0038 /* AIF1DRC2_HI_COMP - [5:3] */
+#define WM8995_AIF1DRC2_HI_COMP_SHIFT 3 /* AIF1DRC2_HI_COMP - [5:3] */
+#define WM8995_AIF1DRC2_HI_COMP_WIDTH 3 /* AIF1DRC2_HI_COMP - [5:3] */
+#define WM8995_AIF1DRC2_LO_COMP_MASK 0x0007 /* AIF1DRC2_LO_COMP - [2:0] */
+#define WM8995_AIF1DRC2_LO_COMP_SHIFT 0 /* AIF1DRC2_LO_COMP - [2:0] */
+#define WM8995_AIF1DRC2_LO_COMP_WIDTH 3 /* AIF1DRC2_LO_COMP - [2:0] */
+
+/*
+ * R1107 (0x453) - AIF1 DRC2 (4)
+ */
+#define WM8995_AIF1DRC2_KNEE_IP_MASK 0x07E0 /* AIF1DRC2_KNEE_IP - [10:5] */
+#define WM8995_AIF1DRC2_KNEE_IP_SHIFT 5 /* AIF1DRC2_KNEE_IP - [10:5] */
+#define WM8995_AIF1DRC2_KNEE_IP_WIDTH 6 /* AIF1DRC2_KNEE_IP - [10:5] */
+#define WM8995_AIF1DRC2_KNEE_OP_MASK 0x001F /* AIF1DRC2_KNEE_OP - [4:0] */
+#define WM8995_AIF1DRC2_KNEE_OP_SHIFT 0 /* AIF1DRC2_KNEE_OP - [4:0] */
+#define WM8995_AIF1DRC2_KNEE_OP_WIDTH 5 /* AIF1DRC2_KNEE_OP - [4:0] */
+
+/*
+ * R1108 (0x454) - AIF1 DRC2 (5)
+ */
+#define WM8995_AIF1DRC2_KNEE2_IP_MASK 0x03E0 /* AIF1DRC2_KNEE2_IP - [9:5] */
+#define WM8995_AIF1DRC2_KNEE2_IP_SHIFT 5 /* AIF1DRC2_KNEE2_IP - [9:5] */
+#define WM8995_AIF1DRC2_KNEE2_IP_WIDTH 5 /* AIF1DRC2_KNEE2_IP - [9:5] */
+#define WM8995_AIF1DRC2_KNEE2_OP_MASK 0x001F /* AIF1DRC2_KNEE2_OP - [4:0] */
+#define WM8995_AIF1DRC2_KNEE2_OP_SHIFT 0 /* AIF1DRC2_KNEE2_OP - [4:0] */
+#define WM8995_AIF1DRC2_KNEE2_OP_WIDTH 5 /* AIF1DRC2_KNEE2_OP - [4:0] */
+
+/*
+ * R1152 (0x480) - AIF1 DAC1 EQ Gains (1)
+ */
+#define WM8995_AIF1DAC1_EQ_B1_GAIN_MASK 0xF800 /* AIF1DAC1_EQ_B1_GAIN - [15:11] */
+#define WM8995_AIF1DAC1_EQ_B1_GAIN_SHIFT 11 /* AIF1DAC1_EQ_B1_GAIN - [15:11] */
+#define WM8995_AIF1DAC1_EQ_B1_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B1_GAIN - [15:11] */
+#define WM8995_AIF1DAC1_EQ_B2_GAIN_MASK 0x07C0 /* AIF1DAC1_EQ_B2_GAIN - [10:6] */
+#define WM8995_AIF1DAC1_EQ_B2_GAIN_SHIFT 6 /* AIF1DAC1_EQ_B2_GAIN - [10:6] */
+#define WM8995_AIF1DAC1_EQ_B2_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B2_GAIN - [10:6] */
+#define WM8995_AIF1DAC1_EQ_B3_GAIN_MASK 0x003E /* AIF1DAC1_EQ_B3_GAIN - [5:1] */
+#define WM8995_AIF1DAC1_EQ_B3_GAIN_SHIFT 1 /* AIF1DAC1_EQ_B3_GAIN - [5:1] */
+#define WM8995_AIF1DAC1_EQ_B3_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B3_GAIN - [5:1] */
+#define WM8995_AIF1DAC1_EQ_ENA 0x0001 /* AIF1DAC1_EQ_ENA */
+#define WM8995_AIF1DAC1_EQ_ENA_MASK 0x0001 /* AIF1DAC1_EQ_ENA */
+#define WM8995_AIF1DAC1_EQ_ENA_SHIFT 0 /* AIF1DAC1_EQ_ENA */
+#define WM8995_AIF1DAC1_EQ_ENA_WIDTH 1 /* AIF1DAC1_EQ_ENA */
+
+/*
+ * R1153 (0x481) - AIF1 DAC1 EQ Gains (2)
+ */
+#define WM8995_AIF1DAC1_EQ_B4_GAIN_MASK 0xF800 /* AIF1DAC1_EQ_B4_GAIN - [15:11] */
+#define WM8995_AIF1DAC1_EQ_B4_GAIN_SHIFT 11 /* AIF1DAC1_EQ_B4_GAIN - [15:11] */
+#define WM8995_AIF1DAC1_EQ_B4_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B4_GAIN - [15:11] */
+#define WM8995_AIF1DAC1_EQ_B5_GAIN_MASK 0x07C0 /* AIF1DAC1_EQ_B5_GAIN - [10:6] */
+#define WM8995_AIF1DAC1_EQ_B5_GAIN_SHIFT 6 /* AIF1DAC1_EQ_B5_GAIN - [10:6] */
+#define WM8995_AIF1DAC1_EQ_B5_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B5_GAIN - [10:6] */
+
+/*
+ * R1154 (0x482) - AIF1 DAC1 EQ Band 1 A
+ */
+#define WM8995_AIF1DAC1_EQ_B1_A_MASK 0xFFFF /* AIF1DAC1_EQ_B1_A - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B1_A_SHIFT 0 /* AIF1DAC1_EQ_B1_A - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B1_A_WIDTH 16 /* AIF1DAC1_EQ_B1_A - [15:0] */
+
+/*
+ * R1155 (0x483) - AIF1 DAC1 EQ Band 1 B
+ */
+#define WM8995_AIF1DAC1_EQ_B1_B_MASK 0xFFFF /* AIF1DAC1_EQ_B1_B - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B1_B_SHIFT 0 /* AIF1DAC1_EQ_B1_B - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B1_B_WIDTH 16 /* AIF1DAC1_EQ_B1_B - [15:0] */
+
+/*
+ * R1156 (0x484) - AIF1 DAC1 EQ Band 1 PG
+ */
+#define WM8995_AIF1DAC1_EQ_B1_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B1_PG - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B1_PG_SHIFT 0 /* AIF1DAC1_EQ_B1_PG - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B1_PG_WIDTH 16 /* AIF1DAC1_EQ_B1_PG - [15:0] */
+
+/*
+ * R1157 (0x485) - AIF1 DAC1 EQ Band 2 A
+ */
+#define WM8995_AIF1DAC1_EQ_B2_A_MASK 0xFFFF /* AIF1DAC1_EQ_B2_A - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B2_A_SHIFT 0 /* AIF1DAC1_EQ_B2_A - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B2_A_WIDTH 16 /* AIF1DAC1_EQ_B2_A - [15:0] */
+
+/*
+ * R1158 (0x486) - AIF1 DAC1 EQ Band 2 B
+ */
+#define WM8995_AIF1DAC1_EQ_B2_B_MASK 0xFFFF /* AIF1DAC1_EQ_B2_B - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B2_B_SHIFT 0 /* AIF1DAC1_EQ_B2_B - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B2_B_WIDTH 16 /* AIF1DAC1_EQ_B2_B - [15:0] */
+
+/*
+ * R1159 (0x487) - AIF1 DAC1 EQ Band 2 C
+ */
+#define WM8995_AIF1DAC1_EQ_B2_C_MASK 0xFFFF /* AIF1DAC1_EQ_B2_C - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B2_C_SHIFT 0 /* AIF1DAC1_EQ_B2_C - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B2_C_WIDTH 16 /* AIF1DAC1_EQ_B2_C - [15:0] */
+
+/*
+ * R1160 (0x488) - AIF1 DAC1 EQ Band 2 PG
+ */
+#define WM8995_AIF1DAC1_EQ_B2_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B2_PG - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B2_PG_SHIFT 0 /* AIF1DAC1_EQ_B2_PG - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B2_PG_WIDTH 16 /* AIF1DAC1_EQ_B2_PG - [15:0] */
+
+/*
+ * R1161 (0x489) - AIF1 DAC1 EQ Band 3 A
+ */
+#define WM8995_AIF1DAC1_EQ_B3_A_MASK 0xFFFF /* AIF1DAC1_EQ_B3_A - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B3_A_SHIFT 0 /* AIF1DAC1_EQ_B3_A - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B3_A_WIDTH 16 /* AIF1DAC1_EQ_B3_A - [15:0] */
+
+/*
+ * R1162 (0x48A) - AIF1 DAC1 EQ Band 3 B
+ */
+#define WM8995_AIF1DAC1_EQ_B3_B_MASK 0xFFFF /* AIF1DAC1_EQ_B3_B - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B3_B_SHIFT 0 /* AIF1DAC1_EQ_B3_B - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B3_B_WIDTH 16 /* AIF1DAC1_EQ_B3_B - [15:0] */
+
+/*
+ * R1163 (0x48B) - AIF1 DAC1 EQ Band 3 C
+ */
+#define WM8995_AIF1DAC1_EQ_B3_C_MASK 0xFFFF /* AIF1DAC1_EQ_B3_C - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B3_C_SHIFT 0 /* AIF1DAC1_EQ_B3_C - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B3_C_WIDTH 16 /* AIF1DAC1_EQ_B3_C - [15:0] */
+
+/*
+ * R1164 (0x48C) - AIF1 DAC1 EQ Band 3 PG
+ */
+#define WM8995_AIF1DAC1_EQ_B3_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B3_PG - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B3_PG_SHIFT 0 /* AIF1DAC1_EQ_B3_PG - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B3_PG_WIDTH 16 /* AIF1DAC1_EQ_B3_PG - [15:0] */
+
+/*
+ * R1165 (0x48D) - AIF1 DAC1 EQ Band 4 A
+ */
+#define WM8995_AIF1DAC1_EQ_B4_A_MASK 0xFFFF /* AIF1DAC1_EQ_B4_A - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B4_A_SHIFT 0 /* AIF1DAC1_EQ_B4_A - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B4_A_WIDTH 16 /* AIF1DAC1_EQ_B4_A - [15:0] */
+
+/*
+ * R1166 (0x48E) - AIF1 DAC1 EQ Band 4 B
+ */
+#define WM8995_AIF1DAC1_EQ_B4_B_MASK 0xFFFF /* AIF1DAC1_EQ_B4_B - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B4_B_SHIFT 0 /* AIF1DAC1_EQ_B4_B - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B4_B_WIDTH 16 /* AIF1DAC1_EQ_B4_B - [15:0] */
+
+/*
+ * R1167 (0x48F) - AIF1 DAC1 EQ Band 4 C
+ */
+#define WM8995_AIF1DAC1_EQ_B4_C_MASK 0xFFFF /* AIF1DAC1_EQ_B4_C - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B4_C_SHIFT 0 /* AIF1DAC1_EQ_B4_C - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B4_C_WIDTH 16 /* AIF1DAC1_EQ_B4_C - [15:0] */
+
+/*
+ * R1168 (0x490) - AIF1 DAC1 EQ Band 4 PG
+ */
+#define WM8995_AIF1DAC1_EQ_B4_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B4_PG - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B4_PG_SHIFT 0 /* AIF1DAC1_EQ_B4_PG - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B4_PG_WIDTH 16 /* AIF1DAC1_EQ_B4_PG - [15:0] */
+
+/*
+ * R1169 (0x491) - AIF1 DAC1 EQ Band 5 A
+ */
+#define WM8995_AIF1DAC1_EQ_B5_A_MASK 0xFFFF /* AIF1DAC1_EQ_B5_A - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B5_A_SHIFT 0 /* AIF1DAC1_EQ_B5_A - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B5_A_WIDTH 16 /* AIF1DAC1_EQ_B5_A - [15:0] */
+
+/*
+ * R1170 (0x492) - AIF1 DAC1 EQ Band 5 B
+ */
+#define WM8995_AIF1DAC1_EQ_B5_B_MASK 0xFFFF /* AIF1DAC1_EQ_B5_B - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B5_B_SHIFT 0 /* AIF1DAC1_EQ_B5_B - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B5_B_WIDTH 16 /* AIF1DAC1_EQ_B5_B - [15:0] */
+
+/*
+ * R1171 (0x493) - AIF1 DAC1 EQ Band 5 PG
+ */
+#define WM8995_AIF1DAC1_EQ_B5_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B5_PG - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B5_PG_SHIFT 0 /* AIF1DAC1_EQ_B5_PG - [15:0] */
+#define WM8995_AIF1DAC1_EQ_B5_PG_WIDTH 16 /* AIF1DAC1_EQ_B5_PG - [15:0] */
+
+/*
+ * R1184 (0x4A0) - AIF1 DAC2 EQ Gains (1)
+ */
+#define WM8995_AIF1DAC2_EQ_B1_GAIN_MASK 0xF800 /* AIF1DAC2_EQ_B1_GAIN - [15:11] */
+#define WM8995_AIF1DAC2_EQ_B1_GAIN_SHIFT 11 /* AIF1DAC2_EQ_B1_GAIN - [15:11] */
+#define WM8995_AIF1DAC2_EQ_B1_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B1_GAIN - [15:11] */
+#define WM8995_AIF1DAC2_EQ_B2_GAIN_MASK 0x07C0 /* AIF1DAC2_EQ_B2_GAIN - [10:6] */
+#define WM8995_AIF1DAC2_EQ_B2_GAIN_SHIFT 6 /* AIF1DAC2_EQ_B2_GAIN - [10:6] */
+#define WM8995_AIF1DAC2_EQ_B2_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B2_GAIN - [10:6] */
+#define WM8995_AIF1DAC2_EQ_B3_GAIN_MASK 0x003E /* AIF1DAC2_EQ_B3_GAIN - [5:1] */
+#define WM8995_AIF1DAC2_EQ_B3_GAIN_SHIFT 1 /* AIF1DAC2_EQ_B3_GAIN - [5:1] */
+#define WM8995_AIF1DAC2_EQ_B3_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B3_GAIN - [5:1] */
+#define WM8995_AIF1DAC2_EQ_ENA 0x0001 /* AIF1DAC2_EQ_ENA */
+#define WM8995_AIF1DAC2_EQ_ENA_MASK 0x0001 /* AIF1DAC2_EQ_ENA */
+#define WM8995_AIF1DAC2_EQ_ENA_SHIFT 0 /* AIF1DAC2_EQ_ENA */
+#define WM8995_AIF1DAC2_EQ_ENA_WIDTH 1 /* AIF1DAC2_EQ_ENA */
+
+/*
+ * R1185 (0x4A1) - AIF1 DAC2 EQ Gains (2)
+ */
+#define WM8995_AIF1DAC2_EQ_B4_GAIN_MASK 0xF800 /* AIF1DAC2_EQ_B4_GAIN - [15:11] */
+#define WM8995_AIF1DAC2_EQ_B4_GAIN_SHIFT 11 /* AIF1DAC2_EQ_B4_GAIN - [15:11] */
+#define WM8995_AIF1DAC2_EQ_B4_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B4_GAIN - [15:11] */
+#define WM8995_AIF1DAC2_EQ_B5_GAIN_MASK 0x07C0 /* AIF1DAC2_EQ_B5_GAIN - [10:6] */
+#define WM8995_AIF1DAC2_EQ_B5_GAIN_SHIFT 6 /* AIF1DAC2_EQ_B5_GAIN - [10:6] */
+#define WM8995_AIF1DAC2_EQ_B5_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B5_GAIN - [10:6] */
+
+/*
+ * R1186 (0x4A2) - AIF1 DAC2 EQ Band 1 A
+ */
+#define WM8995_AIF1DAC2_EQ_B1_A_MASK 0xFFFF /* AIF1DAC2_EQ_B1_A - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B1_A_SHIFT 0 /* AIF1DAC2_EQ_B1_A - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B1_A_WIDTH 16 /* AIF1DAC2_EQ_B1_A - [15:0] */
+
+/*
+ * R1187 (0x4A3) - AIF1 DAC2 EQ Band 1 B
+ */
+#define WM8995_AIF1DAC2_EQ_B1_B_MASK 0xFFFF /* AIF1DAC2_EQ_B1_B - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B1_B_SHIFT 0 /* AIF1DAC2_EQ_B1_B - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B1_B_WIDTH 16 /* AIF1DAC2_EQ_B1_B - [15:0] */
+
+/*
+ * R1188 (0x4A4) - AIF1 DAC2 EQ Band 1 PG
+ */
+#define WM8995_AIF1DAC2_EQ_B1_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B1_PG - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B1_PG_SHIFT 0 /* AIF1DAC2_EQ_B1_PG - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B1_PG_WIDTH 16 /* AIF1DAC2_EQ_B1_PG - [15:0] */
+
+/*
+ * R1189 (0x4A5) - AIF1 DAC2 EQ Band 2 A
+ */
+#define WM8995_AIF1DAC2_EQ_B2_A_MASK 0xFFFF /* AIF1DAC2_EQ_B2_A - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B2_A_SHIFT 0 /* AIF1DAC2_EQ_B2_A - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B2_A_WIDTH 16 /* AIF1DAC2_EQ_B2_A - [15:0] */
+
+/*
+ * R1190 (0x4A6) - AIF1 DAC2 EQ Band 2 B
+ */
+#define WM8995_AIF1DAC2_EQ_B2_B_MASK 0xFFFF /* AIF1DAC2_EQ_B2_B - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B2_B_SHIFT 0 /* AIF1DAC2_EQ_B2_B - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B2_B_WIDTH 16 /* AIF1DAC2_EQ_B2_B - [15:0] */
+
+/*
+ * R1191 (0x4A7) - AIF1 DAC2 EQ Band 2 C
+ */
+#define WM8995_AIF1DAC2_EQ_B2_C_MASK 0xFFFF /* AIF1DAC2_EQ_B2_C - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B2_C_SHIFT 0 /* AIF1DAC2_EQ_B2_C - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B2_C_WIDTH 16 /* AIF1DAC2_EQ_B2_C - [15:0] */
+
+/*
+ * R1192 (0x4A8) - AIF1 DAC2 EQ Band 2 PG
+ */
+#define WM8995_AIF1DAC2_EQ_B2_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B2_PG - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B2_PG_SHIFT 0 /* AIF1DAC2_EQ_B2_PG - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B2_PG_WIDTH 16 /* AIF1DAC2_EQ_B2_PG - [15:0] */
+
+/*
+ * R1193 (0x4A9) - AIF1 DAC2 EQ Band 3 A
+ */
+#define WM8995_AIF1DAC2_EQ_B3_A_MASK 0xFFFF /* AIF1DAC2_EQ_B3_A - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B3_A_SHIFT 0 /* AIF1DAC2_EQ_B3_A - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B3_A_WIDTH 16 /* AIF1DAC2_EQ_B3_A - [15:0] */
+
+/*
+ * R1194 (0x4AA) - AIF1 DAC2 EQ Band 3 B
+ */
+#define WM8995_AIF1DAC2_EQ_B3_B_MASK 0xFFFF /* AIF1DAC2_EQ_B3_B - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B3_B_SHIFT 0 /* AIF1DAC2_EQ_B3_B - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B3_B_WIDTH 16 /* AIF1DAC2_EQ_B3_B - [15:0] */
+
+/*
+ * R1195 (0x4AB) - AIF1 DAC2 EQ Band 3 C
+ */
+#define WM8995_AIF1DAC2_EQ_B3_C_MASK 0xFFFF /* AIF1DAC2_EQ_B3_C - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B3_C_SHIFT 0 /* AIF1DAC2_EQ_B3_C - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B3_C_WIDTH 16 /* AIF1DAC2_EQ_B3_C - [15:0] */
+
+/*
+ * R1196 (0x4AC) - AIF1 DAC2 EQ Band 3 PG
+ */
+#define WM8995_AIF1DAC2_EQ_B3_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B3_PG - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B3_PG_SHIFT 0 /* AIF1DAC2_EQ_B3_PG - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B3_PG_WIDTH 16 /* AIF1DAC2_EQ_B3_PG - [15:0] */
+
+/*
+ * R1197 (0x4AD) - AIF1 DAC2 EQ Band 4 A
+ */
+#define WM8995_AIF1DAC2_EQ_B4_A_MASK 0xFFFF /* AIF1DAC2_EQ_B4_A - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B4_A_SHIFT 0 /* AIF1DAC2_EQ_B4_A - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B4_A_WIDTH 16 /* AIF1DAC2_EQ_B4_A - [15:0] */
+
+/*
+ * R1198 (0x4AE) - AIF1 DAC2 EQ Band 4 B
+ */
+#define WM8995_AIF1DAC2_EQ_B4_B_MASK 0xFFFF /* AIF1DAC2_EQ_B4_B - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B4_B_SHIFT 0 /* AIF1DAC2_EQ_B4_B - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B4_B_WIDTH 16 /* AIF1DAC2_EQ_B4_B - [15:0] */
+
+/*
+ * R1199 (0x4AF) - AIF1 DAC2 EQ Band 4 C
+ */
+#define WM8995_AIF1DAC2_EQ_B4_C_MASK 0xFFFF /* AIF1DAC2_EQ_B4_C - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B4_C_SHIFT 0 /* AIF1DAC2_EQ_B4_C - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B4_C_WIDTH 16 /* AIF1DAC2_EQ_B4_C - [15:0] */
+
+/*
+ * R1200 (0x4B0) - AIF1 DAC2 EQ Band 4 PG
+ */
+#define WM8995_AIF1DAC2_EQ_B4_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B4_PG - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B4_PG_SHIFT 0 /* AIF1DAC2_EQ_B4_PG - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B4_PG_WIDTH 16 /* AIF1DAC2_EQ_B4_PG - [15:0] */
+
+/*
+ * R1201 (0x4B1) - AIF1 DAC2 EQ Band 5 A
+ */
+#define WM8995_AIF1DAC2_EQ_B5_A_MASK 0xFFFF /* AIF1DAC2_EQ_B5_A - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B5_A_SHIFT 0 /* AIF1DAC2_EQ_B5_A - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B5_A_WIDTH 16 /* AIF1DAC2_EQ_B5_A - [15:0] */
+
+/*
+ * R1202 (0x4B2) - AIF1 DAC2 EQ Band 5 B
+ */
+#define WM8995_AIF1DAC2_EQ_B5_B_MASK 0xFFFF /* AIF1DAC2_EQ_B5_B - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B5_B_SHIFT 0 /* AIF1DAC2_EQ_B5_B - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B5_B_WIDTH 16 /* AIF1DAC2_EQ_B5_B - [15:0] */
+
+/*
+ * R1203 (0x4B3) - AIF1 DAC2 EQ Band 5 PG
+ */
+#define WM8995_AIF1DAC2_EQ_B5_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B5_PG - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B5_PG_SHIFT 0 /* AIF1DAC2_EQ_B5_PG - [15:0] */
+#define WM8995_AIF1DAC2_EQ_B5_PG_WIDTH 16 /* AIF1DAC2_EQ_B5_PG - [15:0] */
+
+/*
+ * R1280 (0x500) - AIF2 ADC Left Volume
+ */
+#define WM8995_AIF2ADC_VU 0x0100 /* AIF2ADC_VU */
+#define WM8995_AIF2ADC_VU_MASK 0x0100 /* AIF2ADC_VU */
+#define WM8995_AIF2ADC_VU_SHIFT 8 /* AIF2ADC_VU */
+#define WM8995_AIF2ADC_VU_WIDTH 1 /* AIF2ADC_VU */
+#define WM8995_AIF2ADCL_VOL_MASK 0x00FF /* AIF2ADCL_VOL - [7:0] */
+#define WM8995_AIF2ADCL_VOL_SHIFT 0 /* AIF2ADCL_VOL - [7:0] */
+#define WM8995_AIF2ADCL_VOL_WIDTH 8 /* AIF2ADCL_VOL - [7:0] */
+
+/*
+ * R1281 (0x501) - AIF2 ADC Right Volume
+ */
+#define WM8995_AIF2ADC_VU 0x0100 /* AIF2ADC_VU */
+#define WM8995_AIF2ADC_VU_MASK 0x0100 /* AIF2ADC_VU */
+#define WM8995_AIF2ADC_VU_SHIFT 8 /* AIF2ADC_VU */
+#define WM8995_AIF2ADC_VU_WIDTH 1 /* AIF2ADC_VU */
+#define WM8995_AIF2ADCR_VOL_MASK 0x00FF /* AIF2ADCR_VOL - [7:0] */
+#define WM8995_AIF2ADCR_VOL_SHIFT 0 /* AIF2ADCR_VOL - [7:0] */
+#define WM8995_AIF2ADCR_VOL_WIDTH 8 /* AIF2ADCR_VOL - [7:0] */
+
+/*
+ * R1282 (0x502) - AIF2 DAC Left Volume
+ */
+#define WM8995_AIF2DAC_VU 0x0100 /* AIF2DAC_VU */
+#define WM8995_AIF2DAC_VU_MASK 0x0100 /* AIF2DAC_VU */
+#define WM8995_AIF2DAC_VU_SHIFT 8 /* AIF2DAC_VU */
+#define WM8995_AIF2DAC_VU_WIDTH 1 /* AIF2DAC_VU */
+#define WM8995_AIF2DACL_VOL_MASK 0x00FF /* AIF2DACL_VOL - [7:0] */
+#define WM8995_AIF2DACL_VOL_SHIFT 0 /* AIF2DACL_VOL - [7:0] */
+#define WM8995_AIF2DACL_VOL_WIDTH 8 /* AIF2DACL_VOL - [7:0] */
+
+/*
+ * R1283 (0x503) - AIF2 DAC Right Volume
+ */
+#define WM8995_AIF2DAC_VU 0x0100 /* AIF2DAC_VU */
+#define WM8995_AIF2DAC_VU_MASK 0x0100 /* AIF2DAC_VU */
+#define WM8995_AIF2DAC_VU_SHIFT 8 /* AIF2DAC_VU */
+#define WM8995_AIF2DAC_VU_WIDTH 1 /* AIF2DAC_VU */
+#define WM8995_AIF2DACR_VOL_MASK 0x00FF /* AIF2DACR_VOL - [7:0] */
+#define WM8995_AIF2DACR_VOL_SHIFT 0 /* AIF2DACR_VOL - [7:0] */
+#define WM8995_AIF2DACR_VOL_WIDTH 8 /* AIF2DACR_VOL - [7:0] */
+
+/*
+ * R1296 (0x510) - AIF2 ADC Filters
+ */
+#define WM8995_AIF2ADC_4FS 0x8000 /* AIF2ADC_4FS */
+#define WM8995_AIF2ADC_4FS_MASK 0x8000 /* AIF2ADC_4FS */
+#define WM8995_AIF2ADC_4FS_SHIFT 15 /* AIF2ADC_4FS */
+#define WM8995_AIF2ADC_4FS_WIDTH 1 /* AIF2ADC_4FS */
+#define WM8995_AIF2ADCL_HPF 0x1000 /* AIF2ADCL_HPF */
+#define WM8995_AIF2ADCL_HPF_MASK 0x1000 /* AIF2ADCL_HPF */
+#define WM8995_AIF2ADCL_HPF_SHIFT 12 /* AIF2ADCL_HPF */
+#define WM8995_AIF2ADCL_HPF_WIDTH 1 /* AIF2ADCL_HPF */
+#define WM8995_AIF2ADCR_HPF 0x0800 /* AIF2ADCR_HPF */
+#define WM8995_AIF2ADCR_HPF_MASK 0x0800 /* AIF2ADCR_HPF */
+#define WM8995_AIF2ADCR_HPF_SHIFT 11 /* AIF2ADCR_HPF */
+#define WM8995_AIF2ADCR_HPF_WIDTH 1 /* AIF2ADCR_HPF */
+#define WM8995_AIF2ADC_HPF_MODE 0x0008 /* AIF2ADC_HPF_MODE */
+#define WM8995_AIF2ADC_HPF_MODE_MASK 0x0008 /* AIF2ADC_HPF_MODE */
+#define WM8995_AIF2ADC_HPF_MODE_SHIFT 3 /* AIF2ADC_HPF_MODE */
+#define WM8995_AIF2ADC_HPF_MODE_WIDTH 1 /* AIF2ADC_HPF_MODE */
+#define WM8995_AIF2ADC_HPF_CUT_MASK 0x0007 /* AIF2ADC_HPF_CUT - [2:0] */
+#define WM8995_AIF2ADC_HPF_CUT_SHIFT 0 /* AIF2ADC_HPF_CUT - [2:0] */
+#define WM8995_AIF2ADC_HPF_CUT_WIDTH 3 /* AIF2ADC_HPF_CUT - [2:0] */
+
+/*
+ * R1312 (0x520) - AIF2 DAC Filters (1)
+ */
+#define WM8995_AIF2DAC_MUTE 0x0200 /* AIF2DAC_MUTE */
+#define WM8995_AIF2DAC_MUTE_MASK 0x0200 /* AIF2DAC_MUTE */
+#define WM8995_AIF2DAC_MUTE_SHIFT 9 /* AIF2DAC_MUTE */
+#define WM8995_AIF2DAC_MUTE_WIDTH 1 /* AIF2DAC_MUTE */
+#define WM8995_AIF2DAC_MONO 0x0080 /* AIF2DAC_MONO */
+#define WM8995_AIF2DAC_MONO_MASK 0x0080 /* AIF2DAC_MONO */
+#define WM8995_AIF2DAC_MONO_SHIFT 7 /* AIF2DAC_MONO */
+#define WM8995_AIF2DAC_MONO_WIDTH 1 /* AIF2DAC_MONO */
+#define WM8995_AIF2DAC_MUTERATE 0x0020 /* AIF2DAC_MUTERATE */
+#define WM8995_AIF2DAC_MUTERATE_MASK 0x0020 /* AIF2DAC_MUTERATE */
+#define WM8995_AIF2DAC_MUTERATE_SHIFT 5 /* AIF2DAC_MUTERATE */
+#define WM8995_AIF2DAC_MUTERATE_WIDTH 1 /* AIF2DAC_MUTERATE */
+#define WM8995_AIF2DAC_UNMUTE_RAMP 0x0010 /* AIF2DAC_UNMUTE_RAMP */
+#define WM8995_AIF2DAC_UNMUTE_RAMP_MASK 0x0010 /* AIF2DAC_UNMUTE_RAMP */
+#define WM8995_AIF2DAC_UNMUTE_RAMP_SHIFT 4 /* AIF2DAC_UNMUTE_RAMP */
+#define WM8995_AIF2DAC_UNMUTE_RAMP_WIDTH 1 /* AIF2DAC_UNMUTE_RAMP */
+#define WM8995_AIF2DAC_DEEMP_MASK 0x0006 /* AIF2DAC_DEEMP - [2:1] */
+#define WM8995_AIF2DAC_DEEMP_SHIFT 1 /* AIF2DAC_DEEMP - [2:1] */
+#define WM8995_AIF2DAC_DEEMP_WIDTH 2 /* AIF2DAC_DEEMP - [2:1] */
+
+/*
+ * R1313 (0x521) - AIF2 DAC Filters (2)
+ */
+#define WM8995_AIF2DAC_3D_GAIN_MASK 0x3E00 /* AIF2DAC_3D_GAIN - [13:9] */
+#define WM8995_AIF2DAC_3D_GAIN_SHIFT 9 /* AIF2DAC_3D_GAIN - [13:9] */
+#define WM8995_AIF2DAC_3D_GAIN_WIDTH 5 /* AIF2DAC_3D_GAIN - [13:9] */
+#define WM8995_AIF2DAC_3D_ENA 0x0100 /* AIF2DAC_3D_ENA */
+#define WM8995_AIF2DAC_3D_ENA_MASK 0x0100 /* AIF2DAC_3D_ENA */
+#define WM8995_AIF2DAC_3D_ENA_SHIFT 8 /* AIF2DAC_3D_ENA */
+#define WM8995_AIF2DAC_3D_ENA_WIDTH 1 /* AIF2DAC_3D_ENA */
+
+/*
+ * R1344 (0x540) - AIF2 DRC (1)
+ */
+#define WM8995_AIF2DRC_SIG_DET_RMS_MASK 0xF800 /* AIF2DRC_SIG_DET_RMS - [15:11] */
+#define WM8995_AIF2DRC_SIG_DET_RMS_SHIFT 11 /* AIF2DRC_SIG_DET_RMS - [15:11] */
+#define WM8995_AIF2DRC_SIG_DET_RMS_WIDTH 5 /* AIF2DRC_SIG_DET_RMS - [15:11] */
+#define WM8995_AIF2DRC_SIG_DET_PK_MASK 0x0600 /* AIF2DRC_SIG_DET_PK - [10:9] */
+#define WM8995_AIF2DRC_SIG_DET_PK_SHIFT 9 /* AIF2DRC_SIG_DET_PK - [10:9] */
+#define WM8995_AIF2DRC_SIG_DET_PK_WIDTH 2 /* AIF2DRC_SIG_DET_PK - [10:9] */
+#define WM8995_AIF2DRC_NG_ENA 0x0100 /* AIF2DRC_NG_ENA */
+#define WM8995_AIF2DRC_NG_ENA_MASK 0x0100 /* AIF2DRC_NG_ENA */
+#define WM8995_AIF2DRC_NG_ENA_SHIFT 8 /* AIF2DRC_NG_ENA */
+#define WM8995_AIF2DRC_NG_ENA_WIDTH 1 /* AIF2DRC_NG_ENA */
+#define WM8995_AIF2DRC_SIG_DET_MODE 0x0080 /* AIF2DRC_SIG_DET_MODE */
+#define WM8995_AIF2DRC_SIG_DET_MODE_MASK 0x0080 /* AIF2DRC_SIG_DET_MODE */
+#define WM8995_AIF2DRC_SIG_DET_MODE_SHIFT 7 /* AIF2DRC_SIG_DET_MODE */
+#define WM8995_AIF2DRC_SIG_DET_MODE_WIDTH 1 /* AIF2DRC_SIG_DET_MODE */
+#define WM8995_AIF2DRC_SIG_DET 0x0040 /* AIF2DRC_SIG_DET */
+#define WM8995_AIF2DRC_SIG_DET_MASK 0x0040 /* AIF2DRC_SIG_DET */
+#define WM8995_AIF2DRC_SIG_DET_SHIFT 6 /* AIF2DRC_SIG_DET */
+#define WM8995_AIF2DRC_SIG_DET_WIDTH 1 /* AIF2DRC_SIG_DET */
+#define WM8995_AIF2DRC_KNEE2_OP_ENA 0x0020 /* AIF2DRC_KNEE2_OP_ENA */
+#define WM8995_AIF2DRC_KNEE2_OP_ENA_MASK 0x0020 /* AIF2DRC_KNEE2_OP_ENA */
+#define WM8995_AIF2DRC_KNEE2_OP_ENA_SHIFT 5 /* AIF2DRC_KNEE2_OP_ENA */
+#define WM8995_AIF2DRC_KNEE2_OP_ENA_WIDTH 1 /* AIF2DRC_KNEE2_OP_ENA */
+#define WM8995_AIF2DRC_QR 0x0010 /* AIF2DRC_QR */
+#define WM8995_AIF2DRC_QR_MASK 0x0010 /* AIF2DRC_QR */
+#define WM8995_AIF2DRC_QR_SHIFT 4 /* AIF2DRC_QR */
+#define WM8995_AIF2DRC_QR_WIDTH 1 /* AIF2DRC_QR */
+#define WM8995_AIF2DRC_ANTICLIP 0x0008 /* AIF2DRC_ANTICLIP */
+#define WM8995_AIF2DRC_ANTICLIP_MASK 0x0008 /* AIF2DRC_ANTICLIP */
+#define WM8995_AIF2DRC_ANTICLIP_SHIFT 3 /* AIF2DRC_ANTICLIP */
+#define WM8995_AIF2DRC_ANTICLIP_WIDTH 1 /* AIF2DRC_ANTICLIP */
+#define WM8995_AIF2DAC_DRC_ENA 0x0004 /* AIF2DAC_DRC_ENA */
+#define WM8995_AIF2DAC_DRC_ENA_MASK 0x0004 /* AIF2DAC_DRC_ENA */
+#define WM8995_AIF2DAC_DRC_ENA_SHIFT 2 /* AIF2DAC_DRC_ENA */
+#define WM8995_AIF2DAC_DRC_ENA_WIDTH 1 /* AIF2DAC_DRC_ENA */
+#define WM8995_AIF2ADCL_DRC_ENA 0x0002 /* AIF2ADCL_DRC_ENA */
+#define WM8995_AIF2ADCL_DRC_ENA_MASK 0x0002 /* AIF2ADCL_DRC_ENA */
+#define WM8995_AIF2ADCL_DRC_ENA_SHIFT 1 /* AIF2ADCL_DRC_ENA */
+#define WM8995_AIF2ADCL_DRC_ENA_WIDTH 1 /* AIF2ADCL_DRC_ENA */
+#define WM8995_AIF2ADCR_DRC_ENA 0x0001 /* AIF2ADCR_DRC_ENA */
+#define WM8995_AIF2ADCR_DRC_ENA_MASK 0x0001 /* AIF2ADCR_DRC_ENA */
+#define WM8995_AIF2ADCR_DRC_ENA_SHIFT 0 /* AIF2ADCR_DRC_ENA */
+#define WM8995_AIF2ADCR_DRC_ENA_WIDTH 1 /* AIF2ADCR_DRC_ENA */
+
+/*
+ * R1345 (0x541) - AIF2 DRC (2)
+ */
+#define WM8995_AIF2DRC_ATK_MASK 0x1E00 /* AIF2DRC_ATK - [12:9] */
+#define WM8995_AIF2DRC_ATK_SHIFT 9 /* AIF2DRC_ATK - [12:9] */
+#define WM8995_AIF2DRC_ATK_WIDTH 4 /* AIF2DRC_ATK - [12:9] */
+#define WM8995_AIF2DRC_DCY_MASK 0x01E0 /* AIF2DRC_DCY - [8:5] */
+#define WM8995_AIF2DRC_DCY_SHIFT 5 /* AIF2DRC_DCY - [8:5] */
+#define WM8995_AIF2DRC_DCY_WIDTH 4 /* AIF2DRC_DCY - [8:5] */
+#define WM8995_AIF2DRC_MINGAIN_MASK 0x001C /* AIF2DRC_MINGAIN - [4:2] */
+#define WM8995_AIF2DRC_MINGAIN_SHIFT 2 /* AIF2DRC_MINGAIN - [4:2] */
+#define WM8995_AIF2DRC_MINGAIN_WIDTH 3 /* AIF2DRC_MINGAIN - [4:2] */
+#define WM8995_AIF2DRC_MAXGAIN_MASK 0x0003 /* AIF2DRC_MAXGAIN - [1:0] */
+#define WM8995_AIF2DRC_MAXGAIN_SHIFT 0 /* AIF2DRC_MAXGAIN - [1:0] */
+#define WM8995_AIF2DRC_MAXGAIN_WIDTH 2 /* AIF2DRC_MAXGAIN - [1:0] */
+
+/*
+ * R1346 (0x542) - AIF2 DRC (3)
+ */
+#define WM8995_AIF2DRC_NG_MINGAIN_MASK 0xF000 /* AIF2DRC_NG_MINGAIN - [15:12] */
+#define WM8995_AIF2DRC_NG_MINGAIN_SHIFT 12 /* AIF2DRC_NG_MINGAIN - [15:12] */
+#define WM8995_AIF2DRC_NG_MINGAIN_WIDTH 4 /* AIF2DRC_NG_MINGAIN - [15:12] */
+#define WM8995_AIF2DRC_NG_EXP_MASK 0x0C00 /* AIF2DRC_NG_EXP - [11:10] */
+#define WM8995_AIF2DRC_NG_EXP_SHIFT 10 /* AIF2DRC_NG_EXP - [11:10] */
+#define WM8995_AIF2DRC_NG_EXP_WIDTH 2 /* AIF2DRC_NG_EXP - [11:10] */
+#define WM8995_AIF2DRC_QR_THR_MASK 0x0300 /* AIF2DRC_QR_THR - [9:8] */
+#define WM8995_AIF2DRC_QR_THR_SHIFT 8 /* AIF2DRC_QR_THR - [9:8] */
+#define WM8995_AIF2DRC_QR_THR_WIDTH 2 /* AIF2DRC_QR_THR - [9:8] */
+#define WM8995_AIF2DRC_QR_DCY_MASK 0x00C0 /* AIF2DRC_QR_DCY - [7:6] */
+#define WM8995_AIF2DRC_QR_DCY_SHIFT 6 /* AIF2DRC_QR_DCY - [7:6] */
+#define WM8995_AIF2DRC_QR_DCY_WIDTH 2 /* AIF2DRC_QR_DCY - [7:6] */
+#define WM8995_AIF2DRC_HI_COMP_MASK 0x0038 /* AIF2DRC_HI_COMP - [5:3] */
+#define WM8995_AIF2DRC_HI_COMP_SHIFT 3 /* AIF2DRC_HI_COMP - [5:3] */
+#define WM8995_AIF2DRC_HI_COMP_WIDTH 3 /* AIF2DRC_HI_COMP - [5:3] */
+#define WM8995_AIF2DRC_LO_COMP_MASK 0x0007 /* AIF2DRC_LO_COMP - [2:0] */
+#define WM8995_AIF2DRC_LO_COMP_SHIFT 0 /* AIF2DRC_LO_COMP - [2:0] */
+#define WM8995_AIF2DRC_LO_COMP_WIDTH 3 /* AIF2DRC_LO_COMP - [2:0] */
+
+/*
+ * R1347 (0x543) - AIF2 DRC (4)
+ */
+#define WM8995_AIF2DRC_KNEE_IP_MASK 0x07E0 /* AIF2DRC_KNEE_IP - [10:5] */
+#define WM8995_AIF2DRC_KNEE_IP_SHIFT 5 /* AIF2DRC_KNEE_IP - [10:5] */
+#define WM8995_AIF2DRC_KNEE_IP_WIDTH 6 /* AIF2DRC_KNEE_IP - [10:5] */
+#define WM8995_AIF2DRC_KNEE_OP_MASK 0x001F /* AIF2DRC_KNEE_OP - [4:0] */
+#define WM8995_AIF2DRC_KNEE_OP_SHIFT 0 /* AIF2DRC_KNEE_OP - [4:0] */
+#define WM8995_AIF2DRC_KNEE_OP_WIDTH 5 /* AIF2DRC_KNEE_OP - [4:0] */
+
+/*
+ * R1348 (0x544) - AIF2 DRC (5)
+ */
+#define WM8995_AIF2DRC_KNEE2_IP_MASK 0x03E0 /* AIF2DRC_KNEE2_IP - [9:5] */
+#define WM8995_AIF2DRC_KNEE2_IP_SHIFT 5 /* AIF2DRC_KNEE2_IP - [9:5] */
+#define WM8995_AIF2DRC_KNEE2_IP_WIDTH 5 /* AIF2DRC_KNEE2_IP - [9:5] */
+#define WM8995_AIF2DRC_KNEE2_OP_MASK 0x001F /* AIF2DRC_KNEE2_OP - [4:0] */
+#define WM8995_AIF2DRC_KNEE2_OP_SHIFT 0 /* AIF2DRC_KNEE2_OP - [4:0] */
+#define WM8995_AIF2DRC_KNEE2_OP_WIDTH 5 /* AIF2DRC_KNEE2_OP - [4:0] */
+
+/*
+ * R1408 (0x580) - AIF2 EQ Gains (1)
+ */
+#define WM8995_AIF2DAC_EQ_B1_GAIN_MASK 0xF800 /* AIF2DAC_EQ_B1_GAIN - [15:11] */
+#define WM8995_AIF2DAC_EQ_B1_GAIN_SHIFT 11 /* AIF2DAC_EQ_B1_GAIN - [15:11] */
+#define WM8995_AIF2DAC_EQ_B1_GAIN_WIDTH 5 /* AIF2DAC_EQ_B1_GAIN - [15:11] */
+#define WM8995_AIF2DAC_EQ_B2_GAIN_MASK 0x07C0 /* AIF2DAC_EQ_B2_GAIN - [10:6] */
+#define WM8995_AIF2DAC_EQ_B2_GAIN_SHIFT 6 /* AIF2DAC_EQ_B2_GAIN - [10:6] */
+#define WM8995_AIF2DAC_EQ_B2_GAIN_WIDTH 5 /* AIF2DAC_EQ_B2_GAIN - [10:6] */
+#define WM8995_AIF2DAC_EQ_B3_GAIN_MASK 0x003E /* AIF2DAC_EQ_B3_GAIN - [5:1] */
+#define WM8995_AIF2DAC_EQ_B3_GAIN_SHIFT 1 /* AIF2DAC_EQ_B3_GAIN - [5:1] */
+#define WM8995_AIF2DAC_EQ_B3_GAIN_WIDTH 5 /* AIF2DAC_EQ_B3_GAIN - [5:1] */
+#define WM8995_AIF2DAC_EQ_ENA 0x0001 /* AIF2DAC_EQ_ENA */
+#define WM8995_AIF2DAC_EQ_ENA_MASK 0x0001 /* AIF2DAC_EQ_ENA */
+#define WM8995_AIF2DAC_EQ_ENA_SHIFT 0 /* AIF2DAC_EQ_ENA */
+#define WM8995_AIF2DAC_EQ_ENA_WIDTH 1 /* AIF2DAC_EQ_ENA */
+
+/*
+ * R1409 (0x581) - AIF2 EQ Gains (2)
+ */
+#define WM8995_AIF2DAC_EQ_B4_GAIN_MASK 0xF800 /* AIF2DAC_EQ_B4_GAIN - [15:11] */
+#define WM8995_AIF2DAC_EQ_B4_GAIN_SHIFT 11 /* AIF2DAC_EQ_B4_GAIN - [15:11] */
+#define WM8995_AIF2DAC_EQ_B4_GAIN_WIDTH 5 /* AIF2DAC_EQ_B4_GAIN - [15:11] */
+#define WM8995_AIF2DAC_EQ_B5_GAIN_MASK 0x07C0 /* AIF2DAC_EQ_B5_GAIN - [10:6] */
+#define WM8995_AIF2DAC_EQ_B5_GAIN_SHIFT 6 /* AIF2DAC_EQ_B5_GAIN - [10:6] */
+#define WM8995_AIF2DAC_EQ_B5_GAIN_WIDTH 5 /* AIF2DAC_EQ_B5_GAIN - [10:6] */
+
+/*
+ * R1410 (0x582) - AIF2 EQ Band 1 A
+ */
+#define WM8995_AIF2DAC_EQ_B1_A_MASK 0xFFFF /* AIF2DAC_EQ_B1_A - [15:0] */
+#define WM8995_AIF2DAC_EQ_B1_A_SHIFT 0 /* AIF2DAC_EQ_B1_A - [15:0] */
+#define WM8995_AIF2DAC_EQ_B1_A_WIDTH 16 /* AIF2DAC_EQ_B1_A - [15:0] */
+
+/*
+ * R1411 (0x583) - AIF2 EQ Band 1 B
+ */
+#define WM8995_AIF2DAC_EQ_B1_B_MASK 0xFFFF /* AIF2DAC_EQ_B1_B - [15:0] */
+#define WM8995_AIF2DAC_EQ_B1_B_SHIFT 0 /* AIF2DAC_EQ_B1_B - [15:0] */
+#define WM8995_AIF2DAC_EQ_B1_B_WIDTH 16 /* AIF2DAC_EQ_B1_B - [15:0] */
+
+/*
+ * R1412 (0x584) - AIF2 EQ Band 1 PG
+ */
+#define WM8995_AIF2DAC_EQ_B1_PG_MASK 0xFFFF /* AIF2DAC_EQ_B1_PG - [15:0] */
+#define WM8995_AIF2DAC_EQ_B1_PG_SHIFT 0 /* AIF2DAC_EQ_B1_PG - [15:0] */
+#define WM8995_AIF2DAC_EQ_B1_PG_WIDTH 16 /* AIF2DAC_EQ_B1_PG - [15:0] */
+
+/*
+ * R1413 (0x585) - AIF2 EQ Band 2 A
+ */
+#define WM8995_AIF2DAC_EQ_B2_A_MASK 0xFFFF /* AIF2DAC_EQ_B2_A - [15:0] */
+#define WM8995_AIF2DAC_EQ_B2_A_SHIFT 0 /* AIF2DAC_EQ_B2_A - [15:0] */
+#define WM8995_AIF2DAC_EQ_B2_A_WIDTH 16 /* AIF2DAC_EQ_B2_A - [15:0] */
+
+/*
+ * R1414 (0x586) - AIF2 EQ Band 2 B
+ */
+#define WM8995_AIF2DAC_EQ_B2_B_MASK 0xFFFF /* AIF2DAC_EQ_B2_B - [15:0] */
+#define WM8995_AIF2DAC_EQ_B2_B_SHIFT 0 /* AIF2DAC_EQ_B2_B - [15:0] */
+#define WM8995_AIF2DAC_EQ_B2_B_WIDTH 16 /* AIF2DAC_EQ_B2_B - [15:0] */
+
+/*
+ * R1415 (0x587) - AIF2 EQ Band 2 C
+ */
+#define WM8995_AIF2DAC_EQ_B2_C_MASK 0xFFFF /* AIF2DAC_EQ_B2_C - [15:0] */
+#define WM8995_AIF2DAC_EQ_B2_C_SHIFT 0 /* AIF2DAC_EQ_B2_C - [15:0] */
+#define WM8995_AIF2DAC_EQ_B2_C_WIDTH 16 /* AIF2DAC_EQ_B2_C - [15:0] */
+
+/*
+ * R1416 (0x588) - AIF2 EQ Band 2 PG
+ */
+#define WM8995_AIF2DAC_EQ_B2_PG_MASK 0xFFFF /* AIF2DAC_EQ_B2_PG - [15:0] */
+#define WM8995_AIF2DAC_EQ_B2_PG_SHIFT 0 /* AIF2DAC_EQ_B2_PG - [15:0] */
+#define WM8995_AIF2DAC_EQ_B2_PG_WIDTH 16 /* AIF2DAC_EQ_B2_PG - [15:0] */
+
+/*
+ * R1417 (0x589) - AIF2 EQ Band 3 A
+ */
+#define WM8995_AIF2DAC_EQ_B3_A_MASK 0xFFFF /* AIF2DAC_EQ_B3_A - [15:0] */
+#define WM8995_AIF2DAC_EQ_B3_A_SHIFT 0 /* AIF2DAC_EQ_B3_A - [15:0] */
+#define WM8995_AIF2DAC_EQ_B3_A_WIDTH 16 /* AIF2DAC_EQ_B3_A - [15:0] */
+
+/*
+ * R1418 (0x58A) - AIF2 EQ Band 3 B
+ */
+#define WM8995_AIF2DAC_EQ_B3_B_MASK 0xFFFF /* AIF2DAC_EQ_B3_B - [15:0] */
+#define WM8995_AIF2DAC_EQ_B3_B_SHIFT 0 /* AIF2DAC_EQ_B3_B - [15:0] */
+#define WM8995_AIF2DAC_EQ_B3_B_WIDTH 16 /* AIF2DAC_EQ_B3_B - [15:0] */
+
+/*
+ * R1419 (0x58B) - AIF2 EQ Band 3 C
+ */
+#define WM8995_AIF2DAC_EQ_B3_C_MASK 0xFFFF /* AIF2DAC_EQ_B3_C - [15:0] */
+#define WM8995_AIF2DAC_EQ_B3_C_SHIFT 0 /* AIF2DAC_EQ_B3_C - [15:0] */
+#define WM8995_AIF2DAC_EQ_B3_C_WIDTH 16 /* AIF2DAC_EQ_B3_C - [15:0] */
+
+/*
+ * R1420 (0x58C) - AIF2 EQ Band 3 PG
+ */
+#define WM8995_AIF2DAC_EQ_B3_PG_MASK 0xFFFF /* AIF2DAC_EQ_B3_PG - [15:0] */
+#define WM8995_AIF2DAC_EQ_B3_PG_SHIFT 0 /* AIF2DAC_EQ_B3_PG - [15:0] */
+#define WM8995_AIF2DAC_EQ_B3_PG_WIDTH 16 /* AIF2DAC_EQ_B3_PG - [15:0] */
+
+/*
+ * R1421 (0x58D) - AIF2 EQ Band 4 A
+ */
+#define WM8995_AIF2DAC_EQ_B4_A_MASK 0xFFFF /* AIF2DAC_EQ_B4_A - [15:0] */
+#define WM8995_AIF2DAC_EQ_B4_A_SHIFT 0 /* AIF2DAC_EQ_B4_A - [15:0] */
+#define WM8995_AIF2DAC_EQ_B4_A_WIDTH 16 /* AIF2DAC_EQ_B4_A - [15:0] */
+
+/*
+ * R1422 (0x58E) - AIF2 EQ Band 4 B
+ */
+#define WM8995_AIF2DAC_EQ_B4_B_MASK 0xFFFF /* AIF2DAC_EQ_B4_B - [15:0] */
+#define WM8995_AIF2DAC_EQ_B4_B_SHIFT 0 /* AIF2DAC_EQ_B4_B - [15:0] */
+#define WM8995_AIF2DAC_EQ_B4_B_WIDTH 16 /* AIF2DAC_EQ_B4_B - [15:0] */
+
+/*
+ * R1423 (0x58F) - AIF2 EQ Band 4 C
+ */
+#define WM8995_AIF2DAC_EQ_B4_C_MASK 0xFFFF /* AIF2DAC_EQ_B4_C - [15:0] */
+#define WM8995_AIF2DAC_EQ_B4_C_SHIFT 0 /* AIF2DAC_EQ_B4_C - [15:0] */
+#define WM8995_AIF2DAC_EQ_B4_C_WIDTH 16 /* AIF2DAC_EQ_B4_C - [15:0] */
+
+/*
+ * R1424 (0x590) - AIF2 EQ Band 4 PG
+ */
+#define WM8995_AIF2DAC_EQ_B4_PG_MASK 0xFFFF /* AIF2DAC_EQ_B4_PG - [15:0] */
+#define WM8995_AIF2DAC_EQ_B4_PG_SHIFT 0 /* AIF2DAC_EQ_B4_PG - [15:0] */
+#define WM8995_AIF2DAC_EQ_B4_PG_WIDTH 16 /* AIF2DAC_EQ_B4_PG - [15:0] */
+
+/*
+ * R1425 (0x591) - AIF2 EQ Band 5 A
+ */
+#define WM8995_AIF2DAC_EQ_B5_A_MASK 0xFFFF /* AIF2DAC_EQ_B5_A - [15:0] */
+#define WM8995_AIF2DAC_EQ_B5_A_SHIFT 0 /* AIF2DAC_EQ_B5_A - [15:0] */
+#define WM8995_AIF2DAC_EQ_B5_A_WIDTH 16 /* AIF2DAC_EQ_B5_A - [15:0] */
+
+/*
+ * R1426 (0x592) - AIF2 EQ Band 5 B
+ */
+#define WM8995_AIF2DAC_EQ_B5_B_MASK 0xFFFF /* AIF2DAC_EQ_B5_B - [15:0] */
+#define WM8995_AIF2DAC_EQ_B5_B_SHIFT 0 /* AIF2DAC_EQ_B5_B - [15:0] */
+#define WM8995_AIF2DAC_EQ_B5_B_WIDTH 16 /* AIF2DAC_EQ_B5_B - [15:0] */
+
+/*
+ * R1427 (0x593) - AIF2 EQ Band 5 PG
+ */
+#define WM8995_AIF2DAC_EQ_B5_PG_MASK 0xFFFF /* AIF2DAC_EQ_B5_PG - [15:0] */
+#define WM8995_AIF2DAC_EQ_B5_PG_SHIFT 0 /* AIF2DAC_EQ_B5_PG - [15:0] */
+#define WM8995_AIF2DAC_EQ_B5_PG_WIDTH 16 /* AIF2DAC_EQ_B5_PG - [15:0] */
+
+/*
+ * R1536 (0x600) - DAC1 Mixer Volumes
+ */
+#define WM8995_ADCR_DAC1_VOL_MASK 0x03E0 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8995_ADCR_DAC1_VOL_SHIFT 5 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8995_ADCR_DAC1_VOL_WIDTH 5 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8995_ADCL_DAC1_VOL_MASK 0x001F /* ADCL_DAC1_VOL - [4:0] */
+#define WM8995_ADCL_DAC1_VOL_SHIFT 0 /* ADCL_DAC1_VOL - [4:0] */
+#define WM8995_ADCL_DAC1_VOL_WIDTH 5 /* ADCL_DAC1_VOL - [4:0] */
+
+/*
+ * R1537 (0x601) - DAC1 Left Mixer Routing
+ */
+#define WM8995_ADCR_TO_DAC1L 0x0020 /* ADCR_TO_DAC1L */
+#define WM8995_ADCR_TO_DAC1L_MASK 0x0020 /* ADCR_TO_DAC1L */
+#define WM8995_ADCR_TO_DAC1L_SHIFT 5 /* ADCR_TO_DAC1L */
+#define WM8995_ADCR_TO_DAC1L_WIDTH 1 /* ADCR_TO_DAC1L */
+#define WM8995_ADCL_TO_DAC1L 0x0010 /* ADCL_TO_DAC1L */
+#define WM8995_ADCL_TO_DAC1L_MASK 0x0010 /* ADCL_TO_DAC1L */
+#define WM8995_ADCL_TO_DAC1L_SHIFT 4 /* ADCL_TO_DAC1L */
+#define WM8995_ADCL_TO_DAC1L_WIDTH 1 /* ADCL_TO_DAC1L */
+#define WM8995_AIF2DACL_TO_DAC1L 0x0004 /* AIF2DACL_TO_DAC1L */
+#define WM8995_AIF2DACL_TO_DAC1L_MASK 0x0004 /* AIF2DACL_TO_DAC1L */
+#define WM8995_AIF2DACL_TO_DAC1L_SHIFT 2 /* AIF2DACL_TO_DAC1L */
+#define WM8995_AIF2DACL_TO_DAC1L_WIDTH 1 /* AIF2DACL_TO_DAC1L */
+#define WM8995_AIF1DAC2L_TO_DAC1L 0x0002 /* AIF1DAC2L_TO_DAC1L */
+#define WM8995_AIF1DAC2L_TO_DAC1L_MASK 0x0002 /* AIF1DAC2L_TO_DAC1L */
+#define WM8995_AIF1DAC2L_TO_DAC1L_SHIFT 1 /* AIF1DAC2L_TO_DAC1L */
+#define WM8995_AIF1DAC2L_TO_DAC1L_WIDTH 1 /* AIF1DAC2L_TO_DAC1L */
+#define WM8995_AIF1DAC1L_TO_DAC1L 0x0001 /* AIF1DAC1L_TO_DAC1L */
+#define WM8995_AIF1DAC1L_TO_DAC1L_MASK 0x0001 /* AIF1DAC1L_TO_DAC1L */
+#define WM8995_AIF1DAC1L_TO_DAC1L_SHIFT 0 /* AIF1DAC1L_TO_DAC1L */
+#define WM8995_AIF1DAC1L_TO_DAC1L_WIDTH 1 /* AIF1DAC1L_TO_DAC1L */
+
+/*
+ * R1538 (0x602) - DAC1 Right Mixer Routing
+ */
+#define WM8995_ADCR_TO_DAC1R 0x0020 /* ADCR_TO_DAC1R */
+#define WM8995_ADCR_TO_DAC1R_MASK 0x0020 /* ADCR_TO_DAC1R */
+#define WM8995_ADCR_TO_DAC1R_SHIFT 5 /* ADCR_TO_DAC1R */
+#define WM8995_ADCR_TO_DAC1R_WIDTH 1 /* ADCR_TO_DAC1R */
+#define WM8995_ADCL_TO_DAC1R 0x0010 /* ADCL_TO_DAC1R */
+#define WM8995_ADCL_TO_DAC1R_MASK 0x0010 /* ADCL_TO_DAC1R */
+#define WM8995_ADCL_TO_DAC1R_SHIFT 4 /* ADCL_TO_DAC1R */
+#define WM8995_ADCL_TO_DAC1R_WIDTH 1 /* ADCL_TO_DAC1R */
+#define WM8995_AIF2DACR_TO_DAC1R 0x0004 /* AIF2DACR_TO_DAC1R */
+#define WM8995_AIF2DACR_TO_DAC1R_MASK 0x0004 /* AIF2DACR_TO_DAC1R */
+#define WM8995_AIF2DACR_TO_DAC1R_SHIFT 2 /* AIF2DACR_TO_DAC1R */
+#define WM8995_AIF2DACR_TO_DAC1R_WIDTH 1 /* AIF2DACR_TO_DAC1R */
+#define WM8995_AIF1DAC2R_TO_DAC1R 0x0002 /* AIF1DAC2R_TO_DAC1R */
+#define WM8995_AIF1DAC2R_TO_DAC1R_MASK 0x0002 /* AIF1DAC2R_TO_DAC1R */
+#define WM8995_AIF1DAC2R_TO_DAC1R_SHIFT 1 /* AIF1DAC2R_TO_DAC1R */
+#define WM8995_AIF1DAC2R_TO_DAC1R_WIDTH 1 /* AIF1DAC2R_TO_DAC1R */
+#define WM8995_AIF1DAC1R_TO_DAC1R 0x0001 /* AIF1DAC1R_TO_DAC1R */
+#define WM8995_AIF1DAC1R_TO_DAC1R_MASK 0x0001 /* AIF1DAC1R_TO_DAC1R */
+#define WM8995_AIF1DAC1R_TO_DAC1R_SHIFT 0 /* AIF1DAC1R_TO_DAC1R */
+#define WM8995_AIF1DAC1R_TO_DAC1R_WIDTH 1 /* AIF1DAC1R_TO_DAC1R */
+
+/*
+ * R1539 (0x603) - DAC2 Mixer Volumes
+ */
+#define WM8995_ADCR_DAC2_VOL_MASK 0x03E0 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8995_ADCR_DAC2_VOL_SHIFT 5 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8995_ADCR_DAC2_VOL_WIDTH 5 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8995_ADCL_DAC2_VOL_MASK 0x001F /* ADCL_DAC2_VOL - [4:0] */
+#define WM8995_ADCL_DAC2_VOL_SHIFT 0 /* ADCL_DAC2_VOL - [4:0] */
+#define WM8995_ADCL_DAC2_VOL_WIDTH 5 /* ADCL_DAC2_VOL - [4:0] */
+
+/*
+ * R1540 (0x604) - DAC2 Left Mixer Routing
+ */
+#define WM8995_ADCR_TO_DAC2L 0x0020 /* ADCR_TO_DAC2L */
+#define WM8995_ADCR_TO_DAC2L_MASK 0x0020 /* ADCR_TO_DAC2L */
+#define WM8995_ADCR_TO_DAC2L_SHIFT 5 /* ADCR_TO_DAC2L */
+#define WM8995_ADCR_TO_DAC2L_WIDTH 1 /* ADCR_TO_DAC2L */
+#define WM8995_ADCL_TO_DAC2L 0x0010 /* ADCL_TO_DAC2L */
+#define WM8995_ADCL_TO_DAC2L_MASK 0x0010 /* ADCL_TO_DAC2L */
+#define WM8995_ADCL_TO_DAC2L_SHIFT 4 /* ADCL_TO_DAC2L */
+#define WM8995_ADCL_TO_DAC2L_WIDTH 1 /* ADCL_TO_DAC2L */
+#define WM8995_AIF2DACL_TO_DAC2L 0x0004 /* AIF2DACL_TO_DAC2L */
+#define WM8995_AIF2DACL_TO_DAC2L_MASK 0x0004 /* AIF2DACL_TO_DAC2L */
+#define WM8995_AIF2DACL_TO_DAC2L_SHIFT 2 /* AIF2DACL_TO_DAC2L */
+#define WM8995_AIF2DACL_TO_DAC2L_WIDTH 1 /* AIF2DACL_TO_DAC2L */
+#define WM8995_AIF1DAC2L_TO_DAC2L 0x0002 /* AIF1DAC2L_TO_DAC2L */
+#define WM8995_AIF1DAC2L_TO_DAC2L_MASK 0x0002 /* AIF1DAC2L_TO_DAC2L */
+#define WM8995_AIF1DAC2L_TO_DAC2L_SHIFT 1 /* AIF1DAC2L_TO_DAC2L */
+#define WM8995_AIF1DAC2L_TO_DAC2L_WIDTH 1 /* AIF1DAC2L_TO_DAC2L */
+#define WM8995_AIF1DAC1L_TO_DAC2L 0x0001 /* AIF1DAC1L_TO_DAC2L */
+#define WM8995_AIF1DAC1L_TO_DAC2L_MASK 0x0001 /* AIF1DAC1L_TO_DAC2L */
+#define WM8995_AIF1DAC1L_TO_DAC2L_SHIFT 0 /* AIF1DAC1L_TO_DAC2L */
+#define WM8995_AIF1DAC1L_TO_DAC2L_WIDTH 1 /* AIF1DAC1L_TO_DAC2L */
+
+/*
+ * R1541 (0x605) - DAC2 Right Mixer Routing
+ */
+#define WM8995_ADCR_TO_DAC2R 0x0020 /* ADCR_TO_DAC2R */
+#define WM8995_ADCR_TO_DAC2R_MASK 0x0020 /* ADCR_TO_DAC2R */
+#define WM8995_ADCR_TO_DAC2R_SHIFT 5 /* ADCR_TO_DAC2R */
+#define WM8995_ADCR_TO_DAC2R_WIDTH 1 /* ADCR_TO_DAC2R */
+#define WM8995_ADCL_TO_DAC2R 0x0010 /* ADCL_TO_DAC2R */
+#define WM8995_ADCL_TO_DAC2R_MASK 0x0010 /* ADCL_TO_DAC2R */
+#define WM8995_ADCL_TO_DAC2R_SHIFT 4 /* ADCL_TO_DAC2R */
+#define WM8995_ADCL_TO_DAC2R_WIDTH 1 /* ADCL_TO_DAC2R */
+#define WM8995_AIF2DACR_TO_DAC2R 0x0004 /* AIF2DACR_TO_DAC2R */
+#define WM8995_AIF2DACR_TO_DAC2R_MASK 0x0004 /* AIF2DACR_TO_DAC2R */
+#define WM8995_AIF2DACR_TO_DAC2R_SHIFT 2 /* AIF2DACR_TO_DAC2R */
+#define WM8995_AIF2DACR_TO_DAC2R_WIDTH 1 /* AIF2DACR_TO_DAC2R */
+#define WM8995_AIF1DAC2R_TO_DAC2R 0x0002 /* AIF1DAC2R_TO_DAC2R */
+#define WM8995_AIF1DAC2R_TO_DAC2R_MASK 0x0002 /* AIF1DAC2R_TO_DAC2R */
+#define WM8995_AIF1DAC2R_TO_DAC2R_SHIFT 1 /* AIF1DAC2R_TO_DAC2R */
+#define WM8995_AIF1DAC2R_TO_DAC2R_WIDTH 1 /* AIF1DAC2R_TO_DAC2R */
+#define WM8995_AIF1DAC1R_TO_DAC2R 0x0001 /* AIF1DAC1R_TO_DAC2R */
+#define WM8995_AIF1DAC1R_TO_DAC2R_MASK 0x0001 /* AIF1DAC1R_TO_DAC2R */
+#define WM8995_AIF1DAC1R_TO_DAC2R_SHIFT 0 /* AIF1DAC1R_TO_DAC2R */
+#define WM8995_AIF1DAC1R_TO_DAC2R_WIDTH 1 /* AIF1DAC1R_TO_DAC2R */
+
+/*
+ * R1542 (0x606) - AIF1 ADC1 Left Mixer Routing
+ */
+#define WM8995_ADC1L_TO_AIF1ADC1L 0x0002 /* ADC1L_TO_AIF1ADC1L */
+#define WM8995_ADC1L_TO_AIF1ADC1L_MASK 0x0002 /* ADC1L_TO_AIF1ADC1L */
+#define WM8995_ADC1L_TO_AIF1ADC1L_SHIFT 1 /* ADC1L_TO_AIF1ADC1L */
+#define WM8995_ADC1L_TO_AIF1ADC1L_WIDTH 1 /* ADC1L_TO_AIF1ADC1L */
+#define WM8995_AIF2DACL_TO_AIF1ADC1L 0x0001 /* AIF2DACL_TO_AIF1ADC1L */
+#define WM8995_AIF2DACL_TO_AIF1ADC1L_MASK 0x0001 /* AIF2DACL_TO_AIF1ADC1L */
+#define WM8995_AIF2DACL_TO_AIF1ADC1L_SHIFT 0 /* AIF2DACL_TO_AIF1ADC1L */
+#define WM8995_AIF2DACL_TO_AIF1ADC1L_WIDTH 1 /* AIF2DACL_TO_AIF1ADC1L */
+
+/*
+ * R1543 (0x607) - AIF1 ADC1 Right Mixer Routing
+ */
+#define WM8995_ADC1R_TO_AIF1ADC1R 0x0002 /* ADC1R_TO_AIF1ADC1R */
+#define WM8995_ADC1R_TO_AIF1ADC1R_MASK 0x0002 /* ADC1R_TO_AIF1ADC1R */
+#define WM8995_ADC1R_TO_AIF1ADC1R_SHIFT 1 /* ADC1R_TO_AIF1ADC1R */
+#define WM8995_ADC1R_TO_AIF1ADC1R_WIDTH 1 /* ADC1R_TO_AIF1ADC1R */
+#define WM8995_AIF2DACR_TO_AIF1ADC1R 0x0001 /* AIF2DACR_TO_AIF1ADC1R */
+#define WM8995_AIF2DACR_TO_AIF1ADC1R_MASK 0x0001 /* AIF2DACR_TO_AIF1ADC1R */
+#define WM8995_AIF2DACR_TO_AIF1ADC1R_SHIFT 0 /* AIF2DACR_TO_AIF1ADC1R */
+#define WM8995_AIF2DACR_TO_AIF1ADC1R_WIDTH 1 /* AIF2DACR_TO_AIF1ADC1R */
+
+/*
+ * R1544 (0x608) - AIF1 ADC2 Left Mixer Routing
+ */
+#define WM8995_ADC2L_TO_AIF1ADC2L 0x0002 /* ADC2L_TO_AIF1ADC2L */
+#define WM8995_ADC2L_TO_AIF1ADC2L_MASK 0x0002 /* ADC2L_TO_AIF1ADC2L */
+#define WM8995_ADC2L_TO_AIF1ADC2L_SHIFT 1 /* ADC2L_TO_AIF1ADC2L */
+#define WM8995_ADC2L_TO_AIF1ADC2L_WIDTH 1 /* ADC2L_TO_AIF1ADC2L */
+#define WM8995_AIF2DACL_TO_AIF1ADC2L 0x0001 /* AIF2DACL_TO_AIF1ADC2L */
+#define WM8995_AIF2DACL_TO_AIF1ADC2L_MASK 0x0001 /* AIF2DACL_TO_AIF1ADC2L */
+#define WM8995_AIF2DACL_TO_AIF1ADC2L_SHIFT 0 /* AIF2DACL_TO_AIF1ADC2L */
+#define WM8995_AIF2DACL_TO_AIF1ADC2L_WIDTH 1 /* AIF2DACL_TO_AIF1ADC2L */
+
+/*
+ * R1545 (0x609) - AIF1 ADC2 Right mixer Routing
+ */
+#define WM8995_ADC2R_TO_AIF1ADC2R 0x0002 /* ADC2R_TO_AIF1ADC2R */
+#define WM8995_ADC2R_TO_AIF1ADC2R_MASK 0x0002 /* ADC2R_TO_AIF1ADC2R */
+#define WM8995_ADC2R_TO_AIF1ADC2R_SHIFT 1 /* ADC2R_TO_AIF1ADC2R */
+#define WM8995_ADC2R_TO_AIF1ADC2R_WIDTH 1 /* ADC2R_TO_AIF1ADC2R */
+#define WM8995_AIF2DACR_TO_AIF1ADC2R 0x0001 /* AIF2DACR_TO_AIF1ADC2R */
+#define WM8995_AIF2DACR_TO_AIF1ADC2R_MASK 0x0001 /* AIF2DACR_TO_AIF1ADC2R */
+#define WM8995_AIF2DACR_TO_AIF1ADC2R_SHIFT 0 /* AIF2DACR_TO_AIF1ADC2R */
+#define WM8995_AIF2DACR_TO_AIF1ADC2R_WIDTH 1 /* AIF2DACR_TO_AIF1ADC2R */
+
+/*
+ * R1552 (0x610) - DAC Softmute
+ */
+#define WM8995_DAC_SOFTMUTEMODE 0x0002 /* DAC_SOFTMUTEMODE */
+#define WM8995_DAC_SOFTMUTEMODE_MASK 0x0002 /* DAC_SOFTMUTEMODE */
+#define WM8995_DAC_SOFTMUTEMODE_SHIFT 1 /* DAC_SOFTMUTEMODE */
+#define WM8995_DAC_SOFTMUTEMODE_WIDTH 1 /* DAC_SOFTMUTEMODE */
+#define WM8995_DAC_MUTERATE 0x0001 /* DAC_MUTERATE */
+#define WM8995_DAC_MUTERATE_MASK 0x0001 /* DAC_MUTERATE */
+#define WM8995_DAC_MUTERATE_SHIFT 0 /* DAC_MUTERATE */
+#define WM8995_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
+
+/*
+ * R1568 (0x620) - Oversampling
+ */
+#define WM8995_ADC_OSR128 0x0002 /* ADC_OSR128 */
+#define WM8995_ADC_OSR128_MASK 0x0002 /* ADC_OSR128 */
+#define WM8995_ADC_OSR128_SHIFT 1 /* ADC_OSR128 */
+#define WM8995_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */
+#define WM8995_DAC_OSR128 0x0001 /* DAC_OSR128 */
+#define WM8995_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */
+#define WM8995_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */
+#define WM8995_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */
+
+/*
+ * R1569 (0x621) - Sidetone
+ */
+#define WM8995_ST_LPF 0x1000 /* ST_LPF */
+#define WM8995_ST_LPF_MASK 0x1000 /* ST_LPF */
+#define WM8995_ST_LPF_SHIFT 12 /* ST_LPF */
+#define WM8995_ST_LPF_WIDTH 1 /* ST_LPF */
+#define WM8995_ST_HPF_CUT_MASK 0x0380 /* ST_HPF_CUT - [9:7] */
+#define WM8995_ST_HPF_CUT_SHIFT 7 /* ST_HPF_CUT - [9:7] */
+#define WM8995_ST_HPF_CUT_WIDTH 3 /* ST_HPF_CUT - [9:7] */
+#define WM8995_ST_HPF 0x0040 /* ST_HPF */
+#define WM8995_ST_HPF_MASK 0x0040 /* ST_HPF */
+#define WM8995_ST_HPF_SHIFT 6 /* ST_HPF */
+#define WM8995_ST_HPF_WIDTH 1 /* ST_HPF */
+#define WM8995_STR_SEL 0x0002 /* STR_SEL */
+#define WM8995_STR_SEL_MASK 0x0002 /* STR_SEL */
+#define WM8995_STR_SEL_SHIFT 1 /* STR_SEL */
+#define WM8995_STR_SEL_WIDTH 1 /* STR_SEL */
+#define WM8995_STL_SEL 0x0001 /* STL_SEL */
+#define WM8995_STL_SEL_MASK 0x0001 /* STL_SEL */
+#define WM8995_STL_SEL_SHIFT 0 /* STL_SEL */
+#define WM8995_STL_SEL_WIDTH 1 /* STL_SEL */
+
+/*
+ * R1792 (0x700) - GPIO 1
+ */
+#define WM8995_GP1_DIR 0x8000 /* GP1_DIR */
+#define WM8995_GP1_DIR_MASK 0x8000 /* GP1_DIR */
+#define WM8995_GP1_DIR_SHIFT 15 /* GP1_DIR */
+#define WM8995_GP1_DIR_WIDTH 1 /* GP1_DIR */
+#define WM8995_GP1_PU 0x4000 /* GP1_PU */
+#define WM8995_GP1_PU_MASK 0x4000 /* GP1_PU */
+#define WM8995_GP1_PU_SHIFT 14 /* GP1_PU */
+#define WM8995_GP1_PU_WIDTH 1 /* GP1_PU */
+#define WM8995_GP1_PD 0x2000 /* GP1_PD */
+#define WM8995_GP1_PD_MASK 0x2000 /* GP1_PD */
+#define WM8995_GP1_PD_SHIFT 13 /* GP1_PD */
+#define WM8995_GP1_PD_WIDTH 1 /* GP1_PD */
+#define WM8995_GP1_POL 0x0400 /* GP1_POL */
+#define WM8995_GP1_POL_MASK 0x0400 /* GP1_POL */
+#define WM8995_GP1_POL_SHIFT 10 /* GP1_POL */
+#define WM8995_GP1_POL_WIDTH 1 /* GP1_POL */
+#define WM8995_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */
+#define WM8995_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */
+#define WM8995_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */
+#define WM8995_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */
+#define WM8995_GP1_DB 0x0100 /* GP1_DB */
+#define WM8995_GP1_DB_MASK 0x0100 /* GP1_DB */
+#define WM8995_GP1_DB_SHIFT 8 /* GP1_DB */
+#define WM8995_GP1_DB_WIDTH 1 /* GP1_DB */
+#define WM8995_GP1_LVL 0x0040 /* GP1_LVL */
+#define WM8995_GP1_LVL_MASK 0x0040 /* GP1_LVL */
+#define WM8995_GP1_LVL_SHIFT 6 /* GP1_LVL */
+#define WM8995_GP1_LVL_WIDTH 1 /* GP1_LVL */
+#define WM8995_GP1_FN_MASK 0x001F /* GP1_FN - [4:0] */
+#define WM8995_GP1_FN_SHIFT 0 /* GP1_FN - [4:0] */
+#define WM8995_GP1_FN_WIDTH 5 /* GP1_FN - [4:0] */
+
+/*
+ * R1793 (0x701) - GPIO 2
+ */
+#define WM8995_GP2_DIR 0x8000 /* GP2_DIR */
+#define WM8995_GP2_DIR_MASK 0x8000 /* GP2_DIR */
+#define WM8995_GP2_DIR_SHIFT 15 /* GP2_DIR */
+#define WM8995_GP2_DIR_WIDTH 1 /* GP2_DIR */
+#define WM8995_GP2_PU 0x4000 /* GP2_PU */
+#define WM8995_GP2_PU_MASK 0x4000 /* GP2_PU */
+#define WM8995_GP2_PU_SHIFT 14 /* GP2_PU */
+#define WM8995_GP2_PU_WIDTH 1 /* GP2_PU */
+#define WM8995_GP2_PD 0x2000 /* GP2_PD */
+#define WM8995_GP2_PD_MASK 0x2000 /* GP2_PD */
+#define WM8995_GP2_PD_SHIFT 13 /* GP2_PD */
+#define WM8995_GP2_PD_WIDTH 1 /* GP2_PD */
+#define WM8995_GP2_POL 0x0400 /* GP2_POL */
+#define WM8995_GP2_POL_MASK 0x0400 /* GP2_POL */
+#define WM8995_GP2_POL_SHIFT 10 /* GP2_POL */
+#define WM8995_GP2_POL_WIDTH 1 /* GP2_POL */
+#define WM8995_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */
+#define WM8995_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */
+#define WM8995_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */
+#define WM8995_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */
+#define WM8995_GP2_DB 0x0100 /* GP2_DB */
+#define WM8995_GP2_DB_MASK 0x0100 /* GP2_DB */
+#define WM8995_GP2_DB_SHIFT 8 /* GP2_DB */
+#define WM8995_GP2_DB_WIDTH 1 /* GP2_DB */
+#define WM8995_GP2_LVL 0x0040 /* GP2_LVL */
+#define WM8995_GP2_LVL_MASK 0x0040 /* GP2_LVL */
+#define WM8995_GP2_LVL_SHIFT 6 /* GP2_LVL */
+#define WM8995_GP2_LVL_WIDTH 1 /* GP2_LVL */
+#define WM8995_GP2_FN_MASK 0x001F /* GP2_FN - [4:0] */
+#define WM8995_GP2_FN_SHIFT 0 /* GP2_FN - [4:0] */
+#define WM8995_GP2_FN_WIDTH 5 /* GP2_FN - [4:0] */
+
+/*
+ * R1794 (0x702) - GPIO 3
+ */
+#define WM8995_GP3_DIR 0x8000 /* GP3_DIR */
+#define WM8995_GP3_DIR_MASK 0x8000 /* GP3_DIR */
+#define WM8995_GP3_DIR_SHIFT 15 /* GP3_DIR */
+#define WM8995_GP3_DIR_WIDTH 1 /* GP3_DIR */
+#define WM8995_GP3_PU 0x4000 /* GP3_PU */
+#define WM8995_GP3_PU_MASK 0x4000 /* GP3_PU */
+#define WM8995_GP3_PU_SHIFT 14 /* GP3_PU */
+#define WM8995_GP3_PU_WIDTH 1 /* GP3_PU */
+#define WM8995_GP3_PD 0x2000 /* GP3_PD */
+#define WM8995_GP3_PD_MASK 0x2000 /* GP3_PD */
+#define WM8995_GP3_PD_SHIFT 13 /* GP3_PD */
+#define WM8995_GP3_PD_WIDTH 1 /* GP3_PD */
+#define WM8995_GP3_POL 0x0400 /* GP3_POL */
+#define WM8995_GP3_POL_MASK 0x0400 /* GP3_POL */
+#define WM8995_GP3_POL_SHIFT 10 /* GP3_POL */
+#define WM8995_GP3_POL_WIDTH 1 /* GP3_POL */
+#define WM8995_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */
+#define WM8995_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */
+#define WM8995_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */
+#define WM8995_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */
+#define WM8995_GP3_DB 0x0100 /* GP3_DB */
+#define WM8995_GP3_DB_MASK 0x0100 /* GP3_DB */
+#define WM8995_GP3_DB_SHIFT 8 /* GP3_DB */
+#define WM8995_GP3_DB_WIDTH 1 /* GP3_DB */
+#define WM8995_GP3_LVL 0x0040 /* GP3_LVL */
+#define WM8995_GP3_LVL_MASK 0x0040 /* GP3_LVL */
+#define WM8995_GP3_LVL_SHIFT 6 /* GP3_LVL */
+#define WM8995_GP3_LVL_WIDTH 1 /* GP3_LVL */
+#define WM8995_GP3_FN_MASK 0x001F /* GP3_FN - [4:0] */
+#define WM8995_GP3_FN_SHIFT 0 /* GP3_FN - [4:0] */
+#define WM8995_GP3_FN_WIDTH 5 /* GP3_FN - [4:0] */
+
+/*
+ * R1795 (0x703) - GPIO 4
+ */
+#define WM8995_GP4_DIR 0x8000 /* GP4_DIR */
+#define WM8995_GP4_DIR_MASK 0x8000 /* GP4_DIR */
+#define WM8995_GP4_DIR_SHIFT 15 /* GP4_DIR */
+#define WM8995_GP4_DIR_WIDTH 1 /* GP4_DIR */
+#define WM8995_GP4_PU 0x4000 /* GP4_PU */
+#define WM8995_GP4_PU_MASK 0x4000 /* GP4_PU */
+#define WM8995_GP4_PU_SHIFT 14 /* GP4_PU */
+#define WM8995_GP4_PU_WIDTH 1 /* GP4_PU */
+#define WM8995_GP4_PD 0x2000 /* GP4_PD */
+#define WM8995_GP4_PD_MASK 0x2000 /* GP4_PD */
+#define WM8995_GP4_PD_SHIFT 13 /* GP4_PD */
+#define WM8995_GP4_PD_WIDTH 1 /* GP4_PD */
+#define WM8995_GP4_POL 0x0400 /* GP4_POL */
+#define WM8995_GP4_POL_MASK 0x0400 /* GP4_POL */
+#define WM8995_GP4_POL_SHIFT 10 /* GP4_POL */
+#define WM8995_GP4_POL_WIDTH 1 /* GP4_POL */
+#define WM8995_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */
+#define WM8995_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */
+#define WM8995_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */
+#define WM8995_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */
+#define WM8995_GP4_DB 0x0100 /* GP4_DB */
+#define WM8995_GP4_DB_MASK 0x0100 /* GP4_DB */
+#define WM8995_GP4_DB_SHIFT 8 /* GP4_DB */
+#define WM8995_GP4_DB_WIDTH 1 /* GP4_DB */
+#define WM8995_GP4_LVL 0x0040 /* GP4_LVL */
+#define WM8995_GP4_LVL_MASK 0x0040 /* GP4_LVL */
+#define WM8995_GP4_LVL_SHIFT 6 /* GP4_LVL */
+#define WM8995_GP4_LVL_WIDTH 1 /* GP4_LVL */
+#define WM8995_GP4_FN_MASK 0x001F /* GP4_FN - [4:0] */
+#define WM8995_GP4_FN_SHIFT 0 /* GP4_FN - [4:0] */
+#define WM8995_GP4_FN_WIDTH 5 /* GP4_FN - [4:0] */
+
+/*
+ * R1796 (0x704) - GPIO 5
+ */
+#define WM8995_GP5_DIR 0x8000 /* GP5_DIR */
+#define WM8995_GP5_DIR_MASK 0x8000 /* GP5_DIR */
+#define WM8995_GP5_DIR_SHIFT 15 /* GP5_DIR */
+#define WM8995_GP5_DIR_WIDTH 1 /* GP5_DIR */
+#define WM8995_GP5_PU 0x4000 /* GP5_PU */
+#define WM8995_GP5_PU_MASK 0x4000 /* GP5_PU */
+#define WM8995_GP5_PU_SHIFT 14 /* GP5_PU */
+#define WM8995_GP5_PU_WIDTH 1 /* GP5_PU */
+#define WM8995_GP5_PD 0x2000 /* GP5_PD */
+#define WM8995_GP5_PD_MASK 0x2000 /* GP5_PD */
+#define WM8995_GP5_PD_SHIFT 13 /* GP5_PD */
+#define WM8995_GP5_PD_WIDTH 1 /* GP5_PD */
+#define WM8995_GP5_POL 0x0400 /* GP5_POL */
+#define WM8995_GP5_POL_MASK 0x0400 /* GP5_POL */
+#define WM8995_GP5_POL_SHIFT 10 /* GP5_POL */
+#define WM8995_GP5_POL_WIDTH 1 /* GP5_POL */
+#define WM8995_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */
+#define WM8995_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */
+#define WM8995_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */
+#define WM8995_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */
+#define WM8995_GP5_DB 0x0100 /* GP5_DB */
+#define WM8995_GP5_DB_MASK 0x0100 /* GP5_DB */
+#define WM8995_GP5_DB_SHIFT 8 /* GP5_DB */
+#define WM8995_GP5_DB_WIDTH 1 /* GP5_DB */
+#define WM8995_GP5_LVL 0x0040 /* GP5_LVL */
+#define WM8995_GP5_LVL_MASK 0x0040 /* GP5_LVL */
+#define WM8995_GP5_LVL_SHIFT 6 /* GP5_LVL */
+#define WM8995_GP5_LVL_WIDTH 1 /* GP5_LVL */
+#define WM8995_GP5_FN_MASK 0x001F /* GP5_FN - [4:0] */
+#define WM8995_GP5_FN_SHIFT 0 /* GP5_FN - [4:0] */
+#define WM8995_GP5_FN_WIDTH 5 /* GP5_FN - [4:0] */
+
+/*
+ * R1797 (0x705) - GPIO 6
+ */
+#define WM8995_GP6_DIR 0x8000 /* GP6_DIR */
+#define WM8995_GP6_DIR_MASK 0x8000 /* GP6_DIR */
+#define WM8995_GP6_DIR_SHIFT 15 /* GP6_DIR */
+#define WM8995_GP6_DIR_WIDTH 1 /* GP6_DIR */
+#define WM8995_GP6_PU 0x4000 /* GP6_PU */
+#define WM8995_GP6_PU_MASK 0x4000 /* GP6_PU */
+#define WM8995_GP6_PU_SHIFT 14 /* GP6_PU */
+#define WM8995_GP6_PU_WIDTH 1 /* GP6_PU */
+#define WM8995_GP6_PD 0x2000 /* GP6_PD */
+#define WM8995_GP6_PD_MASK 0x2000 /* GP6_PD */
+#define WM8995_GP6_PD_SHIFT 13 /* GP6_PD */
+#define WM8995_GP6_PD_WIDTH 1 /* GP6_PD */
+#define WM8995_GP6_POL 0x0400 /* GP6_POL */
+#define WM8995_GP6_POL_MASK 0x0400 /* GP6_POL */
+#define WM8995_GP6_POL_SHIFT 10 /* GP6_POL */
+#define WM8995_GP6_POL_WIDTH 1 /* GP6_POL */
+#define WM8995_GP6_OP_CFG 0x0200 /* GP6_OP_CFG */
+#define WM8995_GP6_OP_CFG_MASK 0x0200 /* GP6_OP_CFG */
+#define WM8995_GP6_OP_CFG_SHIFT 9 /* GP6_OP_CFG */
+#define WM8995_GP6_OP_CFG_WIDTH 1 /* GP6_OP_CFG */
+#define WM8995_GP6_DB 0x0100 /* GP6_DB */
+#define WM8995_GP6_DB_MASK 0x0100 /* GP6_DB */
+#define WM8995_GP6_DB_SHIFT 8 /* GP6_DB */
+#define WM8995_GP6_DB_WIDTH 1 /* GP6_DB */
+#define WM8995_GP6_LVL 0x0040 /* GP6_LVL */
+#define WM8995_GP6_LVL_MASK 0x0040 /* GP6_LVL */
+#define WM8995_GP6_LVL_SHIFT 6 /* GP6_LVL */
+#define WM8995_GP6_LVL_WIDTH 1 /* GP6_LVL */
+#define WM8995_GP6_FN_MASK 0x001F /* GP6_FN - [4:0] */
+#define WM8995_GP6_FN_SHIFT 0 /* GP6_FN - [4:0] */
+#define WM8995_GP6_FN_WIDTH 5 /* GP6_FN - [4:0] */
+
+/*
+ * R1798 (0x706) - GPIO 7
+ */
+#define WM8995_GP7_DIR 0x8000 /* GP7_DIR */
+#define WM8995_GP7_DIR_MASK 0x8000 /* GP7_DIR */
+#define WM8995_GP7_DIR_SHIFT 15 /* GP7_DIR */
+#define WM8995_GP7_DIR_WIDTH 1 /* GP7_DIR */
+#define WM8995_GP7_PU 0x4000 /* GP7_PU */
+#define WM8995_GP7_PU_MASK 0x4000 /* GP7_PU */
+#define WM8995_GP7_PU_SHIFT 14 /* GP7_PU */
+#define WM8995_GP7_PU_WIDTH 1 /* GP7_PU */
+#define WM8995_GP7_PD 0x2000 /* GP7_PD */
+#define WM8995_GP7_PD_MASK 0x2000 /* GP7_PD */
+#define WM8995_GP7_PD_SHIFT 13 /* GP7_PD */
+#define WM8995_GP7_PD_WIDTH 1 /* GP7_PD */
+#define WM8995_GP7_POL 0x0400 /* GP7_POL */
+#define WM8995_GP7_POL_MASK 0x0400 /* GP7_POL */
+#define WM8995_GP7_POL_SHIFT 10 /* GP7_POL */
+#define WM8995_GP7_POL_WIDTH 1 /* GP7_POL */
+#define WM8995_GP7_OP_CFG 0x0200 /* GP7_OP_CFG */
+#define WM8995_GP7_OP_CFG_MASK 0x0200 /* GP7_OP_CFG */
+#define WM8995_GP7_OP_CFG_SHIFT 9 /* GP7_OP_CFG */
+#define WM8995_GP7_OP_CFG_WIDTH 1 /* GP7_OP_CFG */
+#define WM8995_GP7_DB 0x0100 /* GP7_DB */
+#define WM8995_GP7_DB_MASK 0x0100 /* GP7_DB */
+#define WM8995_GP7_DB_SHIFT 8 /* GP7_DB */
+#define WM8995_GP7_DB_WIDTH 1 /* GP7_DB */
+#define WM8995_GP7_LVL 0x0040 /* GP7_LVL */
+#define WM8995_GP7_LVL_MASK 0x0040 /* GP7_LVL */
+#define WM8995_GP7_LVL_SHIFT 6 /* GP7_LVL */
+#define WM8995_GP7_LVL_WIDTH 1 /* GP7_LVL */
+#define WM8995_GP7_FN_MASK 0x001F /* GP7_FN - [4:0] */
+#define WM8995_GP7_FN_SHIFT 0 /* GP7_FN - [4:0] */
+#define WM8995_GP7_FN_WIDTH 5 /* GP7_FN - [4:0] */
+
+/*
+ * R1799 (0x707) - GPIO 8
+ */
+#define WM8995_GP8_DIR 0x8000 /* GP8_DIR */
+#define WM8995_GP8_DIR_MASK 0x8000 /* GP8_DIR */
+#define WM8995_GP8_DIR_SHIFT 15 /* GP8_DIR */
+#define WM8995_GP8_DIR_WIDTH 1 /* GP8_DIR */
+#define WM8995_GP8_PU 0x4000 /* GP8_PU */
+#define WM8995_GP8_PU_MASK 0x4000 /* GP8_PU */
+#define WM8995_GP8_PU_SHIFT 14 /* GP8_PU */
+#define WM8995_GP8_PU_WIDTH 1 /* GP8_PU */
+#define WM8995_GP8_PD 0x2000 /* GP8_PD */
+#define WM8995_GP8_PD_MASK 0x2000 /* GP8_PD */
+#define WM8995_GP8_PD_SHIFT 13 /* GP8_PD */
+#define WM8995_GP8_PD_WIDTH 1 /* GP8_PD */
+#define WM8995_GP8_POL 0x0400 /* GP8_POL */
+#define WM8995_GP8_POL_MASK 0x0400 /* GP8_POL */
+#define WM8995_GP8_POL_SHIFT 10 /* GP8_POL */
+#define WM8995_GP8_POL_WIDTH 1 /* GP8_POL */
+#define WM8995_GP8_OP_CFG 0x0200 /* GP8_OP_CFG */
+#define WM8995_GP8_OP_CFG_MASK 0x0200 /* GP8_OP_CFG */
+#define WM8995_GP8_OP_CFG_SHIFT 9 /* GP8_OP_CFG */
+#define WM8995_GP8_OP_CFG_WIDTH 1 /* GP8_OP_CFG */
+#define WM8995_GP8_DB 0x0100 /* GP8_DB */
+#define WM8995_GP8_DB_MASK 0x0100 /* GP8_DB */
+#define WM8995_GP8_DB_SHIFT 8 /* GP8_DB */
+#define WM8995_GP8_DB_WIDTH 1 /* GP8_DB */
+#define WM8995_GP8_LVL 0x0040 /* GP8_LVL */
+#define WM8995_GP8_LVL_MASK 0x0040 /* GP8_LVL */
+#define WM8995_GP8_LVL_SHIFT 6 /* GP8_LVL */
+#define WM8995_GP8_LVL_WIDTH 1 /* GP8_LVL */
+#define WM8995_GP8_FN_MASK 0x001F /* GP8_FN - [4:0] */
+#define WM8995_GP8_FN_SHIFT 0 /* GP8_FN - [4:0] */
+#define WM8995_GP8_FN_WIDTH 5 /* GP8_FN - [4:0] */
+
+/*
+ * R1800 (0x708) - GPIO 9
+ */
+#define WM8995_GP9_DIR 0x8000 /* GP9_DIR */
+#define WM8995_GP9_DIR_MASK 0x8000 /* GP9_DIR */
+#define WM8995_GP9_DIR_SHIFT 15 /* GP9_DIR */
+#define WM8995_GP9_DIR_WIDTH 1 /* GP9_DIR */
+#define WM8995_GP9_PU 0x4000 /* GP9_PU */
+#define WM8995_GP9_PU_MASK 0x4000 /* GP9_PU */
+#define WM8995_GP9_PU_SHIFT 14 /* GP9_PU */
+#define WM8995_GP9_PU_WIDTH 1 /* GP9_PU */
+#define WM8995_GP9_PD 0x2000 /* GP9_PD */
+#define WM8995_GP9_PD_MASK 0x2000 /* GP9_PD */
+#define WM8995_GP9_PD_SHIFT 13 /* GP9_PD */
+#define WM8995_GP9_PD_WIDTH 1 /* GP9_PD */
+#define WM8995_GP9_POL 0x0400 /* GP9_POL */
+#define WM8995_GP9_POL_MASK 0x0400 /* GP9_POL */
+#define WM8995_GP9_POL_SHIFT 10 /* GP9_POL */
+#define WM8995_GP9_POL_WIDTH 1 /* GP9_POL */
+#define WM8995_GP9_OP_CFG 0x0200 /* GP9_OP_CFG */
+#define WM8995_GP9_OP_CFG_MASK 0x0200 /* GP9_OP_CFG */
+#define WM8995_GP9_OP_CFG_SHIFT 9 /* GP9_OP_CFG */
+#define WM8995_GP9_OP_CFG_WIDTH 1 /* GP9_OP_CFG */
+#define WM8995_GP9_DB 0x0100 /* GP9_DB */
+#define WM8995_GP9_DB_MASK 0x0100 /* GP9_DB */
+#define WM8995_GP9_DB_SHIFT 8 /* GP9_DB */
+#define WM8995_GP9_DB_WIDTH 1 /* GP9_DB */
+#define WM8995_GP9_LVL 0x0040 /* GP9_LVL */
+#define WM8995_GP9_LVL_MASK 0x0040 /* GP9_LVL */
+#define WM8995_GP9_LVL_SHIFT 6 /* GP9_LVL */
+#define WM8995_GP9_LVL_WIDTH 1 /* GP9_LVL */
+#define WM8995_GP9_FN_MASK 0x001F /* GP9_FN - [4:0] */
+#define WM8995_GP9_FN_SHIFT 0 /* GP9_FN - [4:0] */
+#define WM8995_GP9_FN_WIDTH 5 /* GP9_FN - [4:0] */
+
+/*
+ * R1801 (0x709) - GPIO 10
+ */
+#define WM8995_GP10_DIR 0x8000 /* GP10_DIR */
+#define WM8995_GP10_DIR_MASK 0x8000 /* GP10_DIR */
+#define WM8995_GP10_DIR_SHIFT 15 /* GP10_DIR */
+#define WM8995_GP10_DIR_WIDTH 1 /* GP10_DIR */
+#define WM8995_GP10_PU 0x4000 /* GP10_PU */
+#define WM8995_GP10_PU_MASK 0x4000 /* GP10_PU */
+#define WM8995_GP10_PU_SHIFT 14 /* GP10_PU */
+#define WM8995_GP10_PU_WIDTH 1 /* GP10_PU */
+#define WM8995_GP10_PD 0x2000 /* GP10_PD */
+#define WM8995_GP10_PD_MASK 0x2000 /* GP10_PD */
+#define WM8995_GP10_PD_SHIFT 13 /* GP10_PD */
+#define WM8995_GP10_PD_WIDTH 1 /* GP10_PD */
+#define WM8995_GP10_POL 0x0400 /* GP10_POL */
+#define WM8995_GP10_POL_MASK 0x0400 /* GP10_POL */
+#define WM8995_GP10_POL_SHIFT 10 /* GP10_POL */
+#define WM8995_GP10_POL_WIDTH 1 /* GP10_POL */
+#define WM8995_GP10_OP_CFG 0x0200 /* GP10_OP_CFG */
+#define WM8995_GP10_OP_CFG_MASK 0x0200 /* GP10_OP_CFG */
+#define WM8995_GP10_OP_CFG_SHIFT 9 /* GP10_OP_CFG */
+#define WM8995_GP10_OP_CFG_WIDTH 1 /* GP10_OP_CFG */
+#define WM8995_GP10_DB 0x0100 /* GP10_DB */
+#define WM8995_GP10_DB_MASK 0x0100 /* GP10_DB */
+#define WM8995_GP10_DB_SHIFT 8 /* GP10_DB */
+#define WM8995_GP10_DB_WIDTH 1 /* GP10_DB */
+#define WM8995_GP10_LVL 0x0040 /* GP10_LVL */
+#define WM8995_GP10_LVL_MASK 0x0040 /* GP10_LVL */
+#define WM8995_GP10_LVL_SHIFT 6 /* GP10_LVL */
+#define WM8995_GP10_LVL_WIDTH 1 /* GP10_LVL */
+#define WM8995_GP10_FN_MASK 0x001F /* GP10_FN - [4:0] */
+#define WM8995_GP10_FN_SHIFT 0 /* GP10_FN - [4:0] */
+#define WM8995_GP10_FN_WIDTH 5 /* GP10_FN - [4:0] */
+
+/*
+ * R1802 (0x70A) - GPIO 11
+ */
+#define WM8995_GP11_DIR 0x8000 /* GP11_DIR */
+#define WM8995_GP11_DIR_MASK 0x8000 /* GP11_DIR */
+#define WM8995_GP11_DIR_SHIFT 15 /* GP11_DIR */
+#define WM8995_GP11_DIR_WIDTH 1 /* GP11_DIR */
+#define WM8995_GP11_PU 0x4000 /* GP11_PU */
+#define WM8995_GP11_PU_MASK 0x4000 /* GP11_PU */
+#define WM8995_GP11_PU_SHIFT 14 /* GP11_PU */
+#define WM8995_GP11_PU_WIDTH 1 /* GP11_PU */
+#define WM8995_GP11_PD 0x2000 /* GP11_PD */
+#define WM8995_GP11_PD_MASK 0x2000 /* GP11_PD */
+#define WM8995_GP11_PD_SHIFT 13 /* GP11_PD */
+#define WM8995_GP11_PD_WIDTH 1 /* GP11_PD */
+#define WM8995_GP11_POL 0x0400 /* GP11_POL */
+#define WM8995_GP11_POL_MASK 0x0400 /* GP11_POL */
+#define WM8995_GP11_POL_SHIFT 10 /* GP11_POL */
+#define WM8995_GP11_POL_WIDTH 1 /* GP11_POL */
+#define WM8995_GP11_OP_CFG 0x0200 /* GP11_OP_CFG */
+#define WM8995_GP11_OP_CFG_MASK 0x0200 /* GP11_OP_CFG */
+#define WM8995_GP11_OP_CFG_SHIFT 9 /* GP11_OP_CFG */
+#define WM8995_GP11_OP_CFG_WIDTH 1 /* GP11_OP_CFG */
+#define WM8995_GP11_DB 0x0100 /* GP11_DB */
+#define WM8995_GP11_DB_MASK 0x0100 /* GP11_DB */
+#define WM8995_GP11_DB_SHIFT 8 /* GP11_DB */
+#define WM8995_GP11_DB_WIDTH 1 /* GP11_DB */
+#define WM8995_GP11_LVL 0x0040 /* GP11_LVL */
+#define WM8995_GP11_LVL_MASK 0x0040 /* GP11_LVL */
+#define WM8995_GP11_LVL_SHIFT 6 /* GP11_LVL */
+#define WM8995_GP11_LVL_WIDTH 1 /* GP11_LVL */
+#define WM8995_GP11_FN_MASK 0x001F /* GP11_FN - [4:0] */
+#define WM8995_GP11_FN_SHIFT 0 /* GP11_FN - [4:0] */
+#define WM8995_GP11_FN_WIDTH 5 /* GP11_FN - [4:0] */
+
+/*
+ * R1803 (0x70B) - GPIO 12
+ */
+#define WM8995_GP12_DIR 0x8000 /* GP12_DIR */
+#define WM8995_GP12_DIR_MASK 0x8000 /* GP12_DIR */
+#define WM8995_GP12_DIR_SHIFT 15 /* GP12_DIR */
+#define WM8995_GP12_DIR_WIDTH 1 /* GP12_DIR */
+#define WM8995_GP12_PU 0x4000 /* GP12_PU */
+#define WM8995_GP12_PU_MASK 0x4000 /* GP12_PU */
+#define WM8995_GP12_PU_SHIFT 14 /* GP12_PU */
+#define WM8995_GP12_PU_WIDTH 1 /* GP12_PU */
+#define WM8995_GP12_PD 0x2000 /* GP12_PD */
+#define WM8995_GP12_PD_MASK 0x2000 /* GP12_PD */
+#define WM8995_GP12_PD_SHIFT 13 /* GP12_PD */
+#define WM8995_GP12_PD_WIDTH 1 /* GP12_PD */
+#define WM8995_GP12_POL 0x0400 /* GP12_POL */
+#define WM8995_GP12_POL_MASK 0x0400 /* GP12_POL */
+#define WM8995_GP12_POL_SHIFT 10 /* GP12_POL */
+#define WM8995_GP12_POL_WIDTH 1 /* GP12_POL */
+#define WM8995_GP12_OP_CFG 0x0200 /* GP12_OP_CFG */
+#define WM8995_GP12_OP_CFG_MASK 0x0200 /* GP12_OP_CFG */
+#define WM8995_GP12_OP_CFG_SHIFT 9 /* GP12_OP_CFG */
+#define WM8995_GP12_OP_CFG_WIDTH 1 /* GP12_OP_CFG */
+#define WM8995_GP12_DB 0x0100 /* GP12_DB */
+#define WM8995_GP12_DB_MASK 0x0100 /* GP12_DB */
+#define WM8995_GP12_DB_SHIFT 8 /* GP12_DB */
+#define WM8995_GP12_DB_WIDTH 1 /* GP12_DB */
+#define WM8995_GP12_LVL 0x0040 /* GP12_LVL */
+#define WM8995_GP12_LVL_MASK 0x0040 /* GP12_LVL */
+#define WM8995_GP12_LVL_SHIFT 6 /* GP12_LVL */
+#define WM8995_GP12_LVL_WIDTH 1 /* GP12_LVL */
+#define WM8995_GP12_FN_MASK 0x001F /* GP12_FN - [4:0] */
+#define WM8995_GP12_FN_SHIFT 0 /* GP12_FN - [4:0] */
+#define WM8995_GP12_FN_WIDTH 5 /* GP12_FN - [4:0] */
+
+/*
+ * R1804 (0x70C) - GPIO 13
+ */
+#define WM8995_GP13_DIR 0x8000 /* GP13_DIR */
+#define WM8995_GP13_DIR_MASK 0x8000 /* GP13_DIR */
+#define WM8995_GP13_DIR_SHIFT 15 /* GP13_DIR */
+#define WM8995_GP13_DIR_WIDTH 1 /* GP13_DIR */
+#define WM8995_GP13_PU 0x4000 /* GP13_PU */
+#define WM8995_GP13_PU_MASK 0x4000 /* GP13_PU */
+#define WM8995_GP13_PU_SHIFT 14 /* GP13_PU */
+#define WM8995_GP13_PU_WIDTH 1 /* GP13_PU */
+#define WM8995_GP13_PD 0x2000 /* GP13_PD */
+#define WM8995_GP13_PD_MASK 0x2000 /* GP13_PD */
+#define WM8995_GP13_PD_SHIFT 13 /* GP13_PD */
+#define WM8995_GP13_PD_WIDTH 1 /* GP13_PD */
+#define WM8995_GP13_POL 0x0400 /* GP13_POL */
+#define WM8995_GP13_POL_MASK 0x0400 /* GP13_POL */
+#define WM8995_GP13_POL_SHIFT 10 /* GP13_POL */
+#define WM8995_GP13_POL_WIDTH 1 /* GP13_POL */
+#define WM8995_GP13_OP_CFG 0x0200 /* GP13_OP_CFG */
+#define WM8995_GP13_OP_CFG_MASK 0x0200 /* GP13_OP_CFG */
+#define WM8995_GP13_OP_CFG_SHIFT 9 /* GP13_OP_CFG */
+#define WM8995_GP13_OP_CFG_WIDTH 1 /* GP13_OP_CFG */
+#define WM8995_GP13_DB 0x0100 /* GP13_DB */
+#define WM8995_GP13_DB_MASK 0x0100 /* GP13_DB */
+#define WM8995_GP13_DB_SHIFT 8 /* GP13_DB */
+#define WM8995_GP13_DB_WIDTH 1 /* GP13_DB */
+#define WM8995_GP13_LVL 0x0040 /* GP13_LVL */
+#define WM8995_GP13_LVL_MASK 0x0040 /* GP13_LVL */
+#define WM8995_GP13_LVL_SHIFT 6 /* GP13_LVL */
+#define WM8995_GP13_LVL_WIDTH 1 /* GP13_LVL */
+#define WM8995_GP13_FN_MASK 0x001F /* GP13_FN - [4:0] */
+#define WM8995_GP13_FN_SHIFT 0 /* GP13_FN - [4:0] */
+#define WM8995_GP13_FN_WIDTH 5 /* GP13_FN - [4:0] */
+
+/*
+ * R1805 (0x70D) - GPIO 14
+ */
+#define WM8995_GP14_DIR 0x8000 /* GP14_DIR */
+#define WM8995_GP14_DIR_MASK 0x8000 /* GP14_DIR */
+#define WM8995_GP14_DIR_SHIFT 15 /* GP14_DIR */
+#define WM8995_GP14_DIR_WIDTH 1 /* GP14_DIR */
+#define WM8995_GP14_PU 0x4000 /* GP14_PU */
+#define WM8995_GP14_PU_MASK 0x4000 /* GP14_PU */
+#define WM8995_GP14_PU_SHIFT 14 /* GP14_PU */
+#define WM8995_GP14_PU_WIDTH 1 /* GP14_PU */
+#define WM8995_GP14_PD 0x2000 /* GP14_PD */
+#define WM8995_GP14_PD_MASK 0x2000 /* GP14_PD */
+#define WM8995_GP14_PD_SHIFT 13 /* GP14_PD */
+#define WM8995_GP14_PD_WIDTH 1 /* GP14_PD */
+#define WM8995_GP14_POL 0x0400 /* GP14_POL */
+#define WM8995_GP14_POL_MASK 0x0400 /* GP14_POL */
+#define WM8995_GP14_POL_SHIFT 10 /* GP14_POL */
+#define WM8995_GP14_POL_WIDTH 1 /* GP14_POL */
+#define WM8995_GP14_OP_CFG 0x0200 /* GP14_OP_CFG */
+#define WM8995_GP14_OP_CFG_MASK 0x0200 /* GP14_OP_CFG */
+#define WM8995_GP14_OP_CFG_SHIFT 9 /* GP14_OP_CFG */
+#define WM8995_GP14_OP_CFG_WIDTH 1 /* GP14_OP_CFG */
+#define WM8995_GP14_DB 0x0100 /* GP14_DB */
+#define WM8995_GP14_DB_MASK 0x0100 /* GP14_DB */
+#define WM8995_GP14_DB_SHIFT 8 /* GP14_DB */
+#define WM8995_GP14_DB_WIDTH 1 /* GP14_DB */
+#define WM8995_GP14_LVL 0x0040 /* GP14_LVL */
+#define WM8995_GP14_LVL_MASK 0x0040 /* GP14_LVL */
+#define WM8995_GP14_LVL_SHIFT 6 /* GP14_LVL */
+#define WM8995_GP14_LVL_WIDTH 1 /* GP14_LVL */
+#define WM8995_GP14_FN_MASK 0x001F /* GP14_FN - [4:0] */
+#define WM8995_GP14_FN_SHIFT 0 /* GP14_FN - [4:0] */
+#define WM8995_GP14_FN_WIDTH 5 /* GP14_FN - [4:0] */
+
+/*
+ * R1824 (0x720) - Pull Control (1)
+ */
+#define WM8995_DMICDAT3_PD 0x4000 /* DMICDAT3_PD */
+#define WM8995_DMICDAT3_PD_MASK 0x4000 /* DMICDAT3_PD */
+#define WM8995_DMICDAT3_PD_SHIFT 14 /* DMICDAT3_PD */
+#define WM8995_DMICDAT3_PD_WIDTH 1 /* DMICDAT3_PD */
+#define WM8995_DMICDAT2_PD 0x1000 /* DMICDAT2_PD */
+#define WM8995_DMICDAT2_PD_MASK 0x1000 /* DMICDAT2_PD */
+#define WM8995_DMICDAT2_PD_SHIFT 12 /* DMICDAT2_PD */
+#define WM8995_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */
+#define WM8995_DMICDAT1_PD 0x0400 /* DMICDAT1_PD */
+#define WM8995_DMICDAT1_PD_MASK 0x0400 /* DMICDAT1_PD */
+#define WM8995_DMICDAT1_PD_SHIFT 10 /* DMICDAT1_PD */
+#define WM8995_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */
+#define WM8995_MCLK2_PU 0x0200 /* MCLK2_PU */
+#define WM8995_MCLK2_PU_MASK 0x0200 /* MCLK2_PU */
+#define WM8995_MCLK2_PU_SHIFT 9 /* MCLK2_PU */
+#define WM8995_MCLK2_PU_WIDTH 1 /* MCLK2_PU */
+#define WM8995_MCLK2_PD 0x0100 /* MCLK2_PD */
+#define WM8995_MCLK2_PD_MASK 0x0100 /* MCLK2_PD */
+#define WM8995_MCLK2_PD_SHIFT 8 /* MCLK2_PD */
+#define WM8995_MCLK2_PD_WIDTH 1 /* MCLK2_PD */
+#define WM8995_MCLK1_PU 0x0080 /* MCLK1_PU */
+#define WM8995_MCLK1_PU_MASK 0x0080 /* MCLK1_PU */
+#define WM8995_MCLK1_PU_SHIFT 7 /* MCLK1_PU */
+#define WM8995_MCLK1_PU_WIDTH 1 /* MCLK1_PU */
+#define WM8995_MCLK1_PD 0x0040 /* MCLK1_PD */
+#define WM8995_MCLK1_PD_MASK 0x0040 /* MCLK1_PD */
+#define WM8995_MCLK1_PD_SHIFT 6 /* MCLK1_PD */
+#define WM8995_MCLK1_PD_WIDTH 1 /* MCLK1_PD */
+#define WM8995_DACDAT1_PU 0x0020 /* DACDAT1_PU */
+#define WM8995_DACDAT1_PU_MASK 0x0020 /* DACDAT1_PU */
+#define WM8995_DACDAT1_PU_SHIFT 5 /* DACDAT1_PU */
+#define WM8995_DACDAT1_PU_WIDTH 1 /* DACDAT1_PU */
+#define WM8995_DACDAT1_PD 0x0010 /* DACDAT1_PD */
+#define WM8995_DACDAT1_PD_MASK 0x0010 /* DACDAT1_PD */
+#define WM8995_DACDAT1_PD_SHIFT 4 /* DACDAT1_PD */
+#define WM8995_DACDAT1_PD_WIDTH 1 /* DACDAT1_PD */
+#define WM8995_DACLRCLK1_PU 0x0008 /* DACLRCLK1_PU */
+#define WM8995_DACLRCLK1_PU_MASK 0x0008 /* DACLRCLK1_PU */
+#define WM8995_DACLRCLK1_PU_SHIFT 3 /* DACLRCLK1_PU */
+#define WM8995_DACLRCLK1_PU_WIDTH 1 /* DACLRCLK1_PU */
+#define WM8995_DACLRCLK1_PD 0x0004 /* DACLRCLK1_PD */
+#define WM8995_DACLRCLK1_PD_MASK 0x0004 /* DACLRCLK1_PD */
+#define WM8995_DACLRCLK1_PD_SHIFT 2 /* DACLRCLK1_PD */
+#define WM8995_DACLRCLK1_PD_WIDTH 1 /* DACLRCLK1_PD */
+#define WM8995_BCLK1_PU 0x0002 /* BCLK1_PU */
+#define WM8995_BCLK1_PU_MASK 0x0002 /* BCLK1_PU */
+#define WM8995_BCLK1_PU_SHIFT 1 /* BCLK1_PU */
+#define WM8995_BCLK1_PU_WIDTH 1 /* BCLK1_PU */
+#define WM8995_BCLK1_PD 0x0001 /* BCLK1_PD */
+#define WM8995_BCLK1_PD_MASK 0x0001 /* BCLK1_PD */
+#define WM8995_BCLK1_PD_SHIFT 0 /* BCLK1_PD */
+#define WM8995_BCLK1_PD_WIDTH 1 /* BCLK1_PD */
+
+/*
+ * R1825 (0x721) - Pull Control (2)
+ */
+#define WM8995_LDO1ENA_PD 0x0010 /* LDO1ENA_PD */
+#define WM8995_LDO1ENA_PD_MASK 0x0010 /* LDO1ENA_PD */
+#define WM8995_LDO1ENA_PD_SHIFT 4 /* LDO1ENA_PD */
+#define WM8995_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */
+#define WM8995_MODE_PD 0x0004 /* MODE_PD */
+#define WM8995_MODE_PD_MASK 0x0004 /* MODE_PD */
+#define WM8995_MODE_PD_SHIFT 2 /* MODE_PD */
+#define WM8995_MODE_PD_WIDTH 1 /* MODE_PD */
+#define WM8995_CSNADDR_PD 0x0001 /* CSNADDR_PD */
+#define WM8995_CSNADDR_PD_MASK 0x0001 /* CSNADDR_PD */
+#define WM8995_CSNADDR_PD_SHIFT 0 /* CSNADDR_PD */
+#define WM8995_CSNADDR_PD_WIDTH 1 /* CSNADDR_PD */
+
+/*
+ * R1840 (0x730) - Interrupt Status 1
+ */
+#define WM8995_GP14_EINT 0x2000 /* GP14_EINT */
+#define WM8995_GP14_EINT_MASK 0x2000 /* GP14_EINT */
+#define WM8995_GP14_EINT_SHIFT 13 /* GP14_EINT */
+#define WM8995_GP14_EINT_WIDTH 1 /* GP14_EINT */
+#define WM8995_GP13_EINT 0x1000 /* GP13_EINT */
+#define WM8995_GP13_EINT_MASK 0x1000 /* GP13_EINT */
+#define WM8995_GP13_EINT_SHIFT 12 /* GP13_EINT */
+#define WM8995_GP13_EINT_WIDTH 1 /* GP13_EINT */
+#define WM8995_GP12_EINT 0x0800 /* GP12_EINT */
+#define WM8995_GP12_EINT_MASK 0x0800 /* GP12_EINT */
+#define WM8995_GP12_EINT_SHIFT 11 /* GP12_EINT */
+#define WM8995_GP12_EINT_WIDTH 1 /* GP12_EINT */
+#define WM8995_GP11_EINT 0x0400 /* GP11_EINT */
+#define WM8995_GP11_EINT_MASK 0x0400 /* GP11_EINT */
+#define WM8995_GP11_EINT_SHIFT 10 /* GP11_EINT */
+#define WM8995_GP11_EINT_WIDTH 1 /* GP11_EINT */
+#define WM8995_GP10_EINT 0x0200 /* GP10_EINT */
+#define WM8995_GP10_EINT_MASK 0x0200 /* GP10_EINT */
+#define WM8995_GP10_EINT_SHIFT 9 /* GP10_EINT */
+#define WM8995_GP10_EINT_WIDTH 1 /* GP10_EINT */
+#define WM8995_GP9_EINT 0x0100 /* GP9_EINT */
+#define WM8995_GP9_EINT_MASK 0x0100 /* GP9_EINT */
+#define WM8995_GP9_EINT_SHIFT 8 /* GP9_EINT */
+#define WM8995_GP9_EINT_WIDTH 1 /* GP9_EINT */
+#define WM8995_GP8_EINT 0x0080 /* GP8_EINT */
+#define WM8995_GP8_EINT_MASK 0x0080 /* GP8_EINT */
+#define WM8995_GP8_EINT_SHIFT 7 /* GP8_EINT */
+#define WM8995_GP8_EINT_WIDTH 1 /* GP8_EINT */
+#define WM8995_GP7_EINT 0x0040 /* GP7_EINT */
+#define WM8995_GP7_EINT_MASK 0x0040 /* GP7_EINT */
+#define WM8995_GP7_EINT_SHIFT 6 /* GP7_EINT */
+#define WM8995_GP7_EINT_WIDTH 1 /* GP7_EINT */
+#define WM8995_GP6_EINT 0x0020 /* GP6_EINT */
+#define WM8995_GP6_EINT_MASK 0x0020 /* GP6_EINT */
+#define WM8995_GP6_EINT_SHIFT 5 /* GP6_EINT */
+#define WM8995_GP6_EINT_WIDTH 1 /* GP6_EINT */
+#define WM8995_GP5_EINT 0x0010 /* GP5_EINT */
+#define WM8995_GP5_EINT_MASK 0x0010 /* GP5_EINT */
+#define WM8995_GP5_EINT_SHIFT 4 /* GP5_EINT */
+#define WM8995_GP5_EINT_WIDTH 1 /* GP5_EINT */
+#define WM8995_GP4_EINT 0x0008 /* GP4_EINT */
+#define WM8995_GP4_EINT_MASK 0x0008 /* GP4_EINT */
+#define WM8995_GP4_EINT_SHIFT 3 /* GP4_EINT */
+#define WM8995_GP4_EINT_WIDTH 1 /* GP4_EINT */
+#define WM8995_GP3_EINT 0x0004 /* GP3_EINT */
+#define WM8995_GP3_EINT_MASK 0x0004 /* GP3_EINT */
+#define WM8995_GP3_EINT_SHIFT 2 /* GP3_EINT */
+#define WM8995_GP3_EINT_WIDTH 1 /* GP3_EINT */
+#define WM8995_GP2_EINT 0x0002 /* GP2_EINT */
+#define WM8995_GP2_EINT_MASK 0x0002 /* GP2_EINT */
+#define WM8995_GP2_EINT_SHIFT 1 /* GP2_EINT */
+#define WM8995_GP2_EINT_WIDTH 1 /* GP2_EINT */
+#define WM8995_GP1_EINT 0x0001 /* GP1_EINT */
+#define WM8995_GP1_EINT_MASK 0x0001 /* GP1_EINT */
+#define WM8995_GP1_EINT_SHIFT 0 /* GP1_EINT */
+#define WM8995_GP1_EINT_WIDTH 1 /* GP1_EINT */
+
+/*
+ * R1841 (0x731) - Interrupt Status 2
+ */
+#define WM8995_DCS_DONE_23_EINT 0x1000 /* DCS_DONE_23_EINT */
+#define WM8995_DCS_DONE_23_EINT_MASK 0x1000 /* DCS_DONE_23_EINT */
+#define WM8995_DCS_DONE_23_EINT_SHIFT 12 /* DCS_DONE_23_EINT */
+#define WM8995_DCS_DONE_23_EINT_WIDTH 1 /* DCS_DONE_23_EINT */
+#define WM8995_DCS_DONE_01_EINT 0x0800 /* DCS_DONE_01_EINT */
+#define WM8995_DCS_DONE_01_EINT_MASK 0x0800 /* DCS_DONE_01_EINT */
+#define WM8995_DCS_DONE_01_EINT_SHIFT 11 /* DCS_DONE_01_EINT */
+#define WM8995_DCS_DONE_01_EINT_WIDTH 1 /* DCS_DONE_01_EINT */
+#define WM8995_WSEQ_DONE_EINT 0x0400 /* WSEQ_DONE_EINT */
+#define WM8995_WSEQ_DONE_EINT_MASK 0x0400 /* WSEQ_DONE_EINT */
+#define WM8995_WSEQ_DONE_EINT_SHIFT 10 /* WSEQ_DONE_EINT */
+#define WM8995_WSEQ_DONE_EINT_WIDTH 1 /* WSEQ_DONE_EINT */
+#define WM8995_FIFOS_ERR_EINT 0x0200 /* FIFOS_ERR_EINT */
+#define WM8995_FIFOS_ERR_EINT_MASK 0x0200 /* FIFOS_ERR_EINT */
+#define WM8995_FIFOS_ERR_EINT_SHIFT 9 /* FIFOS_ERR_EINT */
+#define WM8995_FIFOS_ERR_EINT_WIDTH 1 /* FIFOS_ERR_EINT */
+#define WM8995_AIF2DRC_SIG_DET_EINT 0x0100 /* AIF2DRC_SIG_DET_EINT */
+#define WM8995_AIF2DRC_SIG_DET_EINT_MASK 0x0100 /* AIF2DRC_SIG_DET_EINT */
+#define WM8995_AIF2DRC_SIG_DET_EINT_SHIFT 8 /* AIF2DRC_SIG_DET_EINT */
+#define WM8995_AIF2DRC_SIG_DET_EINT_WIDTH 1 /* AIF2DRC_SIG_DET_EINT */
+#define WM8995_AIF1DRC2_SIG_DET_EINT 0x0080 /* AIF1DRC2_SIG_DET_EINT */
+#define WM8995_AIF1DRC2_SIG_DET_EINT_MASK 0x0080 /* AIF1DRC2_SIG_DET_EINT */
+#define WM8995_AIF1DRC2_SIG_DET_EINT_SHIFT 7 /* AIF1DRC2_SIG_DET_EINT */
+#define WM8995_AIF1DRC2_SIG_DET_EINT_WIDTH 1 /* AIF1DRC2_SIG_DET_EINT */
+#define WM8995_AIF1DRC1_SIG_DET_EINT 0x0040 /* AIF1DRC1_SIG_DET_EINT */
+#define WM8995_AIF1DRC1_SIG_DET_EINT_MASK 0x0040 /* AIF1DRC1_SIG_DET_EINT */
+#define WM8995_AIF1DRC1_SIG_DET_EINT_SHIFT 6 /* AIF1DRC1_SIG_DET_EINT */
+#define WM8995_AIF1DRC1_SIG_DET_EINT_WIDTH 1 /* AIF1DRC1_SIG_DET_EINT */
+#define WM8995_SRC2_LOCK_EINT 0x0020 /* SRC2_LOCK_EINT */
+#define WM8995_SRC2_LOCK_EINT_MASK 0x0020 /* SRC2_LOCK_EINT */
+#define WM8995_SRC2_LOCK_EINT_SHIFT 5 /* SRC2_LOCK_EINT */
+#define WM8995_SRC2_LOCK_EINT_WIDTH 1 /* SRC2_LOCK_EINT */
+#define WM8995_SRC1_LOCK_EINT 0x0010 /* SRC1_LOCK_EINT */
+#define WM8995_SRC1_LOCK_EINT_MASK 0x0010 /* SRC1_LOCK_EINT */
+#define WM8995_SRC1_LOCK_EINT_SHIFT 4 /* SRC1_LOCK_EINT */
+#define WM8995_SRC1_LOCK_EINT_WIDTH 1 /* SRC1_LOCK_EINT */
+#define WM8995_FLL2_LOCK_EINT 0x0008 /* FLL2_LOCK_EINT */
+#define WM8995_FLL2_LOCK_EINT_MASK 0x0008 /* FLL2_LOCK_EINT */
+#define WM8995_FLL2_LOCK_EINT_SHIFT 3 /* FLL2_LOCK_EINT */
+#define WM8995_FLL2_LOCK_EINT_WIDTH 1 /* FLL2_LOCK_EINT */
+#define WM8995_FLL1_LOCK_EINT 0x0004 /* FLL1_LOCK_EINT */
+#define WM8995_FLL1_LOCK_EINT_MASK 0x0004 /* FLL1_LOCK_EINT */
+#define WM8995_FLL1_LOCK_EINT_SHIFT 2 /* FLL1_LOCK_EINT */
+#define WM8995_FLL1_LOCK_EINT_WIDTH 1 /* FLL1_LOCK_EINT */
+#define WM8995_HP_DONE_EINT 0x0002 /* HP_DONE_EINT */
+#define WM8995_HP_DONE_EINT_MASK 0x0002 /* HP_DONE_EINT */
+#define WM8995_HP_DONE_EINT_SHIFT 1 /* HP_DONE_EINT */
+#define WM8995_HP_DONE_EINT_WIDTH 1 /* HP_DONE_EINT */
+#define WM8995_MICD_EINT 0x0001 /* MICD_EINT */
+#define WM8995_MICD_EINT_MASK 0x0001 /* MICD_EINT */
+#define WM8995_MICD_EINT_SHIFT 0 /* MICD_EINT */
+#define WM8995_MICD_EINT_WIDTH 1 /* MICD_EINT */
+
+/*
+ * R1842 (0x732) - Interrupt Raw Status 2
+ */
+#define WM8995_DCS_DONE_23_STS 0x1000 /* DCS_DONE_23_STS */
+#define WM8995_DCS_DONE_23_STS_MASK 0x1000 /* DCS_DONE_23_STS */
+#define WM8995_DCS_DONE_23_STS_SHIFT 12 /* DCS_DONE_23_STS */
+#define WM8995_DCS_DONE_23_STS_WIDTH 1 /* DCS_DONE_23_STS */
+#define WM8995_DCS_DONE_01_STS 0x0800 /* DCS_DONE_01_STS */
+#define WM8995_DCS_DONE_01_STS_MASK 0x0800 /* DCS_DONE_01_STS */
+#define WM8995_DCS_DONE_01_STS_SHIFT 11 /* DCS_DONE_01_STS */
+#define WM8995_DCS_DONE_01_STS_WIDTH 1 /* DCS_DONE_01_STS */
+#define WM8995_WSEQ_DONE_STS 0x0400 /* WSEQ_DONE_STS */
+#define WM8995_WSEQ_DONE_STS_MASK 0x0400 /* WSEQ_DONE_STS */
+#define WM8995_WSEQ_DONE_STS_SHIFT 10 /* WSEQ_DONE_STS */
+#define WM8995_WSEQ_DONE_STS_WIDTH 1 /* WSEQ_DONE_STS */
+#define WM8995_FIFOS_ERR_STS 0x0200 /* FIFOS_ERR_STS */
+#define WM8995_FIFOS_ERR_STS_MASK 0x0200 /* FIFOS_ERR_STS */
+#define WM8995_FIFOS_ERR_STS_SHIFT 9 /* FIFOS_ERR_STS */
+#define WM8995_FIFOS_ERR_STS_WIDTH 1 /* FIFOS_ERR_STS */
+#define WM8995_AIF2DRC_SIG_DET_STS 0x0100 /* AIF2DRC_SIG_DET_STS */
+#define WM8995_AIF2DRC_SIG_DET_STS_MASK 0x0100 /* AIF2DRC_SIG_DET_STS */
+#define WM8995_AIF2DRC_SIG_DET_STS_SHIFT 8 /* AIF2DRC_SIG_DET_STS */
+#define WM8995_AIF2DRC_SIG_DET_STS_WIDTH 1 /* AIF2DRC_SIG_DET_STS */
+#define WM8995_AIF1DRC2_SIG_DET_STS 0x0080 /* AIF1DRC2_SIG_DET_STS */
+#define WM8995_AIF1DRC2_SIG_DET_STS_MASK 0x0080 /* AIF1DRC2_SIG_DET_STS */
+#define WM8995_AIF1DRC2_SIG_DET_STS_SHIFT 7 /* AIF1DRC2_SIG_DET_STS */
+#define WM8995_AIF1DRC2_SIG_DET_STS_WIDTH 1 /* AIF1DRC2_SIG_DET_STS */
+#define WM8995_AIF1DRC1_SIG_DET_STS 0x0040 /* AIF1DRC1_SIG_DET_STS */
+#define WM8995_AIF1DRC1_SIG_DET_STS_MASK 0x0040 /* AIF1DRC1_SIG_DET_STS */
+#define WM8995_AIF1DRC1_SIG_DET_STS_SHIFT 6 /* AIF1DRC1_SIG_DET_STS */
+#define WM8995_AIF1DRC1_SIG_DET_STS_WIDTH 1 /* AIF1DRC1_SIG_DET_STS */
+#define WM8995_SRC2_LOCK_STS 0x0020 /* SRC2_LOCK_STS */
+#define WM8995_SRC2_LOCK_STS_MASK 0x0020 /* SRC2_LOCK_STS */
+#define WM8995_SRC2_LOCK_STS_SHIFT 5 /* SRC2_LOCK_STS */
+#define WM8995_SRC2_LOCK_STS_WIDTH 1 /* SRC2_LOCK_STS */
+#define WM8995_SRC1_LOCK_STS 0x0010 /* SRC1_LOCK_STS */
+#define WM8995_SRC1_LOCK_STS_MASK 0x0010 /* SRC1_LOCK_STS */
+#define WM8995_SRC1_LOCK_STS_SHIFT 4 /* SRC1_LOCK_STS */
+#define WM8995_SRC1_LOCK_STS_WIDTH 1 /* SRC1_LOCK_STS */
+#define WM8995_FLL2_LOCK_STS 0x0008 /* FLL2_LOCK_STS */
+#define WM8995_FLL2_LOCK_STS_MASK 0x0008 /* FLL2_LOCK_STS */
+#define WM8995_FLL2_LOCK_STS_SHIFT 3 /* FLL2_LOCK_STS */
+#define WM8995_FLL2_LOCK_STS_WIDTH 1 /* FLL2_LOCK_STS */
+#define WM8995_FLL1_LOCK_STS 0x0004 /* FLL1_LOCK_STS */
+#define WM8995_FLL1_LOCK_STS_MASK 0x0004 /* FLL1_LOCK_STS */
+#define WM8995_FLL1_LOCK_STS_SHIFT 2 /* FLL1_LOCK_STS */
+#define WM8995_FLL1_LOCK_STS_WIDTH 1 /* FLL1_LOCK_STS */
+
+/*
+ * R1848 (0x738) - Interrupt Status 1 Mask
+ */
+#define WM8995_IM_GP14_EINT 0x2000 /* IM_GP14_EINT */
+#define WM8995_IM_GP14_EINT_MASK 0x2000 /* IM_GP14_EINT */
+#define WM8995_IM_GP14_EINT_SHIFT 13 /* IM_GP14_EINT */
+#define WM8995_IM_GP14_EINT_WIDTH 1 /* IM_GP14_EINT */
+#define WM8995_IM_GP13_EINT 0x1000 /* IM_GP13_EINT */
+#define WM8995_IM_GP13_EINT_MASK 0x1000 /* IM_GP13_EINT */
+#define WM8995_IM_GP13_EINT_SHIFT 12 /* IM_GP13_EINT */
+#define WM8995_IM_GP13_EINT_WIDTH 1 /* IM_GP13_EINT */
+#define WM8995_IM_GP12_EINT 0x0800 /* IM_GP12_EINT */
+#define WM8995_IM_GP12_EINT_MASK 0x0800 /* IM_GP12_EINT */
+#define WM8995_IM_GP12_EINT_SHIFT 11 /* IM_GP12_EINT */
+#define WM8995_IM_GP12_EINT_WIDTH 1 /* IM_GP12_EINT */
+#define WM8995_IM_GP11_EINT 0x0400 /* IM_GP11_EINT */
+#define WM8995_IM_GP11_EINT_MASK 0x0400 /* IM_GP11_EINT */
+#define WM8995_IM_GP11_EINT_SHIFT 10 /* IM_GP11_EINT */
+#define WM8995_IM_GP11_EINT_WIDTH 1 /* IM_GP11_EINT */
+#define WM8995_IM_GP10_EINT 0x0200 /* IM_GP10_EINT */
+#define WM8995_IM_GP10_EINT_MASK 0x0200 /* IM_GP10_EINT */
+#define WM8995_IM_GP10_EINT_SHIFT 9 /* IM_GP10_EINT */
+#define WM8995_IM_GP10_EINT_WIDTH 1 /* IM_GP10_EINT */
+#define WM8995_IM_GP9_EINT 0x0100 /* IM_GP9_EINT */
+#define WM8995_IM_GP9_EINT_MASK 0x0100 /* IM_GP9_EINT */
+#define WM8995_IM_GP9_EINT_SHIFT 8 /* IM_GP9_EINT */
+#define WM8995_IM_GP9_EINT_WIDTH 1 /* IM_GP9_EINT */
+#define WM8995_IM_GP8_EINT 0x0080 /* IM_GP8_EINT */
+#define WM8995_IM_GP8_EINT_MASK 0x0080 /* IM_GP8_EINT */
+#define WM8995_IM_GP8_EINT_SHIFT 7 /* IM_GP8_EINT */
+#define WM8995_IM_GP8_EINT_WIDTH 1 /* IM_GP8_EINT */
+#define WM8995_IM_GP7_EINT 0x0040 /* IM_GP7_EINT */
+#define WM8995_IM_GP7_EINT_MASK 0x0040 /* IM_GP7_EINT */
+#define WM8995_IM_GP7_EINT_SHIFT 6 /* IM_GP7_EINT */
+#define WM8995_IM_GP7_EINT_WIDTH 1 /* IM_GP7_EINT */
+#define WM8995_IM_GP6_EINT 0x0020 /* IM_GP6_EINT */
+#define WM8995_IM_GP6_EINT_MASK 0x0020 /* IM_GP6_EINT */
+#define WM8995_IM_GP6_EINT_SHIFT 5 /* IM_GP6_EINT */
+#define WM8995_IM_GP6_EINT_WIDTH 1 /* IM_GP6_EINT */
+#define WM8995_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */
+#define WM8995_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */
+#define WM8995_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */
+#define WM8995_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */
+#define WM8995_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */
+#define WM8995_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */
+#define WM8995_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */
+#define WM8995_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */
+#define WM8995_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */
+#define WM8995_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */
+#define WM8995_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */
+#define WM8995_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */
+#define WM8995_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */
+#define WM8995_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */
+#define WM8995_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */
+#define WM8995_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */
+#define WM8995_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */
+#define WM8995_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */
+#define WM8995_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */
+#define WM8995_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */
+
+/*
+ * R1849 (0x739) - Interrupt Status 2 Mask
+ */
+#define WM8995_IM_DCS_DONE_23_EINT 0x1000 /* IM_DCS_DONE_23_EINT */
+#define WM8995_IM_DCS_DONE_23_EINT_MASK 0x1000 /* IM_DCS_DONE_23_EINT */
+#define WM8995_IM_DCS_DONE_23_EINT_SHIFT 12 /* IM_DCS_DONE_23_EINT */
+#define WM8995_IM_DCS_DONE_23_EINT_WIDTH 1 /* IM_DCS_DONE_23_EINT */
+#define WM8995_IM_DCS_DONE_01_EINT 0x0800 /* IM_DCS_DONE_01_EINT */
+#define WM8995_IM_DCS_DONE_01_EINT_MASK 0x0800 /* IM_DCS_DONE_01_EINT */
+#define WM8995_IM_DCS_DONE_01_EINT_SHIFT 11 /* IM_DCS_DONE_01_EINT */
+#define WM8995_IM_DCS_DONE_01_EINT_WIDTH 1 /* IM_DCS_DONE_01_EINT */
+#define WM8995_IM_WSEQ_DONE_EINT 0x0400 /* IM_WSEQ_DONE_EINT */
+#define WM8995_IM_WSEQ_DONE_EINT_MASK 0x0400 /* IM_WSEQ_DONE_EINT */
+#define WM8995_IM_WSEQ_DONE_EINT_SHIFT 10 /* IM_WSEQ_DONE_EINT */
+#define WM8995_IM_WSEQ_DONE_EINT_WIDTH 1 /* IM_WSEQ_DONE_EINT */
+#define WM8995_IM_FIFOS_ERR_EINT 0x0200 /* IM_FIFOS_ERR_EINT */
+#define WM8995_IM_FIFOS_ERR_EINT_MASK 0x0200 /* IM_FIFOS_ERR_EINT */
+#define WM8995_IM_FIFOS_ERR_EINT_SHIFT 9 /* IM_FIFOS_ERR_EINT */
+#define WM8995_IM_FIFOS_ERR_EINT_WIDTH 1 /* IM_FIFOS_ERR_EINT */
+#define WM8995_IM_AIF2DRC_SIG_DET_EINT 0x0100 /* IM_AIF2DRC_SIG_DET_EINT */
+#define WM8995_IM_AIF2DRC_SIG_DET_EINT_MASK 0x0100 /* IM_AIF2DRC_SIG_DET_EINT */
+#define WM8995_IM_AIF2DRC_SIG_DET_EINT_SHIFT 8 /* IM_AIF2DRC_SIG_DET_EINT */
+#define WM8995_IM_AIF2DRC_SIG_DET_EINT_WIDTH 1 /* IM_AIF2DRC_SIG_DET_EINT */
+#define WM8995_IM_AIF1DRC2_SIG_DET_EINT 0x0080 /* IM_AIF1DRC2_SIG_DET_EINT */
+#define WM8995_IM_AIF1DRC2_SIG_DET_EINT_MASK 0x0080 /* IM_AIF1DRC2_SIG_DET_EINT */
+#define WM8995_IM_AIF1DRC2_SIG_DET_EINT_SHIFT 7 /* IM_AIF1DRC2_SIG_DET_EINT */
+#define WM8995_IM_AIF1DRC2_SIG_DET_EINT_WIDTH 1 /* IM_AIF1DRC2_SIG_DET_EINT */
+#define WM8995_IM_AIF1DRC1_SIG_DET_EINT 0x0040 /* IM_AIF1DRC1_SIG_DET_EINT */
+#define WM8995_IM_AIF1DRC1_SIG_DET_EINT_MASK 0x0040 /* IM_AIF1DRC1_SIG_DET_EINT */
+#define WM8995_IM_AIF1DRC1_SIG_DET_EINT_SHIFT 6 /* IM_AIF1DRC1_SIG_DET_EINT */
+#define WM8995_IM_AIF1DRC1_SIG_DET_EINT_WIDTH 1 /* IM_AIF1DRC1_SIG_DET_EINT */
+#define WM8995_IM_SRC2_LOCK_EINT 0x0020 /* IM_SRC2_LOCK_EINT */
+#define WM8995_IM_SRC2_LOCK_EINT_MASK 0x0020 /* IM_SRC2_LOCK_EINT */
+#define WM8995_IM_SRC2_LOCK_EINT_SHIFT 5 /* IM_SRC2_LOCK_EINT */
+#define WM8995_IM_SRC2_LOCK_EINT_WIDTH 1 /* IM_SRC2_LOCK_EINT */
+#define WM8995_IM_SRC1_LOCK_EINT 0x0010 /* IM_SRC1_LOCK_EINT */
+#define WM8995_IM_SRC1_LOCK_EINT_MASK 0x0010 /* IM_SRC1_LOCK_EINT */
+#define WM8995_IM_SRC1_LOCK_EINT_SHIFT 4 /* IM_SRC1_LOCK_EINT */
+#define WM8995_IM_SRC1_LOCK_EINT_WIDTH 1 /* IM_SRC1_LOCK_EINT */
+#define WM8995_IM_FLL2_LOCK_EINT 0x0008 /* IM_FLL2_LOCK_EINT */
+#define WM8995_IM_FLL2_LOCK_EINT_MASK 0x0008 /* IM_FLL2_LOCK_EINT */
+#define WM8995_IM_FLL2_LOCK_EINT_SHIFT 3 /* IM_FLL2_LOCK_EINT */
+#define WM8995_IM_FLL2_LOCK_EINT_WIDTH 1 /* IM_FLL2_LOCK_EINT */
+#define WM8995_IM_FLL1_LOCK_EINT 0x0004 /* IM_FLL1_LOCK_EINT */
+#define WM8995_IM_FLL1_LOCK_EINT_MASK 0x0004 /* IM_FLL1_LOCK_EINT */
+#define WM8995_IM_FLL1_LOCK_EINT_SHIFT 2 /* IM_FLL1_LOCK_EINT */
+#define WM8995_IM_FLL1_LOCK_EINT_WIDTH 1 /* IM_FLL1_LOCK_EINT */
+#define WM8995_IM_HP_DONE_EINT 0x0002 /* IM_HP_DONE_EINT */
+#define WM8995_IM_HP_DONE_EINT_MASK 0x0002 /* IM_HP_DONE_EINT */
+#define WM8995_IM_HP_DONE_EINT_SHIFT 1 /* IM_HP_DONE_EINT */
+#define WM8995_IM_HP_DONE_EINT_WIDTH 1 /* IM_HP_DONE_EINT */
+#define WM8995_IM_MICD_EINT 0x0001 /* IM_MICD_EINT */
+#define WM8995_IM_MICD_EINT_MASK 0x0001 /* IM_MICD_EINT */
+#define WM8995_IM_MICD_EINT_SHIFT 0 /* IM_MICD_EINT */
+#define WM8995_IM_MICD_EINT_WIDTH 1 /* IM_MICD_EINT */
+
+/*
+ * R1856 (0x740) - Interrupt Control
+ */
+#define WM8995_IM_IRQ 0x0001 /* IM_IRQ */
+#define WM8995_IM_IRQ_MASK 0x0001 /* IM_IRQ */
+#define WM8995_IM_IRQ_SHIFT 0 /* IM_IRQ */
+#define WM8995_IM_IRQ_WIDTH 1 /* IM_IRQ */
+
+/*
+ * R2048 (0x800) - Left PDM Speaker 1
+ */
+#define WM8995_SPK1L_ENA 0x0010 /* SPK1L_ENA */
+#define WM8995_SPK1L_ENA_MASK 0x0010 /* SPK1L_ENA */
+#define WM8995_SPK1L_ENA_SHIFT 4 /* SPK1L_ENA */
+#define WM8995_SPK1L_ENA_WIDTH 1 /* SPK1L_ENA */
+#define WM8995_SPK1L_MUTE 0x0008 /* SPK1L_MUTE */
+#define WM8995_SPK1L_MUTE_MASK 0x0008 /* SPK1L_MUTE */
+#define WM8995_SPK1L_MUTE_SHIFT 3 /* SPK1L_MUTE */
+#define WM8995_SPK1L_MUTE_WIDTH 1 /* SPK1L_MUTE */
+#define WM8995_SPK1L_MUTE_ZC 0x0004 /* SPK1L_MUTE_ZC */
+#define WM8995_SPK1L_MUTE_ZC_MASK 0x0004 /* SPK1L_MUTE_ZC */
+#define WM8995_SPK1L_MUTE_ZC_SHIFT 2 /* SPK1L_MUTE_ZC */
+#define WM8995_SPK1L_MUTE_ZC_WIDTH 1 /* SPK1L_MUTE_ZC */
+#define WM8995_SPK1L_SRC_MASK 0x0003 /* SPK1L_SRC - [1:0] */
+#define WM8995_SPK1L_SRC_SHIFT 0 /* SPK1L_SRC - [1:0] */
+#define WM8995_SPK1L_SRC_WIDTH 2 /* SPK1L_SRC - [1:0] */
+
+/*
+ * R2049 (0x801) - Right PDM Speaker 1
+ */
+#define WM8995_SPK1R_ENA 0x0010 /* SPK1R_ENA */
+#define WM8995_SPK1R_ENA_MASK 0x0010 /* SPK1R_ENA */
+#define WM8995_SPK1R_ENA_SHIFT 4 /* SPK1R_ENA */
+#define WM8995_SPK1R_ENA_WIDTH 1 /* SPK1R_ENA */
+#define WM8995_SPK1R_MUTE 0x0008 /* SPK1R_MUTE */
+#define WM8995_SPK1R_MUTE_MASK 0x0008 /* SPK1R_MUTE */
+#define WM8995_SPK1R_MUTE_SHIFT 3 /* SPK1R_MUTE */
+#define WM8995_SPK1R_MUTE_WIDTH 1 /* SPK1R_MUTE */
+#define WM8995_SPK1R_MUTE_ZC 0x0004 /* SPK1R_MUTE_ZC */
+#define WM8995_SPK1R_MUTE_ZC_MASK 0x0004 /* SPK1R_MUTE_ZC */
+#define WM8995_SPK1R_MUTE_ZC_SHIFT 2 /* SPK1R_MUTE_ZC */
+#define WM8995_SPK1R_MUTE_ZC_WIDTH 1 /* SPK1R_MUTE_ZC */
+#define WM8995_SPK1R_SRC_MASK 0x0003 /* SPK1R_SRC - [1:0] */
+#define WM8995_SPK1R_SRC_SHIFT 0 /* SPK1R_SRC - [1:0] */
+#define WM8995_SPK1R_SRC_WIDTH 2 /* SPK1R_SRC - [1:0] */
+
+/*
+ * R2050 (0x802) - PDM Speaker 1 Mute Sequence
+ */
+#define WM8995_SPK1_MUTE_SEQ1_MASK 0x00FF /* SPK1_MUTE_SEQ1 - [7:0] */
+#define WM8995_SPK1_MUTE_SEQ1_SHIFT 0 /* SPK1_MUTE_SEQ1 - [7:0] */
+#define WM8995_SPK1_MUTE_SEQ1_WIDTH 8 /* SPK1_MUTE_SEQ1 - [7:0] */
+
+/*
+ * R2056 (0x808) - Left PDM Speaker 2
+ */
+#define WM8995_SPK2L_ENA 0x0010 /* SPK2L_ENA */
+#define WM8995_SPK2L_ENA_MASK 0x0010 /* SPK2L_ENA */
+#define WM8995_SPK2L_ENA_SHIFT 4 /* SPK2L_ENA */
+#define WM8995_SPK2L_ENA_WIDTH 1 /* SPK2L_ENA */
+#define WM8995_SPK2L_MUTE 0x0008 /* SPK2L_MUTE */
+#define WM8995_SPK2L_MUTE_MASK 0x0008 /* SPK2L_MUTE */
+#define WM8995_SPK2L_MUTE_SHIFT 3 /* SPK2L_MUTE */
+#define WM8995_SPK2L_MUTE_WIDTH 1 /* SPK2L_MUTE */
+#define WM8995_SPK2L_MUTE_ZC 0x0004 /* SPK2L_MUTE_ZC */
+#define WM8995_SPK2L_MUTE_ZC_MASK 0x0004 /* SPK2L_MUTE_ZC */
+#define WM8995_SPK2L_MUTE_ZC_SHIFT 2 /* SPK2L_MUTE_ZC */
+#define WM8995_SPK2L_MUTE_ZC_WIDTH 1 /* SPK2L_MUTE_ZC */
+#define WM8995_SPK2L_SRC_MASK 0x0003 /* SPK2L_SRC - [1:0] */
+#define WM8995_SPK2L_SRC_SHIFT 0 /* SPK2L_SRC - [1:0] */
+#define WM8995_SPK2L_SRC_WIDTH 2 /* SPK2L_SRC - [1:0] */
+
+/*
+ * R2057 (0x809) - Right PDM Speaker 2
+ */
+#define WM8995_SPK2R_ENA 0x0010 /* SPK2R_ENA */
+#define WM8995_SPK2R_ENA_MASK 0x0010 /* SPK2R_ENA */
+#define WM8995_SPK2R_ENA_SHIFT 4 /* SPK2R_ENA */
+#define WM8995_SPK2R_ENA_WIDTH 1 /* SPK2R_ENA */
+#define WM8995_SPK2R_MUTE 0x0008 /* SPK2R_MUTE */
+#define WM8995_SPK2R_MUTE_MASK 0x0008 /* SPK2R_MUTE */
+#define WM8995_SPK2R_MUTE_SHIFT 3 /* SPK2R_MUTE */
+#define WM8995_SPK2R_MUTE_WIDTH 1 /* SPK2R_MUTE */
+#define WM8995_SPK2R_MUTE_ZC 0x0004 /* SPK2R_MUTE_ZC */
+#define WM8995_SPK2R_MUTE_ZC_MASK 0x0004 /* SPK2R_MUTE_ZC */
+#define WM8995_SPK2R_MUTE_ZC_SHIFT 2 /* SPK2R_MUTE_ZC */
+#define WM8995_SPK2R_MUTE_ZC_WIDTH 1 /* SPK2R_MUTE_ZC */
+#define WM8995_SPK2R_SRC_MASK 0x0003 /* SPK2R_SRC - [1:0] */
+#define WM8995_SPK2R_SRC_SHIFT 0 /* SPK2R_SRC - [1:0] */
+#define WM8995_SPK2R_SRC_WIDTH 2 /* SPK2R_SRC - [1:0] */
+
+/*
+ * R2058 (0x80A) - PDM Speaker 2 Mute Sequence
+ */
+#define WM8995_SPK2_MUTE_SEQ1_MASK 0x00FF /* SPK2_MUTE_SEQ1 - [7:0] */
+#define WM8995_SPK2_MUTE_SEQ1_SHIFT 0 /* SPK2_MUTE_SEQ1 - [7:0] */
+#define WM8995_SPK2_MUTE_SEQ1_WIDTH 8 /* SPK2_MUTE_SEQ1 - [7:0] */
+
+#define WM8995_CLASS_W_SWITCH(xname, reg, shift, max, invert) \
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, wm8995_put_class_w)
+
+struct wm8995_reg_access {
+ u16 read;
+ u16 write;
+ u16 vol;
+};
+
+/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
+enum clk_src {
+ WM8995_SYSCLK_MCLK1 = 1,
+ WM8995_SYSCLK_MCLK2,
+ WM8995_SYSCLK_FLL1,
+ WM8995_SYSCLK_FLL2,
+ WM8995_SYSCLK_OPCLK
+};
+
+#define WM8995_FLL1 1
+#define WM8995_FLL2 2
+
+#define WM8995_FLL_SRC_MCLK1 1
+#define WM8995_FLL_SRC_MCLK2 2
+#define WM8995_FLL_SRC_LRCLK 3
+#define WM8995_FLL_SRC_BCLK 4
+
+#endif /* _WM8995_H */
diff --git a/kernel/sound/soc/codecs/wm8996.c b/kernel/sound/soc/codecs/wm8996.c
new file mode 100644
index 000000000..308748a02
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8996.c
@@ -0,0 +1,3111 @@
+/*
+ * wm8996.c - WM8996 audio codec interface
+ *
+ * Copyright 2011-2 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gcd.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <trace/events/asoc.h>
+
+#include <sound/wm8996.h>
+#include "wm8996.h"
+
+#define WM8996_AIFS 2
+
+#define HPOUT1L 1
+#define HPOUT1R 2
+#define HPOUT2L 4
+#define HPOUT2R 8
+
+#define WM8996_NUM_SUPPLIES 3
+static const char *wm8996_supply_names[WM8996_NUM_SUPPLIES] = {
+ "DBVDD",
+ "AVDD1",
+ "AVDD2",
+};
+
+struct wm8996_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+
+ int ldo1ena;
+
+ int sysclk;
+ int sysclk_src;
+
+ int fll_src;
+ int fll_fref;
+ int fll_fout;
+
+ struct completion fll_lock;
+
+ u16 dcs_pending;
+ struct completion dcs_done;
+
+ u16 hpout_ena;
+ u16 hpout_pending;
+
+ struct regulator_bulk_data supplies[WM8996_NUM_SUPPLIES];
+ struct notifier_block disable_nb[WM8996_NUM_SUPPLIES];
+ int bg_ena;
+
+ struct wm8996_pdata pdata;
+
+ int rx_rate[WM8996_AIFS];
+ int bclk_rate[WM8996_AIFS];
+
+ /* Platform dependant ReTune mobile configuration */
+ int num_retune_mobile_texts;
+ const char **retune_mobile_texts;
+ int retune_mobile_cfg[2];
+ struct soc_enum retune_mobile_enum;
+
+ struct snd_soc_jack *jack;
+ bool detecting;
+ bool jack_mic;
+ int jack_flips;
+ wm8996_polarity_fn polarity_cb;
+
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio_chip;
+#endif
+};
+
+/* We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define WM8996_REGULATOR_EVENT(n) \
+static int wm8996_regulator_event_##n(struct notifier_block *nb, \
+ unsigned long event, void *data) \
+{ \
+ struct wm8996_priv *wm8996 = container_of(nb, struct wm8996_priv, \
+ disable_nb[n]); \
+ if (event & REGULATOR_EVENT_DISABLE) { \
+ regcache_mark_dirty(wm8996->regmap); \
+ } \
+ return 0; \
+}
+
+WM8996_REGULATOR_EVENT(0)
+WM8996_REGULATOR_EVENT(1)
+WM8996_REGULATOR_EVENT(2)
+
+static struct reg_default wm8996_reg[] = {
+ { WM8996_POWER_MANAGEMENT_1, 0x0 },
+ { WM8996_POWER_MANAGEMENT_2, 0x0 },
+ { WM8996_POWER_MANAGEMENT_3, 0x0 },
+ { WM8996_POWER_MANAGEMENT_4, 0x0 },
+ { WM8996_POWER_MANAGEMENT_5, 0x0 },
+ { WM8996_POWER_MANAGEMENT_6, 0x0 },
+ { WM8996_POWER_MANAGEMENT_7, 0x10 },
+ { WM8996_POWER_MANAGEMENT_8, 0x0 },
+ { WM8996_LEFT_LINE_INPUT_VOLUME, 0x0 },
+ { WM8996_RIGHT_LINE_INPUT_VOLUME, 0x0 },
+ { WM8996_LINE_INPUT_CONTROL, 0x0 },
+ { WM8996_DAC1_HPOUT1_VOLUME, 0x88 },
+ { WM8996_DAC2_HPOUT2_VOLUME, 0x88 },
+ { WM8996_DAC1_LEFT_VOLUME, 0x2c0 },
+ { WM8996_DAC1_RIGHT_VOLUME, 0x2c0 },
+ { WM8996_DAC2_LEFT_VOLUME, 0x2c0 },
+ { WM8996_DAC2_RIGHT_VOLUME, 0x2c0 },
+ { WM8996_OUTPUT1_LEFT_VOLUME, 0x80 },
+ { WM8996_OUTPUT1_RIGHT_VOLUME, 0x80 },
+ { WM8996_OUTPUT2_LEFT_VOLUME, 0x80 },
+ { WM8996_OUTPUT2_RIGHT_VOLUME, 0x80 },
+ { WM8996_MICBIAS_1, 0x39 },
+ { WM8996_MICBIAS_2, 0x39 },
+ { WM8996_LDO_1, 0x3 },
+ { WM8996_LDO_2, 0x13 },
+ { WM8996_ACCESSORY_DETECT_MODE_1, 0x4 },
+ { WM8996_ACCESSORY_DETECT_MODE_2, 0x0 },
+ { WM8996_HEADPHONE_DETECT_1, 0x20 },
+ { WM8996_HEADPHONE_DETECT_2, 0x0 },
+ { WM8996_MIC_DETECT_1, 0x7600 },
+ { WM8996_MIC_DETECT_2, 0xbf },
+ { WM8996_CHARGE_PUMP_1, 0x1f25 },
+ { WM8996_CHARGE_PUMP_2, 0xab19 },
+ { WM8996_DC_SERVO_1, 0x0 },
+ { WM8996_DC_SERVO_3, 0x0 },
+ { WM8996_DC_SERVO_5, 0x2a2a },
+ { WM8996_DC_SERVO_6, 0x0 },
+ { WM8996_DC_SERVO_7, 0x0 },
+ { WM8996_ANALOGUE_HP_1, 0x0 },
+ { WM8996_ANALOGUE_HP_2, 0x0 },
+ { WM8996_CONTROL_INTERFACE_1, 0x8004 },
+ { WM8996_WRITE_SEQUENCER_CTRL_1, 0x0 },
+ { WM8996_WRITE_SEQUENCER_CTRL_2, 0x0 },
+ { WM8996_AIF_CLOCKING_1, 0x0 },
+ { WM8996_AIF_CLOCKING_2, 0x0 },
+ { WM8996_CLOCKING_1, 0x10 },
+ { WM8996_CLOCKING_2, 0x0 },
+ { WM8996_AIF_RATE, 0x83 },
+ { WM8996_FLL_CONTROL_1, 0x0 },
+ { WM8996_FLL_CONTROL_2, 0x0 },
+ { WM8996_FLL_CONTROL_3, 0x0 },
+ { WM8996_FLL_CONTROL_4, 0x5dc0 },
+ { WM8996_FLL_CONTROL_5, 0xc84 },
+ { WM8996_FLL_EFS_1, 0x0 },
+ { WM8996_FLL_EFS_2, 0x2 },
+ { WM8996_AIF1_CONTROL, 0x0 },
+ { WM8996_AIF1_BCLK, 0x0 },
+ { WM8996_AIF1_TX_LRCLK_1, 0x80 },
+ { WM8996_AIF1_TX_LRCLK_2, 0x8 },
+ { WM8996_AIF1_RX_LRCLK_1, 0x80 },
+ { WM8996_AIF1_RX_LRCLK_2, 0x0 },
+ { WM8996_AIF1TX_DATA_CONFIGURATION_1, 0x1818 },
+ { WM8996_AIF1TX_DATA_CONFIGURATION_2, 0 },
+ { WM8996_AIF1RX_DATA_CONFIGURATION, 0x1818 },
+ { WM8996_AIF1TX_CHANNEL_0_CONFIGURATION, 0x0 },
+ { WM8996_AIF1TX_CHANNEL_1_CONFIGURATION, 0x0 },
+ { WM8996_AIF1TX_CHANNEL_2_CONFIGURATION, 0x0 },
+ { WM8996_AIF1TX_CHANNEL_3_CONFIGURATION, 0x0 },
+ { WM8996_AIF1TX_CHANNEL_4_CONFIGURATION, 0x0 },
+ { WM8996_AIF1TX_CHANNEL_5_CONFIGURATION, 0x0 },
+ { WM8996_AIF1RX_CHANNEL_0_CONFIGURATION, 0x0 },
+ { WM8996_AIF1RX_CHANNEL_1_CONFIGURATION, 0x0 },
+ { WM8996_AIF1RX_CHANNEL_2_CONFIGURATION, 0x0 },
+ { WM8996_AIF1RX_CHANNEL_3_CONFIGURATION, 0x0 },
+ { WM8996_AIF1RX_CHANNEL_4_CONFIGURATION, 0x0 },
+ { WM8996_AIF1RX_CHANNEL_5_CONFIGURATION, 0x0 },
+ { WM8996_AIF1RX_MONO_CONFIGURATION, 0x0 },
+ { WM8996_AIF1TX_TEST, 0x7 },
+ { WM8996_AIF2_CONTROL, 0x0 },
+ { WM8996_AIF2_BCLK, 0x0 },
+ { WM8996_AIF2_TX_LRCLK_1, 0x80 },
+ { WM8996_AIF2_TX_LRCLK_2, 0x8 },
+ { WM8996_AIF2_RX_LRCLK_1, 0x80 },
+ { WM8996_AIF2_RX_LRCLK_2, 0x0 },
+ { WM8996_AIF2TX_DATA_CONFIGURATION_1, 0x1818 },
+ { WM8996_AIF2RX_DATA_CONFIGURATION, 0x1818 },
+ { WM8996_AIF2RX_DATA_CONFIGURATION, 0x0 },
+ { WM8996_AIF2TX_CHANNEL_0_CONFIGURATION, 0x0 },
+ { WM8996_AIF2TX_CHANNEL_1_CONFIGURATION, 0x0 },
+ { WM8996_AIF2RX_CHANNEL_0_CONFIGURATION, 0x0 },
+ { WM8996_AIF2RX_CHANNEL_1_CONFIGURATION, 0x0 },
+ { WM8996_AIF2RX_MONO_CONFIGURATION, 0x0 },
+ { WM8996_AIF2TX_TEST, 0x1 },
+ { WM8996_DSP1_TX_LEFT_VOLUME, 0xc0 },
+ { WM8996_DSP1_TX_RIGHT_VOLUME, 0xc0 },
+ { WM8996_DSP1_RX_LEFT_VOLUME, 0xc0 },
+ { WM8996_DSP1_RX_RIGHT_VOLUME, 0xc0 },
+ { WM8996_DSP1_TX_FILTERS, 0x2000 },
+ { WM8996_DSP1_RX_FILTERS_1, 0x200 },
+ { WM8996_DSP1_RX_FILTERS_2, 0x10 },
+ { WM8996_DSP1_DRC_1, 0x98 },
+ { WM8996_DSP1_DRC_2, 0x845 },
+ { WM8996_DSP1_RX_EQ_GAINS_1, 0x6318 },
+ { WM8996_DSP1_RX_EQ_GAINS_2, 0x6300 },
+ { WM8996_DSP1_RX_EQ_BAND_1_A, 0xfca },
+ { WM8996_DSP1_RX_EQ_BAND_1_B, 0x400 },
+ { WM8996_DSP1_RX_EQ_BAND_1_PG, 0xd8 },
+ { WM8996_DSP1_RX_EQ_BAND_2_A, 0x1eb5 },
+ { WM8996_DSP1_RX_EQ_BAND_2_B, 0xf145 },
+ { WM8996_DSP1_RX_EQ_BAND_2_C, 0xb75 },
+ { WM8996_DSP1_RX_EQ_BAND_2_PG, 0x1c5 },
+ { WM8996_DSP1_RX_EQ_BAND_3_A, 0x1c58 },
+ { WM8996_DSP1_RX_EQ_BAND_3_B, 0xf373 },
+ { WM8996_DSP1_RX_EQ_BAND_3_C, 0xa54 },
+ { WM8996_DSP1_RX_EQ_BAND_3_PG, 0x558 },
+ { WM8996_DSP1_RX_EQ_BAND_4_A, 0x168e },
+ { WM8996_DSP1_RX_EQ_BAND_4_B, 0xf829 },
+ { WM8996_DSP1_RX_EQ_BAND_4_C, 0x7ad },
+ { WM8996_DSP1_RX_EQ_BAND_4_PG, 0x1103 },
+ { WM8996_DSP1_RX_EQ_BAND_5_A, 0x564 },
+ { WM8996_DSP1_RX_EQ_BAND_5_B, 0x559 },
+ { WM8996_DSP1_RX_EQ_BAND_5_PG, 0x4000 },
+ { WM8996_DSP2_TX_LEFT_VOLUME, 0xc0 },
+ { WM8996_DSP2_TX_RIGHT_VOLUME, 0xc0 },
+ { WM8996_DSP2_RX_LEFT_VOLUME, 0xc0 },
+ { WM8996_DSP2_RX_RIGHT_VOLUME, 0xc0 },
+ { WM8996_DSP2_TX_FILTERS, 0x2000 },
+ { WM8996_DSP2_RX_FILTERS_1, 0x200 },
+ { WM8996_DSP2_RX_FILTERS_2, 0x10 },
+ { WM8996_DSP2_DRC_1, 0x98 },
+ { WM8996_DSP2_DRC_2, 0x845 },
+ { WM8996_DSP2_RX_EQ_GAINS_1, 0x6318 },
+ { WM8996_DSP2_RX_EQ_GAINS_2, 0x6300 },
+ { WM8996_DSP2_RX_EQ_BAND_1_A, 0xfca },
+ { WM8996_DSP2_RX_EQ_BAND_1_B, 0x400 },
+ { WM8996_DSP2_RX_EQ_BAND_1_PG, 0xd8 },
+ { WM8996_DSP2_RX_EQ_BAND_2_A, 0x1eb5 },
+ { WM8996_DSP2_RX_EQ_BAND_2_B, 0xf145 },
+ { WM8996_DSP2_RX_EQ_BAND_2_C, 0xb75 },
+ { WM8996_DSP2_RX_EQ_BAND_2_PG, 0x1c5 },
+ { WM8996_DSP2_RX_EQ_BAND_3_A, 0x1c58 },
+ { WM8996_DSP2_RX_EQ_BAND_3_B, 0xf373 },
+ { WM8996_DSP2_RX_EQ_BAND_3_C, 0xa54 },
+ { WM8996_DSP2_RX_EQ_BAND_3_PG, 0x558 },
+ { WM8996_DSP2_RX_EQ_BAND_4_A, 0x168e },
+ { WM8996_DSP2_RX_EQ_BAND_4_B, 0xf829 },
+ { WM8996_DSP2_RX_EQ_BAND_4_C, 0x7ad },
+ { WM8996_DSP2_RX_EQ_BAND_4_PG, 0x1103 },
+ { WM8996_DSP2_RX_EQ_BAND_5_A, 0x564 },
+ { WM8996_DSP2_RX_EQ_BAND_5_B, 0x559 },
+ { WM8996_DSP2_RX_EQ_BAND_5_PG, 0x4000 },
+ { WM8996_DAC1_MIXER_VOLUMES, 0x0 },
+ { WM8996_DAC1_LEFT_MIXER_ROUTING, 0x0 },
+ { WM8996_DAC1_RIGHT_MIXER_ROUTING, 0x0 },
+ { WM8996_DAC2_MIXER_VOLUMES, 0x0 },
+ { WM8996_DAC2_LEFT_MIXER_ROUTING, 0x0 },
+ { WM8996_DAC2_RIGHT_MIXER_ROUTING, 0x0 },
+ { WM8996_DSP1_TX_LEFT_MIXER_ROUTING, 0x0 },
+ { WM8996_DSP1_TX_RIGHT_MIXER_ROUTING, 0x0 },
+ { WM8996_DSP2_TX_LEFT_MIXER_ROUTING, 0x0 },
+ { WM8996_DSP2_TX_RIGHT_MIXER_ROUTING, 0x0 },
+ { WM8996_DSP_TX_MIXER_SELECT, 0x0 },
+ { WM8996_DAC_SOFTMUTE, 0x0 },
+ { WM8996_OVERSAMPLING, 0xd },
+ { WM8996_SIDETONE, 0x1040 },
+ { WM8996_GPIO_1, 0xa101 },
+ { WM8996_GPIO_2, 0xa101 },
+ { WM8996_GPIO_3, 0xa101 },
+ { WM8996_GPIO_4, 0xa101 },
+ { WM8996_GPIO_5, 0xa101 },
+ { WM8996_PULL_CONTROL_1, 0x0 },
+ { WM8996_PULL_CONTROL_2, 0x140 },
+ { WM8996_INTERRUPT_STATUS_1_MASK, 0x1f },
+ { WM8996_INTERRUPT_STATUS_2_MASK, 0x1ecf },
+ { WM8996_LEFT_PDM_SPEAKER, 0x0 },
+ { WM8996_RIGHT_PDM_SPEAKER, 0x1 },
+ { WM8996_PDM_SPEAKER_MUTE_SEQUENCE, 0x69 },
+ { WM8996_PDM_SPEAKER_VOLUME, 0x66 },
+};
+
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 150, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(out_digital_tlv, -1200, 150, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -900, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -900, 150, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(threedstereo_tlv, -1600, 183, 1);
+
+static const char *sidetone_hpf_text[] = {
+ "2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(sidetone_hpf,
+ WM8996_SIDETONE, 7, sidetone_hpf_text);
+
+static const char *hpf_mode_text[] = {
+ "HiFi", "Custom", "Voice"
+};
+
+static SOC_ENUM_SINGLE_DECL(dsp1tx_hpf_mode,
+ WM8996_DSP1_TX_FILTERS, 3, hpf_mode_text);
+
+static SOC_ENUM_SINGLE_DECL(dsp2tx_hpf_mode,
+ WM8996_DSP2_TX_FILTERS, 3, hpf_mode_text);
+
+static const char *hpf_cutoff_text[] = {
+ "50Hz", "75Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(dsp1tx_hpf_cutoff,
+ WM8996_DSP1_TX_FILTERS, 0, hpf_cutoff_text);
+
+static SOC_ENUM_SINGLE_DECL(dsp2tx_hpf_cutoff,
+ WM8996_DSP2_TX_FILTERS, 0, hpf_cutoff_text);
+
+static void wm8996_set_retune_mobile(struct snd_soc_codec *codec, int block)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct wm8996_pdata *pdata = &wm8996->pdata;
+ int base, best, best_val, save, i, cfg, iface;
+
+ if (!wm8996->num_retune_mobile_texts)
+ return;
+
+ switch (block) {
+ case 0:
+ base = WM8996_DSP1_RX_EQ_GAINS_1;
+ if (snd_soc_read(codec, WM8996_POWER_MANAGEMENT_8) &
+ WM8996_DSP1RX_SRC)
+ iface = 1;
+ else
+ iface = 0;
+ break;
+ case 1:
+ base = WM8996_DSP1_RX_EQ_GAINS_2;
+ if (snd_soc_read(codec, WM8996_POWER_MANAGEMENT_8) &
+ WM8996_DSP2RX_SRC)
+ iface = 1;
+ else
+ iface = 0;
+ break;
+ default:
+ return;
+ }
+
+ /* Find the version of the currently selected configuration
+ * with the nearest sample rate. */
+ cfg = wm8996->retune_mobile_cfg[block];
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8996->retune_mobile_texts[cfg]) == 0 &&
+ abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8996->rx_rate[iface]) < best_val) {
+ best = i;
+ best_val = abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8996->rx_rate[iface]);
+ }
+ }
+
+ dev_dbg(codec->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
+ block,
+ pdata->retune_mobile_cfgs[best].name,
+ pdata->retune_mobile_cfgs[best].rate,
+ wm8996->rx_rate[iface]);
+
+ /* The EQ will be disabled while reconfiguring it, remember the
+ * current configuration.
+ */
+ save = snd_soc_read(codec, base);
+ save &= WM8996_DSP1RX_EQ_ENA;
+
+ for (i = 0; i < ARRAY_SIZE(pdata->retune_mobile_cfgs[best].regs); i++)
+ snd_soc_update_bits(codec, base + i, 0xffff,
+ pdata->retune_mobile_cfgs[best].regs[i]);
+
+ snd_soc_update_bits(codec, base, WM8996_DSP1RX_EQ_ENA, save);
+}
+
+/* Icky as hell but saves code duplication */
+static int wm8996_get_retune_mobile_block(const char *name)
+{
+ if (strcmp(name, "DSP1 EQ Mode") == 0)
+ return 0;
+ if (strcmp(name, "DSP2 EQ Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static int wm8996_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct wm8996_pdata *pdata = &wm8996->pdata;
+ int block = wm8996_get_retune_mobile_block(kcontrol->id.name);
+ int value = ucontrol->value.integer.value[0];
+
+ if (block < 0)
+ return block;
+
+ if (value >= pdata->num_retune_mobile_cfgs)
+ return -EINVAL;
+
+ wm8996->retune_mobile_cfg[block] = value;
+
+ wm8996_set_retune_mobile(codec, block);
+
+ return 0;
+}
+
+static int wm8996_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int block = wm8996_get_retune_mobile_block(kcontrol->id.name);
+
+ if (block < 0)
+ return block;
+ ucontrol->value.enumerated.item[0] = wm8996->retune_mobile_cfg[block];
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wm8996_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Capture Volume", WM8996_LEFT_LINE_INPUT_VOLUME,
+ WM8996_RIGHT_LINE_INPUT_VOLUME, 0, 31, 0, inpga_tlv),
+SOC_DOUBLE_R("Capture ZC Switch", WM8996_LEFT_LINE_INPUT_VOLUME,
+ WM8996_RIGHT_LINE_INPUT_VOLUME, 5, 1, 0),
+
+SOC_DOUBLE_TLV("DAC1 Sidetone Volume", WM8996_DAC1_MIXER_VOLUMES,
+ 0, 5, 24, 0, sidetone_tlv),
+SOC_DOUBLE_TLV("DAC2 Sidetone Volume", WM8996_DAC2_MIXER_VOLUMES,
+ 0, 5, 24, 0, sidetone_tlv),
+SOC_SINGLE("Sidetone LPF Switch", WM8996_SIDETONE, 12, 1, 0),
+SOC_ENUM("Sidetone HPF Cut-off", sidetone_hpf),
+SOC_SINGLE("Sidetone HPF Switch", WM8996_SIDETONE, 6, 1, 0),
+
+SOC_DOUBLE_R_TLV("DSP1 Capture Volume", WM8996_DSP1_TX_LEFT_VOLUME,
+ WM8996_DSP1_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("DSP2 Capture Volume", WM8996_DSP2_TX_LEFT_VOLUME,
+ WM8996_DSP2_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+
+SOC_SINGLE("DSP1 Capture Notch Filter Switch", WM8996_DSP1_TX_FILTERS,
+ 13, 1, 0),
+SOC_DOUBLE("DSP1 Capture HPF Switch", WM8996_DSP1_TX_FILTERS, 12, 11, 1, 0),
+SOC_ENUM("DSP1 Capture HPF Mode", dsp1tx_hpf_mode),
+SOC_ENUM("DSP1 Capture HPF Cutoff", dsp1tx_hpf_cutoff),
+
+SOC_SINGLE("DSP2 Capture Notch Filter Switch", WM8996_DSP2_TX_FILTERS,
+ 13, 1, 0),
+SOC_DOUBLE("DSP2 Capture HPF Switch", WM8996_DSP2_TX_FILTERS, 12, 11, 1, 0),
+SOC_ENUM("DSP2 Capture HPF Mode", dsp2tx_hpf_mode),
+SOC_ENUM("DSP2 Capture HPF Cutoff", dsp2tx_hpf_cutoff),
+
+SOC_DOUBLE_R_TLV("DSP1 Playback Volume", WM8996_DSP1_RX_LEFT_VOLUME,
+ WM8996_DSP1_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_SINGLE("DSP1 Playback Switch", WM8996_DSP1_RX_FILTERS_1, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DSP2 Playback Volume", WM8996_DSP2_RX_LEFT_VOLUME,
+ WM8996_DSP2_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_SINGLE("DSP2 Playback Switch", WM8996_DSP2_RX_FILTERS_1, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DAC1 Volume", WM8996_DAC1_LEFT_VOLUME,
+ WM8996_DAC1_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_DOUBLE_R("DAC1 Switch", WM8996_DAC1_LEFT_VOLUME,
+ WM8996_DAC1_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DAC2 Volume", WM8996_DAC2_LEFT_VOLUME,
+ WM8996_DAC2_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_DOUBLE_R("DAC2 Switch", WM8996_DAC2_LEFT_VOLUME,
+ WM8996_DAC2_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_SINGLE("Speaker High Performance Switch", WM8996_OVERSAMPLING, 3, 1, 0),
+SOC_SINGLE("DMIC High Performance Switch", WM8996_OVERSAMPLING, 2, 1, 0),
+SOC_SINGLE("ADC High Performance Switch", WM8996_OVERSAMPLING, 1, 1, 0),
+SOC_SINGLE("DAC High Performance Switch", WM8996_OVERSAMPLING, 0, 1, 0),
+
+SOC_SINGLE("DAC Soft Mute Switch", WM8996_DAC_SOFTMUTE, 1, 1, 0),
+SOC_SINGLE("DAC Slow Soft Mute Switch", WM8996_DAC_SOFTMUTE, 0, 1, 0),
+
+SOC_SINGLE("DSP1 3D Stereo Switch", WM8996_DSP1_RX_FILTERS_2, 8, 1, 0),
+SOC_SINGLE("DSP2 3D Stereo Switch", WM8996_DSP2_RX_FILTERS_2, 8, 1, 0),
+
+SOC_SINGLE_TLV("DSP1 3D Stereo Volume", WM8996_DSP1_RX_FILTERS_2, 10, 15,
+ 0, threedstereo_tlv),
+SOC_SINGLE_TLV("DSP2 3D Stereo Volume", WM8996_DSP2_RX_FILTERS_2, 10, 15,
+ 0, threedstereo_tlv),
+
+SOC_DOUBLE_TLV("Digital Output 1 Volume", WM8996_DAC1_HPOUT1_VOLUME, 0, 4,
+ 8, 0, out_digital_tlv),
+SOC_DOUBLE_TLV("Digital Output 2 Volume", WM8996_DAC2_HPOUT2_VOLUME, 0, 4,
+ 8, 0, out_digital_tlv),
+
+SOC_DOUBLE_R_TLV("Output 1 Volume", WM8996_OUTPUT1_LEFT_VOLUME,
+ WM8996_OUTPUT1_RIGHT_VOLUME, 0, 12, 0, out_tlv),
+SOC_DOUBLE_R("Output 1 ZC Switch", WM8996_OUTPUT1_LEFT_VOLUME,
+ WM8996_OUTPUT1_RIGHT_VOLUME, 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Output 2 Volume", WM8996_OUTPUT2_LEFT_VOLUME,
+ WM8996_OUTPUT2_RIGHT_VOLUME, 0, 12, 0, out_tlv),
+SOC_DOUBLE_R("Output 2 ZC Switch", WM8996_OUTPUT2_LEFT_VOLUME,
+ WM8996_OUTPUT2_RIGHT_VOLUME, 7, 1, 0),
+
+SOC_DOUBLE_TLV("Speaker Volume", WM8996_PDM_SPEAKER_VOLUME, 0, 4, 8, 0,
+ spk_tlv),
+SOC_DOUBLE_R("Speaker Switch", WM8996_LEFT_PDM_SPEAKER,
+ WM8996_RIGHT_PDM_SPEAKER, 3, 1, 1),
+SOC_DOUBLE_R("Speaker ZC Switch", WM8996_LEFT_PDM_SPEAKER,
+ WM8996_RIGHT_PDM_SPEAKER, 2, 1, 0),
+
+SOC_SINGLE("DSP1 EQ Switch", WM8996_DSP1_RX_EQ_GAINS_1, 0, 1, 0),
+SOC_SINGLE("DSP2 EQ Switch", WM8996_DSP2_RX_EQ_GAINS_1, 0, 1, 0),
+
+SOC_SINGLE("DSP1 DRC TXL Switch", WM8996_DSP1_DRC_1, 0, 1, 0),
+SOC_SINGLE("DSP1 DRC TXR Switch", WM8996_DSP1_DRC_1, 1, 1, 0),
+SOC_SINGLE("DSP1 DRC RX Switch", WM8996_DSP1_DRC_1, 2, 1, 0),
+SND_SOC_BYTES_MASK("DSP1 DRC", WM8996_DSP1_DRC_1, 5,
+ WM8996_DSP1RX_DRC_ENA | WM8996_DSP1TXL_DRC_ENA |
+ WM8996_DSP1TXR_DRC_ENA),
+
+SOC_SINGLE("DSP2 DRC TXL Switch", WM8996_DSP2_DRC_1, 0, 1, 0),
+SOC_SINGLE("DSP2 DRC TXR Switch", WM8996_DSP2_DRC_1, 1, 1, 0),
+SOC_SINGLE("DSP2 DRC RX Switch", WM8996_DSP2_DRC_1, 2, 1, 0),
+SND_SOC_BYTES_MASK("DSP2 DRC", WM8996_DSP2_DRC_1, 5,
+ WM8996_DSP2RX_DRC_ENA | WM8996_DSP2TXL_DRC_ENA |
+ WM8996_DSP2TXR_DRC_ENA),
+};
+
+static const struct snd_kcontrol_new wm8996_eq_controls[] = {
+SOC_SINGLE_TLV("DSP1 EQ B1 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B2 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B3 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B4 Volume", WM8996_DSP1_RX_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B5 Volume", WM8996_DSP1_RX_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+
+SOC_SINGLE_TLV("DSP2 EQ B1 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B2 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B3 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B4 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B5 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+};
+
+static void wm8996_bg_enable(struct snd_soc_codec *codec)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+
+ wm8996->bg_ena++;
+ if (wm8996->bg_ena == 1) {
+ snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
+ WM8996_BG_ENA, WM8996_BG_ENA);
+ msleep(2);
+ }
+}
+
+static void wm8996_bg_disable(struct snd_soc_codec *codec)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+
+ wm8996->bg_ena--;
+ if (!wm8996->bg_ena)
+ snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
+ WM8996_BG_ENA, 0);
+}
+
+static int bg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wm8996_bg_enable(codec);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wm8996_bg_disable(codec);
+ break;
+ default:
+ WARN(1, "Invalid event %d\n", event);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int cp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(5);
+ break;
+ default:
+ WARN(1, "Invalid event %d\n", event);
+ }
+
+ return 0;
+}
+
+static int rmv_short_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+
+ /* Record which outputs we enabled */
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ wm8996->hpout_pending &= ~w->shift;
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ wm8996->hpout_pending |= w->shift;
+ break;
+ default:
+ WARN(1, "Invalid event %d\n", event);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
+{
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+ unsigned long timeout = 200;
+
+ snd_soc_write(codec, WM8996_DC_SERVO_2, mask);
+
+ /* Use the interrupt if possible */
+ do {
+ if (i2c->irq) {
+ timeout = wait_for_completion_timeout(&wm8996->dcs_done,
+ msecs_to_jiffies(200));
+ if (timeout == 0)
+ dev_err(codec->dev, "DC servo timed out\n");
+
+ } else {
+ msleep(1);
+ timeout--;
+ }
+
+ ret = snd_soc_read(codec, WM8996_DC_SERVO_2);
+ dev_dbg(codec->dev, "DC servo state: %x\n", ret);
+ } while (timeout && ret & mask);
+
+ if (timeout == 0)
+ dev_err(codec->dev, "DC servo timed out for %x\n", mask);
+ else
+ dev_dbg(codec->dev, "DC servo complete for %x\n", mask);
+}
+
+static void wm8996_seq_notifier(struct snd_soc_dapm_context *dapm,
+ enum snd_soc_dapm_type event, int subseq)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ u16 val, mask;
+
+ /* Complete any pending DC servo starts */
+ if (wm8996->dcs_pending) {
+ dev_dbg(codec->dev, "Starting DC servo for %x\n",
+ wm8996->dcs_pending);
+
+ /* Trigger a startup sequence */
+ wait_for_dc_servo(codec, wm8996->dcs_pending
+ << WM8996_DCS_TRIG_STARTUP_0_SHIFT);
+
+ wm8996->dcs_pending = 0;
+ }
+
+ if (wm8996->hpout_pending != wm8996->hpout_ena) {
+ dev_dbg(codec->dev, "Applying RMV_SHORTs %x->%x\n",
+ wm8996->hpout_ena, wm8996->hpout_pending);
+
+ val = 0;
+ mask = 0;
+ if (wm8996->hpout_pending & HPOUT1L) {
+ val |= WM8996_HPOUT1L_RMV_SHORT | WM8996_HPOUT1L_OUTP;
+ mask |= WM8996_HPOUT1L_RMV_SHORT | WM8996_HPOUT1L_OUTP;
+ } else {
+ mask |= WM8996_HPOUT1L_RMV_SHORT |
+ WM8996_HPOUT1L_OUTP |
+ WM8996_HPOUT1L_DLY;
+ }
+
+ if (wm8996->hpout_pending & HPOUT1R) {
+ val |= WM8996_HPOUT1R_RMV_SHORT | WM8996_HPOUT1R_OUTP;
+ mask |= WM8996_HPOUT1R_RMV_SHORT | WM8996_HPOUT1R_OUTP;
+ } else {
+ mask |= WM8996_HPOUT1R_RMV_SHORT |
+ WM8996_HPOUT1R_OUTP |
+ WM8996_HPOUT1R_DLY;
+ }
+
+ snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1, mask, val);
+
+ val = 0;
+ mask = 0;
+ if (wm8996->hpout_pending & HPOUT2L) {
+ val |= WM8996_HPOUT2L_RMV_SHORT | WM8996_HPOUT2L_OUTP;
+ mask |= WM8996_HPOUT2L_RMV_SHORT | WM8996_HPOUT2L_OUTP;
+ } else {
+ mask |= WM8996_HPOUT2L_RMV_SHORT |
+ WM8996_HPOUT2L_OUTP |
+ WM8996_HPOUT2L_DLY;
+ }
+
+ if (wm8996->hpout_pending & HPOUT2R) {
+ val |= WM8996_HPOUT2R_RMV_SHORT | WM8996_HPOUT2R_OUTP;
+ mask |= WM8996_HPOUT2R_RMV_SHORT | WM8996_HPOUT2R_OUTP;
+ } else {
+ mask |= WM8996_HPOUT2R_RMV_SHORT |
+ WM8996_HPOUT2R_OUTP |
+ WM8996_HPOUT2R_DLY;
+ }
+
+ snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_2, mask, val);
+
+ wm8996->hpout_ena = wm8996->hpout_pending;
+ }
+}
+
+static int dcs_start(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ wm8996->dcs_pending |= 1 << w->shift;
+ break;
+ default:
+ WARN(1, "Invalid event %d\n", event);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char *sidetone_text[] = {
+ "IN1", "IN2",
+};
+
+static SOC_ENUM_SINGLE_DECL(left_sidetone_enum,
+ WM8996_SIDETONE, 0, sidetone_text);
+
+static const struct snd_kcontrol_new left_sidetone =
+ SOC_DAPM_ENUM("Left Sidetone", left_sidetone_enum);
+
+static SOC_ENUM_SINGLE_DECL(right_sidetone_enum,
+ WM8996_SIDETONE, 1, sidetone_text);
+
+static const struct snd_kcontrol_new right_sidetone =
+ SOC_DAPM_ENUM("Right Sidetone", right_sidetone_enum);
+
+static const char *spk_text[] = {
+ "DAC1L", "DAC1R", "DAC2L", "DAC2R"
+};
+
+static SOC_ENUM_SINGLE_DECL(spkl_enum,
+ WM8996_LEFT_PDM_SPEAKER, 0, spk_text);
+
+static const struct snd_kcontrol_new spkl_mux =
+ SOC_DAPM_ENUM("SPKL", spkl_enum);
+
+static SOC_ENUM_SINGLE_DECL(spkr_enum,
+ WM8996_RIGHT_PDM_SPEAKER, 0, spk_text);
+
+static const struct snd_kcontrol_new spkr_mux =
+ SOC_DAPM_ENUM("SPKR", spkr_enum);
+
+static const char *dsp1rx_text[] = {
+ "AIF1", "AIF2"
+};
+
+static SOC_ENUM_SINGLE_DECL(dsp1rx_enum,
+ WM8996_POWER_MANAGEMENT_8, 0, dsp1rx_text);
+
+static const struct snd_kcontrol_new dsp1rx =
+ SOC_DAPM_ENUM("DSP1RX", dsp1rx_enum);
+
+static const char *dsp2rx_text[] = {
+ "AIF2", "AIF1"
+};
+
+static SOC_ENUM_SINGLE_DECL(dsp2rx_enum,
+ WM8996_POWER_MANAGEMENT_8, 4, dsp2rx_text);
+
+static const struct snd_kcontrol_new dsp2rx =
+ SOC_DAPM_ENUM("DSP2RX", dsp2rx_enum);
+
+static const char *aif2tx_text[] = {
+ "DSP2", "DSP1", "AIF1"
+};
+
+static SOC_ENUM_SINGLE_DECL(aif2tx_enum,
+ WM8996_POWER_MANAGEMENT_8, 6, aif2tx_text);
+
+static const struct snd_kcontrol_new aif2tx =
+ SOC_DAPM_ENUM("AIF2TX", aif2tx_enum);
+
+static const char *inmux_text[] = {
+ "ADC", "DMIC1", "DMIC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(in1_enum,
+ WM8996_POWER_MANAGEMENT_7, 0, inmux_text);
+
+static const struct snd_kcontrol_new in1_mux =
+ SOC_DAPM_ENUM("IN1 Mux", in1_enum);
+
+static SOC_ENUM_SINGLE_DECL(in2_enum,
+ WM8996_POWER_MANAGEMENT_7, 4, inmux_text);
+
+static const struct snd_kcontrol_new in2_mux =
+ SOC_DAPM_ENUM("IN2 Mux", in2_enum);
+
+static const struct snd_kcontrol_new dac2r_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac2l_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC2_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC2_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC2_LEFT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC2_LEFT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1r_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1l_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC1_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC1_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC1_LEFT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC1_LEFT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp1txl[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP1_TX_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP1_TX_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp1txr[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP1_TX_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP1_TX_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp2txl[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP2_TX_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP2_TX_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp2txr[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP2_TX_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP2_TX_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+
+static const struct snd_soc_dapm_widget wm8996_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1LN"),
+SND_SOC_DAPM_INPUT("IN1LP"),
+SND_SOC_DAPM_INPUT("IN1RN"),
+SND_SOC_DAPM_INPUT("IN1RP"),
+
+SND_SOC_DAPM_INPUT("IN2LN"),
+SND_SOC_DAPM_INPUT("IN2LP"),
+SND_SOC_DAPM_INPUT("IN2RN"),
+SND_SOC_DAPM_INPUT("IN2RP"),
+
+SND_SOC_DAPM_INPUT("DMIC1DAT"),
+SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8996_AIF_CLOCKING_1, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8996_CHARGE_PUMP_1, 15, 0, cp_event,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_SUPPLY("Bandgap", SND_SOC_NOPM, 0, 0, bg_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICB1 Audio", WM8996_MICBIAS_1, 4, 1, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICB2 Audio", WM8996_MICBIAS_2, 4, 1, NULL, 0),
+SND_SOC_DAPM_MICBIAS("MICB2", WM8996_POWER_MANAGEMENT_1, 9, 0),
+SND_SOC_DAPM_MICBIAS("MICB1", WM8996_POWER_MANAGEMENT_1, 8, 0),
+
+SND_SOC_DAPM_PGA("IN1L PGA", WM8996_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN1R PGA", WM8996_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("IN1L Mux", WM8996_POWER_MANAGEMENT_7, 2, 0, &in1_mux),
+SND_SOC_DAPM_MUX("IN1R Mux", WM8996_POWER_MANAGEMENT_7, 3, 0, &in1_mux),
+SND_SOC_DAPM_MUX("IN2L Mux", WM8996_POWER_MANAGEMENT_7, 6, 0, &in2_mux),
+SND_SOC_DAPM_MUX("IN2R Mux", WM8996_POWER_MANAGEMENT_7, 7, 0, &in2_mux),
+
+SND_SOC_DAPM_SUPPLY("DMIC2", WM8996_POWER_MANAGEMENT_7, 9, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DMIC1", WM8996_POWER_MANAGEMENT_7, 8, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8996_POWER_MANAGEMENT_3, 5, 0),
+SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8996_POWER_MANAGEMENT_3, 4, 0),
+SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8996_POWER_MANAGEMENT_3, 3, 0),
+SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8996_POWER_MANAGEMENT_3, 2, 0),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8996_POWER_MANAGEMENT_3, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8996_POWER_MANAGEMENT_3, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &left_sidetone),
+SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &right_sidetone),
+
+SND_SOC_DAPM_AIF_IN("DSP2RXL", NULL, 0, WM8996_POWER_MANAGEMENT_3, 11, 0),
+SND_SOC_DAPM_AIF_IN("DSP2RXR", NULL, 1, WM8996_POWER_MANAGEMENT_3, 10, 0),
+SND_SOC_DAPM_AIF_IN("DSP1RXL", NULL, 0, WM8996_POWER_MANAGEMENT_3, 9, 0),
+SND_SOC_DAPM_AIF_IN("DSP1RXR", NULL, 1, WM8996_POWER_MANAGEMENT_3, 8, 0),
+
+SND_SOC_DAPM_MIXER("DSP2TXL", WM8996_POWER_MANAGEMENT_5, 11, 0,
+ dsp2txl, ARRAY_SIZE(dsp2txl)),
+SND_SOC_DAPM_MIXER("DSP2TXR", WM8996_POWER_MANAGEMENT_5, 10, 0,
+ dsp2txr, ARRAY_SIZE(dsp2txr)),
+SND_SOC_DAPM_MIXER("DSP1TXL", WM8996_POWER_MANAGEMENT_5, 9, 0,
+ dsp1txl, ARRAY_SIZE(dsp1txl)),
+SND_SOC_DAPM_MIXER("DSP1TXR", WM8996_POWER_MANAGEMENT_5, 8, 0,
+ dsp1txr, ARRAY_SIZE(dsp1txr)),
+
+SND_SOC_DAPM_MIXER("DAC2L Mixer", SND_SOC_NOPM, 0, 0,
+ dac2l_mix, ARRAY_SIZE(dac2l_mix)),
+SND_SOC_DAPM_MIXER("DAC2R Mixer", SND_SOC_NOPM, 0, 0,
+ dac2r_mix, ARRAY_SIZE(dac2r_mix)),
+SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0,
+ dac1l_mix, ARRAY_SIZE(dac1l_mix)),
+SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0,
+ dac1r_mix, ARRAY_SIZE(dac1r_mix)),
+
+SND_SOC_DAPM_DAC("DAC2L", NULL, WM8996_POWER_MANAGEMENT_5, 3, 0),
+SND_SOC_DAPM_DAC("DAC2R", NULL, WM8996_POWER_MANAGEMENT_5, 2, 0),
+SND_SOC_DAPM_DAC("DAC1L", NULL, WM8996_POWER_MANAGEMENT_5, 1, 0),
+SND_SOC_DAPM_DAC("DAC1R", NULL, WM8996_POWER_MANAGEMENT_5, 0, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, WM8996_POWER_MANAGEMENT_4, 9, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX0", NULL, 1, WM8996_POWER_MANAGEMENT_4, 8, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, WM8996_POWER_MANAGEMENT_6, 9, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX0", NULL, 1, WM8996_POWER_MANAGEMENT_6, 8, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 5, WM8996_POWER_MANAGEMENT_4, 5, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 4, WM8996_POWER_MANAGEMENT_4, 4, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 3, WM8996_POWER_MANAGEMENT_4, 3, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 2, WM8996_POWER_MANAGEMENT_4, 2, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 1, WM8996_POWER_MANAGEMENT_4, 1, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX0", NULL, 0, WM8996_POWER_MANAGEMENT_4, 0, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 5, WM8996_POWER_MANAGEMENT_6, 5, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 4, WM8996_POWER_MANAGEMENT_6, 4, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 3, WM8996_POWER_MANAGEMENT_6, 3, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 2, WM8996_POWER_MANAGEMENT_6, 2, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 1, WM8996_POWER_MANAGEMENT_6, 1, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX0", NULL, 0, WM8996_POWER_MANAGEMENT_6, 0, 0),
+
+/* We route as stereo pairs so define some dummy widgets to squash
+ * things down for now. RXA = 0,1, RXB = 2,3 and so on */
+SND_SOC_DAPM_PGA("AIF1RXA", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF1RXB", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF1RXC", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF2RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DSP2TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("DSP1RX", SND_SOC_NOPM, 0, 0, &dsp1rx),
+SND_SOC_DAPM_MUX("DSP2RX", SND_SOC_NOPM, 0, 0, &dsp2rx),
+SND_SOC_DAPM_MUX("AIF2TX", SND_SOC_NOPM, 0, 0, &aif2tx),
+
+SND_SOC_DAPM_MUX("SPKL", SND_SOC_NOPM, 0, 0, &spkl_mux),
+SND_SOC_DAPM_MUX("SPKR", SND_SOC_NOPM, 0, 0, &spkr_mux),
+SND_SOC_DAPM_PGA("SPKL PGA", WM8996_LEFT_PDM_SPEAKER, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPKR PGA", WM8996_RIGHT_PDM_SPEAKER, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("HPOUT2L PGA", 0, WM8996_POWER_MANAGEMENT_1, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_DLY", 1, WM8996_ANALOGUE_HP_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_DCS", 2, WM8996_DC_SERVO_1, 2, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT2L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2L, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT2R PGA", 0, WM8996_POWER_MANAGEMENT_1, 6, 0,NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_DLY", 1, WM8996_ANALOGUE_HP_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_DCS", 2, WM8996_DC_SERVO_1, 3, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT2R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2R, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT1L PGA", 0, WM8996_POWER_MANAGEMENT_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_DLY", 1, WM8996_ANALOGUE_HP_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_DCS", 2, WM8996_DC_SERVO_1, 0, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT1L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1L, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT1R PGA", 0, WM8996_POWER_MANAGEMENT_1, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_DLY", 1, WM8996_ANALOGUE_HP_1, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_DCS", 2, WM8996_DC_SERVO_1, 1, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT1R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1R, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("SPKDAT"),
+};
+
+static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
+ { "AIFCLK", NULL, "SYSCLK" },
+ { "SYSDSPCLK", NULL, "SYSCLK" },
+ { "Charge Pump", NULL, "SYSCLK" },
+ { "Charge Pump", NULL, "CPVDD" },
+
+ { "MICB1", NULL, "LDO2" },
+ { "MICB1", NULL, "MICB1 Audio" },
+ { "MICB1", NULL, "Bandgap" },
+ { "MICB2", NULL, "LDO2" },
+ { "MICB2", NULL, "MICB2 Audio" },
+ { "MICB2", NULL, "Bandgap" },
+
+ { "AIF1RX0", NULL, "AIF1 Playback" },
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+
+ { "AIF2RX0", NULL, "AIF2 Playback" },
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+
+ { "AIF1 Capture", NULL, "AIF1TX0" },
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+
+ { "AIF2 Capture", NULL, "AIF2TX0" },
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+
+ { "IN1L PGA", NULL, "IN2LN" },
+ { "IN1L PGA", NULL, "IN2LP" },
+ { "IN1L PGA", NULL, "IN1LN" },
+ { "IN1L PGA", NULL, "IN1LP" },
+ { "IN1L PGA", NULL, "Bandgap" },
+
+ { "IN1R PGA", NULL, "IN2RN" },
+ { "IN1R PGA", NULL, "IN2RP" },
+ { "IN1R PGA", NULL, "IN1RN" },
+ { "IN1R PGA", NULL, "IN1RP" },
+ { "IN1R PGA", NULL, "Bandgap" },
+
+ { "ADCL", NULL, "IN1L PGA" },
+
+ { "ADCR", NULL, "IN1R PGA" },
+
+ { "DMIC1L", NULL, "DMIC1DAT" },
+ { "DMIC1R", NULL, "DMIC1DAT" },
+ { "DMIC2L", NULL, "DMIC2DAT" },
+ { "DMIC2R", NULL, "DMIC2DAT" },
+
+ { "DMIC2L", NULL, "DMIC2" },
+ { "DMIC2R", NULL, "DMIC2" },
+ { "DMIC1L", NULL, "DMIC1" },
+ { "DMIC1R", NULL, "DMIC1" },
+
+ { "IN1L Mux", "ADC", "ADCL" },
+ { "IN1L Mux", "DMIC1", "DMIC1L" },
+ { "IN1L Mux", "DMIC2", "DMIC2L" },
+
+ { "IN1R Mux", "ADC", "ADCR" },
+ { "IN1R Mux", "DMIC1", "DMIC1R" },
+ { "IN1R Mux", "DMIC2", "DMIC2R" },
+
+ { "IN2L Mux", "ADC", "ADCL" },
+ { "IN2L Mux", "DMIC1", "DMIC1L" },
+ { "IN2L Mux", "DMIC2", "DMIC2L" },
+
+ { "IN2R Mux", "ADC", "ADCR" },
+ { "IN2R Mux", "DMIC1", "DMIC1R" },
+ { "IN2R Mux", "DMIC2", "DMIC2R" },
+
+ { "Left Sidetone", "IN1", "IN1L Mux" },
+ { "Left Sidetone", "IN2", "IN2L Mux" },
+
+ { "Right Sidetone", "IN1", "IN1R Mux" },
+ { "Right Sidetone", "IN2", "IN2R Mux" },
+
+ { "DSP1TXL", "IN1 Switch", "IN1L Mux" },
+ { "DSP1TXR", "IN1 Switch", "IN1R Mux" },
+
+ { "DSP2TXL", "IN1 Switch", "IN2L Mux" },
+ { "DSP2TXR", "IN1 Switch", "IN2R Mux" },
+
+ { "AIF1TX0", NULL, "DSP1TXL" },
+ { "AIF1TX1", NULL, "DSP1TXR" },
+ { "AIF1TX2", NULL, "DSP2TXL" },
+ { "AIF1TX3", NULL, "DSP2TXR" },
+ { "AIF1TX4", NULL, "AIF2RX0" },
+ { "AIF1TX5", NULL, "AIF2RX1" },
+
+ { "AIF1RX0", NULL, "AIFCLK" },
+ { "AIF1RX1", NULL, "AIFCLK" },
+ { "AIF1RX2", NULL, "AIFCLK" },
+ { "AIF1RX3", NULL, "AIFCLK" },
+ { "AIF1RX4", NULL, "AIFCLK" },
+ { "AIF1RX5", NULL, "AIFCLK" },
+
+ { "AIF2RX0", NULL, "AIFCLK" },
+ { "AIF2RX1", NULL, "AIFCLK" },
+
+ { "AIF1TX0", NULL, "AIFCLK" },
+ { "AIF1TX1", NULL, "AIFCLK" },
+ { "AIF1TX2", NULL, "AIFCLK" },
+ { "AIF1TX3", NULL, "AIFCLK" },
+ { "AIF1TX4", NULL, "AIFCLK" },
+ { "AIF1TX5", NULL, "AIFCLK" },
+
+ { "AIF2TX0", NULL, "AIFCLK" },
+ { "AIF2TX1", NULL, "AIFCLK" },
+
+ { "DSP1RXL", NULL, "SYSDSPCLK" },
+ { "DSP1RXR", NULL, "SYSDSPCLK" },
+ { "DSP2RXL", NULL, "SYSDSPCLK" },
+ { "DSP2RXR", NULL, "SYSDSPCLK" },
+ { "DSP1TXL", NULL, "SYSDSPCLK" },
+ { "DSP1TXR", NULL, "SYSDSPCLK" },
+ { "DSP2TXL", NULL, "SYSDSPCLK" },
+ { "DSP2TXR", NULL, "SYSDSPCLK" },
+
+ { "AIF1RXA", NULL, "AIF1RX0" },
+ { "AIF1RXA", NULL, "AIF1RX1" },
+ { "AIF1RXB", NULL, "AIF1RX2" },
+ { "AIF1RXB", NULL, "AIF1RX3" },
+ { "AIF1RXC", NULL, "AIF1RX4" },
+ { "AIF1RXC", NULL, "AIF1RX5" },
+
+ { "AIF2RX", NULL, "AIF2RX0" },
+ { "AIF2RX", NULL, "AIF2RX1" },
+
+ { "AIF2TX", "DSP2", "DSP2TX" },
+ { "AIF2TX", "DSP1", "DSP1RX" },
+ { "AIF2TX", "AIF1", "AIF1RXC" },
+
+ { "DSP1RXL", NULL, "DSP1RX" },
+ { "DSP1RXR", NULL, "DSP1RX" },
+ { "DSP2RXL", NULL, "DSP2RX" },
+ { "DSP2RXR", NULL, "DSP2RX" },
+
+ { "DSP2TX", NULL, "DSP2TXL" },
+ { "DSP2TX", NULL, "DSP2TXR" },
+
+ { "DSP1RX", "AIF1", "AIF1RXA" },
+ { "DSP1RX", "AIF2", "AIF2RX" },
+
+ { "DSP2RX", "AIF1", "AIF1RXB" },
+ { "DSP2RX", "AIF2", "AIF2RX" },
+
+ { "DAC2L Mixer", "DSP2 Switch", "DSP2RXL" },
+ { "DAC2L Mixer", "DSP1 Switch", "DSP1RXL" },
+ { "DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC2L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC2R Mixer", "DSP2 Switch", "DSP2RXR" },
+ { "DAC2R Mixer", "DSP1 Switch", "DSP1RXR" },
+ { "DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1L Mixer", "DSP2 Switch", "DSP2RXL" },
+ { "DAC1L Mixer", "DSP1 Switch", "DSP1RXL" },
+ { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1R Mixer", "DSP2 Switch", "DSP2RXR" },
+ { "DAC1R Mixer", "DSP1 Switch", "DSP1RXR" },
+ { "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1L", NULL, "DAC1L Mixer" },
+ { "DAC1R", NULL, "DAC1R Mixer" },
+ { "DAC2L", NULL, "DAC2L Mixer" },
+ { "DAC2R", NULL, "DAC2R Mixer" },
+
+ { "HPOUT2L PGA", NULL, "Charge Pump" },
+ { "HPOUT2L PGA", NULL, "Bandgap" },
+ { "HPOUT2L PGA", NULL, "DAC2L" },
+ { "HPOUT2L_DLY", NULL, "HPOUT2L PGA" },
+ { "HPOUT2L_DCS", NULL, "HPOUT2L_DLY" },
+ { "HPOUT2L_RMV_SHORT", NULL, "HPOUT2L_DCS" },
+
+ { "HPOUT2R PGA", NULL, "Charge Pump" },
+ { "HPOUT2R PGA", NULL, "Bandgap" },
+ { "HPOUT2R PGA", NULL, "DAC2R" },
+ { "HPOUT2R_DLY", NULL, "HPOUT2R PGA" },
+ { "HPOUT2R_DCS", NULL, "HPOUT2R_DLY" },
+ { "HPOUT2R_RMV_SHORT", NULL, "HPOUT2R_DCS" },
+
+ { "HPOUT1L PGA", NULL, "Charge Pump" },
+ { "HPOUT1L PGA", NULL, "Bandgap" },
+ { "HPOUT1L PGA", NULL, "DAC1L" },
+ { "HPOUT1L_DLY", NULL, "HPOUT1L PGA" },
+ { "HPOUT1L_DCS", NULL, "HPOUT1L_DLY" },
+ { "HPOUT1L_RMV_SHORT", NULL, "HPOUT1L_DCS" },
+
+ { "HPOUT1R PGA", NULL, "Charge Pump" },
+ { "HPOUT1R PGA", NULL, "Bandgap" },
+ { "HPOUT1R PGA", NULL, "DAC1R" },
+ { "HPOUT1R_DLY", NULL, "HPOUT1R PGA" },
+ { "HPOUT1R_DCS", NULL, "HPOUT1R_DLY" },
+ { "HPOUT1R_RMV_SHORT", NULL, "HPOUT1R_DCS" },
+
+ { "HPOUT2L", NULL, "HPOUT2L_RMV_SHORT" },
+ { "HPOUT2R", NULL, "HPOUT2R_RMV_SHORT" },
+ { "HPOUT1L", NULL, "HPOUT1L_RMV_SHORT" },
+ { "HPOUT1R", NULL, "HPOUT1R_RMV_SHORT" },
+
+ { "SPKL", "DAC1L", "DAC1L" },
+ { "SPKL", "DAC1R", "DAC1R" },
+ { "SPKL", "DAC2L", "DAC2L" },
+ { "SPKL", "DAC2R", "DAC2R" },
+
+ { "SPKR", "DAC1L", "DAC1L" },
+ { "SPKR", "DAC1R", "DAC1R" },
+ { "SPKR", "DAC2L", "DAC2L" },
+ { "SPKR", "DAC2R", "DAC2R" },
+
+ { "SPKL PGA", NULL, "SPKL" },
+ { "SPKR PGA", NULL, "SPKR" },
+
+ { "SPKDAT", NULL, "SPKL PGA" },
+ { "SPKDAT", NULL, "SPKR PGA" },
+};
+
+static bool wm8996_readable_register(struct device *dev, unsigned int reg)
+{
+ /* Due to the sparseness of the register map the compiler
+ * output from an explicit switch statement ends up being much
+ * more efficient than a table.
+ */
+ switch (reg) {
+ case WM8996_SOFTWARE_RESET:
+ case WM8996_POWER_MANAGEMENT_1:
+ case WM8996_POWER_MANAGEMENT_2:
+ case WM8996_POWER_MANAGEMENT_3:
+ case WM8996_POWER_MANAGEMENT_4:
+ case WM8996_POWER_MANAGEMENT_5:
+ case WM8996_POWER_MANAGEMENT_6:
+ case WM8996_POWER_MANAGEMENT_7:
+ case WM8996_POWER_MANAGEMENT_8:
+ case WM8996_LEFT_LINE_INPUT_VOLUME:
+ case WM8996_RIGHT_LINE_INPUT_VOLUME:
+ case WM8996_LINE_INPUT_CONTROL:
+ case WM8996_DAC1_HPOUT1_VOLUME:
+ case WM8996_DAC2_HPOUT2_VOLUME:
+ case WM8996_DAC1_LEFT_VOLUME:
+ case WM8996_DAC1_RIGHT_VOLUME:
+ case WM8996_DAC2_LEFT_VOLUME:
+ case WM8996_DAC2_RIGHT_VOLUME:
+ case WM8996_OUTPUT1_LEFT_VOLUME:
+ case WM8996_OUTPUT1_RIGHT_VOLUME:
+ case WM8996_OUTPUT2_LEFT_VOLUME:
+ case WM8996_OUTPUT2_RIGHT_VOLUME:
+ case WM8996_MICBIAS_1:
+ case WM8996_MICBIAS_2:
+ case WM8996_LDO_1:
+ case WM8996_LDO_2:
+ case WM8996_ACCESSORY_DETECT_MODE_1:
+ case WM8996_ACCESSORY_DETECT_MODE_2:
+ case WM8996_HEADPHONE_DETECT_1:
+ case WM8996_HEADPHONE_DETECT_2:
+ case WM8996_MIC_DETECT_1:
+ case WM8996_MIC_DETECT_2:
+ case WM8996_MIC_DETECT_3:
+ case WM8996_CHARGE_PUMP_1:
+ case WM8996_CHARGE_PUMP_2:
+ case WM8996_DC_SERVO_1:
+ case WM8996_DC_SERVO_2:
+ case WM8996_DC_SERVO_3:
+ case WM8996_DC_SERVO_5:
+ case WM8996_DC_SERVO_6:
+ case WM8996_DC_SERVO_7:
+ case WM8996_DC_SERVO_READBACK_0:
+ case WM8996_ANALOGUE_HP_1:
+ case WM8996_ANALOGUE_HP_2:
+ case WM8996_CHIP_REVISION:
+ case WM8996_CONTROL_INTERFACE_1:
+ case WM8996_WRITE_SEQUENCER_CTRL_1:
+ case WM8996_WRITE_SEQUENCER_CTRL_2:
+ case WM8996_AIF_CLOCKING_1:
+ case WM8996_AIF_CLOCKING_2:
+ case WM8996_CLOCKING_1:
+ case WM8996_CLOCKING_2:
+ case WM8996_AIF_RATE:
+ case WM8996_FLL_CONTROL_1:
+ case WM8996_FLL_CONTROL_2:
+ case WM8996_FLL_CONTROL_3:
+ case WM8996_FLL_CONTROL_4:
+ case WM8996_FLL_CONTROL_5:
+ case WM8996_FLL_CONTROL_6:
+ case WM8996_FLL_EFS_1:
+ case WM8996_FLL_EFS_2:
+ case WM8996_AIF1_CONTROL:
+ case WM8996_AIF1_BCLK:
+ case WM8996_AIF1_TX_LRCLK_1:
+ case WM8996_AIF1_TX_LRCLK_2:
+ case WM8996_AIF1_RX_LRCLK_1:
+ case WM8996_AIF1_RX_LRCLK_2:
+ case WM8996_AIF1TX_DATA_CONFIGURATION_1:
+ case WM8996_AIF1TX_DATA_CONFIGURATION_2:
+ case WM8996_AIF1RX_DATA_CONFIGURATION:
+ case WM8996_AIF1TX_CHANNEL_0_CONFIGURATION:
+ case WM8996_AIF1TX_CHANNEL_1_CONFIGURATION:
+ case WM8996_AIF1TX_CHANNEL_2_CONFIGURATION:
+ case WM8996_AIF1TX_CHANNEL_3_CONFIGURATION:
+ case WM8996_AIF1TX_CHANNEL_4_CONFIGURATION:
+ case WM8996_AIF1TX_CHANNEL_5_CONFIGURATION:
+ case WM8996_AIF1RX_CHANNEL_0_CONFIGURATION:
+ case WM8996_AIF1RX_CHANNEL_1_CONFIGURATION:
+ case WM8996_AIF1RX_CHANNEL_2_CONFIGURATION:
+ case WM8996_AIF1RX_CHANNEL_3_CONFIGURATION:
+ case WM8996_AIF1RX_CHANNEL_4_CONFIGURATION:
+ case WM8996_AIF1RX_CHANNEL_5_CONFIGURATION:
+ case WM8996_AIF1RX_MONO_CONFIGURATION:
+ case WM8996_AIF1TX_TEST:
+ case WM8996_AIF2_CONTROL:
+ case WM8996_AIF2_BCLK:
+ case WM8996_AIF2_TX_LRCLK_1:
+ case WM8996_AIF2_TX_LRCLK_2:
+ case WM8996_AIF2_RX_LRCLK_1:
+ case WM8996_AIF2_RX_LRCLK_2:
+ case WM8996_AIF2TX_DATA_CONFIGURATION_1:
+ case WM8996_AIF2TX_DATA_CONFIGURATION_2:
+ case WM8996_AIF2RX_DATA_CONFIGURATION:
+ case WM8996_AIF2TX_CHANNEL_0_CONFIGURATION:
+ case WM8996_AIF2TX_CHANNEL_1_CONFIGURATION:
+ case WM8996_AIF2RX_CHANNEL_0_CONFIGURATION:
+ case WM8996_AIF2RX_CHANNEL_1_CONFIGURATION:
+ case WM8996_AIF2RX_MONO_CONFIGURATION:
+ case WM8996_AIF2TX_TEST:
+ case WM8996_DSP1_TX_LEFT_VOLUME:
+ case WM8996_DSP1_TX_RIGHT_VOLUME:
+ case WM8996_DSP1_RX_LEFT_VOLUME:
+ case WM8996_DSP1_RX_RIGHT_VOLUME:
+ case WM8996_DSP1_TX_FILTERS:
+ case WM8996_DSP1_RX_FILTERS_1:
+ case WM8996_DSP1_RX_FILTERS_2:
+ case WM8996_DSP1_DRC_1:
+ case WM8996_DSP1_DRC_2:
+ case WM8996_DSP1_DRC_3:
+ case WM8996_DSP1_DRC_4:
+ case WM8996_DSP1_DRC_5:
+ case WM8996_DSP1_RX_EQ_GAINS_1:
+ case WM8996_DSP1_RX_EQ_GAINS_2:
+ case WM8996_DSP1_RX_EQ_BAND_1_A:
+ case WM8996_DSP1_RX_EQ_BAND_1_B:
+ case WM8996_DSP1_RX_EQ_BAND_1_PG:
+ case WM8996_DSP1_RX_EQ_BAND_2_A:
+ case WM8996_DSP1_RX_EQ_BAND_2_B:
+ case WM8996_DSP1_RX_EQ_BAND_2_C:
+ case WM8996_DSP1_RX_EQ_BAND_2_PG:
+ case WM8996_DSP1_RX_EQ_BAND_3_A:
+ case WM8996_DSP1_RX_EQ_BAND_3_B:
+ case WM8996_DSP1_RX_EQ_BAND_3_C:
+ case WM8996_DSP1_RX_EQ_BAND_3_PG:
+ case WM8996_DSP1_RX_EQ_BAND_4_A:
+ case WM8996_DSP1_RX_EQ_BAND_4_B:
+ case WM8996_DSP1_RX_EQ_BAND_4_C:
+ case WM8996_DSP1_RX_EQ_BAND_4_PG:
+ case WM8996_DSP1_RX_EQ_BAND_5_A:
+ case WM8996_DSP1_RX_EQ_BAND_5_B:
+ case WM8996_DSP1_RX_EQ_BAND_5_PG:
+ case WM8996_DSP2_TX_LEFT_VOLUME:
+ case WM8996_DSP2_TX_RIGHT_VOLUME:
+ case WM8996_DSP2_RX_LEFT_VOLUME:
+ case WM8996_DSP2_RX_RIGHT_VOLUME:
+ case WM8996_DSP2_TX_FILTERS:
+ case WM8996_DSP2_RX_FILTERS_1:
+ case WM8996_DSP2_RX_FILTERS_2:
+ case WM8996_DSP2_DRC_1:
+ case WM8996_DSP2_DRC_2:
+ case WM8996_DSP2_DRC_3:
+ case WM8996_DSP2_DRC_4:
+ case WM8996_DSP2_DRC_5:
+ case WM8996_DSP2_RX_EQ_GAINS_1:
+ case WM8996_DSP2_RX_EQ_GAINS_2:
+ case WM8996_DSP2_RX_EQ_BAND_1_A:
+ case WM8996_DSP2_RX_EQ_BAND_1_B:
+ case WM8996_DSP2_RX_EQ_BAND_1_PG:
+ case WM8996_DSP2_RX_EQ_BAND_2_A:
+ case WM8996_DSP2_RX_EQ_BAND_2_B:
+ case WM8996_DSP2_RX_EQ_BAND_2_C:
+ case WM8996_DSP2_RX_EQ_BAND_2_PG:
+ case WM8996_DSP2_RX_EQ_BAND_3_A:
+ case WM8996_DSP2_RX_EQ_BAND_3_B:
+ case WM8996_DSP2_RX_EQ_BAND_3_C:
+ case WM8996_DSP2_RX_EQ_BAND_3_PG:
+ case WM8996_DSP2_RX_EQ_BAND_4_A:
+ case WM8996_DSP2_RX_EQ_BAND_4_B:
+ case WM8996_DSP2_RX_EQ_BAND_4_C:
+ case WM8996_DSP2_RX_EQ_BAND_4_PG:
+ case WM8996_DSP2_RX_EQ_BAND_5_A:
+ case WM8996_DSP2_RX_EQ_BAND_5_B:
+ case WM8996_DSP2_RX_EQ_BAND_5_PG:
+ case WM8996_DAC1_MIXER_VOLUMES:
+ case WM8996_DAC1_LEFT_MIXER_ROUTING:
+ case WM8996_DAC1_RIGHT_MIXER_ROUTING:
+ case WM8996_DAC2_MIXER_VOLUMES:
+ case WM8996_DAC2_LEFT_MIXER_ROUTING:
+ case WM8996_DAC2_RIGHT_MIXER_ROUTING:
+ case WM8996_DSP1_TX_LEFT_MIXER_ROUTING:
+ case WM8996_DSP1_TX_RIGHT_MIXER_ROUTING:
+ case WM8996_DSP2_TX_LEFT_MIXER_ROUTING:
+ case WM8996_DSP2_TX_RIGHT_MIXER_ROUTING:
+ case WM8996_DSP_TX_MIXER_SELECT:
+ case WM8996_DAC_SOFTMUTE:
+ case WM8996_OVERSAMPLING:
+ case WM8996_SIDETONE:
+ case WM8996_GPIO_1:
+ case WM8996_GPIO_2:
+ case WM8996_GPIO_3:
+ case WM8996_GPIO_4:
+ case WM8996_GPIO_5:
+ case WM8996_PULL_CONTROL_1:
+ case WM8996_PULL_CONTROL_2:
+ case WM8996_INTERRUPT_STATUS_1:
+ case WM8996_INTERRUPT_STATUS_2:
+ case WM8996_INTERRUPT_RAW_STATUS_2:
+ case WM8996_INTERRUPT_STATUS_1_MASK:
+ case WM8996_INTERRUPT_STATUS_2_MASK:
+ case WM8996_INTERRUPT_CONTROL:
+ case WM8996_LEFT_PDM_SPEAKER:
+ case WM8996_RIGHT_PDM_SPEAKER:
+ case WM8996_PDM_SPEAKER_MUTE_SEQUENCE:
+ case WM8996_PDM_SPEAKER_VOLUME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static bool wm8996_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8996_SOFTWARE_RESET:
+ case WM8996_CHIP_REVISION:
+ case WM8996_LDO_1:
+ case WM8996_LDO_2:
+ case WM8996_INTERRUPT_STATUS_1:
+ case WM8996_INTERRUPT_STATUS_2:
+ case WM8996_INTERRUPT_RAW_STATUS_2:
+ case WM8996_DC_SERVO_READBACK_0:
+ case WM8996_DC_SERVO_2:
+ case WM8996_DC_SERVO_6:
+ case WM8996_DC_SERVO_7:
+ case WM8996_FLL_CONTROL_6:
+ case WM8996_MIC_DETECT_3:
+ case WM8996_HEADPHONE_DETECT_1:
+ case WM8996_HEADPHONE_DETECT_2:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static const int bclk_divs[] = {
+ 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
+};
+
+static void wm8996_update_bclk(struct snd_soc_codec *codec)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int aif, best, cur_val, bclk_rate, bclk_reg, i;
+
+ /* Don't bother if we're in a low frequency idle mode that
+ * can't support audio.
+ */
+ if (wm8996->sysclk < 64000)
+ return;
+
+ for (aif = 0; aif < WM8996_AIFS; aif++) {
+ switch (aif) {
+ case 0:
+ bclk_reg = WM8996_AIF1_BCLK;
+ break;
+ case 1:
+ bclk_reg = WM8996_AIF2_BCLK;
+ break;
+ }
+
+ bclk_rate = wm8996->bclk_rate[aif];
+
+ /* Pick a divisor for BCLK as close as we can get to ideal */
+ best = 0;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ cur_val = (wm8996->sysclk / bclk_divs[i]) - bclk_rate;
+ if (cur_val < 0) /* BCLK table is sorted */
+ break;
+ best = i;
+ }
+ bclk_rate = wm8996->sysclk / bclk_divs[best];
+ dev_dbg(codec->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
+ bclk_divs[best], bclk_rate);
+
+ snd_soc_update_bits(codec, bclk_reg,
+ WM8996_AIF1_BCLK_DIV_MASK, best);
+ }
+}
+
+static int wm8996_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* Put the MICBIASes into regulating mode */
+ snd_soc_update_bits(codec, WM8996_MICBIAS_1,
+ WM8996_MICB1_MODE, 0);
+ snd_soc_update_bits(codec, WM8996_MICBIAS_2,
+ WM8996_MICB2_MODE, 0);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies),
+ wm8996->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (wm8996->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8996->pdata.ldo_ena,
+ 1);
+ msleep(5);
+ }
+
+ regcache_cache_only(wm8996->regmap, false);
+ regcache_sync(wm8996->regmap);
+ }
+
+ /* Bypass the MICBIASes for lowest power */
+ snd_soc_update_bits(codec, WM8996_MICBIAS_1,
+ WM8996_MICB1_MODE, WM8996_MICB1_MODE);
+ snd_soc_update_bits(codec, WM8996_MICBIAS_2,
+ WM8996_MICB2_MODE, WM8996_MICB2_MODE);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ regcache_cache_only(wm8996->regmap, true);
+ if (wm8996->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
+ regcache_cache_only(wm8996->regmap, true);
+ }
+ regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies),
+ wm8996->supplies);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int wm8996_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int aifctrl = 0;
+ int bclk = 0;
+ int lrclk_tx = 0;
+ int lrclk_rx = 0;
+ int aifctrl_reg, bclk_reg, lrclk_tx_reg, lrclk_rx_reg;
+
+ switch (dai->id) {
+ case 0:
+ aifctrl_reg = WM8996_AIF1_CONTROL;
+ bclk_reg = WM8996_AIF1_BCLK;
+ lrclk_tx_reg = WM8996_AIF1_TX_LRCLK_2;
+ lrclk_rx_reg = WM8996_AIF1_RX_LRCLK_2;
+ break;
+ case 1:
+ aifctrl_reg = WM8996_AIF2_CONTROL;
+ bclk_reg = WM8996_AIF2_BCLK;
+ lrclk_tx_reg = WM8996_AIF2_TX_LRCLK_2;
+ lrclk_rx_reg = WM8996_AIF2_RX_LRCLK_2;
+ break;
+ default:
+ WARN(1, "Invalid dai id %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk |= WM8996_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk_tx |= WM8996_AIF1TX_LRCLK_INV;
+ lrclk_rx |= WM8996_AIF1RX_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk |= WM8996_AIF1_BCLK_INV;
+ lrclk_tx |= WM8996_AIF1TX_LRCLK_INV;
+ lrclk_rx |= WM8996_AIF1RX_LRCLK_INV;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ lrclk_tx |= WM8996_AIF1TX_LRCLK_MSTR;
+ lrclk_rx |= WM8996_AIF1RX_LRCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ bclk |= WM8996_AIF1_BCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ bclk |= WM8996_AIF1_BCLK_MSTR;
+ lrclk_tx |= WM8996_AIF1TX_LRCLK_MSTR;
+ lrclk_rx |= WM8996_AIF1RX_LRCLK_MSTR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aifctrl |= 1;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aifctrl |= 2;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aifctrl |= 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, aifctrl_reg, WM8996_AIF1_FMT_MASK, aifctrl);
+ snd_soc_update_bits(codec, bclk_reg,
+ WM8996_AIF1_BCLK_INV | WM8996_AIF1_BCLK_MSTR,
+ bclk);
+ snd_soc_update_bits(codec, lrclk_tx_reg,
+ WM8996_AIF1TX_LRCLK_INV |
+ WM8996_AIF1TX_LRCLK_MSTR,
+ lrclk_tx);
+ snd_soc_update_bits(codec, lrclk_rx_reg,
+ WM8996_AIF1RX_LRCLK_INV |
+ WM8996_AIF1RX_LRCLK_MSTR,
+ lrclk_rx);
+
+ return 0;
+}
+
+static const int dsp_divs[] = {
+ 48000, 32000, 16000, 8000
+};
+
+static int wm8996_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int bits, i, bclk_rate, best;
+ int aifdata = 0;
+ int lrclk = 0;
+ int dsp = 0;
+ int aifdata_reg, lrclk_reg, dsp_shift;
+
+ switch (dai->id) {
+ case 0:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ (snd_soc_read(codec, WM8996_GPIO_1)) & WM8996_GP1_FN_MASK) {
+ aifdata_reg = WM8996_AIF1RX_DATA_CONFIGURATION;
+ lrclk_reg = WM8996_AIF1_RX_LRCLK_1;
+ } else {
+ aifdata_reg = WM8996_AIF1TX_DATA_CONFIGURATION_1;
+ lrclk_reg = WM8996_AIF1_TX_LRCLK_1;
+ }
+ dsp_shift = 0;
+ break;
+ case 1:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ (snd_soc_read(codec, WM8996_GPIO_2)) & WM8996_GP2_FN_MASK) {
+ aifdata_reg = WM8996_AIF2RX_DATA_CONFIGURATION;
+ lrclk_reg = WM8996_AIF2_RX_LRCLK_1;
+ } else {
+ aifdata_reg = WM8996_AIF2TX_DATA_CONFIGURATION_1;
+ lrclk_reg = WM8996_AIF2_TX_LRCLK_1;
+ }
+ dsp_shift = WM8996_DSP2_DIV_SHIFT;
+ break;
+ default:
+ WARN(1, "Invalid dai id %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0) {
+ dev_err(codec->dev, "Unsupported BCLK rate: %d\n", bclk_rate);
+ return bclk_rate;
+ }
+
+ wm8996->bclk_rate[dai->id] = bclk_rate;
+ wm8996->rx_rate[dai->id] = params_rate(params);
+
+ /* Needs looking at for TDM */
+ bits = snd_pcm_format_width(params_format(params));
+ if (bits < 0)
+ return bits;
+ aifdata |= (bits << WM8996_AIF1TX_WL_SHIFT) | bits;
+
+ best = 0;
+ for (i = 0; i < ARRAY_SIZE(dsp_divs); i++) {
+ if (abs(dsp_divs[i] - params_rate(params)) <
+ abs(dsp_divs[best] - params_rate(params)))
+ best = i;
+ }
+ dsp |= i << dsp_shift;
+
+ wm8996_update_bclk(codec);
+
+ lrclk = bclk_rate / params_rate(params);
+ dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
+ lrclk, bclk_rate / lrclk);
+
+ snd_soc_update_bits(codec, aifdata_reg,
+ WM8996_AIF1TX_WL_MASK |
+ WM8996_AIF1TX_SLOT_LEN_MASK,
+ aifdata);
+ snd_soc_update_bits(codec, lrclk_reg, WM8996_AIF1RX_RATE_MASK,
+ lrclk);
+ snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_2,
+ WM8996_DSP1_DIV_MASK << dsp_shift, dsp);
+
+ return 0;
+}
+
+static int wm8996_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int lfclk = 0;
+ int ratediv = 0;
+ int sync = WM8996_REG_SYNC;
+ int src;
+ int old;
+
+ if (freq == wm8996->sysclk && clk_id == wm8996->sysclk_src)
+ return 0;
+
+ /* Disable SYSCLK while we reconfigure */
+ old = snd_soc_read(codec, WM8996_AIF_CLOCKING_1) & WM8996_SYSCLK_ENA;
+ snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_1,
+ WM8996_SYSCLK_ENA, 0);
+
+ switch (clk_id) {
+ case WM8996_SYSCLK_MCLK1:
+ wm8996->sysclk = freq;
+ src = 0;
+ break;
+ case WM8996_SYSCLK_MCLK2:
+ wm8996->sysclk = freq;
+ src = 1;
+ break;
+ case WM8996_SYSCLK_FLL:
+ wm8996->sysclk = freq;
+ src = 2;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ switch (wm8996->sysclk) {
+ case 5644800:
+ case 6144000:
+ snd_soc_update_bits(codec, WM8996_AIF_RATE,
+ WM8996_SYSCLK_RATE, 0);
+ break;
+ case 22579200:
+ case 24576000:
+ ratediv = WM8996_SYSCLK_DIV;
+ wm8996->sysclk /= 2;
+ case 11289600:
+ case 12288000:
+ snd_soc_update_bits(codec, WM8996_AIF_RATE,
+ WM8996_SYSCLK_RATE, WM8996_SYSCLK_RATE);
+ break;
+ case 32000:
+ case 32768:
+ lfclk = WM8996_LFCLK_ENA;
+ sync = 0;
+ break;
+ default:
+ dev_warn(codec->dev, "Unsupported clock rate %dHz\n",
+ wm8996->sysclk);
+ return -EINVAL;
+ }
+
+ wm8996_update_bclk(codec);
+
+ snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_1,
+ WM8996_SYSCLK_SRC_MASK | WM8996_SYSCLK_DIV_MASK,
+ src << WM8996_SYSCLK_SRC_SHIFT | ratediv);
+ snd_soc_update_bits(codec, WM8996_CLOCKING_1, WM8996_LFCLK_ENA, lfclk);
+ snd_soc_update_bits(codec, WM8996_CONTROL_INTERFACE_1,
+ WM8996_REG_SYNC, sync);
+ snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_1,
+ WM8996_SYSCLK_ENA, old);
+
+ wm8996->sysclk_src = clk_id;
+
+ return 0;
+}
+
+struct _fll_div {
+ u16 fll_fratio;
+ u16 fll_outdiv;
+ u16 fll_refclk_div;
+ u16 fll_loop_gain;
+ u16 fll_ref_freq;
+ u16 n;
+ u16 theta;
+ u16 lambda;
+};
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fll_fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ unsigned int target;
+ unsigned int div;
+ unsigned int fratio, gcd_fll;
+ int i;
+
+ /* Fref must be <=13.5MHz */
+ div = 1;
+ fll_div->fll_refclk_div = 0;
+ while ((Fref / div) > 13500000) {
+ div *= 2;
+ fll_div->fll_refclk_div++;
+
+ if (div > 8) {
+ pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+ }
+
+ pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout);
+
+ /* Apply the division for our remaining calculations */
+ Fref /= div;
+
+ if (Fref >= 3000000)
+ fll_div->fll_loop_gain = 5;
+ else
+ fll_div->fll_loop_gain = 0;
+
+ if (Fref >= 48000)
+ fll_div->fll_ref_freq = 0;
+ else
+ fll_div->fll_ref_freq = 1;
+
+ /* Fvco should be 90-100MHz; don't check the upper bound */
+ div = 2;
+ while (Fout * div < 90000000) {
+ div++;
+ if (div > 64) {
+ pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ target = Fout * div;
+ fll_div->fll_outdiv = div - 1;
+
+ pr_debug("FLL Fvco=%dHz\n", target);
+
+ /* Find an appropraite FLL_FRATIO and factor it out of the target */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+ fratio = fll_fratios[i].ratio;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_fratios)) {
+ pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+ return -EINVAL;
+ }
+
+ fll_div->n = target / (fratio * Fref);
+
+ if (target % Fref == 0) {
+ fll_div->theta = 0;
+ fll_div->lambda = 0;
+ } else {
+ gcd_fll = gcd(target, fratio * Fref);
+
+ fll_div->theta = (target - (fll_div->n * fratio * Fref))
+ / gcd_fll;
+ fll_div->lambda = (fratio * Fref) / gcd_fll;
+ }
+
+ pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n",
+ fll_div->n, fll_div->theta, fll_div->lambda);
+ pr_debug("FLL_FRATIO=%x FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n",
+ fll_div->fll_fratio, fll_div->fll_outdiv,
+ fll_div->fll_refclk_div);
+
+ return 0;
+}
+
+static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct _fll_div fll_div;
+ unsigned long timeout, time_left;
+ int ret, reg, retry;
+
+ /* Any change? */
+ if (source == wm8996->fll_src && Fref == wm8996->fll_fref &&
+ Fout == wm8996->fll_fout)
+ return 0;
+
+ if (Fout == 0) {
+ dev_dbg(codec->dev, "FLL disabled\n");
+
+ wm8996->fll_fref = 0;
+ wm8996->fll_fout = 0;
+
+ snd_soc_update_bits(codec, WM8996_FLL_CONTROL_1,
+ WM8996_FLL_ENA, 0);
+
+ wm8996_bg_disable(codec);
+
+ return 0;
+ }
+
+ ret = fll_factors(&fll_div, Fref, Fout);
+ if (ret != 0)
+ return ret;
+
+ switch (source) {
+ case WM8996_FLL_MCLK1:
+ reg = 0;
+ break;
+ case WM8996_FLL_MCLK2:
+ reg = 1;
+ break;
+ case WM8996_FLL_DACLRCLK1:
+ reg = 2;
+ break;
+ case WM8996_FLL_BCLK1:
+ reg = 3;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown FLL source %d\n", ret);
+ return -EINVAL;
+ }
+
+ reg |= fll_div.fll_refclk_div << WM8996_FLL_REFCLK_DIV_SHIFT;
+ reg |= fll_div.fll_ref_freq << WM8996_FLL_REF_FREQ_SHIFT;
+
+ snd_soc_update_bits(codec, WM8996_FLL_CONTROL_5,
+ WM8996_FLL_REFCLK_DIV_MASK | WM8996_FLL_REF_FREQ |
+ WM8996_FLL_REFCLK_SRC_MASK, reg);
+
+ reg = 0;
+ if (fll_div.theta || fll_div.lambda)
+ reg |= WM8996_FLL_EFS_ENA | (3 << WM8996_FLL_LFSR_SEL_SHIFT);
+ else
+ reg |= 1 << WM8996_FLL_LFSR_SEL_SHIFT;
+ snd_soc_write(codec, WM8996_FLL_EFS_2, reg);
+
+ snd_soc_update_bits(codec, WM8996_FLL_CONTROL_2,
+ WM8996_FLL_OUTDIV_MASK |
+ WM8996_FLL_FRATIO_MASK,
+ (fll_div.fll_outdiv << WM8996_FLL_OUTDIV_SHIFT) |
+ (fll_div.fll_fratio));
+
+ snd_soc_write(codec, WM8996_FLL_CONTROL_3, fll_div.theta);
+
+ snd_soc_update_bits(codec, WM8996_FLL_CONTROL_4,
+ WM8996_FLL_N_MASK | WM8996_FLL_LOOP_GAIN_MASK,
+ (fll_div.n << WM8996_FLL_N_SHIFT) |
+ fll_div.fll_loop_gain);
+
+ snd_soc_write(codec, WM8996_FLL_EFS_1, fll_div.lambda);
+
+ /* Enable the bandgap if it's not already enabled */
+ ret = snd_soc_read(codec, WM8996_FLL_CONTROL_1);
+ if (!(ret & WM8996_FLL_ENA))
+ wm8996_bg_enable(codec);
+
+ /* Clear any pending completions (eg, from failed startups) */
+ try_wait_for_completion(&wm8996->fll_lock);
+
+ snd_soc_update_bits(codec, WM8996_FLL_CONTROL_1,
+ WM8996_FLL_ENA, WM8996_FLL_ENA);
+
+ /* The FLL supports live reconfiguration - kick that in case we were
+ * already enabled.
+ */
+ snd_soc_write(codec, WM8996_FLL_CONTROL_6, WM8996_FLL_SWITCH_CLK);
+
+ /* Wait for the FLL to lock, using the interrupt if possible */
+ if (Fref > 1000000)
+ timeout = usecs_to_jiffies(300);
+ else
+ timeout = msecs_to_jiffies(2);
+
+ /* Allow substantially longer if we've actually got the IRQ, poll
+ * at a slightly higher rate if we don't.
+ */
+ if (i2c->irq)
+ timeout *= 10;
+ else
+ /* ensure timeout of atleast 1 jiffies */
+ timeout = timeout/2 ? : 1;
+
+ for (retry = 0; retry < 10; retry++) {
+ time_left = wait_for_completion_timeout(&wm8996->fll_lock,
+ timeout);
+ if (time_left != 0) {
+ WARN_ON(!i2c->irq);
+ ret = 1;
+ break;
+ }
+
+ ret = snd_soc_read(codec, WM8996_INTERRUPT_RAW_STATUS_2);
+ if (ret & WM8996_FLL_LOCK_STS)
+ break;
+ }
+ if (retry == 10) {
+ dev_err(codec->dev, "Timed out waiting for FLL\n");
+ ret = -ETIMEDOUT;
+ }
+
+ dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+
+ wm8996->fll_fref = Fref;
+ wm8996->fll_fout = Fout;
+ wm8996->fll_src = source;
+
+ return ret;
+}
+
+#ifdef CONFIG_GPIOLIB
+static inline struct wm8996_priv *gpio_to_wm8996(struct gpio_chip *chip)
+{
+ return container_of(chip, struct wm8996_priv, gpio_chip);
+}
+
+static void wm8996_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+
+ regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset,
+ WM8996_GP1_LVL, !!value << WM8996_GP1_LVL_SHIFT);
+}
+
+static int wm8996_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+ int val;
+
+ val = (1 << WM8996_GP1_FN_SHIFT) | (!!value << WM8996_GP1_LVL_SHIFT);
+
+ return regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset,
+ WM8996_GP1_FN_MASK | WM8996_GP1_DIR |
+ WM8996_GP1_LVL, val);
+}
+
+static int wm8996_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(wm8996->regmap, WM8996_GPIO_1 + offset, &reg);
+ if (ret < 0)
+ return ret;
+
+ return (reg & WM8996_GP1_LVL) != 0;
+}
+
+static int wm8996_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+
+ return regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset,
+ WM8996_GP1_FN_MASK | WM8996_GP1_DIR,
+ (1 << WM8996_GP1_FN_SHIFT) |
+ (1 << WM8996_GP1_DIR_SHIFT));
+}
+
+static struct gpio_chip wm8996_template_chip = {
+ .label = "wm8996",
+ .owner = THIS_MODULE,
+ .direction_output = wm8996_gpio_direction_out,
+ .set = wm8996_gpio_set,
+ .direction_input = wm8996_gpio_direction_in,
+ .get = wm8996_gpio_get,
+ .can_sleep = 1,
+};
+
+static void wm8996_init_gpio(struct wm8996_priv *wm8996)
+{
+ int ret;
+
+ wm8996->gpio_chip = wm8996_template_chip;
+ wm8996->gpio_chip.ngpio = 5;
+ wm8996->gpio_chip.dev = wm8996->dev;
+
+ if (wm8996->pdata.gpio_base)
+ wm8996->gpio_chip.base = wm8996->pdata.gpio_base;
+ else
+ wm8996->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&wm8996->gpio_chip);
+ if (ret != 0)
+ dev_err(wm8996->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
+static void wm8996_free_gpio(struct wm8996_priv *wm8996)
+{
+ gpiochip_remove(&wm8996->gpio_chip);
+}
+#else
+static void wm8996_init_gpio(struct wm8996_priv *wm8996)
+{
+}
+
+static void wm8996_free_gpio(struct wm8996_priv *wm8996)
+{
+}
+#endif
+
+/**
+ * wm8996_detect - Enable default WM8996 jack detection
+ *
+ * The WM8996 has advanced accessory detection support for headsets.
+ * This function provides a default implementation which integrates
+ * the majority of this functionality with minimal user configuration.
+ *
+ * This will detect headset, headphone and short circuit button and
+ * will also detect inverted microphone ground connections and update
+ * the polarity of the connections.
+ */
+int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ wm8996_polarity_fn polarity_cb)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ wm8996->jack = jack;
+ wm8996->detecting = true;
+ wm8996->polarity_cb = polarity_cb;
+ wm8996->jack_flips = 0;
+
+ if (wm8996->polarity_cb)
+ wm8996->polarity_cb(codec, 0);
+
+ /* Clear discarge to avoid noise during detection */
+ snd_soc_update_bits(codec, WM8996_MICBIAS_1,
+ WM8996_MICB1_DISCH, 0);
+ snd_soc_update_bits(codec, WM8996_MICBIAS_2,
+ WM8996_MICB2_DISCH, 0);
+
+ /* LDO2 powers the microphones, SYSCLK clocks detection */
+ snd_soc_dapm_mutex_lock(dapm);
+
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "LDO2");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "SYSCLK");
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ /* We start off just enabling microphone detection - even a
+ * plain headphone will trigger detection.
+ */
+ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ WM8996_MICD_ENA, WM8996_MICD_ENA);
+
+ /* Slowest detection rate, gives debounce for initial detection */
+ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ WM8996_MICD_RATE_MASK,
+ WM8996_MICD_RATE_MASK);
+
+ /* Enable interrupts and we're off */
+ snd_soc_update_bits(codec, WM8996_INTERRUPT_STATUS_2_MASK,
+ WM8996_IM_MICD_EINT | WM8996_HP_DONE_EINT, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8996_detect);
+
+static void wm8996_hpdet_irq(struct snd_soc_codec *codec)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int val, reg, report;
+
+ /* Assume headphone in error conditions; we need to report
+ * something or we stall our state machine.
+ */
+ report = SND_JACK_HEADPHONE;
+
+ reg = snd_soc_read(codec, WM8996_HEADPHONE_DETECT_2);
+ if (reg < 0) {
+ dev_err(codec->dev, "Failed to read HPDET status\n");
+ goto out;
+ }
+
+ if (!(reg & WM8996_HP_DONE)) {
+ dev_err(codec->dev, "Got HPDET IRQ but HPDET is busy\n");
+ goto out;
+ }
+
+ val = reg & WM8996_HP_LVL_MASK;
+
+ dev_dbg(codec->dev, "HPDET measured %d ohms\n", val);
+
+ /* If we've got high enough impedence then report as line,
+ * otherwise assume headphone.
+ */
+ if (val >= 126)
+ report = SND_JACK_LINEOUT;
+ else
+ report = SND_JACK_HEADPHONE;
+
+out:
+ if (wm8996->jack_mic)
+ report |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(wm8996->jack, report,
+ SND_JACK_LINEOUT | SND_JACK_HEADSET);
+
+ wm8996->detecting = false;
+
+ /* If the output isn't running re-clamp it */
+ if (!(snd_soc_read(codec, WM8996_POWER_MANAGEMENT_1) &
+ (WM8996_HPOUT1L_ENA | WM8996_HPOUT1R_RMV_SHORT)))
+ snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
+ WM8996_HPOUT1L_RMV_SHORT |
+ WM8996_HPOUT1R_RMV_SHORT, 0);
+
+ /* Go back to looking at the microphone */
+ snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_1,
+ WM8996_JD_MODE_MASK, 0);
+ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA,
+ WM8996_MICD_ENA);
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "Bandgap");
+ snd_soc_dapm_sync(&codec->dapm);
+}
+
+static void wm8996_hpdet_start(struct snd_soc_codec *codec)
+{
+ /* Unclamp the output, we can't measure while we're shorting it */
+ snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
+ WM8996_HPOUT1L_RMV_SHORT |
+ WM8996_HPOUT1R_RMV_SHORT,
+ WM8996_HPOUT1L_RMV_SHORT |
+ WM8996_HPOUT1R_RMV_SHORT);
+
+ /* We need bandgap for HPDET */
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "Bandgap");
+ snd_soc_dapm_sync(&codec->dapm);
+
+ /* Go into headphone detect left mode */
+ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, 0);
+ snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_1,
+ WM8996_JD_MODE_MASK, 1);
+
+ /* Trigger a measurement */
+ snd_soc_update_bits(codec, WM8996_HEADPHONE_DETECT_1,
+ WM8996_HP_POLL, WM8996_HP_POLL);
+}
+
+static void wm8996_report_headphone(struct snd_soc_codec *codec)
+{
+ dev_dbg(codec->dev, "Headphone detected\n");
+ wm8996_hpdet_start(codec);
+
+ /* Increase the detection rate a bit for responsiveness. */
+ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ WM8996_MICD_RATE_MASK |
+ WM8996_MICD_BIAS_STARTTIME_MASK,
+ 7 << WM8996_MICD_RATE_SHIFT |
+ 7 << WM8996_MICD_BIAS_STARTTIME_SHIFT);
+}
+
+static void wm8996_micd(struct snd_soc_codec *codec)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int val, reg;
+
+ val = snd_soc_read(codec, WM8996_MIC_DETECT_3);
+
+ dev_dbg(codec->dev, "Microphone event: %x\n", val);
+
+ if (!(val & WM8996_MICD_VALID)) {
+ dev_warn(codec->dev, "Microphone detection state invalid\n");
+ return;
+ }
+
+ /* No accessory, reset everything and report removal */
+ if (!(val & WM8996_MICD_STS)) {
+ dev_dbg(codec->dev, "Jack removal detected\n");
+ wm8996->jack_mic = false;
+ wm8996->detecting = true;
+ wm8996->jack_flips = 0;
+ snd_soc_jack_report(wm8996->jack, 0,
+ SND_JACK_LINEOUT | SND_JACK_HEADSET |
+ SND_JACK_BTN_0);
+
+ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ WM8996_MICD_RATE_MASK |
+ WM8996_MICD_BIAS_STARTTIME_MASK,
+ WM8996_MICD_RATE_MASK |
+ 9 << WM8996_MICD_BIAS_STARTTIME_SHIFT);
+ return;
+ }
+
+ /* If the measurement is very high we've got a microphone,
+ * either we just detected one or if we already reported then
+ * we've got a button release event.
+ */
+ if (val & 0x400) {
+ if (wm8996->detecting) {
+ dev_dbg(codec->dev, "Microphone detected\n");
+ wm8996->jack_mic = true;
+ wm8996_hpdet_start(codec);
+
+ /* Increase poll rate to give better responsiveness
+ * for buttons */
+ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ WM8996_MICD_RATE_MASK |
+ WM8996_MICD_BIAS_STARTTIME_MASK,
+ 5 << WM8996_MICD_RATE_SHIFT |
+ 7 << WM8996_MICD_BIAS_STARTTIME_SHIFT);
+ } else {
+ dev_dbg(codec->dev, "Mic button up\n");
+ snd_soc_jack_report(wm8996->jack, 0, SND_JACK_BTN_0);
+ }
+
+ return;
+ }
+
+ /* If we detected a lower impedence during initial startup
+ * then we probably have the wrong polarity, flip it. Don't
+ * do this for the lowest impedences to speed up detection of
+ * plain headphones. If both polarities report a low
+ * impedence then give up and report headphones.
+ */
+ if (wm8996->detecting && (val & 0x3f0)) {
+ wm8996->jack_flips++;
+
+ if (wm8996->jack_flips > 1) {
+ wm8996_report_headphone(codec);
+ return;
+ }
+
+ reg = snd_soc_read(codec, WM8996_ACCESSORY_DETECT_MODE_2);
+ reg ^= WM8996_HPOUT1FB_SRC | WM8996_MICD_SRC |
+ WM8996_MICD_BIAS_SRC;
+ snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_2,
+ WM8996_HPOUT1FB_SRC | WM8996_MICD_SRC |
+ WM8996_MICD_BIAS_SRC, reg);
+
+ if (wm8996->polarity_cb)
+ wm8996->polarity_cb(codec,
+ (reg & WM8996_MICD_SRC) != 0);
+
+ dev_dbg(codec->dev, "Set microphone polarity to %d\n",
+ (reg & WM8996_MICD_SRC) != 0);
+
+ return;
+ }
+
+ /* Don't distinguish between buttons, just report any low
+ * impedence as BTN_0.
+ */
+ if (val & 0x3fc) {
+ if (wm8996->jack_mic) {
+ dev_dbg(codec->dev, "Mic button detected\n");
+ snd_soc_jack_report(wm8996->jack, SND_JACK_BTN_0,
+ SND_JACK_BTN_0);
+ } else if (wm8996->detecting) {
+ wm8996_report_headphone(codec);
+ }
+ }
+}
+
+static irqreturn_t wm8996_irq(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int irq_val;
+
+ irq_val = snd_soc_read(codec, WM8996_INTERRUPT_STATUS_2);
+ if (irq_val < 0) {
+ dev_err(codec->dev, "Failed to read IRQ status: %d\n",
+ irq_val);
+ return IRQ_NONE;
+ }
+ irq_val &= ~snd_soc_read(codec, WM8996_INTERRUPT_STATUS_2_MASK);
+
+ if (!irq_val)
+ return IRQ_NONE;
+
+ snd_soc_write(codec, WM8996_INTERRUPT_STATUS_2, irq_val);
+
+ if (irq_val & (WM8996_DCS_DONE_01_EINT | WM8996_DCS_DONE_23_EINT)) {
+ dev_dbg(codec->dev, "DC servo IRQ\n");
+ complete(&wm8996->dcs_done);
+ }
+
+ if (irq_val & WM8996_FIFOS_ERR_EINT)
+ dev_err(codec->dev, "Digital core FIFO error\n");
+
+ if (irq_val & WM8996_FLL_LOCK_EINT) {
+ dev_dbg(codec->dev, "FLL locked\n");
+ complete(&wm8996->fll_lock);
+ }
+
+ if (irq_val & WM8996_MICD_EINT)
+ wm8996_micd(codec);
+
+ if (irq_val & WM8996_HP_DONE_EINT)
+ wm8996_hpdet_irq(codec);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wm8996_edge_irq(int irq, void *data)
+{
+ irqreturn_t ret = IRQ_NONE;
+ irqreturn_t val;
+
+ do {
+ val = wm8996_irq(irq, data);
+ if (val != IRQ_NONE)
+ ret = val;
+ } while (val != IRQ_NONE);
+
+ return ret;
+}
+
+static void wm8996_retune_mobile_pdata(struct snd_soc_codec *codec)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct wm8996_pdata *pdata = &wm8996->pdata;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("DSP1 EQ Mode",
+ wm8996->retune_mobile_enum,
+ wm8996_get_retune_mobile_enum,
+ wm8996_put_retune_mobile_enum),
+ SOC_ENUM_EXT("DSP2 EQ Mode",
+ wm8996->retune_mobile_enum,
+ wm8996_get_retune_mobile_enum,
+ wm8996_put_retune_mobile_enum),
+ };
+ int ret, i, j;
+ const char **t;
+
+ /* We need an array of texts for the enum API but the number
+ * of texts is likely to be less than the number of
+ * configurations due to the sample rate dependency of the
+ * configurations. */
+ wm8996->num_retune_mobile_texts = 0;
+ wm8996->retune_mobile_texts = NULL;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ for (j = 0; j < wm8996->num_retune_mobile_texts; j++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8996->retune_mobile_texts[j]) == 0)
+ break;
+ }
+
+ if (j != wm8996->num_retune_mobile_texts)
+ continue;
+
+ /* Expand the array... */
+ t = krealloc(wm8996->retune_mobile_texts,
+ sizeof(char *) *
+ (wm8996->num_retune_mobile_texts + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* ...store the new entry... */
+ t[wm8996->num_retune_mobile_texts] =
+ pdata->retune_mobile_cfgs[i].name;
+
+ /* ...and remember the new version. */
+ wm8996->num_retune_mobile_texts++;
+ wm8996->retune_mobile_texts = t;
+ }
+
+ dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+ wm8996->num_retune_mobile_texts);
+
+ wm8996->retune_mobile_enum.items = wm8996->num_retune_mobile_texts;
+ wm8996->retune_mobile_enum.texts = wm8996->retune_mobile_texts;
+
+ ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev,
+ "Failed to add ReTune Mobile controls: %d\n", ret);
+}
+
+static const struct regmap_config wm8996_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+
+ .max_register = WM8996_MAX_REGISTER,
+ .reg_defaults = wm8996_reg,
+ .num_reg_defaults = ARRAY_SIZE(wm8996_reg),
+ .volatile_reg = wm8996_volatile_register,
+ .readable_reg = wm8996_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int wm8996_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ int irq_flags;
+
+ wm8996->codec = codec;
+
+ init_completion(&wm8996->dcs_done);
+ init_completion(&wm8996->fll_lock);
+
+ if (wm8996->pdata.num_retune_mobile_cfgs)
+ wm8996_retune_mobile_pdata(codec);
+ else
+ snd_soc_add_codec_controls(codec, wm8996_eq_controls,
+ ARRAY_SIZE(wm8996_eq_controls));
+
+ if (i2c->irq) {
+ if (wm8996->pdata.irq_flags)
+ irq_flags = wm8996->pdata.irq_flags;
+ else
+ irq_flags = IRQF_TRIGGER_LOW;
+
+ irq_flags |= IRQF_ONESHOT;
+
+ if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
+ ret = request_threaded_irq(i2c->irq, NULL,
+ wm8996_edge_irq,
+ irq_flags, "wm8996", codec);
+ else
+ ret = request_threaded_irq(i2c->irq, NULL, wm8996_irq,
+ irq_flags, "wm8996", codec);
+
+ if (ret == 0) {
+ /* Unmask the interrupt */
+ snd_soc_update_bits(codec, WM8996_INTERRUPT_CONTROL,
+ WM8996_IM_IRQ, 0);
+
+ /* Enable error reporting and DC servo status */
+ snd_soc_update_bits(codec,
+ WM8996_INTERRUPT_STATUS_2_MASK,
+ WM8996_IM_DCS_DONE_23_EINT |
+ WM8996_IM_DCS_DONE_01_EINT |
+ WM8996_IM_FLL_LOCK_EINT |
+ WM8996_IM_FIFOS_ERR_EINT,
+ 0);
+ } else {
+ dev_err(codec->dev, "Failed to request IRQ: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int wm8996_remove(struct snd_soc_codec *codec)
+{
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+
+ snd_soc_update_bits(codec, WM8996_INTERRUPT_CONTROL,
+ WM8996_IM_IRQ, WM8996_IM_IRQ);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, codec);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8996 = {
+ .probe = wm8996_probe,
+ .remove = wm8996_remove,
+ .set_bias_level = wm8996_set_bias_level,
+ .idle_bias_off = true,
+ .seq_notifier = wm8996_seq_notifier,
+ .controls = wm8996_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8996_snd_controls),
+ .dapm_widgets = wm8996_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8996_dapm_widgets),
+ .dapm_routes = wm8996_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8996_dapm_routes),
+ .set_pll = wm8996_set_fll,
+};
+
+#define WM8996_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000)
+#define WM8996_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm8996_dai_ops = {
+ .set_fmt = wm8996_set_fmt,
+ .hw_params = wm8996_hw_params,
+ .set_sysclk = wm8996_set_sysclk,
+};
+
+static struct snd_soc_dai_driver wm8996_dai[] = {
+ {
+ .name = "wm8996-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8996_RATES,
+ .formats = WM8996_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8996_RATES,
+ .formats = WM8996_FORMATS,
+ .sig_bits = 24,
+ },
+ .ops = &wm8996_dai_ops,
+ },
+ {
+ .name = "wm8996-aif2",
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8996_RATES,
+ .formats = WM8996_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8996_RATES,
+ .formats = WM8996_FORMATS,
+ .sig_bits = 24,
+ },
+ .ops = &wm8996_dai_ops,
+ },
+};
+
+static int wm8996_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8996_priv *wm8996;
+ int ret, i;
+ unsigned int reg;
+
+ wm8996 = devm_kzalloc(&i2c->dev, sizeof(struct wm8996_priv),
+ GFP_KERNEL);
+ if (wm8996 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm8996);
+ wm8996->dev = &i2c->dev;
+
+ if (dev_get_platdata(&i2c->dev))
+ memcpy(&wm8996->pdata, dev_get_platdata(&i2c->dev),
+ sizeof(wm8996->pdata));
+
+ if (wm8996->pdata.ldo_ena > 0) {
+ ret = gpio_request_one(wm8996->pdata.ldo_ena,
+ GPIOF_OUT_INIT_LOW, "WM8996 ENA");
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request GPIO %d: %d\n",
+ wm8996->pdata.ldo_ena, ret);
+ goto err;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++)
+ wm8996->supplies[i].supply = wm8996_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8996->supplies),
+ wm8996->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ goto err_gpio;
+ }
+
+ wm8996->disable_nb[0].notifier_call = wm8996_regulator_event_0;
+ wm8996->disable_nb[1].notifier_call = wm8996_regulator_event_1;
+ wm8996->disable_nb[2].notifier_call = wm8996_regulator_event_2;
+
+ /* This should really be moved into the regulator core */
+ for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) {
+ ret = regulator_register_notifier(wm8996->supplies[i].consumer,
+ &wm8996->disable_nb[i]);
+ if (ret != 0) {
+ dev_err(&i2c->dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies),
+ wm8996->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_gpio;
+ }
+
+ if (wm8996->pdata.ldo_ena > 0) {
+ gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 1);
+ msleep(5);
+ }
+
+ wm8996->regmap = devm_regmap_init_i2c(i2c, &wm8996_regmap);
+ if (IS_ERR(wm8996->regmap)) {
+ ret = PTR_ERR(wm8996->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ goto err_enable;
+ }
+
+ ret = regmap_read(wm8996->regmap, WM8996_SOFTWARE_RESET, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret);
+ goto err_regmap;
+ }
+ if (reg != 0x8915) {
+ dev_err(&i2c->dev, "Device is not a WM8996, ID %x\n", reg);
+ ret = -EINVAL;
+ goto err_regmap;
+ }
+
+ ret = regmap_read(wm8996->regmap, WM8996_CHIP_REVISION, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_regmap;
+ }
+
+ dev_info(&i2c->dev, "revision %c\n",
+ (reg & WM8996_CHIP_REV_MASK) + 'A');
+
+ if (wm8996->pdata.ldo_ena > 0) {
+ gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
+ regcache_cache_only(wm8996->regmap, true);
+ } else {
+ ret = regmap_write(wm8996->regmap, WM8996_SOFTWARE_RESET,
+ 0x8915);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
+ goto err_regmap;
+ }
+ }
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
+
+ /* Apply platform data settings */
+ regmap_update_bits(wm8996->regmap, WM8996_LINE_INPUT_CONTROL,
+ WM8996_INL_MODE_MASK | WM8996_INR_MODE_MASK,
+ wm8996->pdata.inl_mode << WM8996_INL_MODE_SHIFT |
+ wm8996->pdata.inr_mode);
+
+ for (i = 0; i < ARRAY_SIZE(wm8996->pdata.gpio_default); i++) {
+ if (!wm8996->pdata.gpio_default[i])
+ continue;
+
+ regmap_write(wm8996->regmap, WM8996_GPIO_1 + i,
+ wm8996->pdata.gpio_default[i] & 0xffff);
+ }
+
+ if (wm8996->pdata.spkmute_seq)
+ regmap_update_bits(wm8996->regmap,
+ WM8996_PDM_SPEAKER_MUTE_SEQUENCE,
+ WM8996_SPK_MUTE_ENDIAN |
+ WM8996_SPK_MUTE_SEQ1_MASK,
+ wm8996->pdata.spkmute_seq);
+
+ regmap_update_bits(wm8996->regmap, WM8996_ACCESSORY_DETECT_MODE_2,
+ WM8996_MICD_BIAS_SRC | WM8996_HPOUT1FB_SRC |
+ WM8996_MICD_SRC, wm8996->pdata.micdet_def);
+
+ /* Latch volume update bits */
+ regmap_update_bits(wm8996->regmap, WM8996_LEFT_LINE_INPUT_VOLUME,
+ WM8996_IN1_VU, WM8996_IN1_VU);
+ regmap_update_bits(wm8996->regmap, WM8996_RIGHT_LINE_INPUT_VOLUME,
+ WM8996_IN1_VU, WM8996_IN1_VU);
+
+ regmap_update_bits(wm8996->regmap, WM8996_DAC1_LEFT_VOLUME,
+ WM8996_DAC1_VU, WM8996_DAC1_VU);
+ regmap_update_bits(wm8996->regmap, WM8996_DAC1_RIGHT_VOLUME,
+ WM8996_DAC1_VU, WM8996_DAC1_VU);
+ regmap_update_bits(wm8996->regmap, WM8996_DAC2_LEFT_VOLUME,
+ WM8996_DAC2_VU, WM8996_DAC2_VU);
+ regmap_update_bits(wm8996->regmap, WM8996_DAC2_RIGHT_VOLUME,
+ WM8996_DAC2_VU, WM8996_DAC2_VU);
+
+ regmap_update_bits(wm8996->regmap, WM8996_OUTPUT1_LEFT_VOLUME,
+ WM8996_DAC1_VU, WM8996_DAC1_VU);
+ regmap_update_bits(wm8996->regmap, WM8996_OUTPUT1_RIGHT_VOLUME,
+ WM8996_DAC1_VU, WM8996_DAC1_VU);
+ regmap_update_bits(wm8996->regmap, WM8996_OUTPUT2_LEFT_VOLUME,
+ WM8996_DAC2_VU, WM8996_DAC2_VU);
+ regmap_update_bits(wm8996->regmap, WM8996_OUTPUT2_RIGHT_VOLUME,
+ WM8996_DAC2_VU, WM8996_DAC2_VU);
+
+ regmap_update_bits(wm8996->regmap, WM8996_DSP1_TX_LEFT_VOLUME,
+ WM8996_DSP1TX_VU, WM8996_DSP1TX_VU);
+ regmap_update_bits(wm8996->regmap, WM8996_DSP1_TX_RIGHT_VOLUME,
+ WM8996_DSP1TX_VU, WM8996_DSP1TX_VU);
+ regmap_update_bits(wm8996->regmap, WM8996_DSP2_TX_LEFT_VOLUME,
+ WM8996_DSP2TX_VU, WM8996_DSP2TX_VU);
+ regmap_update_bits(wm8996->regmap, WM8996_DSP2_TX_RIGHT_VOLUME,
+ WM8996_DSP2TX_VU, WM8996_DSP2TX_VU);
+
+ regmap_update_bits(wm8996->regmap, WM8996_DSP1_RX_LEFT_VOLUME,
+ WM8996_DSP1RX_VU, WM8996_DSP1RX_VU);
+ regmap_update_bits(wm8996->regmap, WM8996_DSP1_RX_RIGHT_VOLUME,
+ WM8996_DSP1RX_VU, WM8996_DSP1RX_VU);
+ regmap_update_bits(wm8996->regmap, WM8996_DSP2_RX_LEFT_VOLUME,
+ WM8996_DSP2RX_VU, WM8996_DSP2RX_VU);
+ regmap_update_bits(wm8996->regmap, WM8996_DSP2_RX_RIGHT_VOLUME,
+ WM8996_DSP2RX_VU, WM8996_DSP2RX_VU);
+
+ /* No support currently for the underclocked TDM modes and
+ * pick a default TDM layout with each channel pair working with
+ * slots 0 and 1. */
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF1RX_CHANNEL_0_CONFIGURATION,
+ WM8996_AIF1RX_CHAN0_SLOTS_MASK |
+ WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1RX_CHAN0_SLOTS_SHIFT | 0);
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF1RX_CHANNEL_1_CONFIGURATION,
+ WM8996_AIF1RX_CHAN1_SLOTS_MASK |
+ WM8996_AIF1RX_CHAN1_START_SLOT_MASK,
+ 1 << WM8996_AIF1RX_CHAN1_SLOTS_SHIFT | 1);
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF1RX_CHANNEL_2_CONFIGURATION,
+ WM8996_AIF1RX_CHAN2_SLOTS_MASK |
+ WM8996_AIF1RX_CHAN2_START_SLOT_MASK,
+ 1 << WM8996_AIF1RX_CHAN2_SLOTS_SHIFT | 0);
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF1RX_CHANNEL_3_CONFIGURATION,
+ WM8996_AIF1RX_CHAN3_SLOTS_MASK |
+ WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1RX_CHAN3_SLOTS_SHIFT | 1);
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF1RX_CHANNEL_4_CONFIGURATION,
+ WM8996_AIF1RX_CHAN4_SLOTS_MASK |
+ WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1RX_CHAN4_SLOTS_SHIFT | 0);
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF1RX_CHANNEL_5_CONFIGURATION,
+ WM8996_AIF1RX_CHAN5_SLOTS_MASK |
+ WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1RX_CHAN5_SLOTS_SHIFT | 1);
+
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF2RX_CHANNEL_0_CONFIGURATION,
+ WM8996_AIF2RX_CHAN0_SLOTS_MASK |
+ WM8996_AIF2RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF2RX_CHAN0_SLOTS_SHIFT | 0);
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF2RX_CHANNEL_1_CONFIGURATION,
+ WM8996_AIF2RX_CHAN1_SLOTS_MASK |
+ WM8996_AIF2RX_CHAN1_START_SLOT_MASK,
+ 1 << WM8996_AIF2RX_CHAN1_SLOTS_SHIFT | 1);
+
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF1TX_CHANNEL_0_CONFIGURATION,
+ WM8996_AIF1TX_CHAN0_SLOTS_MASK |
+ WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN0_SLOTS_SHIFT | 0);
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF1TX_CHANNEL_1_CONFIGURATION,
+ WM8996_AIF1TX_CHAN1_SLOTS_MASK |
+ WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF1TX_CHANNEL_2_CONFIGURATION,
+ WM8996_AIF1TX_CHAN2_SLOTS_MASK |
+ WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN2_SLOTS_SHIFT | 0);
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF1TX_CHANNEL_3_CONFIGURATION,
+ WM8996_AIF1TX_CHAN3_SLOTS_MASK |
+ WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN3_SLOTS_SHIFT | 1);
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF1TX_CHANNEL_4_CONFIGURATION,
+ WM8996_AIF1TX_CHAN4_SLOTS_MASK |
+ WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN4_SLOTS_SHIFT | 0);
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF1TX_CHANNEL_5_CONFIGURATION,
+ WM8996_AIF1TX_CHAN5_SLOTS_MASK |
+ WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN5_SLOTS_SHIFT | 1);
+
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF2TX_CHANNEL_0_CONFIGURATION,
+ WM8996_AIF2TX_CHAN0_SLOTS_MASK |
+ WM8996_AIF2TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF2TX_CHAN0_SLOTS_SHIFT | 0);
+ regmap_update_bits(wm8996->regmap,
+ WM8996_AIF1TX_CHANNEL_1_CONFIGURATION,
+ WM8996_AIF2TX_CHAN1_SLOTS_MASK |
+ WM8996_AIF2TX_CHAN1_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
+
+ /* If the TX LRCLK pins are not in LRCLK mode configure the
+ * AIFs to source their clocks from the RX LRCLKs.
+ */
+ ret = regmap_read(wm8996->regmap, WM8996_GPIO_1, &reg);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to read GPIO1: %d\n", ret);
+ goto err_regmap;
+ }
+
+ if (reg & WM8996_GP1_FN_MASK)
+ regmap_update_bits(wm8996->regmap, WM8996_AIF1_TX_LRCLK_2,
+ WM8996_AIF1TX_LRCLK_MODE,
+ WM8996_AIF1TX_LRCLK_MODE);
+
+ ret = regmap_read(wm8996->regmap, WM8996_GPIO_2, &reg);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to read GPIO2: %d\n", ret);
+ goto err_regmap;
+ }
+
+ if (reg & WM8996_GP2_FN_MASK)
+ regmap_update_bits(wm8996->regmap, WM8996_AIF2_TX_LRCLK_2,
+ WM8996_AIF2TX_LRCLK_MODE,
+ WM8996_AIF2TX_LRCLK_MODE);
+
+ wm8996_init_gpio(wm8996);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8996, wm8996_dai,
+ ARRAY_SIZE(wm8996_dai));
+ if (ret < 0)
+ goto err_gpiolib;
+
+ return ret;
+
+err_gpiolib:
+ wm8996_free_gpio(wm8996);
+err_regmap:
+err_enable:
+ if (wm8996->pdata.ldo_ena > 0)
+ gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
+ regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
+err_gpio:
+ if (wm8996->pdata.ldo_ena > 0)
+ gpio_free(wm8996->pdata.ldo_ena);
+err:
+
+ return ret;
+}
+
+static int wm8996_i2c_remove(struct i2c_client *client)
+{
+ struct wm8996_priv *wm8996 = i2c_get_clientdata(client);
+ int i;
+
+ snd_soc_unregister_codec(&client->dev);
+ wm8996_free_gpio(wm8996);
+ if (wm8996->pdata.ldo_ena > 0) {
+ gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
+ gpio_free(wm8996->pdata.ldo_ena);
+ }
+ for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++)
+ regulator_unregister_notifier(wm8996->supplies[i].consumer,
+ &wm8996->disable_nb[i]);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm8996_i2c_id[] = {
+ { "wm8996", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8996_i2c_id);
+
+static struct i2c_driver wm8996_i2c_driver = {
+ .driver = {
+ .name = "wm8996",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8996_i2c_probe,
+ .remove = wm8996_i2c_remove,
+ .id_table = wm8996_i2c_id,
+};
+
+module_i2c_driver(wm8996_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8996 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm8996.h b/kernel/sound/soc/codecs/wm8996.h
new file mode 100644
index 000000000..de9ac3e44
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8996.h
@@ -0,0 +1,3721 @@
+/*
+ * wm8996.h - WM8996 audio codec interface
+ *
+ * Copyright 2011 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.
+ */
+
+#ifndef _WM8996_H
+#define _WM8996_H
+
+#define WM8996_SYSCLK_MCLK1 1
+#define WM8996_SYSCLK_MCLK2 2
+#define WM8996_SYSCLK_FLL 3
+
+#define WM8996_FLL_MCLK1 1
+#define WM8996_FLL_MCLK2 2
+#define WM8996_FLL_DACLRCLK1 3
+#define WM8996_FLL_BCLK1 4
+
+typedef void (*wm8996_polarity_fn)(struct snd_soc_codec *codec, int polarity);
+
+int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ wm8996_polarity_fn polarity_cb);
+
+/*
+ * Register values.
+ */
+#define WM8996_SOFTWARE_RESET 0x00
+#define WM8996_POWER_MANAGEMENT_1 0x01
+#define WM8996_POWER_MANAGEMENT_2 0x02
+#define WM8996_POWER_MANAGEMENT_3 0x03
+#define WM8996_POWER_MANAGEMENT_4 0x04
+#define WM8996_POWER_MANAGEMENT_5 0x05
+#define WM8996_POWER_MANAGEMENT_6 0x06
+#define WM8996_POWER_MANAGEMENT_7 0x07
+#define WM8996_POWER_MANAGEMENT_8 0x08
+#define WM8996_LEFT_LINE_INPUT_VOLUME 0x10
+#define WM8996_RIGHT_LINE_INPUT_VOLUME 0x11
+#define WM8996_LINE_INPUT_CONTROL 0x12
+#define WM8996_DAC1_HPOUT1_VOLUME 0x15
+#define WM8996_DAC2_HPOUT2_VOLUME 0x16
+#define WM8996_DAC1_LEFT_VOLUME 0x18
+#define WM8996_DAC1_RIGHT_VOLUME 0x19
+#define WM8996_DAC2_LEFT_VOLUME 0x1A
+#define WM8996_DAC2_RIGHT_VOLUME 0x1B
+#define WM8996_OUTPUT1_LEFT_VOLUME 0x1C
+#define WM8996_OUTPUT1_RIGHT_VOLUME 0x1D
+#define WM8996_OUTPUT2_LEFT_VOLUME 0x1E
+#define WM8996_OUTPUT2_RIGHT_VOLUME 0x1F
+#define WM8996_MICBIAS_1 0x20
+#define WM8996_MICBIAS_2 0x21
+#define WM8996_LDO_1 0x28
+#define WM8996_LDO_2 0x29
+#define WM8996_ACCESSORY_DETECT_MODE_1 0x30
+#define WM8996_ACCESSORY_DETECT_MODE_2 0x31
+#define WM8996_HEADPHONE_DETECT_1 0x34
+#define WM8996_HEADPHONE_DETECT_2 0x35
+#define WM8996_MIC_DETECT_1 0x38
+#define WM8996_MIC_DETECT_2 0x39
+#define WM8996_MIC_DETECT_3 0x3A
+#define WM8996_CHARGE_PUMP_1 0x40
+#define WM8996_CHARGE_PUMP_2 0x41
+#define WM8996_DC_SERVO_1 0x50
+#define WM8996_DC_SERVO_2 0x51
+#define WM8996_DC_SERVO_3 0x52
+#define WM8996_DC_SERVO_5 0x54
+#define WM8996_DC_SERVO_6 0x55
+#define WM8996_DC_SERVO_7 0x56
+#define WM8996_DC_SERVO_READBACK_0 0x57
+#define WM8996_ANALOGUE_HP_1 0x60
+#define WM8996_ANALOGUE_HP_2 0x61
+#define WM8996_CHIP_REVISION 0x100
+#define WM8996_CONTROL_INTERFACE_1 0x101
+#define WM8996_WRITE_SEQUENCER_CTRL_1 0x110
+#define WM8996_WRITE_SEQUENCER_CTRL_2 0x111
+#define WM8996_AIF_CLOCKING_1 0x200
+#define WM8996_AIF_CLOCKING_2 0x201
+#define WM8996_CLOCKING_1 0x208
+#define WM8996_CLOCKING_2 0x209
+#define WM8996_AIF_RATE 0x210
+#define WM8996_FLL_CONTROL_1 0x220
+#define WM8996_FLL_CONTROL_2 0x221
+#define WM8996_FLL_CONTROL_3 0x222
+#define WM8996_FLL_CONTROL_4 0x223
+#define WM8996_FLL_CONTROL_5 0x224
+#define WM8996_FLL_CONTROL_6 0x225
+#define WM8996_FLL_EFS_1 0x226
+#define WM8996_FLL_EFS_2 0x227
+#define WM8996_AIF1_CONTROL 0x300
+#define WM8996_AIF1_BCLK 0x301
+#define WM8996_AIF1_TX_LRCLK_1 0x302
+#define WM8996_AIF1_TX_LRCLK_2 0x303
+#define WM8996_AIF1_RX_LRCLK_1 0x304
+#define WM8996_AIF1_RX_LRCLK_2 0x305
+#define WM8996_AIF1TX_DATA_CONFIGURATION_1 0x306
+#define WM8996_AIF1TX_DATA_CONFIGURATION_2 0x307
+#define WM8996_AIF1RX_DATA_CONFIGURATION 0x308
+#define WM8996_AIF1TX_CHANNEL_0_CONFIGURATION 0x309
+#define WM8996_AIF1TX_CHANNEL_1_CONFIGURATION 0x30A
+#define WM8996_AIF1TX_CHANNEL_2_CONFIGURATION 0x30B
+#define WM8996_AIF1TX_CHANNEL_3_CONFIGURATION 0x30C
+#define WM8996_AIF1TX_CHANNEL_4_CONFIGURATION 0x30D
+#define WM8996_AIF1TX_CHANNEL_5_CONFIGURATION 0x30E
+#define WM8996_AIF1RX_CHANNEL_0_CONFIGURATION 0x30F
+#define WM8996_AIF1RX_CHANNEL_1_CONFIGURATION 0x310
+#define WM8996_AIF1RX_CHANNEL_2_CONFIGURATION 0x311
+#define WM8996_AIF1RX_CHANNEL_3_CONFIGURATION 0x312
+#define WM8996_AIF1RX_CHANNEL_4_CONFIGURATION 0x313
+#define WM8996_AIF1RX_CHANNEL_5_CONFIGURATION 0x314
+#define WM8996_AIF1RX_MONO_CONFIGURATION 0x315
+#define WM8996_AIF1TX_TEST 0x31A
+#define WM8996_AIF2_CONTROL 0x320
+#define WM8996_AIF2_BCLK 0x321
+#define WM8996_AIF2_TX_LRCLK_1 0x322
+#define WM8996_AIF2_TX_LRCLK_2 0x323
+#define WM8996_AIF2_RX_LRCLK_1 0x324
+#define WM8996_AIF2_RX_LRCLK_2 0x325
+#define WM8996_AIF2TX_DATA_CONFIGURATION_1 0x326
+#define WM8996_AIF2TX_DATA_CONFIGURATION_2 0x327
+#define WM8996_AIF2RX_DATA_CONFIGURATION 0x328
+#define WM8996_AIF2TX_CHANNEL_0_CONFIGURATION 0x329
+#define WM8996_AIF2TX_CHANNEL_1_CONFIGURATION 0x32A
+#define WM8996_AIF2RX_CHANNEL_0_CONFIGURATION 0x32B
+#define WM8996_AIF2RX_CHANNEL_1_CONFIGURATION 0x32C
+#define WM8996_AIF2RX_MONO_CONFIGURATION 0x32D
+#define WM8996_AIF2TX_TEST 0x32F
+#define WM8996_DSP1_TX_LEFT_VOLUME 0x400
+#define WM8996_DSP1_TX_RIGHT_VOLUME 0x401
+#define WM8996_DSP1_RX_LEFT_VOLUME 0x402
+#define WM8996_DSP1_RX_RIGHT_VOLUME 0x403
+#define WM8996_DSP1_TX_FILTERS 0x410
+#define WM8996_DSP1_RX_FILTERS_1 0x420
+#define WM8996_DSP1_RX_FILTERS_2 0x421
+#define WM8996_DSP1_DRC_1 0x440
+#define WM8996_DSP1_DRC_2 0x441
+#define WM8996_DSP1_DRC_3 0x442
+#define WM8996_DSP1_DRC_4 0x443
+#define WM8996_DSP1_DRC_5 0x444
+#define WM8996_DSP1_RX_EQ_GAINS_1 0x480
+#define WM8996_DSP1_RX_EQ_GAINS_2 0x481
+#define WM8996_DSP1_RX_EQ_BAND_1_A 0x482
+#define WM8996_DSP1_RX_EQ_BAND_1_B 0x483
+#define WM8996_DSP1_RX_EQ_BAND_1_PG 0x484
+#define WM8996_DSP1_RX_EQ_BAND_2_A 0x485
+#define WM8996_DSP1_RX_EQ_BAND_2_B 0x486
+#define WM8996_DSP1_RX_EQ_BAND_2_C 0x487
+#define WM8996_DSP1_RX_EQ_BAND_2_PG 0x488
+#define WM8996_DSP1_RX_EQ_BAND_3_A 0x489
+#define WM8996_DSP1_RX_EQ_BAND_3_B 0x48A
+#define WM8996_DSP1_RX_EQ_BAND_3_C 0x48B
+#define WM8996_DSP1_RX_EQ_BAND_3_PG 0x48C
+#define WM8996_DSP1_RX_EQ_BAND_4_A 0x48D
+#define WM8996_DSP1_RX_EQ_BAND_4_B 0x48E
+#define WM8996_DSP1_RX_EQ_BAND_4_C 0x48F
+#define WM8996_DSP1_RX_EQ_BAND_4_PG 0x490
+#define WM8996_DSP1_RX_EQ_BAND_5_A 0x491
+#define WM8996_DSP1_RX_EQ_BAND_5_B 0x492
+#define WM8996_DSP1_RX_EQ_BAND_5_PG 0x493
+#define WM8996_DSP2_TX_LEFT_VOLUME 0x500
+#define WM8996_DSP2_TX_RIGHT_VOLUME 0x501
+#define WM8996_DSP2_RX_LEFT_VOLUME 0x502
+#define WM8996_DSP2_RX_RIGHT_VOLUME 0x503
+#define WM8996_DSP2_TX_FILTERS 0x510
+#define WM8996_DSP2_RX_FILTERS_1 0x520
+#define WM8996_DSP2_RX_FILTERS_2 0x521
+#define WM8996_DSP2_DRC_1 0x540
+#define WM8996_DSP2_DRC_2 0x541
+#define WM8996_DSP2_DRC_3 0x542
+#define WM8996_DSP2_DRC_4 0x543
+#define WM8996_DSP2_DRC_5 0x544
+#define WM8996_DSP2_RX_EQ_GAINS_1 0x580
+#define WM8996_DSP2_RX_EQ_GAINS_2 0x581
+#define WM8996_DSP2_RX_EQ_BAND_1_A 0x582
+#define WM8996_DSP2_RX_EQ_BAND_1_B 0x583
+#define WM8996_DSP2_RX_EQ_BAND_1_PG 0x584
+#define WM8996_DSP2_RX_EQ_BAND_2_A 0x585
+#define WM8996_DSP2_RX_EQ_BAND_2_B 0x586
+#define WM8996_DSP2_RX_EQ_BAND_2_C 0x587
+#define WM8996_DSP2_RX_EQ_BAND_2_PG 0x588
+#define WM8996_DSP2_RX_EQ_BAND_3_A 0x589
+#define WM8996_DSP2_RX_EQ_BAND_3_B 0x58A
+#define WM8996_DSP2_RX_EQ_BAND_3_C 0x58B
+#define WM8996_DSP2_RX_EQ_BAND_3_PG 0x58C
+#define WM8996_DSP2_RX_EQ_BAND_4_A 0x58D
+#define WM8996_DSP2_RX_EQ_BAND_4_B 0x58E
+#define WM8996_DSP2_RX_EQ_BAND_4_C 0x58F
+#define WM8996_DSP2_RX_EQ_BAND_4_PG 0x590
+#define WM8996_DSP2_RX_EQ_BAND_5_A 0x591
+#define WM8996_DSP2_RX_EQ_BAND_5_B 0x592
+#define WM8996_DSP2_RX_EQ_BAND_5_PG 0x593
+#define WM8996_DAC1_MIXER_VOLUMES 0x600
+#define WM8996_DAC1_LEFT_MIXER_ROUTING 0x601
+#define WM8996_DAC1_RIGHT_MIXER_ROUTING 0x602
+#define WM8996_DAC2_MIXER_VOLUMES 0x603
+#define WM8996_DAC2_LEFT_MIXER_ROUTING 0x604
+#define WM8996_DAC2_RIGHT_MIXER_ROUTING 0x605
+#define WM8996_DSP1_TX_LEFT_MIXER_ROUTING 0x606
+#define WM8996_DSP1_TX_RIGHT_MIXER_ROUTING 0x607
+#define WM8996_DSP2_TX_LEFT_MIXER_ROUTING 0x608
+#define WM8996_DSP2_TX_RIGHT_MIXER_ROUTING 0x609
+#define WM8996_DSP_TX_MIXER_SELECT 0x60A
+#define WM8996_DAC_SOFTMUTE 0x610
+#define WM8996_OVERSAMPLING 0x620
+#define WM8996_SIDETONE 0x621
+#define WM8996_GPIO_1 0x700
+#define WM8996_GPIO_2 0x701
+#define WM8996_GPIO_3 0x702
+#define WM8996_GPIO_4 0x703
+#define WM8996_GPIO_5 0x704
+#define WM8996_PULL_CONTROL_1 0x720
+#define WM8996_PULL_CONTROL_2 0x721
+#define WM8996_INTERRUPT_STATUS_1 0x730
+#define WM8996_INTERRUPT_STATUS_2 0x731
+#define WM8996_INTERRUPT_RAW_STATUS_2 0x732
+#define WM8996_INTERRUPT_STATUS_1_MASK 0x738
+#define WM8996_INTERRUPT_STATUS_2_MASK 0x739
+#define WM8996_INTERRUPT_CONTROL 0x740
+#define WM8996_LEFT_PDM_SPEAKER 0x800
+#define WM8996_RIGHT_PDM_SPEAKER 0x801
+#define WM8996_PDM_SPEAKER_MUTE_SEQUENCE 0x802
+#define WM8996_PDM_SPEAKER_VOLUME 0x803
+#define WM8996_WRITE_SEQUENCER_0 0x3000
+#define WM8996_WRITE_SEQUENCER_1 0x3001
+#define WM8996_WRITE_SEQUENCER_2 0x3002
+#define WM8996_WRITE_SEQUENCER_3 0x3003
+#define WM8996_WRITE_SEQUENCER_4 0x3004
+#define WM8996_WRITE_SEQUENCER_5 0x3005
+#define WM8996_WRITE_SEQUENCER_6 0x3006
+#define WM8996_WRITE_SEQUENCER_7 0x3007
+#define WM8996_WRITE_SEQUENCER_8 0x3008
+#define WM8996_WRITE_SEQUENCER_9 0x3009
+#define WM8996_WRITE_SEQUENCER_10 0x300A
+#define WM8996_WRITE_SEQUENCER_11 0x300B
+#define WM8996_WRITE_SEQUENCER_12 0x300C
+#define WM8996_WRITE_SEQUENCER_13 0x300D
+#define WM8996_WRITE_SEQUENCER_14 0x300E
+#define WM8996_WRITE_SEQUENCER_15 0x300F
+#define WM8996_WRITE_SEQUENCER_16 0x3010
+#define WM8996_WRITE_SEQUENCER_17 0x3011
+#define WM8996_WRITE_SEQUENCER_18 0x3012
+#define WM8996_WRITE_SEQUENCER_19 0x3013
+#define WM8996_WRITE_SEQUENCER_20 0x3014
+#define WM8996_WRITE_SEQUENCER_21 0x3015
+#define WM8996_WRITE_SEQUENCER_22 0x3016
+#define WM8996_WRITE_SEQUENCER_23 0x3017
+#define WM8996_WRITE_SEQUENCER_24 0x3018
+#define WM8996_WRITE_SEQUENCER_25 0x3019
+#define WM8996_WRITE_SEQUENCER_26 0x301A
+#define WM8996_WRITE_SEQUENCER_27 0x301B
+#define WM8996_WRITE_SEQUENCER_28 0x301C
+#define WM8996_WRITE_SEQUENCER_29 0x301D
+#define WM8996_WRITE_SEQUENCER_30 0x301E
+#define WM8996_WRITE_SEQUENCER_31 0x301F
+#define WM8996_WRITE_SEQUENCER_32 0x3020
+#define WM8996_WRITE_SEQUENCER_33 0x3021
+#define WM8996_WRITE_SEQUENCER_34 0x3022
+#define WM8996_WRITE_SEQUENCER_35 0x3023
+#define WM8996_WRITE_SEQUENCER_36 0x3024
+#define WM8996_WRITE_SEQUENCER_37 0x3025
+#define WM8996_WRITE_SEQUENCER_38 0x3026
+#define WM8996_WRITE_SEQUENCER_39 0x3027
+#define WM8996_WRITE_SEQUENCER_40 0x3028
+#define WM8996_WRITE_SEQUENCER_41 0x3029
+#define WM8996_WRITE_SEQUENCER_42 0x302A
+#define WM8996_WRITE_SEQUENCER_43 0x302B
+#define WM8996_WRITE_SEQUENCER_44 0x302C
+#define WM8996_WRITE_SEQUENCER_45 0x302D
+#define WM8996_WRITE_SEQUENCER_46 0x302E
+#define WM8996_WRITE_SEQUENCER_47 0x302F
+#define WM8996_WRITE_SEQUENCER_48 0x3030
+#define WM8996_WRITE_SEQUENCER_49 0x3031
+#define WM8996_WRITE_SEQUENCER_50 0x3032
+#define WM8996_WRITE_SEQUENCER_51 0x3033
+#define WM8996_WRITE_SEQUENCER_52 0x3034
+#define WM8996_WRITE_SEQUENCER_53 0x3035
+#define WM8996_WRITE_SEQUENCER_54 0x3036
+#define WM8996_WRITE_SEQUENCER_55 0x3037
+#define WM8996_WRITE_SEQUENCER_56 0x3038
+#define WM8996_WRITE_SEQUENCER_57 0x3039
+#define WM8996_WRITE_SEQUENCER_58 0x303A
+#define WM8996_WRITE_SEQUENCER_59 0x303B
+#define WM8996_WRITE_SEQUENCER_60 0x303C
+#define WM8996_WRITE_SEQUENCER_61 0x303D
+#define WM8996_WRITE_SEQUENCER_62 0x303E
+#define WM8996_WRITE_SEQUENCER_63 0x303F
+#define WM8996_WRITE_SEQUENCER_64 0x3040
+#define WM8996_WRITE_SEQUENCER_65 0x3041
+#define WM8996_WRITE_SEQUENCER_66 0x3042
+#define WM8996_WRITE_SEQUENCER_67 0x3043
+#define WM8996_WRITE_SEQUENCER_68 0x3044
+#define WM8996_WRITE_SEQUENCER_69 0x3045
+#define WM8996_WRITE_SEQUENCER_70 0x3046
+#define WM8996_WRITE_SEQUENCER_71 0x3047
+#define WM8996_WRITE_SEQUENCER_72 0x3048
+#define WM8996_WRITE_SEQUENCER_73 0x3049
+#define WM8996_WRITE_SEQUENCER_74 0x304A
+#define WM8996_WRITE_SEQUENCER_75 0x304B
+#define WM8996_WRITE_SEQUENCER_76 0x304C
+#define WM8996_WRITE_SEQUENCER_77 0x304D
+#define WM8996_WRITE_SEQUENCER_78 0x304E
+#define WM8996_WRITE_SEQUENCER_79 0x304F
+#define WM8996_WRITE_SEQUENCER_80 0x3050
+#define WM8996_WRITE_SEQUENCER_81 0x3051
+#define WM8996_WRITE_SEQUENCER_82 0x3052
+#define WM8996_WRITE_SEQUENCER_83 0x3053
+#define WM8996_WRITE_SEQUENCER_84 0x3054
+#define WM8996_WRITE_SEQUENCER_85 0x3055
+#define WM8996_WRITE_SEQUENCER_86 0x3056
+#define WM8996_WRITE_SEQUENCER_87 0x3057
+#define WM8996_WRITE_SEQUENCER_88 0x3058
+#define WM8996_WRITE_SEQUENCER_89 0x3059
+#define WM8996_WRITE_SEQUENCER_90 0x305A
+#define WM8996_WRITE_SEQUENCER_91 0x305B
+#define WM8996_WRITE_SEQUENCER_92 0x305C
+#define WM8996_WRITE_SEQUENCER_93 0x305D
+#define WM8996_WRITE_SEQUENCER_94 0x305E
+#define WM8996_WRITE_SEQUENCER_95 0x305F
+#define WM8996_WRITE_SEQUENCER_96 0x3060
+#define WM8996_WRITE_SEQUENCER_97 0x3061
+#define WM8996_WRITE_SEQUENCER_98 0x3062
+#define WM8996_WRITE_SEQUENCER_99 0x3063
+#define WM8996_WRITE_SEQUENCER_100 0x3064
+#define WM8996_WRITE_SEQUENCER_101 0x3065
+#define WM8996_WRITE_SEQUENCER_102 0x3066
+#define WM8996_WRITE_SEQUENCER_103 0x3067
+#define WM8996_WRITE_SEQUENCER_104 0x3068
+#define WM8996_WRITE_SEQUENCER_105 0x3069
+#define WM8996_WRITE_SEQUENCER_106 0x306A
+#define WM8996_WRITE_SEQUENCER_107 0x306B
+#define WM8996_WRITE_SEQUENCER_108 0x306C
+#define WM8996_WRITE_SEQUENCER_109 0x306D
+#define WM8996_WRITE_SEQUENCER_110 0x306E
+#define WM8996_WRITE_SEQUENCER_111 0x306F
+#define WM8996_WRITE_SEQUENCER_112 0x3070
+#define WM8996_WRITE_SEQUENCER_113 0x3071
+#define WM8996_WRITE_SEQUENCER_114 0x3072
+#define WM8996_WRITE_SEQUENCER_115 0x3073
+#define WM8996_WRITE_SEQUENCER_116 0x3074
+#define WM8996_WRITE_SEQUENCER_117 0x3075
+#define WM8996_WRITE_SEQUENCER_118 0x3076
+#define WM8996_WRITE_SEQUENCER_119 0x3077
+#define WM8996_WRITE_SEQUENCER_120 0x3078
+#define WM8996_WRITE_SEQUENCER_121 0x3079
+#define WM8996_WRITE_SEQUENCER_122 0x307A
+#define WM8996_WRITE_SEQUENCER_123 0x307B
+#define WM8996_WRITE_SEQUENCER_124 0x307C
+#define WM8996_WRITE_SEQUENCER_125 0x307D
+#define WM8996_WRITE_SEQUENCER_126 0x307E
+#define WM8996_WRITE_SEQUENCER_127 0x307F
+#define WM8996_WRITE_SEQUENCER_128 0x3080
+#define WM8996_WRITE_SEQUENCER_129 0x3081
+#define WM8996_WRITE_SEQUENCER_130 0x3082
+#define WM8996_WRITE_SEQUENCER_131 0x3083
+#define WM8996_WRITE_SEQUENCER_132 0x3084
+#define WM8996_WRITE_SEQUENCER_133 0x3085
+#define WM8996_WRITE_SEQUENCER_134 0x3086
+#define WM8996_WRITE_SEQUENCER_135 0x3087
+#define WM8996_WRITE_SEQUENCER_136 0x3088
+#define WM8996_WRITE_SEQUENCER_137 0x3089
+#define WM8996_WRITE_SEQUENCER_138 0x308A
+#define WM8996_WRITE_SEQUENCER_139 0x308B
+#define WM8996_WRITE_SEQUENCER_140 0x308C
+#define WM8996_WRITE_SEQUENCER_141 0x308D
+#define WM8996_WRITE_SEQUENCER_142 0x308E
+#define WM8996_WRITE_SEQUENCER_143 0x308F
+#define WM8996_WRITE_SEQUENCER_144 0x3090
+#define WM8996_WRITE_SEQUENCER_145 0x3091
+#define WM8996_WRITE_SEQUENCER_146 0x3092
+#define WM8996_WRITE_SEQUENCER_147 0x3093
+#define WM8996_WRITE_SEQUENCER_148 0x3094
+#define WM8996_WRITE_SEQUENCER_149 0x3095
+#define WM8996_WRITE_SEQUENCER_150 0x3096
+#define WM8996_WRITE_SEQUENCER_151 0x3097
+#define WM8996_WRITE_SEQUENCER_152 0x3098
+#define WM8996_WRITE_SEQUENCER_153 0x3099
+#define WM8996_WRITE_SEQUENCER_154 0x309A
+#define WM8996_WRITE_SEQUENCER_155 0x309B
+#define WM8996_WRITE_SEQUENCER_156 0x309C
+#define WM8996_WRITE_SEQUENCER_157 0x309D
+#define WM8996_WRITE_SEQUENCER_158 0x309E
+#define WM8996_WRITE_SEQUENCER_159 0x309F
+#define WM8996_WRITE_SEQUENCER_160 0x30A0
+#define WM8996_WRITE_SEQUENCER_161 0x30A1
+#define WM8996_WRITE_SEQUENCER_162 0x30A2
+#define WM8996_WRITE_SEQUENCER_163 0x30A3
+#define WM8996_WRITE_SEQUENCER_164 0x30A4
+#define WM8996_WRITE_SEQUENCER_165 0x30A5
+#define WM8996_WRITE_SEQUENCER_166 0x30A6
+#define WM8996_WRITE_SEQUENCER_167 0x30A7
+#define WM8996_WRITE_SEQUENCER_168 0x30A8
+#define WM8996_WRITE_SEQUENCER_169 0x30A9
+#define WM8996_WRITE_SEQUENCER_170 0x30AA
+#define WM8996_WRITE_SEQUENCER_171 0x30AB
+#define WM8996_WRITE_SEQUENCER_172 0x30AC
+#define WM8996_WRITE_SEQUENCER_173 0x30AD
+#define WM8996_WRITE_SEQUENCER_174 0x30AE
+#define WM8996_WRITE_SEQUENCER_175 0x30AF
+#define WM8996_WRITE_SEQUENCER_176 0x30B0
+#define WM8996_WRITE_SEQUENCER_177 0x30B1
+#define WM8996_WRITE_SEQUENCER_178 0x30B2
+#define WM8996_WRITE_SEQUENCER_179 0x30B3
+#define WM8996_WRITE_SEQUENCER_180 0x30B4
+#define WM8996_WRITE_SEQUENCER_181 0x30B5
+#define WM8996_WRITE_SEQUENCER_182 0x30B6
+#define WM8996_WRITE_SEQUENCER_183 0x30B7
+#define WM8996_WRITE_SEQUENCER_184 0x30B8
+#define WM8996_WRITE_SEQUENCER_185 0x30B9
+#define WM8996_WRITE_SEQUENCER_186 0x30BA
+#define WM8996_WRITE_SEQUENCER_187 0x30BB
+#define WM8996_WRITE_SEQUENCER_188 0x30BC
+#define WM8996_WRITE_SEQUENCER_189 0x30BD
+#define WM8996_WRITE_SEQUENCER_190 0x30BE
+#define WM8996_WRITE_SEQUENCER_191 0x30BF
+#define WM8996_WRITE_SEQUENCER_192 0x30C0
+#define WM8996_WRITE_SEQUENCER_193 0x30C1
+#define WM8996_WRITE_SEQUENCER_194 0x30C2
+#define WM8996_WRITE_SEQUENCER_195 0x30C3
+#define WM8996_WRITE_SEQUENCER_196 0x30C4
+#define WM8996_WRITE_SEQUENCER_197 0x30C5
+#define WM8996_WRITE_SEQUENCER_198 0x30C6
+#define WM8996_WRITE_SEQUENCER_199 0x30C7
+#define WM8996_WRITE_SEQUENCER_200 0x30C8
+#define WM8996_WRITE_SEQUENCER_201 0x30C9
+#define WM8996_WRITE_SEQUENCER_202 0x30CA
+#define WM8996_WRITE_SEQUENCER_203 0x30CB
+#define WM8996_WRITE_SEQUENCER_204 0x30CC
+#define WM8996_WRITE_SEQUENCER_205 0x30CD
+#define WM8996_WRITE_SEQUENCER_206 0x30CE
+#define WM8996_WRITE_SEQUENCER_207 0x30CF
+#define WM8996_WRITE_SEQUENCER_208 0x30D0
+#define WM8996_WRITE_SEQUENCER_209 0x30D1
+#define WM8996_WRITE_SEQUENCER_210 0x30D2
+#define WM8996_WRITE_SEQUENCER_211 0x30D3
+#define WM8996_WRITE_SEQUENCER_212 0x30D4
+#define WM8996_WRITE_SEQUENCER_213 0x30D5
+#define WM8996_WRITE_SEQUENCER_214 0x30D6
+#define WM8996_WRITE_SEQUENCER_215 0x30D7
+#define WM8996_WRITE_SEQUENCER_216 0x30D8
+#define WM8996_WRITE_SEQUENCER_217 0x30D9
+#define WM8996_WRITE_SEQUENCER_218 0x30DA
+#define WM8996_WRITE_SEQUENCER_219 0x30DB
+#define WM8996_WRITE_SEQUENCER_220 0x30DC
+#define WM8996_WRITE_SEQUENCER_221 0x30DD
+#define WM8996_WRITE_SEQUENCER_222 0x30DE
+#define WM8996_WRITE_SEQUENCER_223 0x30DF
+#define WM8996_WRITE_SEQUENCER_224 0x30E0
+#define WM8996_WRITE_SEQUENCER_225 0x30E1
+#define WM8996_WRITE_SEQUENCER_226 0x30E2
+#define WM8996_WRITE_SEQUENCER_227 0x30E3
+#define WM8996_WRITE_SEQUENCER_228 0x30E4
+#define WM8996_WRITE_SEQUENCER_229 0x30E5
+#define WM8996_WRITE_SEQUENCER_230 0x30E6
+#define WM8996_WRITE_SEQUENCER_231 0x30E7
+#define WM8996_WRITE_SEQUENCER_232 0x30E8
+#define WM8996_WRITE_SEQUENCER_233 0x30E9
+#define WM8996_WRITE_SEQUENCER_234 0x30EA
+#define WM8996_WRITE_SEQUENCER_235 0x30EB
+#define WM8996_WRITE_SEQUENCER_236 0x30EC
+#define WM8996_WRITE_SEQUENCER_237 0x30ED
+#define WM8996_WRITE_SEQUENCER_238 0x30EE
+#define WM8996_WRITE_SEQUENCER_239 0x30EF
+#define WM8996_WRITE_SEQUENCER_240 0x30F0
+#define WM8996_WRITE_SEQUENCER_241 0x30F1
+#define WM8996_WRITE_SEQUENCER_242 0x30F2
+#define WM8996_WRITE_SEQUENCER_243 0x30F3
+#define WM8996_WRITE_SEQUENCER_244 0x30F4
+#define WM8996_WRITE_SEQUENCER_245 0x30F5
+#define WM8996_WRITE_SEQUENCER_246 0x30F6
+#define WM8996_WRITE_SEQUENCER_247 0x30F7
+#define WM8996_WRITE_SEQUENCER_248 0x30F8
+#define WM8996_WRITE_SEQUENCER_249 0x30F9
+#define WM8996_WRITE_SEQUENCER_250 0x30FA
+#define WM8996_WRITE_SEQUENCER_251 0x30FB
+#define WM8996_WRITE_SEQUENCER_252 0x30FC
+#define WM8996_WRITE_SEQUENCER_253 0x30FD
+#define WM8996_WRITE_SEQUENCER_254 0x30FE
+#define WM8996_WRITE_SEQUENCER_255 0x30FF
+#define WM8996_WRITE_SEQUENCER_256 0x3100
+#define WM8996_WRITE_SEQUENCER_257 0x3101
+#define WM8996_WRITE_SEQUENCER_258 0x3102
+#define WM8996_WRITE_SEQUENCER_259 0x3103
+#define WM8996_WRITE_SEQUENCER_260 0x3104
+#define WM8996_WRITE_SEQUENCER_261 0x3105
+#define WM8996_WRITE_SEQUENCER_262 0x3106
+#define WM8996_WRITE_SEQUENCER_263 0x3107
+#define WM8996_WRITE_SEQUENCER_264 0x3108
+#define WM8996_WRITE_SEQUENCER_265 0x3109
+#define WM8996_WRITE_SEQUENCER_266 0x310A
+#define WM8996_WRITE_SEQUENCER_267 0x310B
+#define WM8996_WRITE_SEQUENCER_268 0x310C
+#define WM8996_WRITE_SEQUENCER_269 0x310D
+#define WM8996_WRITE_SEQUENCER_270 0x310E
+#define WM8996_WRITE_SEQUENCER_271 0x310F
+#define WM8996_WRITE_SEQUENCER_272 0x3110
+#define WM8996_WRITE_SEQUENCER_273 0x3111
+#define WM8996_WRITE_SEQUENCER_274 0x3112
+#define WM8996_WRITE_SEQUENCER_275 0x3113
+#define WM8996_WRITE_SEQUENCER_276 0x3114
+#define WM8996_WRITE_SEQUENCER_277 0x3115
+#define WM8996_WRITE_SEQUENCER_278 0x3116
+#define WM8996_WRITE_SEQUENCER_279 0x3117
+#define WM8996_WRITE_SEQUENCER_280 0x3118
+#define WM8996_WRITE_SEQUENCER_281 0x3119
+#define WM8996_WRITE_SEQUENCER_282 0x311A
+#define WM8996_WRITE_SEQUENCER_283 0x311B
+#define WM8996_WRITE_SEQUENCER_284 0x311C
+#define WM8996_WRITE_SEQUENCER_285 0x311D
+#define WM8996_WRITE_SEQUENCER_286 0x311E
+#define WM8996_WRITE_SEQUENCER_287 0x311F
+#define WM8996_WRITE_SEQUENCER_288 0x3120
+#define WM8996_WRITE_SEQUENCER_289 0x3121
+#define WM8996_WRITE_SEQUENCER_290 0x3122
+#define WM8996_WRITE_SEQUENCER_291 0x3123
+#define WM8996_WRITE_SEQUENCER_292 0x3124
+#define WM8996_WRITE_SEQUENCER_293 0x3125
+#define WM8996_WRITE_SEQUENCER_294 0x3126
+#define WM8996_WRITE_SEQUENCER_295 0x3127
+#define WM8996_WRITE_SEQUENCER_296 0x3128
+#define WM8996_WRITE_SEQUENCER_297 0x3129
+#define WM8996_WRITE_SEQUENCER_298 0x312A
+#define WM8996_WRITE_SEQUENCER_299 0x312B
+#define WM8996_WRITE_SEQUENCER_300 0x312C
+#define WM8996_WRITE_SEQUENCER_301 0x312D
+#define WM8996_WRITE_SEQUENCER_302 0x312E
+#define WM8996_WRITE_SEQUENCER_303 0x312F
+#define WM8996_WRITE_SEQUENCER_304 0x3130
+#define WM8996_WRITE_SEQUENCER_305 0x3131
+#define WM8996_WRITE_SEQUENCER_306 0x3132
+#define WM8996_WRITE_SEQUENCER_307 0x3133
+#define WM8996_WRITE_SEQUENCER_308 0x3134
+#define WM8996_WRITE_SEQUENCER_309 0x3135
+#define WM8996_WRITE_SEQUENCER_310 0x3136
+#define WM8996_WRITE_SEQUENCER_311 0x3137
+#define WM8996_WRITE_SEQUENCER_312 0x3138
+#define WM8996_WRITE_SEQUENCER_313 0x3139
+#define WM8996_WRITE_SEQUENCER_314 0x313A
+#define WM8996_WRITE_SEQUENCER_315 0x313B
+#define WM8996_WRITE_SEQUENCER_316 0x313C
+#define WM8996_WRITE_SEQUENCER_317 0x313D
+#define WM8996_WRITE_SEQUENCER_318 0x313E
+#define WM8996_WRITE_SEQUENCER_319 0x313F
+#define WM8996_WRITE_SEQUENCER_320 0x3140
+#define WM8996_WRITE_SEQUENCER_321 0x3141
+#define WM8996_WRITE_SEQUENCER_322 0x3142
+#define WM8996_WRITE_SEQUENCER_323 0x3143
+#define WM8996_WRITE_SEQUENCER_324 0x3144
+#define WM8996_WRITE_SEQUENCER_325 0x3145
+#define WM8996_WRITE_SEQUENCER_326 0x3146
+#define WM8996_WRITE_SEQUENCER_327 0x3147
+#define WM8996_WRITE_SEQUENCER_328 0x3148
+#define WM8996_WRITE_SEQUENCER_329 0x3149
+#define WM8996_WRITE_SEQUENCER_330 0x314A
+#define WM8996_WRITE_SEQUENCER_331 0x314B
+#define WM8996_WRITE_SEQUENCER_332 0x314C
+#define WM8996_WRITE_SEQUENCER_333 0x314D
+#define WM8996_WRITE_SEQUENCER_334 0x314E
+#define WM8996_WRITE_SEQUENCER_335 0x314F
+#define WM8996_WRITE_SEQUENCER_336 0x3150
+#define WM8996_WRITE_SEQUENCER_337 0x3151
+#define WM8996_WRITE_SEQUENCER_338 0x3152
+#define WM8996_WRITE_SEQUENCER_339 0x3153
+#define WM8996_WRITE_SEQUENCER_340 0x3154
+#define WM8996_WRITE_SEQUENCER_341 0x3155
+#define WM8996_WRITE_SEQUENCER_342 0x3156
+#define WM8996_WRITE_SEQUENCER_343 0x3157
+#define WM8996_WRITE_SEQUENCER_344 0x3158
+#define WM8996_WRITE_SEQUENCER_345 0x3159
+#define WM8996_WRITE_SEQUENCER_346 0x315A
+#define WM8996_WRITE_SEQUENCER_347 0x315B
+#define WM8996_WRITE_SEQUENCER_348 0x315C
+#define WM8996_WRITE_SEQUENCER_349 0x315D
+#define WM8996_WRITE_SEQUENCER_350 0x315E
+#define WM8996_WRITE_SEQUENCER_351 0x315F
+#define WM8996_WRITE_SEQUENCER_352 0x3160
+#define WM8996_WRITE_SEQUENCER_353 0x3161
+#define WM8996_WRITE_SEQUENCER_354 0x3162
+#define WM8996_WRITE_SEQUENCER_355 0x3163
+#define WM8996_WRITE_SEQUENCER_356 0x3164
+#define WM8996_WRITE_SEQUENCER_357 0x3165
+#define WM8996_WRITE_SEQUENCER_358 0x3166
+#define WM8996_WRITE_SEQUENCER_359 0x3167
+#define WM8996_WRITE_SEQUENCER_360 0x3168
+#define WM8996_WRITE_SEQUENCER_361 0x3169
+#define WM8996_WRITE_SEQUENCER_362 0x316A
+#define WM8996_WRITE_SEQUENCER_363 0x316B
+#define WM8996_WRITE_SEQUENCER_364 0x316C
+#define WM8996_WRITE_SEQUENCER_365 0x316D
+#define WM8996_WRITE_SEQUENCER_366 0x316E
+#define WM8996_WRITE_SEQUENCER_367 0x316F
+#define WM8996_WRITE_SEQUENCER_368 0x3170
+#define WM8996_WRITE_SEQUENCER_369 0x3171
+#define WM8996_WRITE_SEQUENCER_370 0x3172
+#define WM8996_WRITE_SEQUENCER_371 0x3173
+#define WM8996_WRITE_SEQUENCER_372 0x3174
+#define WM8996_WRITE_SEQUENCER_373 0x3175
+#define WM8996_WRITE_SEQUENCER_374 0x3176
+#define WM8996_WRITE_SEQUENCER_375 0x3177
+#define WM8996_WRITE_SEQUENCER_376 0x3178
+#define WM8996_WRITE_SEQUENCER_377 0x3179
+#define WM8996_WRITE_SEQUENCER_378 0x317A
+#define WM8996_WRITE_SEQUENCER_379 0x317B
+#define WM8996_WRITE_SEQUENCER_380 0x317C
+#define WM8996_WRITE_SEQUENCER_381 0x317D
+#define WM8996_WRITE_SEQUENCER_382 0x317E
+#define WM8996_WRITE_SEQUENCER_383 0x317F
+#define WM8996_WRITE_SEQUENCER_384 0x3180
+#define WM8996_WRITE_SEQUENCER_385 0x3181
+#define WM8996_WRITE_SEQUENCER_386 0x3182
+#define WM8996_WRITE_SEQUENCER_387 0x3183
+#define WM8996_WRITE_SEQUENCER_388 0x3184
+#define WM8996_WRITE_SEQUENCER_389 0x3185
+#define WM8996_WRITE_SEQUENCER_390 0x3186
+#define WM8996_WRITE_SEQUENCER_391 0x3187
+#define WM8996_WRITE_SEQUENCER_392 0x3188
+#define WM8996_WRITE_SEQUENCER_393 0x3189
+#define WM8996_WRITE_SEQUENCER_394 0x318A
+#define WM8996_WRITE_SEQUENCER_395 0x318B
+#define WM8996_WRITE_SEQUENCER_396 0x318C
+#define WM8996_WRITE_SEQUENCER_397 0x318D
+#define WM8996_WRITE_SEQUENCER_398 0x318E
+#define WM8996_WRITE_SEQUENCER_399 0x318F
+#define WM8996_WRITE_SEQUENCER_400 0x3190
+#define WM8996_WRITE_SEQUENCER_401 0x3191
+#define WM8996_WRITE_SEQUENCER_402 0x3192
+#define WM8996_WRITE_SEQUENCER_403 0x3193
+#define WM8996_WRITE_SEQUENCER_404 0x3194
+#define WM8996_WRITE_SEQUENCER_405 0x3195
+#define WM8996_WRITE_SEQUENCER_406 0x3196
+#define WM8996_WRITE_SEQUENCER_407 0x3197
+#define WM8996_WRITE_SEQUENCER_408 0x3198
+#define WM8996_WRITE_SEQUENCER_409 0x3199
+#define WM8996_WRITE_SEQUENCER_410 0x319A
+#define WM8996_WRITE_SEQUENCER_411 0x319B
+#define WM8996_WRITE_SEQUENCER_412 0x319C
+#define WM8996_WRITE_SEQUENCER_413 0x319D
+#define WM8996_WRITE_SEQUENCER_414 0x319E
+#define WM8996_WRITE_SEQUENCER_415 0x319F
+#define WM8996_WRITE_SEQUENCER_416 0x31A0
+#define WM8996_WRITE_SEQUENCER_417 0x31A1
+#define WM8996_WRITE_SEQUENCER_418 0x31A2
+#define WM8996_WRITE_SEQUENCER_419 0x31A3
+#define WM8996_WRITE_SEQUENCER_420 0x31A4
+#define WM8996_WRITE_SEQUENCER_421 0x31A5
+#define WM8996_WRITE_SEQUENCER_422 0x31A6
+#define WM8996_WRITE_SEQUENCER_423 0x31A7
+#define WM8996_WRITE_SEQUENCER_424 0x31A8
+#define WM8996_WRITE_SEQUENCER_425 0x31A9
+#define WM8996_WRITE_SEQUENCER_426 0x31AA
+#define WM8996_WRITE_SEQUENCER_427 0x31AB
+#define WM8996_WRITE_SEQUENCER_428 0x31AC
+#define WM8996_WRITE_SEQUENCER_429 0x31AD
+#define WM8996_WRITE_SEQUENCER_430 0x31AE
+#define WM8996_WRITE_SEQUENCER_431 0x31AF
+#define WM8996_WRITE_SEQUENCER_432 0x31B0
+#define WM8996_WRITE_SEQUENCER_433 0x31B1
+#define WM8996_WRITE_SEQUENCER_434 0x31B2
+#define WM8996_WRITE_SEQUENCER_435 0x31B3
+#define WM8996_WRITE_SEQUENCER_436 0x31B4
+#define WM8996_WRITE_SEQUENCER_437 0x31B5
+#define WM8996_WRITE_SEQUENCER_438 0x31B6
+#define WM8996_WRITE_SEQUENCER_439 0x31B7
+#define WM8996_WRITE_SEQUENCER_440 0x31B8
+#define WM8996_WRITE_SEQUENCER_441 0x31B9
+#define WM8996_WRITE_SEQUENCER_442 0x31BA
+#define WM8996_WRITE_SEQUENCER_443 0x31BB
+#define WM8996_WRITE_SEQUENCER_444 0x31BC
+#define WM8996_WRITE_SEQUENCER_445 0x31BD
+#define WM8996_WRITE_SEQUENCER_446 0x31BE
+#define WM8996_WRITE_SEQUENCER_447 0x31BF
+#define WM8996_WRITE_SEQUENCER_448 0x31C0
+#define WM8996_WRITE_SEQUENCER_449 0x31C1
+#define WM8996_WRITE_SEQUENCER_450 0x31C2
+#define WM8996_WRITE_SEQUENCER_451 0x31C3
+#define WM8996_WRITE_SEQUENCER_452 0x31C4
+#define WM8996_WRITE_SEQUENCER_453 0x31C5
+#define WM8996_WRITE_SEQUENCER_454 0x31C6
+#define WM8996_WRITE_SEQUENCER_455 0x31C7
+#define WM8996_WRITE_SEQUENCER_456 0x31C8
+#define WM8996_WRITE_SEQUENCER_457 0x31C9
+#define WM8996_WRITE_SEQUENCER_458 0x31CA
+#define WM8996_WRITE_SEQUENCER_459 0x31CB
+#define WM8996_WRITE_SEQUENCER_460 0x31CC
+#define WM8996_WRITE_SEQUENCER_461 0x31CD
+#define WM8996_WRITE_SEQUENCER_462 0x31CE
+#define WM8996_WRITE_SEQUENCER_463 0x31CF
+#define WM8996_WRITE_SEQUENCER_464 0x31D0
+#define WM8996_WRITE_SEQUENCER_465 0x31D1
+#define WM8996_WRITE_SEQUENCER_466 0x31D2
+#define WM8996_WRITE_SEQUENCER_467 0x31D3
+#define WM8996_WRITE_SEQUENCER_468 0x31D4
+#define WM8996_WRITE_SEQUENCER_469 0x31D5
+#define WM8996_WRITE_SEQUENCER_470 0x31D6
+#define WM8996_WRITE_SEQUENCER_471 0x31D7
+#define WM8996_WRITE_SEQUENCER_472 0x31D8
+#define WM8996_WRITE_SEQUENCER_473 0x31D9
+#define WM8996_WRITE_SEQUENCER_474 0x31DA
+#define WM8996_WRITE_SEQUENCER_475 0x31DB
+#define WM8996_WRITE_SEQUENCER_476 0x31DC
+#define WM8996_WRITE_SEQUENCER_477 0x31DD
+#define WM8996_WRITE_SEQUENCER_478 0x31DE
+#define WM8996_WRITE_SEQUENCER_479 0x31DF
+#define WM8996_WRITE_SEQUENCER_480 0x31E0
+#define WM8996_WRITE_SEQUENCER_481 0x31E1
+#define WM8996_WRITE_SEQUENCER_482 0x31E2
+#define WM8996_WRITE_SEQUENCER_483 0x31E3
+#define WM8996_WRITE_SEQUENCER_484 0x31E4
+#define WM8996_WRITE_SEQUENCER_485 0x31E5
+#define WM8996_WRITE_SEQUENCER_486 0x31E6
+#define WM8996_WRITE_SEQUENCER_487 0x31E7
+#define WM8996_WRITE_SEQUENCER_488 0x31E8
+#define WM8996_WRITE_SEQUENCER_489 0x31E9
+#define WM8996_WRITE_SEQUENCER_490 0x31EA
+#define WM8996_WRITE_SEQUENCER_491 0x31EB
+#define WM8996_WRITE_SEQUENCER_492 0x31EC
+#define WM8996_WRITE_SEQUENCER_493 0x31ED
+#define WM8996_WRITE_SEQUENCER_494 0x31EE
+#define WM8996_WRITE_SEQUENCER_495 0x31EF
+#define WM8996_WRITE_SEQUENCER_496 0x31F0
+#define WM8996_WRITE_SEQUENCER_497 0x31F1
+#define WM8996_WRITE_SEQUENCER_498 0x31F2
+#define WM8996_WRITE_SEQUENCER_499 0x31F3
+#define WM8996_WRITE_SEQUENCER_500 0x31F4
+#define WM8996_WRITE_SEQUENCER_501 0x31F5
+#define WM8996_WRITE_SEQUENCER_502 0x31F6
+#define WM8996_WRITE_SEQUENCER_503 0x31F7
+#define WM8996_WRITE_SEQUENCER_504 0x31F8
+#define WM8996_WRITE_SEQUENCER_505 0x31F9
+#define WM8996_WRITE_SEQUENCER_506 0x31FA
+#define WM8996_WRITE_SEQUENCER_507 0x31FB
+#define WM8996_WRITE_SEQUENCER_508 0x31FC
+#define WM8996_WRITE_SEQUENCER_509 0x31FD
+#define WM8996_WRITE_SEQUENCER_510 0x31FE
+#define WM8996_WRITE_SEQUENCER_511 0x31FF
+
+#define WM8996_REGISTER_COUNT 706
+#define WM8996_MAX_REGISTER 0x31FF
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM8996_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */
+#define WM8996_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */
+#define WM8996_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8996_MICB2_ENA 0x0200 /* MICB2_ENA */
+#define WM8996_MICB2_ENA_MASK 0x0200 /* MICB2_ENA */
+#define WM8996_MICB2_ENA_SHIFT 9 /* MICB2_ENA */
+#define WM8996_MICB2_ENA_WIDTH 1 /* MICB2_ENA */
+#define WM8996_MICB1_ENA 0x0100 /* MICB1_ENA */
+#define WM8996_MICB1_ENA_MASK 0x0100 /* MICB1_ENA */
+#define WM8996_MICB1_ENA_SHIFT 8 /* MICB1_ENA */
+#define WM8996_MICB1_ENA_WIDTH 1 /* MICB1_ENA */
+#define WM8996_HPOUT2L_ENA 0x0080 /* HPOUT2L_ENA */
+#define WM8996_HPOUT2L_ENA_MASK 0x0080 /* HPOUT2L_ENA */
+#define WM8996_HPOUT2L_ENA_SHIFT 7 /* HPOUT2L_ENA */
+#define WM8996_HPOUT2L_ENA_WIDTH 1 /* HPOUT2L_ENA */
+#define WM8996_HPOUT2R_ENA 0x0040 /* HPOUT2R_ENA */
+#define WM8996_HPOUT2R_ENA_MASK 0x0040 /* HPOUT2R_ENA */
+#define WM8996_HPOUT2R_ENA_SHIFT 6 /* HPOUT2R_ENA */
+#define WM8996_HPOUT2R_ENA_WIDTH 1 /* HPOUT2R_ENA */
+#define WM8996_HPOUT1L_ENA 0x0020 /* HPOUT1L_ENA */
+#define WM8996_HPOUT1L_ENA_MASK 0x0020 /* HPOUT1L_ENA */
+#define WM8996_HPOUT1L_ENA_SHIFT 5 /* HPOUT1L_ENA */
+#define WM8996_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */
+#define WM8996_HPOUT1R_ENA 0x0010 /* HPOUT1R_ENA */
+#define WM8996_HPOUT1R_ENA_MASK 0x0010 /* HPOUT1R_ENA */
+#define WM8996_HPOUT1R_ENA_SHIFT 4 /* HPOUT1R_ENA */
+#define WM8996_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */
+#define WM8996_BG_ENA 0x0001 /* BG_ENA */
+#define WM8996_BG_ENA_MASK 0x0001 /* BG_ENA */
+#define WM8996_BG_ENA_SHIFT 0 /* BG_ENA */
+#define WM8996_BG_ENA_WIDTH 1 /* BG_ENA */
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8996_OPCLK_ENA 0x0800 /* OPCLK_ENA */
+#define WM8996_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */
+#define WM8996_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */
+#define WM8996_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */
+#define WM8996_INL_ENA 0x0020 /* INL_ENA */
+#define WM8996_INL_ENA_MASK 0x0020 /* INL_ENA */
+#define WM8996_INL_ENA_SHIFT 5 /* INL_ENA */
+#define WM8996_INL_ENA_WIDTH 1 /* INL_ENA */
+#define WM8996_INR_ENA 0x0010 /* INR_ENA */
+#define WM8996_INR_ENA_MASK 0x0010 /* INR_ENA */
+#define WM8996_INR_ENA_SHIFT 4 /* INR_ENA */
+#define WM8996_INR_ENA_WIDTH 1 /* INR_ENA */
+#define WM8996_LDO2_ENA 0x0002 /* LDO2_ENA */
+#define WM8996_LDO2_ENA_MASK 0x0002 /* LDO2_ENA */
+#define WM8996_LDO2_ENA_SHIFT 1 /* LDO2_ENA */
+#define WM8996_LDO2_ENA_WIDTH 1 /* LDO2_ENA */
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8996_DSP2RXL_ENA 0x0800 /* DSP2RXL_ENA */
+#define WM8996_DSP2RXL_ENA_MASK 0x0800 /* DSP2RXL_ENA */
+#define WM8996_DSP2RXL_ENA_SHIFT 11 /* DSP2RXL_ENA */
+#define WM8996_DSP2RXL_ENA_WIDTH 1 /* DSP2RXL_ENA */
+#define WM8996_DSP2RXR_ENA 0x0400 /* DSP2RXR_ENA */
+#define WM8996_DSP2RXR_ENA_MASK 0x0400 /* DSP2RXR_ENA */
+#define WM8996_DSP2RXR_ENA_SHIFT 10 /* DSP2RXR_ENA */
+#define WM8996_DSP2RXR_ENA_WIDTH 1 /* DSP2RXR_ENA */
+#define WM8996_DSP1RXL_ENA 0x0200 /* DSP1RXL_ENA */
+#define WM8996_DSP1RXL_ENA_MASK 0x0200 /* DSP1RXL_ENA */
+#define WM8996_DSP1RXL_ENA_SHIFT 9 /* DSP1RXL_ENA */
+#define WM8996_DSP1RXL_ENA_WIDTH 1 /* DSP1RXL_ENA */
+#define WM8996_DSP1RXR_ENA 0x0100 /* DSP1RXR_ENA */
+#define WM8996_DSP1RXR_ENA_MASK 0x0100 /* DSP1RXR_ENA */
+#define WM8996_DSP1RXR_ENA_SHIFT 8 /* DSP1RXR_ENA */
+#define WM8996_DSP1RXR_ENA_WIDTH 1 /* DSP1RXR_ENA */
+#define WM8996_DMIC2L_ENA 0x0020 /* DMIC2L_ENA */
+#define WM8996_DMIC2L_ENA_MASK 0x0020 /* DMIC2L_ENA */
+#define WM8996_DMIC2L_ENA_SHIFT 5 /* DMIC2L_ENA */
+#define WM8996_DMIC2L_ENA_WIDTH 1 /* DMIC2L_ENA */
+#define WM8996_DMIC2R_ENA 0x0010 /* DMIC2R_ENA */
+#define WM8996_DMIC2R_ENA_MASK 0x0010 /* DMIC2R_ENA */
+#define WM8996_DMIC2R_ENA_SHIFT 4 /* DMIC2R_ENA */
+#define WM8996_DMIC2R_ENA_WIDTH 1 /* DMIC2R_ENA */
+#define WM8996_DMIC1L_ENA 0x0008 /* DMIC1L_ENA */
+#define WM8996_DMIC1L_ENA_MASK 0x0008 /* DMIC1L_ENA */
+#define WM8996_DMIC1L_ENA_SHIFT 3 /* DMIC1L_ENA */
+#define WM8996_DMIC1L_ENA_WIDTH 1 /* DMIC1L_ENA */
+#define WM8996_DMIC1R_ENA 0x0004 /* DMIC1R_ENA */
+#define WM8996_DMIC1R_ENA_MASK 0x0004 /* DMIC1R_ENA */
+#define WM8996_DMIC1R_ENA_SHIFT 2 /* DMIC1R_ENA */
+#define WM8996_DMIC1R_ENA_WIDTH 1 /* DMIC1R_ENA */
+#define WM8996_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8996_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */
+#define WM8996_ADCL_ENA_SHIFT 1 /* ADCL_ENA */
+#define WM8996_ADCL_ENA_WIDTH 1 /* ADCL_ENA */
+#define WM8996_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8996_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */
+#define WM8996_ADCR_ENA_SHIFT 0 /* ADCR_ENA */
+#define WM8996_ADCR_ENA_WIDTH 1 /* ADCR_ENA */
+
+/*
+ * R4 (0x04) - Power Management (4)
+ */
+#define WM8996_AIF2RX_CHAN1_ENA 0x0200 /* AIF2RX_CHAN1_ENA */
+#define WM8996_AIF2RX_CHAN1_ENA_MASK 0x0200 /* AIF2RX_CHAN1_ENA */
+#define WM8996_AIF2RX_CHAN1_ENA_SHIFT 9 /* AIF2RX_CHAN1_ENA */
+#define WM8996_AIF2RX_CHAN1_ENA_WIDTH 1 /* AIF2RX_CHAN1_ENA */
+#define WM8996_AIF2RX_CHAN0_ENA 0x0100 /* AIF2RX_CHAN0_ENA */
+#define WM8996_AIF2RX_CHAN0_ENA_MASK 0x0100 /* AIF2RX_CHAN0_ENA */
+#define WM8996_AIF2RX_CHAN0_ENA_SHIFT 8 /* AIF2RX_CHAN0_ENA */
+#define WM8996_AIF2RX_CHAN0_ENA_WIDTH 1 /* AIF2RX_CHAN0_ENA */
+#define WM8996_AIF1RX_CHAN5_ENA 0x0020 /* AIF1RX_CHAN5_ENA */
+#define WM8996_AIF1RX_CHAN5_ENA_MASK 0x0020 /* AIF1RX_CHAN5_ENA */
+#define WM8996_AIF1RX_CHAN5_ENA_SHIFT 5 /* AIF1RX_CHAN5_ENA */
+#define WM8996_AIF1RX_CHAN5_ENA_WIDTH 1 /* AIF1RX_CHAN5_ENA */
+#define WM8996_AIF1RX_CHAN4_ENA 0x0010 /* AIF1RX_CHAN4_ENA */
+#define WM8996_AIF1RX_CHAN4_ENA_MASK 0x0010 /* AIF1RX_CHAN4_ENA */
+#define WM8996_AIF1RX_CHAN4_ENA_SHIFT 4 /* AIF1RX_CHAN4_ENA */
+#define WM8996_AIF1RX_CHAN4_ENA_WIDTH 1 /* AIF1RX_CHAN4_ENA */
+#define WM8996_AIF1RX_CHAN3_ENA 0x0008 /* AIF1RX_CHAN3_ENA */
+#define WM8996_AIF1RX_CHAN3_ENA_MASK 0x0008 /* AIF1RX_CHAN3_ENA */
+#define WM8996_AIF1RX_CHAN3_ENA_SHIFT 3 /* AIF1RX_CHAN3_ENA */
+#define WM8996_AIF1RX_CHAN3_ENA_WIDTH 1 /* AIF1RX_CHAN3_ENA */
+#define WM8996_AIF1RX_CHAN2_ENA 0x0004 /* AIF1RX_CHAN2_ENA */
+#define WM8996_AIF1RX_CHAN2_ENA_MASK 0x0004 /* AIF1RX_CHAN2_ENA */
+#define WM8996_AIF1RX_CHAN2_ENA_SHIFT 2 /* AIF1RX_CHAN2_ENA */
+#define WM8996_AIF1RX_CHAN2_ENA_WIDTH 1 /* AIF1RX_CHAN2_ENA */
+#define WM8996_AIF1RX_CHAN1_ENA 0x0002 /* AIF1RX_CHAN1_ENA */
+#define WM8996_AIF1RX_CHAN1_ENA_MASK 0x0002 /* AIF1RX_CHAN1_ENA */
+#define WM8996_AIF1RX_CHAN1_ENA_SHIFT 1 /* AIF1RX_CHAN1_ENA */
+#define WM8996_AIF1RX_CHAN1_ENA_WIDTH 1 /* AIF1RX_CHAN1_ENA */
+#define WM8996_AIF1RX_CHAN0_ENA 0x0001 /* AIF1RX_CHAN0_ENA */
+#define WM8996_AIF1RX_CHAN0_ENA_MASK 0x0001 /* AIF1RX_CHAN0_ENA */
+#define WM8996_AIF1RX_CHAN0_ENA_SHIFT 0 /* AIF1RX_CHAN0_ENA */
+#define WM8996_AIF1RX_CHAN0_ENA_WIDTH 1 /* AIF1RX_CHAN0_ENA */
+
+/*
+ * R5 (0x05) - Power Management (5)
+ */
+#define WM8996_DSP2TXL_ENA 0x0800 /* DSP2TXL_ENA */
+#define WM8996_DSP2TXL_ENA_MASK 0x0800 /* DSP2TXL_ENA */
+#define WM8996_DSP2TXL_ENA_SHIFT 11 /* DSP2TXL_ENA */
+#define WM8996_DSP2TXL_ENA_WIDTH 1 /* DSP2TXL_ENA */
+#define WM8996_DSP2TXR_ENA 0x0400 /* DSP2TXR_ENA */
+#define WM8996_DSP2TXR_ENA_MASK 0x0400 /* DSP2TXR_ENA */
+#define WM8996_DSP2TXR_ENA_SHIFT 10 /* DSP2TXR_ENA */
+#define WM8996_DSP2TXR_ENA_WIDTH 1 /* DSP2TXR_ENA */
+#define WM8996_DSP1TXL_ENA 0x0200 /* DSP1TXL_ENA */
+#define WM8996_DSP1TXL_ENA_MASK 0x0200 /* DSP1TXL_ENA */
+#define WM8996_DSP1TXL_ENA_SHIFT 9 /* DSP1TXL_ENA */
+#define WM8996_DSP1TXL_ENA_WIDTH 1 /* DSP1TXL_ENA */
+#define WM8996_DSP1TXR_ENA 0x0100 /* DSP1TXR_ENA */
+#define WM8996_DSP1TXR_ENA_MASK 0x0100 /* DSP1TXR_ENA */
+#define WM8996_DSP1TXR_ENA_SHIFT 8 /* DSP1TXR_ENA */
+#define WM8996_DSP1TXR_ENA_WIDTH 1 /* DSP1TXR_ENA */
+#define WM8996_DAC2L_ENA 0x0008 /* DAC2L_ENA */
+#define WM8996_DAC2L_ENA_MASK 0x0008 /* DAC2L_ENA */
+#define WM8996_DAC2L_ENA_SHIFT 3 /* DAC2L_ENA */
+#define WM8996_DAC2L_ENA_WIDTH 1 /* DAC2L_ENA */
+#define WM8996_DAC2R_ENA 0x0004 /* DAC2R_ENA */
+#define WM8996_DAC2R_ENA_MASK 0x0004 /* DAC2R_ENA */
+#define WM8996_DAC2R_ENA_SHIFT 2 /* DAC2R_ENA */
+#define WM8996_DAC2R_ENA_WIDTH 1 /* DAC2R_ENA */
+#define WM8996_DAC1L_ENA 0x0002 /* DAC1L_ENA */
+#define WM8996_DAC1L_ENA_MASK 0x0002 /* DAC1L_ENA */
+#define WM8996_DAC1L_ENA_SHIFT 1 /* DAC1L_ENA */
+#define WM8996_DAC1L_ENA_WIDTH 1 /* DAC1L_ENA */
+#define WM8996_DAC1R_ENA 0x0001 /* DAC1R_ENA */
+#define WM8996_DAC1R_ENA_MASK 0x0001 /* DAC1R_ENA */
+#define WM8996_DAC1R_ENA_SHIFT 0 /* DAC1R_ENA */
+#define WM8996_DAC1R_ENA_WIDTH 1 /* DAC1R_ENA */
+
+/*
+ * R6 (0x06) - Power Management (6)
+ */
+#define WM8996_AIF2TX_CHAN1_ENA 0x0200 /* AIF2TX_CHAN1_ENA */
+#define WM8996_AIF2TX_CHAN1_ENA_MASK 0x0200 /* AIF2TX_CHAN1_ENA */
+#define WM8996_AIF2TX_CHAN1_ENA_SHIFT 9 /* AIF2TX_CHAN1_ENA */
+#define WM8996_AIF2TX_CHAN1_ENA_WIDTH 1 /* AIF2TX_CHAN1_ENA */
+#define WM8996_AIF2TX_CHAN0_ENA 0x0100 /* AIF2TX_CHAN0_ENA */
+#define WM8996_AIF2TX_CHAN0_ENA_MASK 0x0100 /* AIF2TX_CHAN0_ENA */
+#define WM8996_AIF2TX_CHAN0_ENA_SHIFT 8 /* AIF2TX_CHAN0_ENA */
+#define WM8996_AIF2TX_CHAN0_ENA_WIDTH 1 /* AIF2TX_CHAN0_ENA */
+#define WM8996_AIF1TX_CHAN5_ENA 0x0020 /* AIF1TX_CHAN5_ENA */
+#define WM8996_AIF1TX_CHAN5_ENA_MASK 0x0020 /* AIF1TX_CHAN5_ENA */
+#define WM8996_AIF1TX_CHAN5_ENA_SHIFT 5 /* AIF1TX_CHAN5_ENA */
+#define WM8996_AIF1TX_CHAN5_ENA_WIDTH 1 /* AIF1TX_CHAN5_ENA */
+#define WM8996_AIF1TX_CHAN4_ENA 0x0010 /* AIF1TX_CHAN4_ENA */
+#define WM8996_AIF1TX_CHAN4_ENA_MASK 0x0010 /* AIF1TX_CHAN4_ENA */
+#define WM8996_AIF1TX_CHAN4_ENA_SHIFT 4 /* AIF1TX_CHAN4_ENA */
+#define WM8996_AIF1TX_CHAN4_ENA_WIDTH 1 /* AIF1TX_CHAN4_ENA */
+#define WM8996_AIF1TX_CHAN3_ENA 0x0008 /* AIF1TX_CHAN3_ENA */
+#define WM8996_AIF1TX_CHAN3_ENA_MASK 0x0008 /* AIF1TX_CHAN3_ENA */
+#define WM8996_AIF1TX_CHAN3_ENA_SHIFT 3 /* AIF1TX_CHAN3_ENA */
+#define WM8996_AIF1TX_CHAN3_ENA_WIDTH 1 /* AIF1TX_CHAN3_ENA */
+#define WM8996_AIF1TX_CHAN2_ENA 0x0004 /* AIF1TX_CHAN2_ENA */
+#define WM8996_AIF1TX_CHAN2_ENA_MASK 0x0004 /* AIF1TX_CHAN2_ENA */
+#define WM8996_AIF1TX_CHAN2_ENA_SHIFT 2 /* AIF1TX_CHAN2_ENA */
+#define WM8996_AIF1TX_CHAN2_ENA_WIDTH 1 /* AIF1TX_CHAN2_ENA */
+#define WM8996_AIF1TX_CHAN1_ENA 0x0002 /* AIF1TX_CHAN1_ENA */
+#define WM8996_AIF1TX_CHAN1_ENA_MASK 0x0002 /* AIF1TX_CHAN1_ENA */
+#define WM8996_AIF1TX_CHAN1_ENA_SHIFT 1 /* AIF1TX_CHAN1_ENA */
+#define WM8996_AIF1TX_CHAN1_ENA_WIDTH 1 /* AIF1TX_CHAN1_ENA */
+#define WM8996_AIF1TX_CHAN0_ENA 0x0001 /* AIF1TX_CHAN0_ENA */
+#define WM8996_AIF1TX_CHAN0_ENA_MASK 0x0001 /* AIF1TX_CHAN0_ENA */
+#define WM8996_AIF1TX_CHAN0_ENA_SHIFT 0 /* AIF1TX_CHAN0_ENA */
+#define WM8996_AIF1TX_CHAN0_ENA_WIDTH 1 /* AIF1TX_CHAN0_ENA */
+
+/*
+ * R7 (0x07) - Power Management (7)
+ */
+#define WM8996_DMIC2_FN 0x0200 /* DMIC2_FN */
+#define WM8996_DMIC2_FN_MASK 0x0200 /* DMIC2_FN */
+#define WM8996_DMIC2_FN_SHIFT 9 /* DMIC2_FN */
+#define WM8996_DMIC2_FN_WIDTH 1 /* DMIC2_FN */
+#define WM8996_DMIC1_FN 0x0100 /* DMIC1_FN */
+#define WM8996_DMIC1_FN_MASK 0x0100 /* DMIC1_FN */
+#define WM8996_DMIC1_FN_SHIFT 8 /* DMIC1_FN */
+#define WM8996_DMIC1_FN_WIDTH 1 /* DMIC1_FN */
+#define WM8996_ADC_DMIC_DSP2R_ENA 0x0080 /* ADC_DMIC_DSP2R_ENA */
+#define WM8996_ADC_DMIC_DSP2R_ENA_MASK 0x0080 /* ADC_DMIC_DSP2R_ENA */
+#define WM8996_ADC_DMIC_DSP2R_ENA_SHIFT 7 /* ADC_DMIC_DSP2R_ENA */
+#define WM8996_ADC_DMIC_DSP2R_ENA_WIDTH 1 /* ADC_DMIC_DSP2R_ENA */
+#define WM8996_ADC_DMIC_DSP2L_ENA 0x0040 /* ADC_DMIC_DSP2L_ENA */
+#define WM8996_ADC_DMIC_DSP2L_ENA_MASK 0x0040 /* ADC_DMIC_DSP2L_ENA */
+#define WM8996_ADC_DMIC_DSP2L_ENA_SHIFT 6 /* ADC_DMIC_DSP2L_ENA */
+#define WM8996_ADC_DMIC_DSP2L_ENA_WIDTH 1 /* ADC_DMIC_DSP2L_ENA */
+#define WM8996_ADC_DMIC_SRC2_MASK 0x0030 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8996_ADC_DMIC_SRC2_SHIFT 4 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8996_ADC_DMIC_SRC2_WIDTH 2 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8996_ADC_DMIC_DSP1R_ENA 0x0008 /* ADC_DMIC_DSP1R_ENA */
+#define WM8996_ADC_DMIC_DSP1R_ENA_MASK 0x0008 /* ADC_DMIC_DSP1R_ENA */
+#define WM8996_ADC_DMIC_DSP1R_ENA_SHIFT 3 /* ADC_DMIC_DSP1R_ENA */
+#define WM8996_ADC_DMIC_DSP1R_ENA_WIDTH 1 /* ADC_DMIC_DSP1R_ENA */
+#define WM8996_ADC_DMIC_DSP1L_ENA 0x0004 /* ADC_DMIC_DSP1L_ENA */
+#define WM8996_ADC_DMIC_DSP1L_ENA_MASK 0x0004 /* ADC_DMIC_DSP1L_ENA */
+#define WM8996_ADC_DMIC_DSP1L_ENA_SHIFT 2 /* ADC_DMIC_DSP1L_ENA */
+#define WM8996_ADC_DMIC_DSP1L_ENA_WIDTH 1 /* ADC_DMIC_DSP1L_ENA */
+#define WM8996_ADC_DMIC_SRC1_MASK 0x0003 /* ADC_DMIC_SRC1 - [1:0] */
+#define WM8996_ADC_DMIC_SRC1_SHIFT 0 /* ADC_DMIC_SRC1 - [1:0] */
+#define WM8996_ADC_DMIC_SRC1_WIDTH 2 /* ADC_DMIC_SRC1 - [1:0] */
+
+/*
+ * R8 (0x08) - Power Management (8)
+ */
+#define WM8996_AIF2TX_SRC_MASK 0x00C0 /* AIF2TX_SRC - [7:6] */
+#define WM8996_AIF2TX_SRC_SHIFT 6 /* AIF2TX_SRC - [7:6] */
+#define WM8996_AIF2TX_SRC_WIDTH 2 /* AIF2TX_SRC - [7:6] */
+#define WM8996_DSP2RX_SRC 0x0010 /* DSP2RX_SRC */
+#define WM8996_DSP2RX_SRC_MASK 0x0010 /* DSP2RX_SRC */
+#define WM8996_DSP2RX_SRC_SHIFT 4 /* DSP2RX_SRC */
+#define WM8996_DSP2RX_SRC_WIDTH 1 /* DSP2RX_SRC */
+#define WM8996_DSP1RX_SRC 0x0001 /* DSP1RX_SRC */
+#define WM8996_DSP1RX_SRC_MASK 0x0001 /* DSP1RX_SRC */
+#define WM8996_DSP1RX_SRC_SHIFT 0 /* DSP1RX_SRC */
+#define WM8996_DSP1RX_SRC_WIDTH 1 /* DSP1RX_SRC */
+
+/*
+ * R16 (0x10) - Left Line Input Volume
+ */
+#define WM8996_IN1_VU 0x0080 /* IN1_VU */
+#define WM8996_IN1_VU_MASK 0x0080 /* IN1_VU */
+#define WM8996_IN1_VU_SHIFT 7 /* IN1_VU */
+#define WM8996_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8996_IN1L_ZC 0x0020 /* IN1L_ZC */
+#define WM8996_IN1L_ZC_MASK 0x0020 /* IN1L_ZC */
+#define WM8996_IN1L_ZC_SHIFT 5 /* IN1L_ZC */
+#define WM8996_IN1L_ZC_WIDTH 1 /* IN1L_ZC */
+#define WM8996_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */
+#define WM8996_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */
+#define WM8996_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */
+
+/*
+ * R17 (0x11) - Right Line Input Volume
+ */
+#define WM8996_IN1_VU 0x0080 /* IN1_VU */
+#define WM8996_IN1_VU_MASK 0x0080 /* IN1_VU */
+#define WM8996_IN1_VU_SHIFT 7 /* IN1_VU */
+#define WM8996_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8996_IN1R_ZC 0x0020 /* IN1R_ZC */
+#define WM8996_IN1R_ZC_MASK 0x0020 /* IN1R_ZC */
+#define WM8996_IN1R_ZC_SHIFT 5 /* IN1R_ZC */
+#define WM8996_IN1R_ZC_WIDTH 1 /* IN1R_ZC */
+#define WM8996_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */
+#define WM8996_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */
+#define WM8996_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */
+
+/*
+ * R18 (0x12) - Line Input Control
+ */
+#define WM8996_INL_MODE_MASK 0x000C /* INL_MODE - [3:2] */
+#define WM8996_INL_MODE_SHIFT 2 /* INL_MODE - [3:2] */
+#define WM8996_INL_MODE_WIDTH 2 /* INL_MODE - [3:2] */
+#define WM8996_INR_MODE_MASK 0x0003 /* INR_MODE - [1:0] */
+#define WM8996_INR_MODE_SHIFT 0 /* INR_MODE - [1:0] */
+#define WM8996_INR_MODE_WIDTH 2 /* INR_MODE - [1:0] */
+
+/*
+ * R21 (0x15) - DAC1 HPOUT1 Volume
+ */
+#define WM8996_DAC1R_HPOUT1R_VOL_MASK 0x00F0 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8996_DAC1R_HPOUT1R_VOL_SHIFT 4 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8996_DAC1R_HPOUT1R_VOL_WIDTH 4 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8996_DAC1L_HPOUT1L_VOL_MASK 0x000F /* DAC1L_HPOUT1L_VOL - [3:0] */
+#define WM8996_DAC1L_HPOUT1L_VOL_SHIFT 0 /* DAC1L_HPOUT1L_VOL - [3:0] */
+#define WM8996_DAC1L_HPOUT1L_VOL_WIDTH 4 /* DAC1L_HPOUT1L_VOL - [3:0] */
+
+/*
+ * R22 (0x16) - DAC2 HPOUT2 Volume
+ */
+#define WM8996_DAC2R_HPOUT2R_VOL_MASK 0x00F0 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8996_DAC2R_HPOUT2R_VOL_SHIFT 4 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8996_DAC2R_HPOUT2R_VOL_WIDTH 4 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8996_DAC2L_HPOUT2L_VOL_MASK 0x000F /* DAC2L_HPOUT2L_VOL - [3:0] */
+#define WM8996_DAC2L_HPOUT2L_VOL_SHIFT 0 /* DAC2L_HPOUT2L_VOL - [3:0] */
+#define WM8996_DAC2L_HPOUT2L_VOL_WIDTH 4 /* DAC2L_HPOUT2L_VOL - [3:0] */
+
+/*
+ * R24 (0x18) - DAC1 Left Volume
+ */
+#define WM8996_DAC1L_MUTE 0x0200 /* DAC1L_MUTE */
+#define WM8996_DAC1L_MUTE_MASK 0x0200 /* DAC1L_MUTE */
+#define WM8996_DAC1L_MUTE_SHIFT 9 /* DAC1L_MUTE */
+#define WM8996_DAC1L_MUTE_WIDTH 1 /* DAC1L_MUTE */
+#define WM8996_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8996_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8996_DAC1L_VOL_MASK 0x00FF /* DAC1L_VOL - [7:0] */
+#define WM8996_DAC1L_VOL_SHIFT 0 /* DAC1L_VOL - [7:0] */
+#define WM8996_DAC1L_VOL_WIDTH 8 /* DAC1L_VOL - [7:0] */
+
+/*
+ * R25 (0x19) - DAC1 Right Volume
+ */
+#define WM8996_DAC1R_MUTE 0x0200 /* DAC1R_MUTE */
+#define WM8996_DAC1R_MUTE_MASK 0x0200 /* DAC1R_MUTE */
+#define WM8996_DAC1R_MUTE_SHIFT 9 /* DAC1R_MUTE */
+#define WM8996_DAC1R_MUTE_WIDTH 1 /* DAC1R_MUTE */
+#define WM8996_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8996_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8996_DAC1R_VOL_MASK 0x00FF /* DAC1R_VOL - [7:0] */
+#define WM8996_DAC1R_VOL_SHIFT 0 /* DAC1R_VOL - [7:0] */
+#define WM8996_DAC1R_VOL_WIDTH 8 /* DAC1R_VOL - [7:0] */
+
+/*
+ * R26 (0x1A) - DAC2 Left Volume
+ */
+#define WM8996_DAC2L_MUTE 0x0200 /* DAC2L_MUTE */
+#define WM8996_DAC2L_MUTE_MASK 0x0200 /* DAC2L_MUTE */
+#define WM8996_DAC2L_MUTE_SHIFT 9 /* DAC2L_MUTE */
+#define WM8996_DAC2L_MUTE_WIDTH 1 /* DAC2L_MUTE */
+#define WM8996_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8996_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8996_DAC2L_VOL_MASK 0x00FF /* DAC2L_VOL - [7:0] */
+#define WM8996_DAC2L_VOL_SHIFT 0 /* DAC2L_VOL - [7:0] */
+#define WM8996_DAC2L_VOL_WIDTH 8 /* DAC2L_VOL - [7:0] */
+
+/*
+ * R27 (0x1B) - DAC2 Right Volume
+ */
+#define WM8996_DAC2R_MUTE 0x0200 /* DAC2R_MUTE */
+#define WM8996_DAC2R_MUTE_MASK 0x0200 /* DAC2R_MUTE */
+#define WM8996_DAC2R_MUTE_SHIFT 9 /* DAC2R_MUTE */
+#define WM8996_DAC2R_MUTE_WIDTH 1 /* DAC2R_MUTE */
+#define WM8996_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8996_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8996_DAC2R_VOL_MASK 0x00FF /* DAC2R_VOL - [7:0] */
+#define WM8996_DAC2R_VOL_SHIFT 0 /* DAC2R_VOL - [7:0] */
+#define WM8996_DAC2R_VOL_WIDTH 8 /* DAC2R_VOL - [7:0] */
+
+/*
+ * R28 (0x1C) - Output1 Left Volume
+ */
+#define WM8996_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8996_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8996_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */
+#define WM8996_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */
+#define WM8996_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */
+#define WM8996_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */
+#define WM8996_HPOUT1L_VOL_MASK 0x000F /* HPOUT1L_VOL - [3:0] */
+#define WM8996_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [3:0] */
+#define WM8996_HPOUT1L_VOL_WIDTH 4 /* HPOUT1L_VOL - [3:0] */
+
+/*
+ * R29 (0x1D) - Output1 Right Volume
+ */
+#define WM8996_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8996_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8996_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */
+#define WM8996_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */
+#define WM8996_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */
+#define WM8996_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */
+#define WM8996_HPOUT1R_VOL_MASK 0x000F /* HPOUT1R_VOL - [3:0] */
+#define WM8996_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [3:0] */
+#define WM8996_HPOUT1R_VOL_WIDTH 4 /* HPOUT1R_VOL - [3:0] */
+
+/*
+ * R30 (0x1E) - Output2 Left Volume
+ */
+#define WM8996_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8996_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8996_HPOUT2L_ZC 0x0080 /* HPOUT2L_ZC */
+#define WM8996_HPOUT2L_ZC_MASK 0x0080 /* HPOUT2L_ZC */
+#define WM8996_HPOUT2L_ZC_SHIFT 7 /* HPOUT2L_ZC */
+#define WM8996_HPOUT2L_ZC_WIDTH 1 /* HPOUT2L_ZC */
+#define WM8996_HPOUT2L_VOL_MASK 0x000F /* HPOUT2L_VOL - [3:0] */
+#define WM8996_HPOUT2L_VOL_SHIFT 0 /* HPOUT2L_VOL - [3:0] */
+#define WM8996_HPOUT2L_VOL_WIDTH 4 /* HPOUT2L_VOL - [3:0] */
+
+/*
+ * R31 (0x1F) - Output2 Right Volume
+ */
+#define WM8996_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8996_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8996_HPOUT2R_ZC 0x0080 /* HPOUT2R_ZC */
+#define WM8996_HPOUT2R_ZC_MASK 0x0080 /* HPOUT2R_ZC */
+#define WM8996_HPOUT2R_ZC_SHIFT 7 /* HPOUT2R_ZC */
+#define WM8996_HPOUT2R_ZC_WIDTH 1 /* HPOUT2R_ZC */
+#define WM8996_HPOUT2R_VOL_MASK 0x000F /* HPOUT2R_VOL - [3:0] */
+#define WM8996_HPOUT2R_VOL_SHIFT 0 /* HPOUT2R_VOL - [3:0] */
+#define WM8996_HPOUT2R_VOL_WIDTH 4 /* HPOUT2R_VOL - [3:0] */
+
+/*
+ * R32 (0x20) - MICBIAS (1)
+ */
+#define WM8996_MICB1_RATE 0x0020 /* MICB1_RATE */
+#define WM8996_MICB1_RATE_MASK 0x0020 /* MICB1_RATE */
+#define WM8996_MICB1_RATE_SHIFT 5 /* MICB1_RATE */
+#define WM8996_MICB1_RATE_WIDTH 1 /* MICB1_RATE */
+#define WM8996_MICB1_MODE 0x0010 /* MICB1_MODE */
+#define WM8996_MICB1_MODE_MASK 0x0010 /* MICB1_MODE */
+#define WM8996_MICB1_MODE_SHIFT 4 /* MICB1_MODE */
+#define WM8996_MICB1_MODE_WIDTH 1 /* MICB1_MODE */
+#define WM8996_MICB1_LVL_MASK 0x000E /* MICB1_LVL - [3:1] */
+#define WM8996_MICB1_LVL_SHIFT 1 /* MICB1_LVL - [3:1] */
+#define WM8996_MICB1_LVL_WIDTH 3 /* MICB1_LVL - [3:1] */
+#define WM8996_MICB1_DISCH 0x0001 /* MICB1_DISCH */
+#define WM8996_MICB1_DISCH_MASK 0x0001 /* MICB1_DISCH */
+#define WM8996_MICB1_DISCH_SHIFT 0 /* MICB1_DISCH */
+#define WM8996_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */
+
+/*
+ * R33 (0x21) - MICBIAS (2)
+ */
+#define WM8996_MICB2_RATE 0x0020 /* MICB2_RATE */
+#define WM8996_MICB2_RATE_MASK 0x0020 /* MICB2_RATE */
+#define WM8996_MICB2_RATE_SHIFT 5 /* MICB2_RATE */
+#define WM8996_MICB2_RATE_WIDTH 1 /* MICB2_RATE */
+#define WM8996_MICB2_MODE 0x0010 /* MICB2_MODE */
+#define WM8996_MICB2_MODE_MASK 0x0010 /* MICB2_MODE */
+#define WM8996_MICB2_MODE_SHIFT 4 /* MICB2_MODE */
+#define WM8996_MICB2_MODE_WIDTH 1 /* MICB2_MODE */
+#define WM8996_MICB2_LVL_MASK 0x000E /* MICB2_LVL - [3:1] */
+#define WM8996_MICB2_LVL_SHIFT 1 /* MICB2_LVL - [3:1] */
+#define WM8996_MICB2_LVL_WIDTH 3 /* MICB2_LVL - [3:1] */
+#define WM8996_MICB2_DISCH 0x0001 /* MICB2_DISCH */
+#define WM8996_MICB2_DISCH_MASK 0x0001 /* MICB2_DISCH */
+#define WM8996_MICB2_DISCH_SHIFT 0 /* MICB2_DISCH */
+#define WM8996_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */
+
+/*
+ * R40 (0x28) - LDO 1
+ */
+#define WM8996_LDO1_MODE 0x0020 /* LDO1_MODE */
+#define WM8996_LDO1_MODE_MASK 0x0020 /* LDO1_MODE */
+#define WM8996_LDO1_MODE_SHIFT 5 /* LDO1_MODE */
+#define WM8996_LDO1_MODE_WIDTH 1 /* LDO1_MODE */
+#define WM8996_LDO1_VSEL_MASK 0x0006 /* LDO1_VSEL - [2:1] */
+#define WM8996_LDO1_VSEL_SHIFT 1 /* LDO1_VSEL - [2:1] */
+#define WM8996_LDO1_VSEL_WIDTH 2 /* LDO1_VSEL - [2:1] */
+#define WM8996_LDO1_DISCH 0x0001 /* LDO1_DISCH */
+#define WM8996_LDO1_DISCH_MASK 0x0001 /* LDO1_DISCH */
+#define WM8996_LDO1_DISCH_SHIFT 0 /* LDO1_DISCH */
+#define WM8996_LDO1_DISCH_WIDTH 1 /* LDO1_DISCH */
+
+/*
+ * R41 (0x29) - LDO 2
+ */
+#define WM8996_LDO2_MODE 0x0020 /* LDO2_MODE */
+#define WM8996_LDO2_MODE_MASK 0x0020 /* LDO2_MODE */
+#define WM8996_LDO2_MODE_SHIFT 5 /* LDO2_MODE */
+#define WM8996_LDO2_MODE_WIDTH 1 /* LDO2_MODE */
+#define WM8996_LDO2_VSEL_MASK 0x001E /* LDO2_VSEL - [4:1] */
+#define WM8996_LDO2_VSEL_SHIFT 1 /* LDO2_VSEL - [4:1] */
+#define WM8996_LDO2_VSEL_WIDTH 4 /* LDO2_VSEL - [4:1] */
+#define WM8996_LDO2_DISCH 0x0001 /* LDO2_DISCH */
+#define WM8996_LDO2_DISCH_MASK 0x0001 /* LDO2_DISCH */
+#define WM8996_LDO2_DISCH_SHIFT 0 /* LDO2_DISCH */
+#define WM8996_LDO2_DISCH_WIDTH 1 /* LDO2_DISCH */
+
+/*
+ * R48 (0x30) - Accessory Detect Mode 1
+ */
+#define WM8996_JD_MODE_MASK 0x0003 /* JD_MODE - [1:0] */
+#define WM8996_JD_MODE_SHIFT 0 /* JD_MODE - [1:0] */
+#define WM8996_JD_MODE_WIDTH 2 /* JD_MODE - [1:0] */
+
+/*
+ * R49 (0x31) - Accessory Detect Mode 2
+ */
+#define WM8996_HPOUT1FB_SRC 0x0004 /* HPOUT1FB_SRC */
+#define WM8996_HPOUT1FB_SRC_MASK 0x0004 /* HPOUT1FB_SRC */
+#define WM8996_HPOUT1FB_SRC_SHIFT 2 /* HPOUT1FB_SRC */
+#define WM8996_HPOUT1FB_SRC_WIDTH 1 /* HPOUT1FB_SRC */
+#define WM8996_MICD_SRC 0x0002 /* MICD_SRC */
+#define WM8996_MICD_SRC_MASK 0x0002 /* MICD_SRC */
+#define WM8996_MICD_SRC_SHIFT 1 /* MICD_SRC */
+#define WM8996_MICD_SRC_WIDTH 1 /* MICD_SRC */
+#define WM8996_MICD_BIAS_SRC 0x0001 /* MICD_BIAS_SRC */
+#define WM8996_MICD_BIAS_SRC_MASK 0x0001 /* MICD_BIAS_SRC */
+#define WM8996_MICD_BIAS_SRC_SHIFT 0 /* MICD_BIAS_SRC */
+#define WM8996_MICD_BIAS_SRC_WIDTH 1 /* MICD_BIAS_SRC */
+
+/*
+ * R52 (0x34) - Headphone Detect 1
+ */
+#define WM8996_HP_HOLDTIME_MASK 0x00E0 /* HP_HOLDTIME - [7:5] */
+#define WM8996_HP_HOLDTIME_SHIFT 5 /* HP_HOLDTIME - [7:5] */
+#define WM8996_HP_HOLDTIME_WIDTH 3 /* HP_HOLDTIME - [7:5] */
+#define WM8996_HP_CLK_DIV_MASK 0x0018 /* HP_CLK_DIV - [4:3] */
+#define WM8996_HP_CLK_DIV_SHIFT 3 /* HP_CLK_DIV - [4:3] */
+#define WM8996_HP_CLK_DIV_WIDTH 2 /* HP_CLK_DIV - [4:3] */
+#define WM8996_HP_STEP_SIZE 0x0002 /* HP_STEP_SIZE */
+#define WM8996_HP_STEP_SIZE_MASK 0x0002 /* HP_STEP_SIZE */
+#define WM8996_HP_STEP_SIZE_SHIFT 1 /* HP_STEP_SIZE */
+#define WM8996_HP_STEP_SIZE_WIDTH 1 /* HP_STEP_SIZE */
+#define WM8996_HP_POLL 0x0001 /* HP_POLL */
+#define WM8996_HP_POLL_MASK 0x0001 /* HP_POLL */
+#define WM8996_HP_POLL_SHIFT 0 /* HP_POLL */
+#define WM8996_HP_POLL_WIDTH 1 /* HP_POLL */
+
+/*
+ * R53 (0x35) - Headphone Detect 2
+ */
+#define WM8996_HP_DONE 0x0080 /* HP_DONE */
+#define WM8996_HP_DONE_MASK 0x0080 /* HP_DONE */
+#define WM8996_HP_DONE_SHIFT 7 /* HP_DONE */
+#define WM8996_HP_DONE_WIDTH 1 /* HP_DONE */
+#define WM8996_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */
+#define WM8996_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */
+#define WM8996_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */
+
+/*
+ * R56 (0x38) - Mic Detect 1
+ */
+#define WM8996_MICD_BIAS_STARTTIME_MASK 0xF000 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8996_MICD_BIAS_STARTTIME_SHIFT 12 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8996_MICD_BIAS_STARTTIME_WIDTH 4 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8996_MICD_RATE_MASK 0x0F00 /* MICD_RATE - [11:8] */
+#define WM8996_MICD_RATE_SHIFT 8 /* MICD_RATE - [11:8] */
+#define WM8996_MICD_RATE_WIDTH 4 /* MICD_RATE - [11:8] */
+#define WM8996_MICD_DBTIME 0x0002 /* MICD_DBTIME */
+#define WM8996_MICD_DBTIME_MASK 0x0002 /* MICD_DBTIME */
+#define WM8996_MICD_DBTIME_SHIFT 1 /* MICD_DBTIME */
+#define WM8996_MICD_DBTIME_WIDTH 1 /* MICD_DBTIME */
+#define WM8996_MICD_ENA 0x0001 /* MICD_ENA */
+#define WM8996_MICD_ENA_MASK 0x0001 /* MICD_ENA */
+#define WM8996_MICD_ENA_SHIFT 0 /* MICD_ENA */
+#define WM8996_MICD_ENA_WIDTH 1 /* MICD_ENA */
+
+/*
+ * R57 (0x39) - Mic Detect 2
+ */
+#define WM8996_MICD_LVL_SEL_MASK 0x00FF /* MICD_LVL_SEL - [7:0] */
+#define WM8996_MICD_LVL_SEL_SHIFT 0 /* MICD_LVL_SEL - [7:0] */
+#define WM8996_MICD_LVL_SEL_WIDTH 8 /* MICD_LVL_SEL - [7:0] */
+
+/*
+ * R58 (0x3A) - Mic Detect 3
+ */
+#define WM8996_MICD_LVL_MASK 0x07FC /* MICD_LVL - [10:2] */
+#define WM8996_MICD_LVL_SHIFT 2 /* MICD_LVL - [10:2] */
+#define WM8996_MICD_LVL_WIDTH 9 /* MICD_LVL - [10:2] */
+#define WM8996_MICD_VALID 0x0002 /* MICD_VALID */
+#define WM8996_MICD_VALID_MASK 0x0002 /* MICD_VALID */
+#define WM8996_MICD_VALID_SHIFT 1 /* MICD_VALID */
+#define WM8996_MICD_VALID_WIDTH 1 /* MICD_VALID */
+#define WM8996_MICD_STS 0x0001 /* MICD_STS */
+#define WM8996_MICD_STS_MASK 0x0001 /* MICD_STS */
+#define WM8996_MICD_STS_SHIFT 0 /* MICD_STS */
+#define WM8996_MICD_STS_WIDTH 1 /* MICD_STS */
+
+/*
+ * R64 (0x40) - Charge Pump (1)
+ */
+#define WM8996_CP_ENA 0x8000 /* CP_ENA */
+#define WM8996_CP_ENA_MASK 0x8000 /* CP_ENA */
+#define WM8996_CP_ENA_SHIFT 15 /* CP_ENA */
+#define WM8996_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R65 (0x41) - Charge Pump (2)
+ */
+#define WM8996_CP_DISCH 0x8000 /* CP_DISCH */
+#define WM8996_CP_DISCH_MASK 0x8000 /* CP_DISCH */
+#define WM8996_CP_DISCH_SHIFT 15 /* CP_DISCH */
+#define WM8996_CP_DISCH_WIDTH 1 /* CP_DISCH */
+
+/*
+ * R80 (0x50) - DC Servo (1)
+ */
+#define WM8996_DCS_ENA_CHAN_3 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8996_DCS_ENA_CHAN_3_MASK 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8996_DCS_ENA_CHAN_3_SHIFT 3 /* DCS_ENA_CHAN_3 */
+#define WM8996_DCS_ENA_CHAN_3_WIDTH 1 /* DCS_ENA_CHAN_3 */
+#define WM8996_DCS_ENA_CHAN_2 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8996_DCS_ENA_CHAN_2_MASK 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8996_DCS_ENA_CHAN_2_SHIFT 2 /* DCS_ENA_CHAN_2 */
+#define WM8996_DCS_ENA_CHAN_2_WIDTH 1 /* DCS_ENA_CHAN_2 */
+#define WM8996_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8996_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8996_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */
+#define WM8996_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */
+#define WM8996_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8996_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8996_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */
+#define WM8996_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */
+
+/*
+ * R81 (0x51) - DC Servo (2)
+ */
+#define WM8996_DCS_TRIG_SINGLE_3 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8996_DCS_TRIG_SINGLE_3_MASK 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8996_DCS_TRIG_SINGLE_3_SHIFT 15 /* DCS_TRIG_SINGLE_3 */
+#define WM8996_DCS_TRIG_SINGLE_3_WIDTH 1 /* DCS_TRIG_SINGLE_3 */
+#define WM8996_DCS_TRIG_SINGLE_2 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8996_DCS_TRIG_SINGLE_2_MASK 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8996_DCS_TRIG_SINGLE_2_SHIFT 14 /* DCS_TRIG_SINGLE_2 */
+#define WM8996_DCS_TRIG_SINGLE_2_WIDTH 1 /* DCS_TRIG_SINGLE_2 */
+#define WM8996_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8996_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8996_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */
+#define WM8996_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */
+#define WM8996_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8996_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8996_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */
+#define WM8996_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */
+#define WM8996_DCS_TRIG_SERIES_3 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8996_DCS_TRIG_SERIES_3_MASK 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8996_DCS_TRIG_SERIES_3_SHIFT 11 /* DCS_TRIG_SERIES_3 */
+#define WM8996_DCS_TRIG_SERIES_3_WIDTH 1 /* DCS_TRIG_SERIES_3 */
+#define WM8996_DCS_TRIG_SERIES_2 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8996_DCS_TRIG_SERIES_2_MASK 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8996_DCS_TRIG_SERIES_2_SHIFT 10 /* DCS_TRIG_SERIES_2 */
+#define WM8996_DCS_TRIG_SERIES_2_WIDTH 1 /* DCS_TRIG_SERIES_2 */
+#define WM8996_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8996_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8996_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */
+#define WM8996_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */
+#define WM8996_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8996_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8996_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */
+#define WM8996_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */
+#define WM8996_DCS_TRIG_STARTUP_3 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8996_DCS_TRIG_STARTUP_3_MASK 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8996_DCS_TRIG_STARTUP_3_SHIFT 7 /* DCS_TRIG_STARTUP_3 */
+#define WM8996_DCS_TRIG_STARTUP_3_WIDTH 1 /* DCS_TRIG_STARTUP_3 */
+#define WM8996_DCS_TRIG_STARTUP_2 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8996_DCS_TRIG_STARTUP_2_MASK 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8996_DCS_TRIG_STARTUP_2_SHIFT 6 /* DCS_TRIG_STARTUP_2 */
+#define WM8996_DCS_TRIG_STARTUP_2_WIDTH 1 /* DCS_TRIG_STARTUP_2 */
+#define WM8996_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8996_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8996_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */
+#define WM8996_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */
+#define WM8996_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8996_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8996_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */
+#define WM8996_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */
+#define WM8996_DCS_TRIG_DAC_WR_3 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8996_DCS_TRIG_DAC_WR_3_MASK 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8996_DCS_TRIG_DAC_WR_3_SHIFT 3 /* DCS_TRIG_DAC_WR_3 */
+#define WM8996_DCS_TRIG_DAC_WR_3_WIDTH 1 /* DCS_TRIG_DAC_WR_3 */
+#define WM8996_DCS_TRIG_DAC_WR_2 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8996_DCS_TRIG_DAC_WR_2_MASK 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8996_DCS_TRIG_DAC_WR_2_SHIFT 2 /* DCS_TRIG_DAC_WR_2 */
+#define WM8996_DCS_TRIG_DAC_WR_2_WIDTH 1 /* DCS_TRIG_DAC_WR_2 */
+#define WM8996_DCS_TRIG_DAC_WR_1 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8996_DCS_TRIG_DAC_WR_1_MASK 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8996_DCS_TRIG_DAC_WR_1_SHIFT 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8996_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8996_DCS_TRIG_DAC_WR_0 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8996_DCS_TRIG_DAC_WR_0_MASK 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8996_DCS_TRIG_DAC_WR_0_SHIFT 0 /* DCS_TRIG_DAC_WR_0 */
+#define WM8996_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */
+
+/*
+ * R82 (0x52) - DC Servo (3)
+ */
+#define WM8996_DCS_TIMER_PERIOD_23_MASK 0x0F00 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8996_DCS_TIMER_PERIOD_23_SHIFT 8 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8996_DCS_TIMER_PERIOD_23_WIDTH 4 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8996_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8996_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8996_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R84 (0x54) - DC Servo (5)
+ */
+#define WM8996_DCS_SERIES_NO_23_MASK 0x7F00 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8996_DCS_SERIES_NO_23_SHIFT 8 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8996_DCS_SERIES_NO_23_WIDTH 7 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8996_DCS_SERIES_NO_01_MASK 0x007F /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8996_DCS_SERIES_NO_01_SHIFT 0 /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8996_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [6:0] */
+
+/*
+ * R85 (0x55) - DC Servo (6)
+ */
+#define WM8996_DCS_DAC_WR_VAL_3_MASK 0xFF00 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8996_DCS_DAC_WR_VAL_3_SHIFT 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8996_DCS_DAC_WR_VAL_3_WIDTH 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8996_DCS_DAC_WR_VAL_2_MASK 0x00FF /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8996_DCS_DAC_WR_VAL_2_SHIFT 0 /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8996_DCS_DAC_WR_VAL_2_WIDTH 8 /* DCS_DAC_WR_VAL_2 - [7:0] */
+
+/*
+ * R86 (0x56) - DC Servo (7)
+ */
+#define WM8996_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8996_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8996_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8996_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8996_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8996_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R87 (0x57) - DC Servo Readback 0
+ */
+#define WM8996_DCS_CAL_COMPLETE_MASK 0x0F00 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8996_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8996_DCS_CAL_COMPLETE_WIDTH 4 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8996_DCS_DAC_WR_COMPLETE_MASK 0x00F0 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8996_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8996_DCS_DAC_WR_COMPLETE_WIDTH 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8996_DCS_STARTUP_COMPLETE_MASK 0x000F /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8996_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8996_DCS_STARTUP_COMPLETE_WIDTH 4 /* DCS_STARTUP_COMPLETE - [3:0] */
+
+/*
+ * R96 (0x60) - Analogue HP (1)
+ */
+#define WM8996_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8996_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8996_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */
+#define WM8996_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */
+#define WM8996_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */
+#define WM8996_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */
+#define WM8996_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */
+#define WM8996_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */
+#define WM8996_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */
+#define WM8996_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */
+#define WM8996_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */
+#define WM8996_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */
+#define WM8996_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8996_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8996_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */
+#define WM8996_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */
+#define WM8996_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */
+#define WM8996_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */
+#define WM8996_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */
+#define WM8996_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */
+#define WM8996_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */
+#define WM8996_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */
+#define WM8996_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */
+#define WM8996_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */
+
+/*
+ * R97 (0x61) - Analogue HP (2)
+ */
+#define WM8996_HPOUT2L_RMV_SHORT 0x0080 /* HPOUT2L_RMV_SHORT */
+#define WM8996_HPOUT2L_RMV_SHORT_MASK 0x0080 /* HPOUT2L_RMV_SHORT */
+#define WM8996_HPOUT2L_RMV_SHORT_SHIFT 7 /* HPOUT2L_RMV_SHORT */
+#define WM8996_HPOUT2L_RMV_SHORT_WIDTH 1 /* HPOUT2L_RMV_SHORT */
+#define WM8996_HPOUT2L_OUTP 0x0040 /* HPOUT2L_OUTP */
+#define WM8996_HPOUT2L_OUTP_MASK 0x0040 /* HPOUT2L_OUTP */
+#define WM8996_HPOUT2L_OUTP_SHIFT 6 /* HPOUT2L_OUTP */
+#define WM8996_HPOUT2L_OUTP_WIDTH 1 /* HPOUT2L_OUTP */
+#define WM8996_HPOUT2L_DLY 0x0020 /* HPOUT2L_DLY */
+#define WM8996_HPOUT2L_DLY_MASK 0x0020 /* HPOUT2L_DLY */
+#define WM8996_HPOUT2L_DLY_SHIFT 5 /* HPOUT2L_DLY */
+#define WM8996_HPOUT2L_DLY_WIDTH 1 /* HPOUT2L_DLY */
+#define WM8996_HPOUT2R_RMV_SHORT 0x0008 /* HPOUT2R_RMV_SHORT */
+#define WM8996_HPOUT2R_RMV_SHORT_MASK 0x0008 /* HPOUT2R_RMV_SHORT */
+#define WM8996_HPOUT2R_RMV_SHORT_SHIFT 3 /* HPOUT2R_RMV_SHORT */
+#define WM8996_HPOUT2R_RMV_SHORT_WIDTH 1 /* HPOUT2R_RMV_SHORT */
+#define WM8996_HPOUT2R_OUTP 0x0004 /* HPOUT2R_OUTP */
+#define WM8996_HPOUT2R_OUTP_MASK 0x0004 /* HPOUT2R_OUTP */
+#define WM8996_HPOUT2R_OUTP_SHIFT 2 /* HPOUT2R_OUTP */
+#define WM8996_HPOUT2R_OUTP_WIDTH 1 /* HPOUT2R_OUTP */
+#define WM8996_HPOUT2R_DLY 0x0002 /* HPOUT2R_DLY */
+#define WM8996_HPOUT2R_DLY_MASK 0x0002 /* HPOUT2R_DLY */
+#define WM8996_HPOUT2R_DLY_SHIFT 1 /* HPOUT2R_DLY */
+#define WM8996_HPOUT2R_DLY_WIDTH 1 /* HPOUT2R_DLY */
+
+/*
+ * R256 (0x100) - Chip Revision
+ */
+#define WM8996_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */
+#define WM8996_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */
+#define WM8996_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */
+
+/*
+ * R257 (0x101) - Control Interface (1)
+ */
+#define WM8996_REG_SYNC 0x8000 /* REG_SYNC */
+#define WM8996_REG_SYNC_MASK 0x8000 /* REG_SYNC */
+#define WM8996_REG_SYNC_SHIFT 15 /* REG_SYNC */
+#define WM8996_REG_SYNC_WIDTH 1 /* REG_SYNC */
+#define WM8996_AUTO_INC 0x0004 /* AUTO_INC */
+#define WM8996_AUTO_INC_MASK 0x0004 /* AUTO_INC */
+#define WM8996_AUTO_INC_SHIFT 2 /* AUTO_INC */
+#define WM8996_AUTO_INC_WIDTH 1 /* AUTO_INC */
+
+/*
+ * R272 (0x110) - Write Sequencer Ctrl (1)
+ */
+#define WM8996_WSEQ_ENA 0x8000 /* WSEQ_ENA */
+#define WM8996_WSEQ_ENA_MASK 0x8000 /* WSEQ_ENA */
+#define WM8996_WSEQ_ENA_SHIFT 15 /* WSEQ_ENA */
+#define WM8996_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM8996_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM8996_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM8996_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM8996_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8996_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM8996_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM8996_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM8996_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8996_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */
+#define WM8996_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */
+#define WM8996_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */
+
+/*
+ * R273 (0x111) - Write Sequencer Ctrl (2)
+ */
+#define WM8996_WSEQ_BUSY 0x0100 /* WSEQ_BUSY */
+#define WM8996_WSEQ_BUSY_MASK 0x0100 /* WSEQ_BUSY */
+#define WM8996_WSEQ_BUSY_SHIFT 8 /* WSEQ_BUSY */
+#define WM8996_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+#define WM8996_WSEQ_CURRENT_INDEX_MASK 0x007F /* WSEQ_CURRENT_INDEX - [6:0] */
+#define WM8996_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [6:0] */
+#define WM8996_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [6:0] */
+
+/*
+ * R512 (0x200) - AIF Clocking (1)
+ */
+#define WM8996_SYSCLK_SRC_MASK 0x0018 /* SYSCLK_SRC - [4:3] */
+#define WM8996_SYSCLK_SRC_SHIFT 3 /* SYSCLK_SRC - [4:3] */
+#define WM8996_SYSCLK_SRC_WIDTH 2 /* SYSCLK_SRC - [4:3] */
+#define WM8996_SYSCLK_INV 0x0004 /* SYSCLK_INV */
+#define WM8996_SYSCLK_INV_MASK 0x0004 /* SYSCLK_INV */
+#define WM8996_SYSCLK_INV_SHIFT 2 /* SYSCLK_INV */
+#define WM8996_SYSCLK_INV_WIDTH 1 /* SYSCLK_INV */
+#define WM8996_SYSCLK_DIV 0x0002 /* SYSCLK_DIV */
+#define WM8996_SYSCLK_DIV_MASK 0x0002 /* SYSCLK_DIV */
+#define WM8996_SYSCLK_DIV_SHIFT 1 /* SYSCLK_DIV */
+#define WM8996_SYSCLK_DIV_WIDTH 1 /* SYSCLK_DIV */
+#define WM8996_SYSCLK_ENA 0x0001 /* SYSCLK_ENA */
+#define WM8996_SYSCLK_ENA_MASK 0x0001 /* SYSCLK_ENA */
+#define WM8996_SYSCLK_ENA_SHIFT 0 /* SYSCLK_ENA */
+#define WM8996_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */
+
+/*
+ * R513 (0x201) - AIF Clocking (2)
+ */
+#define WM8996_DSP2_DIV_MASK 0x0018 /* DSP2_DIV - [4:3] */
+#define WM8996_DSP2_DIV_SHIFT 3 /* DSP2_DIV - [4:3] */
+#define WM8996_DSP2_DIV_WIDTH 2 /* DSP2_DIV - [4:3] */
+#define WM8996_DSP1_DIV_MASK 0x0003 /* DSP1_DIV - [1:0] */
+#define WM8996_DSP1_DIV_SHIFT 0 /* DSP1_DIV - [1:0] */
+#define WM8996_DSP1_DIV_WIDTH 2 /* DSP1_DIV - [1:0] */
+
+/*
+ * R520 (0x208) - Clocking (1)
+ */
+#define WM8996_LFCLK_ENA 0x0020 /* LFCLK_ENA */
+#define WM8996_LFCLK_ENA_MASK 0x0020 /* LFCLK_ENA */
+#define WM8996_LFCLK_ENA_SHIFT 5 /* LFCLK_ENA */
+#define WM8996_LFCLK_ENA_WIDTH 1 /* LFCLK_ENA */
+#define WM8996_TOCLK_ENA 0x0010 /* TOCLK_ENA */
+#define WM8996_TOCLK_ENA_MASK 0x0010 /* TOCLK_ENA */
+#define WM8996_TOCLK_ENA_SHIFT 4 /* TOCLK_ENA */
+#define WM8996_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */
+#define WM8996_AIFCLK_ENA 0x0004 /* AIFCLK_ENA */
+#define WM8996_AIFCLK_ENA_MASK 0x0004 /* AIFCLK_ENA */
+#define WM8996_AIFCLK_ENA_SHIFT 2 /* AIFCLK_ENA */
+#define WM8996_AIFCLK_ENA_WIDTH 1 /* AIFCLK_ENA */
+#define WM8996_SYSDSPCLK_ENA 0x0002 /* SYSDSPCLK_ENA */
+#define WM8996_SYSDSPCLK_ENA_MASK 0x0002 /* SYSDSPCLK_ENA */
+#define WM8996_SYSDSPCLK_ENA_SHIFT 1 /* SYSDSPCLK_ENA */
+#define WM8996_SYSDSPCLK_ENA_WIDTH 1 /* SYSDSPCLK_ENA */
+
+/*
+ * R521 (0x209) - Clocking (2)
+ */
+#define WM8996_TOCLK_DIV_MASK 0x0700 /* TOCLK_DIV - [10:8] */
+#define WM8996_TOCLK_DIV_SHIFT 8 /* TOCLK_DIV - [10:8] */
+#define WM8996_TOCLK_DIV_WIDTH 3 /* TOCLK_DIV - [10:8] */
+#define WM8996_DBCLK_DIV_MASK 0x00F0 /* DBCLK_DIV - [7:4] */
+#define WM8996_DBCLK_DIV_SHIFT 4 /* DBCLK_DIV - [7:4] */
+#define WM8996_DBCLK_DIV_WIDTH 4 /* DBCLK_DIV - [7:4] */
+#define WM8996_OPCLK_DIV_MASK 0x0007 /* OPCLK_DIV - [2:0] */
+#define WM8996_OPCLK_DIV_SHIFT 0 /* OPCLK_DIV - [2:0] */
+#define WM8996_OPCLK_DIV_WIDTH 3 /* OPCLK_DIV - [2:0] */
+
+/*
+ * R528 (0x210) - AIF Rate
+ */
+#define WM8996_SYSCLK_RATE 0x0001 /* SYSCLK_RATE */
+#define WM8996_SYSCLK_RATE_MASK 0x0001 /* SYSCLK_RATE */
+#define WM8996_SYSCLK_RATE_SHIFT 0 /* SYSCLK_RATE */
+#define WM8996_SYSCLK_RATE_WIDTH 1 /* SYSCLK_RATE */
+
+/*
+ * R544 (0x220) - FLL Control (1)
+ */
+#define WM8996_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */
+#define WM8996_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */
+#define WM8996_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */
+#define WM8996_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */
+#define WM8996_FLL_ENA 0x0001 /* FLL_ENA */
+#define WM8996_FLL_ENA_MASK 0x0001 /* FLL_ENA */
+#define WM8996_FLL_ENA_SHIFT 0 /* FLL_ENA */
+#define WM8996_FLL_ENA_WIDTH 1 /* FLL_ENA */
+
+/*
+ * R545 (0x221) - FLL Control (2)
+ */
+#define WM8996_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */
+#define WM8996_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */
+#define WM8996_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */
+#define WM8996_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
+#define WM8996_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
+#define WM8996_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
+
+/*
+ * R546 (0x222) - FLL Control (3)
+ */
+#define WM8996_FLL_THETA_MASK 0xFFFF /* FLL_THETA - [15:0] */
+#define WM8996_FLL_THETA_SHIFT 0 /* FLL_THETA - [15:0] */
+#define WM8996_FLL_THETA_WIDTH 16 /* FLL_THETA - [15:0] */
+
+/*
+ * R547 (0x223) - FLL Control (4)
+ */
+#define WM8996_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */
+#define WM8996_FLL_N_SHIFT 5 /* FLL_N - [14:5] */
+#define WM8996_FLL_N_WIDTH 10 /* FLL_N - [14:5] */
+#define WM8996_FLL_LOOP_GAIN_MASK 0x000F /* FLL_LOOP_GAIN - [3:0] */
+#define WM8996_FLL_LOOP_GAIN_SHIFT 0 /* FLL_LOOP_GAIN - [3:0] */
+#define WM8996_FLL_LOOP_GAIN_WIDTH 4 /* FLL_LOOP_GAIN - [3:0] */
+
+/*
+ * R548 (0x224) - FLL Control (5)
+ */
+#define WM8996_FLL_FRC_NCO_VAL_MASK 0x1F80 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8996_FLL_FRC_NCO_VAL_SHIFT 7 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8996_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8996_FLL_FRC_NCO 0x0040 /* FLL_FRC_NCO */
+#define WM8996_FLL_FRC_NCO_MASK 0x0040 /* FLL_FRC_NCO */
+#define WM8996_FLL_FRC_NCO_SHIFT 6 /* FLL_FRC_NCO */
+#define WM8996_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */
+#define WM8996_FLL_REFCLK_DIV_MASK 0x0018 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8996_FLL_REFCLK_DIV_SHIFT 3 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8996_FLL_REFCLK_DIV_WIDTH 2 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8996_FLL_REF_FREQ 0x0004 /* FLL_REF_FREQ */
+#define WM8996_FLL_REF_FREQ_MASK 0x0004 /* FLL_REF_FREQ */
+#define WM8996_FLL_REF_FREQ_SHIFT 2 /* FLL_REF_FREQ */
+#define WM8996_FLL_REF_FREQ_WIDTH 1 /* FLL_REF_FREQ */
+#define WM8996_FLL_REFCLK_SRC_MASK 0x0003 /* FLL_REFCLK_SRC - [1:0] */
+#define WM8996_FLL_REFCLK_SRC_SHIFT 0 /* FLL_REFCLK_SRC - [1:0] */
+#define WM8996_FLL_REFCLK_SRC_WIDTH 2 /* FLL_REFCLK_SRC - [1:0] */
+
+/*
+ * R549 (0x225) - FLL Control (6)
+ */
+#define WM8996_FLL_REFCLK_SRC_STS_MASK 0x000C /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8996_FLL_REFCLK_SRC_STS_SHIFT 2 /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8996_FLL_REFCLK_SRC_STS_WIDTH 2 /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8996_FLL_SWITCH_CLK 0x0001 /* FLL_SWITCH_CLK */
+#define WM8996_FLL_SWITCH_CLK_MASK 0x0001 /* FLL_SWITCH_CLK */
+#define WM8996_FLL_SWITCH_CLK_SHIFT 0 /* FLL_SWITCH_CLK */
+#define WM8996_FLL_SWITCH_CLK_WIDTH 1 /* FLL_SWITCH_CLK */
+
+/*
+ * R550 (0x226) - FLL EFS 1
+ */
+#define WM8996_FLL_LAMBDA_MASK 0xFFFF /* FLL_LAMBDA - [15:0] */
+#define WM8996_FLL_LAMBDA_SHIFT 0 /* FLL_LAMBDA - [15:0] */
+#define WM8996_FLL_LAMBDA_WIDTH 16 /* FLL_LAMBDA - [15:0] */
+
+/*
+ * R551 (0x227) - FLL EFS 2
+ */
+#define WM8996_FLL_LFSR_SEL_MASK 0x0006 /* FLL_LFSR_SEL - [2:1] */
+#define WM8996_FLL_LFSR_SEL_SHIFT 1 /* FLL_LFSR_SEL - [2:1] */
+#define WM8996_FLL_LFSR_SEL_WIDTH 2 /* FLL_LFSR_SEL - [2:1] */
+#define WM8996_FLL_EFS_ENA 0x0001 /* FLL_EFS_ENA */
+#define WM8996_FLL_EFS_ENA_MASK 0x0001 /* FLL_EFS_ENA */
+#define WM8996_FLL_EFS_ENA_SHIFT 0 /* FLL_EFS_ENA */
+#define WM8996_FLL_EFS_ENA_WIDTH 1 /* FLL_EFS_ENA */
+
+/*
+ * R768 (0x300) - AIF1 Control
+ */
+#define WM8996_AIF1_TRI 0x0004 /* AIF1_TRI */
+#define WM8996_AIF1_TRI_MASK 0x0004 /* AIF1_TRI */
+#define WM8996_AIF1_TRI_SHIFT 2 /* AIF1_TRI */
+#define WM8996_AIF1_TRI_WIDTH 1 /* AIF1_TRI */
+#define WM8996_AIF1_FMT_MASK 0x0003 /* AIF1_FMT - [1:0] */
+#define WM8996_AIF1_FMT_SHIFT 0 /* AIF1_FMT - [1:0] */
+#define WM8996_AIF1_FMT_WIDTH 2 /* AIF1_FMT - [1:0] */
+
+/*
+ * R769 (0x301) - AIF1 BCLK
+ */
+#define WM8996_AIF1_BCLK_INV 0x0400 /* AIF1_BCLK_INV */
+#define WM8996_AIF1_BCLK_INV_MASK 0x0400 /* AIF1_BCLK_INV */
+#define WM8996_AIF1_BCLK_INV_SHIFT 10 /* AIF1_BCLK_INV */
+#define WM8996_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */
+#define WM8996_AIF1_BCLK_FRC 0x0200 /* AIF1_BCLK_FRC */
+#define WM8996_AIF1_BCLK_FRC_MASK 0x0200 /* AIF1_BCLK_FRC */
+#define WM8996_AIF1_BCLK_FRC_SHIFT 9 /* AIF1_BCLK_FRC */
+#define WM8996_AIF1_BCLK_FRC_WIDTH 1 /* AIF1_BCLK_FRC */
+#define WM8996_AIF1_BCLK_MSTR 0x0100 /* AIF1_BCLK_MSTR */
+#define WM8996_AIF1_BCLK_MSTR_MASK 0x0100 /* AIF1_BCLK_MSTR */
+#define WM8996_AIF1_BCLK_MSTR_SHIFT 8 /* AIF1_BCLK_MSTR */
+#define WM8996_AIF1_BCLK_MSTR_WIDTH 1 /* AIF1_BCLK_MSTR */
+#define WM8996_AIF1_BCLK_DIV_MASK 0x000F /* AIF1_BCLK_DIV - [3:0] */
+#define WM8996_AIF1_BCLK_DIV_SHIFT 0 /* AIF1_BCLK_DIV - [3:0] */
+#define WM8996_AIF1_BCLK_DIV_WIDTH 4 /* AIF1_BCLK_DIV - [3:0] */
+
+/*
+ * R770 (0x302) - AIF1 TX LRCLK(1)
+ */
+#define WM8996_AIF1TX_RATE_MASK 0x07FF /* AIF1TX_RATE - [10:0] */
+#define WM8996_AIF1TX_RATE_SHIFT 0 /* AIF1TX_RATE - [10:0] */
+#define WM8996_AIF1TX_RATE_WIDTH 11 /* AIF1TX_RATE - [10:0] */
+
+/*
+ * R771 (0x303) - AIF1 TX LRCLK(2)
+ */
+#define WM8996_AIF1TX_LRCLK_MODE 0x0008 /* AIF1TX_LRCLK_MODE */
+#define WM8996_AIF1TX_LRCLK_MODE_MASK 0x0008 /* AIF1TX_LRCLK_MODE */
+#define WM8996_AIF1TX_LRCLK_MODE_SHIFT 3 /* AIF1TX_LRCLK_MODE */
+#define WM8996_AIF1TX_LRCLK_MODE_WIDTH 1 /* AIF1TX_LRCLK_MODE */
+#define WM8996_AIF1TX_LRCLK_INV 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM8996_AIF1TX_LRCLK_INV_MASK 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM8996_AIF1TX_LRCLK_INV_SHIFT 2 /* AIF1TX_LRCLK_INV */
+#define WM8996_AIF1TX_LRCLK_INV_WIDTH 1 /* AIF1TX_LRCLK_INV */
+#define WM8996_AIF1TX_LRCLK_FRC 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM8996_AIF1TX_LRCLK_FRC_MASK 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM8996_AIF1TX_LRCLK_FRC_SHIFT 1 /* AIF1TX_LRCLK_FRC */
+#define WM8996_AIF1TX_LRCLK_FRC_WIDTH 1 /* AIF1TX_LRCLK_FRC */
+#define WM8996_AIF1TX_LRCLK_MSTR 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM8996_AIF1TX_LRCLK_MSTR_MASK 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM8996_AIF1TX_LRCLK_MSTR_SHIFT 0 /* AIF1TX_LRCLK_MSTR */
+#define WM8996_AIF1TX_LRCLK_MSTR_WIDTH 1 /* AIF1TX_LRCLK_MSTR */
+
+/*
+ * R772 (0x304) - AIF1 RX LRCLK(1)
+ */
+#define WM8996_AIF1RX_RATE_MASK 0x07FF /* AIF1RX_RATE - [10:0] */
+#define WM8996_AIF1RX_RATE_SHIFT 0 /* AIF1RX_RATE - [10:0] */
+#define WM8996_AIF1RX_RATE_WIDTH 11 /* AIF1RX_RATE - [10:0] */
+
+/*
+ * R773 (0x305) - AIF1 RX LRCLK(2)
+ */
+#define WM8996_AIF1RX_LRCLK_INV 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM8996_AIF1RX_LRCLK_INV_MASK 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM8996_AIF1RX_LRCLK_INV_SHIFT 2 /* AIF1RX_LRCLK_INV */
+#define WM8996_AIF1RX_LRCLK_INV_WIDTH 1 /* AIF1RX_LRCLK_INV */
+#define WM8996_AIF1RX_LRCLK_FRC 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM8996_AIF1RX_LRCLK_FRC_MASK 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM8996_AIF1RX_LRCLK_FRC_SHIFT 1 /* AIF1RX_LRCLK_FRC */
+#define WM8996_AIF1RX_LRCLK_FRC_WIDTH 1 /* AIF1RX_LRCLK_FRC */
+#define WM8996_AIF1RX_LRCLK_MSTR 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM8996_AIF1RX_LRCLK_MSTR_MASK 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM8996_AIF1RX_LRCLK_MSTR_SHIFT 0 /* AIF1RX_LRCLK_MSTR */
+#define WM8996_AIF1RX_LRCLK_MSTR_WIDTH 1 /* AIF1RX_LRCLK_MSTR */
+
+/*
+ * R774 (0x306) - AIF1TX Data Configuration (1)
+ */
+#define WM8996_AIF1TX_WL_MASK 0xFF00 /* AIF1TX_WL - [15:8] */
+#define WM8996_AIF1TX_WL_SHIFT 8 /* AIF1TX_WL - [15:8] */
+#define WM8996_AIF1TX_WL_WIDTH 8 /* AIF1TX_WL - [15:8] */
+#define WM8996_AIF1TX_SLOT_LEN_MASK 0x00FF /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM8996_AIF1TX_SLOT_LEN_SHIFT 0 /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM8996_AIF1TX_SLOT_LEN_WIDTH 8 /* AIF1TX_SLOT_LEN - [7:0] */
+
+/*
+ * R775 (0x307) - AIF1TX Data Configuration (2)
+ */
+#define WM8996_AIF1TX_DAT_TRI 0x0001 /* AIF1TX_DAT_TRI */
+#define WM8996_AIF1TX_DAT_TRI_MASK 0x0001 /* AIF1TX_DAT_TRI */
+#define WM8996_AIF1TX_DAT_TRI_SHIFT 0 /* AIF1TX_DAT_TRI */
+#define WM8996_AIF1TX_DAT_TRI_WIDTH 1 /* AIF1TX_DAT_TRI */
+
+/*
+ * R776 (0x308) - AIF1RX Data Configuration
+ */
+#define WM8996_AIF1RX_WL_MASK 0xFF00 /* AIF1RX_WL - [15:8] */
+#define WM8996_AIF1RX_WL_SHIFT 8 /* AIF1RX_WL - [15:8] */
+#define WM8996_AIF1RX_WL_WIDTH 8 /* AIF1RX_WL - [15:8] */
+#define WM8996_AIF1RX_SLOT_LEN_MASK 0x00FF /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM8996_AIF1RX_SLOT_LEN_SHIFT 0 /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM8996_AIF1RX_SLOT_LEN_WIDTH 8 /* AIF1RX_SLOT_LEN - [7:0] */
+
+/*
+ * R777 (0x309) - AIF1TX Channel 0 Configuration
+ */
+#define WM8996_AIF1TX_CHAN0_DAT_INV 0x8000 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8996_AIF1TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8996_AIF1TX_CHAN0_DAT_INV_SHIFT 15 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8996_AIF1TX_CHAN0_DAT_INV_WIDTH 1 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8996_AIF1TX_CHAN0_SPACING_MASK 0x7E00 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN0_SPACING_SHIFT 9 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN0_SPACING_WIDTH 6 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN0_SLOTS_SHIFT 6 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN0_SLOTS_WIDTH 3 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN0_START_SLOT_MASK 0x003F /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN0_START_SLOT_SHIFT 0 /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN0_START_SLOT_WIDTH 6 /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R778 (0x30A) - AIF1TX Channel 1 Configuration
+ */
+#define WM8996_AIF1TX_CHAN1_DAT_INV 0x8000 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8996_AIF1TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8996_AIF1TX_CHAN1_DAT_INV_SHIFT 15 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8996_AIF1TX_CHAN1_DAT_INV_WIDTH 1 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8996_AIF1TX_CHAN1_SPACING_MASK 0x7E00 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN1_SPACING_SHIFT 9 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN1_SPACING_WIDTH 6 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN1_SLOTS_SHIFT 6 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN1_SLOTS_WIDTH 3 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN1_START_SLOT_MASK 0x003F /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN1_START_SLOT_SHIFT 0 /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN1_START_SLOT_WIDTH 6 /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R779 (0x30B) - AIF1TX Channel 2 Configuration
+ */
+#define WM8996_AIF1TX_CHAN2_DAT_INV 0x8000 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8996_AIF1TX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8996_AIF1TX_CHAN2_DAT_INV_SHIFT 15 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8996_AIF1TX_CHAN2_DAT_INV_WIDTH 1 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8996_AIF1TX_CHAN2_SPACING_MASK 0x7E00 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN2_SPACING_SHIFT 9 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN2_SPACING_WIDTH 6 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN2_SLOTS_SHIFT 6 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN2_SLOTS_WIDTH 3 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN2_START_SLOT_MASK 0x003F /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN2_START_SLOT_SHIFT 0 /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN2_START_SLOT_WIDTH 6 /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+
+/*
+ * R780 (0x30C) - AIF1TX Channel 3 Configuration
+ */
+#define WM8996_AIF1TX_CHAN3_DAT_INV 0x8000 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8996_AIF1TX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8996_AIF1TX_CHAN3_DAT_INV_SHIFT 15 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8996_AIF1TX_CHAN3_DAT_INV_WIDTH 1 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8996_AIF1TX_CHAN3_SPACING_MASK 0x7E00 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN3_SPACING_SHIFT 9 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN3_SPACING_WIDTH 6 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN3_SLOTS_SHIFT 6 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN3_SLOTS_WIDTH 3 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN3_START_SLOT_MASK 0x003F /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN3_START_SLOT_SHIFT 0 /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN3_START_SLOT_WIDTH 6 /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+
+/*
+ * R781 (0x30D) - AIF1TX Channel 4 Configuration
+ */
+#define WM8996_AIF1TX_CHAN4_DAT_INV 0x8000 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8996_AIF1TX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8996_AIF1TX_CHAN4_DAT_INV_SHIFT 15 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8996_AIF1TX_CHAN4_DAT_INV_WIDTH 1 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8996_AIF1TX_CHAN4_SPACING_MASK 0x7E00 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN4_SPACING_SHIFT 9 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN4_SPACING_WIDTH 6 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN4_SLOTS_SHIFT 6 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN4_SLOTS_WIDTH 3 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN4_START_SLOT_MASK 0x003F /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN4_START_SLOT_SHIFT 0 /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN4_START_SLOT_WIDTH 6 /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+
+/*
+ * R782 (0x30E) - AIF1TX Channel 5 Configuration
+ */
+#define WM8996_AIF1TX_CHAN5_DAT_INV 0x8000 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8996_AIF1TX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8996_AIF1TX_CHAN5_DAT_INV_SHIFT 15 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8996_AIF1TX_CHAN5_DAT_INV_WIDTH 1 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8996_AIF1TX_CHAN5_SPACING_MASK 0x7E00 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN5_SPACING_SHIFT 9 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN5_SPACING_WIDTH 6 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN5_SLOTS_SHIFT 6 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN5_SLOTS_WIDTH 3 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN5_START_SLOT_MASK 0x003F /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN5_START_SLOT_SHIFT 0 /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN5_START_SLOT_WIDTH 6 /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+
+/*
+ * R783 (0x30F) - AIF1RX Channel 0 Configuration
+ */
+#define WM8996_AIF1RX_CHAN0_DAT_INV 0x8000 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8996_AIF1RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8996_AIF1RX_CHAN0_DAT_INV_SHIFT 15 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8996_AIF1RX_CHAN0_DAT_INV_WIDTH 1 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8996_AIF1RX_CHAN0_SPACING_MASK 0x7E00 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN0_SPACING_SHIFT 9 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN0_SPACING_WIDTH 6 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN0_SLOTS_SHIFT 6 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN0_SLOTS_WIDTH 3 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN0_START_SLOT_MASK 0x003F /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN0_START_SLOT_SHIFT 0 /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN0_START_SLOT_WIDTH 6 /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R784 (0x310) - AIF1RX Channel 1 Configuration
+ */
+#define WM8996_AIF1RX_CHAN1_DAT_INV 0x8000 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8996_AIF1RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8996_AIF1RX_CHAN1_DAT_INV_SHIFT 15 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8996_AIF1RX_CHAN1_DAT_INV_WIDTH 1 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8996_AIF1RX_CHAN1_SPACING_MASK 0x7E00 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN1_SPACING_SHIFT 9 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN1_SPACING_WIDTH 6 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN1_SLOTS_SHIFT 6 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN1_SLOTS_WIDTH 3 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN1_START_SLOT_MASK 0x003F /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN1_START_SLOT_SHIFT 0 /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN1_START_SLOT_WIDTH 6 /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R785 (0x311) - AIF1RX Channel 2 Configuration
+ */
+#define WM8996_AIF1RX_CHAN2_DAT_INV 0x8000 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8996_AIF1RX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8996_AIF1RX_CHAN2_DAT_INV_SHIFT 15 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8996_AIF1RX_CHAN2_DAT_INV_WIDTH 1 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8996_AIF1RX_CHAN2_SPACING_MASK 0x7E00 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN2_SPACING_SHIFT 9 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN2_SPACING_WIDTH 6 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN2_SLOTS_SHIFT 6 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN2_SLOTS_WIDTH 3 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN2_START_SLOT_MASK 0x003F /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN2_START_SLOT_SHIFT 0 /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN2_START_SLOT_WIDTH 6 /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+
+/*
+ * R786 (0x312) - AIF1RX Channel 3 Configuration
+ */
+#define WM8996_AIF1RX_CHAN3_DAT_INV 0x8000 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8996_AIF1RX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8996_AIF1RX_CHAN3_DAT_INV_SHIFT 15 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8996_AIF1RX_CHAN3_DAT_INV_WIDTH 1 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8996_AIF1RX_CHAN3_SPACING_MASK 0x7E00 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN3_SPACING_SHIFT 9 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN3_SPACING_WIDTH 6 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN3_SLOTS_SHIFT 6 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN3_SLOTS_WIDTH 3 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN3_START_SLOT_MASK 0x003F /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN3_START_SLOT_SHIFT 0 /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN3_START_SLOT_WIDTH 6 /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+
+/*
+ * R787 (0x313) - AIF1RX Channel 4 Configuration
+ */
+#define WM8996_AIF1RX_CHAN4_DAT_INV 0x8000 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8996_AIF1RX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8996_AIF1RX_CHAN4_DAT_INV_SHIFT 15 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8996_AIF1RX_CHAN4_DAT_INV_WIDTH 1 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8996_AIF1RX_CHAN4_SPACING_MASK 0x7E00 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN4_SPACING_SHIFT 9 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN4_SPACING_WIDTH 6 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN4_SLOTS_SHIFT 6 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN4_SLOTS_WIDTH 3 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN4_START_SLOT_MASK 0x003F /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN4_START_SLOT_SHIFT 0 /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN4_START_SLOT_WIDTH 6 /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+
+/*
+ * R788 (0x314) - AIF1RX Channel 5 Configuration
+ */
+#define WM8996_AIF1RX_CHAN5_DAT_INV 0x8000 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8996_AIF1RX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8996_AIF1RX_CHAN5_DAT_INV_SHIFT 15 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8996_AIF1RX_CHAN5_DAT_INV_WIDTH 1 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8996_AIF1RX_CHAN5_SPACING_MASK 0x7E00 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN5_SPACING_SHIFT 9 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN5_SPACING_WIDTH 6 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN5_SLOTS_SHIFT 6 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN5_SLOTS_WIDTH 3 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN5_START_SLOT_MASK 0x003F /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN5_START_SLOT_SHIFT 0 /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN5_START_SLOT_WIDTH 6 /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+
+/*
+ * R789 (0x315) - AIF1RX Mono Configuration
+ */
+#define WM8996_AIF1RX_CHAN4_MONO_MODE 0x0004 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8996_AIF1RX_CHAN4_MONO_MODE_MASK 0x0004 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8996_AIF1RX_CHAN4_MONO_MODE_SHIFT 2 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8996_AIF1RX_CHAN4_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8996_AIF1RX_CHAN2_MONO_MODE 0x0002 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8996_AIF1RX_CHAN2_MONO_MODE_MASK 0x0002 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8996_AIF1RX_CHAN2_MONO_MODE_SHIFT 1 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8996_AIF1RX_CHAN2_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8996_AIF1RX_CHAN0_MONO_MODE 0x0001 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8996_AIF1RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8996_AIF1RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8996_AIF1RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN0_MONO_MODE */
+
+/*
+ * R794 (0x31A) - AIF1TX Test
+ */
+#define WM8996_AIF1TX45_DITHER_ENA 0x0004 /* AIF1TX45_DITHER_ENA */
+#define WM8996_AIF1TX45_DITHER_ENA_MASK 0x0004 /* AIF1TX45_DITHER_ENA */
+#define WM8996_AIF1TX45_DITHER_ENA_SHIFT 2 /* AIF1TX45_DITHER_ENA */
+#define WM8996_AIF1TX45_DITHER_ENA_WIDTH 1 /* AIF1TX45_DITHER_ENA */
+#define WM8996_AIF1TX23_DITHER_ENA 0x0002 /* AIF1TX23_DITHER_ENA */
+#define WM8996_AIF1TX23_DITHER_ENA_MASK 0x0002 /* AIF1TX23_DITHER_ENA */
+#define WM8996_AIF1TX23_DITHER_ENA_SHIFT 1 /* AIF1TX23_DITHER_ENA */
+#define WM8996_AIF1TX23_DITHER_ENA_WIDTH 1 /* AIF1TX23_DITHER_ENA */
+#define WM8996_AIF1TX01_DITHER_ENA 0x0001 /* AIF1TX01_DITHER_ENA */
+#define WM8996_AIF1TX01_DITHER_ENA_MASK 0x0001 /* AIF1TX01_DITHER_ENA */
+#define WM8996_AIF1TX01_DITHER_ENA_SHIFT 0 /* AIF1TX01_DITHER_ENA */
+#define WM8996_AIF1TX01_DITHER_ENA_WIDTH 1 /* AIF1TX01_DITHER_ENA */
+
+/*
+ * R800 (0x320) - AIF2 Control
+ */
+#define WM8996_AIF2_TRI 0x0004 /* AIF2_TRI */
+#define WM8996_AIF2_TRI_MASK 0x0004 /* AIF2_TRI */
+#define WM8996_AIF2_TRI_SHIFT 2 /* AIF2_TRI */
+#define WM8996_AIF2_TRI_WIDTH 1 /* AIF2_TRI */
+#define WM8996_AIF2_FMT_MASK 0x0003 /* AIF2_FMT - [1:0] */
+#define WM8996_AIF2_FMT_SHIFT 0 /* AIF2_FMT - [1:0] */
+#define WM8996_AIF2_FMT_WIDTH 2 /* AIF2_FMT - [1:0] */
+
+/*
+ * R801 (0x321) - AIF2 BCLK
+ */
+#define WM8996_AIF2_BCLK_INV 0x0400 /* AIF2_BCLK_INV */
+#define WM8996_AIF2_BCLK_INV_MASK 0x0400 /* AIF2_BCLK_INV */
+#define WM8996_AIF2_BCLK_INV_SHIFT 10 /* AIF2_BCLK_INV */
+#define WM8996_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */
+#define WM8996_AIF2_BCLK_FRC 0x0200 /* AIF2_BCLK_FRC */
+#define WM8996_AIF2_BCLK_FRC_MASK 0x0200 /* AIF2_BCLK_FRC */
+#define WM8996_AIF2_BCLK_FRC_SHIFT 9 /* AIF2_BCLK_FRC */
+#define WM8996_AIF2_BCLK_FRC_WIDTH 1 /* AIF2_BCLK_FRC */
+#define WM8996_AIF2_BCLK_MSTR 0x0100 /* AIF2_BCLK_MSTR */
+#define WM8996_AIF2_BCLK_MSTR_MASK 0x0100 /* AIF2_BCLK_MSTR */
+#define WM8996_AIF2_BCLK_MSTR_SHIFT 8 /* AIF2_BCLK_MSTR */
+#define WM8996_AIF2_BCLK_MSTR_WIDTH 1 /* AIF2_BCLK_MSTR */
+#define WM8996_AIF2_BCLK_DIV_MASK 0x000F /* AIF2_BCLK_DIV - [3:0] */
+#define WM8996_AIF2_BCLK_DIV_SHIFT 0 /* AIF2_BCLK_DIV - [3:0] */
+#define WM8996_AIF2_BCLK_DIV_WIDTH 4 /* AIF2_BCLK_DIV - [3:0] */
+
+/*
+ * R802 (0x322) - AIF2 TX LRCLK(1)
+ */
+#define WM8996_AIF2TX_RATE_MASK 0x07FF /* AIF2TX_RATE - [10:0] */
+#define WM8996_AIF2TX_RATE_SHIFT 0 /* AIF2TX_RATE - [10:0] */
+#define WM8996_AIF2TX_RATE_WIDTH 11 /* AIF2TX_RATE - [10:0] */
+
+/*
+ * R803 (0x323) - AIF2 TX LRCLK(2)
+ */
+#define WM8996_AIF2TX_LRCLK_MODE 0x0008 /* AIF2TX_LRCLK_MODE */
+#define WM8996_AIF2TX_LRCLK_MODE_MASK 0x0008 /* AIF2TX_LRCLK_MODE */
+#define WM8996_AIF2TX_LRCLK_MODE_SHIFT 3 /* AIF2TX_LRCLK_MODE */
+#define WM8996_AIF2TX_LRCLK_MODE_WIDTH 1 /* AIF2TX_LRCLK_MODE */
+#define WM8996_AIF2TX_LRCLK_INV 0x0004 /* AIF2TX_LRCLK_INV */
+#define WM8996_AIF2TX_LRCLK_INV_MASK 0x0004 /* AIF2TX_LRCLK_INV */
+#define WM8996_AIF2TX_LRCLK_INV_SHIFT 2 /* AIF2TX_LRCLK_INV */
+#define WM8996_AIF2TX_LRCLK_INV_WIDTH 1 /* AIF2TX_LRCLK_INV */
+#define WM8996_AIF2TX_LRCLK_FRC 0x0002 /* AIF2TX_LRCLK_FRC */
+#define WM8996_AIF2TX_LRCLK_FRC_MASK 0x0002 /* AIF2TX_LRCLK_FRC */
+#define WM8996_AIF2TX_LRCLK_FRC_SHIFT 1 /* AIF2TX_LRCLK_FRC */
+#define WM8996_AIF2TX_LRCLK_FRC_WIDTH 1 /* AIF2TX_LRCLK_FRC */
+#define WM8996_AIF2TX_LRCLK_MSTR 0x0001 /* AIF2TX_LRCLK_MSTR */
+#define WM8996_AIF2TX_LRCLK_MSTR_MASK 0x0001 /* AIF2TX_LRCLK_MSTR */
+#define WM8996_AIF2TX_LRCLK_MSTR_SHIFT 0 /* AIF2TX_LRCLK_MSTR */
+#define WM8996_AIF2TX_LRCLK_MSTR_WIDTH 1 /* AIF2TX_LRCLK_MSTR */
+
+/*
+ * R804 (0x324) - AIF2 RX LRCLK(1)
+ */
+#define WM8996_AIF2RX_RATE_MASK 0x07FF /* AIF2RX_RATE - [10:0] */
+#define WM8996_AIF2RX_RATE_SHIFT 0 /* AIF2RX_RATE - [10:0] */
+#define WM8996_AIF2RX_RATE_WIDTH 11 /* AIF2RX_RATE - [10:0] */
+
+/*
+ * R805 (0x325) - AIF2 RX LRCLK(2)
+ */
+#define WM8996_AIF2RX_LRCLK_INV 0x0004 /* AIF2RX_LRCLK_INV */
+#define WM8996_AIF2RX_LRCLK_INV_MASK 0x0004 /* AIF2RX_LRCLK_INV */
+#define WM8996_AIF2RX_LRCLK_INV_SHIFT 2 /* AIF2RX_LRCLK_INV */
+#define WM8996_AIF2RX_LRCLK_INV_WIDTH 1 /* AIF2RX_LRCLK_INV */
+#define WM8996_AIF2RX_LRCLK_FRC 0x0002 /* AIF2RX_LRCLK_FRC */
+#define WM8996_AIF2RX_LRCLK_FRC_MASK 0x0002 /* AIF2RX_LRCLK_FRC */
+#define WM8996_AIF2RX_LRCLK_FRC_SHIFT 1 /* AIF2RX_LRCLK_FRC */
+#define WM8996_AIF2RX_LRCLK_FRC_WIDTH 1 /* AIF2RX_LRCLK_FRC */
+#define WM8996_AIF2RX_LRCLK_MSTR 0x0001 /* AIF2RX_LRCLK_MSTR */
+#define WM8996_AIF2RX_LRCLK_MSTR_MASK 0x0001 /* AIF2RX_LRCLK_MSTR */
+#define WM8996_AIF2RX_LRCLK_MSTR_SHIFT 0 /* AIF2RX_LRCLK_MSTR */
+#define WM8996_AIF2RX_LRCLK_MSTR_WIDTH 1 /* AIF2RX_LRCLK_MSTR */
+
+/*
+ * R806 (0x326) - AIF2TX Data Configuration (1)
+ */
+#define WM8996_AIF2TX_WL_MASK 0xFF00 /* AIF2TX_WL - [15:8] */
+#define WM8996_AIF2TX_WL_SHIFT 8 /* AIF2TX_WL - [15:8] */
+#define WM8996_AIF2TX_WL_WIDTH 8 /* AIF2TX_WL - [15:8] */
+#define WM8996_AIF2TX_SLOT_LEN_MASK 0x00FF /* AIF2TX_SLOT_LEN - [7:0] */
+#define WM8996_AIF2TX_SLOT_LEN_SHIFT 0 /* AIF2TX_SLOT_LEN - [7:0] */
+#define WM8996_AIF2TX_SLOT_LEN_WIDTH 8 /* AIF2TX_SLOT_LEN - [7:0] */
+
+/*
+ * R807 (0x327) - AIF2TX Data Configuration (2)
+ */
+#define WM8996_AIF2TX_DAT_TRI 0x0001 /* AIF2TX_DAT_TRI */
+#define WM8996_AIF2TX_DAT_TRI_MASK 0x0001 /* AIF2TX_DAT_TRI */
+#define WM8996_AIF2TX_DAT_TRI_SHIFT 0 /* AIF2TX_DAT_TRI */
+#define WM8996_AIF2TX_DAT_TRI_WIDTH 1 /* AIF2TX_DAT_TRI */
+
+/*
+ * R808 (0x328) - AIF2RX Data Configuration
+ */
+#define WM8996_AIF2RX_WL_MASK 0xFF00 /* AIF2RX_WL - [15:8] */
+#define WM8996_AIF2RX_WL_SHIFT 8 /* AIF2RX_WL - [15:8] */
+#define WM8996_AIF2RX_WL_WIDTH 8 /* AIF2RX_WL - [15:8] */
+#define WM8996_AIF2RX_SLOT_LEN_MASK 0x00FF /* AIF2RX_SLOT_LEN - [7:0] */
+#define WM8996_AIF2RX_SLOT_LEN_SHIFT 0 /* AIF2RX_SLOT_LEN - [7:0] */
+#define WM8996_AIF2RX_SLOT_LEN_WIDTH 8 /* AIF2RX_SLOT_LEN - [7:0] */
+
+/*
+ * R809 (0x329) - AIF2TX Channel 0 Configuration
+ */
+#define WM8996_AIF2TX_CHAN0_DAT_INV 0x8000 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8996_AIF2TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8996_AIF2TX_CHAN0_DAT_INV_SHIFT 15 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8996_AIF2TX_CHAN0_DAT_INV_WIDTH 1 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8996_AIF2TX_CHAN0_SPACING_MASK 0x7E00 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF2TX_CHAN0_SPACING_SHIFT 9 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF2TX_CHAN0_SPACING_WIDTH 6 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF2TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF2TX_CHAN0_SLOTS_SHIFT 6 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF2TX_CHAN0_SLOTS_WIDTH 3 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF2TX_CHAN0_START_SLOT_MASK 0x003F /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF2TX_CHAN0_START_SLOT_SHIFT 0 /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF2TX_CHAN0_START_SLOT_WIDTH 6 /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R810 (0x32A) - AIF2TX Channel 1 Configuration
+ */
+#define WM8996_AIF2TX_CHAN1_DAT_INV 0x8000 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8996_AIF2TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8996_AIF2TX_CHAN1_DAT_INV_SHIFT 15 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8996_AIF2TX_CHAN1_DAT_INV_WIDTH 1 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8996_AIF2TX_CHAN1_SPACING_MASK 0x7E00 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF2TX_CHAN1_SPACING_SHIFT 9 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF2TX_CHAN1_SPACING_WIDTH 6 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF2TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF2TX_CHAN1_SLOTS_SHIFT 6 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF2TX_CHAN1_SLOTS_WIDTH 3 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF2TX_CHAN1_START_SLOT_MASK 0x003F /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF2TX_CHAN1_START_SLOT_SHIFT 0 /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF2TX_CHAN1_START_SLOT_WIDTH 6 /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R811 (0x32B) - AIF2RX Channel 0 Configuration
+ */
+#define WM8996_AIF2RX_CHAN0_DAT_INV 0x8000 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8996_AIF2RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8996_AIF2RX_CHAN0_DAT_INV_SHIFT 15 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8996_AIF2RX_CHAN0_DAT_INV_WIDTH 1 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8996_AIF2RX_CHAN0_SPACING_MASK 0x7E00 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF2RX_CHAN0_SPACING_SHIFT 9 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF2RX_CHAN0_SPACING_WIDTH 6 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF2RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF2RX_CHAN0_SLOTS_SHIFT 6 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF2RX_CHAN0_SLOTS_WIDTH 3 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF2RX_CHAN0_START_SLOT_MASK 0x003F /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF2RX_CHAN0_START_SLOT_SHIFT 0 /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF2RX_CHAN0_START_SLOT_WIDTH 6 /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R812 (0x32C) - AIF2RX Channel 1 Configuration
+ */
+#define WM8996_AIF2RX_CHAN1_DAT_INV 0x8000 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8996_AIF2RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8996_AIF2RX_CHAN1_DAT_INV_SHIFT 15 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8996_AIF2RX_CHAN1_DAT_INV_WIDTH 1 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8996_AIF2RX_CHAN1_SPACING_MASK 0x7E00 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF2RX_CHAN1_SPACING_SHIFT 9 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF2RX_CHAN1_SPACING_WIDTH 6 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF2RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF2RX_CHAN1_SLOTS_SHIFT 6 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF2RX_CHAN1_SLOTS_WIDTH 3 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF2RX_CHAN1_START_SLOT_MASK 0x003F /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF2RX_CHAN1_START_SLOT_SHIFT 0 /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF2RX_CHAN1_START_SLOT_WIDTH 6 /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R813 (0x32D) - AIF2RX Mono Configuration
+ */
+#define WM8996_AIF2RX_CHAN0_MONO_MODE 0x0001 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8996_AIF2RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8996_AIF2RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8996_AIF2RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF2RX_CHAN0_MONO_MODE */
+
+/*
+ * R815 (0x32F) - AIF2TX Test
+ */
+#define WM8996_AIF2TX_DITHER_ENA 0x0001 /* AIF2TX_DITHER_ENA */
+#define WM8996_AIF2TX_DITHER_ENA_MASK 0x0001 /* AIF2TX_DITHER_ENA */
+#define WM8996_AIF2TX_DITHER_ENA_SHIFT 0 /* AIF2TX_DITHER_ENA */
+#define WM8996_AIF2TX_DITHER_ENA_WIDTH 1 /* AIF2TX_DITHER_ENA */
+
+/*
+ * R1024 (0x400) - DSP1 TX Left Volume
+ */
+#define WM8996_DSP1TX_VU 0x0100 /* DSP1TX_VU */
+#define WM8996_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */
+#define WM8996_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */
+#define WM8996_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */
+#define WM8996_DSP1TXL_VOL_MASK 0x00FF /* DSP1TXL_VOL - [7:0] */
+#define WM8996_DSP1TXL_VOL_SHIFT 0 /* DSP1TXL_VOL - [7:0] */
+#define WM8996_DSP1TXL_VOL_WIDTH 8 /* DSP1TXL_VOL - [7:0] */
+
+/*
+ * R1025 (0x401) - DSP1 TX Right Volume
+ */
+#define WM8996_DSP1TX_VU 0x0100 /* DSP1TX_VU */
+#define WM8996_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */
+#define WM8996_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */
+#define WM8996_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */
+#define WM8996_DSP1TXR_VOL_MASK 0x00FF /* DSP1TXR_VOL - [7:0] */
+#define WM8996_DSP1TXR_VOL_SHIFT 0 /* DSP1TXR_VOL - [7:0] */
+#define WM8996_DSP1TXR_VOL_WIDTH 8 /* DSP1TXR_VOL - [7:0] */
+
+/*
+ * R1026 (0x402) - DSP1 RX Left Volume
+ */
+#define WM8996_DSP1RX_VU 0x0100 /* DSP1RX_VU */
+#define WM8996_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */
+#define WM8996_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */
+#define WM8996_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */
+#define WM8996_DSP1RXL_VOL_MASK 0x00FF /* DSP1RXL_VOL - [7:0] */
+#define WM8996_DSP1RXL_VOL_SHIFT 0 /* DSP1RXL_VOL - [7:0] */
+#define WM8996_DSP1RXL_VOL_WIDTH 8 /* DSP1RXL_VOL - [7:0] */
+
+/*
+ * R1027 (0x403) - DSP1 RX Right Volume
+ */
+#define WM8996_DSP1RX_VU 0x0100 /* DSP1RX_VU */
+#define WM8996_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */
+#define WM8996_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */
+#define WM8996_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */
+#define WM8996_DSP1RXR_VOL_MASK 0x00FF /* DSP1RXR_VOL - [7:0] */
+#define WM8996_DSP1RXR_VOL_SHIFT 0 /* DSP1RXR_VOL - [7:0] */
+#define WM8996_DSP1RXR_VOL_WIDTH 8 /* DSP1RXR_VOL - [7:0] */
+
+/*
+ * R1040 (0x410) - DSP1 TX Filters
+ */
+#define WM8996_DSP1TX_NF 0x2000 /* DSP1TX_NF */
+#define WM8996_DSP1TX_NF_MASK 0x2000 /* DSP1TX_NF */
+#define WM8996_DSP1TX_NF_SHIFT 13 /* DSP1TX_NF */
+#define WM8996_DSP1TX_NF_WIDTH 1 /* DSP1TX_NF */
+#define WM8996_DSP1TXL_HPF 0x1000 /* DSP1TXL_HPF */
+#define WM8996_DSP1TXL_HPF_MASK 0x1000 /* DSP1TXL_HPF */
+#define WM8996_DSP1TXL_HPF_SHIFT 12 /* DSP1TXL_HPF */
+#define WM8996_DSP1TXL_HPF_WIDTH 1 /* DSP1TXL_HPF */
+#define WM8996_DSP1TXR_HPF 0x0800 /* DSP1TXR_HPF */
+#define WM8996_DSP1TXR_HPF_MASK 0x0800 /* DSP1TXR_HPF */
+#define WM8996_DSP1TXR_HPF_SHIFT 11 /* DSP1TXR_HPF */
+#define WM8996_DSP1TXR_HPF_WIDTH 1 /* DSP1TXR_HPF */
+#define WM8996_DSP1TX_HPF_MODE_MASK 0x0018 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8996_DSP1TX_HPF_MODE_SHIFT 3 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8996_DSP1TX_HPF_MODE_WIDTH 2 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8996_DSP1TX_HPF_CUT_MASK 0x0007 /* DSP1TX_HPF_CUT - [2:0] */
+#define WM8996_DSP1TX_HPF_CUT_SHIFT 0 /* DSP1TX_HPF_CUT - [2:0] */
+#define WM8996_DSP1TX_HPF_CUT_WIDTH 3 /* DSP1TX_HPF_CUT - [2:0] */
+
+/*
+ * R1056 (0x420) - DSP1 RX Filters (1)
+ */
+#define WM8996_DSP1RX_MUTE 0x0200 /* DSP1RX_MUTE */
+#define WM8996_DSP1RX_MUTE_MASK 0x0200 /* DSP1RX_MUTE */
+#define WM8996_DSP1RX_MUTE_SHIFT 9 /* DSP1RX_MUTE */
+#define WM8996_DSP1RX_MUTE_WIDTH 1 /* DSP1RX_MUTE */
+#define WM8996_DSP1RX_MONO 0x0080 /* DSP1RX_MONO */
+#define WM8996_DSP1RX_MONO_MASK 0x0080 /* DSP1RX_MONO */
+#define WM8996_DSP1RX_MONO_SHIFT 7 /* DSP1RX_MONO */
+#define WM8996_DSP1RX_MONO_WIDTH 1 /* DSP1RX_MONO */
+#define WM8996_DSP1RX_MUTERATE 0x0020 /* DSP1RX_MUTERATE */
+#define WM8996_DSP1RX_MUTERATE_MASK 0x0020 /* DSP1RX_MUTERATE */
+#define WM8996_DSP1RX_MUTERATE_SHIFT 5 /* DSP1RX_MUTERATE */
+#define WM8996_DSP1RX_MUTERATE_WIDTH 1 /* DSP1RX_MUTERATE */
+#define WM8996_DSP1RX_UNMUTE_RAMP 0x0010 /* DSP1RX_UNMUTE_RAMP */
+#define WM8996_DSP1RX_UNMUTE_RAMP_MASK 0x0010 /* DSP1RX_UNMUTE_RAMP */
+#define WM8996_DSP1RX_UNMUTE_RAMP_SHIFT 4 /* DSP1RX_UNMUTE_RAMP */
+#define WM8996_DSP1RX_UNMUTE_RAMP_WIDTH 1 /* DSP1RX_UNMUTE_RAMP */
+
+/*
+ * R1057 (0x421) - DSP1 RX Filters (2)
+ */
+#define WM8996_DSP1RX_3D_GAIN_MASK 0x3E00 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8996_DSP1RX_3D_GAIN_SHIFT 9 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8996_DSP1RX_3D_GAIN_WIDTH 5 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8996_DSP1RX_3D_ENA 0x0100 /* DSP1RX_3D_ENA */
+#define WM8996_DSP1RX_3D_ENA_MASK 0x0100 /* DSP1RX_3D_ENA */
+#define WM8996_DSP1RX_3D_ENA_SHIFT 8 /* DSP1RX_3D_ENA */
+#define WM8996_DSP1RX_3D_ENA_WIDTH 1 /* DSP1RX_3D_ENA */
+
+/*
+ * R1088 (0x440) - DSP1 DRC (1)
+ */
+#define WM8996_DSP1DRC_SIG_DET_RMS_MASK 0xF800 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8996_DSP1DRC_SIG_DET_RMS_SHIFT 11 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8996_DSP1DRC_SIG_DET_RMS_WIDTH 5 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8996_DSP1DRC_SIG_DET_PK_MASK 0x0600 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8996_DSP1DRC_SIG_DET_PK_SHIFT 9 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8996_DSP1DRC_SIG_DET_PK_WIDTH 2 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8996_DSP1DRC_NG_ENA 0x0100 /* DSP1DRC_NG_ENA */
+#define WM8996_DSP1DRC_NG_ENA_MASK 0x0100 /* DSP1DRC_NG_ENA */
+#define WM8996_DSP1DRC_NG_ENA_SHIFT 8 /* DSP1DRC_NG_ENA */
+#define WM8996_DSP1DRC_NG_ENA_WIDTH 1 /* DSP1DRC_NG_ENA */
+#define WM8996_DSP1DRC_SIG_DET_MODE 0x0080 /* DSP1DRC_SIG_DET_MODE */
+#define WM8996_DSP1DRC_SIG_DET_MODE_MASK 0x0080 /* DSP1DRC_SIG_DET_MODE */
+#define WM8996_DSP1DRC_SIG_DET_MODE_SHIFT 7 /* DSP1DRC_SIG_DET_MODE */
+#define WM8996_DSP1DRC_SIG_DET_MODE_WIDTH 1 /* DSP1DRC_SIG_DET_MODE */
+#define WM8996_DSP1DRC_SIG_DET 0x0040 /* DSP1DRC_SIG_DET */
+#define WM8996_DSP1DRC_SIG_DET_MASK 0x0040 /* DSP1DRC_SIG_DET */
+#define WM8996_DSP1DRC_SIG_DET_SHIFT 6 /* DSP1DRC_SIG_DET */
+#define WM8996_DSP1DRC_SIG_DET_WIDTH 1 /* DSP1DRC_SIG_DET */
+#define WM8996_DSP1DRC_KNEE2_OP_ENA 0x0020 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8996_DSP1DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8996_DSP1DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8996_DSP1DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8996_DSP1DRC_QR 0x0010 /* DSP1DRC_QR */
+#define WM8996_DSP1DRC_QR_MASK 0x0010 /* DSP1DRC_QR */
+#define WM8996_DSP1DRC_QR_SHIFT 4 /* DSP1DRC_QR */
+#define WM8996_DSP1DRC_QR_WIDTH 1 /* DSP1DRC_QR */
+#define WM8996_DSP1DRC_ANTICLIP 0x0008 /* DSP1DRC_ANTICLIP */
+#define WM8996_DSP1DRC_ANTICLIP_MASK 0x0008 /* DSP1DRC_ANTICLIP */
+#define WM8996_DSP1DRC_ANTICLIP_SHIFT 3 /* DSP1DRC_ANTICLIP */
+#define WM8996_DSP1DRC_ANTICLIP_WIDTH 1 /* DSP1DRC_ANTICLIP */
+#define WM8996_DSP1RX_DRC_ENA 0x0004 /* DSP1RX_DRC_ENA */
+#define WM8996_DSP1RX_DRC_ENA_MASK 0x0004 /* DSP1RX_DRC_ENA */
+#define WM8996_DSP1RX_DRC_ENA_SHIFT 2 /* DSP1RX_DRC_ENA */
+#define WM8996_DSP1RX_DRC_ENA_WIDTH 1 /* DSP1RX_DRC_ENA */
+#define WM8996_DSP1TXL_DRC_ENA 0x0002 /* DSP1TXL_DRC_ENA */
+#define WM8996_DSP1TXL_DRC_ENA_MASK 0x0002 /* DSP1TXL_DRC_ENA */
+#define WM8996_DSP1TXL_DRC_ENA_SHIFT 1 /* DSP1TXL_DRC_ENA */
+#define WM8996_DSP1TXL_DRC_ENA_WIDTH 1 /* DSP1TXL_DRC_ENA */
+#define WM8996_DSP1TXR_DRC_ENA 0x0001 /* DSP1TXR_DRC_ENA */
+#define WM8996_DSP1TXR_DRC_ENA_MASK 0x0001 /* DSP1TXR_DRC_ENA */
+#define WM8996_DSP1TXR_DRC_ENA_SHIFT 0 /* DSP1TXR_DRC_ENA */
+#define WM8996_DSP1TXR_DRC_ENA_WIDTH 1 /* DSP1TXR_DRC_ENA */
+
+/*
+ * R1089 (0x441) - DSP1 DRC (2)
+ */
+#define WM8996_DSP1DRC_ATK_MASK 0x1E00 /* DSP1DRC_ATK - [12:9] */
+#define WM8996_DSP1DRC_ATK_SHIFT 9 /* DSP1DRC_ATK - [12:9] */
+#define WM8996_DSP1DRC_ATK_WIDTH 4 /* DSP1DRC_ATK - [12:9] */
+#define WM8996_DSP1DRC_DCY_MASK 0x01E0 /* DSP1DRC_DCY - [8:5] */
+#define WM8996_DSP1DRC_DCY_SHIFT 5 /* DSP1DRC_DCY - [8:5] */
+#define WM8996_DSP1DRC_DCY_WIDTH 4 /* DSP1DRC_DCY - [8:5] */
+#define WM8996_DSP1DRC_MINGAIN_MASK 0x001C /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8996_DSP1DRC_MINGAIN_SHIFT 2 /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8996_DSP1DRC_MINGAIN_WIDTH 3 /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8996_DSP1DRC_MAXGAIN_MASK 0x0003 /* DSP1DRC_MAXGAIN - [1:0] */
+#define WM8996_DSP1DRC_MAXGAIN_SHIFT 0 /* DSP1DRC_MAXGAIN - [1:0] */
+#define WM8996_DSP1DRC_MAXGAIN_WIDTH 2 /* DSP1DRC_MAXGAIN - [1:0] */
+
+/*
+ * R1090 (0x442) - DSP1 DRC (3)
+ */
+#define WM8996_DSP1DRC_NG_MINGAIN_MASK 0xF000 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8996_DSP1DRC_NG_MINGAIN_SHIFT 12 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8996_DSP1DRC_NG_MINGAIN_WIDTH 4 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8996_DSP1DRC_NG_EXP_MASK 0x0C00 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8996_DSP1DRC_NG_EXP_SHIFT 10 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8996_DSP1DRC_NG_EXP_WIDTH 2 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8996_DSP1DRC_QR_THR_MASK 0x0300 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8996_DSP1DRC_QR_THR_SHIFT 8 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8996_DSP1DRC_QR_THR_WIDTH 2 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8996_DSP1DRC_QR_DCY_MASK 0x00C0 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8996_DSP1DRC_QR_DCY_SHIFT 6 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8996_DSP1DRC_QR_DCY_WIDTH 2 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8996_DSP1DRC_HI_COMP_MASK 0x0038 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8996_DSP1DRC_HI_COMP_SHIFT 3 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8996_DSP1DRC_HI_COMP_WIDTH 3 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8996_DSP1DRC_LO_COMP_MASK 0x0007 /* DSP1DRC_LO_COMP - [2:0] */
+#define WM8996_DSP1DRC_LO_COMP_SHIFT 0 /* DSP1DRC_LO_COMP - [2:0] */
+#define WM8996_DSP1DRC_LO_COMP_WIDTH 3 /* DSP1DRC_LO_COMP - [2:0] */
+
+/*
+ * R1091 (0x443) - DSP1 DRC (4)
+ */
+#define WM8996_DSP1DRC_KNEE_IP_MASK 0x07E0 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8996_DSP1DRC_KNEE_IP_SHIFT 5 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8996_DSP1DRC_KNEE_IP_WIDTH 6 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8996_DSP1DRC_KNEE_OP_MASK 0x001F /* DSP1DRC_KNEE_OP - [4:0] */
+#define WM8996_DSP1DRC_KNEE_OP_SHIFT 0 /* DSP1DRC_KNEE_OP - [4:0] */
+#define WM8996_DSP1DRC_KNEE_OP_WIDTH 5 /* DSP1DRC_KNEE_OP - [4:0] */
+
+/*
+ * R1092 (0x444) - DSP1 DRC (5)
+ */
+#define WM8996_DSP1DRC_KNEE2_IP_MASK 0x03E0 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8996_DSP1DRC_KNEE2_IP_SHIFT 5 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8996_DSP1DRC_KNEE2_IP_WIDTH 5 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8996_DSP1DRC_KNEE2_OP_MASK 0x001F /* DSP1DRC_KNEE2_OP - [4:0] */
+#define WM8996_DSP1DRC_KNEE2_OP_SHIFT 0 /* DSP1DRC_KNEE2_OP - [4:0] */
+#define WM8996_DSP1DRC_KNEE2_OP_WIDTH 5 /* DSP1DRC_KNEE2_OP - [4:0] */
+
+/*
+ * R1152 (0x480) - DSP1 RX EQ Gains (1)
+ */
+#define WM8996_DSP1RX_EQ_B1_GAIN_MASK 0xF800 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8996_DSP1RX_EQ_B1_GAIN_SHIFT 11 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8996_DSP1RX_EQ_B1_GAIN_WIDTH 5 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8996_DSP1RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8996_DSP1RX_EQ_B2_GAIN_SHIFT 6 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8996_DSP1RX_EQ_B2_GAIN_WIDTH 5 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8996_DSP1RX_EQ_B3_GAIN_MASK 0x003E /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8996_DSP1RX_EQ_B3_GAIN_SHIFT 1 /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8996_DSP1RX_EQ_B3_GAIN_WIDTH 5 /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8996_DSP1RX_EQ_ENA 0x0001 /* DSP1RX_EQ_ENA */
+#define WM8996_DSP1RX_EQ_ENA_MASK 0x0001 /* DSP1RX_EQ_ENA */
+#define WM8996_DSP1RX_EQ_ENA_SHIFT 0 /* DSP1RX_EQ_ENA */
+#define WM8996_DSP1RX_EQ_ENA_WIDTH 1 /* DSP1RX_EQ_ENA */
+
+/*
+ * R1153 (0x481) - DSP1 RX EQ Gains (2)
+ */
+#define WM8996_DSP1RX_EQ_B4_GAIN_MASK 0xF800 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8996_DSP1RX_EQ_B4_GAIN_SHIFT 11 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8996_DSP1RX_EQ_B4_GAIN_WIDTH 5 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8996_DSP1RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+#define WM8996_DSP1RX_EQ_B5_GAIN_SHIFT 6 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+#define WM8996_DSP1RX_EQ_B5_GAIN_WIDTH 5 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+
+/*
+ * R1154 (0x482) - DSP1 RX EQ Band 1 A
+ */
+#define WM8996_DSP1RX_EQ_B1_A_MASK 0xFFFF /* DSP1RX_EQ_B1_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B1_A_SHIFT 0 /* DSP1RX_EQ_B1_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B1_A_WIDTH 16 /* DSP1RX_EQ_B1_A - [15:0] */
+
+/*
+ * R1155 (0x483) - DSP1 RX EQ Band 1 B
+ */
+#define WM8996_DSP1RX_EQ_B1_B_MASK 0xFFFF /* DSP1RX_EQ_B1_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B1_B_SHIFT 0 /* DSP1RX_EQ_B1_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B1_B_WIDTH 16 /* DSP1RX_EQ_B1_B - [15:0] */
+
+/*
+ * R1156 (0x484) - DSP1 RX EQ Band 1 PG
+ */
+#define WM8996_DSP1RX_EQ_B1_PG_MASK 0xFFFF /* DSP1RX_EQ_B1_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B1_PG_SHIFT 0 /* DSP1RX_EQ_B1_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B1_PG_WIDTH 16 /* DSP1RX_EQ_B1_PG - [15:0] */
+
+/*
+ * R1157 (0x485) - DSP1 RX EQ Band 2 A
+ */
+#define WM8996_DSP1RX_EQ_B2_A_MASK 0xFFFF /* DSP1RX_EQ_B2_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_A_SHIFT 0 /* DSP1RX_EQ_B2_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_A_WIDTH 16 /* DSP1RX_EQ_B2_A - [15:0] */
+
+/*
+ * R1158 (0x486) - DSP1 RX EQ Band 2 B
+ */
+#define WM8996_DSP1RX_EQ_B2_B_MASK 0xFFFF /* DSP1RX_EQ_B2_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_B_SHIFT 0 /* DSP1RX_EQ_B2_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_B_WIDTH 16 /* DSP1RX_EQ_B2_B - [15:0] */
+
+/*
+ * R1159 (0x487) - DSP1 RX EQ Band 2 C
+ */
+#define WM8996_DSP1RX_EQ_B2_C_MASK 0xFFFF /* DSP1RX_EQ_B2_C - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_C_SHIFT 0 /* DSP1RX_EQ_B2_C - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_C_WIDTH 16 /* DSP1RX_EQ_B2_C - [15:0] */
+
+/*
+ * R1160 (0x488) - DSP1 RX EQ Band 2 PG
+ */
+#define WM8996_DSP1RX_EQ_B2_PG_MASK 0xFFFF /* DSP1RX_EQ_B2_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_PG_SHIFT 0 /* DSP1RX_EQ_B2_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_PG_WIDTH 16 /* DSP1RX_EQ_B2_PG - [15:0] */
+
+/*
+ * R1161 (0x489) - DSP1 RX EQ Band 3 A
+ */
+#define WM8996_DSP1RX_EQ_B3_A_MASK 0xFFFF /* DSP1RX_EQ_B3_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_A_SHIFT 0 /* DSP1RX_EQ_B3_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_A_WIDTH 16 /* DSP1RX_EQ_B3_A - [15:0] */
+
+/*
+ * R1162 (0x48A) - DSP1 RX EQ Band 3 B
+ */
+#define WM8996_DSP1RX_EQ_B3_B_MASK 0xFFFF /* DSP1RX_EQ_B3_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_B_SHIFT 0 /* DSP1RX_EQ_B3_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_B_WIDTH 16 /* DSP1RX_EQ_B3_B - [15:0] */
+
+/*
+ * R1163 (0x48B) - DSP1 RX EQ Band 3 C
+ */
+#define WM8996_DSP1RX_EQ_B3_C_MASK 0xFFFF /* DSP1RX_EQ_B3_C - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_C_SHIFT 0 /* DSP1RX_EQ_B3_C - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_C_WIDTH 16 /* DSP1RX_EQ_B3_C - [15:0] */
+
+/*
+ * R1164 (0x48C) - DSP1 RX EQ Band 3 PG
+ */
+#define WM8996_DSP1RX_EQ_B3_PG_MASK 0xFFFF /* DSP1RX_EQ_B3_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_PG_SHIFT 0 /* DSP1RX_EQ_B3_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_PG_WIDTH 16 /* DSP1RX_EQ_B3_PG - [15:0] */
+
+/*
+ * R1165 (0x48D) - DSP1 RX EQ Band 4 A
+ */
+#define WM8996_DSP1RX_EQ_B4_A_MASK 0xFFFF /* DSP1RX_EQ_B4_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_A_SHIFT 0 /* DSP1RX_EQ_B4_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_A_WIDTH 16 /* DSP1RX_EQ_B4_A - [15:0] */
+
+/*
+ * R1166 (0x48E) - DSP1 RX EQ Band 4 B
+ */
+#define WM8996_DSP1RX_EQ_B4_B_MASK 0xFFFF /* DSP1RX_EQ_B4_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_B_SHIFT 0 /* DSP1RX_EQ_B4_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_B_WIDTH 16 /* DSP1RX_EQ_B4_B - [15:0] */
+
+/*
+ * R1167 (0x48F) - DSP1 RX EQ Band 4 C
+ */
+#define WM8996_DSP1RX_EQ_B4_C_MASK 0xFFFF /* DSP1RX_EQ_B4_C - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_C_SHIFT 0 /* DSP1RX_EQ_B4_C - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_C_WIDTH 16 /* DSP1RX_EQ_B4_C - [15:0] */
+
+/*
+ * R1168 (0x490) - DSP1 RX EQ Band 4 PG
+ */
+#define WM8996_DSP1RX_EQ_B4_PG_MASK 0xFFFF /* DSP1RX_EQ_B4_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_PG_SHIFT 0 /* DSP1RX_EQ_B4_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_PG_WIDTH 16 /* DSP1RX_EQ_B4_PG - [15:0] */
+
+/*
+ * R1169 (0x491) - DSP1 RX EQ Band 5 A
+ */
+#define WM8996_DSP1RX_EQ_B5_A_MASK 0xFFFF /* DSP1RX_EQ_B5_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B5_A_SHIFT 0 /* DSP1RX_EQ_B5_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B5_A_WIDTH 16 /* DSP1RX_EQ_B5_A - [15:0] */
+
+/*
+ * R1170 (0x492) - DSP1 RX EQ Band 5 B
+ */
+#define WM8996_DSP1RX_EQ_B5_B_MASK 0xFFFF /* DSP1RX_EQ_B5_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B5_B_SHIFT 0 /* DSP1RX_EQ_B5_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B5_B_WIDTH 16 /* DSP1RX_EQ_B5_B - [15:0] */
+
+/*
+ * R1171 (0x493) - DSP1 RX EQ Band 5 PG
+ */
+#define WM8996_DSP1RX_EQ_B5_PG_MASK 0xFFFF /* DSP1RX_EQ_B5_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B5_PG_SHIFT 0 /* DSP1RX_EQ_B5_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B5_PG_WIDTH 16 /* DSP1RX_EQ_B5_PG - [15:0] */
+
+/*
+ * R1280 (0x500) - DSP2 TX Left Volume
+ */
+#define WM8996_DSP2TX_VU 0x0100 /* DSP2TX_VU */
+#define WM8996_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */
+#define WM8996_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */
+#define WM8996_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */
+#define WM8996_DSP2TXL_VOL_MASK 0x00FF /* DSP2TXL_VOL - [7:0] */
+#define WM8996_DSP2TXL_VOL_SHIFT 0 /* DSP2TXL_VOL - [7:0] */
+#define WM8996_DSP2TXL_VOL_WIDTH 8 /* DSP2TXL_VOL - [7:0] */
+
+/*
+ * R1281 (0x501) - DSP2 TX Right Volume
+ */
+#define WM8996_DSP2TX_VU 0x0100 /* DSP2TX_VU */
+#define WM8996_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */
+#define WM8996_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */
+#define WM8996_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */
+#define WM8996_DSP2TXR_VOL_MASK 0x00FF /* DSP2TXR_VOL - [7:0] */
+#define WM8996_DSP2TXR_VOL_SHIFT 0 /* DSP2TXR_VOL - [7:0] */
+#define WM8996_DSP2TXR_VOL_WIDTH 8 /* DSP2TXR_VOL - [7:0] */
+
+/*
+ * R1282 (0x502) - DSP2 RX Left Volume
+ */
+#define WM8996_DSP2RX_VU 0x0100 /* DSP2RX_VU */
+#define WM8996_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */
+#define WM8996_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */
+#define WM8996_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */
+#define WM8996_DSP2RXL_VOL_MASK 0x00FF /* DSP2RXL_VOL - [7:0] */
+#define WM8996_DSP2RXL_VOL_SHIFT 0 /* DSP2RXL_VOL - [7:0] */
+#define WM8996_DSP2RXL_VOL_WIDTH 8 /* DSP2RXL_VOL - [7:0] */
+
+/*
+ * R1283 (0x503) - DSP2 RX Right Volume
+ */
+#define WM8996_DSP2RX_VU 0x0100 /* DSP2RX_VU */
+#define WM8996_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */
+#define WM8996_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */
+#define WM8996_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */
+#define WM8996_DSP2RXR_VOL_MASK 0x00FF /* DSP2RXR_VOL - [7:0] */
+#define WM8996_DSP2RXR_VOL_SHIFT 0 /* DSP2RXR_VOL - [7:0] */
+#define WM8996_DSP2RXR_VOL_WIDTH 8 /* DSP2RXR_VOL - [7:0] */
+
+/*
+ * R1296 (0x510) - DSP2 TX Filters
+ */
+#define WM8996_DSP2TX_NF 0x2000 /* DSP2TX_NF */
+#define WM8996_DSP2TX_NF_MASK 0x2000 /* DSP2TX_NF */
+#define WM8996_DSP2TX_NF_SHIFT 13 /* DSP2TX_NF */
+#define WM8996_DSP2TX_NF_WIDTH 1 /* DSP2TX_NF */
+#define WM8996_DSP2TXL_HPF 0x1000 /* DSP2TXL_HPF */
+#define WM8996_DSP2TXL_HPF_MASK 0x1000 /* DSP2TXL_HPF */
+#define WM8996_DSP2TXL_HPF_SHIFT 12 /* DSP2TXL_HPF */
+#define WM8996_DSP2TXL_HPF_WIDTH 1 /* DSP2TXL_HPF */
+#define WM8996_DSP2TXR_HPF 0x0800 /* DSP2TXR_HPF */
+#define WM8996_DSP2TXR_HPF_MASK 0x0800 /* DSP2TXR_HPF */
+#define WM8996_DSP2TXR_HPF_SHIFT 11 /* DSP2TXR_HPF */
+#define WM8996_DSP2TXR_HPF_WIDTH 1 /* DSP2TXR_HPF */
+#define WM8996_DSP2TX_HPF_MODE_MASK 0x0018 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8996_DSP2TX_HPF_MODE_SHIFT 3 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8996_DSP2TX_HPF_MODE_WIDTH 2 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8996_DSP2TX_HPF_CUT_MASK 0x0007 /* DSP2TX_HPF_CUT - [2:0] */
+#define WM8996_DSP2TX_HPF_CUT_SHIFT 0 /* DSP2TX_HPF_CUT - [2:0] */
+#define WM8996_DSP2TX_HPF_CUT_WIDTH 3 /* DSP2TX_HPF_CUT - [2:0] */
+
+/*
+ * R1312 (0x520) - DSP2 RX Filters (1)
+ */
+#define WM8996_DSP2RX_MUTE 0x0200 /* DSP2RX_MUTE */
+#define WM8996_DSP2RX_MUTE_MASK 0x0200 /* DSP2RX_MUTE */
+#define WM8996_DSP2RX_MUTE_SHIFT 9 /* DSP2RX_MUTE */
+#define WM8996_DSP2RX_MUTE_WIDTH 1 /* DSP2RX_MUTE */
+#define WM8996_DSP2RX_MONO 0x0080 /* DSP2RX_MONO */
+#define WM8996_DSP2RX_MONO_MASK 0x0080 /* DSP2RX_MONO */
+#define WM8996_DSP2RX_MONO_SHIFT 7 /* DSP2RX_MONO */
+#define WM8996_DSP2RX_MONO_WIDTH 1 /* DSP2RX_MONO */
+#define WM8996_DSP2RX_MUTERATE 0x0020 /* DSP2RX_MUTERATE */
+#define WM8996_DSP2RX_MUTERATE_MASK 0x0020 /* DSP2RX_MUTERATE */
+#define WM8996_DSP2RX_MUTERATE_SHIFT 5 /* DSP2RX_MUTERATE */
+#define WM8996_DSP2RX_MUTERATE_WIDTH 1 /* DSP2RX_MUTERATE */
+#define WM8996_DSP2RX_UNMUTE_RAMP 0x0010 /* DSP2RX_UNMUTE_RAMP */
+#define WM8996_DSP2RX_UNMUTE_RAMP_MASK 0x0010 /* DSP2RX_UNMUTE_RAMP */
+#define WM8996_DSP2RX_UNMUTE_RAMP_SHIFT 4 /* DSP2RX_UNMUTE_RAMP */
+#define WM8996_DSP2RX_UNMUTE_RAMP_WIDTH 1 /* DSP2RX_UNMUTE_RAMP */
+
+/*
+ * R1313 (0x521) - DSP2 RX Filters (2)
+ */
+#define WM8996_DSP2RX_3D_GAIN_MASK 0x3E00 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8996_DSP2RX_3D_GAIN_SHIFT 9 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8996_DSP2RX_3D_GAIN_WIDTH 5 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8996_DSP2RX_3D_ENA 0x0100 /* DSP2RX_3D_ENA */
+#define WM8996_DSP2RX_3D_ENA_MASK 0x0100 /* DSP2RX_3D_ENA */
+#define WM8996_DSP2RX_3D_ENA_SHIFT 8 /* DSP2RX_3D_ENA */
+#define WM8996_DSP2RX_3D_ENA_WIDTH 1 /* DSP2RX_3D_ENA */
+
+/*
+ * R1344 (0x540) - DSP2 DRC (1)
+ */
+#define WM8996_DSP2DRC_SIG_DET_RMS_MASK 0xF800 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8996_DSP2DRC_SIG_DET_RMS_SHIFT 11 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8996_DSP2DRC_SIG_DET_RMS_WIDTH 5 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8996_DSP2DRC_SIG_DET_PK_MASK 0x0600 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8996_DSP2DRC_SIG_DET_PK_SHIFT 9 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8996_DSP2DRC_SIG_DET_PK_WIDTH 2 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8996_DSP2DRC_NG_ENA 0x0100 /* DSP2DRC_NG_ENA */
+#define WM8996_DSP2DRC_NG_ENA_MASK 0x0100 /* DSP2DRC_NG_ENA */
+#define WM8996_DSP2DRC_NG_ENA_SHIFT 8 /* DSP2DRC_NG_ENA */
+#define WM8996_DSP2DRC_NG_ENA_WIDTH 1 /* DSP2DRC_NG_ENA */
+#define WM8996_DSP2DRC_SIG_DET_MODE 0x0080 /* DSP2DRC_SIG_DET_MODE */
+#define WM8996_DSP2DRC_SIG_DET_MODE_MASK 0x0080 /* DSP2DRC_SIG_DET_MODE */
+#define WM8996_DSP2DRC_SIG_DET_MODE_SHIFT 7 /* DSP2DRC_SIG_DET_MODE */
+#define WM8996_DSP2DRC_SIG_DET_MODE_WIDTH 1 /* DSP2DRC_SIG_DET_MODE */
+#define WM8996_DSP2DRC_SIG_DET 0x0040 /* DSP2DRC_SIG_DET */
+#define WM8996_DSP2DRC_SIG_DET_MASK 0x0040 /* DSP2DRC_SIG_DET */
+#define WM8996_DSP2DRC_SIG_DET_SHIFT 6 /* DSP2DRC_SIG_DET */
+#define WM8996_DSP2DRC_SIG_DET_WIDTH 1 /* DSP2DRC_SIG_DET */
+#define WM8996_DSP2DRC_KNEE2_OP_ENA 0x0020 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8996_DSP2DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8996_DSP2DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8996_DSP2DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8996_DSP2DRC_QR 0x0010 /* DSP2DRC_QR */
+#define WM8996_DSP2DRC_QR_MASK 0x0010 /* DSP2DRC_QR */
+#define WM8996_DSP2DRC_QR_SHIFT 4 /* DSP2DRC_QR */
+#define WM8996_DSP2DRC_QR_WIDTH 1 /* DSP2DRC_QR */
+#define WM8996_DSP2DRC_ANTICLIP 0x0008 /* DSP2DRC_ANTICLIP */
+#define WM8996_DSP2DRC_ANTICLIP_MASK 0x0008 /* DSP2DRC_ANTICLIP */
+#define WM8996_DSP2DRC_ANTICLIP_SHIFT 3 /* DSP2DRC_ANTICLIP */
+#define WM8996_DSP2DRC_ANTICLIP_WIDTH 1 /* DSP2DRC_ANTICLIP */
+#define WM8996_DSP2RX_DRC_ENA 0x0004 /* DSP2RX_DRC_ENA */
+#define WM8996_DSP2RX_DRC_ENA_MASK 0x0004 /* DSP2RX_DRC_ENA */
+#define WM8996_DSP2RX_DRC_ENA_SHIFT 2 /* DSP2RX_DRC_ENA */
+#define WM8996_DSP2RX_DRC_ENA_WIDTH 1 /* DSP2RX_DRC_ENA */
+#define WM8996_DSP2TXL_DRC_ENA 0x0002 /* DSP2TXL_DRC_ENA */
+#define WM8996_DSP2TXL_DRC_ENA_MASK 0x0002 /* DSP2TXL_DRC_ENA */
+#define WM8996_DSP2TXL_DRC_ENA_SHIFT 1 /* DSP2TXL_DRC_ENA */
+#define WM8996_DSP2TXL_DRC_ENA_WIDTH 1 /* DSP2TXL_DRC_ENA */
+#define WM8996_DSP2TXR_DRC_ENA 0x0001 /* DSP2TXR_DRC_ENA */
+#define WM8996_DSP2TXR_DRC_ENA_MASK 0x0001 /* DSP2TXR_DRC_ENA */
+#define WM8996_DSP2TXR_DRC_ENA_SHIFT 0 /* DSP2TXR_DRC_ENA */
+#define WM8996_DSP2TXR_DRC_ENA_WIDTH 1 /* DSP2TXR_DRC_ENA */
+
+/*
+ * R1345 (0x541) - DSP2 DRC (2)
+ */
+#define WM8996_DSP2DRC_ATK_MASK 0x1E00 /* DSP2DRC_ATK - [12:9] */
+#define WM8996_DSP2DRC_ATK_SHIFT 9 /* DSP2DRC_ATK - [12:9] */
+#define WM8996_DSP2DRC_ATK_WIDTH 4 /* DSP2DRC_ATK - [12:9] */
+#define WM8996_DSP2DRC_DCY_MASK 0x01E0 /* DSP2DRC_DCY - [8:5] */
+#define WM8996_DSP2DRC_DCY_SHIFT 5 /* DSP2DRC_DCY - [8:5] */
+#define WM8996_DSP2DRC_DCY_WIDTH 4 /* DSP2DRC_DCY - [8:5] */
+#define WM8996_DSP2DRC_MINGAIN_MASK 0x001C /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8996_DSP2DRC_MINGAIN_SHIFT 2 /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8996_DSP2DRC_MINGAIN_WIDTH 3 /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8996_DSP2DRC_MAXGAIN_MASK 0x0003 /* DSP2DRC_MAXGAIN - [1:0] */
+#define WM8996_DSP2DRC_MAXGAIN_SHIFT 0 /* DSP2DRC_MAXGAIN - [1:0] */
+#define WM8996_DSP2DRC_MAXGAIN_WIDTH 2 /* DSP2DRC_MAXGAIN - [1:0] */
+
+/*
+ * R1346 (0x542) - DSP2 DRC (3)
+ */
+#define WM8996_DSP2DRC_NG_MINGAIN_MASK 0xF000 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8996_DSP2DRC_NG_MINGAIN_SHIFT 12 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8996_DSP2DRC_NG_MINGAIN_WIDTH 4 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8996_DSP2DRC_NG_EXP_MASK 0x0C00 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8996_DSP2DRC_NG_EXP_SHIFT 10 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8996_DSP2DRC_NG_EXP_WIDTH 2 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8996_DSP2DRC_QR_THR_MASK 0x0300 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8996_DSP2DRC_QR_THR_SHIFT 8 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8996_DSP2DRC_QR_THR_WIDTH 2 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8996_DSP2DRC_QR_DCY_MASK 0x00C0 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8996_DSP2DRC_QR_DCY_SHIFT 6 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8996_DSP2DRC_QR_DCY_WIDTH 2 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8996_DSP2DRC_HI_COMP_MASK 0x0038 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8996_DSP2DRC_HI_COMP_SHIFT 3 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8996_DSP2DRC_HI_COMP_WIDTH 3 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8996_DSP2DRC_LO_COMP_MASK 0x0007 /* DSP2DRC_LO_COMP - [2:0] */
+#define WM8996_DSP2DRC_LO_COMP_SHIFT 0 /* DSP2DRC_LO_COMP - [2:0] */
+#define WM8996_DSP2DRC_LO_COMP_WIDTH 3 /* DSP2DRC_LO_COMP - [2:0] */
+
+/*
+ * R1347 (0x543) - DSP2 DRC (4)
+ */
+#define WM8996_DSP2DRC_KNEE_IP_MASK 0x07E0 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8996_DSP2DRC_KNEE_IP_SHIFT 5 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8996_DSP2DRC_KNEE_IP_WIDTH 6 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8996_DSP2DRC_KNEE_OP_MASK 0x001F /* DSP2DRC_KNEE_OP - [4:0] */
+#define WM8996_DSP2DRC_KNEE_OP_SHIFT 0 /* DSP2DRC_KNEE_OP - [4:0] */
+#define WM8996_DSP2DRC_KNEE_OP_WIDTH 5 /* DSP2DRC_KNEE_OP - [4:0] */
+
+/*
+ * R1348 (0x544) - DSP2 DRC (5)
+ */
+#define WM8996_DSP2DRC_KNEE2_IP_MASK 0x03E0 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8996_DSP2DRC_KNEE2_IP_SHIFT 5 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8996_DSP2DRC_KNEE2_IP_WIDTH 5 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8996_DSP2DRC_KNEE2_OP_MASK 0x001F /* DSP2DRC_KNEE2_OP - [4:0] */
+#define WM8996_DSP2DRC_KNEE2_OP_SHIFT 0 /* DSP2DRC_KNEE2_OP - [4:0] */
+#define WM8996_DSP2DRC_KNEE2_OP_WIDTH 5 /* DSP2DRC_KNEE2_OP - [4:0] */
+
+/*
+ * R1408 (0x580) - DSP2 RX EQ Gains (1)
+ */
+#define WM8996_DSP2RX_EQ_B1_GAIN_MASK 0xF800 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8996_DSP2RX_EQ_B1_GAIN_SHIFT 11 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8996_DSP2RX_EQ_B1_GAIN_WIDTH 5 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8996_DSP2RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8996_DSP2RX_EQ_B2_GAIN_SHIFT 6 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8996_DSP2RX_EQ_B2_GAIN_WIDTH 5 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8996_DSP2RX_EQ_B3_GAIN_MASK 0x003E /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8996_DSP2RX_EQ_B3_GAIN_SHIFT 1 /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8996_DSP2RX_EQ_B3_GAIN_WIDTH 5 /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8996_DSP2RX_EQ_ENA 0x0001 /* DSP2RX_EQ_ENA */
+#define WM8996_DSP2RX_EQ_ENA_MASK 0x0001 /* DSP2RX_EQ_ENA */
+#define WM8996_DSP2RX_EQ_ENA_SHIFT 0 /* DSP2RX_EQ_ENA */
+#define WM8996_DSP2RX_EQ_ENA_WIDTH 1 /* DSP2RX_EQ_ENA */
+
+/*
+ * R1409 (0x581) - DSP2 RX EQ Gains (2)
+ */
+#define WM8996_DSP2RX_EQ_B4_GAIN_MASK 0xF800 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8996_DSP2RX_EQ_B4_GAIN_SHIFT 11 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8996_DSP2RX_EQ_B4_GAIN_WIDTH 5 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8996_DSP2RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+#define WM8996_DSP2RX_EQ_B5_GAIN_SHIFT 6 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+#define WM8996_DSP2RX_EQ_B5_GAIN_WIDTH 5 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+
+/*
+ * R1410 (0x582) - DSP2 RX EQ Band 1 A
+ */
+#define WM8996_DSP2RX_EQ_B1_A_MASK 0xFFFF /* DSP2RX_EQ_B1_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B1_A_SHIFT 0 /* DSP2RX_EQ_B1_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B1_A_WIDTH 16 /* DSP2RX_EQ_B1_A - [15:0] */
+
+/*
+ * R1411 (0x583) - DSP2 RX EQ Band 1 B
+ */
+#define WM8996_DSP2RX_EQ_B1_B_MASK 0xFFFF /* DSP2RX_EQ_B1_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B1_B_SHIFT 0 /* DSP2RX_EQ_B1_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B1_B_WIDTH 16 /* DSP2RX_EQ_B1_B - [15:0] */
+
+/*
+ * R1412 (0x584) - DSP2 RX EQ Band 1 PG
+ */
+#define WM8996_DSP2RX_EQ_B1_PG_MASK 0xFFFF /* DSP2RX_EQ_B1_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B1_PG_SHIFT 0 /* DSP2RX_EQ_B1_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B1_PG_WIDTH 16 /* DSP2RX_EQ_B1_PG - [15:0] */
+
+/*
+ * R1413 (0x585) - DSP2 RX EQ Band 2 A
+ */
+#define WM8996_DSP2RX_EQ_B2_A_MASK 0xFFFF /* DSP2RX_EQ_B2_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_A_SHIFT 0 /* DSP2RX_EQ_B2_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_A_WIDTH 16 /* DSP2RX_EQ_B2_A - [15:0] */
+
+/*
+ * R1414 (0x586) - DSP2 RX EQ Band 2 B
+ */
+#define WM8996_DSP2RX_EQ_B2_B_MASK 0xFFFF /* DSP2RX_EQ_B2_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_B_SHIFT 0 /* DSP2RX_EQ_B2_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_B_WIDTH 16 /* DSP2RX_EQ_B2_B - [15:0] */
+
+/*
+ * R1415 (0x587) - DSP2 RX EQ Band 2 C
+ */
+#define WM8996_DSP2RX_EQ_B2_C_MASK 0xFFFF /* DSP2RX_EQ_B2_C - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_C_SHIFT 0 /* DSP2RX_EQ_B2_C - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_C_WIDTH 16 /* DSP2RX_EQ_B2_C - [15:0] */
+
+/*
+ * R1416 (0x588) - DSP2 RX EQ Band 2 PG
+ */
+#define WM8996_DSP2RX_EQ_B2_PG_MASK 0xFFFF /* DSP2RX_EQ_B2_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_PG_SHIFT 0 /* DSP2RX_EQ_B2_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_PG_WIDTH 16 /* DSP2RX_EQ_B2_PG - [15:0] */
+
+/*
+ * R1417 (0x589) - DSP2 RX EQ Band 3 A
+ */
+#define WM8996_DSP2RX_EQ_B3_A_MASK 0xFFFF /* DSP2RX_EQ_B3_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_A_SHIFT 0 /* DSP2RX_EQ_B3_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_A_WIDTH 16 /* DSP2RX_EQ_B3_A - [15:0] */
+
+/*
+ * R1418 (0x58A) - DSP2 RX EQ Band 3 B
+ */
+#define WM8996_DSP2RX_EQ_B3_B_MASK 0xFFFF /* DSP2RX_EQ_B3_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_B_SHIFT 0 /* DSP2RX_EQ_B3_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_B_WIDTH 16 /* DSP2RX_EQ_B3_B - [15:0] */
+
+/*
+ * R1419 (0x58B) - DSP2 RX EQ Band 3 C
+ */
+#define WM8996_DSP2RX_EQ_B3_C_MASK 0xFFFF /* DSP2RX_EQ_B3_C - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_C_SHIFT 0 /* DSP2RX_EQ_B3_C - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_C_WIDTH 16 /* DSP2RX_EQ_B3_C - [15:0] */
+
+/*
+ * R1420 (0x58C) - DSP2 RX EQ Band 3 PG
+ */
+#define WM8996_DSP2RX_EQ_B3_PG_MASK 0xFFFF /* DSP2RX_EQ_B3_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_PG_SHIFT 0 /* DSP2RX_EQ_B3_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_PG_WIDTH 16 /* DSP2RX_EQ_B3_PG - [15:0] */
+
+/*
+ * R1421 (0x58D) - DSP2 RX EQ Band 4 A
+ */
+#define WM8996_DSP2RX_EQ_B4_A_MASK 0xFFFF /* DSP2RX_EQ_B4_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_A_SHIFT 0 /* DSP2RX_EQ_B4_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_A_WIDTH 16 /* DSP2RX_EQ_B4_A - [15:0] */
+
+/*
+ * R1422 (0x58E) - DSP2 RX EQ Band 4 B
+ */
+#define WM8996_DSP2RX_EQ_B4_B_MASK 0xFFFF /* DSP2RX_EQ_B4_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_B_SHIFT 0 /* DSP2RX_EQ_B4_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_B_WIDTH 16 /* DSP2RX_EQ_B4_B - [15:0] */
+
+/*
+ * R1423 (0x58F) - DSP2 RX EQ Band 4 C
+ */
+#define WM8996_DSP2RX_EQ_B4_C_MASK 0xFFFF /* DSP2RX_EQ_B4_C - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_C_SHIFT 0 /* DSP2RX_EQ_B4_C - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_C_WIDTH 16 /* DSP2RX_EQ_B4_C - [15:0] */
+
+/*
+ * R1424 (0x590) - DSP2 RX EQ Band 4 PG
+ */
+#define WM8996_DSP2RX_EQ_B4_PG_MASK 0xFFFF /* DSP2RX_EQ_B4_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_PG_SHIFT 0 /* DSP2RX_EQ_B4_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_PG_WIDTH 16 /* DSP2RX_EQ_B4_PG - [15:0] */
+
+/*
+ * R1425 (0x591) - DSP2 RX EQ Band 5 A
+ */
+#define WM8996_DSP2RX_EQ_B5_A_MASK 0xFFFF /* DSP2RX_EQ_B5_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B5_A_SHIFT 0 /* DSP2RX_EQ_B5_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B5_A_WIDTH 16 /* DSP2RX_EQ_B5_A - [15:0] */
+
+/*
+ * R1426 (0x592) - DSP2 RX EQ Band 5 B
+ */
+#define WM8996_DSP2RX_EQ_B5_B_MASK 0xFFFF /* DSP2RX_EQ_B5_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B5_B_SHIFT 0 /* DSP2RX_EQ_B5_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B5_B_WIDTH 16 /* DSP2RX_EQ_B5_B - [15:0] */
+
+/*
+ * R1427 (0x593) - DSP2 RX EQ Band 5 PG
+ */
+#define WM8996_DSP2RX_EQ_B5_PG_MASK 0xFFFF /* DSP2RX_EQ_B5_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B5_PG_SHIFT 0 /* DSP2RX_EQ_B5_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B5_PG_WIDTH 16 /* DSP2RX_EQ_B5_PG - [15:0] */
+
+/*
+ * R1536 (0x600) - DAC1 Mixer Volumes
+ */
+#define WM8996_ADCR_DAC1_VOL_MASK 0x03E0 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8996_ADCR_DAC1_VOL_SHIFT 5 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8996_ADCR_DAC1_VOL_WIDTH 5 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8996_ADCL_DAC1_VOL_MASK 0x001F /* ADCL_DAC1_VOL - [4:0] */
+#define WM8996_ADCL_DAC1_VOL_SHIFT 0 /* ADCL_DAC1_VOL - [4:0] */
+#define WM8996_ADCL_DAC1_VOL_WIDTH 5 /* ADCL_DAC1_VOL - [4:0] */
+
+/*
+ * R1537 (0x601) - DAC1 Left Mixer Routing
+ */
+#define WM8996_ADCR_TO_DAC1L 0x0020 /* ADCR_TO_DAC1L */
+#define WM8996_ADCR_TO_DAC1L_MASK 0x0020 /* ADCR_TO_DAC1L */
+#define WM8996_ADCR_TO_DAC1L_SHIFT 5 /* ADCR_TO_DAC1L */
+#define WM8996_ADCR_TO_DAC1L_WIDTH 1 /* ADCR_TO_DAC1L */
+#define WM8996_ADCL_TO_DAC1L 0x0010 /* ADCL_TO_DAC1L */
+#define WM8996_ADCL_TO_DAC1L_MASK 0x0010 /* ADCL_TO_DAC1L */
+#define WM8996_ADCL_TO_DAC1L_SHIFT 4 /* ADCL_TO_DAC1L */
+#define WM8996_ADCL_TO_DAC1L_WIDTH 1 /* ADCL_TO_DAC1L */
+#define WM8996_DSP2RXL_TO_DAC1L 0x0002 /* DSP2RXL_TO_DAC1L */
+#define WM8996_DSP2RXL_TO_DAC1L_MASK 0x0002 /* DSP2RXL_TO_DAC1L */
+#define WM8996_DSP2RXL_TO_DAC1L_SHIFT 1 /* DSP2RXL_TO_DAC1L */
+#define WM8996_DSP2RXL_TO_DAC1L_WIDTH 1 /* DSP2RXL_TO_DAC1L */
+#define WM8996_DSP1RXL_TO_DAC1L 0x0001 /* DSP1RXL_TO_DAC1L */
+#define WM8996_DSP1RXL_TO_DAC1L_MASK 0x0001 /* DSP1RXL_TO_DAC1L */
+#define WM8996_DSP1RXL_TO_DAC1L_SHIFT 0 /* DSP1RXL_TO_DAC1L */
+#define WM8996_DSP1RXL_TO_DAC1L_WIDTH 1 /* DSP1RXL_TO_DAC1L */
+
+/*
+ * R1538 (0x602) - DAC1 Right Mixer Routing
+ */
+#define WM8996_ADCR_TO_DAC1R 0x0020 /* ADCR_TO_DAC1R */
+#define WM8996_ADCR_TO_DAC1R_MASK 0x0020 /* ADCR_TO_DAC1R */
+#define WM8996_ADCR_TO_DAC1R_SHIFT 5 /* ADCR_TO_DAC1R */
+#define WM8996_ADCR_TO_DAC1R_WIDTH 1 /* ADCR_TO_DAC1R */
+#define WM8996_ADCL_TO_DAC1R 0x0010 /* ADCL_TO_DAC1R */
+#define WM8996_ADCL_TO_DAC1R_MASK 0x0010 /* ADCL_TO_DAC1R */
+#define WM8996_ADCL_TO_DAC1R_SHIFT 4 /* ADCL_TO_DAC1R */
+#define WM8996_ADCL_TO_DAC1R_WIDTH 1 /* ADCL_TO_DAC1R */
+#define WM8996_DSP2RXR_TO_DAC1R 0x0002 /* DSP2RXR_TO_DAC1R */
+#define WM8996_DSP2RXR_TO_DAC1R_MASK 0x0002 /* DSP2RXR_TO_DAC1R */
+#define WM8996_DSP2RXR_TO_DAC1R_SHIFT 1 /* DSP2RXR_TO_DAC1R */
+#define WM8996_DSP2RXR_TO_DAC1R_WIDTH 1 /* DSP2RXR_TO_DAC1R */
+#define WM8996_DSP1RXR_TO_DAC1R 0x0001 /* DSP1RXR_TO_DAC1R */
+#define WM8996_DSP1RXR_TO_DAC1R_MASK 0x0001 /* DSP1RXR_TO_DAC1R */
+#define WM8996_DSP1RXR_TO_DAC1R_SHIFT 0 /* DSP1RXR_TO_DAC1R */
+#define WM8996_DSP1RXR_TO_DAC1R_WIDTH 1 /* DSP1RXR_TO_DAC1R */
+
+/*
+ * R1539 (0x603) - DAC2 Mixer Volumes
+ */
+#define WM8996_ADCR_DAC2_VOL_MASK 0x03E0 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8996_ADCR_DAC2_VOL_SHIFT 5 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8996_ADCR_DAC2_VOL_WIDTH 5 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8996_ADCL_DAC2_VOL_MASK 0x001F /* ADCL_DAC2_VOL - [4:0] */
+#define WM8996_ADCL_DAC2_VOL_SHIFT 0 /* ADCL_DAC2_VOL - [4:0] */
+#define WM8996_ADCL_DAC2_VOL_WIDTH 5 /* ADCL_DAC2_VOL - [4:0] */
+
+/*
+ * R1540 (0x604) - DAC2 Left Mixer Routing
+ */
+#define WM8996_ADCR_TO_DAC2L 0x0020 /* ADCR_TO_DAC2L */
+#define WM8996_ADCR_TO_DAC2L_MASK 0x0020 /* ADCR_TO_DAC2L */
+#define WM8996_ADCR_TO_DAC2L_SHIFT 5 /* ADCR_TO_DAC2L */
+#define WM8996_ADCR_TO_DAC2L_WIDTH 1 /* ADCR_TO_DAC2L */
+#define WM8996_ADCL_TO_DAC2L 0x0010 /* ADCL_TO_DAC2L */
+#define WM8996_ADCL_TO_DAC2L_MASK 0x0010 /* ADCL_TO_DAC2L */
+#define WM8996_ADCL_TO_DAC2L_SHIFT 4 /* ADCL_TO_DAC2L */
+#define WM8996_ADCL_TO_DAC2L_WIDTH 1 /* ADCL_TO_DAC2L */
+#define WM8996_DSP2RXL_TO_DAC2L 0x0002 /* DSP2RXL_TO_DAC2L */
+#define WM8996_DSP2RXL_TO_DAC2L_MASK 0x0002 /* DSP2RXL_TO_DAC2L */
+#define WM8996_DSP2RXL_TO_DAC2L_SHIFT 1 /* DSP2RXL_TO_DAC2L */
+#define WM8996_DSP2RXL_TO_DAC2L_WIDTH 1 /* DSP2RXL_TO_DAC2L */
+#define WM8996_DSP1RXL_TO_DAC2L 0x0001 /* DSP1RXL_TO_DAC2L */
+#define WM8996_DSP1RXL_TO_DAC2L_MASK 0x0001 /* DSP1RXL_TO_DAC2L */
+#define WM8996_DSP1RXL_TO_DAC2L_SHIFT 0 /* DSP1RXL_TO_DAC2L */
+#define WM8996_DSP1RXL_TO_DAC2L_WIDTH 1 /* DSP1RXL_TO_DAC2L */
+
+/*
+ * R1541 (0x605) - DAC2 Right Mixer Routing
+ */
+#define WM8996_ADCR_TO_DAC2R 0x0020 /* ADCR_TO_DAC2R */
+#define WM8996_ADCR_TO_DAC2R_MASK 0x0020 /* ADCR_TO_DAC2R */
+#define WM8996_ADCR_TO_DAC2R_SHIFT 5 /* ADCR_TO_DAC2R */
+#define WM8996_ADCR_TO_DAC2R_WIDTH 1 /* ADCR_TO_DAC2R */
+#define WM8996_ADCL_TO_DAC2R 0x0010 /* ADCL_TO_DAC2R */
+#define WM8996_ADCL_TO_DAC2R_MASK 0x0010 /* ADCL_TO_DAC2R */
+#define WM8996_ADCL_TO_DAC2R_SHIFT 4 /* ADCL_TO_DAC2R */
+#define WM8996_ADCL_TO_DAC2R_WIDTH 1 /* ADCL_TO_DAC2R */
+#define WM8996_DSP2RXR_TO_DAC2R 0x0002 /* DSP2RXR_TO_DAC2R */
+#define WM8996_DSP2RXR_TO_DAC2R_MASK 0x0002 /* DSP2RXR_TO_DAC2R */
+#define WM8996_DSP2RXR_TO_DAC2R_SHIFT 1 /* DSP2RXR_TO_DAC2R */
+#define WM8996_DSP2RXR_TO_DAC2R_WIDTH 1 /* DSP2RXR_TO_DAC2R */
+#define WM8996_DSP1RXR_TO_DAC2R 0x0001 /* DSP1RXR_TO_DAC2R */
+#define WM8996_DSP1RXR_TO_DAC2R_MASK 0x0001 /* DSP1RXR_TO_DAC2R */
+#define WM8996_DSP1RXR_TO_DAC2R_SHIFT 0 /* DSP1RXR_TO_DAC2R */
+#define WM8996_DSP1RXR_TO_DAC2R_WIDTH 1 /* DSP1RXR_TO_DAC2R */
+
+/*
+ * R1542 (0x606) - DSP1 TX Left Mixer Routing
+ */
+#define WM8996_ADC1L_TO_DSP1TXL 0x0002 /* ADC1L_TO_DSP1TXL */
+#define WM8996_ADC1L_TO_DSP1TXL_MASK 0x0002 /* ADC1L_TO_DSP1TXL */
+#define WM8996_ADC1L_TO_DSP1TXL_SHIFT 1 /* ADC1L_TO_DSP1TXL */
+#define WM8996_ADC1L_TO_DSP1TXL_WIDTH 1 /* ADC1L_TO_DSP1TXL */
+#define WM8996_DACL_TO_DSP1TXL 0x0001 /* DACL_TO_DSP1TXL */
+#define WM8996_DACL_TO_DSP1TXL_MASK 0x0001 /* DACL_TO_DSP1TXL */
+#define WM8996_DACL_TO_DSP1TXL_SHIFT 0 /* DACL_TO_DSP1TXL */
+#define WM8996_DACL_TO_DSP1TXL_WIDTH 1 /* DACL_TO_DSP1TXL */
+
+/*
+ * R1543 (0x607) - DSP1 TX Right Mixer Routing
+ */
+#define WM8996_ADC1R_TO_DSP1TXR 0x0002 /* ADC1R_TO_DSP1TXR */
+#define WM8996_ADC1R_TO_DSP1TXR_MASK 0x0002 /* ADC1R_TO_DSP1TXR */
+#define WM8996_ADC1R_TO_DSP1TXR_SHIFT 1 /* ADC1R_TO_DSP1TXR */
+#define WM8996_ADC1R_TO_DSP1TXR_WIDTH 1 /* ADC1R_TO_DSP1TXR */
+#define WM8996_DACR_TO_DSP1TXR 0x0001 /* DACR_TO_DSP1TXR */
+#define WM8996_DACR_TO_DSP1TXR_MASK 0x0001 /* DACR_TO_DSP1TXR */
+#define WM8996_DACR_TO_DSP1TXR_SHIFT 0 /* DACR_TO_DSP1TXR */
+#define WM8996_DACR_TO_DSP1TXR_WIDTH 1 /* DACR_TO_DSP1TXR */
+
+/*
+ * R1544 (0x608) - DSP2 TX Left Mixer Routing
+ */
+#define WM8996_ADC2L_TO_DSP2TXL 0x0002 /* ADC2L_TO_DSP2TXL */
+#define WM8996_ADC2L_TO_DSP2TXL_MASK 0x0002 /* ADC2L_TO_DSP2TXL */
+#define WM8996_ADC2L_TO_DSP2TXL_SHIFT 1 /* ADC2L_TO_DSP2TXL */
+#define WM8996_ADC2L_TO_DSP2TXL_WIDTH 1 /* ADC2L_TO_DSP2TXL */
+#define WM8996_DACL_TO_DSP2TXL 0x0001 /* DACL_TO_DSP2TXL */
+#define WM8996_DACL_TO_DSP2TXL_MASK 0x0001 /* DACL_TO_DSP2TXL */
+#define WM8996_DACL_TO_DSP2TXL_SHIFT 0 /* DACL_TO_DSP2TXL */
+#define WM8996_DACL_TO_DSP2TXL_WIDTH 1 /* DACL_TO_DSP2TXL */
+
+/*
+ * R1545 (0x609) - DSP2 TX Right Mixer Routing
+ */
+#define WM8996_ADC2R_TO_DSP2TXR 0x0002 /* ADC2R_TO_DSP2TXR */
+#define WM8996_ADC2R_TO_DSP2TXR_MASK 0x0002 /* ADC2R_TO_DSP2TXR */
+#define WM8996_ADC2R_TO_DSP2TXR_SHIFT 1 /* ADC2R_TO_DSP2TXR */
+#define WM8996_ADC2R_TO_DSP2TXR_WIDTH 1 /* ADC2R_TO_DSP2TXR */
+#define WM8996_DACR_TO_DSP2TXR 0x0001 /* DACR_TO_DSP2TXR */
+#define WM8996_DACR_TO_DSP2TXR_MASK 0x0001 /* DACR_TO_DSP2TXR */
+#define WM8996_DACR_TO_DSP2TXR_SHIFT 0 /* DACR_TO_DSP2TXR */
+#define WM8996_DACR_TO_DSP2TXR_WIDTH 1 /* DACR_TO_DSP2TXR */
+
+/*
+ * R1546 (0x60A) - DSP TX Mixer Select
+ */
+#define WM8996_DAC_TO_DSPTX_SRC 0x0001 /* DAC_TO_DSPTX_SRC */
+#define WM8996_DAC_TO_DSPTX_SRC_MASK 0x0001 /* DAC_TO_DSPTX_SRC */
+#define WM8996_DAC_TO_DSPTX_SRC_SHIFT 0 /* DAC_TO_DSPTX_SRC */
+#define WM8996_DAC_TO_DSPTX_SRC_WIDTH 1 /* DAC_TO_DSPTX_SRC */
+
+/*
+ * R1552 (0x610) - DAC Softmute
+ */
+#define WM8996_DAC_SOFTMUTEMODE 0x0002 /* DAC_SOFTMUTEMODE */
+#define WM8996_DAC_SOFTMUTEMODE_MASK 0x0002 /* DAC_SOFTMUTEMODE */
+#define WM8996_DAC_SOFTMUTEMODE_SHIFT 1 /* DAC_SOFTMUTEMODE */
+#define WM8996_DAC_SOFTMUTEMODE_WIDTH 1 /* DAC_SOFTMUTEMODE */
+#define WM8996_DAC_MUTERATE 0x0001 /* DAC_MUTERATE */
+#define WM8996_DAC_MUTERATE_MASK 0x0001 /* DAC_MUTERATE */
+#define WM8996_DAC_MUTERATE_SHIFT 0 /* DAC_MUTERATE */
+#define WM8996_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
+
+/*
+ * R1568 (0x620) - Oversampling
+ */
+#define WM8996_SPK_OSR128 0x0008 /* SPK_OSR128 */
+#define WM8996_SPK_OSR128_MASK 0x0008 /* SPK_OSR128 */
+#define WM8996_SPK_OSR128_SHIFT 3 /* SPK_OSR128 */
+#define WM8996_SPK_OSR128_WIDTH 1 /* SPK_OSR128 */
+#define WM8996_DMIC_OSR64 0x0004 /* DMIC_OSR64 */
+#define WM8996_DMIC_OSR64_MASK 0x0004 /* DMIC_OSR64 */
+#define WM8996_DMIC_OSR64_SHIFT 2 /* DMIC_OSR64 */
+#define WM8996_DMIC_OSR64_WIDTH 1 /* DMIC_OSR64 */
+#define WM8996_ADC_OSR128 0x0002 /* ADC_OSR128 */
+#define WM8996_ADC_OSR128_MASK 0x0002 /* ADC_OSR128 */
+#define WM8996_ADC_OSR128_SHIFT 1 /* ADC_OSR128 */
+#define WM8996_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */
+#define WM8996_DAC_OSR128 0x0001 /* DAC_OSR128 */
+#define WM8996_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */
+#define WM8996_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */
+#define WM8996_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */
+
+/*
+ * R1569 (0x621) - Sidetone
+ */
+#define WM8996_ST_LPF 0x1000 /* ST_LPF */
+#define WM8996_ST_LPF_MASK 0x1000 /* ST_LPF */
+#define WM8996_ST_LPF_SHIFT 12 /* ST_LPF */
+#define WM8996_ST_LPF_WIDTH 1 /* ST_LPF */
+#define WM8996_ST_HPF_CUT_MASK 0x0380 /* ST_HPF_CUT - [9:7] */
+#define WM8996_ST_HPF_CUT_SHIFT 7 /* ST_HPF_CUT - [9:7] */
+#define WM8996_ST_HPF_CUT_WIDTH 3 /* ST_HPF_CUT - [9:7] */
+#define WM8996_ST_HPF 0x0040 /* ST_HPF */
+#define WM8996_ST_HPF_MASK 0x0040 /* ST_HPF */
+#define WM8996_ST_HPF_SHIFT 6 /* ST_HPF */
+#define WM8996_ST_HPF_WIDTH 1 /* ST_HPF */
+#define WM8996_STR_SEL 0x0002 /* STR_SEL */
+#define WM8996_STR_SEL_MASK 0x0002 /* STR_SEL */
+#define WM8996_STR_SEL_SHIFT 1 /* STR_SEL */
+#define WM8996_STR_SEL_WIDTH 1 /* STR_SEL */
+#define WM8996_STL_SEL 0x0001 /* STL_SEL */
+#define WM8996_STL_SEL_MASK 0x0001 /* STL_SEL */
+#define WM8996_STL_SEL_SHIFT 0 /* STL_SEL */
+#define WM8996_STL_SEL_WIDTH 1 /* STL_SEL */
+
+/*
+ * R1792 (0x700) - GPIO 1
+ */
+#define WM8996_GP1_DIR 0x8000 /* GP1_DIR */
+#define WM8996_GP1_DIR_MASK 0x8000 /* GP1_DIR */
+#define WM8996_GP1_DIR_SHIFT 15 /* GP1_DIR */
+#define WM8996_GP1_DIR_WIDTH 1 /* GP1_DIR */
+#define WM8996_GP1_PU 0x4000 /* GP1_PU */
+#define WM8996_GP1_PU_MASK 0x4000 /* GP1_PU */
+#define WM8996_GP1_PU_SHIFT 14 /* GP1_PU */
+#define WM8996_GP1_PU_WIDTH 1 /* GP1_PU */
+#define WM8996_GP1_PD 0x2000 /* GP1_PD */
+#define WM8996_GP1_PD_MASK 0x2000 /* GP1_PD */
+#define WM8996_GP1_PD_SHIFT 13 /* GP1_PD */
+#define WM8996_GP1_PD_WIDTH 1 /* GP1_PD */
+#define WM8996_GP1_POL 0x0400 /* GP1_POL */
+#define WM8996_GP1_POL_MASK 0x0400 /* GP1_POL */
+#define WM8996_GP1_POL_SHIFT 10 /* GP1_POL */
+#define WM8996_GP1_POL_WIDTH 1 /* GP1_POL */
+#define WM8996_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */
+#define WM8996_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */
+#define WM8996_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */
+#define WM8996_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */
+#define WM8996_GP1_DB 0x0100 /* GP1_DB */
+#define WM8996_GP1_DB_MASK 0x0100 /* GP1_DB */
+#define WM8996_GP1_DB_SHIFT 8 /* GP1_DB */
+#define WM8996_GP1_DB_WIDTH 1 /* GP1_DB */
+#define WM8996_GP1_LVL 0x0040 /* GP1_LVL */
+#define WM8996_GP1_LVL_MASK 0x0040 /* GP1_LVL */
+#define WM8996_GP1_LVL_SHIFT 6 /* GP1_LVL */
+#define WM8996_GP1_LVL_WIDTH 1 /* GP1_LVL */
+#define WM8996_GP1_FN_MASK 0x000F /* GP1_FN - [3:0] */
+#define WM8996_GP1_FN_SHIFT 0 /* GP1_FN - [3:0] */
+#define WM8996_GP1_FN_WIDTH 4 /* GP1_FN - [3:0] */
+
+/*
+ * R1793 (0x701) - GPIO 2
+ */
+#define WM8996_GP2_DIR 0x8000 /* GP2_DIR */
+#define WM8996_GP2_DIR_MASK 0x8000 /* GP2_DIR */
+#define WM8996_GP2_DIR_SHIFT 15 /* GP2_DIR */
+#define WM8996_GP2_DIR_WIDTH 1 /* GP2_DIR */
+#define WM8996_GP2_PU 0x4000 /* GP2_PU */
+#define WM8996_GP2_PU_MASK 0x4000 /* GP2_PU */
+#define WM8996_GP2_PU_SHIFT 14 /* GP2_PU */
+#define WM8996_GP2_PU_WIDTH 1 /* GP2_PU */
+#define WM8996_GP2_PD 0x2000 /* GP2_PD */
+#define WM8996_GP2_PD_MASK 0x2000 /* GP2_PD */
+#define WM8996_GP2_PD_SHIFT 13 /* GP2_PD */
+#define WM8996_GP2_PD_WIDTH 1 /* GP2_PD */
+#define WM8996_GP2_POL 0x0400 /* GP2_POL */
+#define WM8996_GP2_POL_MASK 0x0400 /* GP2_POL */
+#define WM8996_GP2_POL_SHIFT 10 /* GP2_POL */
+#define WM8996_GP2_POL_WIDTH 1 /* GP2_POL */
+#define WM8996_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */
+#define WM8996_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */
+#define WM8996_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */
+#define WM8996_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */
+#define WM8996_GP2_DB 0x0100 /* GP2_DB */
+#define WM8996_GP2_DB_MASK 0x0100 /* GP2_DB */
+#define WM8996_GP2_DB_SHIFT 8 /* GP2_DB */
+#define WM8996_GP2_DB_WIDTH 1 /* GP2_DB */
+#define WM8996_GP2_LVL 0x0040 /* GP2_LVL */
+#define WM8996_GP2_LVL_MASK 0x0040 /* GP2_LVL */
+#define WM8996_GP2_LVL_SHIFT 6 /* GP2_LVL */
+#define WM8996_GP2_LVL_WIDTH 1 /* GP2_LVL */
+#define WM8996_GP2_FN_MASK 0x000F /* GP2_FN - [3:0] */
+#define WM8996_GP2_FN_SHIFT 0 /* GP2_FN - [3:0] */
+#define WM8996_GP2_FN_WIDTH 4 /* GP2_FN - [3:0] */
+
+/*
+ * R1794 (0x702) - GPIO 3
+ */
+#define WM8996_GP3_DIR 0x8000 /* GP3_DIR */
+#define WM8996_GP3_DIR_MASK 0x8000 /* GP3_DIR */
+#define WM8996_GP3_DIR_SHIFT 15 /* GP3_DIR */
+#define WM8996_GP3_DIR_WIDTH 1 /* GP3_DIR */
+#define WM8996_GP3_PU 0x4000 /* GP3_PU */
+#define WM8996_GP3_PU_MASK 0x4000 /* GP3_PU */
+#define WM8996_GP3_PU_SHIFT 14 /* GP3_PU */
+#define WM8996_GP3_PU_WIDTH 1 /* GP3_PU */
+#define WM8996_GP3_PD 0x2000 /* GP3_PD */
+#define WM8996_GP3_PD_MASK 0x2000 /* GP3_PD */
+#define WM8996_GP3_PD_SHIFT 13 /* GP3_PD */
+#define WM8996_GP3_PD_WIDTH 1 /* GP3_PD */
+#define WM8996_GP3_POL 0x0400 /* GP3_POL */
+#define WM8996_GP3_POL_MASK 0x0400 /* GP3_POL */
+#define WM8996_GP3_POL_SHIFT 10 /* GP3_POL */
+#define WM8996_GP3_POL_WIDTH 1 /* GP3_POL */
+#define WM8996_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */
+#define WM8996_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */
+#define WM8996_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */
+#define WM8996_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */
+#define WM8996_GP3_DB 0x0100 /* GP3_DB */
+#define WM8996_GP3_DB_MASK 0x0100 /* GP3_DB */
+#define WM8996_GP3_DB_SHIFT 8 /* GP3_DB */
+#define WM8996_GP3_DB_WIDTH 1 /* GP3_DB */
+#define WM8996_GP3_LVL 0x0040 /* GP3_LVL */
+#define WM8996_GP3_LVL_MASK 0x0040 /* GP3_LVL */
+#define WM8996_GP3_LVL_SHIFT 6 /* GP3_LVL */
+#define WM8996_GP3_LVL_WIDTH 1 /* GP3_LVL */
+#define WM8996_GP3_FN_MASK 0x000F /* GP3_FN - [3:0] */
+#define WM8996_GP3_FN_SHIFT 0 /* GP3_FN - [3:0] */
+#define WM8996_GP3_FN_WIDTH 4 /* GP3_FN - [3:0] */
+
+/*
+ * R1795 (0x703) - GPIO 4
+ */
+#define WM8996_GP4_DIR 0x8000 /* GP4_DIR */
+#define WM8996_GP4_DIR_MASK 0x8000 /* GP4_DIR */
+#define WM8996_GP4_DIR_SHIFT 15 /* GP4_DIR */
+#define WM8996_GP4_DIR_WIDTH 1 /* GP4_DIR */
+#define WM8996_GP4_PU 0x4000 /* GP4_PU */
+#define WM8996_GP4_PU_MASK 0x4000 /* GP4_PU */
+#define WM8996_GP4_PU_SHIFT 14 /* GP4_PU */
+#define WM8996_GP4_PU_WIDTH 1 /* GP4_PU */
+#define WM8996_GP4_PD 0x2000 /* GP4_PD */
+#define WM8996_GP4_PD_MASK 0x2000 /* GP4_PD */
+#define WM8996_GP4_PD_SHIFT 13 /* GP4_PD */
+#define WM8996_GP4_PD_WIDTH 1 /* GP4_PD */
+#define WM8996_GP4_POL 0x0400 /* GP4_POL */
+#define WM8996_GP4_POL_MASK 0x0400 /* GP4_POL */
+#define WM8996_GP4_POL_SHIFT 10 /* GP4_POL */
+#define WM8996_GP4_POL_WIDTH 1 /* GP4_POL */
+#define WM8996_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */
+#define WM8996_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */
+#define WM8996_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */
+#define WM8996_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */
+#define WM8996_GP4_DB 0x0100 /* GP4_DB */
+#define WM8996_GP4_DB_MASK 0x0100 /* GP4_DB */
+#define WM8996_GP4_DB_SHIFT 8 /* GP4_DB */
+#define WM8996_GP4_DB_WIDTH 1 /* GP4_DB */
+#define WM8996_GP4_LVL 0x0040 /* GP4_LVL */
+#define WM8996_GP4_LVL_MASK 0x0040 /* GP4_LVL */
+#define WM8996_GP4_LVL_SHIFT 6 /* GP4_LVL */
+#define WM8996_GP4_LVL_WIDTH 1 /* GP4_LVL */
+#define WM8996_GP4_FN_MASK 0x000F /* GP4_FN - [3:0] */
+#define WM8996_GP4_FN_SHIFT 0 /* GP4_FN - [3:0] */
+#define WM8996_GP4_FN_WIDTH 4 /* GP4_FN - [3:0] */
+
+/*
+ * R1796 (0x704) - GPIO 5
+ */
+#define WM8996_GP5_DIR 0x8000 /* GP5_DIR */
+#define WM8996_GP5_DIR_MASK 0x8000 /* GP5_DIR */
+#define WM8996_GP5_DIR_SHIFT 15 /* GP5_DIR */
+#define WM8996_GP5_DIR_WIDTH 1 /* GP5_DIR */
+#define WM8996_GP5_PU 0x4000 /* GP5_PU */
+#define WM8996_GP5_PU_MASK 0x4000 /* GP5_PU */
+#define WM8996_GP5_PU_SHIFT 14 /* GP5_PU */
+#define WM8996_GP5_PU_WIDTH 1 /* GP5_PU */
+#define WM8996_GP5_PD 0x2000 /* GP5_PD */
+#define WM8996_GP5_PD_MASK 0x2000 /* GP5_PD */
+#define WM8996_GP5_PD_SHIFT 13 /* GP5_PD */
+#define WM8996_GP5_PD_WIDTH 1 /* GP5_PD */
+#define WM8996_GP5_POL 0x0400 /* GP5_POL */
+#define WM8996_GP5_POL_MASK 0x0400 /* GP5_POL */
+#define WM8996_GP5_POL_SHIFT 10 /* GP5_POL */
+#define WM8996_GP5_POL_WIDTH 1 /* GP5_POL */
+#define WM8996_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */
+#define WM8996_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */
+#define WM8996_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */
+#define WM8996_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */
+#define WM8996_GP5_DB 0x0100 /* GP5_DB */
+#define WM8996_GP5_DB_MASK 0x0100 /* GP5_DB */
+#define WM8996_GP5_DB_SHIFT 8 /* GP5_DB */
+#define WM8996_GP5_DB_WIDTH 1 /* GP5_DB */
+#define WM8996_GP5_LVL 0x0040 /* GP5_LVL */
+#define WM8996_GP5_LVL_MASK 0x0040 /* GP5_LVL */
+#define WM8996_GP5_LVL_SHIFT 6 /* GP5_LVL */
+#define WM8996_GP5_LVL_WIDTH 1 /* GP5_LVL */
+#define WM8996_GP5_FN_MASK 0x000F /* GP5_FN - [3:0] */
+#define WM8996_GP5_FN_SHIFT 0 /* GP5_FN - [3:0] */
+#define WM8996_GP5_FN_WIDTH 4 /* GP5_FN - [3:0] */
+
+/*
+ * R1824 (0x720) - Pull Control (1)
+ */
+#define WM8996_DMICDAT2_PD 0x1000 /* DMICDAT2_PD */
+#define WM8996_DMICDAT2_PD_MASK 0x1000 /* DMICDAT2_PD */
+#define WM8996_DMICDAT2_PD_SHIFT 12 /* DMICDAT2_PD */
+#define WM8996_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */
+#define WM8996_DMICDAT1_PD 0x0400 /* DMICDAT1_PD */
+#define WM8996_DMICDAT1_PD_MASK 0x0400 /* DMICDAT1_PD */
+#define WM8996_DMICDAT1_PD_SHIFT 10 /* DMICDAT1_PD */
+#define WM8996_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */
+#define WM8996_MCLK2_PU 0x0200 /* MCLK2_PU */
+#define WM8996_MCLK2_PU_MASK 0x0200 /* MCLK2_PU */
+#define WM8996_MCLK2_PU_SHIFT 9 /* MCLK2_PU */
+#define WM8996_MCLK2_PU_WIDTH 1 /* MCLK2_PU */
+#define WM8996_MCLK2_PD 0x0100 /* MCLK2_PD */
+#define WM8996_MCLK2_PD_MASK 0x0100 /* MCLK2_PD */
+#define WM8996_MCLK2_PD_SHIFT 8 /* MCLK2_PD */
+#define WM8996_MCLK2_PD_WIDTH 1 /* MCLK2_PD */
+#define WM8996_MCLK1_PU 0x0080 /* MCLK1_PU */
+#define WM8996_MCLK1_PU_MASK 0x0080 /* MCLK1_PU */
+#define WM8996_MCLK1_PU_SHIFT 7 /* MCLK1_PU */
+#define WM8996_MCLK1_PU_WIDTH 1 /* MCLK1_PU */
+#define WM8996_MCLK1_PD 0x0040 /* MCLK1_PD */
+#define WM8996_MCLK1_PD_MASK 0x0040 /* MCLK1_PD */
+#define WM8996_MCLK1_PD_SHIFT 6 /* MCLK1_PD */
+#define WM8996_MCLK1_PD_WIDTH 1 /* MCLK1_PD */
+#define WM8996_DACDAT1_PU 0x0020 /* DACDAT1_PU */
+#define WM8996_DACDAT1_PU_MASK 0x0020 /* DACDAT1_PU */
+#define WM8996_DACDAT1_PU_SHIFT 5 /* DACDAT1_PU */
+#define WM8996_DACDAT1_PU_WIDTH 1 /* DACDAT1_PU */
+#define WM8996_DACDAT1_PD 0x0010 /* DACDAT1_PD */
+#define WM8996_DACDAT1_PD_MASK 0x0010 /* DACDAT1_PD */
+#define WM8996_DACDAT1_PD_SHIFT 4 /* DACDAT1_PD */
+#define WM8996_DACDAT1_PD_WIDTH 1 /* DACDAT1_PD */
+#define WM8996_DACLRCLK1_PU 0x0008 /* DACLRCLK1_PU */
+#define WM8996_DACLRCLK1_PU_MASK 0x0008 /* DACLRCLK1_PU */
+#define WM8996_DACLRCLK1_PU_SHIFT 3 /* DACLRCLK1_PU */
+#define WM8996_DACLRCLK1_PU_WIDTH 1 /* DACLRCLK1_PU */
+#define WM8996_DACLRCLK1_PD 0x0004 /* DACLRCLK1_PD */
+#define WM8996_DACLRCLK1_PD_MASK 0x0004 /* DACLRCLK1_PD */
+#define WM8996_DACLRCLK1_PD_SHIFT 2 /* DACLRCLK1_PD */
+#define WM8996_DACLRCLK1_PD_WIDTH 1 /* DACLRCLK1_PD */
+#define WM8996_BCLK1_PU 0x0002 /* BCLK1_PU */
+#define WM8996_BCLK1_PU_MASK 0x0002 /* BCLK1_PU */
+#define WM8996_BCLK1_PU_SHIFT 1 /* BCLK1_PU */
+#define WM8996_BCLK1_PU_WIDTH 1 /* BCLK1_PU */
+#define WM8996_BCLK1_PD 0x0001 /* BCLK1_PD */
+#define WM8996_BCLK1_PD_MASK 0x0001 /* BCLK1_PD */
+#define WM8996_BCLK1_PD_SHIFT 0 /* BCLK1_PD */
+#define WM8996_BCLK1_PD_WIDTH 1 /* BCLK1_PD */
+
+/*
+ * R1825 (0x721) - Pull Control (2)
+ */
+#define WM8996_LDO1ENA_PD 0x0100 /* LDO1ENA_PD */
+#define WM8996_LDO1ENA_PD_MASK 0x0100 /* LDO1ENA_PD */
+#define WM8996_LDO1ENA_PD_SHIFT 8 /* LDO1ENA_PD */
+#define WM8996_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */
+#define WM8996_ADDR_PD 0x0040 /* ADDR_PD */
+#define WM8996_ADDR_PD_MASK 0x0040 /* ADDR_PD */
+#define WM8996_ADDR_PD_SHIFT 6 /* ADDR_PD */
+#define WM8996_ADDR_PD_WIDTH 1 /* ADDR_PD */
+#define WM8996_DACDAT2_PU 0x0020 /* DACDAT2_PU */
+#define WM8996_DACDAT2_PU_MASK 0x0020 /* DACDAT2_PU */
+#define WM8996_DACDAT2_PU_SHIFT 5 /* DACDAT2_PU */
+#define WM8996_DACDAT2_PU_WIDTH 1 /* DACDAT2_PU */
+#define WM8996_DACDAT2_PD 0x0010 /* DACDAT2_PD */
+#define WM8996_DACDAT2_PD_MASK 0x0010 /* DACDAT2_PD */
+#define WM8996_DACDAT2_PD_SHIFT 4 /* DACDAT2_PD */
+#define WM8996_DACDAT2_PD_WIDTH 1 /* DACDAT2_PD */
+#define WM8996_DACLRCLK2_PU 0x0008 /* DACLRCLK2_PU */
+#define WM8996_DACLRCLK2_PU_MASK 0x0008 /* DACLRCLK2_PU */
+#define WM8996_DACLRCLK2_PU_SHIFT 3 /* DACLRCLK2_PU */
+#define WM8996_DACLRCLK2_PU_WIDTH 1 /* DACLRCLK2_PU */
+#define WM8996_DACLRCLK2_PD 0x0004 /* DACLRCLK2_PD */
+#define WM8996_DACLRCLK2_PD_MASK 0x0004 /* DACLRCLK2_PD */
+#define WM8996_DACLRCLK2_PD_SHIFT 2 /* DACLRCLK2_PD */
+#define WM8996_DACLRCLK2_PD_WIDTH 1 /* DACLRCLK2_PD */
+#define WM8996_BCLK2_PU 0x0002 /* BCLK2_PU */
+#define WM8996_BCLK2_PU_MASK 0x0002 /* BCLK2_PU */
+#define WM8996_BCLK2_PU_SHIFT 1 /* BCLK2_PU */
+#define WM8996_BCLK2_PU_WIDTH 1 /* BCLK2_PU */
+#define WM8996_BCLK2_PD 0x0001 /* BCLK2_PD */
+#define WM8996_BCLK2_PD_MASK 0x0001 /* BCLK2_PD */
+#define WM8996_BCLK2_PD_SHIFT 0 /* BCLK2_PD */
+#define WM8996_BCLK2_PD_WIDTH 1 /* BCLK2_PD */
+
+/*
+ * R1840 (0x730) - Interrupt Status 1
+ */
+#define WM8996_GP5_EINT 0x0010 /* GP5_EINT */
+#define WM8996_GP5_EINT_MASK 0x0010 /* GP5_EINT */
+#define WM8996_GP5_EINT_SHIFT 4 /* GP5_EINT */
+#define WM8996_GP5_EINT_WIDTH 1 /* GP5_EINT */
+#define WM8996_GP4_EINT 0x0008 /* GP4_EINT */
+#define WM8996_GP4_EINT_MASK 0x0008 /* GP4_EINT */
+#define WM8996_GP4_EINT_SHIFT 3 /* GP4_EINT */
+#define WM8996_GP4_EINT_WIDTH 1 /* GP4_EINT */
+#define WM8996_GP3_EINT 0x0004 /* GP3_EINT */
+#define WM8996_GP3_EINT_MASK 0x0004 /* GP3_EINT */
+#define WM8996_GP3_EINT_SHIFT 2 /* GP3_EINT */
+#define WM8996_GP3_EINT_WIDTH 1 /* GP3_EINT */
+#define WM8996_GP2_EINT 0x0002 /* GP2_EINT */
+#define WM8996_GP2_EINT_MASK 0x0002 /* GP2_EINT */
+#define WM8996_GP2_EINT_SHIFT 1 /* GP2_EINT */
+#define WM8996_GP2_EINT_WIDTH 1 /* GP2_EINT */
+#define WM8996_GP1_EINT 0x0001 /* GP1_EINT */
+#define WM8996_GP1_EINT_MASK 0x0001 /* GP1_EINT */
+#define WM8996_GP1_EINT_SHIFT 0 /* GP1_EINT */
+#define WM8996_GP1_EINT_WIDTH 1 /* GP1_EINT */
+
+/*
+ * R1841 (0x731) - Interrupt Status 2
+ */
+#define WM8996_DCS_DONE_23_EINT 0x1000 /* DCS_DONE_23_EINT */
+#define WM8996_DCS_DONE_23_EINT_MASK 0x1000 /* DCS_DONE_23_EINT */
+#define WM8996_DCS_DONE_23_EINT_SHIFT 12 /* DCS_DONE_23_EINT */
+#define WM8996_DCS_DONE_23_EINT_WIDTH 1 /* DCS_DONE_23_EINT */
+#define WM8996_DCS_DONE_01_EINT 0x0800 /* DCS_DONE_01_EINT */
+#define WM8996_DCS_DONE_01_EINT_MASK 0x0800 /* DCS_DONE_01_EINT */
+#define WM8996_DCS_DONE_01_EINT_SHIFT 11 /* DCS_DONE_01_EINT */
+#define WM8996_DCS_DONE_01_EINT_WIDTH 1 /* DCS_DONE_01_EINT */
+#define WM8996_WSEQ_DONE_EINT 0x0400 /* WSEQ_DONE_EINT */
+#define WM8996_WSEQ_DONE_EINT_MASK 0x0400 /* WSEQ_DONE_EINT */
+#define WM8996_WSEQ_DONE_EINT_SHIFT 10 /* WSEQ_DONE_EINT */
+#define WM8996_WSEQ_DONE_EINT_WIDTH 1 /* WSEQ_DONE_EINT */
+#define WM8996_FIFOS_ERR_EINT 0x0200 /* FIFOS_ERR_EINT */
+#define WM8996_FIFOS_ERR_EINT_MASK 0x0200 /* FIFOS_ERR_EINT */
+#define WM8996_FIFOS_ERR_EINT_SHIFT 9 /* FIFOS_ERR_EINT */
+#define WM8996_FIFOS_ERR_EINT_WIDTH 1 /* FIFOS_ERR_EINT */
+#define WM8996_DSP2DRC_SIG_DET_EINT 0x0080 /* DSP2DRC_SIG_DET_EINT */
+#define WM8996_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* DSP2DRC_SIG_DET_EINT */
+#define WM8996_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* DSP2DRC_SIG_DET_EINT */
+#define WM8996_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* DSP2DRC_SIG_DET_EINT */
+#define WM8996_DSP1DRC_SIG_DET_EINT 0x0040 /* DSP1DRC_SIG_DET_EINT */
+#define WM8996_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* DSP1DRC_SIG_DET_EINT */
+#define WM8996_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* DSP1DRC_SIG_DET_EINT */
+#define WM8996_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* DSP1DRC_SIG_DET_EINT */
+#define WM8996_FLL_SW_CLK_DONE_EINT 0x0008 /* FLL_SW_CLK_DONE_EINT */
+#define WM8996_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* FLL_SW_CLK_DONE_EINT */
+#define WM8996_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* FLL_SW_CLK_DONE_EINT */
+#define WM8996_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* FLL_SW_CLK_DONE_EINT */
+#define WM8996_FLL_LOCK_EINT 0x0004 /* FLL_LOCK_EINT */
+#define WM8996_FLL_LOCK_EINT_MASK 0x0004 /* FLL_LOCK_EINT */
+#define WM8996_FLL_LOCK_EINT_SHIFT 2 /* FLL_LOCK_EINT */
+#define WM8996_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */
+#define WM8996_HP_DONE_EINT 0x0002 /* HP_DONE_EINT */
+#define WM8996_HP_DONE_EINT_MASK 0x0002 /* HP_DONE_EINT */
+#define WM8996_HP_DONE_EINT_SHIFT 1 /* HP_DONE_EINT */
+#define WM8996_HP_DONE_EINT_WIDTH 1 /* HP_DONE_EINT */
+#define WM8996_MICD_EINT 0x0001 /* MICD_EINT */
+#define WM8996_MICD_EINT_MASK 0x0001 /* MICD_EINT */
+#define WM8996_MICD_EINT_SHIFT 0 /* MICD_EINT */
+#define WM8996_MICD_EINT_WIDTH 1 /* MICD_EINT */
+
+/*
+ * R1842 (0x732) - Interrupt Raw Status 2
+ */
+#define WM8996_DCS_DONE_23_STS 0x1000 /* DCS_DONE_23_STS */
+#define WM8996_DCS_DONE_23_STS_MASK 0x1000 /* DCS_DONE_23_STS */
+#define WM8996_DCS_DONE_23_STS_SHIFT 12 /* DCS_DONE_23_STS */
+#define WM8996_DCS_DONE_23_STS_WIDTH 1 /* DCS_DONE_23_STS */
+#define WM8996_DCS_DONE_01_STS 0x0800 /* DCS_DONE_01_STS */
+#define WM8996_DCS_DONE_01_STS_MASK 0x0800 /* DCS_DONE_01_STS */
+#define WM8996_DCS_DONE_01_STS_SHIFT 11 /* DCS_DONE_01_STS */
+#define WM8996_DCS_DONE_01_STS_WIDTH 1 /* DCS_DONE_01_STS */
+#define WM8996_WSEQ_DONE_STS 0x0400 /* WSEQ_DONE_STS */
+#define WM8996_WSEQ_DONE_STS_MASK 0x0400 /* WSEQ_DONE_STS */
+#define WM8996_WSEQ_DONE_STS_SHIFT 10 /* WSEQ_DONE_STS */
+#define WM8996_WSEQ_DONE_STS_WIDTH 1 /* WSEQ_DONE_STS */
+#define WM8996_FIFOS_ERR_STS 0x0200 /* FIFOS_ERR_STS */
+#define WM8996_FIFOS_ERR_STS_MASK 0x0200 /* FIFOS_ERR_STS */
+#define WM8996_FIFOS_ERR_STS_SHIFT 9 /* FIFOS_ERR_STS */
+#define WM8996_FIFOS_ERR_STS_WIDTH 1 /* FIFOS_ERR_STS */
+#define WM8996_DSP2DRC_SIG_DET_STS 0x0080 /* DSP2DRC_SIG_DET_STS */
+#define WM8996_DSP2DRC_SIG_DET_STS_MASK 0x0080 /* DSP2DRC_SIG_DET_STS */
+#define WM8996_DSP2DRC_SIG_DET_STS_SHIFT 7 /* DSP2DRC_SIG_DET_STS */
+#define WM8996_DSP2DRC_SIG_DET_STS_WIDTH 1 /* DSP2DRC_SIG_DET_STS */
+#define WM8996_DSP1DRC_SIG_DET_STS 0x0040 /* DSP1DRC_SIG_DET_STS */
+#define WM8996_DSP1DRC_SIG_DET_STS_MASK 0x0040 /* DSP1DRC_SIG_DET_STS */
+#define WM8996_DSP1DRC_SIG_DET_STS_SHIFT 6 /* DSP1DRC_SIG_DET_STS */
+#define WM8996_DSP1DRC_SIG_DET_STS_WIDTH 1 /* DSP1DRC_SIG_DET_STS */
+#define WM8996_FLL_LOCK_STS 0x0004 /* FLL_LOCK_STS */
+#define WM8996_FLL_LOCK_STS_MASK 0x0004 /* FLL_LOCK_STS */
+#define WM8996_FLL_LOCK_STS_SHIFT 2 /* FLL_LOCK_STS */
+#define WM8996_FLL_LOCK_STS_WIDTH 1 /* FLL_LOCK_STS */
+
+/*
+ * R1848 (0x738) - Interrupt Status 1 Mask
+ */
+#define WM8996_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */
+#define WM8996_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */
+#define WM8996_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */
+#define WM8996_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */
+#define WM8996_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */
+#define WM8996_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */
+#define WM8996_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */
+#define WM8996_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */
+#define WM8996_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */
+#define WM8996_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */
+#define WM8996_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */
+#define WM8996_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */
+#define WM8996_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */
+#define WM8996_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */
+#define WM8996_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */
+#define WM8996_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */
+#define WM8996_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */
+#define WM8996_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */
+#define WM8996_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */
+#define WM8996_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */
+
+/*
+ * R1849 (0x739) - Interrupt Status 2 Mask
+ */
+#define WM8996_IM_DCS_DONE_23_EINT 0x1000 /* IM_DCS_DONE_23_EINT */
+#define WM8996_IM_DCS_DONE_23_EINT_MASK 0x1000 /* IM_DCS_DONE_23_EINT */
+#define WM8996_IM_DCS_DONE_23_EINT_SHIFT 12 /* IM_DCS_DONE_23_EINT */
+#define WM8996_IM_DCS_DONE_23_EINT_WIDTH 1 /* IM_DCS_DONE_23_EINT */
+#define WM8996_IM_DCS_DONE_01_EINT 0x0800 /* IM_DCS_DONE_01_EINT */
+#define WM8996_IM_DCS_DONE_01_EINT_MASK 0x0800 /* IM_DCS_DONE_01_EINT */
+#define WM8996_IM_DCS_DONE_01_EINT_SHIFT 11 /* IM_DCS_DONE_01_EINT */
+#define WM8996_IM_DCS_DONE_01_EINT_WIDTH 1 /* IM_DCS_DONE_01_EINT */
+#define WM8996_IM_WSEQ_DONE_EINT 0x0400 /* IM_WSEQ_DONE_EINT */
+#define WM8996_IM_WSEQ_DONE_EINT_MASK 0x0400 /* IM_WSEQ_DONE_EINT */
+#define WM8996_IM_WSEQ_DONE_EINT_SHIFT 10 /* IM_WSEQ_DONE_EINT */
+#define WM8996_IM_WSEQ_DONE_EINT_WIDTH 1 /* IM_WSEQ_DONE_EINT */
+#define WM8996_IM_FIFOS_ERR_EINT 0x0200 /* IM_FIFOS_ERR_EINT */
+#define WM8996_IM_FIFOS_ERR_EINT_MASK 0x0200 /* IM_FIFOS_ERR_EINT */
+#define WM8996_IM_FIFOS_ERR_EINT_SHIFT 9 /* IM_FIFOS_ERR_EINT */
+#define WM8996_IM_FIFOS_ERR_EINT_WIDTH 1 /* IM_FIFOS_ERR_EINT */
+#define WM8996_IM_DSP2DRC_SIG_DET_EINT 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP1DRC_SIG_DET_EINT 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8996_IM_FLL_SW_CLK_DONE_EINT 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8996_IM_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8996_IM_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8996_IM_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8996_IM_FLL_LOCK_EINT 0x0004 /* IM_FLL_LOCK_EINT */
+#define WM8996_IM_FLL_LOCK_EINT_MASK 0x0004 /* IM_FLL_LOCK_EINT */
+#define WM8996_IM_FLL_LOCK_EINT_SHIFT 2 /* IM_FLL_LOCK_EINT */
+#define WM8996_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */
+#define WM8996_IM_HP_DONE_EINT 0x0002 /* IM_HP_DONE_EINT */
+#define WM8996_IM_HP_DONE_EINT_MASK 0x0002 /* IM_HP_DONE_EINT */
+#define WM8996_IM_HP_DONE_EINT_SHIFT 1 /* IM_HP_DONE_EINT */
+#define WM8996_IM_HP_DONE_EINT_WIDTH 1 /* IM_HP_DONE_EINT */
+#define WM8996_IM_MICD_EINT 0x0001 /* IM_MICD_EINT */
+#define WM8996_IM_MICD_EINT_MASK 0x0001 /* IM_MICD_EINT */
+#define WM8996_IM_MICD_EINT_SHIFT 0 /* IM_MICD_EINT */
+#define WM8996_IM_MICD_EINT_WIDTH 1 /* IM_MICD_EINT */
+
+/*
+ * R1856 (0x740) - Interrupt Control
+ */
+#define WM8996_IM_IRQ 0x0001 /* IM_IRQ */
+#define WM8996_IM_IRQ_MASK 0x0001 /* IM_IRQ */
+#define WM8996_IM_IRQ_SHIFT 0 /* IM_IRQ */
+#define WM8996_IM_IRQ_WIDTH 1 /* IM_IRQ */
+
+/*
+ * R2048 (0x800) - Left PDM Speaker
+ */
+#define WM8996_SPKL_ENA 0x0010 /* SPKL_ENA */
+#define WM8996_SPKL_ENA_MASK 0x0010 /* SPKL_ENA */
+#define WM8996_SPKL_ENA_SHIFT 4 /* SPKL_ENA */
+#define WM8996_SPKL_ENA_WIDTH 1 /* SPKL_ENA */
+#define WM8996_SPKL_MUTE 0x0008 /* SPKL_MUTE */
+#define WM8996_SPKL_MUTE_MASK 0x0008 /* SPKL_MUTE */
+#define WM8996_SPKL_MUTE_SHIFT 3 /* SPKL_MUTE */
+#define WM8996_SPKL_MUTE_WIDTH 1 /* SPKL_MUTE */
+#define WM8996_SPKL_MUTE_ZC 0x0004 /* SPKL_MUTE_ZC */
+#define WM8996_SPKL_MUTE_ZC_MASK 0x0004 /* SPKL_MUTE_ZC */
+#define WM8996_SPKL_MUTE_ZC_SHIFT 2 /* SPKL_MUTE_ZC */
+#define WM8996_SPKL_MUTE_ZC_WIDTH 1 /* SPKL_MUTE_ZC */
+#define WM8996_SPKL_SRC_MASK 0x0003 /* SPKL_SRC - [1:0] */
+#define WM8996_SPKL_SRC_SHIFT 0 /* SPKL_SRC - [1:0] */
+#define WM8996_SPKL_SRC_WIDTH 2 /* SPKL_SRC - [1:0] */
+
+/*
+ * R2049 (0x801) - Right PDM Speaker
+ */
+#define WM8996_SPKR_ENA 0x0010 /* SPKR_ENA */
+#define WM8996_SPKR_ENA_MASK 0x0010 /* SPKR_ENA */
+#define WM8996_SPKR_ENA_SHIFT 4 /* SPKR_ENA */
+#define WM8996_SPKR_ENA_WIDTH 1 /* SPKR_ENA */
+#define WM8996_SPKR_MUTE 0x0008 /* SPKR_MUTE */
+#define WM8996_SPKR_MUTE_MASK 0x0008 /* SPKR_MUTE */
+#define WM8996_SPKR_MUTE_SHIFT 3 /* SPKR_MUTE */
+#define WM8996_SPKR_MUTE_WIDTH 1 /* SPKR_MUTE */
+#define WM8996_SPKR_MUTE_ZC 0x0004 /* SPKR_MUTE_ZC */
+#define WM8996_SPKR_MUTE_ZC_MASK 0x0004 /* SPKR_MUTE_ZC */
+#define WM8996_SPKR_MUTE_ZC_SHIFT 2 /* SPKR_MUTE_ZC */
+#define WM8996_SPKR_MUTE_ZC_WIDTH 1 /* SPKR_MUTE_ZC */
+#define WM8996_SPKR_SRC_MASK 0x0003 /* SPKR_SRC - [1:0] */
+#define WM8996_SPKR_SRC_SHIFT 0 /* SPKR_SRC - [1:0] */
+#define WM8996_SPKR_SRC_WIDTH 2 /* SPKR_SRC - [1:0] */
+
+/*
+ * R2050 (0x802) - PDM Speaker Mute Sequence
+ */
+#define WM8996_SPK_MUTE_ENDIAN 0x0100 /* SPK_MUTE_ENDIAN */
+#define WM8996_SPK_MUTE_ENDIAN_MASK 0x0100 /* SPK_MUTE_ENDIAN */
+#define WM8996_SPK_MUTE_ENDIAN_SHIFT 8 /* SPK_MUTE_ENDIAN */
+#define WM8996_SPK_MUTE_ENDIAN_WIDTH 1 /* SPK_MUTE_ENDIAN */
+#define WM8996_SPK_MUTE_SEQ1_MASK 0x00FF /* SPK_MUTE_SEQ1 - [7:0] */
+#define WM8996_SPK_MUTE_SEQ1_SHIFT 0 /* SPK_MUTE_SEQ1 - [7:0] */
+#define WM8996_SPK_MUTE_SEQ1_WIDTH 8 /* SPK_MUTE_SEQ1 - [7:0] */
+
+/*
+ * R2051 (0x803) - PDM Speaker Volume
+ */
+#define WM8996_SPKR_VOL_MASK 0x00F0 /* SPKR_VOL - [7:4] */
+#define WM8996_SPKR_VOL_SHIFT 4 /* SPKR_VOL - [7:4] */
+#define WM8996_SPKR_VOL_WIDTH 4 /* SPKR_VOL - [7:4] */
+#define WM8996_SPKL_VOL_MASK 0x000F /* SPKL_VOL - [3:0] */
+#define WM8996_SPKL_VOL_SHIFT 0 /* SPKL_VOL - [3:0] */
+#define WM8996_SPKL_VOL_WIDTH 4 /* SPKL_VOL - [3:0] */
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm8997.c b/kernel/sound/soc/codecs/wm8997.c
new file mode 100644
index 000000000..a4d117706
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8997.c
@@ -0,0 +1,1181 @@
+/*
+ * wm8997.c -- WM8997 ALSA SoC Audio driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Charles Keepax <ckeepax@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/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 "wm8997.h"
+
+struct wm8997_priv {
+ struct arizona_priv core;
+ struct arizona_fll fll[2];
+};
+
+static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
+static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+
+static const struct reg_default wm8997_sysclk_reva_patch[] = {
+ { 0x301D, 0x7B15 },
+ { 0x301B, 0x0050 },
+ { 0x305D, 0x7B17 },
+ { 0x305B, 0x0050 },
+ { 0x3001, 0x08FE },
+ { 0x3003, 0x00F4 },
+ { 0x3041, 0x08FF },
+ { 0x3043, 0x0005 },
+ { 0x3020, 0x0225 },
+ { 0x3021, 0x0A00 },
+ { 0x3022, 0xE24D },
+ { 0x3023, 0x0800 },
+ { 0x3024, 0xE24D },
+ { 0x3025, 0xF000 },
+ { 0x3060, 0x0226 },
+ { 0x3061, 0x0A00 },
+ { 0x3062, 0xE252 },
+ { 0x3063, 0x0800 },
+ { 0x3064, 0xE252 },
+ { 0x3065, 0xF000 },
+ { 0x3116, 0x022B },
+ { 0x3117, 0xFA00 },
+ { 0x3110, 0x246C },
+ { 0x3111, 0x0A03 },
+ { 0x3112, 0x246E },
+ { 0x3113, 0x0A03 },
+ { 0x3114, 0x2470 },
+ { 0x3115, 0x0A03 },
+ { 0x3126, 0x246C },
+ { 0x3127, 0x0A02 },
+ { 0x3128, 0x246E },
+ { 0x3129, 0x0A02 },
+ { 0x312A, 0x2470 },
+ { 0x312B, 0xFA02 },
+ { 0x3125, 0x0800 },
+};
+
+static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ struct regmap *regmap = arizona->regmap;
+ const struct reg_default *patch = NULL;
+ int i, patch_size;
+
+ switch (arizona->rev) {
+ case 0:
+ patch = wm8997_sysclk_reva_patch;
+ patch_size = ARRAY_SIZE(wm8997_sysclk_reva_patch);
+ break;
+ default:
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (patch)
+ for (i = 0; i < patch_size; i++)
+ regmap_write_async(regmap, patch[i].reg,
+ patch[i].def);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const char *wm8997_osr_text[] = {
+ "Low power", "Normal", "High performance",
+};
+
+static const unsigned int wm8997_osr_val[] = {
+ 0x0, 0x3, 0x5,
+};
+
+static const struct soc_enum wm8997_hpout_osr[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L,
+ ARIZONA_OUT1_OSR_SHIFT, 0x7,
+ ARRAY_SIZE(wm8997_osr_text),
+ wm8997_osr_text, wm8997_osr_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L,
+ ARIZONA_OUT3_OSR_SHIFT, 0x7,
+ ARRAY_SIZE(wm8997_osr_text),
+ wm8997_osr_text, wm8997_osr_val),
+};
+
+#define WM8997_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
+static const struct snd_kcontrol_new wm8997_snd_controls[] = {
+SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2_OSR_SHIFT, 1, 0),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+ ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+ ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+
+SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
+
+ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
+SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0),
+SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
+SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0),
+SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
+SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0),
+SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
+SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0),
+SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5,
+ ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA),
+
+ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
+
+SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
+
+SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+
+SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+
+ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv),
+
+ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUT", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,
+ ARIZONA_OUT4_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
+ ARIZONA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_OUT4L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+
+SOC_ENUM("HPOUT1 OSR", wm8997_hpout_osr[0]),
+SOC_ENUM("EPOUT OSR", wm8997_hpout_osr[1]),
+
+SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
+
+SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
+ ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+WM8997_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+WM8997_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+WM8997_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L),
+WM8997_NG_SRC("SPKOUT", ARIZONA_NOISE_GATE_SELECT_4L),
+WM8997_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L),
+WM8997_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R),
+
+ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
+};
+
+ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUT, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+
+static const char *wm8997_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "EPOUT", "SPKOUT", "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int wm8997_aec_loopback_values[] = {
+ 0, 1, 4, 6, 8, 9,
+};
+
+static const struct soc_enum wm8997_aec_loopback =
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(wm8997_aec_loopback_texts),
+ wm8997_aec_loopback_texts,
+ wm8997_aec_loopback_values);
+
+static const struct snd_kcontrol_new wm8997_aec_loopback_mux =
+ SOC_DAPM_ENUM("AEC Loopback", wm8997_aec_loopback);
+
+static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
+ 0, wm8997_sysclk_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
+ ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
+ ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
+ ARIZONA_MICB2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
+ ARIZONA_MICB3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1,
+ ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0,
+ 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, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0,
+ 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, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &wm8997_aec_loopback_mux),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"),
+ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"),
+ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+
+ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+ARIZONA_MIXER_WIDGETS(Mic, "Mic"),
+ARIZONA_MIXER_WIDGETS(Noise, "Noise"),
+
+ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"),
+ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"),
+ARIZONA_MIXER_WIDGETS(SPKOUT, "SPKOUT"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+
+ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("EPOUTN"),
+SND_SOC_DAPM_OUTPUT("EPOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC", "AEC Loopback" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2L PGA" }, \
+ { name, "IN2R", "IN2R PGA" }, \
+ { name, "Mic Mute Mixer", "Mic Mute Mixer" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }
+
+static const struct snd_soc_dapm_route wm8997_dapm_routes[] = {
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "OUT1L", NULL, "CPVDD" },
+ { "OUT1R", NULL, "CPVDD" },
+ { "OUT3L", NULL, "CPVDD" },
+
+ { "OUT4L", NULL, "SPKVDD" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT3L", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+ { "MICBIAS3", NULL, "MICVDD" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+ { "AIF1 Capture", NULL, "AIF1TX7" },
+ { "AIF1 Capture", NULL, "AIF1TX8" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+ { "AIF1RX7", NULL, "AIF1 Playback" },
+ { "AIF1RX8", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
+
+ { "IN1L PGA", NULL, "IN1L" },
+ { "IN1R PGA", NULL, "IN1R" },
+
+ { "IN2L PGA", NULL, "IN2L" },
+ { "IN2R PGA", NULL, "IN2R" },
+
+ ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"),
+
+ ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUT"),
+ ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+ ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+ ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+ ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+
+ ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
+ ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
+ ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
+ ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
+ ARIZONA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"),
+
+ ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"),
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
+
+ ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ { "AEC Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+
+ { "AEC Loopback", "EPOUT", "OUT3L" },
+ { "EPOUTN", NULL, "OUT3L" },
+ { "EPOUTP", NULL, "OUT3L" },
+
+ { "AEC Loopback", "SPKOUT", "OUT4L" },
+ { "SPKOUTN", NULL, "OUT4L" },
+ { "SPKOUTP", NULL, "OUT4L" },
+
+ { "AEC Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+};
+
+static int wm8997_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm8997_priv *wm8997 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fll_id) {
+ case WM8997_FLL1:
+ return arizona_set_fll(&wm8997->fll[0], source, Fref, Fout);
+ case WM8997_FLL2:
+ return arizona_set_fll(&wm8997->fll[1], source, Fref, Fout);
+ case WM8997_FLL1_REFCLK:
+ return arizona_set_fll_refclk(&wm8997->fll[0], source, Fref,
+ Fout);
+ case WM8997_FLL2_REFCLK:
+ return arizona_set_fll_refclk(&wm8997->fll[1], source, Fref,
+ Fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+#define WM8997_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM8997_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver wm8997_dai[] = {
+ {
+ .name = "wm8997-aif1",
+ .id = 1,
+ .base = ARIZONA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm8997-aif2",
+ .id = 2,
+ .base = ARIZONA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm8997-slim1",
+ .id = 3,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm8997-slim2",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm8997-slim3",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+};
+
+static int wm8997_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ arizona_init_spk(codec);
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+
+ priv->core.arizona->dapm = &codec->dapm;
+
+ return 0;
+}
+
+static int wm8997_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->core.arizona->dapm = NULL;
+
+ return 0;
+}
+
+#define WM8997_DIG_VU 0x0200
+
+static unsigned int wm8997_digital_vu[] = {
+ ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R,
+ ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static struct regmap *wm8997_get_regmap(struct device *dev)
+{
+ struct wm8997_priv *priv = dev_get_drvdata(dev);
+
+ return priv->core.arizona->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8997 = {
+ .probe = wm8997_codec_probe,
+ .remove = wm8997_codec_remove,
+ .get_regmap = wm8997_get_regmap,
+
+ .idle_bias_off = true,
+
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm8997_set_fll,
+
+ .controls = wm8997_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8997_snd_controls),
+ .dapm_widgets = wm8997_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8997_dapm_widgets),
+ .dapm_routes = wm8997_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8997_dapm_routes),
+};
+
+static int wm8997_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct wm8997_priv *wm8997;
+ int i;
+
+ wm8997 = devm_kzalloc(&pdev->dev, sizeof(struct wm8997_priv),
+ GFP_KERNEL);
+ if (wm8997 == NULL)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, wm8997);
+
+ wm8997->core.arizona = arizona;
+ wm8997->core.num_inputs = 4;
+
+ for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++)
+ wm8997->fll[i].vco_mult = 1;
+
+ arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+ &wm8997->fll[0]);
+ arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+ &wm8997->fll[1]);
+
+ /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2,
+ ARIZONA_SAMPLE_RATE_2_MASK, 0x11);
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3,
+ ARIZONA_SAMPLE_RATE_3_MASK, 0x12);
+
+ for (i = 0; i < ARRAY_SIZE(wm8997_dai); i++)
+ arizona_init_dai(&wm8997->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(wm8997_digital_vu); i++)
+ regmap_update_bits(arizona->regmap, wm8997_digital_vu[i],
+ WM8997_DIG_VU, WM8997_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997,
+ wm8997_dai, ARRAY_SIZE(wm8997_dai));
+}
+
+static int wm8997_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver wm8997_codec_driver = {
+ .driver = {
+ .name = "wm8997-codec",
+ },
+ .probe = wm8997_probe,
+ .remove = wm8997_remove,
+};
+
+module_platform_driver(wm8997_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM8997 driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8997-codec");
diff --git a/kernel/sound/soc/codecs/wm8997.h b/kernel/sound/soc/codecs/wm8997.h
new file mode 100644
index 000000000..5e91c6a7d
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm8997.h
@@ -0,0 +1,23 @@
+/*
+ * wm8997.h -- WM8997 ALSA SoC Audio driver
+ *
+ * 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 _WM8997_H
+#define _WM8997_H
+
+#include "arizona.h"
+
+#define WM8997_FLL1 1
+#define WM8997_FLL2 2
+#define WM8997_FLL1_REFCLK 3
+#define WM8997_FLL2_REFCLK 4
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm9081.c b/kernel/sound/soc/codecs/wm9081.c
new file mode 100644
index 000000000..13a3f335e
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm9081.c
@@ -0,0 +1,1395 @@
+/*
+ * wm9081.c -- WM9081 ALSA SoC Audio driver
+ *
+ * Author: Mark Brown
+ *
+ * Copyright 2009-12 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 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/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/initval.h>
+#include <sound/tlv.h>
+
+#include <sound/wm9081.h>
+#include "wm9081.h"
+
+static struct reg_default wm9081_reg[] = {
+ { 2, 0x00B9 }, /* R2 - Analogue Lineout */
+ { 3, 0x00B9 }, /* R3 - Analogue Speaker PGA */
+ { 4, 0x0001 }, /* R4 - VMID Control */
+ { 5, 0x0068 }, /* R5 - Bias Control 1 */
+ { 7, 0x0000 }, /* R7 - Analogue Mixer */
+ { 8, 0x0000 }, /* R8 - Anti Pop Control */
+ { 9, 0x01DB }, /* R9 - Analogue Speaker 1 */
+ { 10, 0x0018 }, /* R10 - Analogue Speaker 2 */
+ { 11, 0x0180 }, /* R11 - Power Management */
+ { 12, 0x0000 }, /* R12 - Clock Control 1 */
+ { 13, 0x0038 }, /* R13 - Clock Control 2 */
+ { 14, 0x4000 }, /* R14 - Clock Control 3 */
+ { 16, 0x0000 }, /* R16 - FLL Control 1 */
+ { 17, 0x0200 }, /* R17 - FLL Control 2 */
+ { 18, 0x0000 }, /* R18 - FLL Control 3 */
+ { 19, 0x0204 }, /* R19 - FLL Control 4 */
+ { 20, 0x0000 }, /* R20 - FLL Control 5 */
+ { 22, 0x0000 }, /* R22 - Audio Interface 1 */
+ { 23, 0x0002 }, /* R23 - Audio Interface 2 */
+ { 24, 0x0008 }, /* R24 - Audio Interface 3 */
+ { 25, 0x0022 }, /* R25 - Audio Interface 4 */
+ { 27, 0x0006 }, /* R27 - Interrupt Status Mask */
+ { 28, 0x0000 }, /* R28 - Interrupt Polarity */
+ { 29, 0x0000 }, /* R29 - Interrupt Control */
+ { 30, 0x00C0 }, /* R30 - DAC Digital 1 */
+ { 31, 0x0008 }, /* R31 - DAC Digital 2 */
+ { 32, 0x09AF }, /* R32 - DRC 1 */
+ { 33, 0x4201 }, /* R33 - DRC 2 */
+ { 34, 0x0000 }, /* R34 - DRC 3 */
+ { 35, 0x0000 }, /* R35 - DRC 4 */
+ { 38, 0x0000 }, /* R38 - Write Sequencer 1 */
+ { 39, 0x0000 }, /* R39 - Write Sequencer 2 */
+ { 40, 0x0002 }, /* R40 - MW Slave 1 */
+ { 42, 0x0000 }, /* R42 - EQ 1 */
+ { 43, 0x0000 }, /* R43 - EQ 2 */
+ { 44, 0x0FCA }, /* R44 - EQ 3 */
+ { 45, 0x0400 }, /* R45 - EQ 4 */
+ { 46, 0x00B8 }, /* R46 - EQ 5 */
+ { 47, 0x1EB5 }, /* R47 - EQ 6 */
+ { 48, 0xF145 }, /* R48 - EQ 7 */
+ { 49, 0x0B75 }, /* R49 - EQ 8 */
+ { 50, 0x01C5 }, /* R50 - EQ 9 */
+ { 51, 0x169E }, /* R51 - EQ 10 */
+ { 52, 0xF829 }, /* R52 - EQ 11 */
+ { 53, 0x07AD }, /* R53 - EQ 12 */
+ { 54, 0x1103 }, /* R54 - EQ 13 */
+ { 55, 0x1C58 }, /* R55 - EQ 14 */
+ { 56, 0xF373 }, /* R56 - EQ 15 */
+ { 57, 0x0A54 }, /* R57 - EQ 16 */
+ { 58, 0x0558 }, /* R58 - EQ 17 */
+ { 59, 0x0564 }, /* R59 - EQ 18 */
+ { 60, 0x0559 }, /* R60 - EQ 19 */
+ { 61, 0x4000 }, /* R61 - EQ 20 */
+};
+
+static struct {
+ int ratio;
+ int clk_sys_rate;
+} clk_sys_rates[] = {
+ { 64, 0 },
+ { 128, 1 },
+ { 192, 2 },
+ { 256, 3 },
+ { 384, 4 },
+ { 512, 5 },
+ { 768, 6 },
+ { 1024, 7 },
+ { 1408, 8 },
+ { 1536, 9 },
+};
+
+static struct {
+ int rate;
+ int sample_rate;
+} sample_rates[] = {
+ { 8000, 0 },
+ { 11025, 1 },
+ { 12000, 2 },
+ { 16000, 3 },
+ { 22050, 4 },
+ { 24000, 5 },
+ { 32000, 6 },
+ { 44100, 7 },
+ { 48000, 8 },
+ { 88200, 9 },
+ { 96000, 10 },
+};
+
+static struct {
+ int div; /* *10 due to .5s */
+ int bclk_div;
+} bclk_divs[] = {
+ { 10, 0 },
+ { 15, 1 },
+ { 20, 2 },
+ { 30, 3 },
+ { 40, 4 },
+ { 50, 5 },
+ { 55, 6 },
+ { 60, 7 },
+ { 80, 8 },
+ { 100, 9 },
+ { 110, 10 },
+ { 120, 11 },
+ { 160, 12 },
+ { 200, 13 },
+ { 220, 14 },
+ { 240, 15 },
+ { 250, 16 },
+ { 300, 17 },
+ { 320, 18 },
+ { 440, 19 },
+ { 480, 20 },
+};
+
+struct wm9081_priv {
+ struct regmap *regmap;
+ int sysclk_source;
+ int mclk_rate;
+ int sysclk_rate;
+ int fs;
+ int bclk;
+ int master;
+ int fll_fref;
+ int fll_fout;
+ int tdm_width;
+ struct wm9081_pdata pdata;
+};
+
+static bool wm9081_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM9081_SOFTWARE_RESET:
+ case WM9081_INTERRUPT_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wm9081_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM9081_SOFTWARE_RESET:
+ case WM9081_ANALOGUE_LINEOUT:
+ case WM9081_ANALOGUE_SPEAKER_PGA:
+ case WM9081_VMID_CONTROL:
+ case WM9081_BIAS_CONTROL_1:
+ case WM9081_ANALOGUE_MIXER:
+ case WM9081_ANTI_POP_CONTROL:
+ case WM9081_ANALOGUE_SPEAKER_1:
+ case WM9081_ANALOGUE_SPEAKER_2:
+ case WM9081_POWER_MANAGEMENT:
+ case WM9081_CLOCK_CONTROL_1:
+ case WM9081_CLOCK_CONTROL_2:
+ case WM9081_CLOCK_CONTROL_3:
+ case WM9081_FLL_CONTROL_1:
+ case WM9081_FLL_CONTROL_2:
+ case WM9081_FLL_CONTROL_3:
+ case WM9081_FLL_CONTROL_4:
+ case WM9081_FLL_CONTROL_5:
+ case WM9081_AUDIO_INTERFACE_1:
+ case WM9081_AUDIO_INTERFACE_2:
+ case WM9081_AUDIO_INTERFACE_3:
+ case WM9081_AUDIO_INTERFACE_4:
+ case WM9081_INTERRUPT_STATUS:
+ case WM9081_INTERRUPT_STATUS_MASK:
+ case WM9081_INTERRUPT_POLARITY:
+ case WM9081_INTERRUPT_CONTROL:
+ case WM9081_DAC_DIGITAL_1:
+ case WM9081_DAC_DIGITAL_2:
+ case WM9081_DRC_1:
+ case WM9081_DRC_2:
+ case WM9081_DRC_3:
+ case WM9081_DRC_4:
+ case WM9081_WRITE_SEQUENCER_1:
+ case WM9081_WRITE_SEQUENCER_2:
+ case WM9081_MW_SLAVE_1:
+ case WM9081_EQ_1:
+ case WM9081_EQ_2:
+ case WM9081_EQ_3:
+ case WM9081_EQ_4:
+ case WM9081_EQ_5:
+ case WM9081_EQ_6:
+ case WM9081_EQ_7:
+ case WM9081_EQ_8:
+ case WM9081_EQ_9:
+ case WM9081_EQ_10:
+ case WM9081_EQ_11:
+ case WM9081_EQ_12:
+ case WM9081_EQ_13:
+ case WM9081_EQ_14:
+ case WM9081_EQ_15:
+ case WM9081_EQ_16:
+ case WM9081_EQ_17:
+ case WM9081_EQ_18:
+ case WM9081_EQ_19:
+ case WM9081_EQ_20:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int wm9081_reset(struct regmap *map)
+{
+ return regmap_write(map, WM9081_SOFTWARE_RESET, 0x9081);
+}
+
+static const DECLARE_TLV_DB_SCALE(drc_in_tlv, -4500, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_out_tlv, -2250, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0);
+static unsigned int drc_max_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 0, TLV_DB_SCALE_ITEM(1200, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(1800, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -300, 50, 0);
+
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(in_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+
+static const char *drc_high_text[] = {
+ "1",
+ "1/2",
+ "1/4",
+ "1/8",
+ "1/16",
+ "0",
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_high, WM9081_DRC_3, 3, drc_high_text);
+
+static const char *drc_low_text[] = {
+ "1",
+ "1/2",
+ "1/4",
+ "1/8",
+ "0",
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_low, WM9081_DRC_3, 0, drc_low_text);
+
+static const char *drc_atk_text[] = {
+ "181us",
+ "181us",
+ "363us",
+ "726us",
+ "1.45ms",
+ "2.9ms",
+ "5.8ms",
+ "11.6ms",
+ "23.2ms",
+ "46.4ms",
+ "92.8ms",
+ "185.6ms",
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_atk, WM9081_DRC_2, 12, drc_atk_text);
+
+static const char *drc_dcy_text[] = {
+ "186ms",
+ "372ms",
+ "743ms",
+ "1.49s",
+ "2.97s",
+ "5.94s",
+ "11.89s",
+ "23.78s",
+ "47.56s",
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_dcy, WM9081_DRC_2, 8, drc_dcy_text);
+
+static const char *drc_qr_dcy_text[] = {
+ "0.725ms",
+ "1.45ms",
+ "5.8ms",
+};
+
+static SOC_ENUM_SINGLE_DECL(drc_qr_dcy, WM9081_DRC_2, 4, drc_qr_dcy_text);
+
+static const char *dac_deemph_text[] = {
+ "None",
+ "32kHz",
+ "44.1kHz",
+ "48kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(dac_deemph, WM9081_DAC_DIGITAL_2, 1,
+ dac_deemph_text);
+
+static const char *speaker_mode_text[] = {
+ "Class D",
+ "Class AB",
+};
+
+static SOC_ENUM_SINGLE_DECL(speaker_mode, WM9081_ANALOGUE_SPEAKER_2, 6,
+ speaker_mode_text);
+
+static int speaker_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_2);
+ if (reg & WM9081_SPK_MODE)
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+/*
+ * Stop any attempts to change speaker mode while the speaker is enabled.
+ *
+ * We also have some special anti-pop controls dependent on speaker
+ * mode which must be changed along with the mode.
+ */
+static int speaker_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int reg_pwr = snd_soc_read(codec, WM9081_POWER_MANAGEMENT);
+ unsigned int reg2 = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_2);
+
+ /* Are we changing anything? */
+ if (ucontrol->value.integer.value[0] ==
+ ((reg2 & WM9081_SPK_MODE) != 0))
+ return 0;
+
+ /* Don't try to change modes while enabled */
+ if (reg_pwr & WM9081_SPK_ENA)
+ return -EINVAL;
+
+ if (ucontrol->value.integer.value[0]) {
+ /* Class AB */
+ reg2 &= ~(WM9081_SPK_INV_MUTE | WM9081_OUT_SPK_CTRL);
+ reg2 |= WM9081_SPK_MODE;
+ } else {
+ /* Class D */
+ reg2 |= WM9081_SPK_INV_MUTE | WM9081_OUT_SPK_CTRL;
+ reg2 &= ~WM9081_SPK_MODE;
+ }
+
+ snd_soc_write(codec, WM9081_ANALOGUE_SPEAKER_2, reg2);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wm9081_snd_controls[] = {
+SOC_SINGLE_TLV("IN1 Volume", WM9081_ANALOGUE_MIXER, 1, 1, 1, in_tlv),
+SOC_SINGLE_TLV("IN2 Volume", WM9081_ANALOGUE_MIXER, 3, 1, 1, in_tlv),
+
+SOC_SINGLE_TLV("Playback Volume", WM9081_DAC_DIGITAL_1, 1, 96, 0, dac_tlv),
+
+SOC_SINGLE("LINEOUT Switch", WM9081_ANALOGUE_LINEOUT, 7, 1, 1),
+SOC_SINGLE("LINEOUT ZC Switch", WM9081_ANALOGUE_LINEOUT, 6, 1, 0),
+SOC_SINGLE_TLV("LINEOUT Volume", WM9081_ANALOGUE_LINEOUT, 0, 63, 0, out_tlv),
+
+SOC_SINGLE("DRC Switch", WM9081_DRC_1, 15, 1, 0),
+SOC_ENUM("DRC High Slope", drc_high),
+SOC_ENUM("DRC Low Slope", drc_low),
+SOC_SINGLE_TLV("DRC Input Volume", WM9081_DRC_4, 5, 60, 1, drc_in_tlv),
+SOC_SINGLE_TLV("DRC Output Volume", WM9081_DRC_4, 0, 30, 1, drc_out_tlv),
+SOC_SINGLE_TLV("DRC Minimum Volume", WM9081_DRC_2, 2, 3, 1, drc_min_tlv),
+SOC_SINGLE_TLV("DRC Maximum Volume", WM9081_DRC_2, 0, 3, 0, drc_max_tlv),
+SOC_ENUM("DRC Attack", drc_atk),
+SOC_ENUM("DRC Decay", drc_dcy),
+SOC_SINGLE("DRC Quick Release Switch", WM9081_DRC_1, 2, 1, 0),
+SOC_SINGLE_TLV("DRC Quick Release Volume", WM9081_DRC_2, 6, 3, 0, drc_qr_tlv),
+SOC_ENUM("DRC Quick Release Decay", drc_qr_dcy),
+SOC_SINGLE_TLV("DRC Startup Volume", WM9081_DRC_1, 6, 18, 0, drc_startup_tlv),
+
+SOC_SINGLE("EQ Switch", WM9081_EQ_1, 0, 1, 0),
+
+SOC_SINGLE("Speaker DC Volume", WM9081_ANALOGUE_SPEAKER_1, 3, 5, 0),
+SOC_SINGLE("Speaker AC Volume", WM9081_ANALOGUE_SPEAKER_1, 0, 5, 0),
+SOC_SINGLE("Speaker Switch", WM9081_ANALOGUE_SPEAKER_PGA, 7, 1, 1),
+SOC_SINGLE("Speaker ZC Switch", WM9081_ANALOGUE_SPEAKER_PGA, 6, 1, 0),
+SOC_SINGLE_TLV("Speaker Volume", WM9081_ANALOGUE_SPEAKER_PGA, 0, 63, 0,
+ out_tlv),
+SOC_ENUM("DAC Deemphasis", dac_deemph),
+SOC_ENUM_EXT("Speaker Mode", speaker_mode, speaker_mode_get, speaker_mode_put),
+};
+
+static const struct snd_kcontrol_new wm9081_eq_controls[] = {
+SOC_SINGLE_TLV("EQ1 Volume", WM9081_EQ_1, 11, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Volume", WM9081_EQ_1, 6, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Volume", WM9081_EQ_1, 1, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Volume", WM9081_EQ_2, 11, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ5 Volume", WM9081_EQ_2, 6, 24, 0, eq_tlv),
+};
+
+static const struct snd_kcontrol_new mixer[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM9081_ANALOGUE_MIXER, 0, 1, 0),
+SOC_DAPM_SINGLE("IN2 Switch", WM9081_ANALOGUE_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("Playback Switch", WM9081_ANALOGUE_MIXER, 4, 1, 0),
+};
+
+struct _fll_div {
+ u16 fll_fratio;
+ u16 fll_outdiv;
+ u16 fll_clk_ref_div;
+ u16 n;
+ u16 k;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fll_fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod, target;
+ unsigned int div;
+ int i;
+
+ /* Fref must be <=13.5MHz */
+ div = 1;
+ while ((Fref / div) > 13500000) {
+ div *= 2;
+
+ if (div > 8) {
+ pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+ }
+ fll_div->fll_clk_ref_div = div / 2;
+
+ pr_debug("Fref=%u Fout=%u\n", Fref, Fout);
+
+ /* Apply the division for our remaining calculations */
+ Fref /= div;
+
+ /* Fvco should be 90-100MHz; don't check the upper bound */
+ div = 0;
+ target = Fout * 2;
+ while (target < 90000000) {
+ div++;
+ target *= 2;
+ if (div > 7) {
+ pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ fll_div->fll_outdiv = div;
+
+ pr_debug("Fvco=%dHz\n", target);
+
+ /* Find an appropriate FLL_FRATIO and factor it out of the target */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+ target /= fll_fratios[i].ratio;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_fratios)) {
+ pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+ return -EINVAL;
+ }
+
+ /* Now, calculate N.K */
+ Ndiv = target / Fref;
+
+ fll_div->n = Ndiv;
+ Nmod = target % Fref;
+ pr_debug("Nmod=%d\n", Nmod);
+
+ /* Calculate fractional part - scale up so we can round. */
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, Fref);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ fll_div->k = K / 10;
+
+ pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n",
+ fll_div->n, fll_div->k,
+ fll_div->fll_fratio, fll_div->fll_outdiv,
+ fll_div->fll_clk_ref_div);
+
+ return 0;
+}
+
+static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+ u16 reg1, reg4, reg5;
+ struct _fll_div fll_div;
+ int ret;
+ int clk_sys_reg;
+
+ /* Any change? */
+ if (Fref == wm9081->fll_fref && Fout == wm9081->fll_fout)
+ return 0;
+
+ /* Disable the FLL */
+ if (Fout == 0) {
+ dev_dbg(codec->dev, "FLL disabled\n");
+ wm9081->fll_fref = 0;
+ wm9081->fll_fout = 0;
+
+ return 0;
+ }
+
+ ret = fll_factors(&fll_div, Fref, Fout);
+ if (ret != 0)
+ return ret;
+
+ reg5 = snd_soc_read(codec, WM9081_FLL_CONTROL_5);
+ reg5 &= ~WM9081_FLL_CLK_SRC_MASK;
+
+ switch (fll_id) {
+ case WM9081_SYSCLK_FLL_MCLK:
+ reg5 |= 0x1;
+ break;
+
+ default:
+ dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
+ return -EINVAL;
+ }
+
+ /* Disable CLK_SYS while we reconfigure */
+ clk_sys_reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_3);
+ if (clk_sys_reg & WM9081_CLK_SYS_ENA)
+ snd_soc_write(codec, WM9081_CLOCK_CONTROL_3,
+ clk_sys_reg & ~WM9081_CLK_SYS_ENA);
+
+ /* Any FLL configuration change requires that the FLL be
+ * disabled first. */
+ reg1 = snd_soc_read(codec, WM9081_FLL_CONTROL_1);
+ reg1 &= ~WM9081_FLL_ENA;
+ snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1);
+
+ /* Apply the configuration */
+ if (fll_div.k)
+ reg1 |= WM9081_FLL_FRAC_MASK;
+ else
+ reg1 &= ~WM9081_FLL_FRAC_MASK;
+ snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1);
+
+ snd_soc_write(codec, WM9081_FLL_CONTROL_2,
+ (fll_div.fll_outdiv << WM9081_FLL_OUTDIV_SHIFT) |
+ (fll_div.fll_fratio << WM9081_FLL_FRATIO_SHIFT));
+ snd_soc_write(codec, WM9081_FLL_CONTROL_3, fll_div.k);
+
+ reg4 = snd_soc_read(codec, WM9081_FLL_CONTROL_4);
+ reg4 &= ~WM9081_FLL_N_MASK;
+ reg4 |= fll_div.n << WM9081_FLL_N_SHIFT;
+ snd_soc_write(codec, WM9081_FLL_CONTROL_4, reg4);
+
+ reg5 &= ~WM9081_FLL_CLK_REF_DIV_MASK;
+ reg5 |= fll_div.fll_clk_ref_div << WM9081_FLL_CLK_REF_DIV_SHIFT;
+ snd_soc_write(codec, WM9081_FLL_CONTROL_5, reg5);
+
+ /* Set gain to the recommended value */
+ snd_soc_update_bits(codec, WM9081_FLL_CONTROL_4,
+ WM9081_FLL_GAIN_MASK, 0);
+
+ /* Enable the FLL */
+ snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1 | WM9081_FLL_ENA);
+
+ /* Then bring CLK_SYS up again if it was disabled */
+ if (clk_sys_reg & WM9081_CLK_SYS_ENA)
+ snd_soc_write(codec, WM9081_CLOCK_CONTROL_3, clk_sys_reg);
+
+ dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
+
+ wm9081->fll_fref = Fref;
+ wm9081->fll_fout = Fout;
+
+ return 0;
+}
+
+static int configure_clock(struct snd_soc_codec *codec)
+{
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+ int new_sysclk, i, target;
+ unsigned int reg;
+ int ret = 0;
+ int mclkdiv = 0;
+ int fll = 0;
+
+ switch (wm9081->sysclk_source) {
+ case WM9081_SYSCLK_MCLK:
+ if (wm9081->mclk_rate > 12225000) {
+ mclkdiv = 1;
+ wm9081->sysclk_rate = wm9081->mclk_rate / 2;
+ } else {
+ wm9081->sysclk_rate = wm9081->mclk_rate;
+ }
+ wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK, 0, 0);
+ break;
+
+ case WM9081_SYSCLK_FLL_MCLK:
+ /* If we have a sample rate calculate a CLK_SYS that
+ * gives us a suitable DAC configuration, plus BCLK.
+ * Ideally we would check to see if we can clock
+ * directly from MCLK and only use the FLL if this is
+ * not the case, though care must be taken with free
+ * running mode.
+ */
+ if (wm9081->master && wm9081->bclk) {
+ /* Make sure we can generate CLK_SYS and BCLK
+ * and that we've got 3MHz for optimal
+ * performance. */
+ for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) {
+ target = wm9081->fs * clk_sys_rates[i].ratio;
+ new_sysclk = target;
+ if (target >= wm9081->bclk &&
+ target > 3000000)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(clk_sys_rates))
+ return -EINVAL;
+
+ } else if (wm9081->fs) {
+ for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) {
+ new_sysclk = clk_sys_rates[i].ratio
+ * wm9081->fs;
+ if (new_sysclk > 3000000)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(clk_sys_rates))
+ return -EINVAL;
+
+ } else {
+ new_sysclk = 12288000;
+ }
+
+ ret = wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK,
+ wm9081->mclk_rate, new_sysclk);
+ if (ret == 0) {
+ wm9081->sysclk_rate = new_sysclk;
+
+ /* Switch SYSCLK over to FLL */
+ fll = 1;
+ } else {
+ wm9081->sysclk_rate = wm9081->mclk_rate;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_1);
+ if (mclkdiv)
+ reg |= WM9081_MCLKDIV2;
+ else
+ reg &= ~WM9081_MCLKDIV2;
+ snd_soc_write(codec, WM9081_CLOCK_CONTROL_1, reg);
+
+ reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_3);
+ if (fll)
+ reg |= WM9081_CLK_SRC_SEL;
+ else
+ reg &= ~WM9081_CLK_SRC_SEL;
+ snd_soc_write(codec, WM9081_CLOCK_CONTROL_3, reg);
+
+ dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm9081->sysclk_rate);
+
+ return ret;
+}
+
+static int clk_sys_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+
+ /* This should be done on init() for bypass paths */
+ switch (wm9081->sysclk_source) {
+ case WM9081_SYSCLK_MCLK:
+ dev_dbg(codec->dev, "Using %dHz MCLK\n", wm9081->mclk_rate);
+ break;
+ case WM9081_SYSCLK_FLL_MCLK:
+ dev_dbg(codec->dev, "Using %dHz MCLK with FLL\n",
+ wm9081->mclk_rate);
+ break;
+ default:
+ dev_err(codec->dev, "System clock not configured\n");
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ configure_clock(codec);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable the FLL if it's running */
+ wm9081_set_fll(codec, 0, 0, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget wm9081_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1"),
+SND_SOC_DAPM_INPUT("IN2"),
+
+SND_SOC_DAPM_DAC("DAC", NULL, WM9081_POWER_MANAGEMENT, 0, 0),
+
+SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0,
+ mixer, ARRAY_SIZE(mixer)),
+
+SND_SOC_DAPM_PGA("LINEOUT PGA", WM9081_POWER_MANAGEMENT, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("LINEOUT"),
+SND_SOC_DAPM_OUTPUT("SPKN"),
+SND_SOC_DAPM_OUTPUT("SPKP"),
+
+SND_SOC_DAPM_SUPPLY("CLK_SYS", WM9081_CLOCK_CONTROL_3, 0, 0, clk_sys_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM9081_CLOCK_CONTROL_3, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("TOCLK", WM9081_CLOCK_CONTROL_3, 2, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("TSENSE", WM9081_POWER_MANAGEMENT, 7, 0, NULL, 0),
+};
+
+
+static const struct snd_soc_dapm_route wm9081_audio_paths[] = {
+ { "DAC", NULL, "CLK_SYS" },
+ { "DAC", NULL, "CLK_DSP" },
+ { "DAC", NULL, "AIF" },
+
+ { "Mixer", "IN1 Switch", "IN1" },
+ { "Mixer", "IN2 Switch", "IN2" },
+ { "Mixer", "Playback Switch", "DAC" },
+
+ { "LINEOUT PGA", NULL, "Mixer" },
+ { "LINEOUT PGA", NULL, "TOCLK" },
+ { "LINEOUT PGA", NULL, "CLK_SYS" },
+
+ { "LINEOUT", NULL, "LINEOUT PGA" },
+
+ { "Speaker PGA", NULL, "Mixer" },
+ { "Speaker PGA", NULL, "TOCLK" },
+ { "Speaker PGA", NULL, "CLK_SYS" },
+
+ { "Speaker", NULL, "Speaker PGA" },
+ { "Speaker", NULL, "TSENSE" },
+
+ { "SPKN", NULL, "Speaker" },
+ { "SPKP", NULL, "Speaker" },
+};
+
+static int wm9081_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID=2*40k */
+ snd_soc_update_bits(codec, WM9081_VMID_CONTROL,
+ WM9081_VMID_SEL_MASK, 0x2);
+
+ /* Normal bias current */
+ snd_soc_update_bits(codec, WM9081_BIAS_CONTROL_1,
+ WM9081_STBY_BIAS_ENA, 0);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ /* Initial cold start */
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ regcache_cache_only(wm9081->regmap, false);
+ regcache_sync(wm9081->regmap);
+
+ /* Disable LINEOUT discharge */
+ snd_soc_update_bits(codec, WM9081_ANTI_POP_CONTROL,
+ WM9081_LINEOUT_DISCH, 0);
+
+ /* Select startup bias source */
+ snd_soc_update_bits(codec, WM9081_BIAS_CONTROL_1,
+ WM9081_BIAS_SRC | WM9081_BIAS_ENA,
+ WM9081_BIAS_SRC | WM9081_BIAS_ENA);
+
+ /* VMID 2*4k; Soft VMID ramp enable */
+ snd_soc_update_bits(codec, WM9081_VMID_CONTROL,
+ WM9081_VMID_RAMP |
+ WM9081_VMID_SEL_MASK,
+ WM9081_VMID_RAMP | 0x6);
+
+ mdelay(100);
+
+ /* Normal bias enable & soft start off */
+ snd_soc_update_bits(codec, WM9081_VMID_CONTROL,
+ WM9081_VMID_RAMP, 0);
+
+ /* Standard bias source */
+ snd_soc_update_bits(codec, WM9081_BIAS_CONTROL_1,
+ WM9081_BIAS_SRC, 0);
+ }
+
+ /* VMID 2*240k */
+ snd_soc_update_bits(codec, WM9081_VMID_CONTROL,
+ WM9081_VMID_SEL_MASK, 0x04);
+
+ /* Standby bias current on */
+ snd_soc_update_bits(codec, WM9081_BIAS_CONTROL_1,
+ WM9081_STBY_BIAS_ENA,
+ WM9081_STBY_BIAS_ENA);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Startup bias source and disable bias */
+ snd_soc_update_bits(codec, WM9081_BIAS_CONTROL_1,
+ WM9081_BIAS_SRC | WM9081_BIAS_ENA,
+ WM9081_BIAS_SRC);
+
+ /* Disable VMID with soft ramping */
+ snd_soc_update_bits(codec, WM9081_VMID_CONTROL,
+ WM9081_VMID_RAMP | WM9081_VMID_SEL_MASK,
+ WM9081_VMID_RAMP);
+
+ /* Actively discharge LINEOUT */
+ snd_soc_update_bits(codec, WM9081_ANTI_POP_CONTROL,
+ WM9081_LINEOUT_DISCH,
+ WM9081_LINEOUT_DISCH);
+
+ regcache_cache_only(wm9081->regmap, true);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int wm9081_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+ unsigned int aif2 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_2);
+
+ aif2 &= ~(WM9081_AIF_BCLK_INV | WM9081_AIF_LRCLK_INV |
+ WM9081_BCLK_DIR | WM9081_LRCLK_DIR | WM9081_AIF_FMT_MASK);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ wm9081->master = 0;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ aif2 |= WM9081_LRCLK_DIR;
+ wm9081->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ aif2 |= WM9081_BCLK_DIR;
+ wm9081->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif2 |= WM9081_LRCLK_DIR | WM9081_BCLK_DIR;
+ wm9081->master = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_B:
+ aif2 |= WM9081_AIF_LRCLK_INV;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif2 |= 0x3;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aif2 |= 0x2;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif2 |= 0x1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif2 |= WM9081_AIF_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif2 |= WM9081_AIF_BCLK_INV | WM9081_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif2 |= WM9081_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif2 |= WM9081_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM9081_AUDIO_INTERFACE_2, aif2);
+
+ return 0;
+}
+
+static int wm9081_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+ int ret, i, best, best_val, cur_val;
+ unsigned int clk_ctrl2, aif1, aif2, aif3, aif4;
+
+ clk_ctrl2 = snd_soc_read(codec, WM9081_CLOCK_CONTROL_2);
+ clk_ctrl2 &= ~(WM9081_CLK_SYS_RATE_MASK | WM9081_SAMPLE_RATE_MASK);
+
+ aif1 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_1);
+
+ aif2 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_2);
+ aif2 &= ~WM9081_AIF_WL_MASK;
+
+ aif3 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_3);
+ aif3 &= ~WM9081_BCLK_DIV_MASK;
+
+ aif4 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_4);
+ aif4 &= ~WM9081_LRCLK_RATE_MASK;
+
+ wm9081->fs = params_rate(params);
+
+ if (wm9081->tdm_width) {
+ /* If TDM is set up then that fixes our BCLK. */
+ int slots = ((aif1 & WM9081_AIFDAC_TDM_MODE_MASK) >>
+ WM9081_AIFDAC_TDM_MODE_SHIFT) + 1;
+
+ wm9081->bclk = wm9081->fs * wm9081->tdm_width * slots;
+ } else {
+ /* Otherwise work out a BCLK from the sample size */
+ wm9081->bclk = 2 * wm9081->fs;
+
+ switch (params_width(params)) {
+ case 16:
+ wm9081->bclk *= 16;
+ break;
+ case 20:
+ wm9081->bclk *= 20;
+ aif2 |= 0x4;
+ break;
+ case 24:
+ wm9081->bclk *= 24;
+ aif2 |= 0x8;
+ break;
+ case 32:
+ wm9081->bclk *= 32;
+ aif2 |= 0xc;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm9081->bclk);
+
+ ret = configure_clock(codec);
+ if (ret != 0)
+ return ret;
+
+ /* Select nearest CLK_SYS_RATE */
+ best = 0;
+ best_val = abs((wm9081->sysclk_rate / clk_sys_rates[0].ratio)
+ - wm9081->fs);
+ for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) {
+ cur_val = abs((wm9081->sysclk_rate /
+ clk_sys_rates[i].ratio) - wm9081->fs);
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+ dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n",
+ clk_sys_rates[best].ratio);
+ clk_ctrl2 |= (clk_sys_rates[best].clk_sys_rate
+ << WM9081_CLK_SYS_RATE_SHIFT);
+
+ /* SAMPLE_RATE */
+ best = 0;
+ best_val = abs(wm9081->fs - sample_rates[0].rate);
+ for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+ /* Closest match */
+ cur_val = abs(wm9081->fs - sample_rates[i].rate);
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+ dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n",
+ sample_rates[best].rate);
+ clk_ctrl2 |= (sample_rates[best].sample_rate
+ << WM9081_SAMPLE_RATE_SHIFT);
+
+ /* BCLK_DIV */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ cur_val = ((wm9081->sysclk_rate * 10) / bclk_divs[i].div)
+ - wm9081->bclk;
+ if (cur_val < 0) /* Table is sorted */
+ break;
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+ wm9081->bclk = (wm9081->sysclk_rate * 10) / bclk_divs[best].div;
+ dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
+ bclk_divs[best].div, wm9081->bclk);
+ aif3 |= bclk_divs[best].bclk_div;
+
+ /* LRCLK is a simple fraction of BCLK */
+ dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm9081->bclk / wm9081->fs);
+ aif4 |= wm9081->bclk / wm9081->fs;
+
+ /* Apply a ReTune Mobile configuration if it's in use */
+ if (wm9081->pdata.num_retune_configs) {
+ struct wm9081_pdata *pdata = &wm9081->pdata;
+ struct wm9081_retune_mobile_setting *s;
+ int eq1;
+
+ best = 0;
+ best_val = abs(pdata->retune_configs[0].rate - wm9081->fs);
+ for (i = 0; i < pdata->num_retune_configs; i++) {
+ cur_val = abs(pdata->retune_configs[i].rate -
+ wm9081->fs);
+ if (cur_val < best_val) {
+ best_val = cur_val;
+ best = i;
+ }
+ }
+ s = &pdata->retune_configs[best];
+
+ dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n",
+ s->name, s->rate);
+
+ /* If the EQ is enabled then disable it while we write out */
+ eq1 = snd_soc_read(codec, WM9081_EQ_1) & WM9081_EQ_ENA;
+ if (eq1 & WM9081_EQ_ENA)
+ snd_soc_write(codec, WM9081_EQ_1, 0);
+
+ /* Write out the other values */
+ for (i = 1; i < ARRAY_SIZE(s->config); i++)
+ snd_soc_write(codec, WM9081_EQ_1 + i, s->config[i]);
+
+ eq1 |= (s->config[0] & ~WM9081_EQ_ENA);
+ snd_soc_write(codec, WM9081_EQ_1, eq1);
+ }
+
+ snd_soc_write(codec, WM9081_CLOCK_CONTROL_2, clk_ctrl2);
+ snd_soc_write(codec, WM9081_AUDIO_INTERFACE_2, aif2);
+ snd_soc_write(codec, WM9081_AUDIO_INTERFACE_3, aif3);
+ snd_soc_write(codec, WM9081_AUDIO_INTERFACE_4, aif4);
+
+ return 0;
+}
+
+static int wm9081_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int reg;
+
+ reg = snd_soc_read(codec, WM9081_DAC_DIGITAL_2);
+
+ if (mute)
+ reg |= WM9081_DAC_MUTE;
+ else
+ reg &= ~WM9081_DAC_MUTE;
+
+ snd_soc_write(codec, WM9081_DAC_DIGITAL_2, reg);
+
+ return 0;
+}
+
+static int wm9081_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case WM9081_SYSCLK_MCLK:
+ case WM9081_SYSCLK_FLL_MCLK:
+ wm9081->sysclk_source = clk_id;
+ wm9081->mclk_rate = freq;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm9081_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+ unsigned int aif1 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_1);
+
+ aif1 &= ~(WM9081_AIFDAC_TDM_SLOT_MASK | WM9081_AIFDAC_TDM_MODE_MASK);
+
+ if (slots < 0 || slots > 4)
+ return -EINVAL;
+
+ wm9081->tdm_width = slot_width;
+
+ if (slots == 0)
+ slots = 1;
+
+ aif1 |= (slots - 1) << WM9081_AIFDAC_TDM_MODE_SHIFT;
+
+ switch (rx_mask) {
+ case 1:
+ break;
+ case 2:
+ aif1 |= 0x10;
+ break;
+ case 4:
+ aif1 |= 0x20;
+ break;
+ case 8:
+ aif1 |= 0x30;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM9081_AUDIO_INTERFACE_1, aif1);
+
+ return 0;
+}
+
+#define WM9081_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM9081_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops wm9081_dai_ops = {
+ .hw_params = wm9081_hw_params,
+ .set_fmt = wm9081_set_dai_fmt,
+ .digital_mute = wm9081_digital_mute,
+ .set_tdm_slot = wm9081_set_tdm_slot,
+};
+
+/* We report two channels because the CODEC processes a stereo signal, even
+ * though it is only capable of handling a mono output.
+ */
+static struct snd_soc_dai_driver wm9081_dai = {
+ .name = "wm9081-hifi",
+ .playback = {
+ .stream_name = "AIF",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9081_RATES,
+ .formats = WM9081_FORMATS,
+ },
+ .ops = &wm9081_dai_ops,
+};
+
+static int wm9081_probe(struct snd_soc_codec *codec)
+{
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
+
+ /* Enable zero cross by default */
+ snd_soc_update_bits(codec, WM9081_ANALOGUE_LINEOUT,
+ WM9081_LINEOUTZC, WM9081_LINEOUTZC);
+ snd_soc_update_bits(codec, WM9081_ANALOGUE_SPEAKER_PGA,
+ WM9081_SPKPGAZC, WM9081_SPKPGAZC);
+
+ if (!wm9081->pdata.num_retune_configs) {
+ dev_dbg(codec->dev,
+ "No ReTune Mobile data, using normal EQ\n");
+ snd_soc_add_codec_controls(codec, wm9081_eq_controls,
+ ARRAY_SIZE(wm9081_eq_controls));
+ }
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm9081 = {
+ .probe = wm9081_probe,
+
+ .set_sysclk = wm9081_set_sysclk,
+ .set_bias_level = wm9081_set_bias_level,
+
+ .idle_bias_off = true,
+
+ .controls = wm9081_snd_controls,
+ .num_controls = ARRAY_SIZE(wm9081_snd_controls),
+ .dapm_widgets = wm9081_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9081_dapm_widgets),
+ .dapm_routes = wm9081_audio_paths,
+ .num_dapm_routes = ARRAY_SIZE(wm9081_audio_paths),
+};
+
+static const struct regmap_config wm9081_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = WM9081_MAX_REGISTER,
+ .reg_defaults = wm9081_reg,
+ .num_reg_defaults = ARRAY_SIZE(wm9081_reg),
+ .volatile_reg = wm9081_volatile_register,
+ .readable_reg = wm9081_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#if IS_ENABLED(CONFIG_I2C)
+static int wm9081_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm9081_priv *wm9081;
+ unsigned int reg;
+ int ret;
+
+ wm9081 = devm_kzalloc(&i2c->dev, sizeof(struct wm9081_priv),
+ GFP_KERNEL);
+ if (wm9081 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm9081);
+
+ wm9081->regmap = devm_regmap_init_i2c(i2c, &wm9081_regmap);
+ if (IS_ERR(wm9081->regmap)) {
+ ret = PTR_ERR(wm9081->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(wm9081->regmap, WM9081_SOFTWARE_RESET, &reg);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret);
+ return ret;
+ }
+ if (reg != 0x9081) {
+ dev_err(&i2c->dev, "Device is not a WM9081: ID=0x%x\n", reg);
+ return -EINVAL;
+ }
+
+ ret = wm9081_reset(wm9081->regmap);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to issue reset\n");
+ return ret;
+ }
+
+ if (dev_get_platdata(&i2c->dev))
+ memcpy(&wm9081->pdata, dev_get_platdata(&i2c->dev),
+ sizeof(wm9081->pdata));
+
+ reg = 0;
+ if (wm9081->pdata.irq_high)
+ reg |= WM9081_IRQ_POL;
+ if (!wm9081->pdata.irq_cmos)
+ reg |= WM9081_IRQ_OP_CTRL;
+ regmap_update_bits(wm9081->regmap, WM9081_INTERRUPT_CONTROL,
+ WM9081_IRQ_POL | WM9081_IRQ_OP_CTRL, reg);
+
+ regcache_cache_only(wm9081->regmap, true);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm9081, &wm9081_dai, 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int wm9081_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm9081_i2c_id[] = {
+ { "wm9081", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id);
+
+static struct i2c_driver wm9081_i2c_driver = {
+ .driver = {
+ .name = "wm9081",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm9081_i2c_probe,
+ .remove = wm9081_i2c_remove,
+ .id_table = wm9081_i2c_id,
+};
+#endif
+
+module_i2c_driver(wm9081_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM9081 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm9081.h b/kernel/sound/soc/codecs/wm9081.h
new file mode 100644
index 000000000..871cccb06
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm9081.h
@@ -0,0 +1,784 @@
+#ifndef WM9081_H
+#define WM9081_H
+
+/*
+ * wm9081.c -- WM9081 ALSA SoC Audio driver
+ *
+ * Author: Mark Brown
+ *
+ * Copyright 2009 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <sound/soc.h>
+
+/*
+ * SYSCLK sources
+ */
+#define WM9081_SYSCLK_MCLK 1 /* Use MCLK without FLL */
+#define WM9081_SYSCLK_FLL_MCLK 2 /* Use MCLK, enabling FLL if required */
+
+/*
+ * Register values.
+ */
+#define WM9081_SOFTWARE_RESET 0x00
+#define WM9081_ANALOGUE_LINEOUT 0x02
+#define WM9081_ANALOGUE_SPEAKER_PGA 0x03
+#define WM9081_VMID_CONTROL 0x04
+#define WM9081_BIAS_CONTROL_1 0x05
+#define WM9081_ANALOGUE_MIXER 0x07
+#define WM9081_ANTI_POP_CONTROL 0x08
+#define WM9081_ANALOGUE_SPEAKER_1 0x09
+#define WM9081_ANALOGUE_SPEAKER_2 0x0A
+#define WM9081_POWER_MANAGEMENT 0x0B
+#define WM9081_CLOCK_CONTROL_1 0x0C
+#define WM9081_CLOCK_CONTROL_2 0x0D
+#define WM9081_CLOCK_CONTROL_3 0x0E
+#define WM9081_FLL_CONTROL_1 0x10
+#define WM9081_FLL_CONTROL_2 0x11
+#define WM9081_FLL_CONTROL_3 0x12
+#define WM9081_FLL_CONTROL_4 0x13
+#define WM9081_FLL_CONTROL_5 0x14
+#define WM9081_AUDIO_INTERFACE_1 0x16
+#define WM9081_AUDIO_INTERFACE_2 0x17
+#define WM9081_AUDIO_INTERFACE_3 0x18
+#define WM9081_AUDIO_INTERFACE_4 0x19
+#define WM9081_INTERRUPT_STATUS 0x1A
+#define WM9081_INTERRUPT_STATUS_MASK 0x1B
+#define WM9081_INTERRUPT_POLARITY 0x1C
+#define WM9081_INTERRUPT_CONTROL 0x1D
+#define WM9081_DAC_DIGITAL_1 0x1E
+#define WM9081_DAC_DIGITAL_2 0x1F
+#define WM9081_DRC_1 0x20
+#define WM9081_DRC_2 0x21
+#define WM9081_DRC_3 0x22
+#define WM9081_DRC_4 0x23
+#define WM9081_WRITE_SEQUENCER_1 0x26
+#define WM9081_WRITE_SEQUENCER_2 0x27
+#define WM9081_MW_SLAVE_1 0x28
+#define WM9081_EQ_1 0x2A
+#define WM9081_EQ_2 0x2B
+#define WM9081_EQ_3 0x2C
+#define WM9081_EQ_4 0x2D
+#define WM9081_EQ_5 0x2E
+#define WM9081_EQ_6 0x2F
+#define WM9081_EQ_7 0x30
+#define WM9081_EQ_8 0x31
+#define WM9081_EQ_9 0x32
+#define WM9081_EQ_10 0x33
+#define WM9081_EQ_11 0x34
+#define WM9081_EQ_12 0x35
+#define WM9081_EQ_13 0x36
+#define WM9081_EQ_14 0x37
+#define WM9081_EQ_15 0x38
+#define WM9081_EQ_16 0x39
+#define WM9081_EQ_17 0x3A
+#define WM9081_EQ_18 0x3B
+#define WM9081_EQ_19 0x3C
+#define WM9081_EQ_20 0x3D
+
+#define WM9081_REGISTER_COUNT 55
+#define WM9081_MAX_REGISTER 0x3D
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM9081_SW_RST_DEV_ID1_MASK 0xFFFF /* SW_RST_DEV_ID1 - [15:0] */
+#define WM9081_SW_RST_DEV_ID1_SHIFT 0 /* SW_RST_DEV_ID1 - [15:0] */
+#define WM9081_SW_RST_DEV_ID1_WIDTH 16 /* SW_RST_DEV_ID1 - [15:0] */
+
+/*
+ * R2 (0x02) - Analogue Lineout
+ */
+#define WM9081_LINEOUT_MUTE 0x0080 /* LINEOUT_MUTE */
+#define WM9081_LINEOUT_MUTE_MASK 0x0080 /* LINEOUT_MUTE */
+#define WM9081_LINEOUT_MUTE_SHIFT 7 /* LINEOUT_MUTE */
+#define WM9081_LINEOUT_MUTE_WIDTH 1 /* LINEOUT_MUTE */
+#define WM9081_LINEOUTZC 0x0040 /* LINEOUTZC */
+#define WM9081_LINEOUTZC_MASK 0x0040 /* LINEOUTZC */
+#define WM9081_LINEOUTZC_SHIFT 6 /* LINEOUTZC */
+#define WM9081_LINEOUTZC_WIDTH 1 /* LINEOUTZC */
+#define WM9081_LINEOUT_VOL_MASK 0x003F /* LINEOUT_VOL - [5:0] */
+#define WM9081_LINEOUT_VOL_SHIFT 0 /* LINEOUT_VOL - [5:0] */
+#define WM9081_LINEOUT_VOL_WIDTH 6 /* LINEOUT_VOL - [5:0] */
+
+/*
+ * R3 (0x03) - Analogue Speaker PGA
+ */
+#define WM9081_SPKPGA_MUTE 0x0080 /* SPKPGA_MUTE */
+#define WM9081_SPKPGA_MUTE_MASK 0x0080 /* SPKPGA_MUTE */
+#define WM9081_SPKPGA_MUTE_SHIFT 7 /* SPKPGA_MUTE */
+#define WM9081_SPKPGA_MUTE_WIDTH 1 /* SPKPGA_MUTE */
+#define WM9081_SPKPGAZC 0x0040 /* SPKPGAZC */
+#define WM9081_SPKPGAZC_MASK 0x0040 /* SPKPGAZC */
+#define WM9081_SPKPGAZC_SHIFT 6 /* SPKPGAZC */
+#define WM9081_SPKPGAZC_WIDTH 1 /* SPKPGAZC */
+#define WM9081_SPKPGA_VOL_MASK 0x003F /* SPKPGA_VOL - [5:0] */
+#define WM9081_SPKPGA_VOL_SHIFT 0 /* SPKPGA_VOL - [5:0] */
+#define WM9081_SPKPGA_VOL_WIDTH 6 /* SPKPGA_VOL - [5:0] */
+
+/*
+ * R4 (0x04) - VMID Control
+ */
+#define WM9081_VMID_BUF_ENA 0x0020 /* VMID_BUF_ENA */
+#define WM9081_VMID_BUF_ENA_MASK 0x0020 /* VMID_BUF_ENA */
+#define WM9081_VMID_BUF_ENA_SHIFT 5 /* VMID_BUF_ENA */
+#define WM9081_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */
+#define WM9081_VMID_RAMP 0x0008 /* VMID_RAMP */
+#define WM9081_VMID_RAMP_MASK 0x0008 /* VMID_RAMP */
+#define WM9081_VMID_RAMP_SHIFT 3 /* VMID_RAMP */
+#define WM9081_VMID_RAMP_WIDTH 1 /* VMID_RAMP */
+#define WM9081_VMID_SEL_MASK 0x0006 /* VMID_SEL - [2:1] */
+#define WM9081_VMID_SEL_SHIFT 1 /* VMID_SEL - [2:1] */
+#define WM9081_VMID_SEL_WIDTH 2 /* VMID_SEL - [2:1] */
+#define WM9081_VMID_FAST_ST 0x0001 /* VMID_FAST_ST */
+#define WM9081_VMID_FAST_ST_MASK 0x0001 /* VMID_FAST_ST */
+#define WM9081_VMID_FAST_ST_SHIFT 0 /* VMID_FAST_ST */
+#define WM9081_VMID_FAST_ST_WIDTH 1 /* VMID_FAST_ST */
+
+/*
+ * R5 (0x05) - Bias Control 1
+ */
+#define WM9081_BIAS_SRC 0x0040 /* BIAS_SRC */
+#define WM9081_BIAS_SRC_MASK 0x0040 /* BIAS_SRC */
+#define WM9081_BIAS_SRC_SHIFT 6 /* BIAS_SRC */
+#define WM9081_BIAS_SRC_WIDTH 1 /* BIAS_SRC */
+#define WM9081_STBY_BIAS_LVL 0x0020 /* STBY_BIAS_LVL */
+#define WM9081_STBY_BIAS_LVL_MASK 0x0020 /* STBY_BIAS_LVL */
+#define WM9081_STBY_BIAS_LVL_SHIFT 5 /* STBY_BIAS_LVL */
+#define WM9081_STBY_BIAS_LVL_WIDTH 1 /* STBY_BIAS_LVL */
+#define WM9081_STBY_BIAS_ENA 0x0010 /* STBY_BIAS_ENA */
+#define WM9081_STBY_BIAS_ENA_MASK 0x0010 /* STBY_BIAS_ENA */
+#define WM9081_STBY_BIAS_ENA_SHIFT 4 /* STBY_BIAS_ENA */
+#define WM9081_STBY_BIAS_ENA_WIDTH 1 /* STBY_BIAS_ENA */
+#define WM9081_BIAS_LVL_MASK 0x000C /* BIAS_LVL - [3:2] */
+#define WM9081_BIAS_LVL_SHIFT 2 /* BIAS_LVL - [3:2] */
+#define WM9081_BIAS_LVL_WIDTH 2 /* BIAS_LVL - [3:2] */
+#define WM9081_BIAS_ENA 0x0002 /* BIAS_ENA */
+#define WM9081_BIAS_ENA_MASK 0x0002 /* BIAS_ENA */
+#define WM9081_BIAS_ENA_SHIFT 1 /* BIAS_ENA */
+#define WM9081_BIAS_ENA_WIDTH 1 /* BIAS_ENA */
+#define WM9081_STARTUP_BIAS_ENA 0x0001 /* STARTUP_BIAS_ENA */
+#define WM9081_STARTUP_BIAS_ENA_MASK 0x0001 /* STARTUP_BIAS_ENA */
+#define WM9081_STARTUP_BIAS_ENA_SHIFT 0 /* STARTUP_BIAS_ENA */
+#define WM9081_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */
+
+/*
+ * R7 (0x07) - Analogue Mixer
+ */
+#define WM9081_DAC_SEL 0x0010 /* DAC_SEL */
+#define WM9081_DAC_SEL_MASK 0x0010 /* DAC_SEL */
+#define WM9081_DAC_SEL_SHIFT 4 /* DAC_SEL */
+#define WM9081_DAC_SEL_WIDTH 1 /* DAC_SEL */
+#define WM9081_IN2_VOL 0x0008 /* IN2_VOL */
+#define WM9081_IN2_VOL_MASK 0x0008 /* IN2_VOL */
+#define WM9081_IN2_VOL_SHIFT 3 /* IN2_VOL */
+#define WM9081_IN2_VOL_WIDTH 1 /* IN2_VOL */
+#define WM9081_IN2_ENA 0x0004 /* IN2_ENA */
+#define WM9081_IN2_ENA_MASK 0x0004 /* IN2_ENA */
+#define WM9081_IN2_ENA_SHIFT 2 /* IN2_ENA */
+#define WM9081_IN2_ENA_WIDTH 1 /* IN2_ENA */
+#define WM9081_IN1_VOL 0x0002 /* IN1_VOL */
+#define WM9081_IN1_VOL_MASK 0x0002 /* IN1_VOL */
+#define WM9081_IN1_VOL_SHIFT 1 /* IN1_VOL */
+#define WM9081_IN1_VOL_WIDTH 1 /* IN1_VOL */
+#define WM9081_IN1_ENA 0x0001 /* IN1_ENA */
+#define WM9081_IN1_ENA_MASK 0x0001 /* IN1_ENA */
+#define WM9081_IN1_ENA_SHIFT 0 /* IN1_ENA */
+#define WM9081_IN1_ENA_WIDTH 1 /* IN1_ENA */
+
+/*
+ * R8 (0x08) - Anti Pop Control
+ */
+#define WM9081_LINEOUT_DISCH 0x0004 /* LINEOUT_DISCH */
+#define WM9081_LINEOUT_DISCH_MASK 0x0004 /* LINEOUT_DISCH */
+#define WM9081_LINEOUT_DISCH_SHIFT 2 /* LINEOUT_DISCH */
+#define WM9081_LINEOUT_DISCH_WIDTH 1 /* LINEOUT_DISCH */
+#define WM9081_LINEOUT_VROI 0x0002 /* LINEOUT_VROI */
+#define WM9081_LINEOUT_VROI_MASK 0x0002 /* LINEOUT_VROI */
+#define WM9081_LINEOUT_VROI_SHIFT 1 /* LINEOUT_VROI */
+#define WM9081_LINEOUT_VROI_WIDTH 1 /* LINEOUT_VROI */
+#define WM9081_LINEOUT_CLAMP 0x0001 /* LINEOUT_CLAMP */
+#define WM9081_LINEOUT_CLAMP_MASK 0x0001 /* LINEOUT_CLAMP */
+#define WM9081_LINEOUT_CLAMP_SHIFT 0 /* LINEOUT_CLAMP */
+#define WM9081_LINEOUT_CLAMP_WIDTH 1 /* LINEOUT_CLAMP */
+
+/*
+ * R9 (0x09) - Analogue Speaker 1
+ */
+#define WM9081_SPK_DCGAIN_MASK 0x0038 /* SPK_DCGAIN - [5:3] */
+#define WM9081_SPK_DCGAIN_SHIFT 3 /* SPK_DCGAIN - [5:3] */
+#define WM9081_SPK_DCGAIN_WIDTH 3 /* SPK_DCGAIN - [5:3] */
+#define WM9081_SPK_ACGAIN_MASK 0x0007 /* SPK_ACGAIN - [2:0] */
+#define WM9081_SPK_ACGAIN_SHIFT 0 /* SPK_ACGAIN - [2:0] */
+#define WM9081_SPK_ACGAIN_WIDTH 3 /* SPK_ACGAIN - [2:0] */
+
+/*
+ * R10 (0x0A) - Analogue Speaker 2
+ */
+#define WM9081_SPK_MODE 0x0040 /* SPK_MODE */
+#define WM9081_SPK_MODE_MASK 0x0040 /* SPK_MODE */
+#define WM9081_SPK_MODE_SHIFT 6 /* SPK_MODE */
+#define WM9081_SPK_MODE_WIDTH 1 /* SPK_MODE */
+#define WM9081_SPK_INV_MUTE 0x0010 /* SPK_INV_MUTE */
+#define WM9081_SPK_INV_MUTE_MASK 0x0010 /* SPK_INV_MUTE */
+#define WM9081_SPK_INV_MUTE_SHIFT 4 /* SPK_INV_MUTE */
+#define WM9081_SPK_INV_MUTE_WIDTH 1 /* SPK_INV_MUTE */
+#define WM9081_OUT_SPK_CTRL 0x0008 /* OUT_SPK_CTRL */
+#define WM9081_OUT_SPK_CTRL_MASK 0x0008 /* OUT_SPK_CTRL */
+#define WM9081_OUT_SPK_CTRL_SHIFT 3 /* OUT_SPK_CTRL */
+#define WM9081_OUT_SPK_CTRL_WIDTH 1 /* OUT_SPK_CTRL */
+
+/*
+ * R11 (0x0B) - Power Management
+ */
+#define WM9081_TSHUT_ENA 0x0100 /* TSHUT_ENA */
+#define WM9081_TSHUT_ENA_MASK 0x0100 /* TSHUT_ENA */
+#define WM9081_TSHUT_ENA_SHIFT 8 /* TSHUT_ENA */
+#define WM9081_TSHUT_ENA_WIDTH 1 /* TSHUT_ENA */
+#define WM9081_TSENSE_ENA 0x0080 /* TSENSE_ENA */
+#define WM9081_TSENSE_ENA_MASK 0x0080 /* TSENSE_ENA */
+#define WM9081_TSENSE_ENA_SHIFT 7 /* TSENSE_ENA */
+#define WM9081_TSENSE_ENA_WIDTH 1 /* TSENSE_ENA */
+#define WM9081_TEMP_SHUT 0x0040 /* TEMP_SHUT */
+#define WM9081_TEMP_SHUT_MASK 0x0040 /* TEMP_SHUT */
+#define WM9081_TEMP_SHUT_SHIFT 6 /* TEMP_SHUT */
+#define WM9081_TEMP_SHUT_WIDTH 1 /* TEMP_SHUT */
+#define WM9081_LINEOUT_ENA 0x0010 /* LINEOUT_ENA */
+#define WM9081_LINEOUT_ENA_MASK 0x0010 /* LINEOUT_ENA */
+#define WM9081_LINEOUT_ENA_SHIFT 4 /* LINEOUT_ENA */
+#define WM9081_LINEOUT_ENA_WIDTH 1 /* LINEOUT_ENA */
+#define WM9081_SPKPGA_ENA 0x0004 /* SPKPGA_ENA */
+#define WM9081_SPKPGA_ENA_MASK 0x0004 /* SPKPGA_ENA */
+#define WM9081_SPKPGA_ENA_SHIFT 2 /* SPKPGA_ENA */
+#define WM9081_SPKPGA_ENA_WIDTH 1 /* SPKPGA_ENA */
+#define WM9081_SPK_ENA 0x0002 /* SPK_ENA */
+#define WM9081_SPK_ENA_MASK 0x0002 /* SPK_ENA */
+#define WM9081_SPK_ENA_SHIFT 1 /* SPK_ENA */
+#define WM9081_SPK_ENA_WIDTH 1 /* SPK_ENA */
+#define WM9081_DAC_ENA 0x0001 /* DAC_ENA */
+#define WM9081_DAC_ENA_MASK 0x0001 /* DAC_ENA */
+#define WM9081_DAC_ENA_SHIFT 0 /* DAC_ENA */
+#define WM9081_DAC_ENA_WIDTH 1 /* DAC_ENA */
+
+/*
+ * R12 (0x0C) - Clock Control 1
+ */
+#define WM9081_CLK_OP_DIV_MASK 0x1C00 /* CLK_OP_DIV - [12:10] */
+#define WM9081_CLK_OP_DIV_SHIFT 10 /* CLK_OP_DIV - [12:10] */
+#define WM9081_CLK_OP_DIV_WIDTH 3 /* CLK_OP_DIV - [12:10] */
+#define WM9081_CLK_TO_DIV_MASK 0x0300 /* CLK_TO_DIV - [9:8] */
+#define WM9081_CLK_TO_DIV_SHIFT 8 /* CLK_TO_DIV - [9:8] */
+#define WM9081_CLK_TO_DIV_WIDTH 2 /* CLK_TO_DIV - [9:8] */
+#define WM9081_MCLKDIV2 0x0080 /* MCLKDIV2 */
+#define WM9081_MCLKDIV2_MASK 0x0080 /* MCLKDIV2 */
+#define WM9081_MCLKDIV2_SHIFT 7 /* MCLKDIV2 */
+#define WM9081_MCLKDIV2_WIDTH 1 /* MCLKDIV2 */
+
+/*
+ * R13 (0x0D) - Clock Control 2
+ */
+#define WM9081_CLK_SYS_RATE_MASK 0x00F0 /* CLK_SYS_RATE - [7:4] */
+#define WM9081_CLK_SYS_RATE_SHIFT 4 /* CLK_SYS_RATE - [7:4] */
+#define WM9081_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [7:4] */
+#define WM9081_SAMPLE_RATE_MASK 0x000F /* SAMPLE_RATE - [3:0] */
+#define WM9081_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [3:0] */
+#define WM9081_SAMPLE_RATE_WIDTH 4 /* SAMPLE_RATE - [3:0] */
+
+/*
+ * R14 (0x0E) - Clock Control 3
+ */
+#define WM9081_CLK_SRC_SEL 0x2000 /* CLK_SRC_SEL */
+#define WM9081_CLK_SRC_SEL_MASK 0x2000 /* CLK_SRC_SEL */
+#define WM9081_CLK_SRC_SEL_SHIFT 13 /* CLK_SRC_SEL */
+#define WM9081_CLK_SRC_SEL_WIDTH 1 /* CLK_SRC_SEL */
+#define WM9081_CLK_OP_ENA 0x0020 /* CLK_OP_ENA */
+#define WM9081_CLK_OP_ENA_MASK 0x0020 /* CLK_OP_ENA */
+#define WM9081_CLK_OP_ENA_SHIFT 5 /* CLK_OP_ENA */
+#define WM9081_CLK_OP_ENA_WIDTH 1 /* CLK_OP_ENA */
+#define WM9081_CLK_TO_ENA 0x0004 /* CLK_TO_ENA */
+#define WM9081_CLK_TO_ENA_MASK 0x0004 /* CLK_TO_ENA */
+#define WM9081_CLK_TO_ENA_SHIFT 2 /* CLK_TO_ENA */
+#define WM9081_CLK_TO_ENA_WIDTH 1 /* CLK_TO_ENA */
+#define WM9081_CLK_DSP_ENA 0x0002 /* CLK_DSP_ENA */
+#define WM9081_CLK_DSP_ENA_MASK 0x0002 /* CLK_DSP_ENA */
+#define WM9081_CLK_DSP_ENA_SHIFT 1 /* CLK_DSP_ENA */
+#define WM9081_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */
+#define WM9081_CLK_SYS_ENA 0x0001 /* CLK_SYS_ENA */
+#define WM9081_CLK_SYS_ENA_MASK 0x0001 /* CLK_SYS_ENA */
+#define WM9081_CLK_SYS_ENA_SHIFT 0 /* CLK_SYS_ENA */
+#define WM9081_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */
+
+/*
+ * R16 (0x10) - FLL Control 1
+ */
+#define WM9081_FLL_HOLD 0x0008 /* FLL_HOLD */
+#define WM9081_FLL_HOLD_MASK 0x0008 /* FLL_HOLD */
+#define WM9081_FLL_HOLD_SHIFT 3 /* FLL_HOLD */
+#define WM9081_FLL_HOLD_WIDTH 1 /* FLL_HOLD */
+#define WM9081_FLL_FRAC 0x0004 /* FLL_FRAC */
+#define WM9081_FLL_FRAC_MASK 0x0004 /* FLL_FRAC */
+#define WM9081_FLL_FRAC_SHIFT 2 /* FLL_FRAC */
+#define WM9081_FLL_FRAC_WIDTH 1 /* FLL_FRAC */
+#define WM9081_FLL_ENA 0x0001 /* FLL_ENA */
+#define WM9081_FLL_ENA_MASK 0x0001 /* FLL_ENA */
+#define WM9081_FLL_ENA_SHIFT 0 /* FLL_ENA */
+#define WM9081_FLL_ENA_WIDTH 1 /* FLL_ENA */
+
+/*
+ * R17 (0x11) - FLL Control 2
+ */
+#define WM9081_FLL_OUTDIV_MASK 0x0700 /* FLL_OUTDIV - [10:8] */
+#define WM9081_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [10:8] */
+#define WM9081_FLL_OUTDIV_WIDTH 3 /* FLL_OUTDIV - [10:8] */
+#define WM9081_FLL_CTRL_RATE_MASK 0x0070 /* FLL_CTRL_RATE - [6:4] */
+#define WM9081_FLL_CTRL_RATE_SHIFT 4 /* FLL_CTRL_RATE - [6:4] */
+#define WM9081_FLL_CTRL_RATE_WIDTH 3 /* FLL_CTRL_RATE - [6:4] */
+#define WM9081_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
+#define WM9081_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
+#define WM9081_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
+
+/*
+ * R18 (0x12) - FLL Control 3
+ */
+#define WM9081_FLL_K_MASK 0xFFFF /* FLL_K - [15:0] */
+#define WM9081_FLL_K_SHIFT 0 /* FLL_K - [15:0] */
+#define WM9081_FLL_K_WIDTH 16 /* FLL_K - [15:0] */
+
+/*
+ * R19 (0x13) - FLL Control 4
+ */
+#define WM9081_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */
+#define WM9081_FLL_N_SHIFT 5 /* FLL_N - [14:5] */
+#define WM9081_FLL_N_WIDTH 10 /* FLL_N - [14:5] */
+#define WM9081_FLL_GAIN_MASK 0x000F /* FLL_GAIN - [3:0] */
+#define WM9081_FLL_GAIN_SHIFT 0 /* FLL_GAIN - [3:0] */
+#define WM9081_FLL_GAIN_WIDTH 4 /* FLL_GAIN - [3:0] */
+
+/*
+ * R20 (0x14) - FLL Control 5
+ */
+#define WM9081_FLL_CLK_REF_DIV_MASK 0x0018 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM9081_FLL_CLK_REF_DIV_SHIFT 3 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM9081_FLL_CLK_REF_DIV_WIDTH 2 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM9081_FLL_CLK_SRC_MASK 0x0003 /* FLL_CLK_SRC - [1:0] */
+#define WM9081_FLL_CLK_SRC_SHIFT 0 /* FLL_CLK_SRC - [1:0] */
+#define WM9081_FLL_CLK_SRC_WIDTH 2 /* FLL_CLK_SRC - [1:0] */
+
+/*
+ * R22 (0x16) - Audio Interface 1
+ */
+#define WM9081_AIFDAC_CHAN 0x0040 /* AIFDAC_CHAN */
+#define WM9081_AIFDAC_CHAN_MASK 0x0040 /* AIFDAC_CHAN */
+#define WM9081_AIFDAC_CHAN_SHIFT 6 /* AIFDAC_CHAN */
+#define WM9081_AIFDAC_CHAN_WIDTH 1 /* AIFDAC_CHAN */
+#define WM9081_AIFDAC_TDM_SLOT_MASK 0x0030 /* AIFDAC_TDM_SLOT - [5:4] */
+#define WM9081_AIFDAC_TDM_SLOT_SHIFT 4 /* AIFDAC_TDM_SLOT - [5:4] */
+#define WM9081_AIFDAC_TDM_SLOT_WIDTH 2 /* AIFDAC_TDM_SLOT - [5:4] */
+#define WM9081_AIFDAC_TDM_MODE_MASK 0x000C /* AIFDAC_TDM_MODE - [3:2] */
+#define WM9081_AIFDAC_TDM_MODE_SHIFT 2 /* AIFDAC_TDM_MODE - [3:2] */
+#define WM9081_AIFDAC_TDM_MODE_WIDTH 2 /* AIFDAC_TDM_MODE - [3:2] */
+#define WM9081_DAC_COMP 0x0002 /* DAC_COMP */
+#define WM9081_DAC_COMP_MASK 0x0002 /* DAC_COMP */
+#define WM9081_DAC_COMP_SHIFT 1 /* DAC_COMP */
+#define WM9081_DAC_COMP_WIDTH 1 /* DAC_COMP */
+#define WM9081_DAC_COMPMODE 0x0001 /* DAC_COMPMODE */
+#define WM9081_DAC_COMPMODE_MASK 0x0001 /* DAC_COMPMODE */
+#define WM9081_DAC_COMPMODE_SHIFT 0 /* DAC_COMPMODE */
+#define WM9081_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */
+
+/*
+ * R23 (0x17) - Audio Interface 2
+ */
+#define WM9081_AIF_TRIS 0x0200 /* AIF_TRIS */
+#define WM9081_AIF_TRIS_MASK 0x0200 /* AIF_TRIS */
+#define WM9081_AIF_TRIS_SHIFT 9 /* AIF_TRIS */
+#define WM9081_AIF_TRIS_WIDTH 1 /* AIF_TRIS */
+#define WM9081_DAC_DAT_INV 0x0100 /* DAC_DAT_INV */
+#define WM9081_DAC_DAT_INV_MASK 0x0100 /* DAC_DAT_INV */
+#define WM9081_DAC_DAT_INV_SHIFT 8 /* DAC_DAT_INV */
+#define WM9081_DAC_DAT_INV_WIDTH 1 /* DAC_DAT_INV */
+#define WM9081_AIF_BCLK_INV 0x0080 /* AIF_BCLK_INV */
+#define WM9081_AIF_BCLK_INV_MASK 0x0080 /* AIF_BCLK_INV */
+#define WM9081_AIF_BCLK_INV_SHIFT 7 /* AIF_BCLK_INV */
+#define WM9081_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */
+#define WM9081_BCLK_DIR 0x0040 /* BCLK_DIR */
+#define WM9081_BCLK_DIR_MASK 0x0040 /* BCLK_DIR */
+#define WM9081_BCLK_DIR_SHIFT 6 /* BCLK_DIR */
+#define WM9081_BCLK_DIR_WIDTH 1 /* BCLK_DIR */
+#define WM9081_LRCLK_DIR 0x0020 /* LRCLK_DIR */
+#define WM9081_LRCLK_DIR_MASK 0x0020 /* LRCLK_DIR */
+#define WM9081_LRCLK_DIR_SHIFT 5 /* LRCLK_DIR */
+#define WM9081_LRCLK_DIR_WIDTH 1 /* LRCLK_DIR */
+#define WM9081_AIF_LRCLK_INV 0x0010 /* AIF_LRCLK_INV */
+#define WM9081_AIF_LRCLK_INV_MASK 0x0010 /* AIF_LRCLK_INV */
+#define WM9081_AIF_LRCLK_INV_SHIFT 4 /* AIF_LRCLK_INV */
+#define WM9081_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */
+#define WM9081_AIF_WL_MASK 0x000C /* AIF_WL - [3:2] */
+#define WM9081_AIF_WL_SHIFT 2 /* AIF_WL - [3:2] */
+#define WM9081_AIF_WL_WIDTH 2 /* AIF_WL - [3:2] */
+#define WM9081_AIF_FMT_MASK 0x0003 /* AIF_FMT - [1:0] */
+#define WM9081_AIF_FMT_SHIFT 0 /* AIF_FMT - [1:0] */
+#define WM9081_AIF_FMT_WIDTH 2 /* AIF_FMT - [1:0] */
+
+/*
+ * R24 (0x18) - Audio Interface 3
+ */
+#define WM9081_BCLK_DIV_MASK 0x001F /* BCLK_DIV - [4:0] */
+#define WM9081_BCLK_DIV_SHIFT 0 /* BCLK_DIV - [4:0] */
+#define WM9081_BCLK_DIV_WIDTH 5 /* BCLK_DIV - [4:0] */
+
+/*
+ * R25 (0x19) - Audio Interface 4
+ */
+#define WM9081_LRCLK_RATE_MASK 0x07FF /* LRCLK_RATE - [10:0] */
+#define WM9081_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [10:0] */
+#define WM9081_LRCLK_RATE_WIDTH 11 /* LRCLK_RATE - [10:0] */
+
+/*
+ * R26 (0x1A) - Interrupt Status
+ */
+#define WM9081_WSEQ_BUSY_EINT 0x0004 /* WSEQ_BUSY_EINT */
+#define WM9081_WSEQ_BUSY_EINT_MASK 0x0004 /* WSEQ_BUSY_EINT */
+#define WM9081_WSEQ_BUSY_EINT_SHIFT 2 /* WSEQ_BUSY_EINT */
+#define WM9081_WSEQ_BUSY_EINT_WIDTH 1 /* WSEQ_BUSY_EINT */
+#define WM9081_TSHUT_EINT 0x0001 /* TSHUT_EINT */
+#define WM9081_TSHUT_EINT_MASK 0x0001 /* TSHUT_EINT */
+#define WM9081_TSHUT_EINT_SHIFT 0 /* TSHUT_EINT */
+#define WM9081_TSHUT_EINT_WIDTH 1 /* TSHUT_EINT */
+
+/*
+ * R27 (0x1B) - Interrupt Status Mask
+ */
+#define WM9081_IM_WSEQ_BUSY_EINT 0x0004 /* IM_WSEQ_BUSY_EINT */
+#define WM9081_IM_WSEQ_BUSY_EINT_MASK 0x0004 /* IM_WSEQ_BUSY_EINT */
+#define WM9081_IM_WSEQ_BUSY_EINT_SHIFT 2 /* IM_WSEQ_BUSY_EINT */
+#define WM9081_IM_WSEQ_BUSY_EINT_WIDTH 1 /* IM_WSEQ_BUSY_EINT */
+#define WM9081_IM_TSHUT_EINT 0x0001 /* IM_TSHUT_EINT */
+#define WM9081_IM_TSHUT_EINT_MASK 0x0001 /* IM_TSHUT_EINT */
+#define WM9081_IM_TSHUT_EINT_SHIFT 0 /* IM_TSHUT_EINT */
+#define WM9081_IM_TSHUT_EINT_WIDTH 1 /* IM_TSHUT_EINT */
+
+/*
+ * R28 (0x1C) - Interrupt Polarity
+ */
+#define WM9081_TSHUT_INV 0x0001 /* TSHUT_INV */
+#define WM9081_TSHUT_INV_MASK 0x0001 /* TSHUT_INV */
+#define WM9081_TSHUT_INV_SHIFT 0 /* TSHUT_INV */
+#define WM9081_TSHUT_INV_WIDTH 1 /* TSHUT_INV */
+
+/*
+ * R29 (0x1D) - Interrupt Control
+ */
+#define WM9081_IRQ_POL 0x8000 /* IRQ_POL */
+#define WM9081_IRQ_POL_MASK 0x8000 /* IRQ_POL */
+#define WM9081_IRQ_POL_SHIFT 15 /* IRQ_POL */
+#define WM9081_IRQ_POL_WIDTH 1 /* IRQ_POL */
+#define WM9081_IRQ_OP_CTRL 0x0001 /* IRQ_OP_CTRL */
+#define WM9081_IRQ_OP_CTRL_MASK 0x0001 /* IRQ_OP_CTRL */
+#define WM9081_IRQ_OP_CTRL_SHIFT 0 /* IRQ_OP_CTRL */
+#define WM9081_IRQ_OP_CTRL_WIDTH 1 /* IRQ_OP_CTRL */
+
+/*
+ * R30 (0x1E) - DAC Digital 1
+ */
+#define WM9081_DAC_VOL_MASK 0x00FF /* DAC_VOL - [7:0] */
+#define WM9081_DAC_VOL_SHIFT 0 /* DAC_VOL - [7:0] */
+#define WM9081_DAC_VOL_WIDTH 8 /* DAC_VOL - [7:0] */
+
+/*
+ * R31 (0x1F) - DAC Digital 2
+ */
+#define WM9081_DAC_MUTERATE 0x0400 /* DAC_MUTERATE */
+#define WM9081_DAC_MUTERATE_MASK 0x0400 /* DAC_MUTERATE */
+#define WM9081_DAC_MUTERATE_SHIFT 10 /* DAC_MUTERATE */
+#define WM9081_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
+#define WM9081_DAC_MUTEMODE 0x0200 /* DAC_MUTEMODE */
+#define WM9081_DAC_MUTEMODE_MASK 0x0200 /* DAC_MUTEMODE */
+#define WM9081_DAC_MUTEMODE_SHIFT 9 /* DAC_MUTEMODE */
+#define WM9081_DAC_MUTEMODE_WIDTH 1 /* DAC_MUTEMODE */
+#define WM9081_DAC_MUTE 0x0008 /* DAC_MUTE */
+#define WM9081_DAC_MUTE_MASK 0x0008 /* DAC_MUTE */
+#define WM9081_DAC_MUTE_SHIFT 3 /* DAC_MUTE */
+#define WM9081_DAC_MUTE_WIDTH 1 /* DAC_MUTE */
+#define WM9081_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */
+#define WM9081_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */
+#define WM9081_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */
+
+/*
+ * R32 (0x20) - DRC 1
+ */
+#define WM9081_DRC_ENA 0x8000 /* DRC_ENA */
+#define WM9081_DRC_ENA_MASK 0x8000 /* DRC_ENA */
+#define WM9081_DRC_ENA_SHIFT 15 /* DRC_ENA */
+#define WM9081_DRC_ENA_WIDTH 1 /* DRC_ENA */
+#define WM9081_DRC_STARTUP_GAIN_MASK 0x07C0 /* DRC_STARTUP_GAIN - [10:6] */
+#define WM9081_DRC_STARTUP_GAIN_SHIFT 6 /* DRC_STARTUP_GAIN - [10:6] */
+#define WM9081_DRC_STARTUP_GAIN_WIDTH 5 /* DRC_STARTUP_GAIN - [10:6] */
+#define WM9081_DRC_FF_DLY 0x0020 /* DRC_FF_DLY */
+#define WM9081_DRC_FF_DLY_MASK 0x0020 /* DRC_FF_DLY */
+#define WM9081_DRC_FF_DLY_SHIFT 5 /* DRC_FF_DLY */
+#define WM9081_DRC_FF_DLY_WIDTH 1 /* DRC_FF_DLY */
+#define WM9081_DRC_QR 0x0004 /* DRC_QR */
+#define WM9081_DRC_QR_MASK 0x0004 /* DRC_QR */
+#define WM9081_DRC_QR_SHIFT 2 /* DRC_QR */
+#define WM9081_DRC_QR_WIDTH 1 /* DRC_QR */
+#define WM9081_DRC_ANTICLIP 0x0002 /* DRC_ANTICLIP */
+#define WM9081_DRC_ANTICLIP_MASK 0x0002 /* DRC_ANTICLIP */
+#define WM9081_DRC_ANTICLIP_SHIFT 1 /* DRC_ANTICLIP */
+#define WM9081_DRC_ANTICLIP_WIDTH 1 /* DRC_ANTICLIP */
+
+/*
+ * R33 (0x21) - DRC 2
+ */
+#define WM9081_DRC_ATK_MASK 0xF000 /* DRC_ATK - [15:12] */
+#define WM9081_DRC_ATK_SHIFT 12 /* DRC_ATK - [15:12] */
+#define WM9081_DRC_ATK_WIDTH 4 /* DRC_ATK - [15:12] */
+#define WM9081_DRC_DCY_MASK 0x0F00 /* DRC_DCY - [11:8] */
+#define WM9081_DRC_DCY_SHIFT 8 /* DRC_DCY - [11:8] */
+#define WM9081_DRC_DCY_WIDTH 4 /* DRC_DCY - [11:8] */
+#define WM9081_DRC_QR_THR_MASK 0x00C0 /* DRC_QR_THR - [7:6] */
+#define WM9081_DRC_QR_THR_SHIFT 6 /* DRC_QR_THR - [7:6] */
+#define WM9081_DRC_QR_THR_WIDTH 2 /* DRC_QR_THR - [7:6] */
+#define WM9081_DRC_QR_DCY_MASK 0x0030 /* DRC_QR_DCY - [5:4] */
+#define WM9081_DRC_QR_DCY_SHIFT 4 /* DRC_QR_DCY - [5:4] */
+#define WM9081_DRC_QR_DCY_WIDTH 2 /* DRC_QR_DCY - [5:4] */
+#define WM9081_DRC_MINGAIN_MASK 0x000C /* DRC_MINGAIN - [3:2] */
+#define WM9081_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [3:2] */
+#define WM9081_DRC_MINGAIN_WIDTH 2 /* DRC_MINGAIN - [3:2] */
+#define WM9081_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */
+#define WM9081_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */
+#define WM9081_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R34 (0x22) - DRC 3
+ */
+#define WM9081_DRC_HI_COMP_MASK 0x0038 /* DRC_HI_COMP - [5:3] */
+#define WM9081_DRC_HI_COMP_SHIFT 3 /* DRC_HI_COMP - [5:3] */
+#define WM9081_DRC_HI_COMP_WIDTH 3 /* DRC_HI_COMP - [5:3] */
+#define WM9081_DRC_LO_COMP_MASK 0x0007 /* DRC_LO_COMP - [2:0] */
+#define WM9081_DRC_LO_COMP_SHIFT 0 /* DRC_LO_COMP - [2:0] */
+#define WM9081_DRC_LO_COMP_WIDTH 3 /* DRC_LO_COMP - [2:0] */
+
+/*
+ * R35 (0x23) - DRC 4
+ */
+#define WM9081_DRC_KNEE_IP_MASK 0x07E0 /* DRC_KNEE_IP - [10:5] */
+#define WM9081_DRC_KNEE_IP_SHIFT 5 /* DRC_KNEE_IP - [10:5] */
+#define WM9081_DRC_KNEE_IP_WIDTH 6 /* DRC_KNEE_IP - [10:5] */
+#define WM9081_DRC_KNEE_OP_MASK 0x001F /* DRC_KNEE_OP - [4:0] */
+#define WM9081_DRC_KNEE_OP_SHIFT 0 /* DRC_KNEE_OP - [4:0] */
+#define WM9081_DRC_KNEE_OP_WIDTH 5 /* DRC_KNEE_OP - [4:0] */
+
+/*
+ * R38 (0x26) - Write Sequencer 1
+ */
+#define WM9081_WSEQ_ENA 0x8000 /* WSEQ_ENA */
+#define WM9081_WSEQ_ENA_MASK 0x8000 /* WSEQ_ENA */
+#define WM9081_WSEQ_ENA_SHIFT 15 /* WSEQ_ENA */
+#define WM9081_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM9081_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM9081_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM9081_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM9081_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM9081_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM9081_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM9081_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM9081_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM9081_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */
+#define WM9081_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */
+#define WM9081_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */
+
+/*
+ * R39 (0x27) - Write Sequencer 2
+ */
+#define WM9081_WSEQ_CURRENT_INDEX_MASK 0x07F0 /* WSEQ_CURRENT_INDEX - [10:4] */
+#define WM9081_WSEQ_CURRENT_INDEX_SHIFT 4 /* WSEQ_CURRENT_INDEX - [10:4] */
+#define WM9081_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [10:4] */
+#define WM9081_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */
+#define WM9081_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */
+#define WM9081_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */
+#define WM9081_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+
+/*
+ * R40 (0x28) - MW Slave 1
+ */
+#define WM9081_SPI_CFG 0x0020 /* SPI_CFG */
+#define WM9081_SPI_CFG_MASK 0x0020 /* SPI_CFG */
+#define WM9081_SPI_CFG_SHIFT 5 /* SPI_CFG */
+#define WM9081_SPI_CFG_WIDTH 1 /* SPI_CFG */
+#define WM9081_SPI_4WIRE 0x0010 /* SPI_4WIRE */
+#define WM9081_SPI_4WIRE_MASK 0x0010 /* SPI_4WIRE */
+#define WM9081_SPI_4WIRE_SHIFT 4 /* SPI_4WIRE */
+#define WM9081_SPI_4WIRE_WIDTH 1 /* SPI_4WIRE */
+#define WM9081_ARA_ENA 0x0008 /* ARA_ENA */
+#define WM9081_ARA_ENA_MASK 0x0008 /* ARA_ENA */
+#define WM9081_ARA_ENA_SHIFT 3 /* ARA_ENA */
+#define WM9081_ARA_ENA_WIDTH 1 /* ARA_ENA */
+#define WM9081_AUTO_INC 0x0002 /* AUTO_INC */
+#define WM9081_AUTO_INC_MASK 0x0002 /* AUTO_INC */
+#define WM9081_AUTO_INC_SHIFT 1 /* AUTO_INC */
+#define WM9081_AUTO_INC_WIDTH 1 /* AUTO_INC */
+
+/*
+ * R42 (0x2A) - EQ 1
+ */
+#define WM9081_EQ_B1_GAIN_MASK 0xF800 /* EQ_B1_GAIN - [15:11] */
+#define WM9081_EQ_B1_GAIN_SHIFT 11 /* EQ_B1_GAIN - [15:11] */
+#define WM9081_EQ_B1_GAIN_WIDTH 5 /* EQ_B1_GAIN - [15:11] */
+#define WM9081_EQ_B2_GAIN_MASK 0x07C0 /* EQ_B2_GAIN - [10:6] */
+#define WM9081_EQ_B2_GAIN_SHIFT 6 /* EQ_B2_GAIN - [10:6] */
+#define WM9081_EQ_B2_GAIN_WIDTH 5 /* EQ_B2_GAIN - [10:6] */
+#define WM9081_EQ_B4_GAIN_MASK 0x003E /* EQ_B4_GAIN - [5:1] */
+#define WM9081_EQ_B4_GAIN_SHIFT 1 /* EQ_B4_GAIN - [5:1] */
+#define WM9081_EQ_B4_GAIN_WIDTH 5 /* EQ_B4_GAIN - [5:1] */
+#define WM9081_EQ_ENA 0x0001 /* EQ_ENA */
+#define WM9081_EQ_ENA_MASK 0x0001 /* EQ_ENA */
+#define WM9081_EQ_ENA_SHIFT 0 /* EQ_ENA */
+#define WM9081_EQ_ENA_WIDTH 1 /* EQ_ENA */
+
+/*
+ * R43 (0x2B) - EQ 2
+ */
+#define WM9081_EQ_B3_GAIN_MASK 0xF800 /* EQ_B3_GAIN - [15:11] */
+#define WM9081_EQ_B3_GAIN_SHIFT 11 /* EQ_B3_GAIN - [15:11] */
+#define WM9081_EQ_B3_GAIN_WIDTH 5 /* EQ_B3_GAIN - [15:11] */
+#define WM9081_EQ_B5_GAIN_MASK 0x07C0 /* EQ_B5_GAIN - [10:6] */
+#define WM9081_EQ_B5_GAIN_SHIFT 6 /* EQ_B5_GAIN - [10:6] */
+#define WM9081_EQ_B5_GAIN_WIDTH 5 /* EQ_B5_GAIN - [10:6] */
+
+/*
+ * R44 (0x2C) - EQ 3
+ */
+#define WM9081_EQ_B1_A_MASK 0xFFFF /* EQ_B1_A - [15:0] */
+#define WM9081_EQ_B1_A_SHIFT 0 /* EQ_B1_A - [15:0] */
+#define WM9081_EQ_B1_A_WIDTH 16 /* EQ_B1_A - [15:0] */
+
+/*
+ * R45 (0x2D) - EQ 4
+ */
+#define WM9081_EQ_B1_B_MASK 0xFFFF /* EQ_B1_B - [15:0] */
+#define WM9081_EQ_B1_B_SHIFT 0 /* EQ_B1_B - [15:0] */
+#define WM9081_EQ_B1_B_WIDTH 16 /* EQ_B1_B - [15:0] */
+
+/*
+ * R46 (0x2E) - EQ 5
+ */
+#define WM9081_EQ_B1_PG_MASK 0xFFFF /* EQ_B1_PG - [15:0] */
+#define WM9081_EQ_B1_PG_SHIFT 0 /* EQ_B1_PG - [15:0] */
+#define WM9081_EQ_B1_PG_WIDTH 16 /* EQ_B1_PG - [15:0] */
+
+/*
+ * R47 (0x2F) - EQ 6
+ */
+#define WM9081_EQ_B2_A_MASK 0xFFFF /* EQ_B2_A - [15:0] */
+#define WM9081_EQ_B2_A_SHIFT 0 /* EQ_B2_A - [15:0] */
+#define WM9081_EQ_B2_A_WIDTH 16 /* EQ_B2_A - [15:0] */
+
+/*
+ * R48 (0x30) - EQ 7
+ */
+#define WM9081_EQ_B2_B_MASK 0xFFFF /* EQ_B2_B - [15:0] */
+#define WM9081_EQ_B2_B_SHIFT 0 /* EQ_B2_B - [15:0] */
+#define WM9081_EQ_B2_B_WIDTH 16 /* EQ_B2_B - [15:0] */
+
+/*
+ * R49 (0x31) - EQ 8
+ */
+#define WM9081_EQ_B2_C_MASK 0xFFFF /* EQ_B2_C - [15:0] */
+#define WM9081_EQ_B2_C_SHIFT 0 /* EQ_B2_C - [15:0] */
+#define WM9081_EQ_B2_C_WIDTH 16 /* EQ_B2_C - [15:0] */
+
+/*
+ * R50 (0x32) - EQ 9
+ */
+#define WM9081_EQ_B2_PG_MASK 0xFFFF /* EQ_B2_PG - [15:0] */
+#define WM9081_EQ_B2_PG_SHIFT 0 /* EQ_B2_PG - [15:0] */
+#define WM9081_EQ_B2_PG_WIDTH 16 /* EQ_B2_PG - [15:0] */
+
+/*
+ * R51 (0x33) - EQ 10
+ */
+#define WM9081_EQ_B4_A_MASK 0xFFFF /* EQ_B4_A - [15:0] */
+#define WM9081_EQ_B4_A_SHIFT 0 /* EQ_B4_A - [15:0] */
+#define WM9081_EQ_B4_A_WIDTH 16 /* EQ_B4_A - [15:0] */
+
+/*
+ * R52 (0x34) - EQ 11
+ */
+#define WM9081_EQ_B4_B_MASK 0xFFFF /* EQ_B4_B - [15:0] */
+#define WM9081_EQ_B4_B_SHIFT 0 /* EQ_B4_B - [15:0] */
+#define WM9081_EQ_B4_B_WIDTH 16 /* EQ_B4_B - [15:0] */
+
+/*
+ * R53 (0x35) - EQ 12
+ */
+#define WM9081_EQ_B4_C_MASK 0xFFFF /* EQ_B4_C - [15:0] */
+#define WM9081_EQ_B4_C_SHIFT 0 /* EQ_B4_C - [15:0] */
+#define WM9081_EQ_B4_C_WIDTH 16 /* EQ_B4_C - [15:0] */
+
+/*
+ * R54 (0x36) - EQ 13
+ */
+#define WM9081_EQ_B4_PG_MASK 0xFFFF /* EQ_B4_PG - [15:0] */
+#define WM9081_EQ_B4_PG_SHIFT 0 /* EQ_B4_PG - [15:0] */
+#define WM9081_EQ_B4_PG_WIDTH 16 /* EQ_B4_PG - [15:0] */
+
+/*
+ * R55 (0x37) - EQ 14
+ */
+#define WM9081_EQ_B3_A_MASK 0xFFFF /* EQ_B3_A - [15:0] */
+#define WM9081_EQ_B3_A_SHIFT 0 /* EQ_B3_A - [15:0] */
+#define WM9081_EQ_B3_A_WIDTH 16 /* EQ_B3_A - [15:0] */
+
+/*
+ * R56 (0x38) - EQ 15
+ */
+#define WM9081_EQ_B3_B_MASK 0xFFFF /* EQ_B3_B - [15:0] */
+#define WM9081_EQ_B3_B_SHIFT 0 /* EQ_B3_B - [15:0] */
+#define WM9081_EQ_B3_B_WIDTH 16 /* EQ_B3_B - [15:0] */
+
+/*
+ * R57 (0x39) - EQ 16
+ */
+#define WM9081_EQ_B3_C_MASK 0xFFFF /* EQ_B3_C - [15:0] */
+#define WM9081_EQ_B3_C_SHIFT 0 /* EQ_B3_C - [15:0] */
+#define WM9081_EQ_B3_C_WIDTH 16 /* EQ_B3_C - [15:0] */
+
+/*
+ * R58 (0x3A) - EQ 17
+ */
+#define WM9081_EQ_B3_PG_MASK 0xFFFF /* EQ_B3_PG - [15:0] */
+#define WM9081_EQ_B3_PG_SHIFT 0 /* EQ_B3_PG - [15:0] */
+#define WM9081_EQ_B3_PG_WIDTH 16 /* EQ_B3_PG - [15:0] */
+
+/*
+ * R59 (0x3B) - EQ 18
+ */
+#define WM9081_EQ_B5_A_MASK 0xFFFF /* EQ_B5_A - [15:0] */
+#define WM9081_EQ_B5_A_SHIFT 0 /* EQ_B5_A - [15:0] */
+#define WM9081_EQ_B5_A_WIDTH 16 /* EQ_B5_A - [15:0] */
+
+/*
+ * R60 (0x3C) - EQ 19
+ */
+#define WM9081_EQ_B5_B_MASK 0xFFFF /* EQ_B5_B - [15:0] */
+#define WM9081_EQ_B5_B_SHIFT 0 /* EQ_B5_B - [15:0] */
+#define WM9081_EQ_B5_B_WIDTH 16 /* EQ_B5_B - [15:0] */
+
+/*
+ * R61 (0x3D) - EQ 20
+ */
+#define WM9081_EQ_B5_PG_MASK 0xFFFF /* EQ_B5_PG - [15:0] */
+#define WM9081_EQ_B5_PG_SHIFT 0 /* EQ_B5_PG - [15:0] */
+#define WM9081_EQ_B5_PG_WIDTH 16 /* EQ_B5_PG - [15:0] */
+
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm9090.c b/kernel/sound/soc/codecs/wm9090.c
new file mode 100644
index 000000000..60d243c90
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm9090.c
@@ -0,0 +1,652 @@
+/*
+ * ALSA SoC WM9090 driver
+ *
+ * Copyright 2009-12 Wolfson Microelectronics
+ *
+ * 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.
+ *
+ * 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 <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/wm9090.h>
+
+#include "wm9090.h"
+
+static const struct reg_default wm9090_reg_defaults[] = {
+ { 1, 0x0006 }, /* R1 - Power Management (1) */
+ { 2, 0x6000 }, /* R2 - Power Management (2) */
+ { 3, 0x0000 }, /* R3 - Power Management (3) */
+ { 6, 0x01C0 }, /* R6 - Clocking 1 */
+ { 22, 0x0003 }, /* R22 - IN1 Line Control */
+ { 23, 0x0003 }, /* R23 - IN2 Line Control */
+ { 24, 0x0083 }, /* R24 - IN1 Line Input A Volume */
+ { 25, 0x0083 }, /* R25 - IN1 Line Input B Volume */
+ { 26, 0x0083 }, /* R26 - IN2 Line Input A Volume */
+ { 27, 0x0083 }, /* R27 - IN2 Line Input B Volume */
+ { 28, 0x002D }, /* R28 - Left Output Volume */
+ { 29, 0x002D }, /* R29 - Right Output Volume */
+ { 34, 0x0100 }, /* R34 - SPKMIXL Attenuation */
+ { 35, 0x0010 }, /* R36 - SPKOUT Mixers */
+ { 37, 0x0140 }, /* R37 - ClassD3 */
+ { 38, 0x0039 }, /* R38 - Speaker Volume Left */
+ { 45, 0x0000 }, /* R45 - Output Mixer1 */
+ { 46, 0x0000 }, /* R46 - Output Mixer2 */
+ { 47, 0x0100 }, /* R47 - Output Mixer3 */
+ { 48, 0x0100 }, /* R48 - Output Mixer4 */
+ { 54, 0x0000 }, /* R54 - Speaker Mixer */
+ { 57, 0x000D }, /* R57 - AntiPOP2 */
+ { 70, 0x0000 }, /* R70 - Write Sequencer 0 */
+ { 71, 0x0000 }, /* R71 - Write Sequencer 1 */
+ { 72, 0x0000 }, /* R72 - Write Sequencer 2 */
+ { 73, 0x0000 }, /* R73 - Write Sequencer 3 */
+ { 74, 0x0000 }, /* R74 - Write Sequencer 4 */
+ { 75, 0x0000 }, /* R75 - Write Sequencer 5 */
+ { 76, 0x1F25 }, /* R76 - Charge Pump 1 */
+ { 85, 0x054A }, /* R85 - DC Servo 1 */
+ { 87, 0x0000 }, /* R87 - DC Servo 3 */
+ { 96, 0x0100 }, /* R96 - Analogue HP 0 */
+ { 98, 0x8640 }, /* R98 - AGC Control 0 */
+ { 99, 0xC000 }, /* R99 - AGC Control 1 */
+ { 100, 0x0200 }, /* R100 - AGC Control 2 */
+};
+
+/* This struct is used to save the context */
+struct wm9090_priv {
+ struct wm9090_platform_data pdata;
+ struct regmap *regmap;
+};
+
+static bool wm9090_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM9090_SOFTWARE_RESET:
+ case WM9090_DC_SERVO_0:
+ case WM9090_DC_SERVO_READBACK_0:
+ case WM9090_DC_SERVO_READBACK_1:
+ case WM9090_DC_SERVO_READBACK_2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool wm9090_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM9090_SOFTWARE_RESET:
+ case WM9090_POWER_MANAGEMENT_1:
+ case WM9090_POWER_MANAGEMENT_2:
+ case WM9090_POWER_MANAGEMENT_3:
+ case WM9090_CLOCKING_1:
+ case WM9090_IN1_LINE_CONTROL:
+ case WM9090_IN2_LINE_CONTROL:
+ case WM9090_IN1_LINE_INPUT_A_VOLUME:
+ case WM9090_IN1_LINE_INPUT_B_VOLUME:
+ case WM9090_IN2_LINE_INPUT_A_VOLUME:
+ case WM9090_IN2_LINE_INPUT_B_VOLUME:
+ case WM9090_LEFT_OUTPUT_VOLUME:
+ case WM9090_RIGHT_OUTPUT_VOLUME:
+ case WM9090_SPKMIXL_ATTENUATION:
+ case WM9090_SPKOUT_MIXERS:
+ case WM9090_CLASSD3:
+ case WM9090_SPEAKER_VOLUME_LEFT:
+ case WM9090_OUTPUT_MIXER1:
+ case WM9090_OUTPUT_MIXER2:
+ case WM9090_OUTPUT_MIXER3:
+ case WM9090_OUTPUT_MIXER4:
+ case WM9090_SPEAKER_MIXER:
+ case WM9090_ANTIPOP2:
+ case WM9090_WRITE_SEQUENCER_0:
+ case WM9090_WRITE_SEQUENCER_1:
+ case WM9090_WRITE_SEQUENCER_2:
+ case WM9090_WRITE_SEQUENCER_3:
+ case WM9090_WRITE_SEQUENCER_4:
+ case WM9090_WRITE_SEQUENCER_5:
+ case WM9090_CHARGE_PUMP_1:
+ case WM9090_DC_SERVO_0:
+ case WM9090_DC_SERVO_1:
+ case WM9090_DC_SERVO_3:
+ case WM9090_DC_SERVO_READBACK_0:
+ case WM9090_DC_SERVO_READBACK_1:
+ case WM9090_DC_SERVO_READBACK_2:
+ case WM9090_ANALOGUE_HP_0:
+ case WM9090_AGC_CONTROL_0:
+ case WM9090_AGC_CONTROL_1:
+ case WM9090_AGC_CONTROL_2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static void wait_for_dc_servo(struct snd_soc_codec *codec)
+{
+ unsigned int reg;
+ int count = 0;
+
+ dev_dbg(codec->dev, "Waiting for DC servo...\n");
+ do {
+ count++;
+ msleep(1);
+ reg = snd_soc_read(codec, WM9090_DC_SERVO_READBACK_0);
+ dev_dbg(codec->dev, "DC servo status: %x\n", reg);
+ } while ((reg & WM9090_DCS_CAL_COMPLETE_MASK)
+ != WM9090_DCS_CAL_COMPLETE_MASK && count < 1000);
+
+ if ((reg & WM9090_DCS_CAL_COMPLETE_MASK)
+ != WM9090_DCS_CAL_COMPLETE_MASK)
+ dev_err(codec->dev, "Timed out waiting for DC Servo\n");
+}
+
+static const unsigned int in_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 0, TLV_DB_SCALE_ITEM(-600, 0, 0),
+ 1, 3, TLV_DB_SCALE_ITEM(-350, 350, 0),
+ 4, 6, TLV_DB_SCALE_ITEM(600, 600, 0),
+};
+static const unsigned int mix_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 2, TLV_DB_SCALE_ITEM(-1200, 300, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(0, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+static const unsigned int spkboost_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
+};
+
+static const struct snd_kcontrol_new wm9090_controls[] = {
+SOC_SINGLE_TLV("IN1A Volume", WM9090_IN1_LINE_INPUT_A_VOLUME, 0, 6, 0,
+ in_tlv),
+SOC_SINGLE("IN1A Switch", WM9090_IN1_LINE_INPUT_A_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN1A ZC Switch", WM9090_IN1_LINE_INPUT_A_VOLUME, 6, 1, 0),
+
+SOC_SINGLE_TLV("IN2A Volume", WM9090_IN2_LINE_INPUT_A_VOLUME, 0, 6, 0,
+ in_tlv),
+SOC_SINGLE("IN2A Switch", WM9090_IN2_LINE_INPUT_A_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN2A ZC Switch", WM9090_IN2_LINE_INPUT_A_VOLUME, 6, 1, 0),
+
+SOC_SINGLE("MIXOUTL Switch", WM9090_OUTPUT_MIXER3, 8, 1, 1),
+SOC_SINGLE_TLV("MIXOUTL IN1A Volume", WM9090_OUTPUT_MIXER3, 6, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("MIXOUTL IN2A Volume", WM9090_OUTPUT_MIXER3, 2, 3, 1,
+ mix_tlv),
+
+SOC_SINGLE("MIXOUTR Switch", WM9090_OUTPUT_MIXER4, 8, 1, 1),
+SOC_SINGLE_TLV("MIXOUTR IN1A Volume", WM9090_OUTPUT_MIXER4, 6, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("MIXOUTR IN2A Volume", WM9090_OUTPUT_MIXER4, 2, 3, 1,
+ mix_tlv),
+
+SOC_SINGLE("SPKMIX Switch", WM9090_SPKMIXL_ATTENUATION, 8, 1, 1),
+SOC_SINGLE_TLV("SPKMIX IN1A Volume", WM9090_SPKMIXL_ATTENUATION, 6, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("SPKMIX IN2A Volume", WM9090_SPKMIXL_ATTENUATION, 2, 3, 1,
+ mix_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Volume", WM9090_LEFT_OUTPUT_VOLUME,
+ WM9090_RIGHT_OUTPUT_VOLUME, 0, 63, 0, out_tlv),
+SOC_DOUBLE_R("Headphone Switch", WM9090_LEFT_OUTPUT_VOLUME,
+ WM9090_RIGHT_OUTPUT_VOLUME, 6, 1, 1),
+SOC_DOUBLE_R("Headphone ZC Switch", WM9090_LEFT_OUTPUT_VOLUME,
+ WM9090_RIGHT_OUTPUT_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("Speaker Volume", WM9090_SPEAKER_VOLUME_LEFT, 0, 63, 0,
+ out_tlv),
+SOC_SINGLE("Speaker Switch", WM9090_SPEAKER_VOLUME_LEFT, 6, 1, 1),
+SOC_SINGLE("Speaker ZC Switch", WM9090_SPEAKER_VOLUME_LEFT, 7, 1, 0),
+SOC_SINGLE_TLV("Speaker Boost Volume", WM9090_CLASSD3, 3, 7, 0, spkboost_tlv),
+};
+
+static const struct snd_kcontrol_new wm9090_in1_se_controls[] = {
+SOC_SINGLE_TLV("IN1B Volume", WM9090_IN1_LINE_INPUT_B_VOLUME, 0, 6, 0,
+ in_tlv),
+SOC_SINGLE("IN1B Switch", WM9090_IN1_LINE_INPUT_B_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN1B ZC Switch", WM9090_IN1_LINE_INPUT_B_VOLUME, 6, 1, 0),
+
+SOC_SINGLE_TLV("SPKMIX IN1B Volume", WM9090_SPKMIXL_ATTENUATION, 4, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("MIXOUTL IN1B Volume", WM9090_OUTPUT_MIXER3, 4, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("MIXOUTR IN1B Volume", WM9090_OUTPUT_MIXER4, 4, 3, 1,
+ mix_tlv),
+};
+
+static const struct snd_kcontrol_new wm9090_in2_se_controls[] = {
+SOC_SINGLE_TLV("IN2B Volume", WM9090_IN2_LINE_INPUT_B_VOLUME, 0, 6, 0,
+ in_tlv),
+SOC_SINGLE("IN2B Switch", WM9090_IN2_LINE_INPUT_B_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN2B ZC Switch", WM9090_IN2_LINE_INPUT_B_VOLUME, 6, 1, 0),
+
+SOC_SINGLE_TLV("SPKMIX IN2B Volume", WM9090_SPKMIXL_ATTENUATION, 0, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("MIXOUTL IN2B Volume", WM9090_OUTPUT_MIXER3, 0, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("MIXOUTR IN2B Volume", WM9090_OUTPUT_MIXER4, 0, 3, 1,
+ mix_tlv),
+};
+
+static int hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ unsigned int reg = snd_soc_read(codec, WM9090_ANALOGUE_HP_0);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, WM9090_CHARGE_PUMP_1,
+ WM9090_CP_ENA, WM9090_CP_ENA);
+
+ msleep(5);
+
+ snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
+ WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA,
+ WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA);
+
+ reg |= WM9090_HPOUT1L_DLY | WM9090_HPOUT1R_DLY;
+ snd_soc_write(codec, WM9090_ANALOGUE_HP_0, reg);
+
+ /* Start the DC servo. We don't currently use the
+ * ability to save the state since we don't have full
+ * control of the analogue paths and they can change
+ * DC offsets; see the WM8904 driver for an example of
+ * doing so.
+ */
+ snd_soc_write(codec, WM9090_DC_SERVO_0,
+ WM9090_DCS_ENA_CHAN_0 |
+ WM9090_DCS_ENA_CHAN_1 |
+ WM9090_DCS_TRIG_STARTUP_1 |
+ WM9090_DCS_TRIG_STARTUP_0);
+ wait_for_dc_servo(codec);
+
+ reg |= WM9090_HPOUT1R_OUTP | WM9090_HPOUT1R_RMV_SHORT |
+ WM9090_HPOUT1L_OUTP | WM9090_HPOUT1L_RMV_SHORT;
+ snd_soc_write(codec, WM9090_ANALOGUE_HP_0, reg);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ reg &= ~(WM9090_HPOUT1L_RMV_SHORT |
+ WM9090_HPOUT1L_DLY |
+ WM9090_HPOUT1L_OUTP |
+ WM9090_HPOUT1R_RMV_SHORT |
+ WM9090_HPOUT1R_DLY |
+ WM9090_HPOUT1R_OUTP);
+
+ snd_soc_write(codec, WM9090_ANALOGUE_HP_0, reg);
+
+ snd_soc_write(codec, WM9090_DC_SERVO_0, 0);
+
+ snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
+ WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA,
+ 0);
+
+ snd_soc_update_bits(codec, WM9090_CHARGE_PUMP_1,
+ WM9090_CP_ENA, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new spkmix[] = {
+SOC_DAPM_SINGLE("IN1A Switch", WM9090_SPEAKER_MIXER, 6, 1, 0),
+SOC_DAPM_SINGLE("IN1B Switch", WM9090_SPEAKER_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("IN2A Switch", WM9090_SPEAKER_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("IN2B Switch", WM9090_SPEAKER_MIXER, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new spkout[] = {
+SOC_DAPM_SINGLE("Mixer Switch", WM9090_SPKOUT_MIXERS, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixoutl[] = {
+SOC_DAPM_SINGLE("IN1A Switch", WM9090_OUTPUT_MIXER1, 6, 1, 0),
+SOC_DAPM_SINGLE("IN1B Switch", WM9090_OUTPUT_MIXER1, 4, 1, 0),
+SOC_DAPM_SINGLE("IN2A Switch", WM9090_OUTPUT_MIXER1, 2, 1, 0),
+SOC_DAPM_SINGLE("IN2B Switch", WM9090_OUTPUT_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixoutr[] = {
+SOC_DAPM_SINGLE("IN1A Switch", WM9090_OUTPUT_MIXER2, 6, 1, 0),
+SOC_DAPM_SINGLE("IN1B Switch", WM9090_OUTPUT_MIXER2, 4, 1, 0),
+SOC_DAPM_SINGLE("IN2A Switch", WM9090_OUTPUT_MIXER2, 2, 1, 0),
+SOC_DAPM_SINGLE("IN2B Switch", WM9090_OUTPUT_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm9090_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1+"),
+SND_SOC_DAPM_INPUT("IN1-"),
+SND_SOC_DAPM_INPUT("IN2+"),
+SND_SOC_DAPM_INPUT("IN2-"),
+
+SND_SOC_DAPM_SUPPLY("OSC", WM9090_POWER_MANAGEMENT_1, 3, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("IN1A PGA", WM9090_POWER_MANAGEMENT_2, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN1B PGA", WM9090_POWER_MANAGEMENT_2, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN2A PGA", WM9090_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN2B PGA", WM9090_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("SPKMIX", WM9090_POWER_MANAGEMENT_3, 3, 0,
+ spkmix, ARRAY_SIZE(spkmix)),
+SND_SOC_DAPM_MIXER("MIXOUTL", WM9090_POWER_MANAGEMENT_3, 5, 0,
+ mixoutl, ARRAY_SIZE(mixoutl)),
+SND_SOC_DAPM_MIXER("MIXOUTR", WM9090_POWER_MANAGEMENT_3, 4, 0,
+ mixoutr, ARRAY_SIZE(mixoutr)),
+
+SND_SOC_DAPM_PGA_E("HP PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ hp_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA("SPKPGA", WM9090_POWER_MANAGEMENT_3, 8, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("SPKOUT", WM9090_POWER_MANAGEMENT_1, 12, 0,
+ spkout, ARRAY_SIZE(spkout)),
+
+SND_SOC_DAPM_OUTPUT("HPR"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("Speaker"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ { "IN1A PGA", NULL, "IN1+" },
+ { "IN2A PGA", NULL, "IN2+" },
+
+ { "SPKMIX", "IN1A Switch", "IN1A PGA" },
+ { "SPKMIX", "IN2A Switch", "IN2A PGA" },
+
+ { "MIXOUTL", "IN1A Switch", "IN1A PGA" },
+ { "MIXOUTL", "IN2A Switch", "IN2A PGA" },
+
+ { "MIXOUTR", "IN1A Switch", "IN1A PGA" },
+ { "MIXOUTR", "IN2A Switch", "IN2A PGA" },
+
+ { "HP PGA", NULL, "OSC" },
+ { "HP PGA", NULL, "MIXOUTL" },
+ { "HP PGA", NULL, "MIXOUTR" },
+
+ { "HPL", NULL, "HP PGA" },
+ { "HPR", NULL, "HP PGA" },
+
+ { "SPKPGA", NULL, "OSC" },
+ { "SPKPGA", NULL, "SPKMIX" },
+
+ { "SPKOUT", "Mixer Switch", "SPKPGA" },
+
+ { "Speaker", NULL, "SPKOUT" },
+};
+
+static const struct snd_soc_dapm_route audio_map_in1_se[] = {
+ { "IN1B PGA", NULL, "IN1-" },
+
+ { "SPKMIX", "IN1B Switch", "IN1B PGA" },
+ { "MIXOUTL", "IN1B Switch", "IN1B PGA" },
+ { "MIXOUTR", "IN1B Switch", "IN1B PGA" },
+};
+
+static const struct snd_soc_dapm_route audio_map_in1_diff[] = {
+ { "IN1A PGA", NULL, "IN1-" },
+};
+
+static const struct snd_soc_dapm_route audio_map_in2_se[] = {
+ { "IN2B PGA", NULL, "IN2-" },
+
+ { "SPKMIX", "IN2B Switch", "IN2B PGA" },
+ { "MIXOUTL", "IN2B Switch", "IN2B PGA" },
+ { "MIXOUTR", "IN2B Switch", "IN2B PGA" },
+};
+
+static const struct snd_soc_dapm_route audio_map_in2_diff[] = {
+ { "IN2A PGA", NULL, "IN2-" },
+};
+
+static int wm9090_add_controls(struct snd_soc_codec *codec)
+{
+ struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int i;
+
+ snd_soc_dapm_new_controls(dapm, wm9090_dapm_widgets,
+ ARRAY_SIZE(wm9090_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_add_codec_controls(codec, wm9090_controls,
+ ARRAY_SIZE(wm9090_controls));
+
+ if (wm9090->pdata.lin1_diff) {
+ snd_soc_dapm_add_routes(dapm, audio_map_in1_diff,
+ ARRAY_SIZE(audio_map_in1_diff));
+ } else {
+ snd_soc_dapm_add_routes(dapm, audio_map_in1_se,
+ ARRAY_SIZE(audio_map_in1_se));
+ snd_soc_add_codec_controls(codec, wm9090_in1_se_controls,
+ ARRAY_SIZE(wm9090_in1_se_controls));
+ }
+
+ if (wm9090->pdata.lin2_diff) {
+ snd_soc_dapm_add_routes(dapm, audio_map_in2_diff,
+ ARRAY_SIZE(audio_map_in2_diff));
+ } else {
+ snd_soc_dapm_add_routes(dapm, audio_map_in2_se,
+ ARRAY_SIZE(audio_map_in2_se));
+ snd_soc_add_codec_controls(codec, wm9090_in2_se_controls,
+ ARRAY_SIZE(wm9090_in2_se_controls));
+ }
+
+ if (wm9090->pdata.agc_ena) {
+ for (i = 0; i < ARRAY_SIZE(wm9090->pdata.agc); i++)
+ snd_soc_write(codec, WM9090_AGC_CONTROL_0 + i,
+ wm9090->pdata.agc[i]);
+ snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_3,
+ WM9090_AGC_ENA, WM9090_AGC_ENA);
+ } else {
+ snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_3,
+ WM9090_AGC_ENA, 0);
+ }
+
+ return 0;
+
+}
+
+/*
+ * The machine driver should call this from their set_bias_level; if there
+ * isn't one then this can just be set as the set_bias_level function.
+ */
+static int wm9090_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_update_bits(codec, WM9090_ANTIPOP2, WM9090_VMID_ENA,
+ WM9090_VMID_ENA);
+ snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
+ WM9090_BIAS_ENA |
+ WM9090_VMID_RES_MASK,
+ WM9090_BIAS_ENA |
+ 1 << WM9090_VMID_RES_SHIFT);
+ msleep(1); /* Probably an overestimate */
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ /* Restore the register cache */
+ regcache_sync(wm9090->regmap);
+ }
+
+ /* We keep VMID off during standby since the combination of
+ * ground referenced outputs and class D speaker mean that
+ * latency is not an issue.
+ */
+ snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
+ WM9090_BIAS_ENA | WM9090_VMID_RES_MASK, 0);
+ snd_soc_update_bits(codec, WM9090_ANTIPOP2,
+ WM9090_VMID_ENA, 0);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int wm9090_probe(struct snd_soc_codec *codec)
+{
+ /* Configure some defaults; they will be written out when we
+ * bring the bias up.
+ */
+ snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_A_VOLUME,
+ WM9090_IN1_VU | WM9090_IN1A_ZC,
+ WM9090_IN1_VU | WM9090_IN1A_ZC);
+ snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_B_VOLUME,
+ WM9090_IN1_VU | WM9090_IN1B_ZC,
+ WM9090_IN1_VU | WM9090_IN1B_ZC);
+ snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_A_VOLUME,
+ WM9090_IN2_VU | WM9090_IN2A_ZC,
+ WM9090_IN2_VU | WM9090_IN2A_ZC);
+ snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_B_VOLUME,
+ WM9090_IN2_VU | WM9090_IN2B_ZC,
+ WM9090_IN2_VU | WM9090_IN2B_ZC);
+ snd_soc_update_bits(codec, WM9090_SPEAKER_VOLUME_LEFT,
+ WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC,
+ WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC);
+ snd_soc_update_bits(codec, WM9090_LEFT_OUTPUT_VOLUME,
+ WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC,
+ WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC);
+ snd_soc_update_bits(codec, WM9090_RIGHT_OUTPUT_VOLUME,
+ WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC,
+ WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC);
+
+ snd_soc_update_bits(codec, WM9090_CLOCKING_1,
+ WM9090_TOCLK_ENA, WM9090_TOCLK_ENA);
+
+ wm9090_add_controls(codec);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm9090 = {
+ .probe = wm9090_probe,
+ .set_bias_level = wm9090_set_bias_level,
+ .suspend_bias_off = true,
+};
+
+static const struct regmap_config wm9090_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = WM9090_MAX_REGISTER,
+ .volatile_reg = wm9090_volatile,
+ .readable_reg = wm9090_readable,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm9090_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm9090_reg_defaults),
+};
+
+
+static int wm9090_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm9090_priv *wm9090;
+ unsigned int reg;
+ int ret;
+
+ wm9090 = devm_kzalloc(&i2c->dev, sizeof(*wm9090), GFP_KERNEL);
+ if (!wm9090)
+ return -ENOMEM;
+
+ wm9090->regmap = devm_regmap_init_i2c(i2c, &wm9090_regmap);
+ if (IS_ERR(wm9090->regmap)) {
+ ret = PTR_ERR(wm9090->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(wm9090->regmap, WM9090_SOFTWARE_RESET, &reg);
+ if (ret < 0)
+ return ret;
+
+ if (reg != 0x9093) {
+ dev_err(&i2c->dev, "Device is not a WM9090, ID=%x\n", reg);
+ return -ENODEV;
+ }
+
+ ret = regmap_write(wm9090->regmap, WM9090_SOFTWARE_RESET, 0);
+ if (ret < 0)
+ return ret;
+
+ if (i2c->dev.platform_data)
+ memcpy(&wm9090->pdata, i2c->dev.platform_data,
+ sizeof(wm9090->pdata));
+
+ i2c_set_clientdata(i2c, wm9090);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm9090, NULL, 0);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wm9090_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm9090_id[] = {
+ { "wm9090", 0 },
+ { "wm9093", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm9090_id);
+
+static struct i2c_driver wm9090_i2c_driver = {
+ .driver = {
+ .name = "wm9090",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm9090_i2c_probe,
+ .remove = wm9090_i2c_remove,
+ .id_table = wm9090_id,
+};
+
+module_i2c_driver(wm9090_i2c_driver);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM9090 ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm9090.h b/kernel/sound/soc/codecs/wm9090.h
new file mode 100644
index 000000000..29b9d9fc7
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm9090.h
@@ -0,0 +1,713 @@
+/*
+ * ALSA SoC WM9090 driver
+ *
+ * Copyright 2009 Wolfson Microelectronics
+ *
+ * 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.
+ *
+ * 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 __WM9090_H
+#define __WM9090_H
+
+/*
+ * Register values.
+ */
+#define WM9090_SOFTWARE_RESET 0x00
+#define WM9090_POWER_MANAGEMENT_1 0x01
+#define WM9090_POWER_MANAGEMENT_2 0x02
+#define WM9090_POWER_MANAGEMENT_3 0x03
+#define WM9090_CLOCKING_1 0x06
+#define WM9090_IN1_LINE_CONTROL 0x16
+#define WM9090_IN2_LINE_CONTROL 0x17
+#define WM9090_IN1_LINE_INPUT_A_VOLUME 0x18
+#define WM9090_IN1_LINE_INPUT_B_VOLUME 0x19
+#define WM9090_IN2_LINE_INPUT_A_VOLUME 0x1A
+#define WM9090_IN2_LINE_INPUT_B_VOLUME 0x1B
+#define WM9090_LEFT_OUTPUT_VOLUME 0x1C
+#define WM9090_RIGHT_OUTPUT_VOLUME 0x1D
+#define WM9090_SPKMIXL_ATTENUATION 0x22
+#define WM9090_SPKOUT_MIXERS 0x24
+#define WM9090_CLASSD3 0x25
+#define WM9090_SPEAKER_VOLUME_LEFT 0x26
+#define WM9090_OUTPUT_MIXER1 0x2D
+#define WM9090_OUTPUT_MIXER2 0x2E
+#define WM9090_OUTPUT_MIXER3 0x2F
+#define WM9090_OUTPUT_MIXER4 0x30
+#define WM9090_SPEAKER_MIXER 0x36
+#define WM9090_ANTIPOP2 0x39
+#define WM9090_WRITE_SEQUENCER_0 0x46
+#define WM9090_WRITE_SEQUENCER_1 0x47
+#define WM9090_WRITE_SEQUENCER_2 0x48
+#define WM9090_WRITE_SEQUENCER_3 0x49
+#define WM9090_WRITE_SEQUENCER_4 0x4A
+#define WM9090_WRITE_SEQUENCER_5 0x4B
+#define WM9090_CHARGE_PUMP_1 0x4C
+#define WM9090_DC_SERVO_0 0x54
+#define WM9090_DC_SERVO_1 0x55
+#define WM9090_DC_SERVO_3 0x57
+#define WM9090_DC_SERVO_READBACK_0 0x58
+#define WM9090_DC_SERVO_READBACK_1 0x59
+#define WM9090_DC_SERVO_READBACK_2 0x5A
+#define WM9090_ANALOGUE_HP_0 0x60
+#define WM9090_AGC_CONTROL_0 0x62
+#define WM9090_AGC_CONTROL_1 0x63
+#define WM9090_AGC_CONTROL_2 0x64
+
+#define WM9090_REGISTER_COUNT 40
+#define WM9090_MAX_REGISTER 0x64
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM9090_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */
+#define WM9090_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */
+#define WM9090_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM9090_SPKOUTL_ENA 0x1000 /* SPKOUTL_ENA */
+#define WM9090_SPKOUTL_ENA_MASK 0x1000 /* SPKOUTL_ENA */
+#define WM9090_SPKOUTL_ENA_SHIFT 12 /* SPKOUTL_ENA */
+#define WM9090_SPKOUTL_ENA_WIDTH 1 /* SPKOUTL_ENA */
+#define WM9090_HPOUT1L_ENA 0x0200 /* HPOUT1L_ENA */
+#define WM9090_HPOUT1L_ENA_MASK 0x0200 /* HPOUT1L_ENA */
+#define WM9090_HPOUT1L_ENA_SHIFT 9 /* HPOUT1L_ENA */
+#define WM9090_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */
+#define WM9090_HPOUT1R_ENA 0x0100 /* HPOUT1R_ENA */
+#define WM9090_HPOUT1R_ENA_MASK 0x0100 /* HPOUT1R_ENA */
+#define WM9090_HPOUT1R_ENA_SHIFT 8 /* HPOUT1R_ENA */
+#define WM9090_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */
+#define WM9090_OSC_ENA 0x0008 /* OSC_ENA */
+#define WM9090_OSC_ENA_MASK 0x0008 /* OSC_ENA */
+#define WM9090_OSC_ENA_SHIFT 3 /* OSC_ENA */
+#define WM9090_OSC_ENA_WIDTH 1 /* OSC_ENA */
+#define WM9090_VMID_RES_MASK 0x0006 /* VMID_RES - [2:1] */
+#define WM9090_VMID_RES_SHIFT 1 /* VMID_RES - [2:1] */
+#define WM9090_VMID_RES_WIDTH 2 /* VMID_RES - [2:1] */
+#define WM9090_BIAS_ENA 0x0001 /* BIAS_ENA */
+#define WM9090_BIAS_ENA_MASK 0x0001 /* BIAS_ENA */
+#define WM9090_BIAS_ENA_SHIFT 0 /* BIAS_ENA */
+#define WM9090_BIAS_ENA_WIDTH 1 /* BIAS_ENA */
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM9090_TSHUT 0x8000 /* TSHUT */
+#define WM9090_TSHUT_MASK 0x8000 /* TSHUT */
+#define WM9090_TSHUT_SHIFT 15 /* TSHUT */
+#define WM9090_TSHUT_WIDTH 1 /* TSHUT */
+#define WM9090_TSHUT_ENA 0x4000 /* TSHUT_ENA */
+#define WM9090_TSHUT_ENA_MASK 0x4000 /* TSHUT_ENA */
+#define WM9090_TSHUT_ENA_SHIFT 14 /* TSHUT_ENA */
+#define WM9090_TSHUT_ENA_WIDTH 1 /* TSHUT_ENA */
+#define WM9090_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */
+#define WM9090_TSHUT_OPDIS_MASK 0x2000 /* TSHUT_OPDIS */
+#define WM9090_TSHUT_OPDIS_SHIFT 13 /* TSHUT_OPDIS */
+#define WM9090_TSHUT_OPDIS_WIDTH 1 /* TSHUT_OPDIS */
+#define WM9090_IN1A_ENA 0x0080 /* IN1A_ENA */
+#define WM9090_IN1A_ENA_MASK 0x0080 /* IN1A_ENA */
+#define WM9090_IN1A_ENA_SHIFT 7 /* IN1A_ENA */
+#define WM9090_IN1A_ENA_WIDTH 1 /* IN1A_ENA */
+#define WM9090_IN1B_ENA 0x0040 /* IN1B_ENA */
+#define WM9090_IN1B_ENA_MASK 0x0040 /* IN1B_ENA */
+#define WM9090_IN1B_ENA_SHIFT 6 /* IN1B_ENA */
+#define WM9090_IN1B_ENA_WIDTH 1 /* IN1B_ENA */
+#define WM9090_IN2A_ENA 0x0020 /* IN2A_ENA */
+#define WM9090_IN2A_ENA_MASK 0x0020 /* IN2A_ENA */
+#define WM9090_IN2A_ENA_SHIFT 5 /* IN2A_ENA */
+#define WM9090_IN2A_ENA_WIDTH 1 /* IN2A_ENA */
+#define WM9090_IN2B_ENA 0x0010 /* IN2B_ENA */
+#define WM9090_IN2B_ENA_MASK 0x0010 /* IN2B_ENA */
+#define WM9090_IN2B_ENA_SHIFT 4 /* IN2B_ENA */
+#define WM9090_IN2B_ENA_WIDTH 1 /* IN2B_ENA */
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM9090_AGC_ENA 0x4000 /* AGC_ENA */
+#define WM9090_AGC_ENA_MASK 0x4000 /* AGC_ENA */
+#define WM9090_AGC_ENA_SHIFT 14 /* AGC_ENA */
+#define WM9090_AGC_ENA_WIDTH 1 /* AGC_ENA */
+#define WM9090_SPKLVOL_ENA 0x0100 /* SPKLVOL_ENA */
+#define WM9090_SPKLVOL_ENA_MASK 0x0100 /* SPKLVOL_ENA */
+#define WM9090_SPKLVOL_ENA_SHIFT 8 /* SPKLVOL_ENA */
+#define WM9090_SPKLVOL_ENA_WIDTH 1 /* SPKLVOL_ENA */
+#define WM9090_MIXOUTL_ENA 0x0020 /* MIXOUTL_ENA */
+#define WM9090_MIXOUTL_ENA_MASK 0x0020 /* MIXOUTL_ENA */
+#define WM9090_MIXOUTL_ENA_SHIFT 5 /* MIXOUTL_ENA */
+#define WM9090_MIXOUTL_ENA_WIDTH 1 /* MIXOUTL_ENA */
+#define WM9090_MIXOUTR_ENA 0x0010 /* MIXOUTR_ENA */
+#define WM9090_MIXOUTR_ENA_MASK 0x0010 /* MIXOUTR_ENA */
+#define WM9090_MIXOUTR_ENA_SHIFT 4 /* MIXOUTR_ENA */
+#define WM9090_MIXOUTR_ENA_WIDTH 1 /* MIXOUTR_ENA */
+#define WM9090_SPKMIX_ENA 0x0008 /* SPKMIX_ENA */
+#define WM9090_SPKMIX_ENA_MASK 0x0008 /* SPKMIX_ENA */
+#define WM9090_SPKMIX_ENA_SHIFT 3 /* SPKMIX_ENA */
+#define WM9090_SPKMIX_ENA_WIDTH 1 /* SPKMIX_ENA */
+
+/*
+ * R6 (0x06) - Clocking 1
+ */
+#define WM9090_TOCLK_RATE 0x8000 /* TOCLK_RATE */
+#define WM9090_TOCLK_RATE_MASK 0x8000 /* TOCLK_RATE */
+#define WM9090_TOCLK_RATE_SHIFT 15 /* TOCLK_RATE */
+#define WM9090_TOCLK_RATE_WIDTH 1 /* TOCLK_RATE */
+#define WM9090_TOCLK_ENA 0x4000 /* TOCLK_ENA */
+#define WM9090_TOCLK_ENA_MASK 0x4000 /* TOCLK_ENA */
+#define WM9090_TOCLK_ENA_SHIFT 14 /* TOCLK_ENA */
+#define WM9090_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */
+
+/*
+ * R22 (0x16) - IN1 Line Control
+ */
+#define WM9090_IN1_DIFF 0x0002 /* IN1_DIFF */
+#define WM9090_IN1_DIFF_MASK 0x0002 /* IN1_DIFF */
+#define WM9090_IN1_DIFF_SHIFT 1 /* IN1_DIFF */
+#define WM9090_IN1_DIFF_WIDTH 1 /* IN1_DIFF */
+#define WM9090_IN1_CLAMP 0x0001 /* IN1_CLAMP */
+#define WM9090_IN1_CLAMP_MASK 0x0001 /* IN1_CLAMP */
+#define WM9090_IN1_CLAMP_SHIFT 0 /* IN1_CLAMP */
+#define WM9090_IN1_CLAMP_WIDTH 1 /* IN1_CLAMP */
+
+/*
+ * R23 (0x17) - IN2 Line Control
+ */
+#define WM9090_IN2_DIFF 0x0002 /* IN2_DIFF */
+#define WM9090_IN2_DIFF_MASK 0x0002 /* IN2_DIFF */
+#define WM9090_IN2_DIFF_SHIFT 1 /* IN2_DIFF */
+#define WM9090_IN2_DIFF_WIDTH 1 /* IN2_DIFF */
+#define WM9090_IN2_CLAMP 0x0001 /* IN2_CLAMP */
+#define WM9090_IN2_CLAMP_MASK 0x0001 /* IN2_CLAMP */
+#define WM9090_IN2_CLAMP_SHIFT 0 /* IN2_CLAMP */
+#define WM9090_IN2_CLAMP_WIDTH 1 /* IN2_CLAMP */
+
+/*
+ * R24 (0x18) - IN1 Line Input A Volume
+ */
+#define WM9090_IN1_VU 0x0100 /* IN1_VU */
+#define WM9090_IN1_VU_MASK 0x0100 /* IN1_VU */
+#define WM9090_IN1_VU_SHIFT 8 /* IN1_VU */
+#define WM9090_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM9090_IN1A_MUTE 0x0080 /* IN1A_MUTE */
+#define WM9090_IN1A_MUTE_MASK 0x0080 /* IN1A_MUTE */
+#define WM9090_IN1A_MUTE_SHIFT 7 /* IN1A_MUTE */
+#define WM9090_IN1A_MUTE_WIDTH 1 /* IN1A_MUTE */
+#define WM9090_IN1A_ZC 0x0040 /* IN1A_ZC */
+#define WM9090_IN1A_ZC_MASK 0x0040 /* IN1A_ZC */
+#define WM9090_IN1A_ZC_SHIFT 6 /* IN1A_ZC */
+#define WM9090_IN1A_ZC_WIDTH 1 /* IN1A_ZC */
+#define WM9090_IN1A_VOL_MASK 0x0007 /* IN1A_VOL - [2:0] */
+#define WM9090_IN1A_VOL_SHIFT 0 /* IN1A_VOL - [2:0] */
+#define WM9090_IN1A_VOL_WIDTH 3 /* IN1A_VOL - [2:0] */
+
+/*
+ * R25 (0x19) - IN1 Line Input B Volume
+ */
+#define WM9090_IN1_VU 0x0100 /* IN1_VU */
+#define WM9090_IN1_VU_MASK 0x0100 /* IN1_VU */
+#define WM9090_IN1_VU_SHIFT 8 /* IN1_VU */
+#define WM9090_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM9090_IN1B_MUTE 0x0080 /* IN1B_MUTE */
+#define WM9090_IN1B_MUTE_MASK 0x0080 /* IN1B_MUTE */
+#define WM9090_IN1B_MUTE_SHIFT 7 /* IN1B_MUTE */
+#define WM9090_IN1B_MUTE_WIDTH 1 /* IN1B_MUTE */
+#define WM9090_IN1B_ZC 0x0040 /* IN1B_ZC */
+#define WM9090_IN1B_ZC_MASK 0x0040 /* IN1B_ZC */
+#define WM9090_IN1B_ZC_SHIFT 6 /* IN1B_ZC */
+#define WM9090_IN1B_ZC_WIDTH 1 /* IN1B_ZC */
+#define WM9090_IN1B_VOL_MASK 0x0007 /* IN1B_VOL - [2:0] */
+#define WM9090_IN1B_VOL_SHIFT 0 /* IN1B_VOL - [2:0] */
+#define WM9090_IN1B_VOL_WIDTH 3 /* IN1B_VOL - [2:0] */
+
+/*
+ * R26 (0x1A) - IN2 Line Input A Volume
+ */
+#define WM9090_IN2_VU 0x0100 /* IN2_VU */
+#define WM9090_IN2_VU_MASK 0x0100 /* IN2_VU */
+#define WM9090_IN2_VU_SHIFT 8 /* IN2_VU */
+#define WM9090_IN2_VU_WIDTH 1 /* IN2_VU */
+#define WM9090_IN2A_MUTE 0x0080 /* IN2A_MUTE */
+#define WM9090_IN2A_MUTE_MASK 0x0080 /* IN2A_MUTE */
+#define WM9090_IN2A_MUTE_SHIFT 7 /* IN2A_MUTE */
+#define WM9090_IN2A_MUTE_WIDTH 1 /* IN2A_MUTE */
+#define WM9090_IN2A_ZC 0x0040 /* IN2A_ZC */
+#define WM9090_IN2A_ZC_MASK 0x0040 /* IN2A_ZC */
+#define WM9090_IN2A_ZC_SHIFT 6 /* IN2A_ZC */
+#define WM9090_IN2A_ZC_WIDTH 1 /* IN2A_ZC */
+#define WM9090_IN2A_VOL_MASK 0x0007 /* IN2A_VOL - [2:0] */
+#define WM9090_IN2A_VOL_SHIFT 0 /* IN2A_VOL - [2:0] */
+#define WM9090_IN2A_VOL_WIDTH 3 /* IN2A_VOL - [2:0] */
+
+/*
+ * R27 (0x1B) - IN2 Line Input B Volume
+ */
+#define WM9090_IN2_VU 0x0100 /* IN2_VU */
+#define WM9090_IN2_VU_MASK 0x0100 /* IN2_VU */
+#define WM9090_IN2_VU_SHIFT 8 /* IN2_VU */
+#define WM9090_IN2_VU_WIDTH 1 /* IN2_VU */
+#define WM9090_IN2B_MUTE 0x0080 /* IN2B_MUTE */
+#define WM9090_IN2B_MUTE_MASK 0x0080 /* IN2B_MUTE */
+#define WM9090_IN2B_MUTE_SHIFT 7 /* IN2B_MUTE */
+#define WM9090_IN2B_MUTE_WIDTH 1 /* IN2B_MUTE */
+#define WM9090_IN2B_ZC 0x0040 /* IN2B_ZC */
+#define WM9090_IN2B_ZC_MASK 0x0040 /* IN2B_ZC */
+#define WM9090_IN2B_ZC_SHIFT 6 /* IN2B_ZC */
+#define WM9090_IN2B_ZC_WIDTH 1 /* IN2B_ZC */
+#define WM9090_IN2B_VOL_MASK 0x0007 /* IN2B_VOL - [2:0] */
+#define WM9090_IN2B_VOL_SHIFT 0 /* IN2B_VOL - [2:0] */
+#define WM9090_IN2B_VOL_WIDTH 3 /* IN2B_VOL - [2:0] */
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM9090_HPOUT1_VU 0x0100 /* HPOUT1_VU */
+#define WM9090_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */
+#define WM9090_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */
+#define WM9090_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */
+#define WM9090_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */
+#define WM9090_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */
+#define WM9090_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */
+#define WM9090_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */
+#define WM9090_HPOUT1L_MUTE 0x0040 /* HPOUT1L_MUTE */
+#define WM9090_HPOUT1L_MUTE_MASK 0x0040 /* HPOUT1L_MUTE */
+#define WM9090_HPOUT1L_MUTE_SHIFT 6 /* HPOUT1L_MUTE */
+#define WM9090_HPOUT1L_MUTE_WIDTH 1 /* HPOUT1L_MUTE */
+#define WM9090_HPOUT1L_VOL_MASK 0x003F /* HPOUT1L_VOL - [5:0] */
+#define WM9090_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [5:0] */
+#define WM9090_HPOUT1L_VOL_WIDTH 6 /* HPOUT1L_VOL - [5:0] */
+
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM9090_HPOUT1_VU 0x0100 /* HPOUT1_VU */
+#define WM9090_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */
+#define WM9090_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */
+#define WM9090_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */
+#define WM9090_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */
+#define WM9090_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */
+#define WM9090_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */
+#define WM9090_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */
+#define WM9090_HPOUT1R_MUTE 0x0040 /* HPOUT1R_MUTE */
+#define WM9090_HPOUT1R_MUTE_MASK 0x0040 /* HPOUT1R_MUTE */
+#define WM9090_HPOUT1R_MUTE_SHIFT 6 /* HPOUT1R_MUTE */
+#define WM9090_HPOUT1R_MUTE_WIDTH 1 /* HPOUT1R_MUTE */
+#define WM9090_HPOUT1R_VOL_MASK 0x003F /* HPOUT1R_VOL - [5:0] */
+#define WM9090_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [5:0] */
+#define WM9090_HPOUT1R_VOL_WIDTH 6 /* HPOUT1R_VOL - [5:0] */
+
+/*
+ * R34 (0x22) - SPKMIXL Attenuation
+ */
+#define WM9090_SPKMIX_MUTE 0x0100 /* SPKMIX_MUTE */
+#define WM9090_SPKMIX_MUTE_MASK 0x0100 /* SPKMIX_MUTE */
+#define WM9090_SPKMIX_MUTE_SHIFT 8 /* SPKMIX_MUTE */
+#define WM9090_SPKMIX_MUTE_WIDTH 1 /* SPKMIX_MUTE */
+#define WM9090_IN1A_SPKMIX_VOL_MASK 0x00C0 /* IN1A_SPKMIX_VOL - [7:6] */
+#define WM9090_IN1A_SPKMIX_VOL_SHIFT 6 /* IN1A_SPKMIX_VOL - [7:6] */
+#define WM9090_IN1A_SPKMIX_VOL_WIDTH 2 /* IN1A_SPKMIX_VOL - [7:6] */
+#define WM9090_IN1B_SPKMIX_VOL_MASK 0x0030 /* IN1B_SPKMIX_VOL - [5:4] */
+#define WM9090_IN1B_SPKMIX_VOL_SHIFT 4 /* IN1B_SPKMIX_VOL - [5:4] */
+#define WM9090_IN1B_SPKMIX_VOL_WIDTH 2 /* IN1B_SPKMIX_VOL - [5:4] */
+#define WM9090_IN2A_SPKMIX_VOL_MASK 0x000C /* IN2A_SPKMIX_VOL - [3:2] */
+#define WM9090_IN2A_SPKMIX_VOL_SHIFT 2 /* IN2A_SPKMIX_VOL - [3:2] */
+#define WM9090_IN2A_SPKMIX_VOL_WIDTH 2 /* IN2A_SPKMIX_VOL - [3:2] */
+#define WM9090_IN2B_SPKMIX_VOL_MASK 0x0003 /* IN2B_SPKMIX_VOL - [1:0] */
+#define WM9090_IN2B_SPKMIX_VOL_SHIFT 0 /* IN2B_SPKMIX_VOL - [1:0] */
+#define WM9090_IN2B_SPKMIX_VOL_WIDTH 2 /* IN2B_SPKMIX_VOL - [1:0] */
+
+/*
+ * R36 (0x24) - SPKOUT Mixers
+ */
+#define WM9090_SPKMIXL_TO_SPKOUTL 0x0010 /* SPKMIXL_TO_SPKOUTL */
+#define WM9090_SPKMIXL_TO_SPKOUTL_MASK 0x0010 /* SPKMIXL_TO_SPKOUTL */
+#define WM9090_SPKMIXL_TO_SPKOUTL_SHIFT 4 /* SPKMIXL_TO_SPKOUTL */
+#define WM9090_SPKMIXL_TO_SPKOUTL_WIDTH 1 /* SPKMIXL_TO_SPKOUTL */
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM9090_SPKOUTL_BOOST_MASK 0x0038 /* SPKOUTL_BOOST - [5:3] */
+#define WM9090_SPKOUTL_BOOST_SHIFT 3 /* SPKOUTL_BOOST - [5:3] */
+#define WM9090_SPKOUTL_BOOST_WIDTH 3 /* SPKOUTL_BOOST - [5:3] */
+
+/*
+ * R38 (0x26) - Speaker Volume Left
+ */
+#define WM9090_SPKOUT_VU 0x0100 /* SPKOUT_VU */
+#define WM9090_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */
+#define WM9090_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */
+#define WM9090_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */
+#define WM9090_SPKOUTL_ZC 0x0080 /* SPKOUTL_ZC */
+#define WM9090_SPKOUTL_ZC_MASK 0x0080 /* SPKOUTL_ZC */
+#define WM9090_SPKOUTL_ZC_SHIFT 7 /* SPKOUTL_ZC */
+#define WM9090_SPKOUTL_ZC_WIDTH 1 /* SPKOUTL_ZC */
+#define WM9090_SPKOUTL_MUTE 0x0040 /* SPKOUTL_MUTE */
+#define WM9090_SPKOUTL_MUTE_MASK 0x0040 /* SPKOUTL_MUTE */
+#define WM9090_SPKOUTL_MUTE_SHIFT 6 /* SPKOUTL_MUTE */
+#define WM9090_SPKOUTL_MUTE_WIDTH 1 /* SPKOUTL_MUTE */
+#define WM9090_SPKOUTL_VOL_MASK 0x003F /* SPKOUTL_VOL - [5:0] */
+#define WM9090_SPKOUTL_VOL_SHIFT 0 /* SPKOUTL_VOL - [5:0] */
+#define WM9090_SPKOUTL_VOL_WIDTH 6 /* SPKOUTL_VOL - [5:0] */
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM9090_IN1A_TO_MIXOUTL 0x0040 /* IN1A_TO_MIXOUTL */
+#define WM9090_IN1A_TO_MIXOUTL_MASK 0x0040 /* IN1A_TO_MIXOUTL */
+#define WM9090_IN1A_TO_MIXOUTL_SHIFT 6 /* IN1A_TO_MIXOUTL */
+#define WM9090_IN1A_TO_MIXOUTL_WIDTH 1 /* IN1A_TO_MIXOUTL */
+#define WM9090_IN2A_TO_MIXOUTL 0x0004 /* IN2A_TO_MIXOUTL */
+#define WM9090_IN2A_TO_MIXOUTL_MASK 0x0004 /* IN2A_TO_MIXOUTL */
+#define WM9090_IN2A_TO_MIXOUTL_SHIFT 2 /* IN2A_TO_MIXOUTL */
+#define WM9090_IN2A_TO_MIXOUTL_WIDTH 1 /* IN2A_TO_MIXOUTL */
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM9090_IN1A_TO_MIXOUTR 0x0040 /* IN1A_TO_MIXOUTR */
+#define WM9090_IN1A_TO_MIXOUTR_MASK 0x0040 /* IN1A_TO_MIXOUTR */
+#define WM9090_IN1A_TO_MIXOUTR_SHIFT 6 /* IN1A_TO_MIXOUTR */
+#define WM9090_IN1A_TO_MIXOUTR_WIDTH 1 /* IN1A_TO_MIXOUTR */
+#define WM9090_IN1B_TO_MIXOUTR 0x0010 /* IN1B_TO_MIXOUTR */
+#define WM9090_IN1B_TO_MIXOUTR_MASK 0x0010 /* IN1B_TO_MIXOUTR */
+#define WM9090_IN1B_TO_MIXOUTR_SHIFT 4 /* IN1B_TO_MIXOUTR */
+#define WM9090_IN1B_TO_MIXOUTR_WIDTH 1 /* IN1B_TO_MIXOUTR */
+#define WM9090_IN2A_TO_MIXOUTR 0x0004 /* IN2A_TO_MIXOUTR */
+#define WM9090_IN2A_TO_MIXOUTR_MASK 0x0004 /* IN2A_TO_MIXOUTR */
+#define WM9090_IN2A_TO_MIXOUTR_SHIFT 2 /* IN2A_TO_MIXOUTR */
+#define WM9090_IN2A_TO_MIXOUTR_WIDTH 1 /* IN2A_TO_MIXOUTR */
+#define WM9090_IN2B_TO_MIXOUTR 0x0001 /* IN2B_TO_MIXOUTR */
+#define WM9090_IN2B_TO_MIXOUTR_MASK 0x0001 /* IN2B_TO_MIXOUTR */
+#define WM9090_IN2B_TO_MIXOUTR_SHIFT 0 /* IN2B_TO_MIXOUTR */
+#define WM9090_IN2B_TO_MIXOUTR_WIDTH 1 /* IN2B_TO_MIXOUTR */
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM9090_MIXOUTL_MUTE 0x0100 /* MIXOUTL_MUTE */
+#define WM9090_MIXOUTL_MUTE_MASK 0x0100 /* MIXOUTL_MUTE */
+#define WM9090_MIXOUTL_MUTE_SHIFT 8 /* MIXOUTL_MUTE */
+#define WM9090_MIXOUTL_MUTE_WIDTH 1 /* MIXOUTL_MUTE */
+#define WM9090_IN1A_MIXOUTL_VOL_MASK 0x00C0 /* IN1A_MIXOUTL_VOL - [7:6] */
+#define WM9090_IN1A_MIXOUTL_VOL_SHIFT 6 /* IN1A_MIXOUTL_VOL - [7:6] */
+#define WM9090_IN1A_MIXOUTL_VOL_WIDTH 2 /* IN1A_MIXOUTL_VOL - [7:6] */
+#define WM9090_IN2A_MIXOUTL_VOL_MASK 0x000C /* IN2A_MIXOUTL_VOL - [3:2] */
+#define WM9090_IN2A_MIXOUTL_VOL_SHIFT 2 /* IN2A_MIXOUTL_VOL - [3:2] */
+#define WM9090_IN2A_MIXOUTL_VOL_WIDTH 2 /* IN2A_MIXOUTL_VOL - [3:2] */
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM9090_MIXOUTR_MUTE 0x0100 /* MIXOUTR_MUTE */
+#define WM9090_MIXOUTR_MUTE_MASK 0x0100 /* MIXOUTR_MUTE */
+#define WM9090_MIXOUTR_MUTE_SHIFT 8 /* MIXOUTR_MUTE */
+#define WM9090_MIXOUTR_MUTE_WIDTH 1 /* MIXOUTR_MUTE */
+#define WM9090_IN1A_MIXOUTR_VOL_MASK 0x00C0 /* IN1A_MIXOUTR_VOL - [7:6] */
+#define WM9090_IN1A_MIXOUTR_VOL_SHIFT 6 /* IN1A_MIXOUTR_VOL - [7:6] */
+#define WM9090_IN1A_MIXOUTR_VOL_WIDTH 2 /* IN1A_MIXOUTR_VOL - [7:6] */
+#define WM9090_IN1B_MIXOUTR_VOL_MASK 0x0030 /* IN1B_MIXOUTR_VOL - [5:4] */
+#define WM9090_IN1B_MIXOUTR_VOL_SHIFT 4 /* IN1B_MIXOUTR_VOL - [5:4] */
+#define WM9090_IN1B_MIXOUTR_VOL_WIDTH 2 /* IN1B_MIXOUTR_VOL - [5:4] */
+#define WM9090_IN2A_MIXOUTR_VOL_MASK 0x000C /* IN2A_MIXOUTR_VOL - [3:2] */
+#define WM9090_IN2A_MIXOUTR_VOL_SHIFT 2 /* IN2A_MIXOUTR_VOL - [3:2] */
+#define WM9090_IN2A_MIXOUTR_VOL_WIDTH 2 /* IN2A_MIXOUTR_VOL - [3:2] */
+#define WM9090_IN2B_MIXOUTR_VOL_MASK 0x0003 /* IN2B_MIXOUTR_VOL - [1:0] */
+#define WM9090_IN2B_MIXOUTR_VOL_SHIFT 0 /* IN2B_MIXOUTR_VOL - [1:0] */
+#define WM9090_IN2B_MIXOUTR_VOL_WIDTH 2 /* IN2B_MIXOUTR_VOL - [1:0] */
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM9090_IN1A_TO_SPKMIX 0x0040 /* IN1A_TO_SPKMIX */
+#define WM9090_IN1A_TO_SPKMIX_MASK 0x0040 /* IN1A_TO_SPKMIX */
+#define WM9090_IN1A_TO_SPKMIX_SHIFT 6 /* IN1A_TO_SPKMIX */
+#define WM9090_IN1A_TO_SPKMIX_WIDTH 1 /* IN1A_TO_SPKMIX */
+#define WM9090_IN1B_TO_SPKMIX 0x0010 /* IN1B_TO_SPKMIX */
+#define WM9090_IN1B_TO_SPKMIX_MASK 0x0010 /* IN1B_TO_SPKMIX */
+#define WM9090_IN1B_TO_SPKMIX_SHIFT 4 /* IN1B_TO_SPKMIX */
+#define WM9090_IN1B_TO_SPKMIX_WIDTH 1 /* IN1B_TO_SPKMIX */
+#define WM9090_IN2A_TO_SPKMIX 0x0004 /* IN2A_TO_SPKMIX */
+#define WM9090_IN2A_TO_SPKMIX_MASK 0x0004 /* IN2A_TO_SPKMIX */
+#define WM9090_IN2A_TO_SPKMIX_SHIFT 2 /* IN2A_TO_SPKMIX */
+#define WM9090_IN2A_TO_SPKMIX_WIDTH 1 /* IN2A_TO_SPKMIX */
+#define WM9090_IN2B_TO_SPKMIX 0x0001 /* IN2B_TO_SPKMIX */
+#define WM9090_IN2B_TO_SPKMIX_MASK 0x0001 /* IN2B_TO_SPKMIX */
+#define WM9090_IN2B_TO_SPKMIX_SHIFT 0 /* IN2B_TO_SPKMIX */
+#define WM9090_IN2B_TO_SPKMIX_WIDTH 1 /* IN2B_TO_SPKMIX */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM9090_VMID_BUF_ENA 0x0008 /* VMID_BUF_ENA */
+#define WM9090_VMID_BUF_ENA_MASK 0x0008 /* VMID_BUF_ENA */
+#define WM9090_VMID_BUF_ENA_SHIFT 3 /* VMID_BUF_ENA */
+#define WM9090_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */
+#define WM9090_VMID_ENA 0x0001 /* VMID_ENA */
+#define WM9090_VMID_ENA_MASK 0x0001 /* VMID_ENA */
+#define WM9090_VMID_ENA_SHIFT 0 /* VMID_ENA */
+#define WM9090_VMID_ENA_WIDTH 1 /* VMID_ENA */
+
+/*
+ * R70 (0x46) - Write Sequencer 0
+ */
+#define WM9090_WSEQ_ENA 0x0100 /* WSEQ_ENA */
+#define WM9090_WSEQ_ENA_MASK 0x0100 /* WSEQ_ENA */
+#define WM9090_WSEQ_ENA_SHIFT 8 /* WSEQ_ENA */
+#define WM9090_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM9090_WSEQ_WRITE_INDEX_MASK 0x000F /* WSEQ_WRITE_INDEX - [3:0] */
+#define WM9090_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [3:0] */
+#define WM9090_WSEQ_WRITE_INDEX_WIDTH 4 /* WSEQ_WRITE_INDEX - [3:0] */
+
+/*
+ * R71 (0x47) - Write Sequencer 1
+ */
+#define WM9090_WSEQ_DATA_WIDTH_MASK 0x7000 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM9090_WSEQ_DATA_WIDTH_SHIFT 12 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM9090_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM9090_WSEQ_DATA_START_MASK 0x0F00 /* WSEQ_DATA_START - [11:8] */
+#define WM9090_WSEQ_DATA_START_SHIFT 8 /* WSEQ_DATA_START - [11:8] */
+#define WM9090_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [11:8] */
+#define WM9090_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */
+#define WM9090_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */
+#define WM9090_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R72 (0x48) - Write Sequencer 2
+ */
+#define WM9090_WSEQ_EOS 0x4000 /* WSEQ_EOS */
+#define WM9090_WSEQ_EOS_MASK 0x4000 /* WSEQ_EOS */
+#define WM9090_WSEQ_EOS_SHIFT 14 /* WSEQ_EOS */
+#define WM9090_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */
+#define WM9090_WSEQ_DELAY_MASK 0x0F00 /* WSEQ_DELAY - [11:8] */
+#define WM9090_WSEQ_DELAY_SHIFT 8 /* WSEQ_DELAY - [11:8] */
+#define WM9090_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [11:8] */
+#define WM9090_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */
+#define WM9090_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */
+#define WM9090_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */
+
+/*
+ * R73 (0x49) - Write Sequencer 3
+ */
+#define WM9090_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM9090_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM9090_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM9090_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM9090_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM9090_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM9090_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM9090_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM9090_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */
+#define WM9090_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */
+#define WM9090_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R74 (0x4A) - Write Sequencer 4
+ */
+#define WM9090_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */
+#define WM9090_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */
+#define WM9090_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */
+#define WM9090_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+
+/*
+ * R75 (0x4B) - Write Sequencer 5
+ */
+#define WM9090_WSEQ_CURRENT_INDEX_MASK 0x003F /* WSEQ_CURRENT_INDEX - [5:0] */
+#define WM9090_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [5:0] */
+#define WM9090_WSEQ_CURRENT_INDEX_WIDTH 6 /* WSEQ_CURRENT_INDEX - [5:0] */
+
+/*
+ * R76 (0x4C) - Charge Pump 1
+ */
+#define WM9090_CP_ENA 0x8000 /* CP_ENA */
+#define WM9090_CP_ENA_MASK 0x8000 /* CP_ENA */
+#define WM9090_CP_ENA_SHIFT 15 /* CP_ENA */
+#define WM9090_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R84 (0x54) - DC Servo 0
+ */
+#define WM9090_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM9090_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM9090_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */
+#define WM9090_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */
+#define WM9090_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM9090_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM9090_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */
+#define WM9090_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */
+#define WM9090_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM9090_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM9090_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */
+#define WM9090_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */
+#define WM9090_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM9090_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM9090_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */
+#define WM9090_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */
+#define WM9090_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM9090_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM9090_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */
+#define WM9090_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */
+#define WM9090_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM9090_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM9090_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */
+#define WM9090_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */
+#define WM9090_DCS_TRIG_DAC_WR_1 0x0008 /* DCS_TRIG_DAC_WR_1 */
+#define WM9090_DCS_TRIG_DAC_WR_1_MASK 0x0008 /* DCS_TRIG_DAC_WR_1 */
+#define WM9090_DCS_TRIG_DAC_WR_1_SHIFT 3 /* DCS_TRIG_DAC_WR_1 */
+#define WM9090_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM9090_DCS_TRIG_DAC_WR_0 0x0004 /* DCS_TRIG_DAC_WR_0 */
+#define WM9090_DCS_TRIG_DAC_WR_0_MASK 0x0004 /* DCS_TRIG_DAC_WR_0 */
+#define WM9090_DCS_TRIG_DAC_WR_0_SHIFT 2 /* DCS_TRIG_DAC_WR_0 */
+#define WM9090_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */
+#define WM9090_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM9090_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM9090_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */
+#define WM9090_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */
+#define WM9090_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM9090_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM9090_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */
+#define WM9090_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */
+
+/*
+ * R85 (0x55) - DC Servo 1
+ */
+#define WM9090_DCS_SERIES_NO_01_MASK 0x0FE0 /* DCS_SERIES_NO_01 - [11:5] */
+#define WM9090_DCS_SERIES_NO_01_SHIFT 5 /* DCS_SERIES_NO_01 - [11:5] */
+#define WM9090_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [11:5] */
+#define WM9090_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM9090_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM9090_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R87 (0x57) - DC Servo 3
+ */
+#define WM9090_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM9090_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM9090_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM9090_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM9090_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM9090_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R88 (0x58) - DC Servo Readback 0
+ */
+#define WM9090_DCS_CAL_COMPLETE_MASK 0x0300 /* DCS_CAL_COMPLETE - [9:8] */
+#define WM9090_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [9:8] */
+#define WM9090_DCS_CAL_COMPLETE_WIDTH 2 /* DCS_CAL_COMPLETE - [9:8] */
+#define WM9090_DCS_DAC_WR_COMPLETE_MASK 0x0030 /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM9090_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM9090_DCS_DAC_WR_COMPLETE_WIDTH 2 /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM9090_DCS_STARTUP_COMPLETE_MASK 0x0003 /* DCS_STARTUP_COMPLETE - [1:0] */
+#define WM9090_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [1:0] */
+#define WM9090_DCS_STARTUP_COMPLETE_WIDTH 2 /* DCS_STARTUP_COMPLETE - [1:0] */
+
+/*
+ * R89 (0x59) - DC Servo Readback 1
+ */
+#define WM9090_DCS_DAC_WR_VAL_1_RD_MASK 0x00FF /* DCS_DAC_WR_VAL_1_RD - [7:0] */
+#define WM9090_DCS_DAC_WR_VAL_1_RD_SHIFT 0 /* DCS_DAC_WR_VAL_1_RD - [7:0] */
+#define WM9090_DCS_DAC_WR_VAL_1_RD_WIDTH 8 /* DCS_DAC_WR_VAL_1_RD - [7:0] */
+
+/*
+ * R90 (0x5A) - DC Servo Readback 2
+ */
+#define WM9090_DCS_DAC_WR_VAL_0_RD_MASK 0x00FF /* DCS_DAC_WR_VAL_0_RD - [7:0] */
+#define WM9090_DCS_DAC_WR_VAL_0_RD_SHIFT 0 /* DCS_DAC_WR_VAL_0_RD - [7:0] */
+#define WM9090_DCS_DAC_WR_VAL_0_RD_WIDTH 8 /* DCS_DAC_WR_VAL_0_RD - [7:0] */
+
+/*
+ * R96 (0x60) - Analogue HP 0
+ */
+#define WM9090_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM9090_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM9090_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */
+#define WM9090_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */
+#define WM9090_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */
+#define WM9090_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */
+#define WM9090_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */
+#define WM9090_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */
+#define WM9090_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */
+#define WM9090_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */
+#define WM9090_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */
+#define WM9090_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */
+#define WM9090_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM9090_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM9090_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */
+#define WM9090_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */
+#define WM9090_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */
+#define WM9090_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */
+#define WM9090_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */
+#define WM9090_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */
+#define WM9090_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */
+#define WM9090_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */
+#define WM9090_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */
+#define WM9090_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */
+
+/*
+ * R98 (0x62) - AGC Control 0
+ */
+#define WM9090_AGC_CLIP_ENA 0x8000 /* AGC_CLIP_ENA */
+#define WM9090_AGC_CLIP_ENA_MASK 0x8000 /* AGC_CLIP_ENA */
+#define WM9090_AGC_CLIP_ENA_SHIFT 15 /* AGC_CLIP_ENA */
+#define WM9090_AGC_CLIP_ENA_WIDTH 1 /* AGC_CLIP_ENA */
+#define WM9090_AGC_CLIP_THR_MASK 0x0F00 /* AGC_CLIP_THR - [11:8] */
+#define WM9090_AGC_CLIP_THR_SHIFT 8 /* AGC_CLIP_THR - [11:8] */
+#define WM9090_AGC_CLIP_THR_WIDTH 4 /* AGC_CLIP_THR - [11:8] */
+#define WM9090_AGC_CLIP_ATK_MASK 0x0070 /* AGC_CLIP_ATK - [6:4] */
+#define WM9090_AGC_CLIP_ATK_SHIFT 4 /* AGC_CLIP_ATK - [6:4] */
+#define WM9090_AGC_CLIP_ATK_WIDTH 3 /* AGC_CLIP_ATK - [6:4] */
+#define WM9090_AGC_CLIP_DCY_MASK 0x0007 /* AGC_CLIP_DCY - [2:0] */
+#define WM9090_AGC_CLIP_DCY_SHIFT 0 /* AGC_CLIP_DCY - [2:0] */
+#define WM9090_AGC_CLIP_DCY_WIDTH 3 /* AGC_CLIP_DCY - [2:0] */
+
+/*
+ * R99 (0x63) - AGC Control 1
+ */
+#define WM9090_AGC_PWR_ENA 0x8000 /* AGC_PWR_ENA */
+#define WM9090_AGC_PWR_ENA_MASK 0x8000 /* AGC_PWR_ENA */
+#define WM9090_AGC_PWR_ENA_SHIFT 15 /* AGC_PWR_ENA */
+#define WM9090_AGC_PWR_ENA_WIDTH 1 /* AGC_PWR_ENA */
+#define WM9090_AGC_PWR_AVG 0x1000 /* AGC_PWR_AVG */
+#define WM9090_AGC_PWR_AVG_MASK 0x1000 /* AGC_PWR_AVG */
+#define WM9090_AGC_PWR_AVG_SHIFT 12 /* AGC_PWR_AVG */
+#define WM9090_AGC_PWR_AVG_WIDTH 1 /* AGC_PWR_AVG */
+#define WM9090_AGC_PWR_THR_MASK 0x0F00 /* AGC_PWR_THR - [11:8] */
+#define WM9090_AGC_PWR_THR_SHIFT 8 /* AGC_PWR_THR - [11:8] */
+#define WM9090_AGC_PWR_THR_WIDTH 4 /* AGC_PWR_THR - [11:8] */
+#define WM9090_AGC_PWR_ATK_MASK 0x0070 /* AGC_PWR_ATK - [6:4] */
+#define WM9090_AGC_PWR_ATK_SHIFT 4 /* AGC_PWR_ATK - [6:4] */
+#define WM9090_AGC_PWR_ATK_WIDTH 3 /* AGC_PWR_ATK - [6:4] */
+#define WM9090_AGC_PWR_DCY_MASK 0x0007 /* AGC_PWR_DCY - [2:0] */
+#define WM9090_AGC_PWR_DCY_SHIFT 0 /* AGC_PWR_DCY - [2:0] */
+#define WM9090_AGC_PWR_DCY_WIDTH 3 /* AGC_PWR_DCY - [2:0] */
+
+/*
+ * R100 (0x64) - AGC Control 2
+ */
+#define WM9090_AGC_RAMP 0x0100 /* AGC_RAMP */
+#define WM9090_AGC_RAMP_MASK 0x0100 /* AGC_RAMP */
+#define WM9090_AGC_RAMP_SHIFT 8 /* AGC_RAMP */
+#define WM9090_AGC_RAMP_WIDTH 1 /* AGC_RAMP */
+#define WM9090_AGC_MINGAIN_MASK 0x003F /* AGC_MINGAIN - [5:0] */
+#define WM9090_AGC_MINGAIN_SHIFT 0 /* AGC_MINGAIN - [5:0] */
+#define WM9090_AGC_MINGAIN_WIDTH 6 /* AGC_MINGAIN - [5:0] */
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm9705.c b/kernel/sound/soc/codecs/wm9705.c
new file mode 100644
index 000000000..5cc457ef8
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm9705.c
@@ -0,0 +1,424 @@
+/*
+ * wm9705.c -- ALSA Soc WM9705 codec support
+ *
+ * Copyright 2008 Ian Molton <spyro@f2s.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 only.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "wm9705.h"
+
+/*
+ * WM9705 register cache
+ */
+static const u16 wm9705_reg[] = {
+ 0x6150, 0x8000, 0x8000, 0x8000, /* 0x0 */
+ 0x0000, 0x8000, 0x8008, 0x8008, /* 0x8 */
+ 0x8808, 0x8808, 0x8808, 0x8808, /* 0x10 */
+ 0x8808, 0x0000, 0x8000, 0x0000, /* 0x18 */
+ 0x0000, 0x0000, 0x0000, 0x000f, /* 0x20 */
+ 0x0605, 0x0000, 0xbb80, 0x0000, /* 0x28 */
+ 0x0000, 0xbb80, 0x0000, 0x0000, /* 0x30 */
+ 0x0000, 0x2000, 0x0000, 0x0000, /* 0x38 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x40 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x48 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x50 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x58 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x60 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x68 */
+ 0x0000, 0x0808, 0x0000, 0x0006, /* 0x70 */
+ 0x0000, 0x0000, 0x574d, 0x4c05, /* 0x78 */
+};
+
+static const struct snd_kcontrol_new wm9705_snd_ac97_controls[] = {
+ SOC_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1),
+ SOC_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1),
+ SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
+ SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
+ SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1),
+ SOC_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1),
+ SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1),
+ SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
+ SOC_SINGLE("PCBeep Playback Volume", AC97_PC_BEEP, 1, 15, 1),
+ SOC_SINGLE("Phone Playback Volume", AC97_PHONE, 0, 31, 1),
+ SOC_DOUBLE("Line Playback Volume", AC97_LINE, 8, 0, 31, 1),
+ SOC_DOUBLE("CD Playback Volume", AC97_CD, 8, 0, 31, 1),
+ SOC_SINGLE("Mic Playback Volume", AC97_MIC, 0, 31, 1),
+ SOC_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 6, 1, 0),
+ SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0),
+ SOC_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1),
+};
+
+static const char *wm9705_mic[] = {"Mic 1", "Mic 2"};
+static const char *wm9705_rec_sel[] = {"Mic", "CD", "NC", "NC",
+ "Line", "Stereo Mix", "Mono Mix", "Phone"};
+
+static SOC_ENUM_SINGLE_DECL(wm9705_enum_mic,
+ AC97_GENERAL_PURPOSE, 8, wm9705_mic);
+static SOC_ENUM_SINGLE_DECL(wm9705_enum_rec_l,
+ AC97_REC_SEL, 8, wm9705_rec_sel);
+static SOC_ENUM_SINGLE_DECL(wm9705_enum_rec_r,
+ AC97_REC_SEL, 0, wm9705_rec_sel);
+
+/* Headphone Mixer */
+static const struct snd_kcontrol_new wm9705_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCBeep Playback Switch", AC97_PC_BEEP, 15, 1, 1),
+ SOC_DAPM_SINGLE("CD Playback Switch", AC97_CD, 15, 1, 1),
+ SOC_DAPM_SINGLE("Mic Playback Switch", AC97_MIC, 15, 1, 1),
+ SOC_DAPM_SINGLE("Phone Playback Switch", AC97_PHONE, 15, 1, 1),
+ SOC_DAPM_SINGLE("Line Playback Switch", AC97_LINE, 15, 1, 1),
+};
+
+/* Mic source */
+static const struct snd_kcontrol_new wm9705_mic_src_controls =
+ SOC_DAPM_ENUM("Route", wm9705_enum_mic);
+
+/* Capture source */
+static const struct snd_kcontrol_new wm9705_capture_selectl_controls =
+ SOC_DAPM_ENUM("Route", wm9705_enum_rec_l);
+static const struct snd_kcontrol_new wm9705_capture_selectr_controls =
+ SOC_DAPM_ENUM("Route", wm9705_enum_rec_r);
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget wm9705_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("Mic Source", SND_SOC_NOPM, 0, 0,
+ &wm9705_mic_src_controls),
+ SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0,
+ &wm9705_capture_selectl_controls),
+ SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0,
+ &wm9705_capture_selectr_controls),
+ SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback",
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback",
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MIXER_NAMED_CTL("HP Mixer", SND_SOC_NOPM, 0, 0,
+ &wm9705_hp_mixer_controls[0],
+ ARRAY_SIZE(wm9705_hp_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA("Headphone PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Speaker PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line out PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Phone PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PCBEEP PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("CD PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("HPOUTL"),
+ SND_SOC_DAPM_OUTPUT("HPOUTR"),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("MONOOUT"),
+ SND_SOC_DAPM_INPUT("PHONE"),
+ SND_SOC_DAPM_INPUT("LINEINL"),
+ SND_SOC_DAPM_INPUT("LINEINR"),
+ SND_SOC_DAPM_INPUT("CDINL"),
+ SND_SOC_DAPM_INPUT("CDINR"),
+ SND_SOC_DAPM_INPUT("PCBEEP"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+};
+
+/* Audio map
+ * WM9705 has no switches to disable the route from the inputs to the HP mixer
+ * so in order to prevent active inputs from forcing the audio outputs to be
+ * constantly enabled, we use the mutes on those inputs to simulate such
+ * controls.
+ */
+static const struct snd_soc_dapm_route wm9705_audio_map[] = {
+ /* HP mixer */
+ {"HP Mixer", "PCBeep Playback Switch", "PCBEEP PGA"},
+ {"HP Mixer", "CD Playback Switch", "CD PGA"},
+ {"HP Mixer", "Mic Playback Switch", "Mic PGA"},
+ {"HP Mixer", "Phone Playback Switch", "Phone PGA"},
+ {"HP Mixer", "Line Playback Switch", "Line PGA"},
+ {"HP Mixer", NULL, "Left DAC"},
+ {"HP Mixer", NULL, "Right DAC"},
+
+ /* mono mixer */
+ {"Mono Mixer", NULL, "HP Mixer"},
+
+ /* outputs */
+ {"Headphone PGA", NULL, "HP Mixer"},
+ {"HPOUTL", NULL, "Headphone PGA"},
+ {"HPOUTR", NULL, "Headphone PGA"},
+ {"Line out PGA", NULL, "HP Mixer"},
+ {"LOUT", NULL, "Line out PGA"},
+ {"ROUT", NULL, "Line out PGA"},
+ {"Mono PGA", NULL, "Mono Mixer"},
+ {"MONOOUT", NULL, "Mono PGA"},
+
+ /* inputs */
+ {"CD PGA", NULL, "CDINL"},
+ {"CD PGA", NULL, "CDINR"},
+ {"Line PGA", NULL, "LINEINL"},
+ {"Line PGA", NULL, "LINEINR"},
+ {"Phone PGA", NULL, "PHONE"},
+ {"Mic Source", "Mic 1", "MIC1"},
+ {"Mic Source", "Mic 2", "MIC2"},
+ {"Mic PGA", NULL, "Mic Source"},
+ {"PCBEEP PGA", NULL, "PCBEEP"},
+
+ /* Left capture selector */
+ {"Left Capture Source", "Mic", "Mic Source"},
+ {"Left Capture Source", "CD", "CDINL"},
+ {"Left Capture Source", "Line", "LINEINL"},
+ {"Left Capture Source", "Stereo Mix", "HP Mixer"},
+ {"Left Capture Source", "Mono Mix", "HP Mixer"},
+ {"Left Capture Source", "Phone", "PHONE"},
+
+ /* Right capture source */
+ {"Right Capture Source", "Mic", "Mic Source"},
+ {"Right Capture Source", "CD", "CDINR"},
+ {"Right Capture Source", "Line", "LINEINR"},
+ {"Right Capture Source", "Stereo Mix", "HP Mixer"},
+ {"Right Capture Source", "Mono Mix", "HP Mixer"},
+ {"Right Capture Source", "Phone", "PHONE"},
+
+ {"ADC PGA", NULL, "Left Capture Source"},
+ {"ADC PGA", NULL, "Right Capture Source"},
+
+ /* ADC's */
+ {"Left ADC", NULL, "ADC PGA"},
+ {"Right ADC", NULL, "ADC PGA"},
+};
+
+/* We use a register cache to enhance read performance. */
+static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ u16 *cache = codec->reg_cache;
+
+ switch (reg) {
+ case AC97_RESET:
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ return soc_ac97_ops->read(ac97, reg);
+ default:
+ reg = reg >> 1;
+
+ if (reg >= (ARRAY_SIZE(wm9705_reg)))
+ return -EIO;
+
+ return cache[reg];
+ }
+}
+
+static int 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;
+
+ soc_ac97_ops->write(ac97, reg, val);
+ reg = reg >> 1;
+ if (reg < (ARRAY_SIZE(wm9705_reg)))
+ cache[reg] = val;
+
+ return 0;
+}
+
+static int ac97_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int reg;
+ u16 vra;
+
+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = AC97_PCM_FRONT_DAC_RATE;
+ else
+ reg = AC97_PCM_LR_ADC_RATE;
+
+ return ac97_write(codec, reg, substream->runtime->rate);
+}
+
+#define WM9705_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+static const struct snd_soc_dai_ops wm9705_dai_ops = {
+ .prepare = ac97_prepare,
+};
+
+static struct snd_soc_dai_driver wm9705_dai[] = {
+ {
+ .name = "wm9705-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9705_AC97_RATES,
+ .formats = SND_SOC_STD_AC97_FMTS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9705_AC97_RATES,
+ .formats = SND_SOC_STD_AC97_FMTS,
+ },
+ .ops = &wm9705_dai_ops,
+ },
+ {
+ .name = "wm9705-aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = WM9705_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ }
+};
+
+static int wm9705_reset(struct snd_soc_codec *codec)
+{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ if (soc_ac97_ops->reset) {
+ soc_ac97_ops->reset(ac97);
+ if (ac97_read(codec, 0) == wm9705_reg[0])
+ return 0; /* Success */
+ }
+
+ dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+
+ return -EIO;
+}
+
+#ifdef CONFIG_PM
+static int wm9705_soc_suspend(struct snd_soc_codec *codec)
+{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ soc_ac97_ops->write(ac97, AC97_POWERDOWN, 0xffff);
+
+ return 0;
+}
+
+static int wm9705_soc_resume(struct snd_soc_codec *codec)
+{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
+ u16 *cache = codec->reg_cache;
+
+ ret = wm9705_reset(codec);
+ if (ret < 0)
+ return ret;
+
+ for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) {
+ soc_ac97_ops->write(ac97, i, cache[i>>1]);
+ }
+
+ return 0;
+}
+#else
+#define wm9705_soc_suspend NULL
+#define wm9705_soc_resume NULL
+#endif
+
+static int wm9705_soc_probe(struct snd_soc_codec *codec)
+{
+ struct snd_ac97 *ac97;
+ int ret = 0;
+
+ ac97 = snd_soc_alloc_ac97_codec(codec);
+ if (IS_ERR(ac97)) {
+ ret = PTR_ERR(ac97);
+ dev_err(codec->dev, "Failed to register AC97 codec\n");
+ return ret;
+ }
+
+ ret = wm9705_reset(codec);
+ if (ret)
+ goto err_put_device;
+
+ ret = device_add(&ac97->dev);
+ if (ret)
+ goto err_put_device;
+
+ snd_soc_codec_set_drvdata(codec, ac97);
+
+ return 0;
+
+err_put_device:
+ put_device(&ac97->dev);
+ return ret;
+}
+
+static int wm9705_soc_remove(struct snd_soc_codec *codec)
+{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_free_ac97_codec(ac97);
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
+ .probe = wm9705_soc_probe,
+ .remove = wm9705_soc_remove,
+ .suspend = wm9705_soc_suspend,
+ .resume = wm9705_soc_resume,
+ .read = ac97_read,
+ .write = ac97_write,
+ .reg_cache_size = ARRAY_SIZE(wm9705_reg),
+ .reg_word_size = sizeof(u16),
+ .reg_cache_step = 2,
+ .reg_cache_default = wm9705_reg,
+
+ .controls = wm9705_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls),
+ .dapm_widgets = wm9705_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
+ .dapm_routes = wm9705_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9705_audio_map),
+};
+
+static int wm9705_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_wm9705, wm9705_dai, ARRAY_SIZE(wm9705_dai));
+}
+
+static int wm9705_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver wm9705_codec_driver = {
+ .driver = {
+ .name = "wm9705-codec",
+ },
+
+ .probe = wm9705_probe,
+ .remove = wm9705_remove,
+};
+
+module_platform_driver(wm9705_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM9705 driver");
+MODULE_AUTHOR("Ian Molton");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/wm9705.h b/kernel/sound/soc/codecs/wm9705.h
new file mode 100644
index 000000000..23ea9ce47
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm9705.h
@@ -0,0 +1,11 @@
+/*
+ * wm9705.h -- WM9705 Soc Audio driver
+ */
+
+#ifndef _WM9705_H
+#define _WM9705_H
+
+#define WM9705_DAI_AC97_HIFI 0
+#define WM9705_DAI_AC97_AUX 1
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm9712.c b/kernel/sound/soc/codecs/wm9712.c
new file mode 100644
index 000000000..98c9525bd
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm9712.c
@@ -0,0 +1,758 @@
+/*
+ * wm9712.c -- ALSA Soc WM9712 codec support
+ *
+ * Copyright 2006-12 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.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "wm9712.h"
+
+struct wm9712_priv {
+ struct snd_ac97 *ac97;
+ unsigned int hp_mixer[2];
+ struct mutex lock;
+};
+
+static unsigned int ac97_read(struct snd_soc_codec *codec,
+ unsigned int reg);
+static int ac97_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int val);
+
+/*
+ * WM9712 register cache
+ */
+static const u16 wm9712_reg[] = {
+ 0x6174, 0x8000, 0x8000, 0x8000, /* 6 */
+ 0x0f0f, 0xaaa0, 0xc008, 0x6808, /* e */
+ 0xe808, 0xaaa0, 0xad00, 0x8000, /* 16 */
+ 0xe808, 0x3000, 0x8000, 0x0000, /* 1e */
+ 0x0000, 0x0000, 0x0000, 0x000f, /* 26 */
+ 0x0405, 0x0410, 0xbb80, 0xbb80, /* 2e */
+ 0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */
+ 0x0000, 0x2000, 0x0000, 0x0000, /* 3e */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 46 */
+ 0x0000, 0x0000, 0xf83e, 0xffff, /* 4e */
+ 0x0000, 0x0000, 0x0000, 0xf83e, /* 56 */
+ 0x0008, 0x0000, 0x0000, 0x0000, /* 5e */
+ 0xb032, 0x3e00, 0x0000, 0x0000, /* 66 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
+ 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
+ 0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */
+};
+
+#define HPL_MIXER 0x0
+#define HPR_MIXER 0x1
+
+static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
+static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"};
+static const char *wm9712_out3_src[] = {"Left", "VREF", "Left + Right",
+ "Mono"};
+static const char *wm9712_spk_src[] = {"Speaker Mix", "Headphone Mix"};
+static const char *wm9712_rec_adc[] = {"Stereo", "Left", "Right", "Mute"};
+static const char *wm9712_base[] = {"Linear Control", "Adaptive Boost"};
+static const char *wm9712_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
+static const char *wm9712_mic[] = {"Mic 1", "Differential", "Mic 2",
+ "Stereo"};
+static const char *wm9712_rec_sel[] = {"Mic", "NC", "NC", "Speaker Mixer",
+ "Line", "Headphone Mixer", "Phone Mixer", "Phone"};
+static const char *wm9712_ng_type[] = {"Constant Gain", "Mute"};
+static const char *wm9712_diff_sel[] = {"Mic", "Line"};
+
+static const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 2000, 0);
+
+static const struct soc_enum wm9712_enum[] = {
+SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9712_alc_select),
+SOC_ENUM_SINGLE(AC97_VIDEO, 12, 4, wm9712_alc_mux),
+SOC_ENUM_SINGLE(AC97_AUX, 9, 4, wm9712_out3_src),
+SOC_ENUM_SINGLE(AC97_AUX, 8, 2, wm9712_spk_src),
+SOC_ENUM_SINGLE(AC97_REC_SEL, 12, 4, wm9712_rec_adc),
+SOC_ENUM_SINGLE(AC97_MASTER_TONE, 15, 2, wm9712_base),
+SOC_ENUM_DOUBLE(AC97_REC_GAIN, 14, 6, 2, wm9712_rec_gain),
+SOC_ENUM_SINGLE(AC97_MIC, 5, 4, wm9712_mic),
+SOC_ENUM_SINGLE(AC97_REC_SEL, 8, 8, wm9712_rec_sel),
+SOC_ENUM_SINGLE(AC97_REC_SEL, 0, 8, wm9712_rec_sel),
+SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9712_ng_type),
+SOC_ENUM_SINGLE(0x5c, 8, 2, wm9712_diff_sel),
+};
+
+static const struct snd_kcontrol_new wm9712_snd_ac97_controls[] = {
+SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
+SOC_SINGLE("Speaker Playback Switch", AC97_MASTER, 15, 1, 1),
+SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
+SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
+SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1),
+
+SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0),
+SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0),
+SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0),
+SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0),
+SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1),
+SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
+
+SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
+SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
+SOC_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0),
+SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
+SOC_ENUM("ALC Function", wm9712_enum[0]),
+SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
+SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 1),
+SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
+SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
+SOC_ENUM("ALC NG Type", wm9712_enum[10]),
+SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 1),
+
+SOC_SINGLE("Mic Headphone Volume", AC97_VIDEO, 12, 7, 1),
+SOC_SINGLE("ALC Headphone Volume", AC97_VIDEO, 7, 7, 1),
+
+SOC_SINGLE("Out3 Switch", AC97_AUX, 15, 1, 1),
+SOC_SINGLE("Out3 ZC Switch", AC97_AUX, 7, 1, 1),
+SOC_SINGLE("Out3 Volume", AC97_AUX, 0, 31, 1),
+
+SOC_SINGLE("PCBeep Bypass Headphone Volume", AC97_PC_BEEP, 12, 7, 1),
+SOC_SINGLE("PCBeep Bypass Speaker Volume", AC97_PC_BEEP, 8, 7, 1),
+SOC_SINGLE("PCBeep Bypass Phone Volume", AC97_PC_BEEP, 4, 7, 1),
+
+SOC_SINGLE("Aux Playback Headphone Volume", AC97_CD, 12, 7, 1),
+SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1),
+SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1),
+
+SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 1),
+SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1),
+
+SOC_SINGLE_TLV("Capture Boost Switch", AC97_REC_SEL, 14, 1, 0, boost_tlv),
+SOC_SINGLE_TLV("Capture to Phone Boost Switch", AC97_REC_SEL, 11, 1, 1,
+ boost_tlv),
+
+SOC_SINGLE("3D Upper Cut-off Switch", AC97_3D_CONTROL, 5, 1, 1),
+SOC_SINGLE("3D Lower Cut-off Switch", AC97_3D_CONTROL, 4, 1, 1),
+SOC_SINGLE("3D Playback Volume", AC97_3D_CONTROL, 0, 15, 0),
+
+SOC_ENUM("Bass Control", wm9712_enum[5]),
+SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1),
+SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1),
+SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0),
+SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 1),
+SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 1),
+
+SOC_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1),
+SOC_ENUM("Capture Volume Steps", wm9712_enum[6]),
+SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 0),
+SOC_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0),
+
+SOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv),
+SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
+SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv),
+};
+
+static const unsigned int wm9712_mixer_mute_regs[] = {
+ AC97_VIDEO,
+ AC97_PCM,
+ AC97_LINE,
+ AC97_PHONE,
+ AC97_CD,
+ AC97_PC_BEEP,
+};
+
+/* We have to create a fake left and right HP mixers because
+ * the codec only has a single control that is shared by both channels.
+ * This makes it impossible to determine the audio path.
+ */
+static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = ucontrol->value.integer.value[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int mixer, mask, shift, old;
+ struct snd_soc_dapm_update update;
+ bool change;
+
+ mixer = mc->shift >> 8;
+ shift = mc->shift & 0xff;
+ mask = 1 << shift;
+
+ mutex_lock(&wm9712->lock);
+ old = wm9712->hp_mixer[mixer];
+ if (ucontrol->value.integer.value[0])
+ wm9712->hp_mixer[mixer] |= mask;
+ else
+ wm9712->hp_mixer[mixer] &= ~mask;
+
+ change = old != wm9712->hp_mixer[mixer];
+ if (change) {
+ update.kcontrol = kcontrol;
+ update.reg = wm9712_mixer_mute_regs[shift];
+ update.mask = 0x8000;
+ if ((wm9712->hp_mixer[0] & mask) ||
+ (wm9712->hp_mixer[1] & mask))
+ update.val = 0x0;
+ else
+ update.val = 0x8000;
+
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
+ &update);
+ }
+
+ mutex_unlock(&wm9712->lock);
+
+ return change;
+}
+
+static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int shift, mixer;
+
+ mixer = mc->shift >> 8;
+ shift = mc->shift & 0xff;
+
+ ucontrol->value.integer.value[0] =
+ (wm9712->hp_mixer[mixer] >> shift) & 1;
+
+ return 0;
+}
+
+#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, \
+ .get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \
+ .private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \
+ (xmixer << 8) | xshift, 1, 0, 0) \
+}
+
+/* Left Headphone Mixers */
+static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
+ WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5),
+ WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4),
+ WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3),
+ WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2),
+ WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1),
+ WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0),
+};
+
+/* Right Headphone Mixers */
+static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
+ WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5),
+ WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4),
+ WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3),
+ WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2),
+ WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1),
+ WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0),
+};
+
+/* Speaker Mixer */
+static const struct snd_kcontrol_new wm9712_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 11, 1, 1),
+ SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 11, 1, 1),
+ SOC_DAPM_SINGLE("Phone Bypass Switch", AC97_PHONE, 14, 1, 1),
+ SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 14, 1, 1),
+ SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 14, 1, 1),
+};
+
+/* Phone Mixer */
+static const struct snd_kcontrol_new wm9712_phone_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 7, 1, 1),
+ SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 7, 1, 1),
+ SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 13, 1, 1),
+ SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 13, 1, 1),
+ SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_MIC, 14, 1, 1),
+ SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_MIC, 13, 1, 1),
+};
+
+/* ALC headphone mux */
+static const struct snd_kcontrol_new wm9712_alc_mux_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[1]);
+
+/* out 3 mux */
+static const struct snd_kcontrol_new wm9712_out3_mux_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[2]);
+
+/* spk mux */
+static const struct snd_kcontrol_new wm9712_spk_mux_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[3]);
+
+/* Capture to Phone mux */
+static const struct snd_kcontrol_new wm9712_capture_phone_mux_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[4]);
+
+/* Capture left select */
+static const struct snd_kcontrol_new wm9712_capture_selectl_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[8]);
+
+/* Capture right select */
+static const struct snd_kcontrol_new wm9712_capture_selectr_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[9]);
+
+/* Mic select */
+static const struct snd_kcontrol_new wm9712_mic_src_controls =
+SOC_DAPM_ENUM("Mic Source Select", wm9712_enum[7]);
+
+/* diff select */
+static const struct snd_kcontrol_new wm9712_diff_sel_controls =
+SOC_DAPM_ENUM("Route", wm9712_enum[11]);
+
+static const struct snd_soc_dapm_widget wm9712_dapm_widgets[] = {
+SND_SOC_DAPM_MUX("ALC Sidetone Mux", SND_SOC_NOPM, 0, 0,
+ &wm9712_alc_mux_controls),
+SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0,
+ &wm9712_out3_mux_controls),
+SND_SOC_DAPM_MUX("Speaker Mux", SND_SOC_NOPM, 0, 0,
+ &wm9712_spk_mux_controls),
+SND_SOC_DAPM_MUX("Capture Phone Mux", SND_SOC_NOPM, 0, 0,
+ &wm9712_capture_phone_mux_controls),
+SND_SOC_DAPM_MUX("Left Capture Select", SND_SOC_NOPM, 0, 0,
+ &wm9712_capture_selectl_controls),
+SND_SOC_DAPM_MUX("Right Capture Select", SND_SOC_NOPM, 0, 0,
+ &wm9712_capture_selectr_controls),
+SND_SOC_DAPM_MUX("Left Mic Select Source", SND_SOC_NOPM, 0, 0,
+ &wm9712_mic_src_controls),
+SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0,
+ &wm9712_mic_src_controls),
+SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
+ &wm9712_diff_sel_controls),
+SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1,
+ &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1,
+ &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)),
+SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
+ &wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
+SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
+ &wm9712_speaker_mixer_controls[0],
+ ARRAY_SIZE(wm9712_speaker_mixer_controls)),
+SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_INT_PAGING, 14, 1),
+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_INT_PAGING, 13, 1),
+SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_INT_PAGING, 12, 1),
+SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_INT_PAGING, 11, 1),
+SND_SOC_DAPM_PGA("Headphone PGA", AC97_INT_PAGING, 4, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Speaker PGA", AC97_INT_PAGING, 3, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Out 3 PGA", AC97_INT_PAGING, 5, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Line PGA", AC97_INT_PAGING, 2, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Phone PGA", AC97_INT_PAGING, 1, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mic PGA", AC97_INT_PAGING, 0, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Differential Mic", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_INT_PAGING, 10, 1),
+SND_SOC_DAPM_OUTPUT("MONOOUT"),
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("LOUT2"),
+SND_SOC_DAPM_OUTPUT("ROUT2"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_INPUT("LINEINL"),
+SND_SOC_DAPM_INPUT("LINEINR"),
+SND_SOC_DAPM_INPUT("PHONE"),
+SND_SOC_DAPM_INPUT("PCBEEP"),
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2"),
+};
+
+static const struct snd_soc_dapm_route wm9712_audio_map[] = {
+ /* virtual mixer - mixes left & right channels for spk and mono */
+ {"AC97 Mixer", NULL, "Left DAC"},
+ {"AC97 Mixer", NULL, "Right DAC"},
+
+ /* Left HP mixer */
+ {"Left HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
+ {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Left HP Mixer", "Phone Bypass Switch", "Phone PGA"},
+ {"Left HP Mixer", "Line Bypass Switch", "Line PGA"},
+ {"Left HP Mixer", "PCM Playback Switch", "Left DAC"},
+ {"Left HP Mixer", "Mic Sidetone Switch", "Mic PGA"},
+ {"Left HP Mixer", NULL, "ALC Sidetone Mux"},
+
+ /* Right HP mixer */
+ {"Right HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
+ {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Right HP Mixer", "Phone Bypass Switch", "Phone PGA"},
+ {"Right HP Mixer", "Line Bypass Switch", "Line PGA"},
+ {"Right HP Mixer", "PCM Playback Switch", "Right DAC"},
+ {"Right HP Mixer", "Mic Sidetone Switch", "Mic PGA"},
+ {"Right HP Mixer", NULL, "ALC Sidetone Mux"},
+
+ /* speaker mixer */
+ {"Speaker Mixer", "PCBeep Bypass Switch", "PCBEEP"},
+ {"Speaker Mixer", "Line Bypass Switch", "Line PGA"},
+ {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"},
+ {"Speaker Mixer", "Phone Bypass Switch", "Phone PGA"},
+ {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"},
+
+ /* Phone mixer */
+ {"Phone Mixer", "PCBeep Bypass Switch", "PCBEEP"},
+ {"Phone Mixer", "Line Bypass Switch", "Line PGA"},
+ {"Phone Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Phone Mixer", "PCM Playback Switch", "AC97 Mixer"},
+ {"Phone Mixer", "Mic 1 Sidetone Switch", "Mic PGA"},
+ {"Phone Mixer", "Mic 2 Sidetone Switch", "Mic PGA"},
+
+ /* inputs */
+ {"Line PGA", NULL, "LINEINL"},
+ {"Line PGA", NULL, "LINEINR"},
+ {"Phone PGA", NULL, "PHONE"},
+ {"Mic PGA", NULL, "MIC1"},
+ {"Mic PGA", NULL, "MIC2"},
+
+ /* microphones */
+ {"Differential Mic", NULL, "MIC1"},
+ {"Differential Mic", NULL, "MIC2"},
+ {"Left Mic Select Source", "Mic 1", "MIC1"},
+ {"Left Mic Select Source", "Mic 2", "MIC2"},
+ {"Left Mic Select Source", "Stereo", "MIC1"},
+ {"Left Mic Select Source", "Differential", "Differential Mic"},
+ {"Right Mic Select Source", "Mic 1", "MIC1"},
+ {"Right Mic Select Source", "Mic 2", "MIC2"},
+ {"Right Mic Select Source", "Stereo", "MIC2"},
+ {"Right Mic Select Source", "Differential", "Differential Mic"},
+
+ /* left capture selector */
+ {"Left Capture Select", "Mic", "MIC1"},
+ {"Left Capture Select", "Speaker Mixer", "Speaker Mixer"},
+ {"Left Capture Select", "Line", "LINEINL"},
+ {"Left Capture Select", "Headphone Mixer", "Left HP Mixer"},
+ {"Left Capture Select", "Phone Mixer", "Phone Mixer"},
+ {"Left Capture Select", "Phone", "PHONE"},
+
+ /* right capture selector */
+ {"Right Capture Select", "Mic", "MIC2"},
+ {"Right Capture Select", "Speaker Mixer", "Speaker Mixer"},
+ {"Right Capture Select", "Line", "LINEINR"},
+ {"Right Capture Select", "Headphone Mixer", "Right HP Mixer"},
+ {"Right Capture Select", "Phone Mixer", "Phone Mixer"},
+ {"Right Capture Select", "Phone", "PHONE"},
+
+ /* ALC Sidetone */
+ {"ALC Sidetone Mux", "Stereo", "Left Capture Select"},
+ {"ALC Sidetone Mux", "Stereo", "Right Capture Select"},
+ {"ALC Sidetone Mux", "Left", "Left Capture Select"},
+ {"ALC Sidetone Mux", "Right", "Right Capture Select"},
+
+ /* ADC's */
+ {"Left ADC", NULL, "Left Capture Select"},
+ {"Right ADC", NULL, "Right Capture Select"},
+
+ /* outputs */
+ {"MONOOUT", NULL, "Phone Mixer"},
+ {"HPOUTL", NULL, "Headphone PGA"},
+ {"Headphone PGA", NULL, "Left HP Mixer"},
+ {"HPOUTR", NULL, "Headphone PGA"},
+ {"Headphone PGA", NULL, "Right HP Mixer"},
+
+ /* mono mixer */
+ {"Mono Mixer", NULL, "Left HP Mixer"},
+ {"Mono Mixer", NULL, "Right HP Mixer"},
+
+ /* Out3 Mux */
+ {"Out3 Mux", "Left", "Left HP Mixer"},
+ {"Out3 Mux", "Mono", "Phone Mixer"},
+ {"Out3 Mux", "Left + Right", "Mono Mixer"},
+ {"Out 3 PGA", NULL, "Out3 Mux"},
+ {"OUT3", NULL, "Out 3 PGA"},
+
+ /* speaker Mux */
+ {"Speaker Mux", "Speaker Mix", "Speaker Mixer"},
+ {"Speaker Mux", "Headphone Mix", "Mono Mixer"},
+ {"Speaker PGA", NULL, "Speaker Mux"},
+ {"LOUT2", NULL, "Speaker PGA"},
+ {"ROUT2", NULL, "Speaker PGA"},
+};
+
+static unsigned int ac97_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+ u16 *cache = codec->reg_cache;
+
+ if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
+ reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
+ reg == AC97_REC_GAIN)
+ return soc_ac97_ops->read(wm9712->ac97, reg);
+ else {
+ reg = reg >> 1;
+
+ if (reg >= (ARRAY_SIZE(wm9712_reg)))
+ return -EIO;
+
+ return cache[reg];
+ }
+}
+
+static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int val)
+{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+ u16 *cache = codec->reg_cache;
+
+ soc_ac97_ops->write(wm9712->ac97, reg, val);
+ reg = reg >> 1;
+ if (reg < (ARRAY_SIZE(wm9712_reg)))
+ cache[reg] = val;
+
+ return 0;
+}
+
+static int ac97_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int reg;
+ u16 vra;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = AC97_PCM_FRONT_DAC_RATE;
+ else
+ reg = AC97_PCM_LR_ADC_RATE;
+
+ return ac97_write(codec, reg, runtime->rate);
+}
+
+static int ac97_aux_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 vra, xsle;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
+ xsle = ac97_read(codec, AC97_PCI_SID);
+ ac97_write(codec, AC97_PCI_SID, xsle | 0x8000);
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENODEV;
+
+ return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
+}
+
+#define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000)
+
+static const struct snd_soc_dai_ops wm9712_dai_ops_hifi = {
+ .prepare = ac97_prepare,
+};
+
+static const struct snd_soc_dai_ops wm9712_dai_ops_aux = {
+ .prepare = ac97_aux_prepare,
+};
+
+static struct snd_soc_dai_driver wm9712_dai[] = {
+{
+ .name = "wm9712-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9712_AC97_RATES,
+ .formats = SND_SOC_STD_AC97_FMTS,},
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9712_AC97_RATES,
+ .formats = SND_SOC_STD_AC97_FMTS,},
+ .ops = &wm9712_dai_ops_hifi,
+},
+{
+ .name = "wm9712-aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = WM9712_AC97_RATES,
+ .formats = SND_SOC_STD_AC97_FMTS,},
+ .ops = &wm9712_dai_ops_aux,
+}
+};
+
+static int wm9712_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ ac97_write(codec, AC97_POWERDOWN, 0x0000);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* disable everything including AC link */
+ ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
+ ac97_write(codec, AC97_POWERDOWN, 0xffff);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
+{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(wm9712->ac97);
+ if (ac97_read(codec, 0) == wm9712_reg[0])
+ return 1;
+ }
+
+ soc_ac97_ops->reset(wm9712->ac97);
+ if (soc_ac97_ops->warm_reset)
+ soc_ac97_ops->warm_reset(wm9712->ac97);
+ if (ac97_read(codec, 0) != wm9712_reg[0])
+ goto err;
+ return 0;
+
+err:
+ dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+ return -EIO;
+}
+
+static int wm9712_soc_resume(struct snd_soc_codec *codec)
+{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
+ u16 *cache = codec->reg_cache;
+
+ ret = wm9712_reset(codec, 1);
+ if (ret < 0)
+ return ret;
+
+ wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ if (ret == 0) {
+ /* Sync reg_cache with the hardware after cold reset */
+ for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i += 2) {
+ if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
+ (i > 0x58 && i != 0x5c))
+ continue;
+ soc_ac97_ops->write(wm9712->ac97, i, cache[i>>1]);
+ }
+ }
+
+ return ret;
+}
+
+static int wm9712_soc_probe(struct snd_soc_codec *codec)
+{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ wm9712->ac97 = snd_soc_alloc_ac97_codec(codec);
+ if (IS_ERR(wm9712->ac97)) {
+ ret = PTR_ERR(wm9712->ac97);
+ dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm9712_reset(codec, 0);
+ if (ret < 0)
+ goto err_put_device;
+
+ ret = device_add(&wm9712->ac97->dev);
+ if (ret)
+ goto err_put_device;
+
+ /* set alc mux to none */
+ ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
+
+ return 0;
+
+err_put_device:
+ put_device(&wm9712->ac97->dev);
+ return ret;
+}
+
+static int wm9712_soc_remove(struct snd_soc_codec *codec)
+{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_free_ac97_codec(wm9712->ac97);
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
+ .probe = wm9712_soc_probe,
+ .remove = wm9712_soc_remove,
+ .resume = wm9712_soc_resume,
+ .read = ac97_read,
+ .write = ac97_write,
+ .set_bias_level = wm9712_set_bias_level,
+ .suspend_bias_off = true,
+ .reg_cache_size = ARRAY_SIZE(wm9712_reg),
+ .reg_word_size = sizeof(u16),
+ .reg_cache_step = 2,
+ .reg_cache_default = wm9712_reg,
+
+ .controls = wm9712_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls),
+ .dapm_widgets = wm9712_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
+ .dapm_routes = wm9712_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9712_audio_map),
+};
+
+static int wm9712_probe(struct platform_device *pdev)
+{
+ struct wm9712_priv *wm9712;
+
+ wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL);
+ if (wm9712 == NULL)
+ return -ENOMEM;
+
+ mutex_init(&wm9712->lock);
+
+ platform_set_drvdata(pdev, wm9712);
+
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
+}
+
+static int wm9712_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver wm9712_codec_driver = {
+ .driver = {
+ .name = "wm9712-codec",
+ },
+
+ .probe = wm9712_probe,
+ .remove = wm9712_remove,
+};
+
+module_platform_driver(wm9712_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm9712.h b/kernel/sound/soc/codecs/wm9712.h
new file mode 100644
index 000000000..fb69c3aa4
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm9712.h
@@ -0,0 +1,11 @@
+/*
+ * wm9712.h -- WM9712 Soc Audio driver
+ */
+
+#ifndef _WM9712_H
+#define _WM9712_H
+
+#define WM9712_DAI_AC97_HIFI 0
+#define WM9712_DAI_AC97_AUX 1
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm9713.c b/kernel/sound/soc/codecs/wm9713.c
new file mode 100644
index 000000000..79552953e
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm9713.c
@@ -0,0 +1,1318 @@
+/*
+ * wm9713.c -- ALSA Soc WM9713 codec support
+ *
+ * Copyright 2006-10 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.
+ *
+ * Features:-
+ *
+ * o Support for AC97 Codec, Voice DAC and Aux DAC
+ * o Support for DAPM
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+
+#include "wm9713.h"
+
+struct wm9713_priv {
+ struct snd_ac97 *ac97;
+ u32 pll_in; /* PLL input frequency */
+ unsigned int hp_mixer[2];
+ struct mutex lock;
+};
+
+static unsigned int ac97_read(struct snd_soc_codec *codec,
+ unsigned int reg);
+static int ac97_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int val);
+
+/*
+ * WM9713 register cache
+ * Reg 0x3c bit 15 is used by touch driver.
+ */
+static const u16 wm9713_reg[] = {
+ 0x6174, 0x8080, 0x8080, 0x8080,
+ 0xc880, 0xe808, 0xe808, 0x0808,
+ 0x00da, 0x8000, 0xd600, 0xaaa0,
+ 0xaaa0, 0xaaa0, 0x0000, 0x0000,
+ 0x0f0f, 0x0040, 0x0000, 0x7f00,
+ 0x0405, 0x0410, 0xbb80, 0xbb80,
+ 0x0000, 0xbb80, 0x0000, 0x4523,
+ 0x0000, 0x2000, 0x7eff, 0xffff,
+ 0x0000, 0x0000, 0x0080, 0x0000,
+ 0x0000, 0x0000, 0xfffe, 0xffff,
+ 0x0000, 0x0000, 0x0000, 0xfffe,
+ 0x4000, 0x0000, 0x0000, 0x0000,
+ 0xb032, 0x3e00, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0006,
+ 0x0001, 0x0000, 0x574d, 0x4c13,
+};
+
+#define HPL_MIXER 0
+#define HPR_MIXER 1
+
+static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
+static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
+static const char *wm9713_rec_src[] =
+ {"Mic 1", "Mic 2", "Line", "Mono In", "Headphone", "Speaker",
+ "Mono Out", "Zh"};
+static const char *wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
+static const char *wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"};
+static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv"};
+static const char *wm9713_spk_pga[] =
+ {"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid",
+ "Speaker Vmid", "Inv Vmid"};
+static const char *wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone",
+ "Headphone Vmid"};
+static const char *wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "Inv 1 Vmid"};
+static const char *wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "Inv 2 Vmid"};
+static const char *wm9713_dac_inv[] =
+ {"Off", "Mono", "Speaker", "Left Headphone", "Right Headphone",
+ "Headphone Mono", "NC", "Vmid"};
+static const char *wm9713_bass[] = {"Linear Control", "Adaptive Boost"};
+static const char *wm9713_ng_type[] = {"Constant Gain", "Mute"};
+static const char *wm9713_mic_select[] = {"Mic 1", "Mic 2 A", "Mic 2 B"};
+static const char *wm9713_micb_select[] = {"MPB", "MPA"};
+
+static const struct soc_enum wm9713_enum[] = {
+SOC_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer), /* record mic mixer 0 */
+SOC_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux), /* record mux hp 1 */
+SOC_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux), /* record mux mono 2 */
+SOC_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src), /* record mux left 3 */
+SOC_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src), /* record mux right 4*/
+SOC_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain), /* record step size 5 */
+SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select), /* alc source select 6*/
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga), /* mono input select 7 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 11, 8, wm9713_spk_pga), /* speaker left input select 8 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 8, 8, wm9713_spk_pga), /* speaker right input select 9 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 6, 3, wm9713_hp_pga), /* headphone left input 10 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 4, 3, wm9713_hp_pga), /* headphone right input 11 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga), /* out 3 source 12 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga), /* out 4 source 13 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 13, 8, wm9713_dac_inv), /* dac invert 1 14 */
+SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */
+SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */
+SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */
+SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */
+SOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */
+};
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(misc_tlv, -1500, 300, 0);
+static unsigned int mic_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+
+static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = {
+SOC_DOUBLE_TLV("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1, out_tlv),
+SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1),
+SOC_DOUBLE_TLV("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1,
+ out_tlv),
+SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1),
+SOC_DOUBLE_TLV("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1, main_tlv),
+SOC_DOUBLE_TLV("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1, main_tlv),
+SOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv),
+SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
+SOC_SINGLE_TLV("Mic 1 Preamp Volume", AC97_3D_CONTROL, 10, 3, 0, mic_tlv),
+SOC_SINGLE_TLV("Mic 2 Preamp Volume", AC97_3D_CONTROL, 12, 3, 0, mic_tlv),
+
+SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0),
+SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1),
+
+SOC_SINGLE("Capture Switch", AC97_CD, 15, 1, 1),
+SOC_ENUM("Capture Volume Steps", wm9713_enum[5]),
+SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0),
+SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0),
+
+SOC_SINGLE_TLV("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1, misc_tlv),
+SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0),
+SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0),
+
+SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
+SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
+SOC_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0),
+SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
+SOC_ENUM("ALC Function", wm9713_enum[6]),
+SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
+SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0),
+SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
+SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
+SOC_ENUM("ALC NG Type", wm9713_enum[17]),
+SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0),
+
+SOC_DOUBLE("Speaker Playback ZC Switch", AC97_MASTER, 14, 6, 1, 0),
+SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0),
+
+SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
+SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0),
+SOC_SINGLE_TLV("Out4 Playback Volume", AC97_MASTER_MONO, 8, 31, 1, out_tlv),
+
+SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1),
+SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0),
+SOC_SINGLE_TLV("Out3 Playback Volume", AC97_MASTER_MONO, 0, 31, 1, out_tlv),
+
+SOC_SINGLE_TLV("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1, main_tlv),
+SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1),
+SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
+SOC_SINGLE_TLV("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1, out_tlv),
+
+SOC_SINGLE_TLV("Headphone Mixer Beep Playback Volume", AC97_AUX, 12, 7, 1,
+ misc_tlv),
+SOC_SINGLE_TLV("Speaker Mixer Beep Playback Volume", AC97_AUX, 8, 7, 1,
+ misc_tlv),
+SOC_SINGLE_TLV("Mono Mixer Beep Playback Volume", AC97_AUX, 4, 7, 1, misc_tlv),
+
+SOC_SINGLE_TLV("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1,
+ misc_tlv),
+SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1),
+SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1),
+
+SOC_SINGLE_TLV("Headphone Mixer Aux Playback Volume", AC97_REC_SEL, 12, 7, 1,
+ misc_tlv),
+
+SOC_SINGLE_TLV("Speaker Mixer Voice Playback Volume", AC97_PCM, 8, 7, 1,
+ misc_tlv),
+SOC_SINGLE_TLV("Speaker Mixer Aux Playback Volume", AC97_REC_SEL, 8, 7, 1,
+ misc_tlv),
+
+SOC_SINGLE_TLV("Mono Mixer Voice Playback Volume", AC97_PCM, 4, 7, 1,
+ misc_tlv),
+SOC_SINGLE_TLV("Mono Mixer Aux Playback Volume", AC97_REC_SEL, 4, 7, 1,
+ misc_tlv),
+
+SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1),
+SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1),
+
+SOC_ENUM("Bass Control", wm9713_enum[16]),
+SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1),
+SOC_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1),
+SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0),
+SOC_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1),
+SOC_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1),
+
+SOC_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0),
+SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0),
+SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
+};
+
+static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u16 status, rate;
+
+ if (WARN_ON(event != SND_SOC_DAPM_PRE_PMD))
+ return -EINVAL;
+
+ /* Gracefully shut down the voice interface. */
+ status = ac97_read(codec, AC97_EXTENDED_MID) | 0x1000;
+ rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF;
+ ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200);
+ schedule_timeout_interruptible(msecs_to_jiffies(1));
+ ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00);
+ ac97_write(codec, AC97_EXTENDED_MID, status);
+
+ return 0;
+}
+
+static const unsigned int wm9713_mixer_mute_regs[] = {
+ AC97_PC_BEEP,
+ AC97_MASTER_TONE,
+ AC97_PHONE,
+ AC97_REC_SEL,
+ AC97_PCM,
+ AC97_AUX,
+};
+
+/* We have to create a fake left and right HP mixers because
+ * the codec only has a single control that is shared by both channels.
+ * This makes it impossible to determine the audio path using the current
+ * register map, thus we add a new (virtual) register to help determine the
+ * audio route within the device.
+ */
+static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = ucontrol->value.integer.value[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int mixer, mask, shift, old;
+ struct snd_soc_dapm_update update;
+ bool change;
+
+ mixer = mc->shift >> 8;
+ shift = mc->shift & 0xff;
+ mask = (1 << shift);
+
+ mutex_lock(&wm9713->lock);
+ old = wm9713->hp_mixer[mixer];
+ if (ucontrol->value.integer.value[0])
+ wm9713->hp_mixer[mixer] |= mask;
+ else
+ wm9713->hp_mixer[mixer] &= ~mask;
+
+ change = old != wm9713->hp_mixer[mixer];
+ if (change) {
+ update.kcontrol = kcontrol;
+ update.reg = wm9713_mixer_mute_regs[shift];
+ update.mask = 0x8000;
+ if ((wm9713->hp_mixer[0] & mask) ||
+ (wm9713->hp_mixer[1] & mask))
+ update.val = 0x0;
+ else
+ update.val = 0x8000;
+
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
+ &update);
+ }
+
+ mutex_unlock(&wm9713->lock);
+
+ return change;
+}
+
+static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int mixer, shift;
+
+ mixer = mc->shift >> 8;
+ shift = mc->shift & 0xff;
+
+ ucontrol->value.integer.value[0] =
+ (wm9713->hp_mixer[mixer] >> shift) & 1;
+
+ return 0;
+}
+
+#define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, \
+ .get = wm9713_hp_mixer_get, .put = wm9713_hp_mixer_put, \
+ .private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \
+ xshift, xmixer, 1, 0, 0) \
+}
+
+/* Left Headphone Mixers */
+static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
+WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5),
+WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4),
+WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3),
+WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2),
+WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1),
+WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0),
+};
+
+/* Right Headphone Mixers */
+static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
+WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5),
+WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4),
+WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3),
+WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2),
+WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1),
+WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0),
+};
+
+/* headphone capture mux */
+static const struct snd_kcontrol_new wm9713_hp_rec_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[1]);
+
+/* headphone mic mux */
+static const struct snd_kcontrol_new wm9713_hp_mic_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[0]);
+
+/* Speaker Mixer */
+static const struct snd_kcontrol_new wm9713_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("Beep Playback Switch", AC97_AUX, 11, 1, 1),
+SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 11, 1, 1),
+SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 11, 1, 1),
+SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 14, 1, 1),
+SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 14, 1, 1),
+SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 14, 1, 1),
+};
+
+/* Mono Mixer */
+static const struct snd_kcontrol_new wm9713_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Beep Playback Switch", AC97_AUX, 7, 1, 1),
+SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 7, 1, 1),
+SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 7, 1, 1),
+SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 13, 1, 1),
+SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 13, 1, 1),
+SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 13, 1, 1),
+SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_LINE, 7, 1, 1),
+SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_LINE, 6, 1, 1),
+};
+
+/* mono mic mux */
+static const struct snd_kcontrol_new wm9713_mono_mic_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[2]);
+
+/* mono output mux */
+static const struct snd_kcontrol_new wm9713_mono_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[7]);
+
+/* speaker left output mux */
+static const struct snd_kcontrol_new wm9713_hp_spkl_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[8]);
+
+/* speaker right output mux */
+static const struct snd_kcontrol_new wm9713_hp_spkr_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[9]);
+
+/* headphone left output mux */
+static const struct snd_kcontrol_new wm9713_hpl_out_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[10]);
+
+/* headphone right output mux */
+static const struct snd_kcontrol_new wm9713_hpr_out_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[11]);
+
+/* Out3 mux */
+static const struct snd_kcontrol_new wm9713_out3_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[12]);
+
+/* Out4 mux */
+static const struct snd_kcontrol_new wm9713_out4_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[13]);
+
+/* DAC inv mux 1 */
+static const struct snd_kcontrol_new wm9713_dac_inv1_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[14]);
+
+/* DAC inv mux 2 */
+static const struct snd_kcontrol_new wm9713_dac_inv2_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[15]);
+
+/* Capture source left */
+static const struct snd_kcontrol_new wm9713_rec_srcl_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[3]);
+
+/* Capture source right */
+static const struct snd_kcontrol_new wm9713_rec_srcr_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[4]);
+
+/* mic source */
+static const struct snd_kcontrol_new wm9713_mic_sel_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[18]);
+
+/* mic source B virtual control */
+static const struct snd_kcontrol_new wm9713_micb_sel_mux_controls =
+SOC_DAPM_ENUM("Route", wm9713_enum[19]);
+
+static const struct snd_soc_dapm_widget wm9713_dapm_widgets[] = {
+SND_SOC_DAPM_MUX("Capture Headphone Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_hp_rec_mux_controls),
+SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_hp_mic_mux_controls),
+SND_SOC_DAPM_MUX("Capture Mono Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_mono_mic_mux_controls),
+SND_SOC_DAPM_MUX("Mono Out Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_mono_mux_controls),
+SND_SOC_DAPM_MUX("Left Speaker Out Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_hp_spkl_mux_controls),
+SND_SOC_DAPM_MUX("Right Speaker Out Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_hp_spkr_mux_controls),
+SND_SOC_DAPM_MUX("Left Headphone Out Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_hpl_out_mux_controls),
+SND_SOC_DAPM_MUX("Right Headphone Out Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_hpr_out_mux_controls),
+SND_SOC_DAPM_MUX("Out 3 Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_out3_mux_controls),
+SND_SOC_DAPM_MUX("Out 4 Mux", SND_SOC_NOPM, 0, 0,
+ &wm9713_out4_mux_controls),
+SND_SOC_DAPM_MUX("DAC Inv Mux 1", SND_SOC_NOPM, 0, 0,
+ &wm9713_dac_inv1_mux_controls),
+SND_SOC_DAPM_MUX("DAC Inv Mux 2", SND_SOC_NOPM, 0, 0,
+ &wm9713_dac_inv2_mux_controls),
+SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0,
+ &wm9713_rec_srcl_mux_controls),
+SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0,
+ &wm9713_rec_srcr_mux_controls),
+SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0,
+ &wm9713_mic_sel_mux_controls),
+SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
+ &wm9713_micb_sel_mux_controls),
+SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
+ &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
+ &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls)),
+SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
+ &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
+SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1,
+ &wm9713_speaker_mixer_controls[0],
+ ARRAY_SIZE(wm9713_speaker_mixer_controls)),
+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_EXTENDED_MID, 7, 1),
+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_EXTENDED_MID, 6, 1),
+SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_DAC_E("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1,
+ wm9713_voice_shutdown, SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1),
+SND_SOC_DAPM_PGA("Left ADC", AC97_EXTENDED_MID, 5, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Right ADC", AC97_EXTENDED_MID, 4, 1, NULL, 0),
+SND_SOC_DAPM_ADC("Left HiFi ADC", "Left HiFi Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_ADC("Right HiFi ADC", "Right HiFi Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_ADC("Left Voice ADC", "Left Voice Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_ADC("Right Voice ADC", "Right Voice Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Right Speaker", AC97_EXTENDED_MSTATUS, 7, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Out 3", AC97_EXTENDED_MSTATUS, 11, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Out 4", AC97_EXTENDED_MSTATUS, 12, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mono Out", AC97_EXTENDED_MSTATUS, 13, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Left Line In", AC97_EXTENDED_MSTATUS, 6, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Right Line In", AC97_EXTENDED_MSTATUS, 5, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mono In", AC97_EXTENDED_MSTATUS, 4, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mic A PGA", AC97_EXTENDED_MSTATUS, 3, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mic B PGA", AC97_EXTENDED_MSTATUS, 2, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mic A Pre Amp", AC97_EXTENDED_MSTATUS, 1, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Mic B Pre Amp", AC97_EXTENDED_MSTATUS, 0, 1, NULL, 0),
+SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_EXTENDED_MSTATUS, 14, 1),
+SND_SOC_DAPM_OUTPUT("MONO"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+SND_SOC_DAPM_OUTPUT("SPKL"),
+SND_SOC_DAPM_OUTPUT("SPKR"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_OUTPUT("OUT4"),
+SND_SOC_DAPM_INPUT("LINEL"),
+SND_SOC_DAPM_INPUT("LINER"),
+SND_SOC_DAPM_INPUT("MONOIN"),
+SND_SOC_DAPM_INPUT("PCBEEP"),
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2A"),
+SND_SOC_DAPM_INPUT("MIC2B"),
+SND_SOC_DAPM_VMID("VMID"),
+};
+
+static const struct snd_soc_dapm_route wm9713_audio_map[] = {
+ /* left HP mixer */
+ {"Left HP Mixer", "Beep Playback Switch", "PCBEEP"},
+ {"Left HP Mixer", "Voice Playback Switch", "Voice DAC"},
+ {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Left HP Mixer", "Bypass Playback Switch", "Left Line In"},
+ {"Left HP Mixer", "PCM Playback Switch", "Left DAC"},
+ {"Left HP Mixer", "MonoIn Playback Switch", "Mono In"},
+ {"Left HP Mixer", NULL, "Capture Headphone Mux"},
+
+ /* right HP mixer */
+ {"Right HP Mixer", "Beep Playback Switch", "PCBEEP"},
+ {"Right HP Mixer", "Voice Playback Switch", "Voice DAC"},
+ {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Right HP Mixer", "Bypass Playback Switch", "Right Line In"},
+ {"Right HP Mixer", "PCM Playback Switch", "Right DAC"},
+ {"Right HP Mixer", "MonoIn Playback Switch", "Mono In"},
+ {"Right HP Mixer", NULL, "Capture Headphone Mux"},
+
+ /* virtual mixer - mixes left & right channels for spk and mono */
+ {"AC97 Mixer", NULL, "Left DAC"},
+ {"AC97 Mixer", NULL, "Right DAC"},
+ {"Line Mixer", NULL, "Right Line In"},
+ {"Line Mixer", NULL, "Left Line In"},
+ {"HP Mixer", NULL, "Left HP Mixer"},
+ {"HP Mixer", NULL, "Right HP Mixer"},
+ {"Capture Mixer", NULL, "Left Capture Source"},
+ {"Capture Mixer", NULL, "Right Capture Source"},
+
+ /* speaker mixer */
+ {"Speaker Mixer", "Beep Playback Switch", "PCBEEP"},
+ {"Speaker Mixer", "Voice Playback Switch", "Voice DAC"},
+ {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Speaker Mixer", "Bypass Playback Switch", "Line Mixer"},
+ {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"},
+ {"Speaker Mixer", "MonoIn Playback Switch", "Mono In"},
+
+ /* mono mixer */
+ {"Mono Mixer", "Beep Playback Switch", "PCBEEP"},
+ {"Mono Mixer", "Voice Playback Switch", "Voice DAC"},
+ {"Mono Mixer", "Aux Playback Switch", "Aux DAC"},
+ {"Mono Mixer", "Bypass Playback Switch", "Line Mixer"},
+ {"Mono Mixer", "PCM Playback Switch", "AC97 Mixer"},
+ {"Mono Mixer", "Mic 1 Sidetone Switch", "Mic A PGA"},
+ {"Mono Mixer", "Mic 2 Sidetone Switch", "Mic B PGA"},
+ {"Mono Mixer", NULL, "Capture Mono Mux"},
+
+ /* DAC inv mux 1 */
+ {"DAC Inv Mux 1", "Mono", "Mono Mixer"},
+ {"DAC Inv Mux 1", "Speaker", "Speaker Mixer"},
+ {"DAC Inv Mux 1", "Left Headphone", "Left HP Mixer"},
+ {"DAC Inv Mux 1", "Right Headphone", "Right HP Mixer"},
+ {"DAC Inv Mux 1", "Headphone Mono", "HP Mixer"},
+
+ /* DAC inv mux 2 */
+ {"DAC Inv Mux 2", "Mono", "Mono Mixer"},
+ {"DAC Inv Mux 2", "Speaker", "Speaker Mixer"},
+ {"DAC Inv Mux 2", "Left Headphone", "Left HP Mixer"},
+ {"DAC Inv Mux 2", "Right Headphone", "Right HP Mixer"},
+ {"DAC Inv Mux 2", "Headphone Mono", "HP Mixer"},
+
+ /* headphone left mux */
+ {"Left Headphone Out Mux", "Headphone", "Left HP Mixer"},
+
+ /* headphone right mux */
+ {"Right Headphone Out Mux", "Headphone", "Right HP Mixer"},
+
+ /* speaker left mux */
+ {"Left Speaker Out Mux", "Headphone", "Left HP Mixer"},
+ {"Left Speaker Out Mux", "Speaker", "Speaker Mixer"},
+ {"Left Speaker Out Mux", "Inv", "DAC Inv Mux 1"},
+
+ /* speaker right mux */
+ {"Right Speaker Out Mux", "Headphone", "Right HP Mixer"},
+ {"Right Speaker Out Mux", "Speaker", "Speaker Mixer"},
+ {"Right Speaker Out Mux", "Inv", "DAC Inv Mux 2"},
+
+ /* mono mux */
+ {"Mono Out Mux", "Mono", "Mono Mixer"},
+ {"Mono Out Mux", "Inv", "DAC Inv Mux 1"},
+
+ /* out 3 mux */
+ {"Out 3 Mux", "Inv 1", "DAC Inv Mux 1"},
+
+ /* out 4 mux */
+ {"Out 4 Mux", "Inv 2", "DAC Inv Mux 2"},
+
+ /* output pga */
+ {"HPL", NULL, "Left Headphone"},
+ {"Left Headphone", NULL, "Left Headphone Out Mux"},
+ {"HPR", NULL, "Right Headphone"},
+ {"Right Headphone", NULL, "Right Headphone Out Mux"},
+ {"OUT3", NULL, "Out 3"},
+ {"Out 3", NULL, "Out 3 Mux"},
+ {"OUT4", NULL, "Out 4"},
+ {"Out 4", NULL, "Out 4 Mux"},
+ {"SPKL", NULL, "Left Speaker"},
+ {"Left Speaker", NULL, "Left Speaker Out Mux"},
+ {"SPKR", NULL, "Right Speaker"},
+ {"Right Speaker", NULL, "Right Speaker Out Mux"},
+ {"MONO", NULL, "Mono Out"},
+ {"Mono Out", NULL, "Mono Out Mux"},
+
+ /* input pga */
+ {"Left Line In", NULL, "LINEL"},
+ {"Right Line In", NULL, "LINER"},
+ {"Mono In", NULL, "MONOIN"},
+ {"Mic A PGA", NULL, "Mic A Pre Amp"},
+ {"Mic B PGA", NULL, "Mic B Pre Amp"},
+
+ /* left capture select */
+ {"Left Capture Source", "Mic 1", "Mic A Pre Amp"},
+ {"Left Capture Source", "Mic 2", "Mic B Pre Amp"},
+ {"Left Capture Source", "Line", "LINEL"},
+ {"Left Capture Source", "Mono In", "MONOIN"},
+ {"Left Capture Source", "Headphone", "Left HP Mixer"},
+ {"Left Capture Source", "Speaker", "Speaker Mixer"},
+ {"Left Capture Source", "Mono Out", "Mono Mixer"},
+
+ /* right capture select */
+ {"Right Capture Source", "Mic 1", "Mic A Pre Amp"},
+ {"Right Capture Source", "Mic 2", "Mic B Pre Amp"},
+ {"Right Capture Source", "Line", "LINER"},
+ {"Right Capture Source", "Mono In", "MONOIN"},
+ {"Right Capture Source", "Headphone", "Right HP Mixer"},
+ {"Right Capture Source", "Speaker", "Speaker Mixer"},
+ {"Right Capture Source", "Mono Out", "Mono Mixer"},
+
+ /* left ADC */
+ {"Left ADC", NULL, "Left Capture Source"},
+ {"Left Voice ADC", NULL, "Left ADC"},
+ {"Left HiFi ADC", NULL, "Left ADC"},
+
+ /* right ADC */
+ {"Right ADC", NULL, "Right Capture Source"},
+ {"Right Voice ADC", NULL, "Right ADC"},
+ {"Right HiFi ADC", NULL, "Right ADC"},
+
+ /* mic */
+ {"Mic A Pre Amp", NULL, "Mic A Source"},
+ {"Mic A Source", "Mic 1", "MIC1"},
+ {"Mic A Source", "Mic 2 A", "MIC2A"},
+ {"Mic A Source", "Mic 2 B", "Mic B Source"},
+ {"Mic B Pre Amp", "MPB", "Mic B Source"},
+ {"Mic B Source", NULL, "MIC2B"},
+
+ /* headphone capture */
+ {"Capture Headphone Mux", "Stereo", "Capture Mixer"},
+ {"Capture Headphone Mux", "Left", "Left Capture Source"},
+ {"Capture Headphone Mux", "Right", "Right Capture Source"},
+
+ /* mono capture */
+ {"Capture Mono Mux", "Stereo", "Capture Mixer"},
+ {"Capture Mono Mux", "Left", "Left Capture Source"},
+ {"Capture Mono Mux", "Right", "Right Capture Source"},
+};
+
+static unsigned int ac97_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+ u16 *cache = codec->reg_cache;
+
+ if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
+ reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
+ reg == AC97_CD)
+ return soc_ac97_ops->read(wm9713->ac97, reg);
+ else {
+ reg = reg >> 1;
+
+ if (reg >= (ARRAY_SIZE(wm9713_reg)))
+ return -EIO;
+
+ return cache[reg];
+ }
+}
+
+static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int val)
+{
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+
+ u16 *cache = codec->reg_cache;
+ soc_ac97_ops->write(wm9713->ac97, reg, val);
+ reg = reg >> 1;
+ if (reg < (ARRAY_SIZE(wm9713_reg)))
+ cache[reg] = val;
+
+ return 0;
+}
+
+/* PLL divisors */
+struct _pll_div {
+ u32 divsel:1;
+ u32 divctl:1;
+ u32 lf:1;
+ u32 n:4;
+ u32 k:24;
+};
+
+/* The size in bits of the PLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 22) * 10)
+
+static void pll_factors(struct snd_soc_codec *codec,
+ struct _pll_div *pll_div, unsigned int source)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod, target;
+
+ /* The the PLL output is always 98.304MHz. */
+ target = 98304000;
+
+ /* If the input frequency is over 14.4MHz then scale it down. */
+ if (source > 14400000) {
+ source >>= 1;
+ pll_div->divsel = 1;
+
+ if (source > 14400000) {
+ source >>= 1;
+ pll_div->divctl = 1;
+ } else
+ pll_div->divctl = 0;
+
+ } else {
+ pll_div->divsel = 0;
+ pll_div->divctl = 0;
+ }
+
+ /* Low frequency sources require an additional divide in the
+ * loop.
+ */
+ if (source < 8192000) {
+ pll_div->lf = 1;
+ target >>= 2;
+ } else
+ pll_div->lf = 0;
+
+ Ndiv = target / source;
+ if ((Ndiv < 5) || (Ndiv > 12))
+ dev_warn(codec->dev,
+ "WM9713 PLL N value %u out of recommended range!\n",
+ Ndiv);
+
+ pll_div->n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div->k = K;
+}
+
+/**
+ * Please note that changing the PLL input frequency may require
+ * resynchronisation with the AC97 controller.
+ */
+static int wm9713_set_pll(struct snd_soc_codec *codec,
+ int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+ u16 reg, reg2;
+ struct _pll_div pll_div;
+
+ /* turn PLL off ? */
+ if (freq_in == 0) {
+ /* disable PLL power and select ext source */
+ reg = ac97_read(codec, AC97_HANDSET_RATE);
+ ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080);
+ reg = ac97_read(codec, AC97_EXTENDED_MID);
+ ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200);
+ wm9713->pll_in = 0;
+ return 0;
+ }
+
+ pll_factors(codec, &pll_div, freq_in);
+
+ if (pll_div.k == 0) {
+ reg = (pll_div.n << 12) | (pll_div.lf << 11) |
+ (pll_div.divsel << 9) | (pll_div.divctl << 8);
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+ } else {
+ /* write the fractional k to the reg 0x46 pages */
+ reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) |
+ (pll_div.divsel << 9) | (pll_div.divctl << 8);
+
+ /* K [21:20] */
+ reg = reg2 | (0x5 << 4) | (pll_div.k >> 20);
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+
+ /* K [19:16] */
+ reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf);
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+
+ /* K [15:12] */
+ reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf);
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+
+ /* K [11:8] */
+ reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf);
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+
+ /* K [7:4] */
+ reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf);
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+
+ reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */
+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
+ }
+
+ /* turn PLL on and select as source */
+ reg = ac97_read(codec, AC97_EXTENDED_MID);
+ ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff);
+ reg = ac97_read(codec, AC97_HANDSET_RATE);
+ ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f);
+ wm9713->pll_in = freq_in;
+
+ /* wait 10ms AC97 link frames for the link to stabilise */
+ schedule_timeout_interruptible(msecs_to_jiffies(10));
+ return 0;
+}
+
+static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ return wm9713_set_pll(codec, pll_id, freq_in, freq_out);
+}
+
+/*
+ * Tristate the PCM DAI lines, tristate can be disabled by calling
+ * wm9713_set_dai_fmt()
+ */
+static int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai,
+ int tristate)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0x9fff;
+
+ if (tristate)
+ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
+
+ return 0;
+}
+
+/*
+ * Configure WM9713 clock dividers.
+ * Voice DAC needs 256 FS
+ */
+static int wm9713_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM9713_PCMCLK_DIV:
+ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xf0ff;
+ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
+ break;
+ case WM9713_CLKA_MULT:
+ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffd;
+ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
+ break;
+ case WM9713_CLKB_MULT:
+ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffb;
+ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
+ break;
+ case WM9713_HIFI_DIV:
+ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0x8fff;
+ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
+ break;
+ case WM9713_PCMBCLK_DIV:
+ reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xf1ff;
+ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg | div);
+ break;
+ case WM9713_PCMCLK_PLL_DIV:
+ reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80;
+ ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x60 | div);
+ break;
+ case WM9713_HIFI_PLL_DIV:
+ reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80;
+ ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x70 | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 gpio = ac97_read(codec, AC97_GPIO_CFG) & 0xffc5;
+ u16 reg = 0x8000;
+
+ /* clock masters */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ reg |= 0x4000;
+ gpio |= 0x0010;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ reg |= 0x6000;
+ gpio |= 0x0018;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg |= 0x2000;
+ gpio |= 0x001a;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ gpio |= 0x0012;
+ break;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ reg |= 0x00c0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ reg |= 0x0040;
+ break;
+ }
+
+ /* DAI format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ reg |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg |= 0x0043;
+ break;
+ }
+
+ ac97_write(codec, AC97_GPIO_CFG, gpio);
+ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
+ return 0;
+}
+
+static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3;
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ reg |= 0x0004;
+ break;
+ case 24:
+ reg |= 0x0008;
+ break;
+ case 32:
+ reg |= 0x000c;
+ break;
+ }
+
+ /* enable PCM interface in master mode */
+ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
+ return 0;
+}
+
+static int ac97_hifi_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int reg;
+ u16 vra;
+
+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = AC97_PCM_FRONT_DAC_RATE;
+ else
+ reg = AC97_PCM_LR_ADC_RATE;
+
+ return ac97_write(codec, reg, runtime->rate);
+}
+
+static int ac97_aux_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u16 vra, xsle;
+
+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
+ xsle = ac97_read(codec, AC97_PCI_SID);
+ ac97_write(codec, AC97_PCI_SID, xsle | 0x8000);
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENODEV;
+
+ return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
+}
+
+#define WM9713_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define WM9713_PCM_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define WM9713_PCM_FORMATS \
+ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
+ SNDRV_PCM_FORMAT_S24_LE)
+
+static const struct snd_soc_dai_ops wm9713_dai_ops_hifi = {
+ .prepare = ac97_hifi_prepare,
+ .set_clkdiv = wm9713_set_dai_clkdiv,
+ .set_pll = wm9713_set_dai_pll,
+};
+
+static const struct snd_soc_dai_ops wm9713_dai_ops_aux = {
+ .prepare = ac97_aux_prepare,
+ .set_clkdiv = wm9713_set_dai_clkdiv,
+ .set_pll = wm9713_set_dai_pll,
+};
+
+static const struct snd_soc_dai_ops wm9713_dai_ops_voice = {
+ .hw_params = wm9713_pcm_hw_params,
+ .set_clkdiv = wm9713_set_dai_clkdiv,
+ .set_pll = wm9713_set_dai_pll,
+ .set_fmt = wm9713_set_dai_fmt,
+ .set_tristate = wm9713_set_dai_tristate,
+};
+
+static struct snd_soc_dai_driver wm9713_dai[] = {
+{
+ .name = "wm9713-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9713_RATES,
+ .formats = SND_SOC_STD_AC97_FMTS,},
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9713_RATES,
+ .formats = SND_SOC_STD_AC97_FMTS,},
+ .ops = &wm9713_dai_ops_hifi,
+ },
+ {
+ .name = "wm9713-aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = WM9713_RATES,
+ .formats = SND_SOC_STD_AC97_FMTS,},
+ .ops = &wm9713_dai_ops_aux,
+ },
+ {
+ .name = "wm9713-voice",
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = WM9713_PCM_RATES,
+ .formats = WM9713_PCM_FORMATS,},
+ .capture = {
+ .stream_name = "Voice Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9713_PCM_RATES,
+ .formats = WM9713_PCM_FORMATS,},
+ .ops = &wm9713_dai_ops_voice,
+ .symmetric_rates = 1,
+ },
+};
+
+int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
+{
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(wm9713->ac97);
+ if (ac97_read(codec, 0) == wm9713_reg[0])
+ return 1;
+ }
+
+ soc_ac97_ops->reset(wm9713->ac97);
+ if (soc_ac97_ops->warm_reset)
+ soc_ac97_ops->warm_reset(wm9713->ac97);
+ if (ac97_read(codec, 0) != wm9713_reg[0]) {
+ dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm9713_reset);
+
+static int wm9713_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* enable thermal shutdown */
+ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff;
+ ac97_write(codec, AC97_EXTENDED_MID, reg);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* enable master bias and vmid */
+ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff;
+ ac97_write(codec, AC97_EXTENDED_MID, reg);
+ ac97_write(codec, AC97_POWERDOWN, 0x0000);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* disable everything including AC link */
+ ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
+ ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
+ ac97_write(codec, AC97_POWERDOWN, 0xffff);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int wm9713_soc_suspend(struct snd_soc_codec *codec)
+{
+ u16 reg;
+
+ /* Disable everything except touchpanel - that will be handled
+ * by the touch driver and left disabled if touch is not in
+ * use. */
+ reg = ac97_read(codec, AC97_EXTENDED_MID);
+ ac97_write(codec, AC97_EXTENDED_MID, reg | 0x7fff);
+ ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
+ ac97_write(codec, AC97_POWERDOWN, 0x6f00);
+ ac97_write(codec, AC97_POWERDOWN, 0xffff);
+
+ return 0;
+}
+
+static int wm9713_soc_resume(struct snd_soc_codec *codec)
+{
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
+ u16 *cache = codec->reg_cache;
+
+ ret = wm9713_reset(codec, 1);
+ if (ret < 0)
+ return ret;
+
+ wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* do we need to re-start the PLL ? */
+ if (wm9713->pll_in)
+ wm9713_set_pll(codec, 0, wm9713->pll_in, 0);
+
+ /* only synchronise the codec if warm reset failed */
+ if (ret == 0) {
+ for (i = 2; i < ARRAY_SIZE(wm9713_reg) << 1; i += 2) {
+ if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
+ i == AC97_EXTENDED_MSTATUS || i > 0x66)
+ continue;
+ soc_ac97_ops->write(wm9713->ac97, i, cache[i>>1]);
+ }
+ }
+
+ return ret;
+}
+
+static int wm9713_soc_probe(struct snd_soc_codec *codec)
+{
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0, reg;
+
+ wm9713->ac97 = snd_soc_alloc_ac97_codec(codec);
+ if (IS_ERR(wm9713->ac97))
+ return PTR_ERR(wm9713->ac97);
+
+ /* do a cold reset for the controller and then try
+ * a warm reset followed by an optional cold reset for codec */
+ wm9713_reset(codec, 0);
+ ret = wm9713_reset(codec, 1);
+ if (ret < 0)
+ goto err_put_device;
+
+ ret = device_add(&wm9713->ac97->dev);
+ if (ret)
+ goto err_put_device;
+
+ /* unmute the adc - move to kcontrol */
+ reg = ac97_read(codec, AC97_CD) & 0x7fff;
+ ac97_write(codec, AC97_CD, reg);
+
+ return 0;
+
+err_put_device:
+ put_device(&wm9713->ac97->dev);
+ return ret;
+}
+
+static int wm9713_soc_remove(struct snd_soc_codec *codec)
+{
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_free_ac97_codec(wm9713->ac97);
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
+ .probe = wm9713_soc_probe,
+ .remove = wm9713_soc_remove,
+ .suspend = wm9713_soc_suspend,
+ .resume = wm9713_soc_resume,
+ .read = ac97_read,
+ .write = ac97_write,
+ .set_bias_level = wm9713_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(wm9713_reg),
+ .reg_word_size = sizeof(u16),
+ .reg_cache_step = 2,
+ .reg_cache_default = wm9713_reg,
+
+ .controls = wm9713_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls),
+ .dapm_widgets = wm9713_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
+ .dapm_routes = wm9713_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map),
+};
+
+static int wm9713_probe(struct platform_device *pdev)
+{
+ struct wm9713_priv *wm9713;
+
+ wm9713 = devm_kzalloc(&pdev->dev, sizeof(*wm9713), GFP_KERNEL);
+ if (wm9713 == NULL)
+ return -ENOMEM;
+
+ mutex_init(&wm9713->lock);
+
+ platform_set_drvdata(pdev, wm9713);
+
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai));
+}
+
+static int wm9713_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver wm9713_codec_driver = {
+ .driver = {
+ .name = "wm9713-codec",
+ },
+
+ .probe = wm9713_probe,
+ .remove = wm9713_remove,
+};
+
+module_platform_driver(wm9713_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM9713/WM9714 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm9713.h b/kernel/sound/soc/codecs/wm9713.h
new file mode 100644
index 000000000..793da863a
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm9713.h
@@ -0,0 +1,50 @@
+/*
+ * wm9713.h -- WM9713 Soc Audio driver
+ */
+
+#ifndef _WM9713_H
+#define _WM9713_H
+
+/* clock inputs */
+#define WM9713_CLKA_PIN 0
+#define WM9713_CLKB_PIN 1
+
+/* clock divider ID's */
+#define WM9713_PCMCLK_DIV 0
+#define WM9713_CLKA_MULT 1
+#define WM9713_CLKB_MULT 2
+#define WM9713_HIFI_DIV 3
+#define WM9713_PCMBCLK_DIV 4
+#define WM9713_PCMCLK_PLL_DIV 5
+#define WM9713_HIFI_PLL_DIV 6
+
+/* Calculate the appropriate bit mask for the external PCM clock divider */
+#define WM9713_PCMDIV(x) ((x - 1) << 8)
+
+/* Calculate the appropriate bit mask for the external HiFi clock divider */
+#define WM9713_HIFIDIV(x) ((x - 1) << 12)
+
+/* MCLK clock mulitipliers */
+#define WM9713_CLKA_X1 (0 << 1)
+#define WM9713_CLKA_X2 (1 << 1)
+#define WM9713_CLKB_X1 (0 << 2)
+#define WM9713_CLKB_X2 (1 << 2)
+
+/* MCLK clock MUX */
+#define WM9713_CLK_MUX_A (0 << 0)
+#define WM9713_CLK_MUX_B (1 << 0)
+
+/* Voice DAI BCLK divider */
+#define WM9713_PCMBCLK_DIV_1 (0 << 9)
+#define WM9713_PCMBCLK_DIV_2 (1 << 9)
+#define WM9713_PCMBCLK_DIV_4 (2 << 9)
+#define WM9713_PCMBCLK_DIV_8 (3 << 9)
+#define WM9713_PCMBCLK_DIV_16 (4 << 9)
+
+#define WM9713_DAI_AC97_HIFI 0
+#define WM9713_DAI_AC97_AUX 1
+#define WM9713_DAI_PCM_VOICE 2
+
+int wm9713_reset(struct snd_soc_codec *codec, int try_warm);
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm_adsp.c b/kernel/sound/soc/codecs/wm_adsp.c
new file mode 100644
index 000000000..d01c20954
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm_adsp.c
@@ -0,0 +1,1747 @@
+/*
+ * wm_adsp.c -- Wolfson ADSP 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/list.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.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/registers.h>
+
+#include "arizona.h"
+#include "wm_adsp.h"
+
+#define adsp_crit(_dsp, fmt, ...) \
+ dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_err(_dsp, fmt, ...) \
+ dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_warn(_dsp, fmt, ...) \
+ dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_info(_dsp, fmt, ...) \
+ dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_dbg(_dsp, fmt, ...) \
+ dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+
+#define ADSP1_CONTROL_1 0x00
+#define ADSP1_CONTROL_2 0x02
+#define ADSP1_CONTROL_3 0x03
+#define ADSP1_CONTROL_4 0x04
+#define ADSP1_CONTROL_5 0x06
+#define ADSP1_CONTROL_6 0x07
+#define ADSP1_CONTROL_7 0x08
+#define ADSP1_CONTROL_8 0x09
+#define ADSP1_CONTROL_9 0x0A
+#define ADSP1_CONTROL_10 0x0B
+#define ADSP1_CONTROL_11 0x0C
+#define ADSP1_CONTROL_12 0x0D
+#define ADSP1_CONTROL_13 0x0F
+#define ADSP1_CONTROL_14 0x10
+#define ADSP1_CONTROL_15 0x11
+#define ADSP1_CONTROL_16 0x12
+#define ADSP1_CONTROL_17 0x13
+#define ADSP1_CONTROL_18 0x14
+#define ADSP1_CONTROL_19 0x16
+#define ADSP1_CONTROL_20 0x17
+#define ADSP1_CONTROL_21 0x18
+#define ADSP1_CONTROL_22 0x1A
+#define ADSP1_CONTROL_23 0x1B
+#define ADSP1_CONTROL_24 0x1C
+#define ADSP1_CONTROL_25 0x1E
+#define ADSP1_CONTROL_26 0x20
+#define ADSP1_CONTROL_27 0x21
+#define ADSP1_CONTROL_28 0x22
+#define ADSP1_CONTROL_29 0x23
+#define ADSP1_CONTROL_30 0x24
+#define ADSP1_CONTROL_31 0x26
+
+/*
+ * ADSP1 Control 19
+ */
+#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+
+
+/*
+ * ADSP1 Control 30
+ */
+#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
+#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
+#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
+#define ADSP1_START 0x0001 /* DSP1_START */
+#define ADSP1_START_MASK 0x0001 /* DSP1_START */
+#define ADSP1_START_SHIFT 0 /* DSP1_START */
+#define ADSP1_START_WIDTH 1 /* DSP1_START */
+
+/*
+ * ADSP1 Control 31
+ */
+#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
+#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
+#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
+
+#define ADSP2_CONTROL 0x0
+#define ADSP2_CLOCKING 0x1
+#define ADSP2_STATUS1 0x4
+#define ADSP2_WDMA_CONFIG_1 0x30
+#define ADSP2_WDMA_CONFIG_2 0x31
+#define ADSP2_RDMA_CONFIG_1 0x34
+
+/*
+ * ADSP2 Control
+ */
+
+#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
+#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
+#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
+#define ADSP2_START 0x0001 /* DSP1_START */
+#define ADSP2_START_MASK 0x0001 /* DSP1_START */
+#define ADSP2_START_SHIFT 0 /* DSP1_START */
+#define ADSP2_START_WIDTH 1 /* DSP1_START */
+
+/*
+ * ADSP2 clocking
+ */
+#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
+#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
+#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
+
+/*
+ * ADSP2 Status 1
+ */
+#define ADSP2_RAM_RDY 0x0001
+#define ADSP2_RAM_RDY_MASK 0x0001
+#define ADSP2_RAM_RDY_SHIFT 0
+#define ADSP2_RAM_RDY_WIDTH 1
+
+struct wm_adsp_buf {
+ struct list_head list;
+ void *buf;
+};
+
+static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
+ struct list_head *list)
+{
+ struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+
+ if (buf == NULL)
+ return NULL;
+
+ buf->buf = vmalloc(len);
+ if (!buf->buf) {
+ vfree(buf);
+ return NULL;
+ }
+ memcpy(buf->buf, src, len);
+
+ if (list)
+ list_add_tail(&buf->list, list);
+
+ return buf;
+}
+
+static void wm_adsp_buf_free(struct list_head *list)
+{
+ while (!list_empty(list)) {
+ struct wm_adsp_buf *buf = list_first_entry(list,
+ struct wm_adsp_buf,
+ list);
+ list_del(&buf->list);
+ vfree(buf->buf);
+ kfree(buf);
+ }
+}
+
+#define WM_ADSP_NUM_FW 4
+
+#define WM_ADSP_FW_MBC_VSS 0
+#define WM_ADSP_FW_TX 1
+#define WM_ADSP_FW_TX_SPK 2
+#define WM_ADSP_FW_RX_ANC 3
+
+static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
+ [WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
+ [WM_ADSP_FW_TX] = "Tx",
+ [WM_ADSP_FW_TX_SPK] = "Tx Speaker",
+ [WM_ADSP_FW_RX_ANC] = "Rx ANC",
+};
+
+static struct {
+ const char *file;
+} wm_adsp_fw[WM_ADSP_NUM_FW] = {
+ [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
+ [WM_ADSP_FW_TX] = { .file = "tx" },
+ [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" },
+ [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
+};
+
+struct wm_coeff_ctl_ops {
+ int (*xget)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+ int (*xput)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+ int (*xinfo)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+};
+
+struct wm_coeff_ctl {
+ const char *name;
+ struct wm_adsp_alg_region region;
+ struct wm_coeff_ctl_ops ops;
+ struct wm_adsp *adsp;
+ void *private;
+ unsigned int enabled:1;
+ struct list_head list;
+ void *cache;
+ size_t len;
+ unsigned int set:1;
+ struct snd_kcontrol *kcontrol;
+};
+
+static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = adsp[e->shift_l].fw;
+
+ return 0;
+}
+
+static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
+
+ if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw)
+ return 0;
+
+ if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
+ return -EINVAL;
+
+ if (adsp[e->shift_l].running)
+ return -EBUSY;
+
+ adsp[e->shift_l].fw = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static const struct soc_enum wm_adsp_fw_enum[] = {
+ SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+};
+
+const struct snd_kcontrol_new wm_adsp1_fw_controls[] = {
+ SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+};
+EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls);
+
+#if IS_ENABLED(CONFIG_SND_SOC_ARIZONA)
+static const struct soc_enum wm_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 wm_adsp2_fw_controls[] = {
+ SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]),
+ SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]),
+ SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]),
+ SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]),
+};
+EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls);
+#endif
+
+static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
+ int type)
+{
+ int i;
+
+ for (i = 0; i < dsp->num_mems; i++)
+ if (dsp->mem[i].type == type)
+ return &dsp->mem[i];
+
+ return NULL;
+}
+
+static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
+ unsigned int offset)
+{
+ if (WARN_ON(!region))
+ return offset;
+ switch (region->type) {
+ case WMFW_ADSP1_PM:
+ return region->base + (offset * 3);
+ case WMFW_ADSP1_DM:
+ return region->base + (offset * 2);
+ case WMFW_ADSP2_XM:
+ return region->base + (offset * 2);
+ case WMFW_ADSP2_YM:
+ return region->base + (offset * 2);
+ case WMFW_ADSP1_ZM:
+ return region->base + (offset * 2);
+ default:
+ WARN(1, "Unknown memory region type");
+ return offset;
+ }
+}
+
+static int wm_coeff_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = ctl->len;
+ return 0;
+}
+
+static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
+ const void *buf, size_t len)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ struct wm_adsp_alg_region *region = &ctl->region;
+ const struct wm_adsp_region *mem;
+ struct wm_adsp *adsp = ctl->adsp;
+ void *scratch;
+ int ret;
+ unsigned int reg;
+
+ mem = wm_adsp_find_region(adsp, region->type);
+ if (!mem) {
+ adsp_err(adsp, "No base for region %x\n",
+ region->type);
+ return -EINVAL;
+ }
+
+ reg = ctl->region.base;
+ reg = wm_adsp_region_to_reg(mem, reg);
+
+ scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
+ if (!scratch)
+ return -ENOMEM;
+
+ ret = regmap_raw_write(adsp->regmap, reg, scratch,
+ ctl->len);
+ if (ret) {
+ adsp_err(adsp, "Failed to write %zu bytes to %x: %d\n",
+ ctl->len, reg, ret);
+ kfree(scratch);
+ return ret;
+ }
+ adsp_dbg(adsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
+
+ kfree(scratch);
+
+ return 0;
+}
+
+static int wm_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ char *p = ucontrol->value.bytes.data;
+
+ memcpy(ctl->cache, p, ctl->len);
+
+ ctl->set = 1;
+ if (!ctl->enabled)
+ return 0;
+
+ return wm_coeff_write_control(kcontrol, p, ctl->len);
+}
+
+static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
+ void *buf, size_t len)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ struct wm_adsp_alg_region *region = &ctl->region;
+ const struct wm_adsp_region *mem;
+ struct wm_adsp *adsp = ctl->adsp;
+ void *scratch;
+ int ret;
+ unsigned int reg;
+
+ mem = wm_adsp_find_region(adsp, region->type);
+ if (!mem) {
+ adsp_err(adsp, "No base for region %x\n",
+ region->type);
+ return -EINVAL;
+ }
+
+ reg = ctl->region.base;
+ reg = wm_adsp_region_to_reg(mem, reg);
+
+ scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
+ if (!scratch)
+ return -ENOMEM;
+
+ ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len);
+ if (ret) {
+ adsp_err(adsp, "Failed to read %zu bytes from %x: %d\n",
+ ctl->len, reg, ret);
+ kfree(scratch);
+ return ret;
+ }
+ adsp_dbg(adsp, "Read %zu bytes from %x\n", ctl->len, reg);
+
+ memcpy(buf, scratch, ctl->len);
+ kfree(scratch);
+
+ return 0;
+}
+
+static int wm_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ char *p = ucontrol->value.bytes.data;
+
+ memcpy(p, ctl->cache, ctl->len);
+ return 0;
+}
+
+struct wmfw_ctl_work {
+ struct wm_adsp *adsp;
+ struct wm_coeff_ctl *ctl;
+ struct work_struct work;
+};
+
+static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl)
+{
+ struct snd_kcontrol_new *kcontrol;
+ int ret;
+
+ if (!ctl || !ctl->name)
+ return -EINVAL;
+
+ kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
+ if (!kcontrol)
+ return -ENOMEM;
+ kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+
+ kcontrol->name = ctl->name;
+ kcontrol->info = wm_coeff_info;
+ kcontrol->get = wm_coeff_get;
+ kcontrol->put = wm_coeff_put;
+ kcontrol->private_value = (unsigned long)ctl;
+
+ ret = snd_soc_add_card_controls(adsp->card,
+ kcontrol, 1);
+ if (ret < 0)
+ goto err_kcontrol;
+
+ kfree(kcontrol);
+
+ ctl->kcontrol = snd_soc_card_get_kcontrol(adsp->card,
+ ctl->name);
+
+ list_add(&ctl->list, &adsp->ctl_list);
+ return 0;
+
+err_kcontrol:
+ kfree(kcontrol);
+ return ret;
+}
+
+static int wm_adsp_load(struct wm_adsp *dsp)
+{
+ LIST_HEAD(buf_list);
+ const struct firmware *firmware;
+ struct regmap *regmap = dsp->regmap;
+ unsigned int pos = 0;
+ const struct wmfw_header *header;
+ const struct wmfw_adsp1_sizes *adsp1_sizes;
+ const struct wmfw_adsp2_sizes *adsp2_sizes;
+ const struct wmfw_footer *footer;
+ const struct wmfw_region *region;
+ const struct wm_adsp_region *mem;
+ const char *region_name;
+ char *file, *text;
+ struct wm_adsp_buf *buf;
+ unsigned int reg;
+ int regions = 0;
+ int ret, offset, type, sizes;
+
+ file = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (file == NULL)
+ return -ENOMEM;
+
+ snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
+ wm_adsp_fw[dsp->fw].file);
+ file[PAGE_SIZE - 1] = '\0';
+
+ ret = request_firmware(&firmware, file, dsp->dev);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to request '%s'\n", file);
+ goto out;
+ }
+ ret = -EINVAL;
+
+ pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
+ if (pos >= firmware->size) {
+ adsp_err(dsp, "%s: file too short, %zu bytes\n",
+ file, firmware->size);
+ goto out_fw;
+ }
+
+ header = (void*)&firmware->data[0];
+
+ if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
+ adsp_err(dsp, "%s: invalid magic\n", file);
+ goto out_fw;
+ }
+
+ if (header->ver != 0) {
+ adsp_err(dsp, "%s: unknown file format %d\n",
+ file, header->ver);
+ goto out_fw;
+ }
+ adsp_info(dsp, "Firmware version: %d\n", header->ver);
+
+ if (header->core != dsp->type) {
+ adsp_err(dsp, "%s: invalid core %d != %d\n",
+ file, header->core, dsp->type);
+ goto out_fw;
+ }
+
+ switch (dsp->type) {
+ case WMFW_ADSP1:
+ pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
+ adsp1_sizes = (void *)&(header[1]);
+ footer = (void *)&(adsp1_sizes[1]);
+ sizes = sizeof(*adsp1_sizes);
+
+ adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
+ file, le32_to_cpu(adsp1_sizes->dm),
+ le32_to_cpu(adsp1_sizes->pm),
+ le32_to_cpu(adsp1_sizes->zm));
+ break;
+
+ case WMFW_ADSP2:
+ pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
+ adsp2_sizes = (void *)&(header[1]);
+ footer = (void *)&(adsp2_sizes[1]);
+ sizes = sizeof(*adsp2_sizes);
+
+ adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
+ file, le32_to_cpu(adsp2_sizes->xm),
+ le32_to_cpu(adsp2_sizes->ym),
+ le32_to_cpu(adsp2_sizes->pm),
+ le32_to_cpu(adsp2_sizes->zm));
+ break;
+
+ default:
+ WARN(1, "Unknown DSP type");
+ goto out_fw;
+ }
+
+ if (le32_to_cpu(header->len) != sizeof(*header) +
+ sizes + sizeof(*footer)) {
+ adsp_err(dsp, "%s: unexpected header length %d\n",
+ file, le32_to_cpu(header->len));
+ goto out_fw;
+ }
+
+ adsp_dbg(dsp, "%s: timestamp %llu\n", file,
+ le64_to_cpu(footer->timestamp));
+
+ while (pos < firmware->size &&
+ pos - firmware->size > sizeof(*region)) {
+ region = (void *)&(firmware->data[pos]);
+ region_name = "Unknown";
+ reg = 0;
+ text = NULL;
+ offset = le32_to_cpu(region->offset) & 0xffffff;
+ type = be32_to_cpu(region->type) & 0xff;
+ mem = wm_adsp_find_region(dsp, type);
+
+ switch (type) {
+ case WMFW_NAME_TEXT:
+ region_name = "Firmware name";
+ text = kzalloc(le32_to_cpu(region->len) + 1,
+ GFP_KERNEL);
+ break;
+ case WMFW_INFO_TEXT:
+ region_name = "Information";
+ text = kzalloc(le32_to_cpu(region->len) + 1,
+ GFP_KERNEL);
+ break;
+ case WMFW_ABSOLUTE:
+ region_name = "Absolute";
+ reg = offset;
+ break;
+ case WMFW_ADSP1_PM:
+ region_name = "PM";
+ reg = wm_adsp_region_to_reg(mem, offset);
+ break;
+ case WMFW_ADSP1_DM:
+ region_name = "DM";
+ reg = wm_adsp_region_to_reg(mem, offset);
+ break;
+ case WMFW_ADSP2_XM:
+ region_name = "XM";
+ reg = wm_adsp_region_to_reg(mem, offset);
+ break;
+ case WMFW_ADSP2_YM:
+ region_name = "YM";
+ reg = wm_adsp_region_to_reg(mem, offset);
+ break;
+ case WMFW_ADSP1_ZM:
+ region_name = "ZM";
+ reg = wm_adsp_region_to_reg(mem, offset);
+ break;
+ default:
+ adsp_warn(dsp,
+ "%s.%d: Unknown region type %x at %d(%x)\n",
+ file, regions, type, pos, pos);
+ break;
+ }
+
+ adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
+ regions, le32_to_cpu(region->len), offset,
+ region_name);
+
+ if (text) {
+ memcpy(text, region->data, le32_to_cpu(region->len));
+ adsp_info(dsp, "%s: %s\n", file, text);
+ kfree(text);
+ }
+
+ if (reg) {
+ buf = wm_adsp_buf_alloc(region->data,
+ le32_to_cpu(region->len),
+ &buf_list);
+ if (!buf) {
+ adsp_err(dsp, "Out of memory\n");
+ ret = -ENOMEM;
+ goto out_fw;
+ }
+
+ ret = regmap_raw_write_async(regmap, reg, buf->buf,
+ le32_to_cpu(region->len));
+ if (ret != 0) {
+ adsp_err(dsp,
+ "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
+ file, regions,
+ le32_to_cpu(region->len), offset,
+ region_name, ret);
+ goto out_fw;
+ }
+ }
+
+ pos += le32_to_cpu(region->len) + sizeof(*region);
+ regions++;
+ }
+
+ ret = regmap_async_complete(regmap);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to complete async write: %d\n", ret);
+ goto out_fw;
+ }
+
+ if (pos > firmware->size)
+ adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
+ file, regions, pos - firmware->size);
+
+out_fw:
+ regmap_async_complete(regmap);
+ wm_adsp_buf_free(&buf_list);
+ release_firmware(firmware);
+out:
+ kfree(file);
+
+ return ret;
+}
+
+static int wm_coeff_init_control_caches(struct wm_adsp *adsp)
+{
+ struct wm_coeff_ctl *ctl;
+ int ret;
+
+ list_for_each_entry(ctl, &adsp->ctl_list, list) {
+ if (!ctl->enabled || ctl->set)
+ continue;
+ ret = wm_coeff_read_control(ctl->kcontrol,
+ ctl->cache,
+ ctl->len);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wm_coeff_sync_controls(struct wm_adsp *adsp)
+{
+ struct wm_coeff_ctl *ctl;
+ int ret;
+
+ list_for_each_entry(ctl, &adsp->ctl_list, list) {
+ if (!ctl->enabled)
+ continue;
+ if (ctl->set) {
+ ret = wm_coeff_write_control(ctl->kcontrol,
+ ctl->cache,
+ ctl->len);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void wm_adsp_ctl_work(struct work_struct *work)
+{
+ struct wmfw_ctl_work *ctl_work = container_of(work,
+ struct wmfw_ctl_work,
+ work);
+
+ wmfw_add_ctl(ctl_work->adsp, ctl_work->ctl);
+ kfree(ctl_work);
+}
+
+static int wm_adsp_create_control(struct wm_adsp *dsp,
+ const struct wm_adsp_alg_region *region)
+
+{
+ struct wm_coeff_ctl *ctl;
+ struct wmfw_ctl_work *ctl_work;
+ char *name;
+ char *region_name;
+ int ret;
+
+ name = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ switch (region->type) {
+ case WMFW_ADSP1_PM:
+ region_name = "PM";
+ break;
+ case WMFW_ADSP1_DM:
+ region_name = "DM";
+ break;
+ case WMFW_ADSP2_XM:
+ region_name = "XM";
+ break;
+ case WMFW_ADSP2_YM:
+ region_name = "YM";
+ break;
+ case WMFW_ADSP1_ZM:
+ region_name = "ZM";
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_name;
+ }
+
+ snprintf(name, PAGE_SIZE, "DSP%d %s %x",
+ dsp->num, region_name, region->alg);
+
+ list_for_each_entry(ctl, &dsp->ctl_list,
+ list) {
+ if (!strcmp(ctl->name, name)) {
+ if (!ctl->enabled)
+ ctl->enabled = 1;
+ goto found;
+ }
+ }
+
+ ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
+ if (!ctl) {
+ ret = -ENOMEM;
+ goto err_name;
+ }
+ ctl->region = *region;
+ ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
+ if (!ctl->name) {
+ ret = -ENOMEM;
+ goto err_ctl;
+ }
+ ctl->enabled = 1;
+ ctl->set = 0;
+ ctl->ops.xget = wm_coeff_get;
+ ctl->ops.xput = wm_coeff_put;
+ ctl->adsp = dsp;
+
+ ctl->len = region->len;
+ ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
+ if (!ctl->cache) {
+ ret = -ENOMEM;
+ goto err_ctl_name;
+ }
+
+ ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
+ if (!ctl_work) {
+ ret = -ENOMEM;
+ goto err_ctl_cache;
+ }
+
+ ctl_work->adsp = dsp;
+ ctl_work->ctl = ctl;
+ INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
+ schedule_work(&ctl_work->work);
+
+found:
+ kfree(name);
+
+ return 0;
+
+err_ctl_cache:
+ kfree(ctl->cache);
+err_ctl_name:
+ kfree(ctl->name);
+err_ctl:
+ kfree(ctl);
+err_name:
+ kfree(name);
+ return ret;
+}
+
+static int wm_adsp_setup_algs(struct wm_adsp *dsp)
+{
+ struct regmap *regmap = dsp->regmap;
+ struct wmfw_adsp1_id_hdr adsp1_id;
+ struct wmfw_adsp2_id_hdr adsp2_id;
+ struct wmfw_adsp1_alg_hdr *adsp1_alg;
+ struct wmfw_adsp2_alg_hdr *adsp2_alg;
+ void *alg, *buf;
+ struct wm_adsp_alg_region *region;
+ const struct wm_adsp_region *mem;
+ unsigned int pos, term;
+ size_t algs, buf_size;
+ __be32 val;
+ int i, ret;
+
+ switch (dsp->type) {
+ case WMFW_ADSP1:
+ mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
+ break;
+ case WMFW_ADSP2:
+ mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
+ break;
+ default:
+ mem = NULL;
+ break;
+ }
+
+ if (WARN_ON(!mem))
+ return -EINVAL;
+
+ switch (dsp->type) {
+ case WMFW_ADSP1:
+ ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
+ sizeof(adsp1_id));
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read algorithm info: %d\n",
+ ret);
+ return ret;
+ }
+
+ buf = &adsp1_id;
+ buf_size = sizeof(adsp1_id);
+
+ algs = be32_to_cpu(adsp1_id.algs);
+ dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
+ adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
+ dsp->fw_id,
+ (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
+ (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
+ be32_to_cpu(adsp1_id.fw.ver) & 0xff,
+ algs);
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP1_ZM;
+ region->alg = be32_to_cpu(adsp1_id.fw.id);
+ region->base = be32_to_cpu(adsp1_id.zm);
+ list_add_tail(&region->list, &dsp->alg_regions);
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP1_DM;
+ region->alg = be32_to_cpu(adsp1_id.fw.id);
+ region->base = be32_to_cpu(adsp1_id.dm);
+ list_add_tail(&region->list, &dsp->alg_regions);
+
+ pos = sizeof(adsp1_id) / 2;
+ term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
+ break;
+
+ case WMFW_ADSP2:
+ ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
+ sizeof(adsp2_id));
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read algorithm info: %d\n",
+ ret);
+ return ret;
+ }
+
+ buf = &adsp2_id;
+ buf_size = sizeof(adsp2_id);
+
+ algs = be32_to_cpu(adsp2_id.algs);
+ dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
+ adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
+ dsp->fw_id,
+ (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
+ (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
+ be32_to_cpu(adsp2_id.fw.ver) & 0xff,
+ algs);
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP2_XM;
+ region->alg = be32_to_cpu(adsp2_id.fw.id);
+ region->base = be32_to_cpu(adsp2_id.xm);
+ list_add_tail(&region->list, &dsp->alg_regions);
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP2_YM;
+ region->alg = be32_to_cpu(adsp2_id.fw.id);
+ region->base = be32_to_cpu(adsp2_id.ym);
+ list_add_tail(&region->list, &dsp->alg_regions);
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP2_ZM;
+ region->alg = be32_to_cpu(adsp2_id.fw.id);
+ region->base = be32_to_cpu(adsp2_id.zm);
+ list_add_tail(&region->list, &dsp->alg_regions);
+
+ pos = sizeof(adsp2_id) / 2;
+ term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
+ break;
+
+ default:
+ WARN(1, "Unknown DSP type");
+ return -EINVAL;
+ }
+
+ if (algs == 0) {
+ adsp_err(dsp, "No algorithms\n");
+ return -EINVAL;
+ }
+
+ if (algs > 1024) {
+ adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
+ print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
+ buf, buf_size);
+ return -EINVAL;
+ }
+
+ /* Read the terminator first to validate the length */
+ ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read algorithm list end: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (be32_to_cpu(val) != 0xbedead)
+ adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
+ term, be32_to_cpu(val));
+
+ alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA);
+ if (!alg)
+ return -ENOMEM;
+
+ ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read algorithm list: %d\n",
+ ret);
+ goto out;
+ }
+
+ adsp1_alg = alg;
+ adsp2_alg = alg;
+
+ for (i = 0; i < algs; i++) {
+ switch (dsp->type) {
+ case WMFW_ADSP1:
+ adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
+ i, be32_to_cpu(adsp1_alg[i].alg.id),
+ (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
+ (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
+ be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
+ be32_to_cpu(adsp1_alg[i].dm),
+ be32_to_cpu(adsp1_alg[i].zm));
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ region->type = WMFW_ADSP1_DM;
+ region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
+ region->base = be32_to_cpu(adsp1_alg[i].dm);
+ region->len = 0;
+ list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp1_alg[i + 1].dm);
+ region->len -= be32_to_cpu(adsp1_alg[i].dm);
+ region->len *= 4;
+ wm_adsp_create_control(dsp, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
+ be32_to_cpu(adsp1_alg[i].alg.id));
+ }
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ region->type = WMFW_ADSP1_ZM;
+ region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
+ region->base = be32_to_cpu(adsp1_alg[i].zm);
+ region->len = 0;
+ list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp1_alg[i + 1].zm);
+ region->len -= be32_to_cpu(adsp1_alg[i].zm);
+ region->len *= 4;
+ wm_adsp_create_control(dsp, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
+ be32_to_cpu(adsp1_alg[i].alg.id));
+ }
+ break;
+
+ case WMFW_ADSP2:
+ adsp_info(dsp,
+ "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
+ i, be32_to_cpu(adsp2_alg[i].alg.id),
+ (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
+ (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
+ be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
+ be32_to_cpu(adsp2_alg[i].xm),
+ be32_to_cpu(adsp2_alg[i].ym),
+ be32_to_cpu(adsp2_alg[i].zm));
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ region->type = WMFW_ADSP2_XM;
+ region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
+ region->base = be32_to_cpu(adsp2_alg[i].xm);
+ region->len = 0;
+ list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp2_alg[i + 1].xm);
+ region->len -= be32_to_cpu(adsp2_alg[i].xm);
+ region->len *= 4;
+ wm_adsp_create_control(dsp, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
+ be32_to_cpu(adsp2_alg[i].alg.id));
+ }
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ region->type = WMFW_ADSP2_YM;
+ region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
+ region->base = be32_to_cpu(adsp2_alg[i].ym);
+ region->len = 0;
+ list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp2_alg[i + 1].ym);
+ region->len -= be32_to_cpu(adsp2_alg[i].ym);
+ region->len *= 4;
+ wm_adsp_create_control(dsp, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
+ be32_to_cpu(adsp2_alg[i].alg.id));
+ }
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ region->type = WMFW_ADSP2_ZM;
+ region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
+ region->base = be32_to_cpu(adsp2_alg[i].zm);
+ region->len = 0;
+ list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp2_alg[i + 1].zm);
+ region->len -= be32_to_cpu(adsp2_alg[i].zm);
+ region->len *= 4;
+ wm_adsp_create_control(dsp, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
+ be32_to_cpu(adsp2_alg[i].alg.id));
+ }
+ break;
+ }
+ }
+
+out:
+ kfree(alg);
+ return ret;
+}
+
+static int wm_adsp_load_coeff(struct wm_adsp *dsp)
+{
+ LIST_HEAD(buf_list);
+ struct regmap *regmap = dsp->regmap;
+ struct wmfw_coeff_hdr *hdr;
+ struct wmfw_coeff_item *blk;
+ const struct firmware *firmware;
+ const struct wm_adsp_region *mem;
+ struct wm_adsp_alg_region *alg_region;
+ const char *region_name;
+ int ret, pos, blocks, type, offset, reg;
+ char *file;
+ struct wm_adsp_buf *buf;
+
+ file = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (file == NULL)
+ return -ENOMEM;
+
+ snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
+ wm_adsp_fw[dsp->fw].file);
+ file[PAGE_SIZE - 1] = '\0';
+
+ ret = request_firmware(&firmware, file, dsp->dev);
+ if (ret != 0) {
+ adsp_warn(dsp, "Failed to request '%s'\n", file);
+ ret = 0;
+ goto out;
+ }
+ ret = -EINVAL;
+
+ if (sizeof(*hdr) >= firmware->size) {
+ adsp_err(dsp, "%s: file too short, %zu bytes\n",
+ file, firmware->size);
+ goto out_fw;
+ }
+
+ hdr = (void*)&firmware->data[0];
+ if (memcmp(hdr->magic, "WMDR", 4) != 0) {
+ adsp_err(dsp, "%s: invalid magic\n", file);
+ goto out_fw;
+ }
+
+ switch (be32_to_cpu(hdr->rev) & 0xff) {
+ case 1:
+ break;
+ default:
+ adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
+ file, be32_to_cpu(hdr->rev) & 0xff);
+ ret = -EINVAL;
+ goto out_fw;
+ }
+
+ adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
+ (le32_to_cpu(hdr->ver) >> 16) & 0xff,
+ (le32_to_cpu(hdr->ver) >> 8) & 0xff,
+ le32_to_cpu(hdr->ver) & 0xff);
+
+ pos = le32_to_cpu(hdr->len);
+
+ blocks = 0;
+ while (pos < firmware->size &&
+ pos - firmware->size > sizeof(*blk)) {
+ blk = (void*)(&firmware->data[pos]);
+
+ type = le16_to_cpu(blk->type);
+ offset = le16_to_cpu(blk->offset);
+
+ adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
+ file, blocks, le32_to_cpu(blk->id),
+ (le32_to_cpu(blk->ver) >> 16) & 0xff,
+ (le32_to_cpu(blk->ver) >> 8) & 0xff,
+ le32_to_cpu(blk->ver) & 0xff);
+ adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
+ file, blocks, le32_to_cpu(blk->len), offset, type);
+
+ reg = 0;
+ region_name = "Unknown";
+ switch (type) {
+ case (WMFW_NAME_TEXT << 8):
+ case (WMFW_INFO_TEXT << 8):
+ break;
+ case (WMFW_ABSOLUTE << 8):
+ /*
+ * Old files may use this for global
+ * coefficients.
+ */
+ if (le32_to_cpu(blk->id) == dsp->fw_id &&
+ offset == 0) {
+ region_name = "global coefficients";
+ mem = wm_adsp_find_region(dsp, type);
+ if (!mem) {
+ adsp_err(dsp, "No ZM\n");
+ break;
+ }
+ reg = wm_adsp_region_to_reg(mem, 0);
+
+ } else {
+ region_name = "register";
+ reg = offset;
+ }
+ break;
+
+ case WMFW_ADSP1_DM:
+ case WMFW_ADSP1_ZM:
+ case WMFW_ADSP2_XM:
+ case WMFW_ADSP2_YM:
+ adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
+ file, blocks, le32_to_cpu(blk->len),
+ type, le32_to_cpu(blk->id));
+
+ mem = wm_adsp_find_region(dsp, type);
+ if (!mem) {
+ adsp_err(dsp, "No base for region %x\n", type);
+ break;
+ }
+
+ reg = 0;
+ list_for_each_entry(alg_region,
+ &dsp->alg_regions, list) {
+ if (le32_to_cpu(blk->id) == alg_region->alg &&
+ type == alg_region->type) {
+ reg = alg_region->base;
+ reg = wm_adsp_region_to_reg(mem,
+ reg);
+ reg += offset;
+ break;
+ }
+ }
+
+ if (reg == 0)
+ adsp_err(dsp, "No %x for algorithm %x\n",
+ type, le32_to_cpu(blk->id));
+ break;
+
+ default:
+ adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
+ file, blocks, type, pos);
+ break;
+ }
+
+ if (reg) {
+ buf = wm_adsp_buf_alloc(blk->data,
+ le32_to_cpu(blk->len),
+ &buf_list);
+ if (!buf) {
+ adsp_err(dsp, "Out of memory\n");
+ ret = -ENOMEM;
+ goto out_fw;
+ }
+
+ adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
+ file, blocks, le32_to_cpu(blk->len),
+ reg);
+ ret = regmap_raw_write_async(regmap, reg, buf->buf,
+ le32_to_cpu(blk->len));
+ if (ret != 0) {
+ adsp_err(dsp,
+ "%s.%d: Failed to write to %x in %s: %d\n",
+ file, blocks, reg, region_name, ret);
+ }
+ }
+
+ pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
+ blocks++;
+ }
+
+ ret = regmap_async_complete(regmap);
+ if (ret != 0)
+ adsp_err(dsp, "Failed to complete async write: %d\n", ret);
+
+ if (pos > firmware->size)
+ adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
+ file, blocks, pos - firmware->size);
+
+out_fw:
+ regmap_async_complete(regmap);
+ release_firmware(firmware);
+ wm_adsp_buf_free(&buf_list);
+out:
+ kfree(file);
+ return ret;
+}
+
+int wm_adsp1_init(struct wm_adsp *adsp)
+{
+ INIT_LIST_HEAD(&adsp->alg_regions);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp1_init);
+
+int wm_adsp1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+ struct wm_adsp *dsp = &dsps[w->shift];
+ struct wm_adsp_alg_region *alg_region;
+ struct wm_coeff_ctl *ctl;
+ int ret;
+ int val;
+
+ dsp->card = codec->component.card;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_SYS_ENA, ADSP1_SYS_ENA);
+
+ /*
+ * For simplicity set the DSP clock rate to be the
+ * SYSCLK rate rather than making it configurable.
+ */
+ if(dsp->sysclk_reg) {
+ ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
+ ret);
+ return ret;
+ }
+
+ val = (val & dsp->sysclk_mask)
+ >> dsp->sysclk_shift;
+
+ ret = regmap_update_bits(dsp->regmap,
+ dsp->base + ADSP1_CONTROL_31,
+ ADSP1_CLK_SEL_MASK, val);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to set clock rate: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ ret = wm_adsp_load(dsp);
+ if (ret != 0)
+ goto err;
+
+ ret = wm_adsp_setup_algs(dsp);
+ if (ret != 0)
+ goto err;
+
+ ret = wm_adsp_load_coeff(dsp);
+ if (ret != 0)
+ goto err;
+
+ /* Initialize caches for enabled and unset controls */
+ ret = wm_coeff_init_control_caches(dsp);
+ if (ret != 0)
+ goto err;
+
+ /* Sync set controls */
+ ret = wm_coeff_sync_controls(dsp);
+ if (ret != 0)
+ goto err;
+
+ /* Start the core running */
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_CORE_ENA | ADSP1_START,
+ ADSP1_CORE_ENA | ADSP1_START);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Halt the core */
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_CORE_ENA | ADSP1_START, 0);
+
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
+ ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
+
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_SYS_ENA, 0);
+
+ list_for_each_entry(ctl, &dsp->ctl_list, list)
+ ctl->enabled = 0;
+
+ while (!list_empty(&dsp->alg_regions)) {
+ alg_region = list_first_entry(&dsp->alg_regions,
+ struct wm_adsp_alg_region,
+ list);
+ list_del(&alg_region->list);
+ kfree(alg_region);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+
+err:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_SYS_ENA, 0);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp1_event);
+
+static int wm_adsp2_ena(struct wm_adsp *dsp)
+{
+ unsigned int val;
+ int ret, count;
+
+ ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, ADSP2_SYS_ENA);
+ if (ret != 0)
+ return ret;
+
+ /* Wait for the RAM to start, should be near instantaneous */
+ for (count = 0; count < 10; ++count) {
+ ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
+ &val);
+ if (ret != 0)
+ return ret;
+
+ if (val & ADSP2_RAM_RDY)
+ break;
+
+ msleep(1);
+ }
+
+ if (!(val & ADSP2_RAM_RDY)) {
+ adsp_err(dsp, "Failed to start DSP RAM\n");
+ return -EBUSY;
+ }
+
+ adsp_dbg(dsp, "RAM ready after %d polls\n", count);
+
+ return 0;
+}
+
+static void wm_adsp2_boot_work(struct work_struct *work)
+{
+ struct wm_adsp *dsp = container_of(work,
+ struct wm_adsp,
+ boot_work);
+ int ret;
+ unsigned int val;
+
+ /*
+ * For simplicity set the DSP clock rate to be the
+ * SYSCLK rate rather than making it configurable.
+ */
+ ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
+ return;
+ }
+ val = (val & ARIZONA_SYSCLK_FREQ_MASK)
+ >> ARIZONA_SYSCLK_FREQ_SHIFT;
+
+ ret = regmap_update_bits_async(dsp->regmap,
+ dsp->base + ADSP2_CLOCKING,
+ ADSP2_CLK_SEL_MASK, val);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
+ return;
+ }
+
+ if (dsp->dvfs) {
+ ret = regmap_read(dsp->regmap,
+ dsp->base + ADSP2_CLOCKING, &val);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read clocking: %d\n", ret);
+ return;
+ }
+
+ if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
+ ret = regulator_enable(dsp->dvfs);
+ if (ret != 0) {
+ adsp_err(dsp,
+ "Failed to enable supply: %d\n",
+ ret);
+ return;
+ }
+
+ ret = regulator_set_voltage(dsp->dvfs,
+ 1800000,
+ 1800000);
+ if (ret != 0) {
+ adsp_err(dsp,
+ "Failed to raise supply: %d\n",
+ ret);
+ return;
+ }
+ }
+ }
+
+ ret = wm_adsp2_ena(dsp);
+ if (ret != 0)
+ return;
+
+ ret = wm_adsp_load(dsp);
+ if (ret != 0)
+ goto err;
+
+ ret = wm_adsp_setup_algs(dsp);
+ if (ret != 0)
+ goto err;
+
+ ret = wm_adsp_load_coeff(dsp);
+ if (ret != 0)
+ goto err;
+
+ /* Initialize caches for enabled and unset controls */
+ ret = wm_coeff_init_control_caches(dsp);
+ if (ret != 0)
+ goto err;
+
+ /* Sync set controls */
+ ret = wm_coeff_sync_controls(dsp);
+ if (ret != 0)
+ goto err;
+
+ dsp->running = true;
+
+ return;
+
+err:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
+}
+
+int wm_adsp2_early_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 wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+ struct wm_adsp *dsp = &dsps[w->shift];
+
+ dsp->card = codec->component.card;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ queue_work(system_unbound_wq, &dsp->boot_work);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
+
+int wm_adsp2_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 wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+ struct wm_adsp *dsp = &dsps[w->shift];
+ struct wm_adsp_alg_region *alg_region;
+ struct wm_coeff_ctl *ctl;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ flush_work(&dsp->boot_work);
+
+ if (!dsp->running)
+ return -EIO;
+
+ ret = regmap_update_bits(dsp->regmap,
+ dsp->base + ADSP2_CONTROL,
+ ADSP2_CORE_ENA | ADSP2_START,
+ ADSP2_CORE_ENA | ADSP2_START);
+ if (ret != 0)
+ goto err;
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ dsp->running = false;
+
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA | ADSP2_CORE_ENA |
+ ADSP2_START, 0);
+
+ /* Make sure DMAs are quiesced */
+ regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
+ regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
+ regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+
+ if (dsp->dvfs) {
+ ret = regulator_set_voltage(dsp->dvfs, 1200000,
+ 1800000);
+ if (ret != 0)
+ adsp_warn(dsp,
+ "Failed to lower supply: %d\n",
+ ret);
+
+ ret = regulator_disable(dsp->dvfs);
+ if (ret != 0)
+ adsp_err(dsp,
+ "Failed to enable supply: %d\n",
+ ret);
+ }
+
+ list_for_each_entry(ctl, &dsp->ctl_list, list)
+ ctl->enabled = 0;
+
+ while (!list_empty(&dsp->alg_regions)) {
+ alg_region = list_first_entry(&dsp->alg_regions,
+ struct wm_adsp_alg_region,
+ list);
+ list_del(&alg_region->list);
+ kfree(alg_region);
+ }
+
+ adsp_dbg(dsp, "Shutdown complete\n");
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+err:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_event);
+
+int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
+{
+ int ret;
+
+ /*
+ * Disable the DSP memory by default when in reset for a small
+ * power saving.
+ */
+ ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL,
+ ADSP2_MEM_ENA, 0);
+ if (ret != 0) {
+ adsp_err(adsp, "Failed to clear memory retention: %d\n", ret);
+ return ret;
+ }
+
+ INIT_LIST_HEAD(&adsp->alg_regions);
+ INIT_LIST_HEAD(&adsp->ctl_list);
+ INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work);
+
+ if (dvfs) {
+ adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
+ if (IS_ERR(adsp->dvfs)) {
+ ret = PTR_ERR(adsp->dvfs);
+ adsp_err(adsp, "Failed to get DCVDD: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(adsp->dvfs);
+ if (ret != 0) {
+ adsp_err(adsp, "Failed to enable DCVDD: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
+ if (ret != 0) {
+ adsp_err(adsp, "Failed to initialise DVFS: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_disable(adsp->dvfs);
+ if (ret != 0) {
+ adsp_err(adsp, "Failed to disable DCVDD: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_init);
+
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/sound/soc/codecs/wm_adsp.h b/kernel/sound/soc/codecs/wm_adsp.h
new file mode 100644
index 000000000..a4f6b64de
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm_adsp.h
@@ -0,0 +1,90 @@
+/*
+ * wm_adsp.h -- Wolfson ADSP 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 __WM_ADSP_H
+#define __WM_ADSP_H
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "wmfw.h"
+
+struct regulator;
+
+struct wm_adsp_region {
+ int type;
+ unsigned int base;
+};
+
+struct wm_adsp_alg_region {
+ struct list_head list;
+ unsigned int alg;
+ int type;
+ unsigned int base;
+ size_t len;
+};
+
+struct wm_adsp {
+ const char *part;
+ int num;
+ int type;
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_card *card;
+
+ int base;
+ int sysclk_reg;
+ int sysclk_mask;
+ int sysclk_shift;
+
+ struct list_head alg_regions;
+
+ int fw_id;
+
+ const struct wm_adsp_region *mem;
+ int num_mems;
+
+ int fw;
+ bool running;
+
+ struct regulator *dvfs;
+
+ struct list_head ctl_list;
+
+ struct work_struct boot_work;
+};
+
+#define WM_ADSP1(wname, num) \
+ SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
+ wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define WM_ADSP2(wname, num) \
+{ .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \
+ .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_early_event, \
+ .event_flags = SND_SOC_DAPM_PRE_PMU }, \
+{ .id = snd_soc_dapm_out_drv, .name = wname, \
+ .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \
+ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+
+extern const struct snd_kcontrol_new wm_adsp1_fw_controls[];
+extern const struct snd_kcontrol_new wm_adsp2_fw_controls[];
+
+int wm_adsp1_init(struct wm_adsp *adsp);
+int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs);
+int wm_adsp1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int wm_adsp2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+#endif
diff --git a/kernel/sound/soc/codecs/wm_hubs.c b/kernel/sound/soc/codecs/wm_hubs.c
new file mode 100644
index 000000000..8366e1965
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm_hubs.c
@@ -0,0 +1,1311 @@
+/*
+ * wm_hubs.c -- WM8993/4 common code
+ *
+ * Copyright 2009-12 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/mfd/wm8994/registers.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 "wm8993.h"
+#include "wm_hubs.h"
+
+const DECLARE_TLV_DB_SCALE(wm_hubs_spkmix_tlv, -300, 300, 0);
+EXPORT_SYMBOL_GPL(wm_hubs_spkmix_tlv);
+
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(inmix_sw_tlv, 0, 3000, 0);
+static const DECLARE_TLV_DB_SCALE(inmix_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(earpiece_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(outmix_tlv, -2100, 300, 0);
+static const DECLARE_TLV_DB_SCALE(spkmixout_tlv, -1800, 600, 1);
+static const DECLARE_TLV_DB_SCALE(outpga_tlv, -5700, 100, 0);
+static const unsigned int spkboost_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(line_tlv, -600, 600, 0);
+
+static const char *speaker_ref_text[] = {
+ "SPKVDD/2",
+ "VMID",
+};
+
+static SOC_ENUM_SINGLE_DECL(speaker_ref,
+ WM8993_SPEAKER_MIXER, 8, speaker_ref_text);
+
+static const char *speaker_mode_text[] = {
+ "Class D",
+ "Class AB",
+};
+
+static SOC_ENUM_SINGLE_DECL(speaker_mode,
+ WM8993_SPKMIXR_ATTENUATION, 8, speaker_mode_text);
+
+static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg;
+ int count = 0;
+ int timeout;
+ unsigned int val;
+
+ val = op | WM8993_DCS_ENA_CHAN_0 | WM8993_DCS_ENA_CHAN_1;
+
+ /* Trigger the command */
+ snd_soc_write(codec, WM8993_DC_SERVO_0, val);
+
+ dev_dbg(codec->dev, "Waiting for DC servo...\n");
+
+ if (hubs->dcs_done_irq)
+ timeout = 4;
+ else
+ timeout = 400;
+
+ do {
+ count++;
+
+ if (hubs->dcs_done_irq)
+ wait_for_completion_timeout(&hubs->dcs_done,
+ msecs_to_jiffies(250));
+ else
+ msleep(1);
+
+ reg = snd_soc_read(codec, WM8993_DC_SERVO_0);
+ dev_dbg(codec->dev, "DC servo: %x\n", reg);
+ } while (reg & op && count < timeout);
+
+ if (reg & op)
+ dev_err(codec->dev, "Timed out waiting for DC Servo %x\n",
+ op);
+}
+
+irqreturn_t wm_hubs_dcs_done(int irq, void *data)
+{
+ struct wm_hubs_data *hubs = data;
+
+ complete(&hubs->dcs_done);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_dcs_done);
+
+static bool wm_hubs_dac_hp_direct(struct snd_soc_codec *codec)
+{
+ int reg;
+
+ /* If we're going via the mixer we'll need to do additional checks */
+ reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER1);
+ if (!(reg & WM8993_DACL_TO_HPOUT1L)) {
+ if (reg & ~WM8993_DACL_TO_MIXOUTL) {
+ dev_vdbg(codec->dev, "Analogue paths connected: %x\n",
+ reg & ~WM8993_DACL_TO_HPOUT1L);
+ return false;
+ } else {
+ dev_vdbg(codec->dev, "HPL connected to mixer\n");
+ }
+ } else {
+ dev_vdbg(codec->dev, "HPL connected to DAC\n");
+ }
+
+ reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER2);
+ if (!(reg & WM8993_DACR_TO_HPOUT1R)) {
+ if (reg & ~WM8993_DACR_TO_MIXOUTR) {
+ dev_vdbg(codec->dev, "Analogue paths connected: %x\n",
+ reg & ~WM8993_DACR_TO_HPOUT1R);
+ return false;
+ } else {
+ dev_vdbg(codec->dev, "HPR connected to mixer\n");
+ }
+ } else {
+ dev_vdbg(codec->dev, "HPR connected to DAC\n");
+ }
+
+ return true;
+}
+
+struct wm_hubs_dcs_cache {
+ struct list_head list;
+ unsigned int left;
+ unsigned int right;
+ u16 dcs_cfg;
+};
+
+static bool wm_hubs_dcs_cache_get(struct snd_soc_codec *codec,
+ struct wm_hubs_dcs_cache **entry)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_dcs_cache *cache;
+ unsigned int left, right;
+
+ left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
+ left &= WM8993_HPOUT1L_VOL_MASK;
+
+ right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
+ right &= WM8993_HPOUT1R_VOL_MASK;
+
+ list_for_each_entry(cache, &hubs->dcs_cache, list) {
+ if (cache->left != left || cache->right != right)
+ continue;
+
+ *entry = cache;
+ return true;
+ }
+
+ return false;
+}
+
+static void wm_hubs_dcs_cache_set(struct snd_soc_codec *codec, u16 dcs_cfg)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_dcs_cache *cache;
+
+ if (hubs->no_cache_dac_hp_direct)
+ return;
+
+ cache = devm_kzalloc(codec->dev, sizeof(*cache), GFP_KERNEL);
+ if (!cache)
+ return;
+
+ cache->left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
+ cache->left &= WM8993_HPOUT1L_VOL_MASK;
+
+ cache->right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
+ cache->right &= WM8993_HPOUT1R_VOL_MASK;
+
+ cache->dcs_cfg = dcs_cfg;
+
+ list_add_tail(&cache->list, &hubs->dcs_cache);
+}
+
+static int wm_hubs_read_dc_servo(struct snd_soc_codec *codec,
+ u16 *reg_l, u16 *reg_r)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ u16 dcs_reg, reg;
+ int ret = 0;
+
+ switch (hubs->dcs_readback_mode) {
+ case 2:
+ dcs_reg = WM8994_DC_SERVO_4E;
+ break;
+ case 1:
+ dcs_reg = WM8994_DC_SERVO_READBACK;
+ break;
+ default:
+ dcs_reg = WM8993_DC_SERVO_3;
+ break;
+ }
+
+ /* Different chips in the family support different readback
+ * methods.
+ */
+ switch (hubs->dcs_readback_mode) {
+ case 0:
+ *reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
+ & WM8993_DCS_INTEG_CHAN_0_MASK;
+ *reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
+ & WM8993_DCS_INTEG_CHAN_1_MASK;
+ break;
+ case 2:
+ case 1:
+ reg = snd_soc_read(codec, dcs_reg);
+ *reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
+ >> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
+ *reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
+ break;
+ default:
+ WARN(1, "Unknown DCS readback method\n");
+ ret = -1;
+ }
+ return ret;
+}
+
+/*
+ * Startup calibration of the DC servo
+ */
+static void enable_dc_servo(struct snd_soc_codec *codec)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct wm_hubs_dcs_cache *cache;
+ s8 offset;
+ u16 reg_l, reg_r, dcs_cfg, dcs_reg;
+
+ switch (hubs->dcs_readback_mode) {
+ case 2:
+ dcs_reg = WM8994_DC_SERVO_4E;
+ break;
+ default:
+ dcs_reg = WM8993_DC_SERVO_3;
+ break;
+ }
+
+ /* If we're using a digital only path and have a previously
+ * callibrated DC servo offset stored then use that. */
+ if (wm_hubs_dac_hp_direct(codec) &&
+ wm_hubs_dcs_cache_get(codec, &cache)) {
+ dev_dbg(codec->dev, "Using cached DCS offset %x for %d,%d\n",
+ cache->dcs_cfg, cache->left, cache->right);
+ snd_soc_write(codec, dcs_reg, cache->dcs_cfg);
+ wait_for_dc_servo(codec,
+ WM8993_DCS_TRIG_DAC_WR_0 |
+ WM8993_DCS_TRIG_DAC_WR_1);
+ return;
+ }
+
+ if (hubs->series_startup) {
+ /* Set for 32 series updates */
+ snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+ WM8993_DCS_SERIES_NO_01_MASK,
+ 32 << WM8993_DCS_SERIES_NO_01_SHIFT);
+ wait_for_dc_servo(codec,
+ WM8993_DCS_TRIG_SERIES_0 |
+ WM8993_DCS_TRIG_SERIES_1);
+ } else {
+ wait_for_dc_servo(codec,
+ WM8993_DCS_TRIG_STARTUP_0 |
+ WM8993_DCS_TRIG_STARTUP_1);
+ }
+
+ if (wm_hubs_read_dc_servo(codec, &reg_l, &reg_r) < 0)
+ return;
+
+ dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);
+
+ /* Apply correction to DC servo result */
+ if (hubs->dcs_codes_l || hubs->dcs_codes_r) {
+ dev_dbg(codec->dev,
+ "Applying %d/%d code DC servo correction\n",
+ hubs->dcs_codes_l, hubs->dcs_codes_r);
+
+ /* HPOUT1R */
+ offset = (s8)reg_r;
+ dev_dbg(codec->dev, "DCS right %d->%d\n", offset,
+ offset + hubs->dcs_codes_r);
+ offset += hubs->dcs_codes_r;
+ dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
+
+ /* HPOUT1L */
+ offset = (s8)reg_l;
+ dev_dbg(codec->dev, "DCS left %d->%d\n", offset,
+ offset + hubs->dcs_codes_l);
+ offset += hubs->dcs_codes_l;
+ dcs_cfg |= (u8)offset;
+
+ dev_dbg(codec->dev, "DCS result: %x\n", dcs_cfg);
+
+ /* Do it */
+ snd_soc_write(codec, dcs_reg, dcs_cfg);
+ wait_for_dc_servo(codec,
+ WM8993_DCS_TRIG_DAC_WR_0 |
+ WM8993_DCS_TRIG_DAC_WR_1);
+ } else {
+ dcs_cfg = reg_r << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
+ dcs_cfg |= reg_l;
+ }
+
+ /* Save the callibrated offset if we're in class W mode and
+ * therefore don't have any analogue signal mixed in. */
+ if (wm_hubs_dac_hp_direct(codec))
+ wm_hubs_dcs_cache_set(codec, dcs_cfg);
+}
+
+/*
+ * Update the DC servo calibration on gain changes
+ */
+static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ /* If we're applying an offset correction then updating the
+ * callibration would be likely to introduce further offsets. */
+ if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update)
+ return ret;
+
+ /* Only need to do this if the outputs are active */
+ if (snd_soc_read(codec, WM8993_POWER_MANAGEMENT_1)
+ & (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA))
+ snd_soc_update_bits(codec,
+ WM8993_DC_SERVO_0,
+ WM8993_DCS_TRIG_SINGLE_0 |
+ WM8993_DCS_TRIG_SINGLE_1,
+ WM8993_DCS_TRIG_SINGLE_0 |
+ WM8993_DCS_TRIG_SINGLE_1);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new analogue_snd_controls[] = {
+SOC_SINGLE_TLV("IN1L Volume", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 0, 31, 0,
+ inpga_tlv),
+SOC_SINGLE("IN1L Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN1L ZC Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 6, 1, 0),
+
+SOC_SINGLE_TLV("IN1R Volume", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 0, 31, 0,
+ inpga_tlv),
+SOC_SINGLE("IN1R Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN1R ZC Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 6, 1, 0),
+
+
+SOC_SINGLE_TLV("IN2L Volume", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 0, 31, 0,
+ inpga_tlv),
+SOC_SINGLE("IN2L Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN2L ZC Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 6, 1, 0),
+
+SOC_SINGLE_TLV("IN2R Volume", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 0, 31, 0,
+ inpga_tlv),
+SOC_SINGLE("IN2R Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN2R ZC Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 6, 1, 0),
+
+SOC_SINGLE_TLV("MIXINL IN2L Volume", WM8993_INPUT_MIXER3, 7, 1, 0,
+ inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINL IN1L Volume", WM8993_INPUT_MIXER3, 4, 1, 0,
+ inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINL Output Record Volume", WM8993_INPUT_MIXER3, 0, 7, 0,
+ inmix_tlv),
+SOC_SINGLE_TLV("MIXINL IN1LP Volume", WM8993_INPUT_MIXER5, 6, 7, 0, inmix_tlv),
+SOC_SINGLE_TLV("MIXINL Direct Voice Volume", WM8993_INPUT_MIXER5, 0, 6, 0,
+ inmix_tlv),
+
+SOC_SINGLE_TLV("MIXINR IN2R Volume", WM8993_INPUT_MIXER4, 7, 1, 0,
+ inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINR IN1R Volume", WM8993_INPUT_MIXER4, 4, 1, 0,
+ inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINR Output Record Volume", WM8993_INPUT_MIXER4, 0, 7, 0,
+ inmix_tlv),
+SOC_SINGLE_TLV("MIXINR IN1RP Volume", WM8993_INPUT_MIXER6, 6, 7, 0, inmix_tlv),
+SOC_SINGLE_TLV("MIXINR Direct Voice Volume", WM8993_INPUT_MIXER6, 0, 6, 0,
+ inmix_tlv),
+
+SOC_SINGLE_TLV("Left Output Mixer IN2RN Volume", WM8993_OUTPUT_MIXER5, 6, 7, 1,
+ outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN2LN Volume", WM8993_OUTPUT_MIXER3, 6, 7, 1,
+ outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN2LP Volume", WM8993_OUTPUT_MIXER3, 9, 7, 1,
+ outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN1L Volume", WM8993_OUTPUT_MIXER3, 0, 7, 1,
+ outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN1R Volume", WM8993_OUTPUT_MIXER3, 3, 7, 1,
+ outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer Right Input Volume",
+ WM8993_OUTPUT_MIXER5, 3, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer Left Input Volume",
+ WM8993_OUTPUT_MIXER5, 0, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer DAC Volume", WM8993_OUTPUT_MIXER5, 9, 7, 1,
+ outmix_tlv),
+
+SOC_SINGLE_TLV("Right Output Mixer IN2LN Volume",
+ WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN2RN Volume",
+ WM8993_OUTPUT_MIXER4, 6, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN1L Volume",
+ WM8993_OUTPUT_MIXER4, 3, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN1R Volume",
+ WM8993_OUTPUT_MIXER4, 0, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN2RP Volume",
+ WM8993_OUTPUT_MIXER4, 9, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer Left Input Volume",
+ WM8993_OUTPUT_MIXER6, 3, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer Right Input Volume",
+ WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer DAC Volume",
+ WM8993_OUTPUT_MIXER6, 9, 7, 1, outmix_tlv),
+
+SOC_DOUBLE_R_TLV("Output Volume", WM8993_LEFT_OPGA_VOLUME,
+ WM8993_RIGHT_OPGA_VOLUME, 0, 63, 0, outpga_tlv),
+SOC_DOUBLE_R("Output Switch", WM8993_LEFT_OPGA_VOLUME,
+ WM8993_RIGHT_OPGA_VOLUME, 6, 1, 0),
+SOC_DOUBLE_R("Output ZC Switch", WM8993_LEFT_OPGA_VOLUME,
+ WM8993_RIGHT_OPGA_VOLUME, 7, 1, 0),
+
+SOC_SINGLE("Earpiece Switch", WM8993_HPOUT2_VOLUME, 5, 1, 1),
+SOC_SINGLE_TLV("Earpiece Volume", WM8993_HPOUT2_VOLUME, 4, 1, 1, earpiece_tlv),
+
+SOC_SINGLE_TLV("SPKL Input Volume", WM8993_SPKMIXL_ATTENUATION,
+ 5, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKL IN1LP Volume", WM8993_SPKMIXL_ATTENUATION,
+ 4, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKL Output Volume", WM8993_SPKMIXL_ATTENUATION,
+ 3, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_SINGLE_TLV("SPKR Input Volume", WM8993_SPKMIXR_ATTENUATION,
+ 5, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKR IN1RP Volume", WM8993_SPKMIXR_ATTENUATION,
+ 4, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKR Output Volume", WM8993_SPKMIXR_ATTENUATION,
+ 3, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_DOUBLE_R_TLV("Speaker Mixer Volume",
+ WM8993_SPKMIXL_ATTENUATION, WM8993_SPKMIXR_ATTENUATION,
+ 0, 3, 1, spkmixout_tlv),
+SOC_DOUBLE_R_TLV("Speaker Volume",
+ WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
+ 0, 63, 0, outpga_tlv),
+SOC_DOUBLE_R("Speaker Switch",
+ WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
+ 6, 1, 0),
+SOC_DOUBLE_R("Speaker ZC Switch",
+ WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
+ 7, 1, 0),
+SOC_DOUBLE_TLV("Speaker Boost Volume", WM8993_SPKOUT_BOOST, 3, 0, 7, 0,
+ spkboost_tlv),
+SOC_ENUM("Speaker Reference", speaker_ref),
+SOC_ENUM("Speaker Mode", speaker_mode),
+
+SOC_DOUBLE_R_EXT_TLV("Headphone Volume",
+ WM8993_LEFT_OUTPUT_VOLUME, WM8993_RIGHT_OUTPUT_VOLUME,
+ 0, 63, 0, snd_soc_get_volsw, wm8993_put_dc_servo,
+ outpga_tlv),
+
+SOC_DOUBLE_R("Headphone Switch", WM8993_LEFT_OUTPUT_VOLUME,
+ WM8993_RIGHT_OUTPUT_VOLUME, 6, 1, 0),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8993_LEFT_OUTPUT_VOLUME,
+ WM8993_RIGHT_OUTPUT_VOLUME, 7, 1, 0),
+
+SOC_SINGLE("LINEOUT1N Switch", WM8993_LINE_OUTPUTS_VOLUME, 6, 1, 1),
+SOC_SINGLE("LINEOUT1P Switch", WM8993_LINE_OUTPUTS_VOLUME, 5, 1, 1),
+SOC_SINGLE_TLV("LINEOUT1 Volume", WM8993_LINE_OUTPUTS_VOLUME, 4, 1, 1,
+ line_tlv),
+
+SOC_SINGLE("LINEOUT2N Switch", WM8993_LINE_OUTPUTS_VOLUME, 2, 1, 1),
+SOC_SINGLE("LINEOUT2P Switch", WM8993_LINE_OUTPUTS_VOLUME, 1, 1, 1),
+SOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1,
+ line_tlv),
+};
+
+static int hp_supply_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (hubs->hp_startup_mode) {
+ case 0:
+ break;
+ case 1:
+ /* Enable the headphone amp */
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_HPOUT1L_ENA |
+ WM8993_HPOUT1R_ENA,
+ WM8993_HPOUT1L_ENA |
+ WM8993_HPOUT1R_ENA);
+
+ /* Enable the second stage */
+ snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ WM8993_HPOUT1L_DLY |
+ WM8993_HPOUT1R_DLY,
+ WM8993_HPOUT1L_DLY |
+ WM8993_HPOUT1R_DLY);
+ break;
+ default:
+ dev_err(codec->dev, "Unknown HP startup mode %d\n",
+ hubs->hp_startup_mode);
+ break;
+ }
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
+ WM8993_CP_ENA, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ unsigned int reg = snd_soc_read(codec, WM8993_ANALOGUE_HP_0);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
+ WM8993_CP_ENA, WM8993_CP_ENA);
+
+ msleep(5);
+
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
+ WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA);
+
+ reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
+ snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
+
+ snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+ WM8993_DCS_TIMER_PERIOD_01_MASK, 0);
+
+ enable_dc_servo(codec);
+
+ reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
+ WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
+ snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ WM8993_HPOUT1L_OUTP |
+ WM8993_HPOUT1R_OUTP |
+ WM8993_HPOUT1L_RMV_SHORT |
+ WM8993_HPOUT1R_RMV_SHORT, 0);
+
+ snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ WM8993_HPOUT1L_DLY |
+ WM8993_HPOUT1R_DLY, 0);
+
+ snd_soc_write(codec, WM8993_DC_SERVO_0, 0);
+
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
+ 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int earpiece_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *control, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u16 reg = snd_soc_read(codec, WM8993_ANTIPOP1) & ~WM8993_HPOUT2_IN_ENA;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ reg |= WM8993_HPOUT2_IN_ENA;
+ snd_soc_write(codec, WM8993_ANTIPOP1, reg);
+ udelay(50);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_write(codec, WM8993_ANTIPOP1, reg);
+ break;
+
+ default:
+ WARN(1, "Invalid event %d\n", event);
+ break;
+ }
+
+ return 0;
+}
+
+static int lineout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *control, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ bool *flag;
+
+ switch (w->shift) {
+ case WM8993_LINEOUT1N_ENA_SHIFT:
+ flag = &hubs->lineout1n_ena;
+ break;
+ case WM8993_LINEOUT1P_ENA_SHIFT:
+ flag = &hubs->lineout1p_ena;
+ break;
+ case WM8993_LINEOUT2N_ENA_SHIFT:
+ flag = &hubs->lineout2n_ena;
+ break;
+ case WM8993_LINEOUT2P_ENA_SHIFT:
+ flag = &hubs->lineout2p_ena;
+ break;
+ default:
+ WARN(1, "Unknown line output");
+ return -EINVAL;
+ }
+
+ *flag = SND_SOC_DAPM_EVENT_ON(event);
+
+ return 0;
+}
+
+static int micbias_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+
+ switch (w->shift) {
+ case WM8993_MICB1_ENA_SHIFT:
+ if (hubs->micb1_delay)
+ msleep(hubs->micb1_delay);
+ break;
+ case WM8993_MICB2_ENA_SHIFT:
+ if (hubs->micb2_delay)
+ msleep(hubs->micb2_delay);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void wm_hubs_update_class_w(struct snd_soc_codec *codec)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ int enable = WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ;
+
+ if (!wm_hubs_dac_hp_direct(codec))
+ enable = false;
+
+ if (hubs->check_class_w_digital && !hubs->check_class_w_digital(codec))
+ enable = false;
+
+ dev_vdbg(codec->dev, "Class W %s\n", enable ? "enabled" : "disabled");
+
+ snd_soc_update_bits(codec, WM8993_CLASS_W_0,
+ WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ, enable);
+
+ snd_soc_write(codec, WM8993_LEFT_OUTPUT_VOLUME,
+ snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME));
+ snd_soc_write(codec, WM8993_RIGHT_OUTPUT_VOLUME,
+ snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME));
+}
+EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
+
+#define WM_HUBS_SINGLE_W(xname, reg, shift, max, invert) \
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, class_w_put_volsw)
+
+static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ int ret;
+
+ ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ wm_hubs_update_class_w(codec);
+
+ return ret;
+}
+
+#define WM_HUBS_ENUM_W(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_enum_double, \
+ .get = snd_soc_dapm_get_enum_double, \
+ .put = class_w_put_double, \
+ .private_value = (unsigned long)&xenum }
+
+static int class_w_put_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ int ret;
+
+ ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ wm_hubs_update_class_w(codec);
+
+ return ret;
+}
+
+static const char *hp_mux_text[] = {
+ "Mixer",
+ "DAC",
+};
+
+static SOC_ENUM_SINGLE_DECL(hpl_enum,
+ WM8993_OUTPUT_MIXER1, 8, hp_mux_text);
+
+const struct snd_kcontrol_new wm_hubs_hpl_mux =
+ WM_HUBS_ENUM_W("Left Headphone Mux", hpl_enum);
+EXPORT_SYMBOL_GPL(wm_hubs_hpl_mux);
+
+static SOC_ENUM_SINGLE_DECL(hpr_enum,
+ WM8993_OUTPUT_MIXER2, 8, hp_mux_text);
+
+const struct snd_kcontrol_new wm_hubs_hpr_mux =
+ WM_HUBS_ENUM_W("Right Headphone Mux", hpr_enum);
+EXPORT_SYMBOL_GPL(wm_hubs_hpr_mux);
+
+static const struct snd_kcontrol_new in1l_pga[] = {
+SOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0),
+SOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new in1r_pga[] = {
+SOC_DAPM_SINGLE("IN1RP Switch", WM8993_INPUT_MIXER2, 1, 1, 0),
+SOC_DAPM_SINGLE("IN1RN Switch", WM8993_INPUT_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new in2l_pga[] = {
+SOC_DAPM_SINGLE("IN2LP Switch", WM8993_INPUT_MIXER2, 7, 1, 0),
+SOC_DAPM_SINGLE("IN2LN Switch", WM8993_INPUT_MIXER2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new in2r_pga[] = {
+SOC_DAPM_SINGLE("IN2RP Switch", WM8993_INPUT_MIXER2, 3, 1, 0),
+SOC_DAPM_SINGLE("IN2RN Switch", WM8993_INPUT_MIXER2, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixinl[] = {
+SOC_DAPM_SINGLE("IN2L Switch", WM8993_INPUT_MIXER3, 8, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_INPUT_MIXER3, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixinr[] = {
+SOC_DAPM_SINGLE("IN2R Switch", WM8993_INPUT_MIXER4, 8, 1, 0),
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_INPUT_MIXER4, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_output_mixer[] = {
+WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0),
+WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0),
+WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0),
+WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0),
+WM_HUBS_SINGLE_W("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0),
+WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0),
+WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0),
+WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_output_mixer[] = {
+WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0),
+WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0),
+WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0),
+WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0),
+WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0),
+WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0),
+WM_HUBS_SINGLE_W("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0),
+WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new earpiece_mixer[] = {
+SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_HPOUT2_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_HPOUT2_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_HPOUT2_MIXER, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_speaker_boost[] = {
+SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 5, 1, 0),
+SOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 4, 1, 0),
+SOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_boost[] = {
+SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 2, 1, 0),
+SOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 1, 1, 0),
+SOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line1_mix[] = {
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER1, 2, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER1, 1, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line1n_mix[] = {
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 6, 1, 0),
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER1, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new line1p_mix[] = {
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line2_mix[] = {
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER2, 2, 1, 0),
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER2, 1, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line2n_mix[] = {
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER2, 5, 1, 0),
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new line2p_mix[] = {
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget analogue_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1LN"),
+SND_SOC_DAPM_INPUT("IN1LP"),
+SND_SOC_DAPM_INPUT("IN2LN"),
+SND_SOC_DAPM_INPUT("IN2LP:VXRN"),
+SND_SOC_DAPM_INPUT("IN1RN"),
+SND_SOC_DAPM_INPUT("IN1RP"),
+SND_SOC_DAPM_INPUT("IN2RN"),
+SND_SOC_DAPM_INPUT("IN2RP:VXRP"),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0,
+ micbias_event, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_SUPPLY("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0,
+ micbias_event, SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_MIXER("IN1L PGA", WM8993_POWER_MANAGEMENT_2, 6, 0,
+ in1l_pga, ARRAY_SIZE(in1l_pga)),
+SND_SOC_DAPM_MIXER("IN1R PGA", WM8993_POWER_MANAGEMENT_2, 4, 0,
+ in1r_pga, ARRAY_SIZE(in1r_pga)),
+
+SND_SOC_DAPM_MIXER("IN2L PGA", WM8993_POWER_MANAGEMENT_2, 7, 0,
+ in2l_pga, ARRAY_SIZE(in2l_pga)),
+SND_SOC_DAPM_MIXER("IN2R PGA", WM8993_POWER_MANAGEMENT_2, 5, 0,
+ in2r_pga, ARRAY_SIZE(in2r_pga)),
+
+SND_SOC_DAPM_MIXER("MIXINL", WM8993_POWER_MANAGEMENT_2, 9, 0,
+ mixinl, ARRAY_SIZE(mixinl)),
+SND_SOC_DAPM_MIXER("MIXINR", WM8993_POWER_MANAGEMENT_2, 8, 0,
+ mixinr, ARRAY_SIZE(mixinr)),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", WM8993_POWER_MANAGEMENT_3, 5, 0,
+ left_output_mixer, ARRAY_SIZE(left_output_mixer)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0,
+ right_output_mixer, ARRAY_SIZE(right_output_mixer)),
+
+SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0, hp_supply_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_OUT_DRV_E("Headphone PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
+ earpiece_mixer, ARRAY_SIZE(earpiece_mixer)),
+SND_SOC_DAPM_PGA_E("Earpiece Driver", WM8993_POWER_MANAGEMENT_1, 11, 0,
+ NULL, 0, earpiece_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_MIXER("SPKL Boost", SND_SOC_NOPM, 0, 0,
+ left_speaker_boost, ARRAY_SIZE(left_speaker_boost)),
+SND_SOC_DAPM_MIXER("SPKR Boost", SND_SOC_NOPM, 0, 0,
+ right_speaker_boost, ARRAY_SIZE(right_speaker_boost)),
+
+SND_SOC_DAPM_SUPPLY("TSHUT", WM8993_POWER_MANAGEMENT_2, 14, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPKL Driver", WM8993_POWER_MANAGEMENT_1, 12, 0,
+ NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPKR Driver", WM8993_POWER_MANAGEMENT_1, 13, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_MIXER("LINEOUT1 Mixer", SND_SOC_NOPM, 0, 0,
+ line1_mix, ARRAY_SIZE(line1_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT2 Mixer", SND_SOC_NOPM, 0, 0,
+ line2_mix, ARRAY_SIZE(line2_mix)),
+
+SND_SOC_DAPM_MIXER("LINEOUT1N Mixer", SND_SOC_NOPM, 0, 0,
+ line1n_mix, ARRAY_SIZE(line1n_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT1P Mixer", SND_SOC_NOPM, 0, 0,
+ line1p_mix, ARRAY_SIZE(line1p_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT2N Mixer", SND_SOC_NOPM, 0, 0,
+ line2n_mix, ARRAY_SIZE(line2n_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT2P Mixer", SND_SOC_NOPM, 0, 0,
+ line2p_mix, ARRAY_SIZE(line2p_mix)),
+
+SND_SOC_DAPM_OUT_DRV_E("LINEOUT1N Driver", WM8993_POWER_MANAGEMENT_3, 13, 0,
+ NULL, 0, lineout_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_OUT_DRV_E("LINEOUT1P Driver", WM8993_POWER_MANAGEMENT_3, 12, 0,
+ NULL, 0, lineout_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_OUT_DRV_E("LINEOUT2N Driver", WM8993_POWER_MANAGEMENT_3, 11, 0,
+ NULL, 0, lineout_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_OUT_DRV_E("LINEOUT2P Driver", WM8993_POWER_MANAGEMENT_3, 10, 0,
+ NULL, 0, lineout_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2P"),
+SND_SOC_DAPM_OUTPUT("HPOUT2N"),
+SND_SOC_DAPM_OUTPUT("LINEOUT1P"),
+SND_SOC_DAPM_OUTPUT("LINEOUT1N"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2P"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2N"),
+};
+
+static const struct snd_soc_dapm_route analogue_routes[] = {
+ { "MICBIAS1", NULL, "CLK_SYS" },
+ { "MICBIAS2", NULL, "CLK_SYS" },
+
+ { "IN1L PGA", "IN1LP Switch", "IN1LP" },
+ { "IN1L PGA", "IN1LN Switch", "IN1LN" },
+
+ { "IN1L PGA", NULL, "VMID" },
+ { "IN1R PGA", NULL, "VMID" },
+ { "IN2L PGA", NULL, "VMID" },
+ { "IN2R PGA", NULL, "VMID" },
+
+ { "IN1R PGA", "IN1RP Switch", "IN1RP" },
+ { "IN1R PGA", "IN1RN Switch", "IN1RN" },
+
+ { "IN2L PGA", "IN2LP Switch", "IN2LP:VXRN" },
+ { "IN2L PGA", "IN2LN Switch", "IN2LN" },
+
+ { "IN2R PGA", "IN2RP Switch", "IN2RP:VXRP" },
+ { "IN2R PGA", "IN2RN Switch", "IN2RN" },
+
+ { "Direct Voice", NULL, "IN2LP:VXRN" },
+ { "Direct Voice", NULL, "IN2RP:VXRP" },
+
+ { "MIXINL", "IN1L Switch", "IN1L PGA" },
+ { "MIXINL", "IN2L Switch", "IN2L PGA" },
+ { "MIXINL", NULL, "Direct Voice" },
+ { "MIXINL", NULL, "IN1LP" },
+ { "MIXINL", NULL, "Left Output Mixer" },
+ { "MIXINL", NULL, "VMID" },
+
+ { "MIXINR", "IN1R Switch", "IN1R PGA" },
+ { "MIXINR", "IN2R Switch", "IN2R PGA" },
+ { "MIXINR", NULL, "Direct Voice" },
+ { "MIXINR", NULL, "IN1RP" },
+ { "MIXINR", NULL, "Right Output Mixer" },
+ { "MIXINR", NULL, "VMID" },
+
+ { "ADCL", NULL, "MIXINL" },
+ { "ADCR", NULL, "MIXINR" },
+
+ { "Left Output Mixer", "Left Input Switch", "MIXINL" },
+ { "Left Output Mixer", "Right Input Switch", "MIXINR" },
+ { "Left Output Mixer", "IN2RN Switch", "IN2RN" },
+ { "Left Output Mixer", "IN2LN Switch", "IN2LN" },
+ { "Left Output Mixer", "IN2LP Switch", "IN2LP:VXRN" },
+ { "Left Output Mixer", "IN1L Switch", "IN1L PGA" },
+ { "Left Output Mixer", "IN1R Switch", "IN1R PGA" },
+
+ { "Right Output Mixer", "Left Input Switch", "MIXINL" },
+ { "Right Output Mixer", "Right Input Switch", "MIXINR" },
+ { "Right Output Mixer", "IN2LN Switch", "IN2LN" },
+ { "Right Output Mixer", "IN2RN Switch", "IN2RN" },
+ { "Right Output Mixer", "IN2RP Switch", "IN2RP:VXRP" },
+ { "Right Output Mixer", "IN1L Switch", "IN1L PGA" },
+ { "Right Output Mixer", "IN1R Switch", "IN1R PGA" },
+
+ { "Left Output PGA", NULL, "Left Output Mixer" },
+ { "Left Output PGA", NULL, "TOCLK" },
+
+ { "Right Output PGA", NULL, "Right Output Mixer" },
+ { "Right Output PGA", NULL, "TOCLK" },
+
+ { "Earpiece Mixer", "Direct Voice Switch", "Direct Voice" },
+ { "Earpiece Mixer", "Left Output Switch", "Left Output PGA" },
+ { "Earpiece Mixer", "Right Output Switch", "Right Output PGA" },
+
+ { "Earpiece Driver", NULL, "VMID" },
+ { "Earpiece Driver", NULL, "Earpiece Mixer" },
+ { "HPOUT2N", NULL, "Earpiece Driver" },
+ { "HPOUT2P", NULL, "Earpiece Driver" },
+
+ { "SPKL", "Input Switch", "MIXINL" },
+ { "SPKL", "IN1LP Switch", "IN1LP" },
+ { "SPKL", "Output Switch", "Left Output PGA" },
+ { "SPKL", NULL, "TOCLK" },
+
+ { "SPKR", "Input Switch", "MIXINR" },
+ { "SPKR", "IN1RP Switch", "IN1RP" },
+ { "SPKR", "Output Switch", "Right Output PGA" },
+ { "SPKR", NULL, "TOCLK" },
+
+ { "SPKL Boost", "Direct Voice Switch", "Direct Voice" },
+ { "SPKL Boost", "SPKL Switch", "SPKL" },
+ { "SPKL Boost", "SPKR Switch", "SPKR" },
+
+ { "SPKR Boost", "Direct Voice Switch", "Direct Voice" },
+ { "SPKR Boost", "SPKR Switch", "SPKR" },
+ { "SPKR Boost", "SPKL Switch", "SPKL" },
+
+ { "SPKL Driver", NULL, "VMID" },
+ { "SPKL Driver", NULL, "SPKL Boost" },
+ { "SPKL Driver", NULL, "CLK_SYS" },
+ { "SPKL Driver", NULL, "TSHUT" },
+
+ { "SPKR Driver", NULL, "VMID" },
+ { "SPKR Driver", NULL, "SPKR Boost" },
+ { "SPKR Driver", NULL, "CLK_SYS" },
+ { "SPKR Driver", NULL, "TSHUT" },
+
+ { "SPKOUTLP", NULL, "SPKL Driver" },
+ { "SPKOUTLN", NULL, "SPKL Driver" },
+ { "SPKOUTRP", NULL, "SPKR Driver" },
+ { "SPKOUTRN", NULL, "SPKR Driver" },
+
+ { "Left Headphone Mux", "Mixer", "Left Output PGA" },
+ { "Right Headphone Mux", "Mixer", "Right Output PGA" },
+
+ { "Headphone PGA", NULL, "Left Headphone Mux" },
+ { "Headphone PGA", NULL, "Right Headphone Mux" },
+ { "Headphone PGA", NULL, "VMID" },
+ { "Headphone PGA", NULL, "CLK_SYS" },
+ { "Headphone PGA", NULL, "Headphone Supply" },
+
+ { "HPOUT1L", NULL, "Headphone PGA" },
+ { "HPOUT1R", NULL, "Headphone PGA" },
+
+ { "LINEOUT1N Driver", NULL, "VMID" },
+ { "LINEOUT1P Driver", NULL, "VMID" },
+ { "LINEOUT2N Driver", NULL, "VMID" },
+ { "LINEOUT2P Driver", NULL, "VMID" },
+
+ { "LINEOUT1N", NULL, "LINEOUT1N Driver" },
+ { "LINEOUT1P", NULL, "LINEOUT1P Driver" },
+ { "LINEOUT2N", NULL, "LINEOUT2N Driver" },
+ { "LINEOUT2P", NULL, "LINEOUT2P Driver" },
+};
+
+static const struct snd_soc_dapm_route lineout1_diff_routes[] = {
+ { "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" },
+ { "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" },
+ { "LINEOUT1 Mixer", "Output Switch", "Left Output PGA" },
+
+ { "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" },
+ { "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" },
+};
+
+static const struct snd_soc_dapm_route lineout1_se_routes[] = {
+ { "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" },
+ { "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" },
+
+ { "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" },
+
+ { "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" },
+ { "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" },
+};
+
+static const struct snd_soc_dapm_route lineout2_diff_routes[] = {
+ { "LINEOUT2 Mixer", "IN1L Switch", "IN1L PGA" },
+ { "LINEOUT2 Mixer", "IN1R Switch", "IN1R PGA" },
+ { "LINEOUT2 Mixer", "Output Switch", "Right Output PGA" },
+
+ { "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" },
+ { "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" },
+};
+
+static const struct snd_soc_dapm_route lineout2_se_routes[] = {
+ { "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" },
+ { "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" },
+
+ { "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" },
+
+ { "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" },
+ { "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" },
+};
+
+int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ /* Latch volume update bits & default ZC on */
+ snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8993_IN1_VU, WM8993_IN1_VU);
+ snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8993_IN1_VU, WM8993_IN1_VU);
+ snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8993_IN2_VU, WM8993_IN2_VU);
+ snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8993_IN2_VU, WM8993_IN2_VU);
+
+ snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_LEFT,
+ WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
+ snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT,
+ WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
+
+ snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME,
+ WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC,
+ WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC);
+ snd_soc_update_bits(codec, WM8993_RIGHT_OUTPUT_VOLUME,
+ WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC,
+ WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC);
+
+ snd_soc_update_bits(codec, WM8993_LEFT_OPGA_VOLUME,
+ WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU,
+ WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU);
+ snd_soc_update_bits(codec, WM8993_RIGHT_OPGA_VOLUME,
+ WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU,
+ WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU);
+
+ snd_soc_add_codec_controls(codec, analogue_snd_controls,
+ ARRAY_SIZE(analogue_snd_controls));
+
+ snd_soc_dapm_new_controls(dapm, analogue_dapm_widgets,
+ ARRAY_SIZE(analogue_dapm_widgets));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_controls);
+
+int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
+ int lineout1_diff, int lineout2_diff)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ hubs->codec = codec;
+
+ INIT_LIST_HEAD(&hubs->dcs_cache);
+ init_completion(&hubs->dcs_done);
+
+ snd_soc_dapm_add_routes(dapm, analogue_routes,
+ ARRAY_SIZE(analogue_routes));
+
+ if (lineout1_diff)
+ snd_soc_dapm_add_routes(dapm,
+ lineout1_diff_routes,
+ ARRAY_SIZE(lineout1_diff_routes));
+ else
+ snd_soc_dapm_add_routes(dapm,
+ lineout1_se_routes,
+ ARRAY_SIZE(lineout1_se_routes));
+
+ if (lineout2_diff)
+ snd_soc_dapm_add_routes(dapm,
+ lineout2_diff_routes,
+ ARRAY_SIZE(lineout2_diff_routes));
+ else
+ snd_soc_dapm_add_routes(dapm,
+ lineout2_se_routes,
+ ARRAY_SIZE(lineout2_se_routes));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
+
+int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
+ int lineout1_diff, int lineout2_diff,
+ int lineout1fb, int lineout2fb,
+ int jd_scthr, int jd_thr,
+ int micbias1_delay, int micbias2_delay,
+ int micbias1_lvl, int micbias2_lvl)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+
+ hubs->lineout1_se = !lineout1_diff;
+ hubs->lineout2_se = !lineout2_diff;
+ hubs->micb1_delay = micbias1_delay;
+ hubs->micb2_delay = micbias2_delay;
+
+ if (!lineout1_diff)
+ snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
+ WM8993_LINEOUT1_MODE,
+ WM8993_LINEOUT1_MODE);
+ if (!lineout2_diff)
+ snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
+ WM8993_LINEOUT2_MODE,
+ WM8993_LINEOUT2_MODE);
+
+ if (!lineout1_diff && !lineout2_diff)
+ snd_soc_update_bits(codec, WM8993_ANTIPOP1,
+ WM8993_LINEOUT_VMID_BUF_ENA,
+ WM8993_LINEOUT_VMID_BUF_ENA);
+
+ if (lineout1fb)
+ snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+ WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
+
+ if (lineout2fb)
+ snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+ WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
+
+ snd_soc_update_bits(codec, WM8993_MICBIAS,
+ WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
+ WM8993_MICB1_LVL | WM8993_MICB2_LVL,
+ jd_scthr << WM8993_JD_SCTHR_SHIFT |
+ jd_thr << WM8993_JD_THR_SHIFT |
+ micbias1_lvl |
+ micbias2_lvl << WM8993_MICB2_LVL_SHIFT);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_handle_analogue_pdata);
+
+void wm_hubs_vmid_ena(struct snd_soc_codec *codec)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ int val = 0;
+
+ if (hubs->lineout1_se)
+ val |= WM8993_LINEOUT1N_ENA | WM8993_LINEOUT1P_ENA;
+
+ if (hubs->lineout2_se)
+ val |= WM8993_LINEOUT2N_ENA | WM8993_LINEOUT2P_ENA;
+
+ /* Enable the line outputs while we power up */
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_3, val, val);
+}
+EXPORT_SYMBOL_GPL(wm_hubs_vmid_ena);
+
+void wm_hubs_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ int mask, val;
+
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ /* Clamp the inputs to VMID while we ramp to charge caps */
+ snd_soc_update_bits(codec, WM8993_INPUTS_CLAMP_REG,
+ WM8993_INPUTS_CLAMP, WM8993_INPUTS_CLAMP);
+ break;
+
+ case SND_SOC_BIAS_ON:
+ /* Turn off any unneded single ended outputs */
+ val = 0;
+ mask = 0;
+
+ if (hubs->lineout1_se)
+ mask |= WM8993_LINEOUT1N_ENA | WM8993_LINEOUT1P_ENA;
+
+ if (hubs->lineout2_se)
+ mask |= WM8993_LINEOUT2N_ENA | WM8993_LINEOUT2P_ENA;
+
+ if (hubs->lineout1_se && hubs->lineout1n_ena)
+ val |= WM8993_LINEOUT1N_ENA;
+
+ if (hubs->lineout1_se && hubs->lineout1p_ena)
+ val |= WM8993_LINEOUT1P_ENA;
+
+ if (hubs->lineout2_se && hubs->lineout2n_ena)
+ val |= WM8993_LINEOUT2N_ENA;
+
+ if (hubs->lineout2_se && hubs->lineout2p_ena)
+ val |= WM8993_LINEOUT2P_ENA;
+
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_3,
+ mask, val);
+
+ /* Remove the input clamps */
+ snd_soc_update_bits(codec, WM8993_INPUTS_CLAMP_REG,
+ WM8993_INPUTS_CLAMP, 0);
+ break;
+
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(wm_hubs_set_bias_level);
+
+MODULE_DESCRIPTION("Shared support for Wolfson hubs products");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/wm_hubs.h b/kernel/sound/soc/codecs/wm_hubs.h
new file mode 100644
index 000000000..24c763df2
--- /dev/null
+++ b/kernel/sound/soc/codecs/wm_hubs.h
@@ -0,0 +1,74 @@
+/*
+ * wm_hubs.h -- WM899x common code
+ *
+ * Copyright 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM_HUBS_H
+#define _WM_HUBS_H
+
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <sound/control.h>
+
+struct snd_soc_codec;
+
+extern const unsigned int wm_hubs_spkmix_tlv[];
+
+/* This *must* be the first element of the codec->private_data struct */
+struct wm_hubs_data {
+ int dcs_codes_l;
+ int dcs_codes_r;
+ int dcs_readback_mode;
+ int hp_startup_mode;
+ int series_startup;
+ int no_series_update;
+
+ bool no_cache_dac_hp_direct;
+ struct list_head dcs_cache;
+ bool (*check_class_w_digital)(struct snd_soc_codec *);
+
+ int micb1_delay;
+ int micb2_delay;
+
+ bool lineout1_se;
+ bool lineout1n_ena;
+ bool lineout1p_ena;
+
+ bool lineout2_se;
+ bool lineout2n_ena;
+ bool lineout2p_ena;
+
+ bool dcs_done_irq;
+ struct completion dcs_done;
+
+ struct snd_soc_codec *codec;
+};
+
+extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
+extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int);
+extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *,
+ int lineout1_diff, int lineout2_diff,
+ int lineout1fb, int lineout2fb,
+ int jd_scthr, int jd_thr,
+ int micbias1_dly, int micbias2_dly,
+ int micbias1_lvl, int micbias2_lvl);
+
+extern irqreturn_t wm_hubs_dcs_done(int irq, void *data);
+extern void wm_hubs_vmid_ena(struct snd_soc_codec *codec);
+extern void wm_hubs_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level);
+extern void wm_hubs_update_class_w(struct snd_soc_codec *codec);
+
+extern const struct snd_kcontrol_new wm_hubs_hpl_mux;
+extern const struct snd_kcontrol_new wm_hubs_hpr_mux;
+
+#endif
diff --git a/kernel/sound/soc/codecs/wmfw.h b/kernel/sound/soc/codecs/wmfw.h
new file mode 100644
index 000000000..ef163360a
--- /dev/null
+++ b/kernel/sound/soc/codecs/wmfw.h
@@ -0,0 +1,133 @@
+/*
+ * wmfw.h - Wolfson firmware format information
+ *
+ * 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 __WMFW_H
+#define __WMFW_H
+
+#include <linux/types.h>
+
+struct wmfw_header {
+ char magic[4];
+ __le32 len;
+ __le16 rev;
+ u8 core;
+ u8 ver;
+} __packed;
+
+struct wmfw_footer {
+ __le64 timestamp;
+ __le32 checksum;
+} __packed;
+
+struct wmfw_adsp1_sizes {
+ __le32 dm;
+ __le32 pm;
+ __le32 zm;
+} __packed;
+
+struct wmfw_adsp2_sizes {
+ __le32 xm;
+ __le32 ym;
+ __le32 pm;
+ __le32 zm;
+} __packed;
+
+struct wmfw_region {
+ union {
+ __be32 type;
+ __le32 offset;
+ };
+ __le32 len;
+ u8 data[];
+} __packed;
+
+struct wmfw_id_hdr {
+ __be32 core_id;
+ __be32 core_rev;
+ __be32 id;
+ __be32 ver;
+} __packed;
+
+struct wmfw_adsp1_id_hdr {
+ struct wmfw_id_hdr fw;
+ __be32 zm;
+ __be32 dm;
+ __be32 algs;
+} __packed;
+
+struct wmfw_adsp2_id_hdr {
+ struct wmfw_id_hdr fw;
+ __be32 zm;
+ __be32 xm;
+ __be32 ym;
+ __be32 algs;
+} __packed;
+
+struct wmfw_alg_hdr {
+ __be32 id;
+ __be32 ver;
+} __packed;
+
+struct wmfw_adsp1_alg_hdr {
+ struct wmfw_alg_hdr alg;
+ __be32 zm;
+ __be32 dm;
+} __packed;
+
+struct wmfw_adsp2_alg_hdr {
+ struct wmfw_alg_hdr alg;
+ __be32 zm;
+ __be32 xm;
+ __be32 ym;
+} __packed;
+
+struct wmfw_coeff_hdr {
+ u8 magic[4];
+ __le32 len;
+ union {
+ __be32 rev;
+ __le32 ver;
+ };
+ union {
+ __be32 core;
+ __le32 core_ver;
+ };
+ u8 data[];
+} __packed;
+
+struct wmfw_coeff_item {
+ __le16 offset;
+ __le16 type;
+ __le32 id;
+ __le32 ver;
+ __le32 sr;
+ __le32 len;
+ u8 data[];
+} __packed;
+
+#define WMFW_ADSP1 1
+#define WMFW_ADSP2 2
+
+#define WMFW_ABSOLUTE 0xf0
+#define WMFW_NAME_TEXT 0xfe
+#define WMFW_INFO_TEXT 0xff
+
+#define WMFW_ADSP1_PM 2
+#define WMFW_ADSP1_DM 3
+#define WMFW_ADSP1_ZM 4
+
+#define WMFW_ADSP2_PM 2
+#define WMFW_ADSP2_ZM 4
+#define WMFW_ADSP2_XM 5
+#define WMFW_ADSP2_YM 6
+
+#endif